diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index ae23208b1..449cc4ee2 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -6,6 +6,7 @@ - fix: Workflow activities don't pick up new default values - safely allow sorting by more fields in collaboration systems - fix: iCal link on calendar doesn't work + - add: Friends Network 7.4.10 - fix: Graphs aren't sized properly using GraphicsMagick diff --git a/docs/upgrades/templates-7.5.0/managefriends.tmpl b/docs/upgrades/templates-7.5.0/managefriends.tmpl new file mode 100755 index 000000000..6fc580a43 --- /dev/null +++ b/docs/upgrades/templates-7.5.0/managefriends.tmpl @@ -0,0 +1,61 @@ +#managefriends_________ +#url: managefriendstemplate +#title: Manage Friends (default) +#menuTitle: Manage Friends +#namespace:friends/manage +#create +

^International("my friends","Friends");

+ + + + + + + + + + + + + + +
^International("name","Friends");^International("status","Friends");
+ +

^International("send friend email instructions","Friends");

+ + + + + + + + + +
+ + +
+
    + +
  • +
    +
+
+~~~ + + diff --git a/docs/upgrades/upgrade_7.4.10-7.5.0.pl b/docs/upgrades/upgrade_7.4.10-7.5.0.pl index fdaf6a562..2a9ca925b 100644 --- a/docs/upgrades/upgrade_7.4.10-7.5.0.pl +++ b/docs/upgrades/upgrade_7.4.10-7.5.0.pl @@ -21,17 +21,83 @@ my $quiet; # this line required my $session = start(); # this line required # upgrade functions go here +addFriendsNetwork($session); finish($session); # this line required ##------------------------------------------------- #sub exampleFunction { -# my $session = shift; -# print "\tWe're doing some stuff here that you should know about.\n" unless ($quiet); -# # and here's our code +# my $session = shift; +# print "\tWe're doing some stuff here that you should know about..." unless $quiet; +# # and here's our code +# print "DONE!\n" unless $quiet; #} +#---------------------------------------------------------------------------- +sub addFriendsNetwork { + my $session = shift; + print "\tInstall the Friend's Network.\n" unless ($quiet); + print "\t\tInstall new Network User Profile Field for not wanting to be friendly.\n" unless ($quiet); + my $field = WebGUI::ProfileField->create( + $session, + 'ableToBeFriend', + { + 'label' => WebGUI::International->new($session)->get('user profile field friend availability', 'WebGUI'), + 'visible' => 0, + 'required' => 0, + 'protected' => 1, + 'editable' => 1, + 'fieldType' => 'yesNo', + 'dataDefault' => 1, + }, + ); + + print "\t\tUpdating Private Messaging Profile Field.\n" unless ($quiet); + my $pmField = WebGUI::ProfileField->new($session,"allowPrivateMessages"); + my %data = ( + label => 'WebGUI::International::get("allow private messages label","WebGUI")', + visible => 1, + possibleValues =>'{ all=>WebGUI::International::get("user profile field private message allow label","WebGUI"), friends=>WebGUI::International::get("user profile field private message friends only label","WebGUI"), none=>WebGUI::International::get("user profile field private message allow none label","WebGUI"),}', + dataDefault =>'["all"]', + fieldType =>'RadioList', + required => 0, + protected => 1, + editable => 1, + ); + $pmField->set(\%data); + $session->db->write("update userProfileData set allowPrivateMessages='all' where allowPrivateMessages='1'"); + $session->db->write("update userProfileData set allowPrivateMessages='none' where allowPrivateMessages='0'"); + + + print "\t\tInstall the table to keep track of friend network invitations.\n" unless ($quiet); + my $db = $session->db; + $session->db->write(<new($session, "pbworkflow000000000001"); + my $activity = $workflow->addActivity("WebGUI::Workflow::Activity::DenyUnansweredFriends", "unansweredfriends_____"); + $activity->set("timeout", 60 * 60 * 24 * 30); + $activity->set("title", "Deny Friend Requests Older Than A Month"); + + print "\t\tAdding friends related settings.\n" unless ($quiet); + $session->setting->add("manageFriendsTemplateId", "managefriends_________"); + + print "\t\tAdd a new column to the users table to keep track of the groupId for friends." unless ($quiet); + $db->write("alter table users add column friendsGroup varchar(22) binary not null default ''"); + print "OK\n" unless $quiet; +} # ---- DO NOT EDIT BELOW THIS LINE ---- diff --git a/lib/WebGUI/Friends.pm b/lib/WebGUI/Friends.pm new file mode 100644 index 000000000..514f2963e --- /dev/null +++ b/lib/WebGUI/Friends.pm @@ -0,0 +1,318 @@ +package WebGUI::Friends; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2007 Plain Black Corporation. + ------------------------------------------------------------------- + Please read the legal notices (docs/legal.txt) and the license + (docs/license.txt) that came with this distribution before using + this software. + ------------------------------------------------------------------- + http://www.plainblack.com info@plainblack.com + ------------------------------------------------------------------- + +=cut + +use strict; +use Class::InsideOut qw(id register public readonly); +use WebGUI::DateTime; +use WebGUI::HTML; +use WebGUI::Inbox; +use WebGUI::International; +use WebGUI::User; +use WebGUI::Utility; + +readonly session => my %session; +readonly user => my %user; + +=head1 NAME + +WebGUI::Friends + +=head1 SYNOPSIS + +my $friends = WebGUI::Friends->new($session, $user); + +$friends->add(\@userIds); +$friends->remove(\@userIds); + +=head1 DESCRIPTION + +A user relationship management system. + +=head1 METHODS + +=cut + + +#------------------------------------------------------------------- + +=head2 add ( \@userIds ) + +Add friends. Also adds the reciprocal relationship. + +=head3 userIds + +An array reference of userIds to add as friends. + +=cut + +sub add { + my $self = shift; + my $userIds = shift; + my $me = $self->user; + $me->friends->addUsers($userIds); + foreach my $userId (@{$userIds}) { + my $friend = WebGUI::User->new($self->session, $userId); + $friend->friends->addUsers([$me->userId]); + } +} + +#------------------------------------------------------------------- + +=head2 approveAddRequest ( inviteId ) + +Sends an approval, sets up the relationship, and deletes the invitation. + +=head3 inviteId + +The unique idenitifer for this invitation. + +=cut + +sub approveAddRequest { + my $self = shift; + my $inviteId = shift; + my $db = $self->session->db; + my $invite = $self->getAddRequest($inviteId); + $self->add([$invite->{inviterId}]); + my $i18n = WebGUI::International->new($self->session, "Friends"); + my $inbox = WebGUI::Inbox->new($self->session); + $inbox->addMessage({ + message => sprintf($i18n->get("invitation accepted by user"), $self->user->getWholeName), + subject => $i18n->get('friends invitation accepted'), + userId => $invite->{inviterId}, + status => 'unread', + sentBy => $self->user->userId, + }); + $inbox->getMessage($invite->{messageId})->setStatus('completed'); + $db->deleteRow("friendInvitations", "inviteId", $inviteId); +} + + +#------------------------------------------------------------------- + +=head2 delete ( \@userIds ) + +Remove friends. + +=head3 userIds + +An array reference of userIds to remove from friends list. + +=cut + +sub delete { + my $self = shift; + my $userIds = shift; + $self->user->friends->deleteUsers($userIds); +} + +#------------------------------------------------------------------- + +=head2 getAddRequest ( inviteId ) + +Returns the invitation data as a hash reference. + +=cut + +sub getAddRequest { + my $self = shift; + my $inviteId = shift; + my $invite = $self->session->db->getRow('friendInvitations', 'inviteId', $inviteId); +} + + +#------------------------------------------------------------------- + +=head getAllPendingAddRequests ( session ) + +Class method. Returns a WebGUI::SQL::ResultSet object with all the unanswered add requests. + +=cut + +sub getAllPendingAddRequests { + my $class = shift; + my $session = shift; + return $session->db->read("select * from friendInvitations order by dateSent"); +} + + +#------------------------------------------------------------------- + +=head2 isFriend ( userId ) + +Returns a booelean indicating whether the userId is already a friend of this user. + +=head3 userId + +The userId to check against this user. + +=cut + +sub isFriend { + my $self = shift; + my $userId = shift; + return isIn($userId, @{$self->user->friends->getUsers}); +} + +#------------------------------------------------------------------- + +=head2 new ( session, user ) + +Constructor. + +=head3 session + +A reference to the current WebGUI::Session object. + +=head3 user + +A reference to a WebGUI::User object that we're going to manage the friends of. Defaults to the current user +attached to the session. + +=cut + +sub new { + my $class = shift; + my $session = shift; + my $user = shift || $session->user; + my $self = register($class); + $session{id $self} = $session; + $user{id $self} = $user; + return $self; +} + +#------------------------------------------------------------------- + +=head2 rejectAddRequest ( inviteId ) + +Sends a rejection notice, and deletes the invitation. + +=head3 inviteId + +The id of an invitation. + +=cut + +sub rejectAddRequest { + my $self = shift; + my $inviteId = shift; + my $db = $self->session->db; + my $invite = $self->getAddRequest($inviteId); + my $i18n = WebGUI::International->new($self->session, "Friends"); + my $inbox = WebGUI::Inbox->new($self->session); + $inbox->addMessage({ + message => sprintf($i18n->get("friends invitation not accepted by user"), $self->user->getWholeName), + subject => $i18n->get('friends invitation not accepted'), + userId => $invite->{inviterId}, + status => 'unread', + }); + $inbox->getMessage($invite->{messageId})->setStatus('completed'); + $self->session->db->deleteRow("friendInvitations", "inviteId", $inviteId); +} + +#------------------------------------------------------------------- + +=head2 sendAddRequest ( userId, message ) + +Sends a request to another user to be added to this user's friends list. Returns an invitationId. + +=head3 userId + +The user to invite to be a friend. + +=head3 message + +The message to lure them to accept. + +=cut + +sub sendAddRequest { + my $self = shift; + my $userId = shift; + my $comments = shift; + my $i18n = WebGUI::International->new($self->session, "Friends"); + + # No sneaky attack paths... + $comments = WebGUI::HTML::filter($comments); + + # Create the invitation url. + my $inviteId = $self->session->id->generate(); + my $inviteUrl = $self->session->url->append($self->session->url->getSiteURL, 'op=friendRequest;inviteId='.$inviteId); + # Build the message + my $messageText = sprintf $i18n->get("invitation approval email"), $self->user->getWholeName, $self->session->url->getSiteURL, $comments, $inviteUrl; + + # send message + my $message = WebGUI::Inbox->new($self->session)->addMessage({ + message => $messageText, + subject => $i18n->get("friends network invitation"), + userId => $userId, + status => 'pending', + sentBy => $self->user->userId, + }); + + # Create the invitation record. + $self->session->db->setRow( + 'friendInvitations', + 'inviteId', + { + inviterId => $self->user->userId, + friendId => $userId, + dateSent => WebGUI::DateTime->new($self->session, time)->toMysql, + comments => $comments, + messageId => $message->getId, + }, + $inviteId, + ); + return $inviteId; +} + +#------------------------------------------------------------------- + +=head2 sendMessage ( subject, message, [ userIds ] ) + +=head3 subject + +The subject of the message. + +=head3 message + +The message itself. + +=head3 userIds + +An array reference of userIds to send the message to. Defaults to all friends. + +=cut + +sub sendMessage { + my $self = shift; + my $subject = shift || "Untitled"; + my $message = shift; + my $userIds = shift || $self->user->friends->getUsers; + my $inbox = WebGUI::Inbox->new($self->session); + my $myId = $self->user->userId; + foreach my $userId (@{$userIds}) { + $inbox->addPrivateMessage({ + message => $message, + subject => $subject, + userId => $userId, + sentBy => $myId, + status => 'unread', + }); + } +} + +1; diff --git a/lib/WebGUI/Inbox.pm b/lib/WebGUI/Inbox.pm index 5beb27906..d7c70bf3c 100644 --- a/lib/WebGUI/Inbox.pm +++ b/lib/WebGUI/Inbox.pm @@ -55,6 +55,33 @@ sub addMessage { #------------------------------------------------------------------- +=head2 addPrivateMessage ( properties[, userToSend] ) + +Adds a new private message to the inbox if the user accepts private messages. + +=head3 properties + +See WebGUI::Inbox::Message::addMessage() for details. + +=cut + +sub addPrivateMessage { + my $self = shift; + my $messageData = shift; + my $isReply = shift; + + my $userId = $messageData->{userId}; + my $sentBy = $messageData->{sentBy} || $self->session->user->userId; + return undef unless $userId; + + my $u = WebGUI::User->new($self->session,$userId); + return undef unless ($isReply || $u->acceptsPrivateMessages($sentBy)); + + return $self->addMessage($messageData); +} + +#------------------------------------------------------------------- + =head2 DESTROY ( ) Deconstructor. diff --git a/lib/WebGUI/Inbox/Message.pm b/lib/WebGUI/Inbox/Message.pm index dd882c5c2..5d9e976fe 100644 --- a/lib/WebGUI/Inbox/Message.pm +++ b/lib/WebGUI/Inbox/Message.pm @@ -72,6 +72,10 @@ A userId of a user attached to this message. A groupId of a group attached to this message. +=head4 sentBy + +A userId that created this message. Defaults to '3' (Admin). + =cut sub create { diff --git a/lib/WebGUI/Operation.pm b/lib/WebGUI/Operation.pm index c721d4411..037a0a690 100644 --- a/lib/WebGUI/Operation.pm +++ b/lib/WebGUI/Operation.pm @@ -171,6 +171,14 @@ sub getOperations { 'inviteUserSave' => 'WebGUI::Operation::Invite', 'acceptInvite' => 'WebGUI::Operation::Invite', + 'addFriend' => 'WebGUI::Operation::Friends', + 'addFriendSave' => 'WebGUI::Operation::Friends', + 'friendRequest' => 'WebGUI::Operation::Friends', + 'friendRequestSave' => 'WebGUI::Operation::Friends', + 'manageFriends' => 'WebGUI::Operation::Friends', + 'removeFriends' => 'WebGUI::Operation::Friends', + 'sendMessageToFriends' => 'WebGUI::Operation::Friends', + 'copyLDAPLink' => 'WebGUI::Operation::LDAPLink', 'deleteLDAPLink' => 'WebGUI::Operation::LDAPLink', 'editLDAPLink' => 'WebGUI::Operation::LDAPLink', diff --git a/lib/WebGUI/Operation/Friends.pm b/lib/WebGUI/Operation/Friends.pm new file mode 100644 index 000000000..fd85096a0 --- /dev/null +++ b/lib/WebGUI/Operation/Friends.pm @@ -0,0 +1,376 @@ +package WebGUI::Operation::Friends; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2007 Plain Black Corporation. +#------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +#------------------------------------------------------------------- +# http://www.plainblack.com info@plainblack.com +#------------------------------------------------------------------- + +use strict; +use WebGUI::Form; +use WebGUI::Friends; +use WebGUI::User; +use WebGUI::International; +use WebGUI::Operation::Shared; + +=head1 NAME + +Package WebGUI::Operation::Friends + +=head1 DESCRIPTION + +Operation handler for handling the friends network. + +=cut + +#------------------------------------------------------------------- + +=head2 www_addFriend ( ) + +Form for inviting a user to become your friend. + +=cut + +sub www_addFriend { + my $session = shift; + return $session->privilege->insufficient() unless ($session->user->isInGroup(2)); + my $friendId = $session->form->get('userId'); + my $protoFriend = WebGUI::User->new($session, $friendId); + + my $i18n = WebGUI::International->new($session, 'Friends'); + + # Check for non-existant user id. + if ((!$protoFriend->username) || (!$protoFriend->profileField('ableToBeFriend'))) { + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('add to friends'), + $i18n->get('does not want to be a friend'), + $session->url->getBackToSiteURL(), + $i18n->get('493', 'WebGUI'); + return $session->style->userStyle($output); + } + + my $output = join '', + sprintf("

%s

\n", $i18n->get('add to friends')), + '

', + sprintf($i18n->get('add to friends description'), + $protoFriend->getWholeName), + '

', + WebGUI::Form::formHeader($session), + WebGUI::Form::hidden($session, + { + name => 'op', + value => 'addFriendSave', + } + ), + WebGUI::Form::hidden($session, + { + name => 'friendId', + value => $friendId, + } + ), + WebGUI::Form::textarea($session, + { + name => 'comments', + value => sprintf($i18n->get('default friend comments'), $protoFriend->getFirstName, $session->user->getFirstName), + } + ), + WebGUI::Form::Submit($session, + { + value => $i18n->get('add') + } + ), + WebGUI::Form::Button($session, + { + value => $i18n->get('cancel', 'WebGUI'), + extras => q|onclick="history.go(-1);" class="backwardButton"|, + } + ), + WebGUI::Form::formFooter($session), + ; + return $session->style->userStyle($output); +} + +#------------------------------------------------------------------- + +=head2 www_addFriendSave ( ) + +Post process the form, check for required fields, handle inviting users who are already +members (determined by email address) and send the email. + +=cut + +sub www_addFriendSave { + my $session = shift; + return $session->privilege->insufficient() unless ($session->user->isInGroup(2)); + + my $friendId = $session->form->get('friendId'); + my $protoFriend = WebGUI::User->new($session, $friendId); + my $i18n = WebGUI::International->new($session, 'Friends'); + + # Check for non-existant user id. + if ((!$protoFriend->username) || (!$protoFriend->profileField('ableToBeFriend'))) { + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('add to friends'), + $i18n->get('does not want to be a friend'), + $session->url->getBackToSiteURL(), + $i18n->get('493', 'WebGUI'); + return $session->style->userStyle($output); + } + + my $friends = WebGUI::Friends->new($session); + $friends->sendAddRequest($friendId, $session->form->get('comments')); + + # display result + my $output = sprintf( + q!

%s

%s

%s

%s

!, + $i18n->get('add to friends'), + sprintf($i18n->get('add to friends confirmation'), $protoFriend->getWholeName), + $session->url->append($session->url->getRequestedUrl, 'op=viewProfile;uid='.$friendId), + sprintf($i18n->get('add to friends profile'), $protoFriend->getFirstName), + $session->url->getBackToSiteURL(), + $i18n->get('493', 'WebGUI'), + ); + return $session->style->userStyle($output); +} + +#------------------------------------------------------------------- + +=head2 www_friendRequest ( ) + +Form for the friend to accept or deny the request. + +=cut + +sub www_friendRequest { + my $session = shift; + return $session->privilege->insufficient() unless ($session->user->isInGroup(2)); + + my $i18n = WebGUI::International->new($session, 'Friends'); + + my $inviteId = $session->form->get('inviteId'); + my $friends = WebGUI::Friends->new($session); + + my $invitation = $friends->getAddRequest($inviteId); + + ##Invalid invite ID + unless (exists $invitation->{friendId}) { ##No userId corresponds to the inviteId + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('invalid invite code'), + $i18n->get('invalid invite code message'), + $session->url->page("op=viewInbox"), + $i18n->get('354', 'WebGUI'); + return $session->style->userStyle($output); + } + + ##Already a friend (check friendId already in the group) + if ($friends->isFriend($invitation->{inviterId})) { + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('invalid invite code'), + $i18n->get('already a friend'), + $session->url->page("op=viewInbox"), + $i18n->get('354', 'WebGUI'); + return $session->style->userStyle($output); + } + + ##Someone else's invite (check friendId vs current userId). + if ($session->user->userId ne $invitation->{friendId}) { ##This isn't your invitation, dude. + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('invalid invite code'), + $i18n->get('not the right user'), + $session->url->page("op=viewInbox"), + $i18n->get('354', 'WebGUI'); + return $session->style->userStyle($output); + } + + ##Everything looks good. Make the form! + my $inviter = WebGUI::User->new($session, $invitation->{inviterId}); + my $output = join '', + sprintf("

%s

\n", $i18n->get('friend request')), + '

', + sprintf($i18n->get('friend request description'), + $inviter->getWholeName), + '

', + WebGUI::Form::formHeader($session), + WebGUI::Form::hidden($session, + { + name => 'op', + value => 'friendRequestSave', + } + ), + WebGUI::Form::hidden($session, + { + name => 'inviteId', + value => $inviteId, + } + ), + WebGUI::Form::textarea($session, + { + name => 'comments', + value => $invitation->{comments}, + extras => 'disabled=disabled', + } + ), + WebGUI::Form::Submit($session, ##Approve + { + name => 'doWhat', + value => $i18n->get('572', 'WebGUI'), + } + ), + WebGUI::Form::Submit($session, ##Deny + { + name => 'doWhat', + value => $i18n->get('574', 'WebGUI'), + } + ), + WebGUI::Form::formFooter($session), + ; + return $session->style->userStyle($output); +} + +#------------------------------------------------------------------- + +=head2 www_friendRequestSave ( ) + +Handle form data from the friend's response to the invitation + +=cut + +sub www_friendRequestSave { + my $session = shift; + return $session->privilege->insufficient() unless ($session->user->isInGroup(2)); + + my $i18n = WebGUI::International->new($session, 'Friends'); + my $doWhat = $session->form->get('doWhat'); + my $inviteId = $session->form->get('inviteId'); + my $friends = WebGUI::Friends->new($session); + my $invite = $friends->getAddRequest($inviteId); + my $inviter = WebGUI::User->new($session, $invite->{inviterId}); + ##Invalid invite ID + if (!$invite->{inviterId}) { ##No userId corresponds to the inviteId + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('invalid invite code'), + $i18n->get('invalid invite code message'), + $session->url->page("op=viewInbox"), + $i18n->get('354', 'WebGUI'); + return $session->style->userStyle($output); + } + + ##If deny, change the status of the request to denied. + if ($doWhat ne $i18n->get('572', 'WebGUI')) { ##request denied + $friends->rejectAddRequest($inviteId); + ##Return screen that says they denied the request. + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('friend request'), + sprintf($i18n->get('you have not been added'), $inviter->getWholeName), + $session->url->page("op=viewInbox"), + $i18n->get('354', 'WebGUI'); + return $session->style->userStyle($output); + } + + ##If accepted, + # set the status to accepted. + $friends->approveAddRequest($inviteId); + + # Return screen that says they accepted the request. + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('friend request'), + sprintf($i18n->get('you have been added'), $inviter->getWholeName), + $session->url->page("op=viewInbox"), + $i18n->get('354', 'WebGUI'); + return $session->style->userStyle($output); +} + +#------------------------------------------------------------------- + +=head2 www_manageFriends ( ) + +Display the list of friends and allow the user to remove friends or +send private messages to a subset of them. + +=cut + +sub www_manageFriends { + my $session = shift; + my ($user, $url, $style) = $session->quick(qw(user url style)); + return $session->privilege->insufficient() unless ($user->isInGroup(2)); + my $i18n = WebGUI::International->new($session, 'Friends'); + + ##You have no friends! + my $friends = $user->friends->getUsers; + unless (scalar(@{$friends})) { + my $output = sprintf qq!

%s

\n

%s

%s!, + $i18n->get('my friends'), + $i18n->get('no friends'), + $url->getBackToSiteURL(), + $i18n->get('493', 'WebGUI'); + return $style->userStyle($output); + } + + # show the friend manager + my %var = ( + "account.options" => WebGUI::Operation::Shared::accountOptions($session), + formHeader => WebGUI::Form::formHeader($session) + . WebGUI::Form::hidden($session, { name => 'op', value => 'sendMessageToFriends', }), + removeFriendButton => WebGUI::Form::button($session, { value => $i18n->get('remove'), extras => q|onclick="confirmRemovalOfFriends(form);"|, }), + subjectForm => WebGUI::Form::text($session, { name=>"subject" }), + sendMessageButton => WebGUI::Form::Submit($session, { value => $i18n->get('send message'), }), + messageForm => WebGUI::Form::textarea($session, { name=>"message" }), + formFooter => WebGUI::Form::formFooter($session), + ); + foreach my $userId (@{ $friends}) { + my $friend = WebGUI::User->new($session, $userId); + push(@{$var{friends}}, { + name => $friend->getWholeName, + profileUrl => $url->append($url->getRequestedUrl, 'op=viewProfile;uid='.$userId), + status => ($friend->isOnline ? $i18n->get('online') : $i18n->get('offline')), + checkboxForm => WebGUI::Form::checkbox($session, { name => 'userId', value => $userId, }), + }); + } + my $template = WebGUI::Asset->new( + $session, + $session->setting->get("manageFriendsTemplateId"), + "WebGUI::Asset::Template", + ); + return $style->userStyle($template->process(\%var)); +} + + +#------------------------------------------------------------------- + +=head2 www_removeFriends () + +Removes friends from the current user's friends list. + +=cut + +sub www_removeFriends { + my $session = shift; + return $session->privilege->insufficient() unless ($session->user->isInGroup(2)); + my @users = $session->form->param("userId"); + WebGUI::Friends->new($session)->delete(\@users); + return www_manageFriends($session); +} + + +#------------------------------------------------------------------- + +=head2 www_sendMessageToFriends () + +Sends a message to selected friends. + +=cut + +sub www_sendMessageToFriends { + my $session = shift; + return $session->privilege->insufficient() unless ($session->user->isInGroup(2)); + my @users = $session->form->param("userId"); + my $friends = WebGUI::Friends->new($session); + $friends->sendMessage($session->form->process("subject", "text"), $session->form->process("message","textarea"), \@users); + return www_manageFriends($session); +} + +1; diff --git a/lib/WebGUI/Operation/Inbox.pm b/lib/WebGUI/Operation/Inbox.pm index b2806e5ec..fd6864cf6 100644 --- a/lib/WebGUI/Operation/Inbox.pm +++ b/lib/WebGUI/Operation/Inbox.pm @@ -153,7 +153,7 @@ sub www_sendPrivateMessage { return $style->userStyle(WebGUI::Asset::Template->new($session,$templateId)->process($vars)); } - unless($userTo->profileField("allowPrivateMessages")) { + unless($userTo->acceptsPrivateMessages($user->userId)) { $vars->{'error_msg'} = $i18n->get('private message blocked error'); return $style->userStyle(WebGUI::Asset::Template->new($session,$templateId)->process($vars)); } @@ -213,7 +213,15 @@ sub www_sendPrivateMessageSave { } } - unless($isReply || $userTo->profileField("allowPrivateMessages")) { + my $message = WebGUI::Inbox->new($session)->addPrivateMessage({ + message => $form->get("message"), + subject => $form->get("subject"), + userId => $uid, + status => 'unread', + sentBy => $user->userId + },$isReply); + + unless(defined $message) { my $output = sprintf qq|

%s

\n

%s

%s|, $i18n->get('private message error'), $i18n->get('private message blocked error'), @@ -222,15 +230,6 @@ sub www_sendPrivateMessageSave { return $style->userStyle($output); } - - WebGUI::Inbox->new($session)->addMessage({ - message => $form->get("message"), - subject => $form->get("subject"), - userId => $uid, - status => 'unread', - sentBy => $user->userId - }); - my $output = sprintf qq!

%s

%s!, $i18n->get('private message sent'), diff --git a/lib/WebGUI/Operation/Profile.pm b/lib/WebGUI/Operation/Profile.pm index dbd40f109..4c074a5c2 100644 --- a/lib/WebGUI/Operation/Profile.pm +++ b/lib/WebGUI/Operation/Profile.pm @@ -23,6 +23,7 @@ use WebGUI::Utility; use WebGUI::ProfileField; use WebGUI::ProfileCategory; use WebGUI::Operation::Shared; +use WebGUI::Operation::Friends; =head1 NAME @@ -280,39 +281,45 @@ A reference to the current session. =cut sub www_viewProfile { - my $session = shift; - my $u = WebGUI::User->new($session,$session->form->process("uid")); - my $i18n = WebGUI::International->new($session); - my $vars = {}; - $vars->{displayTitle} = $i18n->get(347).' '.$u->username; + my $session = shift; + my $u = WebGUI::User->new($session,$session->form->process("uid")); + my $i18n = WebGUI::International->new($session); + my $vars = {}; + $vars->{displayTitle} = $i18n->get(347).' '.$u->username; - return $session->privilege->notMember() if($u->username eq ""); + return $session->privilege->notMember() if($u->username eq ""); - return $session->style->userStyle($vars->{displayTitle}.'. '.$i18n->get(862)) if($u->profileField("publicProfile") < 1 && ($session->user->userId ne $session->form->process("uid") || $session->user->isInGroup(3))); - return $session->privilege->insufficient() if(!$session->user->isInGroup(2)); + return $session->style->userStyle($vars->{displayTitle}.'. '.$i18n->get(862)) if($u->profileField("publicProfile") < 1 && ($session->user->userId ne $session->form->process("uid") || $session->user->isInGroup(3))); + return $session->privilege->insufficient() if(!$session->user->isInGroup(2)); - my @array = (); - foreach my $category (@{WebGUI::ProfileCategory->getCategories($session)}) { - next unless ($category->get("visible")); - push(@array, {'profile.category' => $category->getLabel}); - foreach my $field (@{$category->getFields}) { - next unless ($field->get("visible")); - next if ($field->get("fieldName") eq "email" && !$u->profileField("publicEmail")); - push(@array, { - 'profile.label' => $field->getLabel, - 'profile.value' => $field->formField(undef,2,$u) - }); - } - } - $vars->{'profile.elements'} = \@array; - if ($session->user->userId eq $session->form->process("uid")) { - $vars->{'profile.accountOptions'} = WebGUI::Operation::Shared::accountOptions($session); - } + my @array = (); + foreach my $category (@{WebGUI::ProfileCategory->getCategories($session)}) { + next unless ($category->get("visible")); + push(@array, {'profile.category' => $category->getLabel}); + foreach my $field (@{$category->getFields}) { + next unless ($field->get("visible")); + next if ($field->get("fieldName") eq "email" && !$u->profileField("publicEmail")); + push @array, { + 'profile.label' => $field->getLabel, + 'profile.value' => $field->formField(undef,2,$u), + }; + } + } + $vars->{'profile.elements'} = \@array; + + if ($session->user->userId eq $session->form->process("uid")) { + $vars->{'profile.accountOptions'} = WebGUI::Operation::Shared::accountOptions($session); + } else { - push(@{$vars->{'profile.accountOptions'}}, {'options.display' => ''.$i18n->get('send private message').''}); + ## TODO: Make this more legible code, maybe refactor into a method + push @{$vars->{'profile.accountOptions'}}, { + 'options.display' => 'userId).'">'.$i18n->get('add to friends list', 'Friends').'', + }, { + 'options.display' => ''.$i18n->get('send private message').'', + }; } - return $session->style->userStyle(WebGUI::Asset::Template->new($session,"PBtmpl0000000000000052")->process($vars)); + return $session->style->userStyle(WebGUI::Asset::Template->new($session,"PBtmpl0000000000000052")->process($vars)); } diff --git a/lib/WebGUI/Operation/Settings.pm b/lib/WebGUI/Operation/Settings.pm index 54b5fda90..3eafcd2e2 100644 --- a/lib/WebGUI/Operation/Settings.pm +++ b/lib/WebGUI/Operation/Settings.pm @@ -441,6 +441,15 @@ sub definition { namespace=>"userInvite/Email", defaultValue=>$setting->get("userInvitationsEmailTemplateId"), }); + push(@fields, { + tab => "user", + fieldType => "template", + defaultValue => "managefriends_________", + namespace => "friends/manage", + name => "manageFriendsTemplateId", + label => $i18n->get("manage friends template", "Friends"), + hoverHelp => $i18n->get("manage friends template help", "Friends"), + }); # auth settings my $options; foreach (@{$session->config->get("authMethods")}) { diff --git a/lib/WebGUI/Operation/Shared.pm b/lib/WebGUI/Operation/Shared.pm index 95fd6f144..36119e197 100644 --- a/lib/WebGUI/Operation/Shared.pm +++ b/lib/WebGUI/Operation/Shared.pm @@ -73,6 +73,11 @@ TODO: DOCUMENT ME push @array, { 'options.display' => sprintf('%s', $session->url->page('op=inviteUser'), $i18n->get('invite a friend')), }; + } + unless ($op eq "manageFriends") { + push @array, { + 'options.display' => sprintf('%s', $session->url->page('op=manageFriends'), $i18n->get('see my friends', 'Friends')), + }; } my %logout; $logout{'options.display'} = ''.$i18n->get(64).''; diff --git a/lib/WebGUI/User.pm b/lib/WebGUI/User.pm index ec3b0e2be..a9accaac6 100644 --- a/lib/WebGUI/User.pm +++ b/lib/WebGUI/User.pm @@ -97,6 +97,37 @@ sub addToGroups { $self->session->stow->delete("gotGroupsForUser"); } +#------------------------------------------------------------------- + +=head2 acceptsPrivateMessages ( userId ) + +Returns a boolean of whether or not the user can receive private messages from the user passed in + +=head3 userId + +userId to determine if the user accepts private messages from + +=cut + +sub acceptsPrivateMessages { + my $self = shift; + my $userId = shift; + + my $pmSetting = $self->profileField('allowPrivateMessages'); + + return 0 if ($pmSetting eq "none"); + return 1 if ($pmSetting eq "all"); + + if($pmSetting eq "friends") { + my $friendsGroup = $self->friends; + my $sentBy = WebGUI::User->new($self->session,$userId); + #$self->session->errorHandler->warn($self->isInGroup($friendsGroup->getId)); + return $sentBy->isInGroup($friendsGroup->getId); + } + + return 1; +} + #------------------------------------------------------------------- =head2 authMethod ( [ value ] ) @@ -163,22 +194,24 @@ Deletes this user. =cut sub delete { - my $self = shift; + my $self = shift; $self->uncache; + my $db = $self->session->db; foreach my $groupId (@{$self->getGroups($self->userId)}) { WebGUI::Group->new($self->session,$groupId)->deleteUsers([$self->userId]); } - $self->session->db->write("delete from inbox where userId=? and (groupId is null or groupId='')",[$self->{_userId}]); + $self->friends->delete if ($self->{_user}{"friendsGroup"} ne ""); + $db->write("delete from inbox where userId=? and (groupId is null or groupId='')",[$self->{_userId}]); require WebGUI::Operation::Auth; my $authMethod = WebGUI::Operation::Auth::getInstance($self->session,$self->authMethod,$self->{_userId}); $authMethod->deleteParams($self->{_userId}); - my $rs = $self->session->db->read("select sessionId from userSession where userId=?",[$self->{_userId}]); + my $rs = $db->read("select sessionId from userSession where userId=?",[$self->{_userId}]); while (my ($id) = $rs->array) { - $self->session->db->write("delete from userSessionScratch where sessionId=?",[$id]); + $db->write("delete from userSessionScratch where sessionId=?",[$id]); } - $self->session->db->write("delete from userSession where userId=?",[$self->{_userId}]); - $self->session->db->write("delete from userProfileData where userId=?",[$self->{_userId}]); - $self->session->db->write("delete from users where userId=?",[$self->{_userId}]); + $db->write("delete from userSession where userId=?",[$self->{_userId}]); + $db->write("delete from userProfileData where userId=?",[$self->{_userId}]); + $db->write("delete from users where userId=?",[$self->{_userId}]); } #------------------------------------------------------------------- @@ -213,10 +246,57 @@ Deconstructor. sub DESTROY { my $self = shift; + if (exists $self->{_friendsGroup}) { + $self->{_friendsGroup}->DESTROY; + } undef $self; } +#------------------------------------------------------------------- + +=head2 friends ( ) + +Returns the WebGUI::Group for this user's Friend's Group. + +=cut + +sub friends { + my $self = shift; + if ($self->{_user}{"friendsGroup"} eq "") { + my $myFriends = WebGUI::Group->new($self->session, "new"); + $myFriends->name($self->username." Friends"); + $myFriends->description("Friends of user ".$self->userId); + $myFriends->expireOffset(60*60*24*365*60); + $myFriends->showInForms(0); + $myFriends->isEditable(0); + $myFriends->deleteUsers(['3']); + $self->uncache; + $self->{_user}{"friendsGroup"} = $myFriends->getId; + $self->{_user}{"lastUpdated"} = $self->session->datetime->time(); + $self->session->db->write("update users set friendsGroup=?, lastUpdated=? where userId=?", + [$myFriends->getId, $self->session->datetime->time(), $self->userId]); + return $myFriends; + } + elsif (exists $self->{_friendsGroup}) { + return $self->{_friendsGroup}; + } + return WebGUI::Group->new($self->session, $self->{_user}{"friendsGroup"}); +} + +#------------------------------------------------------------------- + +=head2 getFirstName ( ) + +Returns first name, or alias, or username depeneding upon what exists. + +=cut + +sub getFirstName { + my $self = shift; + return $self->profileField('firstName') || $self->profileField('alias') || $self->username; +} + #------------------------------------------------------------------- =head2 getGroups ( [ withoutExpired ] ) @@ -252,6 +332,23 @@ sub getGroups { } } +#------------------------------------------------------------------- + +=head2 getWholeName ( ) + +Attempts to build the user's whole name from profile fields, and ultimately their alias and username if all else +fails. + +=cut + +sub getWholeName { + my $self = shift; + if ($self->profileField('firstName') and $self->profileField('lastName')) { + return join ' ', $self->profileField('firstName'), $self->profileField('lastName'); + } + return $self->profileField("alias") || $self->username; +} + #------------------------------------------------------------------- # This method is depricated and is provided only for reverse compatibility. See WebGUI::Auth instead. sub identifier { @@ -314,6 +411,22 @@ sub isInGroup { } +#------------------------------------------------------------------- + +=head2 isOnline () + +Returns a boolean indicating whether this user is logged in and actively viewing pages in the site. + +=cut + +sub isOnline { + my $self = shift; + my ($flag) = $self->session->db->quickArray('select count(*) from userSession where userId=? and lastPageView=?', + [$self->userId, time() - 60*10]); + return $flag; +} + + #------------------------------------------------------------------- =head2 karma ( [ amount, source, description ] ) diff --git a/lib/WebGUI/Workflow/Activity/DenyUnansweredFriends.pm b/lib/WebGUI/Workflow/Activity/DenyUnansweredFriends.pm new file mode 100644 index 000000000..9ef884479 --- /dev/null +++ b/lib/WebGUI/Workflow/Activity/DenyUnansweredFriends.pm @@ -0,0 +1,103 @@ +package WebGUI::Workflow::Activity::DenyUnansweredFriends; + + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2007 Plain Black Corporation. + ------------------------------------------------------------------- + Please read the legal notices (docs/legal.txt) and the license + (docs/license.txt) that came with this distribution before using + this software. + ------------------------------------------------------------------- + http://www.plainblack.com info@plainblack.com + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Workflow::Activity'; +use WebGUI::DateTime; +use DateTime::Duration; +use WebGUI::Friends; + +=head1 NAME + +Package WebGUI::Workflow::Activity::DenyUnansweredFriends + +=head1 DESCRIPTION + +This activity denies unanswered "Add a friend" requests after a set period of time. + +=head1 SYNOPSIS + +See WebGUI::Workflow::Activity for details on how to use any activity. + +=head1 METHODS + +These methods are available from this class: + +=cut + + +#------------------------------------------------------------------- + +=head2 definition ( session, definition ) + +See WebGUI::Workflow::Activity::defintion() for details. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, "Friends"); + push(@{$definition}, { + name => $i18n->get("deny unanswered friends"), + properties => { + timeout => { + fieldType => "interval", + label => $i18n->get("timeout"), + defaultValue => 0, + hoverHelp => $i18n->get("timeout help"), + }, + } + }); + return $class->SUPER::definition($session,$definition); +} + + +#------------------------------------------------------------------- + +=head2 execute ( [ object ] ) + +See WebGUI::Workflow::Activity::execute() for details. + +=cut + +sub execute { + my $self = shift; + my $start = time(); + my $session = $self->session; + my $now = WebGUI::DateTime->new($session, $start); + my $outdated = DateTime::Duration->new(seconds => $self->get("timeout")); + my $pending = WebGUI::Friends->getAllPendingAddRequests($session); + while (my $invite = $pending->hashRef) { + my $sentOn = WebGUI::DateTime->new($session, $invite->{dateSent}); + if (DateTime::Duration->compare($now - $sentOn, $outdated) == 1) { + WebGUI::Friends->new($session, WebGUI::User->new($session, $invite->{friendId}))->rejectAddRequest($invite->{inviteId}); + } + if (time() - $start > 55) { + $pending->finish; + return $self->WAITING; + } + } + return $self->COMPLETE; +} + + + +1; + + diff --git a/lib/WebGUI/i18n/English/Friends.pm b/lib/WebGUI/i18n/English/Friends.pm new file mode 100644 index 000000000..c90cbeed8 --- /dev/null +++ b/lib/WebGUI/i18n/English/Friends.pm @@ -0,0 +1,217 @@ +package WebGUI::i18n::English::Friends; + +our $I18N = { + + 'invitation accepted by user' => { + message => q{Your invitation has been accepted by %s.}, + lastUpdated => 0, + }, + + 'friends invitation accepted' => { + message => q{Friends Invitation Accepted}, + lastUpdated => 0, + }, + + 'friends invitation not accepted by user' => { + message => q{Your invitation has not been accepted by %s.}, + lastUpdated => 0, + }, + + 'friends invitation not accepted' => { + message => q{Friends Invitation Not Accepted}, + lastUpdated => 0, + }, + + 'invitation approval email' => { + message => q{%s has requested that you join their friend network on this site %s. + +%s + +Please visit the following url to accept or deny the request: + +%s}, + lastUpdated => 0, + }, + + 'friends network invitation' => { + message => q{Friends Network Invitation}, + lastUpdated => 0, + }, + + 'send friend email instructions' => { + message => q{Check the friends you'd like to send a message to and then fill in the message below.}, + lastUpdated => 0, + }, + + 'confirm remove friends' => { + message => q{Are you certain you wish to remove the selected friends from your list?}, + lastUpdated => 0, + }, + + 'already a friend' => { + message => q{You can't add a friend twice.}, + lastUpdated => 1186028432, + }, + + 'add to friends list' => { + message => q{Add this person to my friends list.}, + lastUpdated => 1186028432, + }, + + 'add to friends' => { + message => q{Add to Friends}, + lastUpdated => 1186030775, + }, + + 'add to friends description' => { + message => q{Do you really want to add %s as a friend?}, + lastUpdated => 1186030776, + }, + + 'add to friends confirmation' => { + message => q{An email has been sent to %s for your request to be added to your friends network.}, + lastUpdated => 1186030776, + }, + + 'add to friends profile' => { + message => q{Return to %s's Profile}, + lastUpdated => 1186030776, + }, + + 'does not want to be a friend' => { + message => q{This user prefers not to be added as a friend.}, + lastUpdated => 1186264488, + }, + + 'manage friends template' => { + message => q{Manage Friends Template}, + lastUpdated => 1186264488, + context => "setting", + }, + + 'manage friends template help' => { + message => q{Which template would you like to use for the "See my friends." screen in the user account?}, + lastUpdated => 1186264488, + context => "setting", + }, + + 'deny unanswered friends' => { + message => q{Deny Unanswered Friends}, + lastUpdated => 1186264488, + context => "workflow activity", + }, + + 'timeout' => { + message => q{Timeout}, + lastUpdated => 1186264488, + context => "workflow activity", + }, + + 'timeout help' => { + message => q{How long should invitations to a friends network go unanswered before we automatically deny the request?}, + lastUpdated => 1186264488, + context => "workflow activity", + }, + + 'add' => { + message => q{Add}, + lastUpdated => 1186264488, + }, + + 'default friend comments' => { + message => q{%s, +I'd like you to be a part of my friends network. + +Thanks, +%s}, + lastUpdated => 1186277362, + }, + + 'friend request' => { + message => q{Friend Request}, + lastUpdated => 1186277362, + }, + + 'friend request description' => { + message => q{%s has requested you be added to their friends list with the following comments:}, + lastUpdated => 1186277362, + }, + + 'invalid invite code' => { + message => q|Invalid invitation code|, + lastUpdated => 1186718713, + }, + + 'invalid invite code message' => { + message => q|The invitation code in your URL is invalid.|, + lastUpdated => 1186718715, + }, + + 'not the right user' => { + message => q|The invitation code you are trying to use is not for you.|, + lastUpdated => 1186718715, + }, + + 'you have not been added' => { + message => q|You have denied %s's request.|, + lastUpdated => 1186718715, + }, + + 'you have been added' => { + message => q|You have been added to %s's Friends List.|, + lastUpdated => 1186718715, + }, + + 'manage friends' => { + message => q|Manage Friends or send them private messages.|, + lastUpdated => 1186975937, + }, + + 'no friends' => { + message => q|You haven't signed up any friends.|, + lastUpdated => 1186976178, + }, + + 'my friends' => { + message => q|My Friends|, + lastUpdated => 1186976178, + }, + + 'name' => { + message => q|Name|, + lastUpdated => 1186976178, + }, + + 'status' => { + message => q|Status|, + lastUpdated => 1186976178, + }, + + 'online' => { + message => q|Online|, + lastUpdated => 1186976178, + }, + + 'offline' => { + message => q|Offline|, + lastUpdated => 1186976178, + }, + + 'send message' => { + message => q|Send Message|, + lastUpdated => 1186976178, + }, + + 'remove' => { + message => q|Remove|, + lastUpdated => 1186976178, + }, + + 'see my friends' => { + message => q|See my friends.|, + lastUpdated => 1187066104, + }, + +}; + +1; diff --git a/lib/WebGUI/i18n/English/WebGUI.pm b/lib/WebGUI/i18n/English/WebGUI.pm index f0267e38c..082c2e4b6 100644 --- a/lib/WebGUI/i18n/English/WebGUI.pm +++ b/lib/WebGUI/i18n/English/WebGUI.pm @@ -3546,7 +3546,22 @@ LongTruncOk=1

}, 'allow private messages label' => { - message => q|Allow Private Messages|, + message => q|Private Message Options|, + lastUpdated => 1181019679, + }, + + 'user profile field private message allow label' => { + message => q|Allow All|, + lastUpdated => 1181019679, + }, + + 'user profile field private message friends only label' => { + message => q|Allow From My Friends Only|, + lastUpdated => 1181019679, + }, + + 'user profile field private message allow none label' => { + message => q|Allow None|, lastUpdated => 1181019679, }, @@ -3875,6 +3890,11 @@ LongTruncOk=1

lastUpdated => 1185162267, }, + 'user profile field friend availability' => { + message => q{Are you available to be added as a Friend?}, + lastUpdated => 1185856549, + }, + }; 1;