--- loncom/interface/lonmsg.pm 2001/07/26 21:40:27 1.9 +++ loncom/interface/lonmsg.pm 2007/05/02 19:56:34 1.202 @@ -1,69 +1,196 @@ # The LearningOnline Network with CAPA -# # Routines for messaging # -# (Routines to control the menu +# $Id: lonmsg.pm,v 1.202 2007/05/02 19:56:34 raeburn Exp $ +# +# Copyright Michigan State University Board of Trustees +# +# This file is part of the LearningOnline Network with CAPA (LON-CAPA). +# +# LON-CAPA is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# LON-CAPA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# (TeX Conversion Module +# You should have received a copy of the GNU General Public License +# along with LON-CAPA; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# 05/29/00,05/30 Gerd Kortemeyer) +# /home/httpd/html/adm/gpl.txt # -# 10/05 Gerd Kortemeyer) +# http://www.lon-capa.org/ # -# 10/19,10/20,10/30, -# 02/06/01 Gerd Kortemeyer 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 vars qw($msgcount); -use HTML::TokeParser; -use Apache::Constants qw(:common); +use Apache::lonnet; +use HTML::TokeParser(); +use Apache::lonlocal; +use Mail::Send; +use LONCAPA qw(:DEFAULT :match); + +{ + my $uniq; + sub get_uniq { + $uniq++; + return $uniq; + } +} # ===================================================================== Package sub packagemsg { - my ($subject,$message,$citation)=@_; - $message=~s/\/\>\;/g; - $citation=~s/\/\>\;/g; - $subject=~s/\/\>\;/g; + my ($subject,$message,$citation,$baseurl,$attachmenturl, + $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 =&HTML::Entities::encode($baseurl,'<>&"'); + #remove machine specification + $attachmenturl =~ s|^http://[^/]+/|/|; + $attachmenturl =&HTML::Entities::encode($attachmenturl,'<>&"'); + my $course_context = &get_course_context(); my $now=time; - $msgcount++; - my $partsubj=$subject; - $partsubj=&Apache::lonnet::escape($partsubj); - $partsubj=substr($partsubj,0,50); - my $msgid=&Apache::lonnet::escape( - $now.':'.$partsubj.':'.$ENV{'user.name'}.':'. - $ENV{'user.domain'}.':'.$msgcount.':'.$$); - return $msgid, - ''.$ENV{'user.name'}.''. - ''.$ENV{'user.domain'}.''. + my $msgcount = &get_uniq(); + unless(defined($msgid)) { + $msgid = &buildmsgid($now,$subject,$env{'user.name'},$env{'user.domain'}, + $msgcount,$course_context,$symb,$error,$$); + } + my $result = ''.$env{'user.name'}.''. + ''.$env{'user.domain'}.''. ''.$subject.''. - ''. - ''.$ENV{'SERVER_NAME'}.''. + ''; + if (defined($crsmsgid)) { + $result.= ''.$course_context.''. + ''.$env{'request.course.sec'}.''. + ''.$msgid.''. + ''.$crsmsgid.''. + ''.$message.''; + return ($msgid,$result); + } + $result .= ''.$ENV{'SERVER_NAME'}.''. ''.$ENV{'HTTP_HOST'}.''. ''.$ENV{'REMOTE_ADDR'}.''. - ''.$ENV{'browser.type'}.''. - ''.$ENV{'browser.os'}.''. - ''.$ENV{'browser.version'}.''. - ''.$ENV{'browser.mathml'}.''. + ''.$env{'browser.type'}.''. + ''.$env{'browser.os'}.''. + ''.$env{'browser.version'}.''. + ''.$env{'browser.mathml'}.''. ''.$ENV{'HTTP_USER_AGENT'}.''. - ''.$ENV{'request.course.id'}.''. - ''.$ENV{'request.role'}.''. - ''.$ENV{'request.filename'}.''. - ''.$msgid.''. - ''.$message.''. - ''.$citation.''; + ''.$course_context.''. + ''.$env{'request.course.sec'}.''. + ''.$env{'request.role'}.''. + ''.$env{'request.filename'}.''. + ''.$msgid.''; + if (ref($recuser) eq 'ARRAY') { + for (my $i=0; $i<@{$recuser}; $i++) { + if ($type eq 'dcmail') { + my ($username,$email) = split(/:/,$$recuser[$i]); + $username = &unescape($username); + $email = &unescape($email); + $username = &HTML::Entities::encode($username,'<>&"'); + $email = &HTML::Entities::encode($email,'<>&"'); + $result .= ''. + $email.''; + } else { + $result .= ''.$$recuser[$i].''. + ''.$$recdomain[$i].''; + } + } + } else { + $result .= ''.$recuser.''. + ''.$recdomain.''; + } + $result .= ''.$message.''; + if (defined($citation)) { + $result.=''.$citation.''; + } + if (defined($baseurl)) { + $result.= ''.$baseurl.''; + } + if (defined($attachmenturl)) { + $result.= ''.$attachmenturl.''; + } + 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.''; + } + return ($msgid,$result); +} + +sub get_course_context { + 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; + } + } + if ($course_context eq '') { + $course_context = $env{'request.course.id'}; + } + return $course_context; } # ================================================== Unpack message into a hash sub unpackagemsg { - my $message=shift; + my ($message,$notoken)=@_; my %content=(); my $parser=HTML::TokeParser->new(\$message); my $token; @@ -71,7 +198,28 @@ sub unpackagemsg { if ($token->[0] eq 'S') { my $entry=$token->[1]; my $value=$parser->get_text('/'.$entry); - $content{$entry}=$value; + if (($entry eq 'recuser') || ($entry eq 'recdomain')) { + push(@{$content{$entry}},$value); + } elsif ($entry eq 'recipient') { + my $username = $token->[2]{'username'}; + $username = &HTML::Entities::decode($username,'<>&"'); + $content{$entry}{$username} = $value; + } else { + $content{$entry}=$value; + } + } + } + if (!exists($content{'recuser'})) { $content{'recuser'} = []; } + if ($content{'attachmenturl'}) { + my ($fname)=($content{'attachmenturl'}=~m|/([^/]+)$|); + if ($notoken) { + $content{'message'}.='

'.&mt('Attachment').': '.$fname.''; + } else { + &Apache::lonnet::allowuploaded('/adm/msg', + $content{'attachmenturl'}); + $content{'message'}.='

'.&mt('Attachment'). + ': '. + $fname.''; } } return %content; @@ -79,18 +227,128 @@ sub unpackagemsg { # ======================================================= Get info out of msgid +sub buildmsgid { + 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.':'.$symb.':'.$error)); +} + sub unpackmsgid { - my $msgid=&Apache::lonnet::unescape(shift); - my ($sendtime,$shortsubj,$fromname,$fromdomain)=split(/\:/, - &Apache::lonnet::unescape($msgid)); - my %status=&Apache::lonnet::get('email_status',[$msgid]); - if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; } - unless ($status{$msgid}) { $status{$msgid}='new'; } - return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid}); -} + my ($msgid,$folder,$skipstatus,$status_cache)=@_; + $msgid=&unescape($msgid); + my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$fromcid, + $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) { + if (ref($status_cache)) { + $status{$msgid} = $status_cache->{$msgid}; + } else { + my $suffix=&foldersuffix($folder); + %status=&Apache::lonnet::get('email_status'.$suffix,[$msgid]); + } + if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; } + unless ($status{$msgid}) { $status{$msgid}='new'; } + } + return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid},$fromcid,$symb,$error); +} + + +sub sendemail { + my ($to,$subject,$body)=@_; + my %senderemails=&Apache::loncommon::getemails(); + my $senderaddress=''; + foreach my $type ('notification','permanentemail','critnotification') { + if ($senderemails{$type}) { + $senderaddress=$senderemails{$type}; + } + } + $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; + 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 (my $fh = $msg->open()) { + print $fh $body; + $fh->close; + } +} + +# ==================================================== Send notification emails + +sub sendnotification { + 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'}; + } + 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 ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid, + $symb,$error) = &Apache::lonmsg::unpackmsgid($msgid); + my $coursetext; + if ($fromcid ne '') { + $coursetext = "\n".&mt('Course').': '; + if ($env{'course.'.$fromcid.'.description'} ne '') { + $coursetext .= $env{'course.'.$fromcid.'.description'}; + } else { + my %coursehash = &Apache::lonnet::coursedescription($fromcid,); + if ($coursehash{'description'} ne '') { + $coursetext .= $coursehash{'description'}; + } + } + $coursetext .= "\n\n"; + } + my $body = $coursetext. + &mt('You received a'.$critical.' message from [_1] in LON-CAPA.',$sender).' '.&mt('The subject is + + [_1] + +',$subj)."\n". +'=== '.&mt('Excerpt')." ============================================================ +$text +======================================================================== + +".&mt('Use + + [_1] + +to access the full message.',$url); + &sendemail($to,'New'.$critical.' message from '.$sender,$body); +} +# ============================================================= 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); + 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)=@_; unless ($message) { return 'empty'; } @@ -99,222 +357,510 @@ sub author_res_msg { my $homeserver=&Apache::lonnet::homeserver($author,$domain); if ($homeserver ne 'no_host') { my $id=unpack("%32C*",$message); + $message .= "

This error occurred on machine ". + $Apache::lonnet::perlvar{'lonHostID'}."

"; my $msgid; ($msgid,$message)=&packagemsg($filename,$message); return &Apache::lonnet::reply('put:'.$domain.':'.$author. ':nohist_res_msgs:'. - &Apache::lonnet::escape($filename.'_'.$id).'='. - &Apache::lonnet::escape($message),$homeserver); + &escape($filename.'_'.$id).'='. + &escape($message),$homeserver); } return 'no_host'; } +# =========================================== Retrieve author resource messages + +sub retrieve_author_res_msg { + my $url=shift; + $url=&Apache::lonnet::declutter($url); + my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//); + my %errormsgs=&Apache::lonnet::dump('nohist_res_msgs',$domain,$author); + my $msgs=''; + foreach (keys %errormsgs) { + if ($_=~/^\Q$url\E\_\d+$/) { + my %content=&unpackagemsg($errormsgs{$_}); + $msgs.='

'. + $content{'time'}.': '.$content{'message'}. + '

'; + } + } + return $msgs; +} + + +# =============================== Delete all author messages related to one URL + +sub del_url_author_res_msg { + my $url=shift; + $url=&Apache::lonnet::declutter($url); + my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//); + my @delmsgs=(); + foreach (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) { + if ($_=~/^\Q$url\E\_\d+$/) { + push (@delmsgs,$_); + } + } + 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; + $url=&Apache::lonnet::declutter($url); + my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//); + my @delmsgs=(); + foreach (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) { + if ($_=~/^\Q$url\E/) { + push (@delmsgs,$_); + } + } + 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)=@_; + my %returnhash=(); + foreach (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) { + $_=~/^(.+)\_\d+/; + $returnhash{$1}=1; + } + return %returnhash; +} + +# ====================================== Add a comment to the User Notes screen + +sub store_instructor_comment { + my ($msg,$uname,$udom) = @_; + my $cid = $env{'request.course.id'}; + my $cnum = $env{'course.'.$cid.'.num'}; + 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 { - my ($user,$domain,$subject,$message)=@_; +sub user_crit_msg_raw { + my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage, + $nosentstore,$recipid)=@_; # 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') { - my $msgid; - ($msgid,$message)=&packagemsg($subject,$message); + ($msgid,$packed_message)=&packagemsg($subject,$message,undef,undef, + undef,undef,undef,undef,undef,undef,undef, + undef,$recipid); + if ($sendback) { $packed_message.='true'; } $status=&Apache::lonnet::critical( 'put:'.$domain.':'.$user.':critical:'. - &Apache::lonnet::escape($msgid).'='. - &Apache::lonnet::escape($message),$homeserver); + &escape($msgid).'='. + &escape($packed_message),$homeserver); + if (defined($sentmessage)) { + $$sentmessage = $packed_message; + } + if (!$nosentstore) { + (undef,my $packed_message_no_citation) = + &packagemsg($subject,$message,undef,undef,undef,$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,$msgid); + } + if ($toperm && $userenv{'permanentemail'}) { + &sendnotification($userenv{'permanentemail'},$user,$domain,$subject,1, + $text,$msgid); + } +# Log this &Apache::lonnet::logthis( 'Sending critical email '.$msgid. ', log status: '. - &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'}, - $ENV{'user.home'}, + &Apache::lonnet::log($env{'user.domain'},$env{'user.name'}, + $env{'user.home'}, 'Sending critical '.$msgid.' to '.$user.' at '.$domain.' with status: ' .$status)); 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 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 + +=cut + +sub user_crit_msg { + my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage, + $nosentstore,$recipid)=@_; + my @status; + my %userenv = &Apache::lonnet::get('environment',['msgforward'], + $domain,$user); + my $msgforward=$userenv{'msgforward'}; + if ($msgforward) { + foreach my $addr (split(/\,/,$msgforward)) { + my ($forwuser,$forwdomain)=split(/\:/,$addr); + push(@status, + &user_crit_msg_raw($forwuser,$forwdomain,$subject,$message, + $sendback,$toperm,$sentmessage,$nosentstore, + $recipid)); + } + } else { + push(@status, + &user_crit_msg_raw($user,$domain,$subject,$message,$sendback, + $toperm,$sentmessage,$nosentstore,$recipid)); + } + if (wantarray) { + return @status; + } + return join(' ',@status); +} + # =================================================== Critical message received sub user_crit_received { - my $message=shift; - my %contents=&unpackagemsg($message); - my $status='rec: '. + my $msgid=shift; + my %message=&Apache::lonnet::get('critical',[$msgid]); + my %contents=&unpackagemsg($message{$msgid},1); + my $status='rec: '.($contents{'sendback'}? &user_normal_msg($contents{'sendername'},$contents{'senderdomain'}, - 'Receipt: '.$ENV{'user.name'}.' at '.$ENV{'user.domain'}, - 'User '.$ENV{'user.name'}.' at '.$ENV{'user.domain'}. - ' acknowledged receipt of message "'. - $contents{'subject'}.'" dated '.$contents{'time'}.".\n\n" - .'Message ID: '.$contents{'msgid'}); + &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); + &Apache::lonnet::put( + 'nohist_email',{$contents{'msgid'} => $message{$msgid}}); $status.=' del: '. &Apache::lonnet::del('critical',[$contents{'msgid'}]); - &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'}, - $ENV{'user.home'},'Received critical message '. + &Apache::lonnet::log($env{'user.domain'},$env{'user.name'}, + $env{'user.home'},'Received critical message '. $contents{'msgid'}. ', '.$status); + return $status; } # ======================================================== Normal communication -sub user_normal_msg { - my ($user,$domain,$subject,$message,$citation)=@_; +sub user_normal_msg_raw { + my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl, + $toperm,$currid,$newid,$sentmessage,$crsmsgid,$symb,$restitle, + $error,$nosentstore,$recipid)=@_; # Check if allowed missing - my $status=''; + my ($status,$packed_message); my $msgid='undefined'; + my $text=$message; unless (($message)&&($user)&&($domain)) { $status='empty'; }; my $homeserver=&Apache::lonnet::homeserver($user,$domain); if ($homeserver ne 'no_host') { - my $msgid; - ($msgid,$message)=&packagemsg($subject,$message,$citation); + ($msgid,$packed_message)= + &packagemsg($subject,$message,$citation,$baseurl, + $attachmenturl,$user,$domain,$currid, + undef,$crsmsgid,$symb,$error,$recipid); + +# Store in user folder $status=&Apache::lonnet::critical( 'put:'.$domain.':'.$user.':nohist_email:'. - &Apache::lonnet::escape($msgid).'='. - &Apache::lonnet::escape($message),$homeserver); - } else { + &escape($msgid).'='. + &escape($packed_message),$homeserver); +# Save new message received time + &Apache::lonnet::put + ('email_status',{'recnewemail'=>time},$domain,$user); +# 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; + } +# Notifications + 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'; - } - &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'}, - $ENV{'user.home'}, - 'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status); + } return $status; } -# =============================================================== Status Change +# New routine that respects "forward" and calls old routine -sub statuschange { - my ($msgid,$newstatus)=@_; - my %status=&Apache::lonnet::get('email_status',[$msgid]); - if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; } - unless ($status{$msgid}) { $status{$msgid}='new'; } - unless (($status{$msgid} eq 'replied') || - ($status{$msgid} eq 'forwarded')) { - &Apache::lonnet::put('email_status',($msgid => $newstatus)); - } -} -# ===================================================================== Handler - -sub handler { - my $r=shift; - -# ----------------------------------------------------------- Set document type - - $r->content_type('text/html'); - $r->send_http_header; - - return OK if $r->header_only; - -# --------------------------- Get query string for limited number of parameters - - map { - my ($name, $value) = split(/=/,$_); - $value =~ tr/+/ /; - $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; - if (($name eq 'display') || ($name eq 'replyto') || - ($name eq 'forward') || ($name eq 'mark') || - ($name eq 'sendreply')) { - unless ($ENV{'form.'.$name}) { - $ENV{'form.'.$name}=$value; - } - } - } (split(/&/,$ENV{'QUERY_STRING'})); +=pod -# --------------------------------------------------------------- Render Output - - $r->print('EMail and Messaging'); - $r->print( - ''); - $r->print('

EMail

'); - if ($ENV{'form.display'}) { - my $msgid=$ENV{'form.display'}; - &statuschange($msgid,'read'); - my %message=&Apache::lonnet::get('nohist_email',[$msgid]); - my %content=&unpackagemsg($message{$msgid}); - $r->print('Subject: '.$content{'subject'}. - '
From: '.$content{'sendername'}.' at '. - $content{'senderdomain'}. - '
Time: '.$content{'time'}.'
Functions: '. - 'Reply
'.
-             $content{'message'}.'

'.$content{'citation'}); - } elsif ($ENV{'form.replyto'}) { - my $msgid=$ENV{'form.replyto'}; - my %message=&Apache::lonnet::get('nohist_email',[$msgid]); - my %content=&unpackagemsg($message{$msgid}); - my $quotemsg='> '.$content{'message'}; - $quotemsg=~s/\r/\n/g; - $quotemsg=~s/\f/\n/g; - $quotemsg=~s/\n+/\n\> /g; - my $subject='Re: '.$content{'subject'}; - $r->print(<<"ENDREPLY"); -
- -Subject:

-

- -

-ENDREPLY - } elsif ($ENV{'form.sendreply'}) { - my $msgid=$ENV{'form.sendreply'}; - my %message=&Apache::lonnet::get('nohist_email',[$msgid]); - my %content=&unpackagemsg($message{$msgid}); - &statuschange($msgid,'replied'); - $r->print('Sending: '.&user_normal_msg($content{'sendername'}, - $content{'senderdomain'}, - $ENV{'form.subject'}, - $ENV{'form.message'})); - } elsif ($ENV{'form.forward'}) { - } elsif ($ENV{'form.mark'}) { - } else { - $r->print(''. - ''); - map { - my ($sendtime,$shortsubj,$fromname,$fromdomain,$status)= - &Apache::lonmsg::unpackmsgid($_); - if ($status eq 'new') { - $r->print(''); - } elsif ($status eq 'read') { - $r->print(''); - } elsif ($status eq 'replied') { - $r->print(''); - } else { - $r->print(''); +=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,$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(/\:/,$_); + push(@status, + &user_normal_msg_raw($forwuser,$forwdomain,$subject,$message, + $citation,$baseurl,$attachmenturl,$toperm, + undef,undef,$sentmessage,undef,$symb, + $restitle,$error,$nosentstore,$recipid)); } - $r->print(''); - } sort split(/\&/,&Apache::lonnet::reply('keys:'. - $ENV{'user.domain'}.':'. - $ENV{'user.name'}.':nohist_email', - $ENV{'user.home'})); - $r->print('
 DateUsernameDomainSubjectStatus
Open'.localtime($sendtime).''. - $fromname.''.$fromdomain.''. - &Apache::lonnet::unescape($shortsubj).''. - $status.'
'); - - } - $r->print(''); - return OK; + } else { + push(@status,&user_normal_msg_raw($user,$domain,$subject,$message, + $citation,$baseurl,$attachmenturl,$toperm, + 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,$senderhome) = @_; + my $sentsubj; + if ($numsent > 1) { + $sentsubj = $subj_prefix.' ('.$numsent.' sent) '.$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); + my $status = &store_sent_mail($sentmsgid,$sentmessage,$senderuname, + $senderdom,$senderhome); + return $status; } -# ================================================= Main program, reset counter -sub BEGIN { - $msgcount=0; +sub store_sent_mail { + my ($msgid,$message,$senderuname,$senderdom,$senderhome) = @_; + if ($senderuname eq '') { + $senderuname = $env{'user.name'}; + } + if ($senderdom eq '') { + $senderdom = $env{'user.domain'}; + } + if ($senderhome eq '') { + $senderhome = $env{'user.home'}; + } + my $status =' '.&Apache::lonnet::critical( + 'put:'.$senderdom.':'.$senderuname.':nohist_email_sent:'. + &escape($msgid).'='.&escape($message),$senderhome); + return $status; } -1; -__END__ +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); + } +} +# =============================================================== Folder suffix +sub foldersuffix { + my $folder=shift; + unless ($folder) { return ''; } + 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__ 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.