Commit 7a5a0a90 authored by Senya's avatar Senya

Allow extended profile fields (previously private profile) to be set public (#5684).

This adds a new boolean field "public_details" to person model.
By default it is false and represents old behaviour. When it is
set to true, extended profile (bio,location,gender,birthday)
get available to people who didn't log into diaspora and to
people you don't share with (i.e. it is made public).

In UI, a bootstrap-switch added on the profile-edit page in order to
change the setting.

This also changes wording from public/private profile to basic/extended.
The latter could be public and limited.
parent e0782437
......@@ -57,6 +57,7 @@ gem "bootstrap-sass", "3.3.5"
gem "compass-rails", "2.0.4"
gem "sass-rails", "5.0.1"
gem "autoprefixer-rails", "5.2.1"
gem "bootstrap-switch-rails", "3.3.3"
# Database
......
......@@ -73,6 +73,7 @@ GEM
bootstrap-sass (3.3.5)
autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19)
bootstrap-switch-rails (3.3.3)
buftok (0.2.0)
builder (3.2.2)
byebug (4.0.5)
......@@ -751,6 +752,7 @@ DEPENDENCIES
autoprefixer-rails (= 5.2.1)
backbone-on-rails (= 1.1.2.1)
bootstrap-sass (= 3.3.5)
bootstrap-switch-rails (= 3.3.3)
capybara (= 2.4.4)
carrierwave (= 0.10.0)
compass-rails (= 2.0.4)
......
......@@ -2,6 +2,7 @@
app.pages.Settings = Backbone.View.extend({
initialize: function() {
$(".settings_visibility").tooltip({placement: "top"});
$("[name='profile[public_details]']").bootstrapSwitch();
}
});
// @license-end
......@@ -9,6 +9,7 @@ app.Router = Backbone.Router.extend({
"conversations": "conversations",
"user/edit": "settings",
"users/sign_up": "registration",
"profile/edit": "settings",
//new hotness
"posts/:id": "singlePost",
......
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later
app.views.ProfileSidebar = app.views.Base.extend({
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'));
}
templateName: "profile_sidebar"
});
// @license-end
......@@ -46,3 +46,4 @@
//= require mentions
//= require bootstrap
//= require osmlocator
//= require bootstrap-switch
......@@ -92,3 +92,5 @@
/* statistics */
@import 'new_styles/statistics';
@import "bootstrap3-switch";
......@@ -148,3 +148,9 @@
&:last-of-type{ float: right; }
}
}
#update_profile_form {
.visibility-hint-icon {
cursor: pointer;
}
}
......@@ -75,7 +75,7 @@ class PeopleController < ApplicationController
def show
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|
format.all do
......@@ -144,7 +144,7 @@ class PeopleController < ApplicationController
if @person
@contact = current_user.contact_for(@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] = {
count: photos_from(@person, :all).count(:all)
}
......
......@@ -25,7 +25,7 @@ class PhotosController < ApplicationController
@posts = current_user.photos_from(@person, max_time: max_time).order('created_at desc')
respond_to do |format|
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] = {
count: current_user.photos_from(@person, limit: :all).count(:all)
}
......
......@@ -40,6 +40,7 @@ class ProfilesController < ApplicationController
#checkbox tags wtf
@profile_attrs[:searchable] ||= false
@profile_attrs[:nsfw] ||= false
@profile_attrs[:public_details] ||= false
if params[:photo_id]
@profile_attrs[:photo] = Photo.where(:author_id => current_user.person_id, :id => params[:photo_id]).first
......@@ -79,6 +80,8 @@ class ProfilesController < ApplicationController
end
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
......@@ -31,7 +31,7 @@ class Person < ActiveRecord::Base
has_one :profile, :dependent => :destroy
delegate :last_name, :image_url, :tag_string, :bio, :location,
:gender, :birthday, :formatted_birthday, :tags, :searchable,
to: :profile
:public_details?, to: :profile
accepts_nested_attributes_for :profile
before_validation :downcase_diaspora_handle
......
......@@ -12,6 +12,6 @@ class ContactPresenter < BasePresenter
end
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
......@@ -10,41 +10,16 @@ class PersonPresenter < BasePresenter
def full_hash
base_hash.merge(
relationship: relationship,
block: is_blocked? ? BlockPresenter.new(current_user_person_block).base_hash : false,
contact: (!own_profile? && has_contact?) ? {id: current_user_person_contact.id} : false,
is_own_profile: own_profile?
relationship: relationship,
block: is_blocked? ? BlockPresenter.new(current_user_person_block).base_hash : false,
contact: (!own_profile? && has_contact?) ? {id: current_user_person_contact.id} : false,
is_own_profile: own_profile?,
show_profile_info: public_details? || own_profile? || person_is_following_current_user
)
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={})
attrs = full_hash_with_avatar
if own_profile? || person_is_following_current_user
attrs.merge!(
location: @presentable.location,
birthday: @presentable.formatted_birthday,
bio: @presentable.bio
)
end
attrs
full_hash_with_profile
end
protected
......@@ -69,6 +44,18 @@ class PersonPresenter < BasePresenter
@presentable.shares_with(current_user)
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
def current_user_person_block
......
......@@ -3,7 +3,13 @@
-# the COPYRIGHT file.
%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')
......@@ -30,6 +36,12 @@
.small-horizontal-spacer
%hr
%h3.inline
= t('profiles.edit.settings')
.small-horizontal-spacer
%h4= t('search')
.well.checkbox
......
......@@ -38,7 +38,11 @@
.stream
%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
......
......@@ -1008,8 +1008,14 @@ en:
profiles:
edit:
your_public_profile: "Your public profile"
your_private_profile: "Your private profile"
basic: "My basic 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"
first_name: "First name"
last_name: "Last name"
......
class AddPublicToProfiles < ActiveRecord::Migration
def change
add_column :profiles, :public_details, :boolean, default: false
end
end
......@@ -11,7 +11,7 @@
#
# 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|
t.string "diaspora_handle", limit: 255
......@@ -409,6 +409,7 @@ ActiveRecord::Schema.define(version: 20150607143809) do
t.string "location", limit: 255
t.string "full_name", limit: 70
t.boolean "nsfw", default: false
t.boolean "public_details", default: false
end
add_index "profiles", ["full_name", "searchable"], name: "index_profiles_on_full_name_and_searchable", using: :btree
......
......@@ -31,13 +31,16 @@ Feature: editing your profile
And the "profile_date_day" field should be filled with "30"
And the "profile_location" field should be filled with "Kamino"
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"
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"
Then I should see "#kamino" 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"
And I confirm the alert
......
......@@ -168,6 +168,15 @@ Then /^the "([^"]*)" checkbox(?: within "([^"]*)")? should not be checked$/ do |
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|
confirm_on_page(page_name)
end
......
......@@ -5,6 +5,7 @@ describe("app.views.ProfileSidebar", function() {
diaspora_id: "alice@umbrella.corp",
name: "Project Alice",
relationship: 'mutual',
show_profile_info: true,
profile: {
bio: "confidential",
location: "underground",
......
......@@ -356,18 +356,19 @@ describe Profile, :type => :model do
profile = FactoryGirl.build :profile
expect(profile.send(:clearable_fields).sort).to eq(
["diaspora_handle",
"first_name",
"last_name",
"image_url",
"image_url_small",
"image_url_medium",
"birthday",
"gender",
"bio",
"searchable",
"nsfw",
"location",
"full_name"].sort
"first_name",
"last_name",
"image_url",
"image_url_small",
"image_url_medium",
"birthday",
"gender",
"bio",
"searchable",
"nsfw",
"location",
"public_details",
"full_name"].sort
)
end
end
......
......@@ -6,26 +6,52 @@ describe PersonPresenter do
describe "#as_json" 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))
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
context "with a current_user" do
let(:current_user) { FactoryGirl.create(: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
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
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(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
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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment