--- loncom/interface/lonmsg.pm 2006/12/06 17:14:50 1.188 +++ loncom/interface/lonmsg.pm 2014/12/11 01:53:22 1.237 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines for messaging # -# $Id: lonmsg.pm,v 1.188 2006/12/06 17:14:50 www Exp $ +# $Id: lonmsg.pm,v 1.237 2014/12/11 01:53:22 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -28,11 +28,183 @@ package Apache::lonmsg; +=pod + +=head1 NAME + +Apache::lonmsg: supports internal messaging + +=head1 SYNOPSIS + +lonmsg provides routines for sending messages. + +Right now, this document will cover just how to send a message, since +it is likely you will not need to programmatically read messages, +since lonmsg already implements that functionality. + +The routines used to package messages and unpackage messages are not +only used by lonmsg when creating/extracting messages for LON-CAPA's +internal messaging system, but also by lonnotify.pm which is available +for use by Domain Coordinators to broadcast standard e-mail to specified +users in their domain. The XML packaging used in the two cases is very +similar. The differences are the use of $uname and +$udom in stored internal messages, compared +with $email in stored +Domain Coordinator e-mail for the storage of information about +recipients of the message/e-mail. + +=head1 SUBROUTINES + +=over + +=pod + +=item packagemsg() + +Package + +=item get_course_context() + +=item unpackagemsg() + +Unpack message into a hash + +=item buildmsgid() + +Get info out of msgid + +=item unpackmsgid() + +=item sendemail() + +=item sendnotification() + +Send notification emails + +=item newmail() + +Check for email + +=item author_res_msg() + +Automated message to the author of a resource + +=item * B: Sends message $message to the owner + of the resource with the URI $filename. + +=item retrieve_author_res_msg() + +Retrieve author resource messages + +=item del_url_author_res_msg() + +Delete all author messages related to one URL + +=item clear_author_res_msg() + +Clear out all author messages in URL path + +=item all_url_author_res_msg() + +Return hash with URLs for which there is a resource message + +=item store_instructor_comment() + +Add a comment to the User Notes screen + +=item user_crit_msg_raw() + +Critical message to a user + +=item user_crit_msg() + +New routine that respects "forward" and calls old routine + +=item * B: + Sends a critical message $message to the $user at $domain. If $sendback + is true, a receipt will be sent to the current user when $user receives + the message. + + Additionally it will check if the user has a Forwarding address + set, and send the message to that address instead + + returns + - in array context a list of results for each message that was sent + - in scalar context a space seperated list of results for each + message sent + + +=item user_crit_received() + +Critical message received + +=item user_normal_msg_raw() + +Normal communication + +=item user_normal_msg() + +New routine that respects "forward" and calls old routine + +=item * B: + Sends a message to the $user at $domain, with subject $subject and message $message. + + Additionally it will check if the user has a Forwarding address + set, and send the message to that address instead + + returns + - in array context a list of results for each message that was sent + - in scalar context a space seperated list of results for each + message sent + +=item store_sent_mail() + +=item store_recipients() + +=item foldersuffix() + +=item get_user_folders() + +User-defined folders + +=item secapply() + +=item B: + +Arguments + $feedurl - /res/ url of resource (only need if $author is true) + $author,$question,$course,$policy - all true/false parameters + if true will attempt to find the addresses of user that should receive + this type of feedback (author - feedback to author of resource $feedurl, + $question 'Resource Content Questions', $course 'Course Content Question', + $policy 'Course Policy') + (Additionally it also checks $env for whether the corresponding form. + element exists, for ease of use in a html response context) + + $defaultflag - (internal should be left blank) if true gather addresses + that aren't for a section even if I have a section + (used for reccursion internally, first we look for + addresses for our specific section then we recurse + and look for non section addresses) + +Returns + $typestyle - string of html text, describing what addresses were found + %to - a hash, which keys are addresses of users to send messages to + the keys will look like name:domain + +=back + +=cut + use strict; use Apache::lonnet; use HTML::TokeParser(); use Apache::lonlocal; use Mail::Send; +use HTML::Entities; +use Encode; use LONCAPA qw(:DEFAULT :match); { @@ -43,42 +215,26 @@ use LONCAPA qw(:DEFAULT :match); } } -# ===================================================================== Package + sub packagemsg { my ($subject,$message,$citation,$baseurl,$attachmenturl, - $recuser,$recdomain,$msgid,$type,$crsmsgid)=@_; + $recuser,$recdomain,$msgid,$type,$crsmsgid,$symb,$error,$recipid)=@_; $message =&HTML::Entities::encode($message,'<>&"'); $citation=&HTML::Entities::encode($citation,'<>&"'); $subject =&HTML::Entities::encode($subject,'<>&"'); #remove machine specification - $baseurl =~ s|^http://[^/]+/|/|; + $baseurl =~ s|^https?://[^/]+/|/|; $baseurl =&HTML::Entities::encode($baseurl,'<>&"'); #remove machine specification - $attachmenturl =~ s|^http://[^/]+/|/|; + $attachmenturl =~ s|^https?://[^/]+/|/|; $attachmenturl =&HTML::Entities::encode($attachmenturl,'<>&"'); - my $course_context; - if (defined($env{'form.replyid'})) { - my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid)= - split(/\:/,&unescape($env{'form.replyid'})); - $course_context = $origcid; - } - foreach my $key (keys(%env)) { - if ($key=~/^form\.(rep)?rec\_(.*)$/) { - my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid) = - split(/\:/,&unescape($2)); - $course_context = $origcid; - last; - } - } - unless(defined($course_context)) { - $course_context = $env{'request.course.id'}; - } + my $course_context = &get_course_context(); my $now=time; my $msgcount = &get_uniq(); unless(defined($msgid)) { $msgid = &buildmsgid($now,$subject,$env{'user.name'},$env{'user.domain'}, - $msgcount,$course_context,$$); + $msgcount,$course_context,$symb,$error,$$); } my $result = ''.$env{'user.name'}.''. ''.$env{'user.domain'}.''. @@ -105,6 +261,9 @@ sub packagemsg { ''.$env{'request.role'}.''. ''.$env{'request.filename'}.''. ''.$msgid.''; + if (defined($env{'form.group'})) { + $result .= ''.$env{'form.group'}.''; + } if (ref($recuser) eq 'ARRAY') { for (my $i=0; $i<@{$recuser}; $i++) { if ($type eq 'dcmail') { @@ -134,13 +293,66 @@ sub packagemsg { if (defined($attachmenturl)) { $result.= ''.$attachmenturl.''; } - return $msgid,$result; + if (defined($symb)) { + $result.= ''.$symb.''; + if ($course_context ne '') { + if ($course_context eq $env{'request.course.id'}) { + my $resource_title = &Apache::lonnet::gettitle($symb); + if (defined($resource_title)) { + $result .= ''.$resource_title.''; + } + } + } + } + if (defined($recipid)) { + $result.= ''.$recipid.''; + } + if ($env{'form.can_reply'} eq 'N') { + $result .= '1'; + } + if ($env{'form.reply_to_addr'}) { + my ($replytoname,$replytodom) = split(/:/,$env{'form.reply_to_addr'}); + if (!($replytoname eq $env{'user.name'} && $replytodom eq $env{'user.domain'})) { + if (&Apache::lonnet::homeserver($replytoname,$replytodom) ne 'no_host') { + $result .= ''.$env{'form.reply_to_addr'}.''; + } + } + } + return ($msgid,$result); +} + +sub get_course_context { + my $course_context; + my $msgkey; + if (defined($env{'form.replyid'})) { + $msgkey = $env{'form.replyid'}; + } elsif (defined($env{'form.forwid'})) { + $msgkey = $env{'form.forwid'} + } elsif (defined($env{'form.multiforwid'})) { + $msgkey = $env{'form.multiforwid'}; + } + if ($msgkey ne '') { + my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid)= + split(/\:/,&unescape($msgkey)); + $course_context = $origcid; + } + foreach my $key (keys(%env)) { + if ($key=~/^form\.(rep)?rec\_(.*)$/) { + my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid) = + split(/\:/,&unescape($2)); + $course_context = $origcid; + last; + } + } + if ($course_context eq '') { + $course_context = $env{'request.course.id'}; + } + return $course_context; } -# ================================================== Unpack message into a hash sub unpackagemsg { - my ($message,$notoken)=@_; + my ($message,$notoken,$noattachmentlink)=@_; my %content=(); my $parser=HTML::TokeParser->new(\$message); my $token; @@ -160,7 +372,7 @@ sub unpackagemsg { } } if (!exists($content{'recuser'})) { $content{'recuser'} = []; } - if ($content{'attachmenturl'}) { + if (($content{'attachmenturl'}) && (!$noattachmentlink)) { my ($fname)=($content{'attachmenturl'}=~m|/([^/]+)$|); if ($notoken) { $content{'message'}.='

'.&mt('Attachment').': '.$fname.''; @@ -175,22 +387,23 @@ sub unpackagemsg { return %content; } -# ======================================================= Get info out of msgid sub buildmsgid { - my ($now,$subject,$uname,$udom,$msgcount,$course_context,$pid) = @_; + my ($now,$subject,$uname,$udom,$msgcount,$course_context,$symb,$error,$pid) = @_; $subject=&escape($subject); + $symb = &escape($symb); return(&escape($now.':'.$subject.':'.$uname.':'. - $udom.':'.$msgcount.':'.$course_context.':'.$pid)); + $udom.':'.$msgcount.':'.$course_context.':'.$pid.':'.$symb.':'.$error)); } sub unpackmsgid { my ($msgid,$folder,$skipstatus,$status_cache)=@_; $msgid=&unescape($msgid); my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$fromcid, - $processid)=split(/\:/,&unescape($msgid)); + $processid,$symb,$error) = split(/\:/,&unescape($msgid)); $shortsubj = &unescape($shortsubj); $shortsubj = &HTML::Entities::decode($shortsubj); + $symb = &unescape($symb); if (!defined($processid)) { $fromcid = ''; } my %status=(); unless ($skipstatus) { @@ -203,27 +416,56 @@ sub unpackmsgid { if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; } unless ($status{$msgid}) { $status{$msgid}='new'; } } - return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid},$fromcid); + return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid},$fromcid,$symb,$error); } sub sendemail { - my ($to,$subject,$body)=@_; - my %senderemails=&Apache::loncommon::getemails(); + my ($to,$subject,$body,$to_uname,$to_udom,$user_lh)=@_; my $senderaddress=''; - foreach my $type ('notification','permanentemail','critnotification') { - if ($senderemails{$type}) { - $senderaddress=$senderemails{$type}; - } + my $replytoaddress=''; + if ($env{'form.can_reply'} eq 'N') { + my $lonhost = $Apache::lonnet::perlvar{'lonHostID'}; + my $hostname = &Apache::lonnet::hostname($lonhost); + $replytoaddress = 'do-not-reply@'.$hostname; + } else { + my %senderemails; + my $have_sender; + if ($env{'form.reply_to_addr'}) { + my ($replytoname,$replytodom) = split(/:/,$env{'form.reply_to_addr'}); + if (!($replytoname eq $env{'user.name'} && $replytodom eq $env{'user.domain'})) { + if (&Apache::lonnet::homeserver($replytoname,$replytodom) ne 'no_host') { + %senderemails = + &Apache::loncommon::getemails($replytoname,$replytodom); + $have_sender = 1; + } + } + } + if (!$have_sender) { + %senderemails=&Apache::loncommon::getemails(); + } + foreach my $type ('permanentemail','critnotification','notification') { + if ($senderemails{$type}) { + ($senderaddress) = split(/,/,$senderemails{$type}); + last if ($senderaddress); + } + } } $body= - "*** ".&mt('This is an automatic message generated by the LON-CAPA system.')."\n". - "*** ".($senderaddress?&mt('You can reply to this message'):&mt('Please do not reply to this address.')."\n*** ". - &mt('A reply will not be received by the recipient!'))."\n\n".$body; + "*** ".&mt_user($user_lh,'This is an automatic e-mail generated by the LON-CAPA system.')."\n". + "*** ".($senderaddress?&mt_user($user_lh,'You can reply to this e-mail'):&mt_user($user_lh,'Please do not reply to this address.')."\n*** ". + &mt_user($user_lh,'A reply will not be received by the recipient!'))."\n\n".$body; + my $msg = new Mail::Send; $msg->to($to); $msg->subject('[LON-CAPA] '.$subject); - if ($senderaddress) { $msg->add('Reply-to',$senderaddress); $msg->add('From',$senderaddress); } + if ($replytoaddress) { + $msg->add('Reply-to',$replytoaddress); + } + if ($senderaddress) { + $msg->add('From',$senderaddress); + } + $msg->add('Content-type','text/plain; charset=UTF-8'); if (my $fh = $msg->open()) { print $fh $body; $fh->close; @@ -233,54 +475,127 @@ sub sendemail { # ==================================================== Send notification emails sub sendnotification { - my ($to,$touname,$toudom,$subj,$crit,$text)=@_; + my ($to,$touname,$toudom,$subj,$crit,$text,$msgid)=@_; my $sender=$env{'environment.firstname'}.' '.$env{'environment.lastname'}; unless ($sender=~/\w/) { - $sender=$env{'user.name'}.'@'.$env{'user.domain'}; + $sender=$env{'user.name'}.':'.$env{'user.domain'}; } my $critical=($crit?' critical':''); + $text=~s/\<\;/\/gs; - $text=~s/\<\/*[^\>]+\>//gs; - my $url='http://'. - $Apache::lonnet::hostname{&Apache::lonnet::homeserver($touname,$toudom)}. - '/adm/email?username='.$touname.'&domain='.$toudom; - my $body=(<]+\>//gs; + $text = &HTML::Entities::decode($text); + $text = &Encode::encode('utf8',$text); + return $text; +} -to access the full message. -ENDMSG - &sendemail($to,'New'.$critical.' message from '.$sender,$body); +sub mynewmail{ + &newmail(); + return $env{'user.mailcheck.lastnewmessagetime'} > $env{'user.mailcheck.lastvisit'}; } -# ============================================================= Check for email + sub newmail { if ((time-$env{'user.mailcheck.time'})>300) { my %what=&Apache::lonnet::get('email_status',['recnewemail']); - &Apache::lonnet::appenv('user.mailcheck.time'=>time); + &Apache::lonnet::appenv({'user.mailcheck.time'=>time}); + &Apache::lonnet::appenv({'user.mailcheck.lastnewmessagetime'=> $what{'recnewemail'}}); if ($what{'recnewemail'}>0) { return 1; } } return 0; } -# =============================== Automated message to the author of a resource -=pod - -=item * B: Sends message $message to the owner - of the resource with the URI $filename. - -=cut sub author_res_msg { my ($filename,$message)=@_; @@ -302,7 +617,7 @@ sub author_res_msg { return 'no_host'; } -# =========================================== Retrieve author resource messages + sub retrieve_author_res_msg { my $url=shift; @@ -310,7 +625,7 @@ sub retrieve_author_res_msg { my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//); my %errormsgs=&Apache::lonnet::dump('nohist_res_msgs',$domain,$author); my $msgs=''; - foreach (keys %errormsgs) { + foreach (keys(%errormsgs)) { if ($_=~/^\Q$url\E\_\d+$/) { my %content=&unpackagemsg($errormsgs{$_}); $msgs.='

'. @@ -322,7 +637,8 @@ sub retrieve_author_res_msg { } -# =============================== Delete all author messages related to one URL + + sub del_url_author_res_msg { my $url=shift; @@ -336,7 +652,7 @@ sub del_url_author_res_msg { } return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author); } -# =================================== Clear out all author messages in URL path + sub clear_author_res_msg { my $url=shift; @@ -350,7 +666,8 @@ sub clear_author_res_msg { } return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author); } -# ================= Return hash with URLs for which there is a resource message + + sub all_url_author_res_msg { my ($author,$domain)=@_; @@ -362,7 +679,6 @@ sub all_url_author_res_msg { return %returnhash; } -# ====================================== Add a comment to the User Notes screen sub store_instructor_comment { my ($msg,$uname,$udom) = @_; @@ -371,41 +687,53 @@ sub store_instructor_comment { my $cdom = $env{'course.'.$cid.'.domain'}; my $subject= &mt('Record').' ['.$uname.':'.$udom.']'; my $result = &user_normal_msg_raw($cnum,$cdom,$subject,$msg); + if ($result eq 'ok' || $result eq 'con_delayed') { + + } return $result; } -# ================================================== Critical message to a user sub user_crit_msg_raw { - my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage)=@_; + my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage, + $nosentstore,$recipid,$attachmenturl)=@_; # Check if allowed missing - my $status=''; + my ($status,$packed_message); my $msgid='undefined'; unless (($message)&&($user)&&($domain)) { $status='empty'; }; my $text=$message; my $homeserver=&Apache::lonnet::homeserver($user,$domain); if ($homeserver ne 'no_host') { - ($msgid,$message)=&packagemsg($subject,$message); - if ($sendback) { $message.='true'; } - $status=&Apache::lonnet::critical( - 'put:'.$domain.':'.$user.':critical:'. - &escape($msgid).'='. - &escape($message),$homeserver); + ($msgid,$packed_message)=&packagemsg($subject,$message,undef,undef, + $attachmenturl,undef,undef,undef,undef,undef, + undef,undef,$recipid); + if ($sendback) { $packed_message.='true'; } + $status=&Apache::lonnet::cput('critical', {$msgid => $packed_message}, + $domain,$user); if (defined($sentmessage)) { - $$sentmessage = $message; + $$sentmessage = $packed_message; + } + if (!$nosentstore) { + (undef,my $packed_message_no_citation) = + &packagemsg($subject,$message,undef,undef,$attachmenturl,$user, + $domain,$msgid); + if ($status eq 'ok' || $status eq 'con_delayed') { + &store_sent_mail($msgid,$packed_message_no_citation); + } } } else { $status='no_host'; } + # Notifications my %userenv = &Apache::loncommon::getemails($user,$domain); if ($userenv{'critnotification'}) { &sendnotification($userenv{'critnotification'},$user,$domain,$subject,1, - $text); + $text,$msgid); } if ($toperm && $userenv{'permanentemail'}) { &sendnotification($userenv{'permanentemail'},$user,$domain,$subject,1, - $text); + $text,$msgid); } # Log this &Apache::lonnet::logthis( @@ -418,26 +746,11 @@ sub user_crit_msg_raw { return $status; } -# New routine that respects "forward" and calls old routine - -=pod - -=item * B: Sends - a critical message $message to the $user at $domain. If $sendback is true, - a reciept will be sent to the current user when $user recieves the message. - - Additionally it will check if the user has a Forwarding address - set, and send the message to that address instead - - returns - - in array context a list of results for each message that was sent - - in scalar context a space seperated list of results for each - message sent -=cut sub user_crit_msg { - my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage)=@_; + my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage, + $nosentstore,$recipid,$attachmenturl)=@_; my @status; my %userenv = &Apache::lonnet::get('environment',['msgforward'], $domain,$user); @@ -447,12 +760,14 @@ sub user_crit_msg { my ($forwuser,$forwdomain)=split(/\:/,$addr); push(@status, &user_crit_msg_raw($forwuser,$forwdomain,$subject,$message, - $sendback,$toperm,$sentmessage)); + $sendback,$toperm,$sentmessage,$nosentstore, + $recipid,$attachmenturl)); } } else { push(@status, &user_crit_msg_raw($user,$domain,$subject,$message,$sendback, - $toperm,$sentmessage)); + $toperm,$sentmessage,$nosentstore,$recipid, + $attachmenturl)); } if (wantarray) { return @status; @@ -460,20 +775,29 @@ sub user_crit_msg { return join(' ',@status); } -# =================================================== Critical message received sub user_crit_received { my $msgid=shift; my %message=&Apache::lonnet::get('critical',[$msgid]); my %contents=&unpackagemsg($message{$msgid},1); + my $destname = $contents{'sendername'}; + my $destdom = $contents{'senderdomain'}; + if ($contents{'replytoaddr'}) { + my ($repname,$repdom) = split(/:/,$contents{'replytoaddr'}); + if (&Apache::lonnet::homeserver($repname,$repdom) ne 'no_host') { + $destname = $repname; + $destdom = $repdom; + } + } my $status='rec: '.($contents{'sendback'}? - &user_normal_msg($contents{'sendername'},$contents{'senderdomain'}, - &mt('Receipt').': '.$env{'user.name'}.' '.&mt('at').' '.$env{'user.domain'}.', '.$contents{'subject'}, - &mt('User').' '.$env{'user.name'}.' '.&mt('at').' '.$env{'user.domain'}. - ' acknowledged receipt of message'."\n".' "'. - $contents{'subject'}.'"'."\n".&mt('dated').' '. - $contents{'time'}.".\n" - ):'no msg req'); + &user_normal_msg($destname,$destdom,&mt('Receipt').': '.$env{'user.name'}. + ' '.&mt('at').' '.$env{'user.domain'}.', '. + $contents{'subject'},&mt('User').' '.$env{'user.name'}. + ' '.&mt('at').' '.$env{'user.domain'}. + ' acknowledged receipt of message'."\n".' "'. + $contents{'subject'}.'"'."\n".&mt('dated').' '. + $contents{'time'}.".\n" + ):'no msg req'); $status.=' trans: '. &Apache::lonnet::put( 'nohist_email',{$contents{'msgid'} => $message{$msgid}}); @@ -486,11 +810,13 @@ sub user_crit_received { return $status; } -# ======================================================== Normal communication + + sub user_normal_msg_raw { my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl, - $toperm,$currid,$newid,$sentmessage,$crsmsgid)=@_; + $toperm,$currid,$newid,$sentmessage,$crsmsgid,$symb,$restitle, + $error,$nosentstore,$recipid)=@_; # Check if allowed missing my ($status,$packed_message); my $msgid='undefined'; @@ -501,107 +827,229 @@ sub user_normal_msg_raw { ($msgid,$packed_message)= &packagemsg($subject,$message,$citation,$baseurl, $attachmenturl,$user,$domain,$currid, - undef,$crsmsgid); + undef,$crsmsgid,$symb,$error,$recipid); # Store in user folder - $status=&Apache::lonnet::critical( - 'put:'.$domain.':'.$user.':nohist_email:'. - &escape($msgid).'='. - &escape($packed_message),$homeserver); + $status= + &Apache::lonnet::cput('nohist_email',{$msgid => $packed_message}, + $domain,$user); # Save new message received time &Apache::lonnet::put ('email_status',{'recnewemail'=>time},$domain,$user); -# Into sent-mail folder unless a broadcast message or critical message - unless (($env{'request.course.id'}) && - (($env{'form.sendmode'} eq 'group') || - (($env{'form.critmsg'}) || ($env{'form.sendbck'})) && - (&Apache::lonnet::allowed('srm',$env{'request.course.id'}) - || &Apache::lonnet::allowed('srm',$env{'request.course.id'}. - '/'.$env{'request.course.sec'})))) { - (undef, my $packed_message_no_citation)= - &packagemsg($subject,$message,undef ,$baseurl, - $attachmenturl,$user,$domain,$currid, - undef,$crsmsgid); - - $status .= &store_sent_mail($msgid,$packed_message_no_citation); +# Into sent-mail folder if sent mail storage required + if (!$nosentstore) { + (undef,my $packed_message_no_citation) = + &packagemsg($subject,$message,undef,$baseurl,$attachmenturl, + $user,$domain,$currid,undef,$crsmsgid,$symb,$error); + if ($status eq 'ok' || $status eq 'con_delayed') { + &store_sent_mail($msgid,$packed_message_no_citation); + } + } + if (ref($newid) eq 'SCALAR') { + $$newid = $msgid; + } + if (ref($sentmessage) eq 'SCALAR') { + $$sentmessage = $packed_message; } - } else { - $status='no_host'; - } - if (defined($newid)) { - $$newid = $msgid; - } - if (defined($sentmessage)) { - $$sentmessage = $packed_message; - } - # Notifications - my %userenv = &Apache::lonnet::get('environment',['notification', - 'permanentemail'], - $domain,$user); - if ($userenv{'notification'}) { - &sendnotification($userenv{'notification'},$user,$domain,$subject,0, - $text); - } - if ($toperm && $userenv{'permanentemail'}) { - &sendnotification($userenv{'permanentemail'},$user,$domain,$subject,0, - $text); - } - &Apache::lonnet::log($env{'user.domain'},$env{'user.name'}, - $env{'user.home'}, - 'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status); + my %userenv = &Apache::loncommon::getemails($user,$domain); + if ($userenv{'notification'}) { + &sendnotification($userenv{'notification'},$user,$domain,$subject,0, + $text,$msgid); + } + if ($toperm && $userenv{'permanentemail'}) { + if ((!$userenv{'notification'}) || ($userenv{'notification'} ne $userenv{'permanentemail'})) { + &sendnotification($userenv{'permanentemail'},$user,$domain,$subject,0, + $text,$msgid); + } + } + &Apache::lonnet::log($env{'user.domain'},$env{'user.name'}, + $env{'user.home'}, + 'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status); + } else { + $status='no_host'; + } return $status; } -# New routine that respects "forward" and calls old routine - -=pod - -=item * B: Sends a message to the - $user at $domain, with subject $subject and message $message. - -=cut - sub user_normal_msg { my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl, - $toperm,$sentmessage)=@_; - my $status=''; + $toperm,$sentmessage,$symb,$restitle,$error,$nosentstore,$recipid)=@_; + my @status; my %userenv = &Apache::lonnet::get('environment',['msgforward'], $domain,$user); my $msgforward=$userenv{'msgforward'}; if ($msgforward) { foreach (split(/\,/,$msgforward)) { my ($forwuser,$forwdomain)=split(/\:/,$_); - $status.= + push(@status, &user_normal_msg_raw($forwuser,$forwdomain,$subject,$message, $citation,$baseurl,$attachmenturl,$toperm, - undef,undef,$sentmessage).' '; + undef,undef,$sentmessage,undef,$symb, + $restitle,$error,$nosentstore,$recipid)); } - } else { - $status=&user_normal_msg_raw($user,$domain,$subject,$message, + } else { + push(@status,&user_normal_msg_raw($user,$domain,$subject,$message, $citation,$baseurl,$attachmenturl,$toperm, - undef,undef,$sentmessage); + undef,undef,$sentmessage,undef,$symb, + $restitle,$error,$nosentstore,$recipid)); + } + if (wantarray) { + return @status; + } + return join(' ',@status); +} + +sub process_sent_mail { + my ($msgsubj,$subj_prefix,$numsent,$stamp,$msgname,$msgdom,$msgcount, + $context,$pid,$savemsg,$recusers,$recudoms,$baseurl,$attachmenturl, + $symb,$error,$senderuname,$senderdom,$recipid) = @_; + my $sentsubj; + if ($numsent > 1) { + $sentsubj = $subj_prefix.' ('.$numsent.' sent) '.$msgsubj; + } else { + if ($subj_prefix) { + $sentsubj = $subj_prefix.' '; + } + $sentsubj .= $msgsubj; } + $sentsubj = &HTML::Entities::encode($sentsubj,'<>&"'); + my $sentmsgid = + &buildmsgid($stamp,$sentsubj,$msgname,$msgdom,$msgcount,$context,$pid); + (undef,my $sentmessage) = + &packagemsg($msgsubj,$savemsg,undef,$baseurl,$attachmenturl,$recusers, + $recudoms,$sentmsgid,undef,undef,$symb,$error,$recipid); + my $status = &store_sent_mail($sentmsgid,$sentmessage,$senderuname, + $senderdom); return $status; } sub store_sent_mail { - my ($msgid,$message) = @_; - my $status =' '.&Apache::lonnet::critical( - 'put:'.$env{'user.domain'}.':'.$env{'user.name'}. - ':nohist_email_sent:'. - &escape($msgid).'='. - &escape($message),$env{'user.home'}); + my ($msgid,$message,$senderuname,$senderdom) = @_; + if ($senderuname eq '') { + $senderuname = $env{'user.name'}; + } + if ($senderdom eq '') { + $senderdom = $env{'user.domain'}; + } + my $status =' '.&Apache::lonnet::cput('nohist_email_sent', + {$msgid => $message}, + $senderdom,$senderuname); return $status; } -# =============================================================== Folder suffix +sub store_recipients { + my ($subject,$sendername,$senderdom,$reciphash) = @_; + my $context = &get_course_context(); + my $now = time(); + my $msgcount = &get_uniq(); + my $recipid = + &buildmsgid($now,$subject,$sendername,$senderdom,$msgcount,$context,$$); + my %recipinfo = ( + $recipid => $reciphash, + ); + my $status = &Apache::lonnet::put('nohist_emailrecip',\%recipinfo, + $senderdom,$sendername); + if ($status eq 'ok') { + return ($recipid,$status); + } else { + return (undef,$status); + } +} + sub foldersuffix { my $folder=shift; unless ($folder) { return ''; } - return '_'.&escape($folder); + my $suffix; + my %folderhash = &get_user_folders($folder); + if (ref($folderhash{$folder}) eq 'HASH') { + $suffix = '_'.&escape($folderhash{$folder}{'id'}); + } else { + $suffix = '_'.&escape($folder); + } + return $suffix; +} + + +sub get_user_folders { + my ($folder) = @_; + my %userfolders = + &Apache::lonnet::dump('email_folders',undef,undef,$folder); + my $lock = "\0".'lock_counter'; # locks db while counter incremented + my $counter = "\0".'idcount'; # used in suffix for email db files + if (defined($userfolders{$lock})) { + delete($userfolders{$lock}); + } + if (defined($userfolders{$counter})) { + delete($userfolders{$counter}); + } + return %userfolders; +} + +sub secapply { + my $rec=shift; + my $defaultflag=shift; + $rec=~s/\s+//g; + unless ($rec =~ /\:/) { + $rec=~s/\@/\:/g; + } + my ($adr,$sections_or_groups)=($rec=~/^([^\(]+)\(([^\)]+)\)/); + if ($sections_or_groups) { + foreach my $item (split(/\;/,$sections_or_groups)) { + if (($item eq $env{'request.course.sec'}) || + ($defaultflag && ($item eq '*'))) { + return $adr; + } elsif ($env{'request.course.groups'}) { + my @usersgroups = split(/:/,$env{'request.course.groups'}); + if (grep(/^\Q$item\E$/,@usersgroups)) { + return $adr; + } + } + } + } else { + return $rec; + } + return ''; +} + +sub decide_receiver { + my ($feedurl,$author,$question,$course,$policy,$defaultflag) = @_; + &Apache::lonenc::check_decrypt(\$feedurl); + my $typestyle=''; + my %to=(); + if ($env{'form.discuss'} eq 'author' ||$author) { + $typestyle.='Submitting as Author Feedback
'; + $feedurl=~ m{^/res/($LONCAPA::domain_re)/($LONCAPA::username_re)/}; + $to{$2.':'.$1}=1; + } + my $cid = $env{'request.course.id'}; + if ($env{'form.discuss'} eq 'question' ||$question) { + $typestyle.=&mt('Submitting as Question').'
'; + foreach my $item (split(/\,/,$env{'course.'.$cid.'.question.email'})) { + my $rec=&secapply($item,$defaultflag); + if ($rec) { $to{$rec}=1; } + } + } + if ($env{'form.discuss'} eq 'course' ||$course) { + $typestyle.=&mt('Submitting as Comment').'
'; + foreach my $item (split(/\,/,$env{'course.'.$cid.'.comment.email'})) { + my $rec=&secapply($item,$defaultflag); + if ($rec) { $to{$rec}=1; } + } + } + if ($env{'form.discuss'} eq 'policy' ||$policy) { + $typestyle.=&mt('Submitting as Policy Feedback').'
'; + foreach my $item (split(/\,/,$env{'course.'.$cid.'.policy.email'})) { + my $rec=&secapply($item,$defaultflag); + if ($rec) { $to{$rec}=1; } + } + } + if ((scalar(%to) eq '0') && (!$defaultflag)) { + ($typestyle,%to)= + &decide_receiver($feedurl,$author,$question,$course,$policy,1); + } + return ($typestyle,%to); } 1;