WebGUI::Inbox::Message::create now passes isInbox flag to WebGUI::Mail::Send::create so that per-user notification settings get used SMS Gateway setting field is now 'text' instead of 'email' so that user can enter a properly formed value (such as 'myemailgateway.com', which is not an email address). Added SMS notification template as distinct from email notification template because SMSs should not be HTML and in general you will want to make your SMS notification message a lot shorter than your html email notification message (160 char limits in many countries). As a result, SMSs are now sent as separate emails to gateway rather than being CCd on email notification. Added smsGatewaySubject setting because many SMS Gateways use email subject for authentication. For consistency, also added Email notification email subject. Fixed handling of different combinations of site-wide sendInboxNotificationsOnly with per-user receiveInboxEmailNotifications and receiveInboxSmsEmailNotifications. * sendInboxNotificationsOnly && receiveInboxEmailNotifications = email notification * sendInboxNotificationsOnly && !receiveInboxEmailNotifications = no email * !sendInboxNotificationsOnly = ignore receiveInboxEmailNotifications * In all cases, SMS is optional and only dependent on receiveInboxSmsEmailNotifications Updated tests and i18n
706 lines
20 KiB
Perl
706 lines
20 KiB
Perl
package WebGUI::Inbox::Message;
|
|
|
|
=head1 LEGAL
|
|
|
|
-------------------------------------------------------------------
|
|
WebGUI is Copyright 2001-2009 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 WebGUI::Mail::Send;
|
|
use WebGUI::International;
|
|
|
|
=head1 NAME
|
|
|
|
Package WebGUI::Inbox::Message;
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This package provides an API for working with inbox messages.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use WebGUI::Inbox::Message;
|
|
|
|
my $message = WebGUI::Inbox::Message->new($session, $messageId);
|
|
|
|
=head1 METHODS
|
|
|
|
These methods are available from this class:
|
|
|
|
=cut
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 create ( session, properties )
|
|
|
|
Creates a new message.
|
|
|
|
=head2 session
|
|
|
|
A reference to the current session.
|
|
|
|
=head3 properties
|
|
|
|
A hash reference containing the properties to update.
|
|
|
|
=head4 message
|
|
|
|
The content of this message.
|
|
|
|
=head4 subject
|
|
|
|
The topic of this message. Defaults to 'Notification'.
|
|
|
|
=head4 status
|
|
|
|
May be "unread", "pending", or "completed". Defaults to "pending".
|
|
|
|
You should set this to "pending" if the message requires an action which will later be completed.
|
|
|
|
WebGUI::Inbox::Message->create( $session, { status => "pending"} )
|
|
|
|
You should set this to "unread" if this is a message without an action, such as a notification.
|
|
|
|
WebGUI::Inbox::Message->create( $session, { status => "unread" } );
|
|
|
|
You should only set this to "completed" if this is an action that would normally be "pending" but for some reason
|
|
requries no further action. For instance, if the user submitting some content is also the approver you may choose
|
|
to simply set the status immediately to completed.
|
|
|
|
=head4 userId
|
|
|
|
A userId of a user attached to this message.
|
|
|
|
=head4 groupId
|
|
|
|
A groupId of a group attached to this message.
|
|
|
|
=head4 sentBy
|
|
|
|
A userId that created this message. Defaults to '3' (Admin).
|
|
|
|
=head4 emailMessage
|
|
|
|
Email message to use rather than inbox message contents.
|
|
|
|
=head4 emailSubject
|
|
|
|
Email subject to use rather than inbox message subject.
|
|
|
|
=head4 smsMessage
|
|
|
|
SMS notification message to send to C<smsAddress>
|
|
|
|
=head4 smsSubject
|
|
|
|
SMS notification subject (typically used for SMS Gateway authentication)
|
|
|
|
=head4 smsAddress
|
|
|
|
Email address that SMS notification is sent to (typically the user's C<cellPhone> C<@> C<smsGateway>)
|
|
|
|
=head3 options
|
|
|
|
A hash reference containing options for handling the message.
|
|
|
|
=head4 no_email
|
|
|
|
If no_email is true, then no email will be made or sent. Only
|
|
the inbox message will be made.
|
|
|
|
=head4 no_sms
|
|
|
|
If no_sms is true, then no attempt to sms notifications will be sent.
|
|
|
|
=head4 overridePerUserDelivery
|
|
|
|
If true, then the C<isInbox> flag will not be passed to L<WebGUI::Mail::Send::Create>, and thus the
|
|
per-user settings for email delivery will not be used. Useful if you want to force this message to
|
|
be sent as an Email rather than allowing the user's C<receiveInboxEmailNotifications> setting to
|
|
take effect.
|
|
|
|
=cut
|
|
|
|
sub create {
|
|
my $class = shift;
|
|
my $session = shift;
|
|
my $properties = shift;
|
|
my $options = shift || {};
|
|
my $self = {};
|
|
my $i18n = WebGUI::International->new($session);
|
|
$self->{_properties}{messageId} = "new";
|
|
$self->{_properties}{status} = $properties->{status} || "pending";
|
|
$self->{_properties}{subject} = $properties->{subject} || $i18n->get(523,'WebGUI');
|
|
$self->{_properties}{message} = $properties->{message};
|
|
$self->{_properties}{dateStamp} = time();
|
|
$self->{_properties}{userId} = $properties->{userId};
|
|
$self->{_properties}{groupId} = $properties->{groupId};
|
|
$self->{_properties}{sentBy} = $properties->{sentBy} || 3;
|
|
unless ( $self->{_properties}{userId} || $self->{_properties}{groupId} ) {
|
|
$self->{_properties}{userId} = $session->user->userId;
|
|
}
|
|
my $status = $self->{_properties}{status};
|
|
my $smsMessage = $properties->{smsMessage};
|
|
my $smsSubject = $properties->{smsSubject};
|
|
my $smsAddress = $properties->{smsAddress};
|
|
|
|
if ($status eq "completed") {
|
|
$self->{_properties}{completedBy} = $session->user->userId;
|
|
$self->{_properties}{completedOn} = time();
|
|
}
|
|
elsif($status ne "pending") {
|
|
$self->{_properties}{status} = "active";
|
|
}
|
|
|
|
$self->{_messageId} = $self->{_properties}{messageId} = $session->db->setRow("inbox","messageId",$self->{_properties});
|
|
$self->{_userId } = $self->{_properties}{userId};
|
|
$self->{_inbox } = $self->{_properties};
|
|
|
|
#Add the message state row for individual user passed in
|
|
if($self->{_properties}{userId}) {
|
|
$session->db->write(
|
|
q{ REPLACE INTO inbox_messageState (messageId,userId) VALUES (?,?) },
|
|
[$self->{_messageId},$self->{_properties}{userId}]
|
|
);
|
|
}
|
|
#Add the message state row for every user in the group
|
|
if($self->{_properties}{groupId}) {
|
|
my $g = WebGUI::Group->new($session,$self->{_properties}{groupId});
|
|
my $users = $g->getAllUsers;
|
|
foreach my $userId (@{$users}) {
|
|
$session->db->write(
|
|
q{ REPLACE INTO inbox_messageState (messageId,userId) VALUES (?,?) },
|
|
[$self->{_messageId},$userId]
|
|
);
|
|
}
|
|
}
|
|
unless ( $options->{ no_email } ) {
|
|
my $subject = (defined $properties->{emailSubject}) ? $properties->{emailSubject} : $self->{_properties}{subject};
|
|
my $mail = WebGUI::Mail::Send->create($session, {
|
|
toUser=>$self->{_properties}{userId},
|
|
toGroup=>$self->{_properties}{groupId},
|
|
subject=>$subject,
|
|
},
|
|
$options->{overridePerUserDelivery} ? undef : 'isInbox',
|
|
);
|
|
my $preface = "";
|
|
my $fromUser = WebGUI::User->new($session, $properties->{sentBy});
|
|
#Don't append prefaces to the visitor users or messages that don't specify a user (default case)
|
|
unless ($fromUser->isVisitor || $fromUser->userId eq 3) { #Can't use isAdmin because it will not send prefaces from normal users who in the admin group
|
|
$preface = sprintf($i18n->get('from user preface', 'Inbox_Message'), $fromUser->username);
|
|
}
|
|
my $msg = (defined $properties->{emailMessage}) ? $properties->{emailMessage} : $self->{_properties}{message};
|
|
$msg = '<p>' . $preface . '</p><br />'.$msg if($preface ne "");
|
|
$mail->addHtml($msg);
|
|
$mail->addFooter;
|
|
$mail->queue;
|
|
}
|
|
|
|
unless ( $options->{ no_sms } ) {
|
|
# If smsAddress provided, send smsMessage too
|
|
if ( $smsAddress && $smsMessage) {
|
|
my $sms = WebGUI::Mail::Send->create(
|
|
$session,
|
|
{ to => $smsAddress,
|
|
subject => $smsSubject,
|
|
}
|
|
);
|
|
if ($sms) {
|
|
$sms->addText($smsMessage);
|
|
$sms->queue;
|
|
}
|
|
}
|
|
}
|
|
|
|
$self->{_session} = $session;
|
|
bless $self, $class;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 delete ( userId )
|
|
|
|
Deletes this message from the inbox for the user passed in
|
|
|
|
=head3 userId
|
|
|
|
User to delete message for. If no user is passed in, the current user will be used.
|
|
|
|
=cut
|
|
|
|
sub delete {
|
|
my $self = shift;
|
|
my $session = $self->session;
|
|
my $db = $session->db;
|
|
my $messageId = $self->getId;
|
|
my $userId = shift || $self->{_userId};
|
|
|
|
$self->setDeleted($userId);
|
|
|
|
my $isActive = $db->quickScalar(
|
|
q{ select count(*) from inbox_messageState where messageId=? and deleted=0 },
|
|
[$messageId]
|
|
);
|
|
#Delete the message from the database if everyone who was sent the message has deleted it
|
|
unless ($isActive) {
|
|
$db->write("delete from inbox where messageId=?",[$messageId]);
|
|
$db->write("delete from inbox_messageState where messageId=?",[$messageId]);
|
|
}
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 DESTROY ( )
|
|
|
|
Deconstructor.
|
|
|
|
=cut
|
|
|
|
sub DESTROY {
|
|
my $self = shift;
|
|
undef $self;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 get ( property )
|
|
|
|
Returns the value of a property.
|
|
|
|
=head3 property
|
|
|
|
The name of any property of an inbox message. See create() for details. In addition to those settable by create, you may also retrieve these:
|
|
|
|
=head4 dateStamp
|
|
|
|
The date the message was created.
|
|
|
|
=head4 completedBy
|
|
|
|
The userId of the user that completed the action associated with this message.
|
|
|
|
=head4 completedOn
|
|
|
|
An epoch date representing when the action associated with this message was completed.
|
|
|
|
=cut
|
|
|
|
sub get {
|
|
my $self = shift;
|
|
my $name = shift;
|
|
|
|
if($name eq "status") {
|
|
my $status = $self->{_properties}{status};
|
|
if($status eq "active") {
|
|
return "read" if($self->{_properties}{isRead});
|
|
return "replied" if($self->{_properties}{repliedTo});
|
|
return "unread";
|
|
}
|
|
return $status;
|
|
}
|
|
|
|
return $self->{_properties}{$name};
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getId ( )
|
|
|
|
Returns the ID of this message.
|
|
|
|
=cut
|
|
|
|
sub getId {
|
|
my $self = shift;
|
|
return $self->{_messageId};
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 getStatus ( [ userId ] )
|
|
|
|
Gets the current status of the message for the user passed in
|
|
|
|
=head3 userId
|
|
|
|
The id of the user to get the status of the message for. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub getStatus {
|
|
my $self = shift;
|
|
my $userId = shift || $self->{_userId};
|
|
|
|
my $status = $self->{_properties}{status};
|
|
my $statusCodes = $self->statusCodes;
|
|
|
|
if($status eq "active") {
|
|
return $statusCodes->{"replied"} if($self->{_properties}{repliedTo});
|
|
return $statusCodes->{"read" } if($self->{_properties}{isRead});
|
|
return $statusCodes->{"unread" };
|
|
}
|
|
|
|
return $statusCodes->{$self->get("status")};
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isRead ( )
|
|
|
|
Returns whether or not the message has been read.
|
|
|
|
=cut
|
|
|
|
sub isRead {
|
|
my $self = shift;
|
|
return $self->{_properties}{isRead};
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 isValidStatus ( status )
|
|
|
|
Returns whether or not the status passed in is valid. Can be called as a class or instance method
|
|
|
|
=head4 status
|
|
|
|
The id of the user that replied to this message. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub isValidStatus {
|
|
my $self = shift;
|
|
my $status = shift;
|
|
return (exists $self->statusCodes->{$status});
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 new ( session, messageId )
|
|
|
|
Constructor used to access existing messages. Use create for making
|
|
new messages.
|
|
|
|
=head3 session
|
|
|
|
A reference to the current session.
|
|
|
|
=head3 messageId
|
|
|
|
The unique id of a message.
|
|
|
|
=head3 userId
|
|
|
|
The userId for this message. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub new {
|
|
my $class = shift;
|
|
my $session = shift;
|
|
my $messageId = shift;
|
|
my $userId = shift || $session->user->userId;
|
|
|
|
#Don't bother going on if a messageId wasn't passed in
|
|
return undef unless $messageId;
|
|
|
|
my $inbox = $session->db->getRow("inbox","messageId",$messageId);
|
|
my $statusValues = $session->db->quickHashRef(
|
|
q{ select isRead, repliedTo, deleted from inbox_messageState where messageId=? and userId=? },
|
|
[$messageId,$userId]
|
|
);
|
|
|
|
#Don't return messages that don't exist
|
|
return undef unless (scalar(keys %{$inbox}));
|
|
|
|
#Don't return deleted messages
|
|
return undef if($statusValues->{deleted});
|
|
|
|
my $self = {};
|
|
|
|
my %properties = (%{$inbox},%{$statusValues});
|
|
|
|
bless {_properties=>\%properties, _inbox=>$inbox, _session=>$session, _messageId=>$messageId, _userId=>$userId}, $class;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 session
|
|
|
|
Returns a reference to the current session.
|
|
|
|
=cut
|
|
|
|
sub session {
|
|
my $self = shift;
|
|
return $self->{_session};
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setCompleted ( [ userId ] )
|
|
|
|
Marks a message completed.
|
|
|
|
=head4 userId
|
|
|
|
The id of the user that completed this task. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub setCompleted {
|
|
my $self = shift;
|
|
my $userId = shift || $self->session->user->userId;
|
|
$self->{_properties}{status} = "completed";
|
|
$self->{_properties}{completedBy} = $userId;
|
|
$self->{_properties}{completedOn} = time();
|
|
$self->{_inbox}{status} = "completed";
|
|
$self->{_inbox}{completedBy} = $userId;
|
|
$self->{_inbox}{completedOn} = time();
|
|
$self->session->db->setRow("inbox","messageId",$self->{_inbox});
|
|
#Completed messages should also be marked read for all users connected to this message
|
|
$self->setReadAll;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setDeleted ( [ userId ] )
|
|
|
|
Marks a message deleted.
|
|
|
|
=head4 userId
|
|
|
|
The id of the user that deleted this message. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub setDeleted {
|
|
my $self = shift;
|
|
my $userId = shift || $self->session->user->userId;
|
|
|
|
$self->session->db->write(
|
|
q{update inbox_messageState set deleted=1 where messageId=? and userId=?},
|
|
[$self->getId,$userId]
|
|
);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setRead ( [ userId ] )
|
|
|
|
Marks a message read.
|
|
|
|
=head4 userId
|
|
|
|
The id of the user that reads this message. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub setRead {
|
|
my $self = shift;
|
|
my $userId = shift || $self->session->user->userId;
|
|
|
|
$self->session->db->write(
|
|
q{update inbox_messageState set isRead=1 where messageId=? and userId=?},
|
|
[$self->getId,$userId]
|
|
);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setReadAll ( )
|
|
|
|
Marks a message read for all users who are connected to this message
|
|
|
|
=cut
|
|
|
|
sub setReadAll {
|
|
my $self = shift;
|
|
|
|
$self->session->db->write(
|
|
q{update inbox_messageState set isRead=1 where messageId=?},
|
|
[$self->getId]
|
|
);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setReplied ( [ userId ] )
|
|
|
|
Marks a message replied.
|
|
|
|
=head4 userId
|
|
|
|
The id of the user that replied to this message. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub setReplied {
|
|
my $self = shift;
|
|
my $userId = shift || $self->session->user->userId;
|
|
|
|
$self->session->db->write(
|
|
q{update inbox_messageState set repliedTo=1, isRead=1 where messageId=? and userId=?},
|
|
[$self->getId,$userId]
|
|
);
|
|
}
|
|
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setStatus ( status,[ userId ] )
|
|
|
|
Sets the current status of the message
|
|
|
|
There are two levels of status for any inbox message:
|
|
|
|
Global Message Status - This is the status of the entire message and holds true for everyone who received the message. These status values are as follows:
|
|
|
|
pending - indicates that there is some action for one of the users who received this message to act on.
|
|
active - indicates that this is a message that requires no action by any users who received this message.
|
|
completed - indicates that the action that was required is now completed.
|
|
|
|
Individual Message Status - This is the status of the message for each individual who received it. If you send a message to a group, each person who received the message will be able to see the message in one of the following states:
|
|
|
|
unread - indicates that this message has not be read by the current user
|
|
read - indicates that this message has been read by the current user
|
|
replied - indicates that the user has replied to this message
|
|
|
|
It is important to note that there is one more status not listed here which is deleted. This is a special state which cannot be set through this method. You should use the setDeleted method in this class if you wish to delete a message for a user.
|
|
|
|
=head4 status
|
|
|
|
Status to mark the message
|
|
|
|
=head4 userId
|
|
|
|
The id of the user that completed this task. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub setStatus {
|
|
my $self = shift;
|
|
my $status = shift;
|
|
my $session = $self->session;
|
|
my $userId = shift || $session->user->userId;
|
|
unless ($status) {
|
|
$session->log->warn("No status passed in for message. Exit without update");
|
|
return undef;
|
|
}
|
|
|
|
unless($self->isValidStatus($status)) {
|
|
$self->session->log->warn("Invalid status $status passed in for message. Exit without update");
|
|
return undef;
|
|
}
|
|
|
|
if($status eq "completed") {
|
|
$self->setCompleted($userId);
|
|
return undef;
|
|
}
|
|
elsif($status eq "read") {
|
|
$self->setRead($userId);
|
|
}
|
|
elsif($status eq "unread") {
|
|
$self->setUnread($userId);
|
|
}
|
|
elsif($status eq "replied") {
|
|
$self->setReplied($userId);
|
|
}
|
|
|
|
#Only let completed stuff go back to pending
|
|
if ( $status eq "pending" && $self->{_properties}{status} eq "completed") {
|
|
$self->{_properties}{status} = "pending";
|
|
$self->{_inbox}{status} = "pending"
|
|
}
|
|
|
|
$self->session->db->setRow("inbox","messageId",$self->{_inbox});
|
|
return undef;
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 setUnread ( [ userId ] )
|
|
|
|
Marks a message unread.
|
|
|
|
=head4 userId
|
|
|
|
The id of the user that reads this message. Defaults to the current user.
|
|
|
|
=cut
|
|
|
|
sub setUnread {
|
|
my $self = shift;
|
|
my $userId = shift || $self->session->user->userId;
|
|
|
|
$self->session->db->write(
|
|
q{update inbox_messageState set isRead=0 where messageId=? and userId=?},
|
|
[$self->getId,$userId]
|
|
);
|
|
}
|
|
|
|
#-------------------------------------------------------------------
|
|
|
|
=head2 statusCodes ( session )
|
|
|
|
Returns a hash ref of valid status values. Can be called as a class or instance method:
|
|
|
|
WebGUI::Inbox::Message->statusCodes($session);
|
|
|
|
my $message = WebGUI::Inbox::Message->new($session, $messageId);
|
|
my $statusCodes = $inbox->statusCodes;
|
|
|
|
There are two levels of status for any inbox message:
|
|
|
|
Global Message Status - This is the status of the entire message and holds true for everyone who received the message. These status values are as follows:
|
|
|
|
pending - indicates that there is some action for one of the users who received this message to act on.
|
|
active - indicates that this is a message that requires no action by any users who received this message.
|
|
completed - indicates that the action that was required is now completed.
|
|
|
|
Individual Message Status - This is the status of the message for each individual who received it. If you send a message to a group, each person who received the message will be able to see the message in one of the following states:
|
|
|
|
unread - indicates that this message has not be read by the current user
|
|
read - indicates that this message has been read by the current user
|
|
replied - indicates that the user has replied to this message
|
|
|
|
It is important to note that there is one more status not listed here which is deleted. This is a special state and you should use the setDeleted method in this class if you wish to delete a message for a user.
|
|
|
|
=head4 session
|
|
|
|
The current session object.
|
|
|
|
=cut
|
|
|
|
sub statusCodes {
|
|
my $self = shift;
|
|
my $session = shift;
|
|
|
|
if(ref $self eq "WebGUI::Inbox::Message") {
|
|
$session = $self->session;
|
|
}
|
|
|
|
my $i18n = WebGUI::International->new($session);
|
|
return {
|
|
"active" => $i18n->get("inbox message status active"),
|
|
"pending" => $i18n->get(552),
|
|
"completed" => $i18n->get(350),
|
|
"unread" => $i18n->get("private message status unread"),
|
|
"read" => $i18n->get("private message status read"),
|
|
"replied" => $i18n->get("private message status replied"),
|
|
}
|
|
}
|
|
|
|
1;
|