From 504de54d61e7e6648f83f3f4f854ea2255aee738 Mon Sep 17 00:00:00 2001 From: JT Smith Date: Sun, 9 Apr 2006 22:36:49 +0000 Subject: [PATCH] started adding email processing capabilities to cs --- lib/WebGUI/Asset.pm | 18 ++ lib/WebGUI/Asset/Post.pm | 157 ++++++++------ lib/WebGUI/Asset/Post/Thread.pm | 19 +- lib/WebGUI/Asset/Wobject.pm | 17 -- lib/WebGUI/Mail/Get.pm | 4 + lib/WebGUI/Mail/Send.pm | 11 +- lib/WebGUI/Storage.pm | 2 +- lib/WebGUI/Storage/Image.pm | 2 +- lib/WebGUI/Workflow/Activity/GetCsMail.pm | 195 ++++++++++++++++++ .../i18n/English/Asset_Collaboration.pm | 24 +++ 10 files changed, 352 insertions(+), 97 deletions(-) create mode 100644 lib/WebGUI/Workflow/Activity/GetCsMail.pm diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm index 818d8859b..c8b0b3279 100644 --- a/lib/WebGUI/Asset.pm +++ b/lib/WebGUI/Asset.pm @@ -1024,6 +1024,24 @@ sub indexContent { } +#------------------------------------------------------------------- + +=head2 logView ( ) + +Logs the view of this asset to the passive profiling mechanism. + +=cut + +sub logView { + my $self = shift; + if ($self->session->setting->get("passiveProfilingEnabled")) { + WebGUI::PassiveProfiling::add($self->session,$self->getId); + WebGUI::PassiveProfiling::addPage($self->session,$self->getId) if ($self->get("className") eq "WebGUI::Asset::Wobject::Layout"); + } + return; +} + + #------------------------------------------------------------------- =head2 manageAssets ( ) diff --git a/lib/WebGUI/Asset/Post.pm b/lib/WebGUI/Asset/Post.pm index 33487dbc2..db2967209 100644 --- a/lib/WebGUI/Asset/Post.pm +++ b/lib/WebGUI/Asset/Post.pm @@ -47,6 +47,21 @@ sub addRevision { my $newStorage = WebGUI::Storage->get($self->session,$self->get("storageId"))->copy; $newSelf->update({storageId=>$newStorage->getId}); } + my $threadId = $newSelf->get("threadId"); + my $now = time(); + if ($threadId eq "") { # new post + if ($newSelf->getParent->get("className") eq "WebGUI::Asset::Wobject::Collaboration") { + $newSelf->update({threadId=>$newSelf->getId}, dateSubmitted=>$now); + } else { + $newSelf->update({threadId=>$newSelf->getParent->get("threadId"), dateSubmitted=>$now}); + } + } + $newSelf->update({ + isHidden => 1, + dateUpdated=>$now, + groupIdView=>$newSelf->getThread->getParent->get("groupIdView"), + groupIdEdit=>$newSelf->getThread->getParent->get("groupIdEdit"), + }); $self->getThread->unmarkRead; return $newSelf; } @@ -106,7 +121,15 @@ sub chopTitle { sub commit { my $self = shift; $self->SUPER::commit; + $self->getThread->unarchive if ($self->getThread->get("status") eq "archived"); $self->notifySubscribers; + if ($self->isNew) { + if ($self->session->setting->get("enableKarma") && $self->getThread->getParent->get("karmaPerPost")) { + my $u = WebGUI::User->new($self->session, $self->get("ownerUserId")); + $u->addKarma($self->getThread->getParent->get("karmaPerPost"), $self->getId, "Collaboration post"); + } + $self->getThread->incrementReplies($self->get("dateUpdated"),$self->getId) if ($self->isReply); + } } #------------------------------------------------------------------- @@ -374,10 +397,10 @@ sub getStorageLocation { } #------------------------------------------------------------------- -sub getSynopsisAndContentFromFormPost { +sub getSynopsisAndContent { my $self = shift; - my $synopsis = $self->session->form->process("synopsis"); - my $body = $self->session->form->process("content"); + my $synopsis = shift; + my $body = shift; unless ($synopsis) { $body =~ s/\n/\^\-\;/ unless ($body =~ m/\^\-\;/); my @content = split(/\^\-\;/,$body); @@ -550,6 +573,19 @@ sub incrementViews { #------------------------------------------------------------------- +=head2 isNew ( ) + +Returns a boolean indicating whether this post is new (not an edit). + +=cut + +sub isNew { + my $self = shift; + return $self->get("dateSubmitted") eq $self->get("dateUpdated"); +} + +#------------------------------------------------------------------- + =head2 isPoster ( ) Returns a boolean that is true if the current user created this post and is not a visitor. @@ -596,13 +632,13 @@ sub notifySubscribers { $inbox->addMessage({ groupId=>$self->getThread->getParent->get("subscriptionGroupId"), status=>"completed", - subject=>$self->get("subject"), + subject=>$self->getThread->getParent->get("mailPrefix").$self->get("subject"), message=>$message }); $inbox->addMessage({ groupId=>$self->getThread->get("subscriptionGroupId"), status=>"completed", - subject=>$self->get("subject"), + subject=>$self->getThread->getParent->get("mailPrefix").$self->get("subject"), message=>$message }); } @@ -613,74 +649,55 @@ sub processPropertiesFromFormPost { my $self = shift; $self->SUPER::processPropertiesFromFormPost; my $i18n = WebGUI::International->new($self->session); - my %data; if ($self->session->form->process("assetId") eq "new") { - if ($self->getParent->get("className") eq "WebGUI::Asset::Wobject::Collaboration") { - $self->update({threadId=>$self->getId}); - } else { - $self->update({threadId=>$self->getParent->get("threadId")}); - } - if ($self->session->setting->get("enableKarma") && $self->getThread->getParent->get("karmaPerPost")) { - my $u = WebGUI::User->new($self->session->user->userId); - $u->addKarma($self->getThread->getParent->get("karmaPerPost"), $self->getId, "Collaboration post"); - } - %data = ( + my %data = ( ownerUserId => $self->session->user->userId, username => $self->session->form->process("visitorName") || $self->session->user->profileField("alias") || $self->session->user->username, - isHidden => 1, ); - $data{url} = $self->fixUrl($self->getThread->get("url")."/1") if ($self->isReply); + $self->update(\%data); if ($self->getThread->getParent->canEdit) { $self->getThread->lock if ($self->session->form->process('lock')); $self->getThread->stick if ($self->session->form->process("stick")); } - $self->getThread->unarchive if ($self->getThread->get("status") eq "archived"); - $self->getThread->incrementReplies($self->get("dateUpdated"),$self->getId) if ($self->isReply); } - $data{groupIdView} =$self->getThread->getParent->get("groupIdView"); - $data{groupIdEdit} = $self->getThread->getParent->get("groupIdEdit"); - ($data{synopsis}, $data{content}) = $self->getSynopsisAndContentFromFormPost; - if ($self->getThread->getParent->get("addEditStampToPosts")) { - $data{content} .= "\n\n --- (".$i18n->get('Edited_on','Asset_Post')." ".$self->session->datetime->epochToHuman(undef,"%z %Z [GMT%O]").$i18n->get('By','Asset_Post').$self->session->user->profileField("alias").") --- \n"; - if ($self->getValue("contentType") eq "mixed" || $self->getValue("contentType") eq "html") { - $data{content} = '

'.$data{content}.'

'; - } - } - $self->update(\%data); $self->getThread->subscribe if ($self->session->form->process("subscribe")); delete $self->{_storageLocation}; my $storage = $self->getStorageLocation; - my $filename; my $attachmentLimit = $self->getThread->getParent->get("attachmentsPerPost"); - $filename = $storage->addFileFromFormPost("file", $attachmentLimit) if $attachmentLimit; - if (defined $filename) { - $self->setSize($storage->getFileSize($filename)); - foreach my $file (@{$storage->getFiles}) { - if ($storage->isImage($file)) { - $storage->generateThumbnail($file,$self->session->setting->get("maxImageSize")); - $storage->deleteFile($file); - $storage->renameFile('thumb-'.$file,$file); - $storage->generateThumbnail($file); - } - } - } - # allows us to let the cs post use it's own workflow approval process - my $currentTag = WebGUI::VersionTag->getWorking($self->session); - if ($currentTag->getAssetCount < 2) { - $currentTag->set({workflowId=>$self->getThread->getParent->get("approvalWorkflow")}); - $currentTag->requestCommit; - } else { - my $newTag = WebGUI::VersionTag->create($self->session, { - name=>$self->getTitle." / ".$self->session->user->username, - workflowId=>$self->getThread->getParent->get("approvalWorkflow") - }); - $self->session->db->write("update assetData set tagId=? where assetId=? and tagId=?",[$newTag->getId, $self->getId, $currentTag->getId]); - $self->purgeCache; - $newTag->requestCommit; - } + $storage->addFileFromFormPost("file", $attachmentLimit) if $attachmentLimit; + $self->postProcess; + $self->requestCommit; } +#------------------------------------------------------------------- + +sub postProcess { + my $self = shift; + my %data = (); + ($data{synopsis}, $data{content}) = $self->getSynopsisAndContent($self->get("synopsis"), $self->get("content")); + my $user = WebGUI::User->new($self->session, $self->get("ownerUserId")); + my $i18n = WebGUI::International->new($self->session, "Asset_Post"); + if ($self->getThread->getParent->get("addEditStampToPosts")) { + $data{content} .= "

\n\n --- (".$i18n->get('Edited_on')." ".$self->session->datetime->epochToHuman(undef,"%z %Z [GMT%O]")." ".$i18n->get('By')." ".$user->profileField("alias").") --- \n

"; + } + $data{url} = $self->fixUrl($self->getThread->get("url")."/1") if ($self->isReply && $self->isNew); + $self->update(\%data); + my $size = 0; + my $storage = $self->getStorageLocation; + foreach my $file (@{$storage->getFiles}) { + if ($storage->isImage($file)) { + $storage->generateThumbnail($file,$self->session->setting->get("maxImageSize")); + $storage->deleteFile($file); + $storage->renameFile('thumb-'.$file,$file); + $storage->generateThumbnail($file); + } + $size += $storage->getFileSize($file); + } + $self->setSize($size); + +} + #------------------------------------------------------------------- sub purge { @@ -704,7 +721,7 @@ See WebGUI::Asset::purgeCache() for details. sub purgeCache { my $self = shift; - $self->getThread->purgeCache if $self->isReply; + WebGUI::Cache->new($self->session,"view_".$self->getThread->getId)->delete; $self->SUPER::purgeCache; } @@ -746,6 +763,24 @@ sub rate { } } +#------------------------------------------------------------------- +# allows us to let the cs post use it's own workflow approval process +sub requestCommit { + my $self = shift; + my $currentTag = WebGUI::VersionTag->getWorking($self->session); + if ($currentTag->getAssetCount < 2) { + $currentTag->set({workflowId=>$self->getThread->getParent->get("approvalWorkflow")}); + $currentTag->requestCommit; + } else { + my $newTag = WebGUI::VersionTag->create($self->session, { + name=>$self->getTitle." / ".$self->session->user->username, + workflowId=>$self->getThread->getParent->get("approvalWorkflow") + }); + $self->session->db->write("update assetData set tagId=? where assetId=? and tagId=?",[$newTag->getId, $self->getId, $currentTag->getId]); + $self->purgeCache; + $newTag->requestCommit; + } +} #------------------------------------------------------------------- @@ -831,13 +866,13 @@ We overload the update method from WebGUI::Asset in order to handle file system sub update { my $self = shift; my %before = ( - owner => $self->get("ownerUserId"), + owner => $self->get("ownerUserId"), view => $self->get("groupIdView"), edit => $self->get("groupIdEdit") ); $self->SUPER::update(@_); if ($self->get("ownerUserId") ne $before{owner} || $self->get("groupIdEdit") ne $before{edit} || $self->get("groupIdView") ne $before{view}) { - $self->getStorageLocation->setPrivileges($self->get("ownerUserId"),$self->get("groupIdView"),$self->get("groupIdEdit")); + $self->getStorageLocation->setPrivileges($self->get("ownerUserId"),$self->get("groupIdView"),$self->get("groupIdEdit")); } } @@ -945,7 +980,7 @@ sub www_edit { $var{'form.header'} .= WebGUI::Form::hidden($self->session, {name=>"proceed", value=>"showConfirmation"}); if ($self->session->form->process("title") || $self->session->form->process("content") || $self->session->form->process("synopsis")) { $var{'preview.title'} = WebGUI::HTML::filter($self->session->form->process("title"),"all"); - ($var{'preview.synopsis'}, $var{'preview.content'}) = $self->getSynopsisAndContentFromFormPost; + ($var{'preview.synopsis'}, $var{'preview.content'}) = $self->getSynopsisAndContent($self->session->form->process("synopsis","textarea"), $self->session->form->process("content","HTMLArea")); $var{'preview.content'} = $self->formatContent($var{'preview.content'},$self->session->form->process("contentType")); for my $i (1..5) { $var{'preview.userDefined'.$i} = WebGUI::HTML::filter($self->session->form->process('userDefined'.$i),"macros"); diff --git a/lib/WebGUI/Asset/Post/Thread.pm b/lib/WebGUI/Asset/Post/Thread.pm index 8f386d162..6431bf88d 100644 --- a/lib/WebGUI/Asset/Post/Thread.pm +++ b/lib/WebGUI/Asset/Post/Thread.pm @@ -529,23 +529,10 @@ sub processPropertiesFromFormPost { #------------------------------------------------------------------- sub purge { my $self = shift; +$self->session->errorHandler->warn("Y"); $self->session->db->write("delete from Thread_read where postId=?",[$self->getId]); - return $self->SUPER::purge; -} - -#------------------------------------------------------------------- - -=head2 purgeCache () - -See WebGUI::Asset::purgeCache() for details. - -=cut - -sub purgeCache { - my $self = shift; - WebGUI::Cache->new($self->session,"view_".$self->getId)->delete; - $self->SUPER::purgeCache; - $self->getParent->purgeCache; +$self->session->errorHandler->warn("Z"); + $self->SUPER::purge; } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Asset/Wobject.pm b/lib/WebGUI/Asset/Wobject.pm index 6841a8c9b..830485fc8 100644 --- a/lib/WebGUI/Asset/Wobject.pm +++ b/lib/WebGUI/Asset/Wobject.pm @@ -242,23 +242,6 @@ sub getEditForm { -#------------------------------------------------------------------- - -=head2 logView ( ) - -Logs the view of the wobject to the passive profiling mechanism. -=cut - -sub logView { - my $self = shift; - if ($self->session->setting->get("passiveProfilingEnabled")) { - WebGUI::PassiveProfiling::add($self->session,$self->get("assetId")); - WebGUI::PassiveProfiling::addPage($self->session,$self->get("assetId")); # add wobjects on asset to passive profile log - } - return; -} - - #------------------------------------------------------------------- =head2 moveCollateralDown ( tableName, keyName, keyValue [ , setName, setValue ] ) diff --git a/lib/WebGUI/Mail/Get.pm b/lib/WebGUI/Mail/Get.pm index e60c244f5..e4d040b58 100644 --- a/lib/WebGUI/Mail/Get.pm +++ b/lib/WebGUI/Mail/Get.pm @@ -116,6 +116,7 @@ Retrieves the next available message from the server. Returns undef if there are cc => 'joe@example.com', subject => 'This is my message subject', inReplyTo => 'some-message-id', + messageId => 'some-message-id', date => 1144536119, parts => [ { @@ -161,12 +162,15 @@ sub getNextMessage { chomp($subject); my $inReplyTo = $head->get("In-Reply-To") || undef; chomp($inReplyTo); + my $messageId = $head->get("Message-Id") || undef; + chomp($messageId); my %data = ( to => $to, from => $from, cc => $cc, subject => $subject, inReplyTo => $inReplyTo, + messageId => $messageId, date => $self->session->datetime->mailToEpoch($head->get("Date")), ); my @segments = (); diff --git a/lib/WebGUI/Mail/Send.pm b/lib/WebGUI/Mail/Send.pm index 6ffd47a6b..634748ff9 100644 --- a/lib/WebGUI/Mail/Send.pm +++ b/lib/WebGUI/Mail/Send.pm @@ -182,10 +182,18 @@ A single email address that this message will originate from. Defaults to the co A single email address that responses to this message will be sent to. -=head contentType +=head4 contentType A mime type for the message. Defaults to "multipart/mixed". +=head4 messageId + +A unique id for this message, in case you want to see what replies come in for it later. One will be automatically generated if you don't specify this. + +=head4 inReplyTo + +If this is a reply to a previous message, then you should specify the messageId of the previous message here. + =cut sub create { @@ -212,6 +220,7 @@ sub create { Cc=>$headers->{cc}, Bcc=>$headers->{bcc}, "Reply-To"=>$headers->{replyTo}, + "In-Reply-To"=>$headers->{inReplyTo}, Subject=>$headers->{subject}, "Message-Id"=>$headers->{messageId} || "WebGUI-".$session->id->generate, Date=>$session->datetime->epochToHuman("","%W, %d %C %y %j:%n:%s %O"), diff --git a/lib/WebGUI/Storage.pm b/lib/WebGUI/Storage.pm index 707e069f2..1ab601e9c 100644 --- a/lib/WebGUI/Storage.pm +++ b/lib/WebGUI/Storage.pm @@ -176,7 +176,7 @@ sub addFileFromFilesystem { #------------------------------------------------------------------- -=head2 addFileFromFormPost ( formVariableName ) +=head2 addFileFromFormPost ( formVariableName, attachmentLimit ) Grabs an attachment from a form POST and saves it to this storage location. diff --git a/lib/WebGUI/Storage/Image.pm b/lib/WebGUI/Storage/Image.pm index acacc534d..3102605f2 100644 --- a/lib/WebGUI/Storage/Image.pm +++ b/lib/WebGUI/Storage/Image.pm @@ -100,7 +100,7 @@ sub generateThumbnail { return 0; } unless ($self->isImage($filename)) { - $self->session->errorHandler->error("Can't generate a thumbnail for something that's not an image."); + $self->session->errorHandler->warn("Can't generate a thumbnail for something that's not an image."); return 0; } my $image = Image::Magick->new; diff --git a/lib/WebGUI/Workflow/Activity/GetCsMail.pm b/lib/WebGUI/Workflow/Activity/GetCsMail.pm new file mode 100644 index 000000000..8a834ec5c --- /dev/null +++ b/lib/WebGUI/Workflow/Activity/GetCsMail.pm @@ -0,0 +1,195 @@ +package WebGUI::Workflow::Activity::GetCsMail; + + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2006 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::Mail::Get; +use WebGUI::Asset; + +=head1 NAME + +Package WebGUI::Workflow::Activity::GetCsMail + +=head1 DESCRIPTION + +Retrieve the incoming mail messages for a Collaboration System. + +=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 addPost ( parent, class, message, user ) + +Adds a post to this collaboration system. + +=head3 parent + +Either a collaboration system object reference, or a thread/post object reference. + +=head3 message + +The message retrieved from WebGUI::Mail::Get. + +=head3 user + +The user doing the posting. + +=cut + +sub addPost { + my $self = shift; + my $parent = shift; + my $message = shift; + my $user = shift; + my @attachments = (); + my $content = ""; + my $class = (ref $parent eq "WebGUI::Asset::Wobject::Collaboration") ? "WebGUI::Asset::Post::Thread" : "WebGUI::Asset::Post"; + foreach my $part (@{$message->{parts}}) { + if (($part->{type} eq "text/plain" || $part->{type} eq "text/html") && $part->{filename} eq "") { + my $text = $message->{content}; + if ($part->{type} eq "text/plain") { + $text =~ s/\n/\
/g; + } + $content .= $text; + } else { + push(@attachments, $part); + } + } + my $post = $parent->addChild({ + className=>$class, + title=>$message->{subject}, + menuTitle => $message->{subject}, + url=>$message->{subject}, + content=>$content, + ownerUserId=>$user->getId, + username=>$user->profileField("alias") || $user->username, + }); + if (scalar(@attachments)) { + my $storage = $post->getStorageLocation; + foreach my $file (@attachments) { + my $filename = $file->{filename}; + unless ($filename) { + $file->{type} =~ m/\/(.*)/; + my $type = $1; + $filename = $self->session->id->generate.".".$type; + } + $storage->addFileFromScalar($file->{content}, $filename); + } + } + $post->postProcess; + $post->requestCommit; +} + +#------------------------------------------------------------------- + +=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, "Asset_Collaboration"); + push(@{$definition}, { + name=>$i18n->get("get cs mail"), + properties=> { } + }); + return $class->SUPER::definition($session,$definition); +} + + +#------------------------------------------------------------------- + +=head2 execute ( ) + +See WebGUI::Workflow::Activity::execute() for details. + +=cut + +sub execute { + my $self = shift; + my $cs = shift; + return $self->COMPLETE unless ($cs->get("getMail")); + my $start = time(); + my $mail = WebGUI::Mail::Get->connect($self->session,{ + server=>$cs->get("mailHost"), + account=>$cs->get("mailAccount"), + password=>$cs->get("mailUser") + }); + return $self->COMPLETE unless (defined $mail); + my $i18n = WebGUI::International->new($self->session, "Asset_Collaboration"); + while (my $message = $mail->getNextMessage) { + next unless (scalar(@{$message->{parts}})); # no content, skip it + my $from = $message->{from}; + $from =~ /<(\S+\@\S+)>/; + $from = $1 || $from; + $from =~ /(\S+\@\S+)/; + my $user = WebGUI::User->newByEmail($self->session, $from); + unless (defined $user) { + my $send = WebGUI::Mail::Send->create($self->session, { + to=>$message->{from}, + inReplyTo=>$message->{messageId}, + subject=>$cs->get("mailPrefix").$i18n->get("rejected")." ".$self->{subject}, + from=>$cs->get("mailAddress") + }); + $send->addText($i18n->get("rejected because no user account")); + $send->send; + next; + } + my $post = undef; + if ($message->{inReplyTo}) { + $message->{inReplyTo} =~ m/WebGUI\-([\w_-]{22})/; + my $id = $1; + $post = WebGUI::Asset->newByDynamicClass($self->session, $id); + } + if (defined $post && $cs->get("allowReplies") && $user->isInGroup($cs->get("postGroupId")) && ($user->isInGroup($cs->get("subscriptionGroupId")) || $user->isInGroup($post->get("subscriptionGroupId")))) { + $self->addPost($post, $message, $user); + } elsif ($user->isInGroup($cs->get("postGroupId")) && $user->isInGroup($cs->get("subscriptionGroupId"))) { + $self->addPost($cs, $message, $user); + } else { + my $send = WebGUI::Mail::Send->create($self->session, { + to=>$message->{from}, + inReplyTo=>$message->{messageId}, + subject=>$cs->get("mailPrefix").$i18n->get("rejected")." ".$self->{subject}, + from=>$cs->get("mailAddress") + }); + $send->addText($i18n->get("rejected because not allowed")); + $send->send; + } + # just in case there are a lot of messages, we should release after a minutes worth of retrieving + last if (time() > $start + 60); + } + $mail->disconnect; + return $self->COMPLETE; +} + + + +1; + + diff --git a/lib/WebGUI/i18n/English/Asset_Collaboration.pm b/lib/WebGUI/i18n/English/Asset_Collaboration.pm index 278f79cba..b95317d4b 100644 --- a/lib/WebGUI/i18n/English/Asset_Collaboration.pm +++ b/lib/WebGUI/i18n/English/Asset_Collaboration.pm @@ -2,6 +2,30 @@ package WebGUI::i18n::English::Asset_Collaboration; our $I18N = { + 'rejected' => { + message => q|Rejected|, + lastUpdated => 0, + context => q|prepended to subject line in rejection emails| + }, + + 'rejected because no user account' => { + message => q|You are not allowed to post messages because we could not find your user account. Perhaps you do not have this email address associated with your user account.|, + lastUpdated => 0, + context => q|rejection letter for posting when a user account could not be looked up| + }, + + 'rejected because not allowed' => { + message => q|You are not allowed to post messages because you either have insufficient privileges, or you are not subscribed to this discussion.|, + lastUpdated => 0, + context => q|rjection letter for posting when not subscribed or not in group to post| + }, + + 'get cs mail' => { + message => q|Get Collaboration System Mail|, + lastUpdated => 0, + context => q|Title of CS Get Mail workflow activity| + }, + 'visitor cache timeout' => { message => q|Visitor Cache Timeout|, lastUpdated => 0