--- loncom/interface/lonmsg.pm 2006/12/06 17:14:50 1.188 +++ loncom/interface/lonmsg.pm 2007/04/22 13:41:22 1.200 @@ -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.200 2007/04/22 13:41:22 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -28,6 +28,37 @@ 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 FUNCTIONS + +=over 4 + +=cut + use strict; use Apache::lonnet; use HTML::TokeParser(); @@ -47,7 +78,7 @@ use LONCAPA qw(:DEFAULT :match); sub packagemsg { my ($subject,$message,$citation,$baseurl,$attachmenturl, - $recuser,$recdomain,$msgid,$type,$crsmsgid)=@_; + $recuser,$recdomain,$msgid,$type,$crsmsgid,$symb,$error)=@_; $message =&HTML::Entities::encode($message,'<>&"'); $citation=&HTML::Entities::encode($citation,'<>&"'); $subject =&HTML::Entities::encode($subject,'<>&"'); @@ -78,7 +109,7 @@ sub packagemsg { 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'}.''. @@ -134,7 +165,18 @@ sub packagemsg { if (defined($attachmenturl)) { $result.= ''.$attachmenturl.''; } - return $msgid,$result; + if (defined($symb)) { + $result.= ''.$symb.''; + if (defined($course_context)) { + if ($course_context eq $env{'request.course.id'}) { + my $resource_title = &Apache::lonnet::gettitle($symb); + if (defined($resource_title)) { + $result .= ''.$resource_title.''; + } + } + } + } + return ($msgid,$result); } # ================================================== Unpack message into a hash @@ -178,19 +220,21 @@ sub unpackagemsg { # ======================================================= 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,7 +247,7 @@ 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); } @@ -233,7 +277,7 @@ 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'}; @@ -243,23 +287,38 @@ sub sendnotification { $text=~s/\>\;/\>/gs; $text=~s/\<\/*[^\>]+\>//gs; my $url='http://'. - $Apache::lonnet::hostname{&Apache::lonnet::homeserver($touname,$toudom)}. + &Apache::lonnet::hostname(&Apache::lonnet::homeserver($touname,$toudom)). '/adm/email?username='.$touname.'&domain='.$toudom; - my $body=(<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); + unless (($env{'request.course.id'}) && + (($env{'form.courserecord'}) && + (&Apache::lonnet::allowed('dff',$env{'request.course.id'}) + || &Apache::lonnet::allowed('dff',$env{'request.course.id'}. + '/'.$env{'request.course.sec'}))) || + (($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,$symb,$error); + if ($status eq 'ok' || $status eq 'con_delayed') { + &store_sent_mail($msgid,$packed_message_no_citation); + } + } + if (defined($newid)) { + $$newid = $msgid; + } + if (defined($sentmessage)) { + $$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::lonnet::get('environment',['notification', + 'permanentemail'], + $domain,$user); + if ($userenv{'notification'}) { + &sendnotification($userenv{'notification'},$user,$domain,$subject,0, + $text,$msgid); + } + if ($toperm && $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; } @@ -557,33 +629,44 @@ sub user_normal_msg_raw { =pod -=item * B: Sends a message to the - $user at $domain, with subject $subject and message $message. +=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 =cut sub user_normal_msg { my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl, - $toperm,$sentmessage)=@_; - my $status=''; + $toperm,$sentmessage,$symb,$restitle,$error)=@_; + 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)); } - } 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)); } - return $status; + if (wantarray) { + return @status; + } + return join(' ',@status); } sub store_sent_mail { @@ -601,9 +684,129 @@ sub store_sent_mail { 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; } +# ========================================================= User-defined folders + +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; + $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 ''; +} + +=pod + +=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 + +=cut + +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); +} + +=pod + +=back + +=cut + 1; __END__