Merge pull request #6162 from cmrd-senya/5684-public-additional-profile

Allow additional profile fields (previously private profile) to be set public (new updated version)
This commit is contained in:
Jonne Haß 2015-07-11 10:18:40 +02:00
commit 397845161f
25 changed files with 132 additions and 69 deletions

View file

@ -43,6 +43,7 @@ bind to an UNIX socket at `unix:tmp/diaspora.sock`. Please change your local
## Features ## Features
* Support color themes [#6033](https://github.com/diaspora/diaspora/pull/6033) * Support color themes [#6033](https://github.com/diaspora/diaspora/pull/6033)
* Add mobile services and privacy settings pages [#6086](https://github.com/diaspora/diaspora/pull/6086) * Add mobile services and privacy settings pages [#6086](https://github.com/diaspora/diaspora/pull/6086)
* Optionally make your extended profile details public [#6162](https://github.com/diaspora/diaspora/pull/6162)
# 0.5.2.0 # 0.5.2.0

View file

@ -57,6 +57,7 @@ gem "bootstrap-sass", "3.3.5"
gem "compass-rails", "2.0.4" gem "compass-rails", "2.0.4"
gem "sass-rails", "5.0.1" gem "sass-rails", "5.0.1"
gem "autoprefixer-rails", "5.2.1" gem "autoprefixer-rails", "5.2.1"
gem "bootstrap-switch-rails", "3.3.3"
# Database # Database

View file

@ -73,6 +73,7 @@ GEM
bootstrap-sass (3.3.5) bootstrap-sass (3.3.5)
autoprefixer-rails (>= 5.0.0.1) autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19) sass (>= 3.2.19)
bootstrap-switch-rails (3.3.3)
buftok (0.2.0) buftok (0.2.0)
builder (3.2.2) builder (3.2.2)
byebug (4.0.5) byebug (4.0.5)
@ -751,6 +752,7 @@ DEPENDENCIES
autoprefixer-rails (= 5.2.1) autoprefixer-rails (= 5.2.1)
backbone-on-rails (= 1.1.2.1) backbone-on-rails (= 1.1.2.1)
bootstrap-sass (= 3.3.5) bootstrap-sass (= 3.3.5)
bootstrap-switch-rails (= 3.3.3)
capybara (= 2.4.4) capybara (= 2.4.4)
carrierwave (= 0.10.0) carrierwave (= 0.10.0)
compass-rails (= 2.0.4) compass-rails (= 2.0.4)

View file

@ -2,6 +2,7 @@
app.pages.Settings = Backbone.View.extend({ app.pages.Settings = Backbone.View.extend({
initialize: function() { initialize: function() {
$(".settings_visibility").tooltip({placement: "top"}); $(".settings_visibility").tooltip({placement: "top"});
$("[name='profile[public_details]']").bootstrapSwitch();
} }
}); });
// @license-end // @license-end

View file

@ -9,6 +9,7 @@ app.Router = Backbone.Router.extend({
"conversations": "conversations", "conversations": "conversations",
"user/edit": "settings", "user/edit": "settings",
"users/sign_up": "registration", "users/sign_up": "registration",
"profile/edit": "settings",
//new hotness //new hotness
"posts/:id": "singlePost", "posts/:id": "singlePost",

View file

@ -1,17 +1,7 @@
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ProfileSidebar = app.views.Base.extend({ app.views.ProfileSidebar = app.views.Base.extend({
templateName: 'profile_sidebar', templateName: "profile_sidebar"
presenter: function() {
return _.extend({}, this.defaultPresenter(), {
show_profile_info: this._shouldShowProfileInfo(),
});
},
_shouldShowProfileInfo: function() {
return (this.model.isSharing() || this.model.get('is_own_profile'));
}
}); });
// @license-end // @license-end

View file

@ -45,3 +45,4 @@
//= require mentions //= require mentions
//= require bootstrap //= require bootstrap
//= require osmlocator //= require osmlocator
//= require bootstrap-switch

View file

@ -92,3 +92,5 @@
/* statistics */ /* statistics */
@import 'new_styles/statistics'; @import 'new_styles/statistics';
@import "bootstrap3-switch";

View file

@ -148,3 +148,9 @@
&:last-of-type{ float: right; } &:last-of-type{ float: right; }
} }
} }
#update_profile_form {
.visibility-hint-icon {
cursor: pointer;
}
}

View file

@ -75,7 +75,7 @@ class PeopleController < ApplicationController
def show def show
mark_corresponding_notifications_read if user_signed_in? mark_corresponding_notifications_read if user_signed_in?
@person_json = PersonPresenter.new(@person, current_user).full_hash_with_profile @person_json = PersonPresenter.new(@person, current_user).as_json
respond_to do |format| respond_to do |format|
format.all do format.all do
@ -144,7 +144,7 @@ class PeopleController < ApplicationController
if @person if @person
@contact = current_user.contact_for(@person) @contact = current_user.contact_for(@person)
@contacts_of_contact = Contact.contact_contacts_for(current_user, @person) @contacts_of_contact = Contact.contact_contacts_for(current_user, @person)
gon.preloads[:person] = PersonPresenter.new(@person, current_user).full_hash_with_profile gon.preloads[:person] = PersonPresenter.new(@person, current_user).as_json
gon.preloads[:photos] = { gon.preloads[:photos] = {
count: photos_from(@person, :all).count(:all) count: photos_from(@person, :all).count(:all)
} }

View file

@ -25,7 +25,7 @@ class PhotosController < ApplicationController
@posts = current_user.photos_from(@person, max_time: max_time).order('created_at desc') @posts = current_user.photos_from(@person, max_time: max_time).order('created_at desc')
respond_to do |format| respond_to do |format|
format.all do format.all do
gon.preloads[:person] = PersonPresenter.new(@person, current_user).full_hash_with_profile gon.preloads[:person] = PersonPresenter.new(@person, current_user).as_json
gon.preloads[:photos] = { gon.preloads[:photos] = {
count: current_user.photos_from(@person, limit: :all).count(:all) count: current_user.photos_from(@person, limit: :all).count(:all)
} }

View file

@ -40,6 +40,7 @@ class ProfilesController < ApplicationController
#checkbox tags wtf #checkbox tags wtf
@profile_attrs[:searchable] ||= false @profile_attrs[:searchable] ||= false
@profile_attrs[:nsfw] ||= false @profile_attrs[:nsfw] ||= false
@profile_attrs[:public_details] ||= false
if params[:photo_id] if params[:photo_id]
@profile_attrs[:photo] = Photo.where(:author_id => current_user.person_id, :id => params[:photo_id]).first @profile_attrs[:photo] = Photo.where(:author_id => current_user.person_id, :id => params[:photo_id]).first
@ -79,6 +80,8 @@ class ProfilesController < ApplicationController
end end
def profile_params def profile_params
params.require(:profile).permit(:first_name, :last_name, :gender, :bio, :location, :searchable, :tag_string, :nsfw, :date => [:year, :month, :day]) || {} params.require(:profile).permit(:first_name, :last_name, :gender, :bio,
:location, :searchable, :tag_string, :nsfw,
:public_details, date: %i(year month day)) || {}
end end
end end

View file

@ -31,7 +31,7 @@ class Person < ActiveRecord::Base
has_one :profile, :dependent => :destroy has_one :profile, :dependent => :destroy
delegate :last_name, :image_url, :tag_string, :bio, :location, delegate :last_name, :image_url, :tag_string, :bio, :location,
:gender, :birthday, :formatted_birthday, :tags, :searchable, :gender, :birthday, :formatted_birthday, :tags, :searchable,
to: :profile :public_details?, to: :profile
accepts_nested_attributes_for :profile accepts_nested_attributes_for :profile
before_validation :downcase_diaspora_handle before_validation :downcase_diaspora_handle

View file

@ -12,6 +12,6 @@ class ContactPresenter < BasePresenter
end end
def full_hash_with_person def full_hash_with_person
full_hash.merge(person: PersonPresenter.new(person, current_user).full_hash_with_profile) full_hash.merge(person: PersonPresenter.new(person, current_user).as_json)
end end
end end

View file

@ -10,41 +10,16 @@ class PersonPresenter < BasePresenter
def full_hash def full_hash
base_hash.merge( base_hash.merge(
relationship: relationship, relationship: relationship,
block: is_blocked? ? BlockPresenter.new(current_user_person_block).base_hash : false, block: is_blocked? ? BlockPresenter.new(current_user_person_block).base_hash : false,
contact: (!own_profile? && has_contact?) ? {id: current_user_person_contact.id} : false, contact: (!own_profile? && has_contact?) ? {id: current_user_person_contact.id} : false,
is_own_profile: own_profile? is_own_profile: own_profile?,
show_profile_info: public_details? || own_profile? || person_is_following_current_user
) )
end end
def full_hash_with_avatar
full_hash.merge(avatar: AvatarPresenter.new(profile).base_hash)
end
def full_hash_with_profile
attrs = full_hash
if own_profile? || person_is_following_current_user
attrs.merge!(profile: ProfilePresenter.new(profile).private_hash)
else
attrs.merge!(profile: ProfilePresenter.new(profile).public_hash)
end
attrs
end
def as_json(_options={}) def as_json(_options={})
attrs = full_hash_with_avatar full_hash_with_profile
if own_profile? || person_is_following_current_user
attrs.merge!(
location: @presentable.location,
birthday: @presentable.formatted_birthday,
bio: @presentable.bio
)
end
attrs
end end
protected protected
@ -69,6 +44,18 @@ class PersonPresenter < BasePresenter
@presentable.shares_with(current_user) @presentable.shares_with(current_user)
end end
def full_hash_with_profile
attrs = full_hash
if attrs[:show_profile_info]
attrs.merge!(profile: ProfilePresenter.new(profile).private_hash)
else
attrs.merge!(profile: ProfilePresenter.new(profile).public_hash)
end
attrs
end
private private
def current_user_person_block def current_user_person_block

View file

@ -3,7 +3,13 @@
-# the COPYRIGHT file. -# the COPYRIGHT file.
%hr %hr
%h3= t('profiles.edit.your_private_profile') %h3.inline
= t("profiles.edit.extended")
= t("profiles.edit.extended_visibility_text")
= check_box_tag "profile[public_details]", true, profile.public_details, {"data-size" => "mini", "data-on-text" => t("profiles.edit.public"), "data-off-text" => t("profiles.edit.limited")}
%span{ :title => t("profiles.edit.extended_hint") }
%i.entypo.circled-help.visibility-hint-icon
.small-horizontal-spacer
%h4= t('profiles.edit.your_bio') %h4= t('profiles.edit.your_bio')
@ -30,6 +36,12 @@
.small-horizontal-spacer .small-horizontal-spacer
%hr
%h3.inline
= t('profiles.edit.settings')
.small-horizontal-spacer
%h4= t('search') %h4= t('search')
.well.checkbox .well.checkbox

View file

@ -38,7 +38,11 @@
.stream .stream
%p{:class => "conversation_#{name}"}= msg %p{:class => "conversation_#{name}"}= msg
%h3= t('profiles.edit.your_public_profile') %h3.inline
= t("profiles.edit.basic")
%span{ :title => t("profiles.edit.basic_hint") }
%i.entypo.circled-help.visibility-hint-icon
.small-horizontal-spacer
= error_messages_for profile = error_messages_for profile

View file

@ -1008,8 +1008,14 @@ en:
profiles: profiles:
edit: edit:
your_public_profile: "Your public profile" basic: "My basic profile"
your_private_profile: "Your private profile" extended: "My extended profile"
settings: "Profile settings"
extended_visibility_text: "Visibility of your extended profile:"
public: "Public"
limited: "Limited"
basic_hint: "Every item in your profile is optional. Your basic profile will always be publicly visible."
extended_hint: "Click the switch to set your extended profile data visibility. Public means it is visible to the internet, limited means only people who you share with will see this information."
your_name: "Your name" your_name: "Your name"
first_name: "First name" first_name: "First name"
last_name: "Last name" last_name: "Last name"

View file

@ -0,0 +1,5 @@
class AddPublicToProfiles < ActiveRecord::Migration
def change
add_column :profiles, :public_details, :boolean, default: false
end
end

View file

@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150607143809) do ActiveRecord::Schema.define(version: 20150630221004) do
create_table "account_deletions", force: :cascade do |t| create_table "account_deletions", force: :cascade do |t|
t.string "diaspora_handle", limit: 255 t.string "diaspora_handle", limit: 255
@ -409,6 +409,7 @@ ActiveRecord::Schema.define(version: 20150607143809) do
t.string "location", limit: 255 t.string "location", limit: 255
t.string "full_name", limit: 70 t.string "full_name", limit: 70
t.boolean "nsfw", default: false t.boolean "nsfw", default: false
t.boolean "public_details", default: false
end end
add_index "profiles", ["full_name", "searchable"], name: "index_profiles_on_full_name_and_searchable", using: :btree add_index "profiles", ["full_name", "searchable"], name: "index_profiles_on_full_name_and_searchable", using: :btree

View file

@ -31,13 +31,16 @@ Feature: editing your profile
And the "profile_date_day" field should be filled with "30" And the "profile_date_day" field should be filled with "30"
And the "profile_location" field should be filled with "Kamino" And the "profile_location" field should be filled with "Kamino"
And I should see "#starwars" within "ul#as-selections-tags" And I should see "#starwars" within "ul#as-selections-tags"
And the "#profile_public_details" bootstrap-switch should be off
When I fill in "profile[tag_string]" with "#kamino" When I fill in "profile[tag_string]" with "#kamino"
And I press the first ".as-result-item" within ".as-results" And I press the first ".as-result-item" within ".as-results"
And I toggle the "#profile_public_details" bootstrap-switch
And I press "update_profile" And I press "update_profile"
Then I should see "#kamino" within "ul#as-selections-tags" Then I should see "#kamino" within "ul#as-selections-tags"
And I should see "#starwars" within "ul#as-selections-tags" And I should see "#starwars" within "ul#as-selections-tags"
And the "#profile_public_details" bootstrap-switch should be on
When I attach the file "spec/fixtures/bad_urls.txt" to "file" within "#file-upload" When I attach the file "spec/fixtures/bad_urls.txt" to "file" within "#file-upload"
And I confirm the alert And I confirm the alert

View file

@ -168,6 +168,15 @@ Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |
end end
end end
Then /^the "([^"]*)" bootstrap-switch should be (on|off)$/ do |label, state|
result = execute_script("return $('#{label}').bootstrapSwitch('state')")
result.should state == "on" ? be_truthy : be_falsey
end
Then /^I toggle the "([^"]*)" bootstrap-switch$/ do |label|
execute_script("return $('#{label}').bootstrapSwitch('toggleState')")
end
Then /^(?:|I )should be on (.+)$/ do |page_name| Then /^(?:|I )should be on (.+)$/ do |page_name|
confirm_on_page(page_name) confirm_on_page(page_name)
end end

View file

@ -5,6 +5,7 @@ describe("app.views.ProfileSidebar", function() {
diaspora_id: "alice@umbrella.corp", diaspora_id: "alice@umbrella.corp",
name: "Project Alice", name: "Project Alice",
relationship: 'mutual', relationship: 'mutual',
show_profile_info: true,
profile: { profile: {
bio: "confidential", bio: "confidential",
location: "underground", location: "underground",

View file

@ -356,18 +356,19 @@ describe Profile, :type => :model do
profile = FactoryGirl.build :profile profile = FactoryGirl.build :profile
expect(profile.send(:clearable_fields).sort).to eq( expect(profile.send(:clearable_fields).sort).to eq(
["diaspora_handle", ["diaspora_handle",
"first_name", "first_name",
"last_name", "last_name",
"image_url", "image_url",
"image_url_small", "image_url_small",
"image_url_medium", "image_url_medium",
"birthday", "birthday",
"gender", "gender",
"bio", "bio",
"searchable", "searchable",
"nsfw", "nsfw",
"location", "location",
"full_name"].sort "public_details",
"full_name"].sort
) )
end end
end end

View file

@ -6,26 +6,52 @@ describe PersonPresenter do
describe "#as_json" do describe "#as_json" do
context "with no current_user" do context "with no current_user" do
it "returns the user's public information if a user is not logged in" do it "returns the user's basic profile" do
expect(PersonPresenter.new(person, nil).as_json).to include(person.as_api_response(:backbone).except(:avatar)) expect(PersonPresenter.new(person, nil).as_json).to include(person.as_api_response(:backbone).except(:avatar))
end end
it "returns the user's additional profile if the user has set additional profile public" do
person.profile.public_details = true
expect(PersonPresenter.new(person, nil).as_json[:profile]).to include(*%i(location bio gender birthday))
end
it "doesn't return user's additional profile if the user hasn't set additional profile public" do
person.profile.public_details = false
expect(PersonPresenter.new(person, nil).as_json[:profile]).not_to include(*%i(location bio gender birthday))
end
end end
context "with a current_user" do context "with a current_user" do
let(:current_user) { FactoryGirl.create(:user)} let(:current_user) { FactoryGirl.create(:user)}
let(:presenter){ PersonPresenter.new(person, current_user) } let(:presenter){ PersonPresenter.new(person, current_user) }
# here private information == addtional user profile, because additional profile by default is private
it "doesn't share private information when the users aren't connected" do it "doesn't share private information when the users aren't connected" do
expect(presenter.full_hash_with_profile[:profile]).not_to have_key(:location) expect(person.profile.public_details).to be_falsey
expect(presenter.as_json[:show_profile_info]).to be_falsey
expect(presenter.as_json[:profile]).not_to have_key(:location)
end
it "shares private information when the users aren't connected, but profile is public" do
person.profile.public_details = true
expect(presenter.as_json[:show_profile_info]).to be_truthy
expect(presenter.as_json[:relationship]).to be(:not_sharing)
expect(presenter.as_json[:profile]).to have_key(:location)
end end
it "has private information when the person is sharing with the current user" do it "has private information when the person is sharing with the current user" do
expect(person).to receive(:shares_with).with(current_user).and_return(true) expect(person).to receive(:shares_with).with(current_user).and_return(true)
expect(presenter.full_hash_with_profile[:profile]).to have_key(:location) expect(person.profile.public_details).to be_falsey
pr_json = presenter.as_json
expect(pr_json[:show_profile_info]).to be_truthy
expect(pr_json[:profile]).to have_key(:location)
end end
it "returns the user's private information if a user is logged in as herself" do it "returns the user's private information if a user is logged in as herself" do
expect(PersonPresenter.new(current_user.person, current_user).as_json).to have_key(:location) current_person_presenter = PersonPresenter.new(current_user.person, current_user)
expect(current_user.person.profile.public_details).to be_falsey
expect(current_person_presenter.as_json[:show_profile_info]).to be_truthy
expect(current_person_presenter.as_json[:profile]).to have_key(:location)
end end
end end
end end