From 6eff997f81fcdb5b220831769cf34b835b35c685 Mon Sep 17 00:00:00 2001 From: Frank Dillon Date: Wed, 19 Nov 2008 05:18:58 +0000 Subject: [PATCH] rfe: User Profile Privacy Settings (#507) - users now have granular control of whether or not their profile fields are viewable - admin settings still apply --- docs/changelog/7.x.x.txt | 1 + .../packages-7.6.4/root_import_account.wgpkg | Bin 16165 -> 16297 bytes docs/upgrades/upgrade_7.6.3-7.6.4.pl | 41 ++++++- lib/WebGUI/Account/Profile.pm | 105 +++++++++++------ lib/WebGUI/ProfileField.pm | 34 +++++- lib/WebGUI/User.pm | 111 ++++++++++++++++++ lib/WebGUI/i18n/English/Account_Profile.pm | 8 ++ lib/WebGUI/i18n/English/WebGUI.pm | 12 +- 8 files changed, 266 insertions(+), 46 deletions(-) diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 7400f9cce..b54283dbf 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -10,6 +10,7 @@ - New Friends Account module added providing a better interface into WebGUI's friends system - rfe: Event hover detail exclusions (#8761) - rfe: Database Link test (#513) + - rfe: User Profile Privacy Settings (#507) - users now have granular control of whether or not their profile fields are viewable - admin settings still apply - The Display Account and My Purchases interfaces have been added to the new Account system - Added a new macro which returns the self deactivation link if the setting is enabled. - Added a new macro which returns the back to site link diff --git a/docs/upgrades/packages-7.6.4/root_import_account.wgpkg b/docs/upgrades/packages-7.6.4/root_import_account.wgpkg index afb5c559f4e357570b79fe715a8c08ed4a10f08c..a9ca7293c453d9478eac99170ee27d6cf474a8fd 100644 GIT binary patch literal 16297 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 DWKisi diff --git a/docs/upgrades/upgrade_7.6.3-7.6.4.pl b/docs/upgrades/upgrade_7.6.3-7.6.4.pl index ea73e82d2..88bdf20f0 100644 --- a/docs/upgrades/upgrade_7.6.3-7.6.4.pl +++ b/docs/upgrades/upgrade_7.6.3-7.6.4.pl @@ -24,6 +24,7 @@ use WebGUI::Asset::Wobject::Survey; use WebGUI::Asset::Wobject::Survey::SurveyJSON; use WebGUI::Asset::Wobject::Survey::ResponseJSON; use WebGUI::ProfileField; +use JSON; my $toVersion = '7.6.4'; my $quiet; # this line required @@ -37,9 +38,48 @@ addPosMode($session); fixFriendsGroups( $session ); upgradeAccount( $session ); removeProcessRecurringPaymentsFromConfig( $session ); +addExtendedProfilePrivileges( $session ); addStorageUrlMacro( $session ); finish($session); # this line required +#---------------------------------------------------------------------------- +sub addExtendedProfilePrivileges { + my $session = shift; + + print qq{\tExtending User Profile Privileges..} if !$quiet; + + my $userProfDesc = $session->db->buildHashRef('describe userProfileData'); + if(grep { $_ =~ /^wg_privacySettings/ } keys %{$userProfDesc}) { + $session->db->write("alter table userProfileData drop column wg_privacySettings"); + } + $session->db->write("alter table userProfileData add wg_privacySettings longtext"); + + my $fields = WebGUI::ProfileField->getFields($session); + + my $users = $session->db->buildArrayRef("select userId from users"); + foreach my $userId (@{$users}) { + my $hash = {}; + foreach my $field (@{$fields}) { + if($field->getId eq "publicEmail") { + my $u = WebGUI::User->new($session,$userId); + $hash->{$field->getId} = $u->profileField("publicEmail") ? "all" : "none"; + next; + } + $hash->{$field->getId} = $field->isViewable ? "all" : "none"; + } + my $json = JSON->new->encode($hash); + $session->db->write("update userProfileData set wg_privacySettings=? where userId=?",[$json,$userId]); + } + + #Delete the public email field + my $publicEmail = WebGUI::ProfileField->new($session,"publicEmail"); + if(defined $publicEmail) { + $publicEmail->delete; + } + + print qq{Finished\n} if !$quiet; +} + #---------------------------------------------------------------------------- sub addPosMode { @@ -447,7 +487,6 @@ sub upgradeAccount { $setting->add("shopStyleTemplateId",""); #Use the userStyle by default $setting->add("shopLayoutTemplateId","aUDsJ-vB9RgP-AYvPOy8FQ"); - #Add inbox changes $session->db->write(q{ create table inbox_messageState ( diff --git a/lib/WebGUI/Account/Profile.pm b/lib/WebGUI/Account/Profile.pm index a8fbb4c2b..f819e69bb 100644 --- a/lib/WebGUI/Account/Profile.pm +++ b/lib/WebGUI/Account/Profile.pm @@ -353,37 +353,50 @@ sub www_edit { #Initialize the category template loop which gets filled inside the loop $var->{'profile_category_loop'} = []; + #Cache the privacy settings + my $privacySettingsHash = WebGUI::ProfileField->getPrivacyOptions($session); #Get the editable categories my $categories = WebGUI::ProfileCategory->getCategories($session, { editable => 1 } ); foreach my $category (@{ $categories } ) { my @fields = (); - use Data::Dumper; foreach my $field (@{ $category->getFields( { editable => 1 } ) }) { - my $fieldId = $field->getId; - my $fieldLabel = $field->getLabel; - my $fieldForm = $field->formField({ extras=>$self->getExtrasStyle($field,\@errorFields,$user->profileField($fieldId)) }); - my $fieldSubtext = $field->isRequired ? "*" : undef; - my $fieldExtras = $field->getExtras; - my $fieldPrivacy = WebGUI::Form::radoList($session,{ + my $fieldId = $field->getId; + my $fieldLabel = $field->getLabel; + my $fieldForm = $field->formField({ extras=>$self->getExtrasStyle($field,\@errorFields,$user->profileField($fieldId)) }); + my $fieldRequired = $field->isRequired; + my $fieldExtras = $field->getExtras; + my $fieldViewable = $field->isViewable; + my $rawPrivacySetting = $user->getProfileFieldPrivacySetting($fieldId); + my $fieldPrivacySetting = $privacySettingsHash->{$rawPrivacySetting}; + + my $fieldPrivacy = WebGUI::Form::selectBox($session,{ name => "privacy_$fieldId", - options => $field->getPrivacyOptions($session), - value => $user->getProfileFieldPrivacySetting($fieldId) + options => $privacySettingsHash, + value => $rawPrivacySetting, + extras => (!$fieldViewable) ? " disabled" : "" }); #Create a seperate template var for each field - $var->{'profile_field_'.$fieldId.'_form' } = $fieldForm; - $var->{'profile_field_'.$fieldId.'_label' } = $fieldLabel; - $var->{'profile_field_'.$fieldId.'_subtext'} = $fieldSubtext; - $var->{'profile_field_'.$fieldId.'_extras' } = $fieldExtras; - $var->{'profile_field_'.$fieldId.'_privacy'} = $fieldPrivacy; + my $fieldBase = 'profile_field_'.$fieldId; + $var->{$fieldBase.'_form' } = $fieldForm; + $var->{$fieldBase.'_label' } = $fieldLabel; + $var->{$fieldBase.'_required' } = $fieldRequired; + $var->{$fieldBase.'_extras' } = $fieldExtras; + $var->{$fieldBase.'_privacy_form' } = $fieldPrivacy; + $var->{$fieldBase.'_field_viewable' } = $fieldViewable; + $var->{$fieldBase.'_privacy_setting' } = $fieldPrivacySetting; + $var->{$fieldBase.'_privacy_is_'.$rawPrivacySetting } = $rawPrivacySetting; push(@fields, { - 'profile_field_id' => $fieldId, - 'profile_field_form' => $fieldForm, - 'profile_field_label' => $fieldLabel, - 'profile_field_subtext' => $field->isRequired ? "*" : undef, - 'profile_field_extras' => $field->getExtras, - 'profile_field_privacy' => $fieldPrivacy, + 'profile_field_id' => $fieldId, + 'profile_field_form' => $fieldForm, + 'profile_field_label' => $fieldLabel, + 'profile_field_required' => $fieldRequired, + 'profile_field_extras' => $fieldExtras, + 'profile_field_viewable' => $fieldViewable, + 'profile_field_privacy_form' => $fieldPrivacy, + 'profile_field_privacy_setting' => $fieldPrivacySetting, + 'profile_field_privacy_is_'.$rawPrivacySetting => $rawPrivacySetting, }); } @@ -420,7 +433,15 @@ sub www_editSave { push (@{$retHash->{errors}},@{$retHash->{warnings}}); unless(scalar(@{$retHash->{errors}})) { - $session->user->updateProfileFields( $retHash->{profile} ); + my $profile = $retHash->{profile}; + my $privacy = {}; + foreach my $fieldName (keys %{$profile}) { + $session->user->profileField($fieldName,$profile->{$fieldName}); + my $privacySetting = $session->form->get("privacy_".$fieldName); + next unless $privacySetting; + $privacy->{$fieldName} = $privacySetting; + } + $session->user->setProfileFieldPrivacySetting($privacy); } #Store the category the error occurred in the object for reference @@ -457,6 +478,8 @@ sub www_view { #Overwrite these $var->{'user_full_name' } = $user->getWholeName; $var->{'user_member_since' } = $user->dateCreated; + $var->{'profile_user_id' } = $user->userId; + $var->{'can_edit_profile' } = $uid eq $session->user->userId; #Check user privileges unless ($user->profileIsViewable($session->user)) { @@ -469,28 +492,38 @@ sub www_view { ); } + #Cache the privacy settings + my $privacySettingsHash = WebGUI::ProfileField->getPrivacyOptions($session); $var->{'profile_category_loop' } = []; foreach my $category (@{WebGUI::ProfileCategory->getCategories($session,{ visible => 1})}) { my @fields = (); foreach my $field (@{$category->getFields({ visible => 1 })}) { - my $fieldId = $field->getId; - my $fieldLabel = $field->getLabel; - my $fieldValue = $field->formField(undef,2,$user); - my $fieldRaw = $user->profileField($fieldId);; + next unless ($user->canViewField($field->getId,$session->user)); + my $rawPrivacySetting = $user->getProfileFieldPrivacySetting($field->getId); + my $privacySetting = $privacySettingsHash->{$rawPrivacySetting}; + my $fieldId = $field->getId; + my $fieldLabel = $field->getLabel; + my $fieldValue = $field->formField(undef,2,$user); + my $fieldRaw = $user->profileField($fieldId);; #Create a seperate template var for each field - $var->{'profile_field_'.$fieldId.'_label' } = $fieldLabel; - $var->{'profile_field_'.$fieldId.'_value' } = $fieldValue; - $var->{'profile_field_'.$fieldId.'_raw' } = $fieldRaw; - + my $fieldBase = 'profile_field_'.$fieldId; + $var->{$fieldBase.'_label' } = $fieldLabel; + $var->{$fieldBase.'_value' } = $fieldValue; + $var->{$fieldBase.'_raw' } = $fieldRaw; + $var->{$fieldBase.'_privacySetting' } = $privacySetting; + $var->{$fieldBase.'_privacy_is_'.$rawPrivacySetting } = "true"; push(@fields, { - 'profile_field_id' => $fieldId, - 'profile_field_is_'.$fieldId => "true", - 'profile_field_label' => $fieldLabel, - 'profile_field_value' => $fieldValue, - 'profile_field_raw' => $fieldRaw + 'profile_field_id' => $fieldId, + 'profile_field_is_'.$fieldId => "true", + 'profile_field_label' => $fieldLabel, + 'profile_field_value' => $fieldValue, + 'profile_field_raw' => $fieldRaw, + 'profile_field_privacySetting' => $privacySetting, + 'profile_field_privacy_is_'.$rawPrivacySetting => "true", }); } - + #Don't bother displaying the category if there's nothing in it. + next unless (scalar(@fields)); #Append the category variables $self->appendCategoryVars($var,$category,\@fields); } @@ -503,8 +536,6 @@ sub www_view { my $privacySetting = $user->profileField("publicProfile") || "none"; $var->{'profile_privacy_'.$privacySetting } = "true"; - $var->{'profile_user_id' } = $user->userId; - $var->{'can_edit_profile' } = $uid eq $session->user->userId; $var->{'acceptsPrivateMessages'} = $user->acceptsPrivateMessages($session->user->userId); $var->{'acceptsFriendsRequests'} = $user->acceptsFriendsRequests($session->user); diff --git a/lib/WebGUI/ProfileField.pm b/lib/WebGUI/ProfileField.pm index bb56e8b36..ac1fa7900 100755 --- a/lib/WebGUI/ProfileField.pm +++ b/lib/WebGUI/ProfileField.pm @@ -66,7 +66,7 @@ Return true iff fieldName is reserved and therefore not usable as a profile fiel sub isReservedFieldName { my $class = shift; my $fieldName = shift; - return isIn($fieldName, ('func', 'op')); + return isIn($fieldName, ('func', 'op', 'wg_privacySettings')); } #------------------------------------------------------------------- @@ -337,7 +337,10 @@ Returns a WebGUI::ProfileCategory object for the category that this profile fiel sub getCategory { my $self = shift; - return WebGUI::ProfileCategory->new($self->session,$self->get("profileCategoryId")); + unless ($self->{_category}) { + $self->{_category} = WebGUI::ProfileCategory->new($self->session,$self->get("profileCategoryId")); + } + return $self->{_category}; } @@ -440,6 +443,27 @@ sub getFormControlClass { #------------------------------------------------------------------- +=head2 getPrivacyOptions ( session ) + +Class method which returns a hash reference containing the privacy options available. + +=cut + +sub getPrivacyOptions { + my $class = shift; + my $session = shift; + my $i18n = WebGUI::International->new($session); + tie my %hash, "Tie::IxHash"; + %hash = ( + all => $i18n->get('user profile field private message allow label'), + friends => $i18n->get('user profile field private message friends only label'), + none => $i18n->get('user profile field private message allow none label'), + ); + return \%hash; +} + +#------------------------------------------------------------------- + =head2 getRequiredFields ( session ) Returns an array reference of WebGUI::ProfileField objects that are marked "required". This is a class method. @@ -512,7 +536,7 @@ Returns a boolean indicating whether this field may be editable by a user. sub isEditable { my $self = shift; - return $self->get("editable") || $self->isRequired; + return $self->getCategory->isEditable && ($self->get("editable") || $self->isRequired); } @@ -575,8 +599,8 @@ Returns a boolean indicating whether this field may be viewed by a user. =cut sub isViewable { - my $self = shift; - return $self->get("visible"); + my $self = shift; + return $self->getCategory->isViewable && $self->get("visible"); } #------------------------------------------------------------------- diff --git a/lib/WebGUI/User.pm b/lib/WebGUI/User.pm index c87419ce6..5e026246d 100644 --- a/lib/WebGUI/User.pm +++ b/lib/WebGUI/User.pm @@ -21,6 +21,7 @@ use WebGUI::DatabaseLink; use WebGUI::Exception; use WebGUI::Utility; use WebGUI::Operation::Shared; +use JSON; =head1 NAME @@ -258,6 +259,42 @@ sub canUseAdminMode { #------------------------------------------------------------------- +=head2 canViewField ( field, user) + +Returns whether or not the user passed in can view the field value for the user. +This will only check the user level privileges. + +=head3 field + +Field to check privileges on + +=head3 user + +User to check field privileges for + +=cut + +sub canViewField { + my $self = shift; + my $session = $self->session; + my $field = shift; + my $user = shift; + + return 0 unless ($field && $user); + #Always true for yourself + return 1 if ($self->userId eq $user->userId); + + my $privacySetting = $self->getProfileFieldPrivacySetting($field); + return 0 unless (WebGUI::Utility::isIn($privacySetting,qw(all none friends))); + return 1 if ($privacySetting eq "all"); + return 0 if ($privacySetting eq "none"); + + #It's friends so return whether or not user is a friend + return WebGUI::Friends->new($session,$self)->isFriend($user->userId); +} + +#------------------------------------------------------------------- + =head2 dateCreated ( ) Returns the epoch for when this user was created. @@ -449,6 +486,42 @@ sub getGroupIdsRecursive { return [ keys %groupIds ]; } +#------------------------------------------------------------------- + +=head2 getProfileFieldPrivacySetting ( [field ]) + +Returns the privacy setting for the field passed in. If no field is passed in the entire hash is returned + +=head3 field + +Field to get privacy setting for. + +=cut + +sub getProfileFieldPrivacySetting { + my $self = shift; + my $session = $self->session; + my $field = shift; + + unless ($self->{_privacySettings}) { + #Look it up manually because we want to cache this separately. + my $privacySettings = $session->db->quickScalar( + q{select wg_privacySettings from userProfileData where userId=?}, + [$self->userId] + ); + $privacySettings = "{}" unless $privacySettings; + $self->{_privacySettings} = JSON->new->decode($privacySettings); + } + + return $self->{_privacySettings} unless ($field); + + #No privacy settings returned the privacy setting field + return "none" if($field eq "wg_privacySettings"); + + return $self->{_privacySettings}->{$field}; +} + + #------------------------------------------------------------------- =head2 getProfileUrl ( [page] ) @@ -806,6 +879,7 @@ sub profileField { my $fieldName = shift; my $value = shift; my $db = $self->session->db; + return "" if ($fieldName eq "wg_privacySettings"); # this is a special internal field, don't try to process it. if (!exists $self->{_profile}{$fieldName} && !$self->session->db->quickScalar("SELECT COUNT(*) FROM userProfileField WHERE fieldName = ?", [$fieldName])) { $self->session->errorHandler->warn("No such profile field: $fieldName"); return undef; @@ -902,6 +976,43 @@ sub session { } +#------------------------------------------------------------------- + +=head2 setProfileFieldPrivacySetting ( settings ) + +Sets the profile field privacy settings + +=head3 settings + +hash ref containing the field and it's corresponding privacy setting + +=cut + +sub setProfileFieldPrivacySetting { + my $self = shift; + my $session = $self->session; + my $settings = shift; + + return undef unless scalar(keys %{$settings}); + + #Get the current settings + my $currentSettings = $self->getProfileFieldPrivacySetting; + + foreach my $fieldId (keys %{$settings}) { + my $privacySetting = $settings->{$fieldId}; + next unless (WebGUI::Utility::isIn($privacySetting,qw(all none friends))); + $currentSettings->{$fieldId} = $settings->{$fieldId}; + } + + #Store the data in the database + my $json = JSON->new->encode($currentSettings); + $session->db->write("update userProfileData set wg_privacySettings=? where userId=?",[$json,$self->userId]); + + #Recache the current settings + $self->{_privacySettings} = $currentSettings; +} + + #------------------------------------------------------------------- =head2 status ( [ value ] ) diff --git a/lib/WebGUI/i18n/English/Account_Profile.pm b/lib/WebGUI/i18n/English/Account_Profile.pm index 83c2e926c..5f3bc7d42 100644 --- a/lib/WebGUI/i18n/English/Account_Profile.pm +++ b/lib/WebGUI/i18n/English/Account_Profile.pm @@ -160,7 +160,15 @@ our $I18N = { lastUpdated => 1225724810, }, + 'set by admin' => { + message => q{site administrator has set this field to not be visible }, + lastUpdated => 1225724810, + }, + 'required field' => { + message => q{required }, + lastUpdated => 1225724810, + }, }; 1; diff --git a/lib/WebGUI/i18n/English/WebGUI.pm b/lib/WebGUI/i18n/English/WebGUI.pm index c2c009969..bcdfcfc1a 100755 --- a/lib/WebGUI/i18n/English/WebGUI.pm +++ b/lib/WebGUI/i18n/English/WebGUI.pm @@ -3603,17 +3603,17 @@ LongTruncOk=1

}, 'user profile field private message allow label' => { - message => q|Allow All|, + message => q|Public|, lastUpdated => 1181019679, }, 'user profile field private message friends only label' => { - message => q|Allow From My Friends Only|, + message => q|Friends Only|, lastUpdated => 1181019679, }, 'user profile field private message allow none label' => { - message => q|Allow None|, + message => q|Private|, lastUpdated => 1181019679, }, @@ -4306,6 +4306,12 @@ Users may override this setting in their profile. context => q{i18n label for time duration in WebGUI::DateTime}, }, + 'profile privacy settings' => { + message => q{Privacy Settings}, + lastUpdated => 1226706547, + context => q{i18n label for time duration in WebGUI::DateTime}, + }, + }; 1;