Diff for /loncom/interface/lonmsg.pm between versions 1.91 and 1.251

version 1.91, 2004/03/01 20:57:47 version 1.251, 2024/02/08 03:02:12
Line 26 Line 26
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
   
   
 package Apache::lonmsg;  package Apache::lonmsg;
   
 =pod  =pod
Line 37  Apache::lonmsg: supports internal messag Line 36  Apache::lonmsg: supports internal messag
   
 =head1 SYNOPSIS  =head1 SYNOPSIS
   
 lonmsg provides routines for sending messages, receiving messages, and  lonmsg provides routines for sending messages.
 a handler to allow users to read, send, and delete messages.  
   
 =head1 OVERVIEW  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.
   
 =head2 Messaging Overview  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 <recuser>$uname</recuser> and
   <recdomain>$udom</recdomain> in stored internal messages, compared
   with <recipient username="$uname:$udom">$email</recipient> in stored
   Domain Coordinator e-mail for the storage of information about
   recipients of the message/e-mail.
   
 X<messages>LON-CAPA provides an internal messaging system similar to  =head1 SUBROUTINES
 email, but customized for LON-CAPA's usage. LON-CAPA implements its  
 own messaging system, rather then building on top of email, because of  
 the features LON-CAPA messages can offer that conventional e-mail can  
 not:  
   
 =over 4  
   
 =item * B<Critical messages>: A message the recipient B<must>  
 acknowlegde receipt of before they are allowed to continue using the  
 system, preventing a user from claiming they never got a message  
   
 =item * B<Receipts>: LON-CAPA can reliably send reciepts informing the  
 sender that it has been read; again, useful for preventing students  
 from claiming they did not see a message. (While conventional e-mail  
 has some reciept support, it's sporadic, e-mail client-specific, and  
 generally the receiver can opt to not send one, making it useless in  
 this case.)  
   
 =item * B<Context>: LON-CAPA knows about the sender, such as where  
 they are in a course. When a student mails an instructor asking for  
 help on the problem, the instructor receives not just the student's  
 question, but all submissions the student has made up to that point,  
 the user's rendering of the problem, and the complete view the student  
 saw of the resource, including discussion up to that point. Finally,  
 the instructor is reading all of this inside of LON-CAPA, not their  
 email program, so they have full access to LON-CAPA's grading  
 interface, or other features they may wish to use in response to the  
 student's query.  
   
 =back  =over
   
 Users can ask LON-CAPA to forward messages to conventional e-mail  =pod
 addresses on their B<PREF> screen, but generally, LON-CAPA messages  
 are much more useful then traditional email can be made to be, even  
 with HTML support.  
   
 Right now, this document will cover just how to send a message, since  =item packagemsg()
 it is likely you will not need to programmatically read messages,  
 since lonmsg already implements that functionality.  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<author_res_msg($filename, $message)>: 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
   
 =head1 FUNCTIONS  =item store_instructor_comment()
   
 =over 4  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<user_crit_msg($user, $domain, $subject, $message, $sendback, $toperm, $sentmessage, $nosentstore, $recipid, $attachmenturl, $permresults, $senthide)>: 
       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<user_normal_msg($user, $domain, $subject, $message, $citation,
          $baseurl, $attachmenturl, $toperm, $sentmessage, $symb, $restitle,
          $error,$nosentstore,$recipid,$permresults,$senthide)>:
    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<decide_receiver($feedurl,$author,$question,$course,$policy,$defaultflag)>:
   
   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.<name>
       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  =cut
   
 use strict;  use strict;
 use Apache::lonnet();  use Apache::lonnet;
 use vars qw($msgcount);  use Apache::loncommon;
 use HTML::TokeParser();  use HTML::TokeParser();
 use Apache::Constants qw(:common);  
 use Apache::loncommon();  
 use Apache::lontexconvert();  
 use HTML::Entities();  
 use Mail::Send;  
 use Apache::lonlocal;  use Apache::lonlocal;
   use HTML::Entities;
   use Encode;
   use LONCAPA qw(:DEFAULT :match);
   
   {
       my $uniq;
       sub get_uniq {
    $uniq++;
    return $uniq;
       }
   }
   
 # Querystring component with sorting type  
 my $sqs;  
   
 # ===================================================================== Package  
   
 sub packagemsg {  sub packagemsg {
     my ($subject,$message,$citation,$baseurl,$attachmenturl)=@_;      my ($subject,$message,$citation,$baseurl,$attachmenturl,$recuser,$recdomain,
     $message =&HTML::Entities::encode($message);   $msgid,$type,$crsmsgid,$symb,$error,$recipid,$senthide,$origmsgid)=@_;
     $citation=&HTML::Entities::encode($citation);      $message =&HTML::Entities::encode($message,'<>&"');
     $subject =&HTML::Entities::encode($subject);      $citation=&HTML::Entities::encode($citation,'<>&"');
       $subject =&HTML::Entities::encode($subject,'<>&"');
     #remove machine specification      #remove machine specification
     $baseurl =~ s|^http://[^/]+/|/|;      $baseurl =~ s|^https?://[^/]+/|/|;
     $baseurl =&HTML::Entities::encode($baseurl);      $baseurl =&HTML::Entities::encode($baseurl,'<>&"');
     #remove machine specification      #remove machine specification
     $attachmenturl =~ s|^http://[^/]+/|/|;      $attachmenturl =~ s|^https?://[^/]+/|/|;
     $attachmenturl =&HTML::Entities::encode($attachmenturl);      $attachmenturl =&HTML::Entities::encode($attachmenturl,'<>&"');
       if ($senthide) {
           foreach my $item ($subject,$message) {
               if ($item ne '') {
                   $item = 'Not shown due to IP block';
               }
           }
           if ($attachmenturl ne '') {
               $attachmenturl = '';
           }
           if ($citation ne '') {
               $citation = '';
           }
           if ($msgid ne '') {
               $msgid = '';
           }
       }
       my $course_context = &get_course_context();
     my $now=time;      my $now=time;
     $msgcount++;      my $ip = &Apache::lonnet::get_requestor_ip();
     my $partsubj=$subject;      my $msgcount = &get_uniq();
     $partsubj=&Apache::lonnet::escape($partsubj);      unless(defined($msgid)) {
     my $msgid=&Apache::lonnet::escape(          $msgid = &buildmsgid($now,$subject,$env{'user.name'},$env{'user.domain'},
            $now.':'.$partsubj.':'.$ENV{'user.name'}.':'.                             $msgcount,$course_context,$symb,$error,$$);
            $ENV{'user.domain'}.':'.$msgcount.':'.$$);      }
     my $result='<sendername>'.$ENV{'user.name'}.'</sendername>'.      my $result = '<sendername>'.$env{'user.name'}.'</sendername>'.
            '<senderdomain>'.$ENV{'user.domain'}.'</senderdomain>'.             '<senderdomain>'.$env{'user.domain'}.'</senderdomain>'.
            '<subject>'.$subject.'</subject>'.             '<subject>'.$subject.'</subject>'.
    '<time>'.&Apache::lonlocal::locallocaltime($now).'</time>'.             '<time>'.&Apache::lonlocal::locallocaltime($now).'</time>';
    '<servername>'.$ENV{'SERVER_NAME'}.'</servername>'.      if (defined($crsmsgid)) {
           $result.= '<courseid>'.$course_context.'</courseid>'.
                     '<coursesec>'.$env{'request.course.sec'}.'</coursesec>'.
                     '<msgid>'.$msgid.'</msgid>'.
                     '<coursemsgid>'.$crsmsgid.'</coursemsgid>'.
                     '<message>'.$message.'</message>';
           return ($msgid,$result);
       }
       $result .= '<servername>'.$ENV{'SERVER_NAME'}.'</servername>'.
            '<host>'.$ENV{'HTTP_HOST'}.'</host>'.             '<host>'.$ENV{'HTTP_HOST'}.'</host>'.
    '<client>'.$ENV{'REMOTE_ADDR'}.'</client>'.     '<client>'.$ip.'</client>'.
    '<browsertype>'.$ENV{'browser.type'}.'</browsertype>'.     '<browsertype>'.$env{'browser.type'}.'</browsertype>'.
    '<browseros>'.$ENV{'browser.os'}.'</browseros>'.     '<browseros>'.$env{'browser.os'}.'</browseros>'.
    '<browserversion>'.$ENV{'browser.version'}.'</browserversion>'.     '<browserversion>'.$env{'browser.version'}.'</browserversion>'.
            '<browsermathml>'.$ENV{'browser.mathml'}.'</browsermathml>'.             '<browsermathml>'.$env{'browser.mathml'}.'</browsermathml>'.
    '<browserraw>'.$ENV{'HTTP_USER_AGENT'}.'</browserraw>'.     '<browserraw>'.$ENV{'HTTP_USER_AGENT'}.'</browserraw>'.
    '<courseid>'.$ENV{'request.course.id'}.'</courseid>'.     '<courseid>'.$course_context.'</courseid>'.
    '<coursesec>'.$ENV{'request.course.sec'}.'</coursesec>'.     '<coursesec>'.$env{'request.course.sec'}.'</coursesec>'.
    '<role>'.$ENV{'request.role'}.'</role>'.     '<role>'.$env{'request.role'}.'</role>'.
    '<resource>'.$ENV{'request.filename'}.'</resource>'.     '<resource>'.$env{'request.filename'}.'</resource>'.
            '<msgid>'.$msgid.'</msgid>'.             '<msgid>'.$msgid.'</msgid>';
    '<message>'.$message.'</message>';      if (defined($env{'form.group'})) {
           $result .= '<group>'.$env{'form.group'}.'</group>';
       }
       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 .= '<recipient username="'.$username.'">'.
                                               $email.'</recipient>';
               } else {
                   $result .= '<recuser>'.$$recuser[$i].'</recuser>'.
                              '<recdomain>'.$$recdomain[$i].'</recdomain>';
               }
           }
       } else {
           $result .= '<recuser>'.$recuser.'</recuser>'.
                      '<recdomain>'.$recdomain.'</recdomain>';
       }
       $result .= '<message>'.$message.'</message>';
     if (defined($citation)) {      if (defined($citation)) {
  $result.='<citation>'.$citation.'</citation>';   $result.='<citation>'.$citation.'</citation>';
     }      }
Line 153  sub packagemsg { Line 310  sub packagemsg {
     if (defined($attachmenturl)) {      if (defined($attachmenturl)) {
  $result.= '<attachmenturl>'.$attachmenturl.'</attachmenturl>';   $result.= '<attachmenturl>'.$attachmenturl.'</attachmenturl>';
     }      }
     return $msgid,$result;      if (defined($symb)) {
           $result.= '<symb>'.$symb.'</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>'.$resource_title.'</resource_title>';
                   }
               }
           }
       }
       if (defined($recipid)) {
           $result.= '<recipid>'.$recipid.'</recipid>';
       }
       if ($env{'form.can_reply'} eq 'N') {
           $result .= '<noreplies>1</noreplies>';
       }
       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 .= '<replytoaddr>'.$env{'form.reply_to_addr'}.'</replytoaddr>';
               }
           }
       }
       if ($senthide) {
           $result .= '<senthide>$origmsgid</senthide>';
       }
       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 {  sub unpackagemsg {
     my ($message,$notoken)=@_;      my ($message,$notoken,$noattachmentlink)=@_;
     my %content=();      my %content=();
     my $parser=HTML::TokeParser->new(\$message);      my $parser=HTML::TokeParser->new(\$message);
     my $token;      my $token;
Line 167  sub unpackagemsg { Line 380  sub unpackagemsg {
        if ($token->[0] eq 'S') {         if ($token->[0] eq 'S') {
    my $entry=$token->[1];     my $entry=$token->[1];
            my $value=$parser->get_text('/'.$entry);             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 ($content{'attachmenturl'}) {      if (!exists($content{'recuser'})) { $content{'recuser'} = []; }
        my ($fname,$ft)=($content{'attachmenturl'}=~/\/(\w+)\.(\w+)$/);      if (($content{'attachmenturl'}) && (!$noattachmentlink)) {
          my ($fname)=($content{'attachmenturl'}=~m|/([^/]+)$|);
        if ($notoken) {         if ($notoken) {
    $content{'message'}.='<p>'.&mt('Attachment').': <tt>'.$fname.'.'.$ft.'</tt>';     $content{'message'}.='<p>'.&mt('Attachment').': <tt>'.$fname.'</tt>';
        } else {         } else {
    $content{'message'}.='<p>'.&mt('Attachment').': <a href="'.     &Apache::lonnet::allowuploaded('/adm/msg',
        &Apache::lonnet::tokenwrapper($content{'attachmenturl'}).    $content{'attachmenturl'});
        '"><tt>'.$fname.'.'.$ft.'</tt></a>';     $content{'message'}.='<p>'.&mt('Attachment').
          ': <a href="'.$content{'attachmenturl'}.'"><tt>'.
          $fname.'</tt></a>';
        }         }
     }      }
     return %content;      return %content;
 }  }
   
 # ======================================================= 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 {  sub unpackmsgid {
     my $msgid=&Apache::lonnet::unescape(shift);      my ($msgid,$folder,$skipstatus,$status_cache,$onlycid)=@_;
     my ($sendtime,$shortsubj,$fromname,$fromdomain)=split(/\:/,      $msgid=&unescape($msgid);
                           &Apache::lonnet::unescape($msgid));      my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$fromcid,
     my %status=&Apache::lonnet::get('email_status',[$msgid]);          $processid,$symb,$error) = split(/\:/,&unescape($msgid));
     if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; }      if (!defined($processid)) { $fromcid = ''; }
     unless ($status{$msgid}) { $status{$msgid}='new'; }      if (($onlycid) && ($onlycid ne $fromcid)) {
     return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid});          return ($sendtime,'',$fromname,$fromdomain,'',$fromcid,'',$error);
 }       }
       $shortsubj = &unescape($shortsubj);
       $shortsubj = &HTML::Entities::decode($shortsubj);
       $symb = &unescape($symb);
       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 {  sub sendemail {
     my ($to,$subject,$body)=@_;      my ($to,$subject,$body,$to_uname,$to_udom,$user_lh,$attachmenturl)=@_;
       my $senderaddress='';
       my $replytoaddress='';
       my $msgsent;
       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,$setreplyto);
           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'})) {
                   $setreplyto = 1;
               } else {
                   if (&Apache::lonnet::homeserver($replytoname,$replytodom) ne 'no_host') {
                       %senderemails = 
                           &Apache::loncommon::getemails($replytoname,$replytodom);
                       $have_sender = 1;
                       $setreplyto = 1;
                   }
               }
           }
           if (!$have_sender) {
               %senderemails=&Apache::loncommon::getemails();
           }
           foreach my $type ('permanentemail','critnotification','notification') {
               if ($senderemails{$type}) {
                   ($senderaddress) = split(/,/,$senderemails{$type});
                   if ($senderaddress) {
                       if ($setreplyto) {
                           $replytoaddress = $senderaddress;
                       }
                       last;
                   }
               }
           }
       }
     $body=      $body=
     "*** ".&mt('This is an automatic message generated by the LON-CAPA system.')."\n".      "*** ".&mt_user($user_lh,'This is an automatic e-mail generated by the LON-CAPA system.')."\n".
     "*** ".&mt('Please do not reply to this address.')."\n\n".$body;      "*** ".($senderaddress?&mt_user($user_lh,'You can reply to this e-mail'):&mt_user($user_lh,'Please do not reply to this address.')."\n*** ".
     my $msg = new Mail::Send;      &mt_user($user_lh,'A reply will not be received by the recipient!'))."\n\n".$body;
     $msg->to($to);      
     $msg->subject('[LON-CAPA] '.$subject);      $attachmenturl = &Apache::lonnet::filelocation("",$attachmenturl);
     if (my $fh = $msg->open('smtp',Server => 'localhost')) {      my $filesize = (stat($attachmenturl))[7];
  print $fh $body;      if ($filesize > 1048576) {
  $fh->close;          # Don't send if it exceeds 1 MB.
           print '<p><span class="LC_error">' 
               .&mt('Email not sent.  Attachment exceeds permitted length.')
               .'</span><br /></p>';
       } else {
           # Otherwise build and send the email
           $subject = '[LON-CAPA] '.$subject;
           &Apache::loncommon::mime_email($senderaddress,$replytoaddress,$to,
                                          $subject,$body,'','',$attachmenturl,'','');
           $msgsent = 1;
     }      }
       return $msgsent;
 }  }
   
 # ==================================================== Send notification emails  # ==================================================== Send notification emails
   
 sub sendnotification {  sub sendnotification {
     my ($to,$touname,$toudom,$subj,$crit)=@_;      my ($to,$touname,$toudom,$subj,$crit,$text,$msgid,$attachmenturl)=@_;
     my $sender=$ENV{'environment.firstname'}.' '.$ENV{'environment.lastname'};      my $sender=$env{'environment.firstname'}.' '.$env{'environment.lastname'};
       unless ($sender=~/\w/) { 
    $sender=$env{'user.name'}.':'.$env{'user.domain'};
       }
     my $critical=($crit?' critical':'');      my $critical=($crit?' critical':'');
     my $url='http://'.      my $numsent = 0;
       $Apache::lonnet::hostname{&Apache::lonnet::homeserver($touname,$toudom)}.  
       '/adm/email?username='.$touname.'&domain='.$toudom;  
     my $body=(<<ENDMSG);  
 You received a$critical message from $sender in LON-CAPA. The subject is  
   
  $subj  
   
 Use      $text=~s/\&lt\;/\</gs;
       $text=~s/\&gt\;/\>/gs;
       my $touhome = &Apache::lonnet::homeserver($touname,$toudom);
       my $url = &Apache::lonnet::url_prefix('',$toudom,$touhome,'email').
                 '/adm/email?username='.$touname.'&domain='.$toudom.
                 '&display='.&escape($msgid);
       my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid,
           $symb,$error) = &Apache::lonmsg::unpackmsgid($msgid);
       my ($coursetext,$body,$bodybegin,$bodysubj,$bodyend);
       my $user_lh = &Apache::loncommon::user_lang($touname,$toudom,$fromcid);
       if ($fromcid ne '') {
           $coursetext = "\n".&mt_user($user_lh,'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 @recipients = split(/,/,$to);
       $bodybegin = $coursetext. 
                  &mt_user($user_lh,
                            'You received a'.$critical.' message from [_1] in LON-CAPA.',$sender).' ';
       $bodysubj = &mt_user($user_lh,'The subject is 
   
    [_1]
   
   ',$subj)."\n".
   '=== '.&mt_user($user_lh,'Excerpt')." ============================================================
   ";
       $bodyend = "
   ========================================================================
   
   ".&mt_user($user_lh,'Use 
   
    [_1]
   
   to access the full message.',$url);
       my %userenv = &Apache::lonnet::get('environment',['notifywithhtml'],$toudom,$touname);
       my $subject = &mt_user($user_lh,"'New'$critical message from [_1]",$sender);
       unless ($subj eq '') {
           $subject = $subj;
       }
    
       my ($blocked,$blocktext,$clientip);
       $clientip = &Apache::lonnet::get_requestor_ip();
       if (!$crit) {
           my %setters;
           my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = 
               &Apache::loncommon::blockcheck(\%setters,'com',$clientip,$touname,$toudom);
           if ($startblock && $endblock) {
               $blocked = 1;
               my $showstart = &Apache::lonlocal::locallocaltime($startblock);
               my $showend = &Apache::lonlocal::locallocaltime($endblock);
               $blocktext = &mt_user($user_lh,'LON-CAPA messages sent to you between [_1] and [_2] will be inaccessible until the end of this time period, because you are a student in a course with an active communications block.',$showstart,$showend);
           } elsif ($by_ip) {
               $blocked = 1;
               $blocktext = &mt_user($user_lh,'LON-CAPA messages sent to you will be inaccessible from your IP address [_1], because communication is being blocked for certain IP address(es).',$clientip);
           }
       }
       if ($userenv{'notifywithhtml'} ne '') {
           my @htmlexcerpt = split(/,/,$userenv{'notifywithhtml'});
           my $htmlfree = &make_htmlfree($text);
           foreach my $addr (@recipients) {
               if ($blocked) {
                   $body = $bodybegin."\n".$blocktext."\n".$bodyend;
               } else {
                   my $sendtext;
                   if (!grep/^\Q$addr\E/,@htmlexcerpt) {
                       $sendtext = $htmlfree;
                   } else {
                       $sendtext = $text;
                   }
                   $body = $bodybegin.$bodysubj.$sendtext.$bodyend;
               }
               if (&sendemail($addr,$subject,$body,$touname,$toudom,$user_lh,$attachmenturl)) {
                   $numsent ++;
               }
           }
       } else {
           if ($blocked) {
               $body = $bodybegin."\n".$blocktext."\n".$bodyend;
           } else {
               my $htmlfree = &make_htmlfree($text);
               $body = $bodybegin.$bodysubj.$htmlfree.$bodyend;
           }
           if (&sendemail($to,$subject,$body,$touname,$toudom,$user_lh,$attachmenturl)) {
               $numsent ++;
           }
       }
       return $numsent;
   }
   
  $url  sub make_htmlfree {
       my ($text) = @_;
       $text =~ s/\<\/*[^\>]+\>//gs;
       $text = &HTML::Entities::decode($text);
       $text = &Encode::encode('utf8',$text);
       return $text;
   }
   
 to access this message.  sub mynewmail{
 ENDMSG   &newmail();
     &sendemail($to,'New'.$critical.' message from '.$sender,$body);   return $env{'user.mailcheck.lastnewmessagetime'} > $env{'user.mailcheck.lastvisit'};
 }  }
 # ============================================================= Check for email  
   
 sub newmail {  sub newmail {
     if ((time-$ENV{'user.mailcheck.time'})>300) {      if ((time-$env{'user.mailcheck.time'})>300) {
         my %what=&Apache::lonnet::get('email_status',['recnewemail']);          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; }          if ($what{'recnewemail'}>0) { return 1; }
     }      }
     return 0;      return 0;
 }  }
   
 # =============================== Automated message to the author of a resource  
   
 =pod  
   
 =item * B<author_res_msg($filename, $message)>: Sends message $message to the owner  
     of the resource with the URI $filename.  
   
 =cut  
   
 sub author_res_msg {  sub author_res_msg {
     my ($filename,$message)=@_;      my ($filename,$message)=@_;
Line 260  sub author_res_msg { Line 646  sub author_res_msg {
     my $homeserver=&Apache::lonnet::homeserver($author,$domain);      my $homeserver=&Apache::lonnet::homeserver($author,$domain);
     if ($homeserver ne 'no_host') {      if ($homeserver ne 'no_host') {
        my $id=unpack("%32C*",$message);         my $id=unpack("%32C*",$message);
          $message .= " <p>This error occurred on machine ".
      $Apache::lonnet::perlvar{'lonHostID'}."</p>";
        my $msgid;         my $msgid;
        ($msgid,$message)=&packagemsg($filename,$message);         ($msgid,$message)=&packagemsg($filename,$message);
        return &Apache::lonnet::reply('put:'.$domain.':'.$author.         return &Apache::lonnet::reply('put:'.$domain.':'.$author.
          ':nohist_res_msgs:'.           ':nohist_res_msgs:'.
           &Apache::lonnet::escape($filename.'_'.$id).'='.            &escape($filename.'_'.$id).'='.
           &Apache::lonnet::escape($message),$homeserver);            &escape($message),$homeserver);
     }      }
     return 'no_host';      return 'no_host';
 }  }
   
 # =========================================== Retrieve author resource messages  
   
 sub retrieve_author_res_msg {  sub retrieve_author_res_msg {
     my $url=shift;      my $url=shift;
     $url=&Apache::lonnet::declutter($url);      $url=&Apache::lonnet::declutter($url);
     my ($domain,$author)=($url=~/^(\w+)\/(\w+)\//);      my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//);
     my %errormsgs=&Apache::lonnet::dump('nohist_res_msgs',$domain,$author);      my %errormsgs=&Apache::lonnet::dump('nohist_res_msgs',$domain,$author);
     my $msgs='';      my $msgs='';
     foreach (keys %errormsgs) {      foreach my $msg (keys(%errormsgs)) {
  if ($_=~/^\Q$url\E\_\d+$/) {   if ($msg =~ /^\Q$url\E\_\d+$/) {
     my %content=&unpackagemsg($errormsgs{$_});      my %content=&unpackagemsg($errormsgs{$msg});
     $msgs.='<p><img src="/adm/lonMisc/bomb.gif" /><b>'.      $msgs.='<p><img src="/adm/lonMisc/bomb.gif" /><b>'.
  $content{'time'}.'</b>: '.$content{'message'}.   $content{'time'}.'</b>: '.$content{'message'}.
  '<br /></p>';   '<br /></p>';
Line 290  sub retrieve_author_res_msg { Line 678  sub retrieve_author_res_msg {
 }  }
   
   
 # =============================== Delete all author messages related to one URL  
   
   
 sub del_url_author_res_msg {  sub del_url_author_res_msg {
     my $url=shift;      my $url=shift;
     $url=&Apache::lonnet::declutter($url);      $url=&Apache::lonnet::declutter($url);
     my ($domain,$author)=($url=~/^(\w+)\/(\w+)\//);      my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//);
     my @delmsgs=();      my @delmsgs=();
     foreach (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {      foreach my $msg (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {
  if ($_=~/^\Q$url\E\_\d+$/) {   if ($msg =~ /^\Q$url\E\_\d+$/) {
     push (@delmsgs,$_);      push (@delmsgs,$msg);
  }   }
     }      }
     return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author);      return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author);
 }  }
   
 # ================= Return hash with URLs for which there is a resource message  
   sub clear_author_res_msg {
       my $url=shift;
       $url=&Apache::lonnet::declutter($url);
       my ($domain,$author)=($url=~/^($match_domain)\/($match_username)\//);
       my @delmsgs=();
       foreach my $msg (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {
    if ($msg =~ /^\Q$url\E/) {
       push (@delmsgs,$msg);
    }
       }
       return &Apache::lonnet::del('nohist_res_msgs',\@delmsgs,$domain,$author);
   }
   
   
   
 sub all_url_author_res_msg {  sub all_url_author_res_msg {
     my ($author,$domain)=@_;      my ($author,$domain)=@_;
     my %returnhash=();      my %returnhash=();
     foreach (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {      foreach my $msg (&Apache::lonnet::getkeys('nohist_res_msgs',$domain,$author)) {
  $_=~/^(.+)\_\d+/;   $msg =~ /^(.+)\_\d+/;
  $returnhash{$1}=1;   $returnhash{$1}=1;
     }      }
     return %returnhash;      return %returnhash;
 }  }
   
 # ================================================== Critical message to a user  
   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;
   }
   
   
 sub user_crit_msg_raw {  sub user_crit_msg_raw {
     my ($user,$domain,$subject,$message,$sendback)=@_;      my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage,
           $nosentstore,$recipid,$attachmenturl,$permresults,$senthide)=@_;
 # Check if allowed missing  # Check if allowed missing
     my $status='';      my ($status,$packed_message);
     my $msgid='undefined';      my $msgid='undefined';
     unless (($message)&&($user)&&($domain)) { $status='empty'; };      unless (($message)&&($user)&&($domain)) { $status='empty'; };
       my $text=$message;
     my $homeserver=&Apache::lonnet::homeserver($user,$domain);      my $homeserver=&Apache::lonnet::homeserver($user,$domain);
     if ($homeserver ne 'no_host') {      if ($homeserver ne 'no_host') {
        ($msgid,$message)=&packagemsg($subject,$message);         ($msgid,$packed_message)=&packagemsg($subject,$message,undef,undef,
        if ($sendback) { $message.='<sendback>true</sendback>'; }                                    $attachmenturl,undef,undef,undef,undef,undef,
        $status=&Apache::lonnet::critical(                                    undef,undef,$recipid);
            'put:'.$domain.':'.$user.':critical:'.         if ($sendback) { $packed_message.='<sendback>true</sendback>'; }
            &Apache::lonnet::escape($msgid).'='.         $status=&Apache::lonnet::cput('critical', {$msgid => $packed_message},
            &Apache::lonnet::escape($message),$homeserver);       $domain,$user);
        if ($ENV{'request.course.id'}) {          if (defined($sentmessage)) {
           &user_normal_msg_raw(              $$sentmessage = $packed_message;
             $ENV{'course.'.$ENV{'request.course.id'}.'.num'},          }
             $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},          if (!$nosentstore) {
             'Critical ['.$user.':'.$domain.']',              my ($sentmsgid,$packed_message_no_citation) =
     $message);              &packagemsg($subject,$message,undef,undef,$attachmenturl,$user,
        }                          $domain,$msgid,undef,undef,undef,undef,undef,$senthide,$msgid);
               if ($status eq 'ok' || $status eq 'con_delayed') {
                   if ($senthide && $sentmsgid) {
                       &store_sent_mail($sentmsgid,$packed_message_no_citation);
                   } else {
                       &store_sent_mail($msgid,$packed_message_no_citation);
                   }
               }
           }
     } else {      } else {
        $status='no_host';         $status='no_host';
     }      }
   
 # Notifications  # Notifications
     my %userenv = &Apache::lonnet::get('environment',['critnotification'],      my %userenv = &Apache::loncommon::getemails($user,$domain);
                                        $domain,$user);      my $critnotify = $userenv{'critnotification'};
     if ($userenv{'critnotification'}) {      my $permemail = $userenv{'permanentemail'};
       &sendnotification($userenv{'critnotification'},$user,$domain,$subject,1);      my $numcrit = 0;
       my $numperm = 0;
       my $permlogmsgstatus;
       if ($critnotify) {
           $numcrit = &sendnotification($critnotify,$user,$domain,$subject,1,$text,$msgid,$attachmenturl);
       }
       if ($toperm && $permemail) {
           if ($critnotify && $numcrit) {
               if (grep(/^\Q$permemail\E/,split(/,/,$critnotify))) {
                   $numperm = 1;
               }
           }
           unless ($numperm) {
               $numperm = &sendnotification($permemail,$user,$domain,$subject,1,$text,$msgid,$attachmenturl);
           }
       }
       if ($toperm) {
           $permlogmsgstatus = '. Perm. email log status '.
                               &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
                                                    "Perm. e-mail count $numperm for $user at $domain");
           if (ref($permresults) eq 'HASH') {
               $permresults->{"$user:$domain"} = $numperm;
           }
     }      }
 # Log this  # Log this
     &Apache::lonnet::logthis(      &Apache::lonnet::logthis(
       'Sending critical email '.$msgid.        'Sending critical '.$msgid.
       ', log status: '.        ', log status: '.
       &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},        &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
                          $ENV{'user.home'},                           $env{'user.home'},
       'Sending critical '.$msgid.' to '.$user.' at '.$domain.' with status: '        'Sending critical '.$msgid.' to '.$user.' at '.$domain.' with status '
       .$status));        .$status).$permlogmsgstatus);
     return $status;      return $status;
 }  }
   
 # New routine that respects "forward" and calls old routine  
   
 =pod  
   
 =item * B<user_crit_msg($user, $domain, $subject, $message, $sendback)>: 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.  
   
 =cut  
   
 sub user_crit_msg {  sub user_crit_msg {
     my ($user,$domain,$subject,$message,$sendback)=@_;      my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage,
     my $status='';          $nosentstore,$recipid,$attachmenturl,$permresults,$senthide)=@_;
       my @status;
     my %userenv = &Apache::lonnet::get('environment',['msgforward'],      my %userenv = &Apache::lonnet::get('environment',['msgforward'],
                                        $domain,$user);                                         $domain,$user);
     my $msgforward=$userenv{'msgforward'};      my $msgforward=$userenv{'msgforward'};
     if ($msgforward) {      if ($msgforward) {
        foreach (split(/\,/,$msgforward)) {         foreach my $addr (split(/\,/,$msgforward)) {
  my ($forwuser,$forwdomain)=split(/\:/,$_);   my ($forwuser,$forwdomain)=split(/\:/,$addr);
          $status.=           push(@status,
    &user_crit_msg_raw($forwuser,$forwdomain,$subject,$message,        &user_crit_msg_raw($forwuser,$forwdomain,$subject,$message,
                 $sendback).' ';   $sendback,$toperm,$sentmessage,$nosentstore,
                                    $recipid,$attachmenturl,$permresults,$senthide));
        }         }
     } else {       } else { 
  $status=&user_crit_msg_raw($user,$domain,$subject,$message,$sendback);   push(@status,
        &user_crit_msg_raw($user,$domain,$subject,$message,$sendback,
    $toperm,$sentmessage,$nosentstore,$recipid,
                                   $attachmenturl,$permresults,$senthide));
     }      }
     return $status;      if (wantarray) {
    return @status;
       }
       return join(' ',@status);
 }  }
   
 # =================================================== Critical message received  
   
 sub user_crit_received {  sub user_crit_received {
     my $msgid=shift;      my $msgid=shift;
     my %message=&Apache::lonnet::get('critical',[$msgid]);      my %message=&Apache::lonnet::get('critical',[$msgid]);
     my %contents=&unpackagemsg($message{$msgid},1);      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'}?      my $status='rec: '.($contents{'sendback'}?
      &user_normal_msg($contents{'sendername'},$contents{'senderdomain'},       &user_normal_msg($destname,$destdom,&mt('Receipt').': '.$env{'user.name'}.
                      &mt('Receipt').': '.$ENV{'user.name'}.' '.&mt('at').' '.$ENV{'user.domain'}.', '.$contents{'subject'},                        ' '.&mt('at').' '.$env{'user.domain'}.', '.
                      &mt('User').' '.$ENV{'user.name'}.' '.&mt('at').' '.$ENV{'user.domain'}.                        $contents{'subject'},&mt('User').' '.$env{'user.name'}.
                      ' acknowledged receipt of message'."\n".'   "'.                        ' '.&mt('at').' '.$env{'user.domain'}.
                      $contents{'subject'}.'"'."\n".&mt('dated').' '.                        ' acknowledged receipt of message'."\n".'   "'.
                      $contents{'time'}.".\n"                        $contents{'subject'}.'"'."\n".&mt('dated').' '.
                      ):'no msg req');                        $contents{'time'}.".\n"
                         ):'no msg req');
     $status.=' trans: '.      $status.=' trans: '.
      &Apache::lonnet::put(       &Apache::lonnet::put(
      'nohist_email',{$contents{'msgid'} => $message{$msgid}});       'nohist_email',{$contents{'msgid'} => $message{$msgid}});
     $status.=' del: '.      $status.=' del: '.
      &Apache::lonnet::del('critical',[$contents{'msgid'}]);       &Apache::lonnet::del('critical',[$contents{'msgid'}]);
     &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},      &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
                          $ENV{'user.home'},'Received critical message '.                           $env{'user.home'},'Received critical message '.
                          $contents{'msgid'}.                           $contents{'msgid'}.
                          ', '.$status);                           ', '.$status);
     return $status;      return $status;
 }  }
   
 # ======================================================== Normal communication  
   
   
 sub user_normal_msg_raw {  sub user_normal_msg_raw {
     my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl)=@_;      my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl,
           $toperm,$currid,$newid,$sentmessage,$crsmsgid,$symb,$restitle,
           $error,$nosentstore,$recipid,$permresults,$senthide)=@_;
 # Check if allowed missing  # Check if allowed missing
     my $status='';      my ($status,$packed_message);
     my $msgid='undefined';      my $msgid='undefined';
       my $text=$message;
     unless (($message)&&($user)&&($domain)) { $status='empty'; };      unless (($message)&&($user)&&($domain)) { $status='empty'; };
     my $homeserver=&Apache::lonnet::homeserver($user,$domain);      my $homeserver=&Apache::lonnet::homeserver($user,$domain);
     if ($homeserver ne 'no_host') {      if ($homeserver ne 'no_host') {
        ($msgid,$message)=&packagemsg($subject,$message,$citation,$baseurl,         ($msgid,$packed_message)=
                                      $attachmenturl);                   &packagemsg($subject,$message,$citation,$baseurl,
        $status=&Apache::lonnet::critical(                                       $attachmenturl,$user,$domain,$currid,
            'put:'.$domain.':'.$user.':nohist_email:'.                                       undef,$crsmsgid,$symb,$error,$recipid);
            &Apache::lonnet::escape($msgid).'='.  
            &Apache::lonnet::escape($message),$homeserver);  # Store in user folder
          $status=
      &Apache::lonnet::cput('nohist_email',{$msgid => $packed_message},
    $domain,$user);
   # Save new message received time
        &Apache::lonnet::put         &Apache::lonnet::put
                          ('email_status',{'recnewemail'=>time},$domain,$user);                           ('email_status',{'recnewemail'=>time},$domain,$user);
     } else {  # Into sent-mail folder if sent mail storage required
        $status='no_host';         if (!$nosentstore) {
     }             my ($sentmsgid,$packed_message_no_citation) =
                  &packagemsg($subject,$message,undef,$baseurl,$attachmenturl,
                              $user,$domain,$currid,undef,$crsmsgid,$symb,$error,
                              undef,$senthide,$msgid);
              if ($status eq 'ok' || $status eq 'con_delayed') {
                  if ($senthide && $sentmsgid) {
                      &store_sent_mail($sentmsgid,$packed_message_no_citation);
                  } else {
                      &store_sent_mail($msgid,$packed_message_no_citation);
                  }
              }
          }
          if (ref($newid) eq 'SCALAR') {
      $$newid = $msgid;
          }
          if (ref($sentmessage) eq 'SCALAR') {
      $$sentmessage = $packed_message;
          }
 # Notifications  # Notifications
     my %userenv = &Apache::lonnet::get('environment',['notification'],         my %userenv = &Apache::loncommon::getemails($user,$domain);
                                        $domain,$user);         my $notify = $userenv{'notification'};
     if ($userenv{'notification'}) {         my $permemail = $userenv{'permanentemail'};
  &sendnotification($userenv{'notification'},$user,$domain,$subject,0);         my $numnotify = 0;
     }         my $numperm = 0;
     &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},         my $permlogmsgstatus;
                          $ENV{'user.home'},         if ($notify) {
       'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status);             $numnotify = &sendnotification($notify,$user,$domain,$subject,0,$text,$msgid,$attachmenturl);
          }
          if ($toperm && $permemail) {
              if ($notify && $numnotify) {
                  if (grep(/^\Q$permemail\E/,split(/,/,$notify))) {
                      $numperm = 1;
                  }
              }
              unless ($numperm) {
                  $numperm = &sendnotification($permemail,$user,$domain,$subject,0,
                                               $text,$msgid,$attachmenturl);
              }
          }
          if ($toperm) {
              $permlogmsgstatus = '. Perm. email log status '.
                            &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},$env{'user.home'},
                                                 "Perm. e-mail count $numperm for $user at $domain");
              if (ref($permresults) eq 'HASH') {
                  $permresults->{"$user:$domain"} = $numperm;
              }
          }
          &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
       $env{'user.home'},
       'Sending '.$msgid.' to '.$user.' at '.$domain.' with status '.$status.
                               $permlogmsgstatus);
      } else {
          $status='no_host';
      }
     return $status;      return $status;
 }  }
   
 # New routine that respects "forward" and calls old routine  
   
 =pod  
   
 =item * B<user_normal_msg($user, $domain, $subject, $message,  
     $citation, $baseurl, $attachmenturl)>: Sends a message to the  
     $user at $domain, with subject $subject and message $message.  
   
 =cut  
   
 sub user_normal_msg {  sub user_normal_msg {
     my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl)=@_;      my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl,
     my $status='';   $toperm,$sentmessage,$symb,$restitle,$error,$nosentstore,$recipid,
           $permresults,$senthide)=@_;
       my @status;
     my %userenv = &Apache::lonnet::get('environment',['msgforward'],      my %userenv = &Apache::lonnet::get('environment',['msgforward'],
                                        $domain,$user);                                         $domain,$user);
     my $msgforward=$userenv{'msgforward'};      my $msgforward=$userenv{'msgforward'};
     if ($msgforward) {      if ($msgforward) {
        foreach (split(/\,/,$msgforward)) {          foreach my $fwd (split(/\,/,$msgforward)) {
  my ($forwuser,$forwdomain)=split(/\:/,$_);      my ($forwuser,$forwdomain)=split(/\:/,$fwd);
          $status.=      push(@status,
   &user_normal_msg_raw($forwuser,$forwdomain,$subject,$message,          &user_normal_msg_raw($forwuser,$forwdomain,$subject,$message,
        $citation,$baseurl,$attachmenturl).' ';       $citation,$baseurl,$attachmenturl,$toperm,
        }       undef,undef,$sentmessage,undef,$symb,
     } else {                                        $restitle,$error,$nosentstore,$recipid,
  $status=&user_normal_msg_raw($user,$domain,$subject,$message,                                       $permresults,$senthide));
      $citation,$baseurl,$attachmenturl);  
     }  
     return $status;  
 }  
   
   
 # =============================================================== Status Change  
   
 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});  
     }  
     if (($newstatus eq 'deleted') || ($newstatus eq 'new')) {  
  &Apache::lonnet::put('email_status',{$msgid => $newstatus});  
     }  
 }  
   
 # ======================================================= Display a course list  
   
 sub discourse {  
     my $r=shift;  
     my %courselist=&Apache::lonnet::dump(  
                    'classlist',  
    $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},  
    $ENV{'course.'.$ENV{'request.course.id'}.'.num'});  
     my $now=time;  
     my %lt=&Apache::lonlocal::texthash('cfa' => 'Check for All',  
             'cfs' => 'Check for Section/Group',  
             'cfn' => 'Check for None');  
     $r->print(<<ENDDISHEADER);  
 <input type=hidden name=sendmode value=group>  
 <script>  
     function checkall() {  
  for (i=0; i<document.forms.compemail.elements.length; i++) {  
             if   
           (document.forms.compemail.elements[i].name.indexOf('send_to_')==0) {  
       document.forms.compemail.elements[i].checked=true;  
             }  
         }          }
     }      } else {
    push(@status,&user_normal_msg_raw($user,$domain,$subject,$message,
     function checksec() {       $citation,$baseurl,$attachmenturl,$toperm,
  for (i=0; i<document.forms.compemail.elements.length; i++) {       undef,undef,$sentmessage,undef,$symb,
             if                                        $restitle,$error,$nosentstore,$recipid,
           (document.forms.compemail.elements[i].name.indexOf                                       $permresults,$senthide));
            ('send_to_&&&'+document.forms.compemail.chksec.value)==0) {      }
       document.forms.compemail.elements[i].checked=true;      if (wantarray) {
             }          return @status;
         }      }
     }      return join(' ',@status);
   }
     function uncheckall() {  
  for (i=0; i<document.forms.compemail.elements.length; i++) {  sub process_sent_mail {
             if       my ($msgsubj,$subj_prefix,$numsent,$stamp,$msgname,$msgdom,$msgcount,
           (document.forms.compemail.elements[i].name.indexOf('send_to_')==0) {          $context,$pid,$savemsg,$recusers,$recudoms,$baseurl,$attachmenturl,
       document.forms.compemail.elements[i].checked=false;          $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;
     }      }
 </script>      $sentsubj = &HTML::Entities::encode($sentsubj,'<>&"');
 <input type=button onClick="checkall()" value="$lt{'cfa'}">&nbsp;      my $sentmsgid = 
 <input type=button onClick="checksec()" value="$lt{'cfs'}">          &buildmsgid($stamp,$sentsubj,$msgname,$msgdom,$msgcount,$context,$pid);
 <input type=text size=5 name=chksec>&nbsp;      (undef,my $sentmessage) =
 <input type=button onClick="uncheckall()" value="$lt{'cfn'}">          &packagemsg($msgsubj,$savemsg,undef,$baseurl,$attachmenturl,$recusers,
 <p>                      $recudoms,$sentmsgid,undef,undef,$symb,$error,$recipid);
 ENDDISHEADER      my $status = &store_sent_mail($sentmsgid,$sentmessage,$senderuname,
     my %coursepersonnel=                                    $senderdom);
        &Apache::lonnet::get_course_adv_roles();      return $status;
     foreach my $role (sort keys %coursepersonnel) {  
        foreach (split(/\,/,$coursepersonnel{$role})) {  
    my ($puname,$pudom)=split(/\:/,$_);  
    $r->print(  
              '<br /><input type="checkbox" name="send_to_&&&&&&_'.  
              $puname.':'.$pudom.'" /> '.  
      &Apache::loncommon::plainname($puname,  
                           $pudom).' ('.$_.'), <i>'.$role.'</i>');  
  }  
     }  
   
     foreach (sort keys %courselist) {  
         my ($end,$start)=split(/\:/,$courselist{$_});  
         my $active=1;  
         if (($end) && ($now>$end)) { $active=0; }  
         if ($active) {  
            my ($sname,$sdom)=split(/\:/,$_);  
            my %reply=&Apache::lonnet::get('environment',  
               ['firstname','middlename','lastname','generation'],  
               $sdom,$sname);  
            my $section=&Apache::lonnet::usection  
        ($sdom,$sname,$ENV{'request.course.id'});  
            $r->print(  
         '<br><input type=checkbox name="send_to_&&&'.$section.'&&&_'.$_.'"> '.  
       $reply{'firstname'}.' '.   
                       $reply{'middlename'}.' '.  
                       $reply{'lastname'}.' '.  
                       $reply{'generation'}.  
                       ' ('.$_.') '.$section);  
         }   
     }  
 }  }
   
 # ==================================================== Display Critical Message  sub store_sent_mail {
       my ($msgid,$message,$senderuname,$senderdom) = @_;
 sub discrit {      if ($senderuname eq '') {
     my $r=shift;          $senderuname = $env{'user.name'};
     my $header = '<h1><font color=red>'.&mt('Critical Messages').'</font></h1>'.      }
         '<form action=/adm/email method=post>'.      if ($senderdom eq '') {
         '<input type=hidden name=confirm value=true>';          $senderdom = $env{'user.domain'};
     my %what=&Apache::lonnet::dump('critical');      }
     my $result = '';      my $status =' '.&Apache::lonnet::cput('nohist_email_sent',
     foreach (sort keys %what) {    {$msgid => $message},
         my %content=&unpackagemsg($what{$_});    $senderdom,$senderuname);
         next if ($content{'senderdomain'} eq '');      return $status;
         $content{'message'}=~s/\n/\<br\>/g;  
         $result.='<hr>'.&mt('From').': <b>'.  
 &Apache::loncommon::aboutmewrapper(  
  &Apache::loncommon::plainname($content{'sendername'},$content{'senderdomain'}),$content{'sendername'},$content{'senderdomain'}).'</b> ('.  
 $content{'sendername'}.'@'.  
             $content{'senderdomain'}.') '.$content{'time'}.  
             '<br>'.&mt('Subject').': '.$content{'subject'}.  
             '<br><blockquote>'.  
               &Apache::lontexconvert::msgtexconverted($content{'message'}).  
             '</blockquote><small>'.  
 &mt('You have to confirm that you received this message. After confirmation, this message will be moved to your regular inbox').  
             '</small><br />'.  
             '<input type=submit name="rec_'.$_.'" value="'.&mt('Confirm Receipt').'">'.  
             '<input type=submit name="reprec_'.$_.'" '.  
                   'value="'.&mt('Confirm Receipt and Reply').'">';  
     }  
     # Check to see if there were any messages.  
     if ($result eq '') {  
         $result = "<h2>".&mt('You have no critical messages.')."</h2>".  
     '<a href="/adm/roles">'.&mt('Select a course').'</a>';  
     } else {  
         $r->print($header);  
     }  
     $r->print($result);  
     $r->print('<input type=hidden name="displayedcrit" value="true"></form>');  
 }  }
   
 # =============================================================== Compose reply  sub store_recipients {
       my ($subject,$sendername,$senderdom,$reciphash) = @_;
 sub comprep {      my $context = &get_course_context();
     my ($r,$msgid)=@_;      my $now = time();
       my %message=&Apache::lonnet::get('nohist_email',[$msgid]);      my $msgcount = &get_uniq();
       my %content=&unpackagemsg($message{$msgid},1);      my $recipid =
       my $quotemsg='> '.$content{'message'};          &buildmsgid($now,$subject,$sendername,$senderdom,$msgcount,$context,$$);
       $quotemsg=~s/\r/\n/g;      my %recipinfo = (
       $quotemsg=~s/\f/\n/g;                           $recipid => $reciphash,
       $quotemsg=~s/\n+/\n\> /g;                      );
       my $torepl=&Apache::loncommon::aboutmewrapper(      my $status = &Apache::lonnet::put('nohist_emailrecip',\%recipinfo,
  &Apache::loncommon::plainname($content{'sendername'},$content{'senderdomain'}),$content{'sendername'},$content{'senderdomain'}).' ('.                                        $senderdom,$sendername); 
 $content{'sendername'}.'@'.      if ($status eq 'ok') {
             $content{'senderdomain'}.')';          return ($recipid,$status);
       my $subject=&mt('Re').': '.$content{'subject'};  
       my $dispcrit='';  
       if (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {  
  my $crithelp = Apache::loncommon::help_open_topic("Course_Critical_Message");  
          $dispcrit=  
  '<input type=checkbox name=critmsg> '.&mt('Send as critical message').' ' . $crithelp .   
  '<br>'.  
  '<input type=checkbox name=sendbck> '.&mt('Send as critical message').' ' .  
  &mt('and return receipt') . $crithelp . '<p>';  
       }  
     my %lt=&Apache::lonlocal::texthash(  
    'to' => 'To',  
    'sb' => 'Subject',  
    'sr' => 'Send Reply',  
    'ca' => 'Cancel'  
    );  
       $r->print(<<"ENDREPLY");  
 <form action="/adm/email" method="post">  
 <input type="hidden" name="sendreply" value="$msgid">  
 $lt{'to'}: $torepl<br />  
 $lt{'sb'}: <input type="text" size=50 name="subject" value="$subject"><p>  
 <textarea name="message" cols="84" rows="10" wrap="hard">  
 $quotemsg  
 </textarea></p><br />  
 $dispcrit  
 <input type="submit" name="send" value="$lt{'sr'}" />  
 <input type="submit" name="cancel" value="$lt{'ca'}"/ >  
 </form>  
 ENDREPLY  
 }  
   
 sub sortedmessages {  
     my @messages = &Apache::lonnet::getkeys('nohist_email');  
     #unpack the varibles and repack into temp for sorting  
     my @temp;  
     foreach (@messages) {  
  my $msgid=&Apache::lonnet::escape($_);  
  my ($sendtime,$shortsubj,$fromname,$fromdomain,$status)=  
     &Apache::lonmsg::unpackmsgid($msgid);  
  my @temp1 = ($sendtime,$shortsubj,$fromname,$fromdomain,$status,  
      $msgid);  
  push @temp ,\@temp1;  
     }  
     #default sort  
     @temp = sort  {$a->[0] <=> $b->[0]} @temp;      
     if ($ENV{'form.sortedby'} eq "date"){  
         @temp = sort  {$a->[0] <=> $b->[0]} @temp;      
     }  
     if ($ENV{'form.sortedby'} eq "revdate"){  
     @temp = sort  {$b->[0] <=> $a->[0]} @temp;   
     }  
     if ($ENV{'form.sortedby'} eq "user"){  
  @temp = sort  {lc($a->[2]) cmp lc($b->[2])} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "revuser"){  
  @temp = sort  {lc($b->[2]) cmp lc($a->[2])} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "domain"){  
         @temp = sort  {$a->[3] cmp $b->[3]} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "revdomain"){  
         @temp = sort  {$b->[3] cmp $a->[3]} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "subject"){  
         @temp = sort  {lc($a->[1]) cmp lc($b->[1])} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "revsubject"){  
         @temp = sort  {lc($b->[1]) cmp lc($a->[1])} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "status"){  
         @temp = sort  {$a->[4] cmp $b->[4]} @temp;  
     }  
     if ($ENV{'form.sortedby'} eq "revstatus"){  
         @temp = sort  {$b->[4] cmp $a->[4]} @temp;  
     }  
     return @temp;  
 }  
   
 # ======================================================== Display all messages  
   
 sub disall {  
     my $r=shift;  
      $r->print(<<ENDDISHEADER);  
 <script>  
     function checkall() {  
  for (i=0; i<document.forms.disall.elements.length; i++) {  
             if   
           (document.forms.disall.elements[i].name.indexOf('delmark_')==0) {  
       document.forms.disall.elements[i].checked=true;  
             }  
         }  
     }  
   
     function uncheckall() {  
  for (i=0; i<document.forms.disall.elements.length; i++) {  
             if   
           (document.forms.disall.elements[i].name.indexOf('delmark_')==0) {  
       document.forms.disall.elements[i].checked=false;  
             }  
         }  
     }  
 </script>  
 ENDDISHEADER  
     $r->print('<h1>'.&mt('Display All Messages').'</h1><form method=post name=disall '.  
       'action="/adm/email">'.  
       '<table border=2><tr><th colspan=2>&nbsp</th><th>');  
     if ($ENV{'form.sortedby'} eq "revdate") {  
  $r->print('<a href = "?sortedby=date">'.&mt('Date').'</a></th>');  
     } else {  
  $r->print('<a href = "?sortedby=revdate">'.&mt('Date').'</a></th>');  
     }  
     $r->print('<th>');  
     if ($ENV{'form.sortedby'} eq "revuser") {  
  $r->print('<a href = "?sortedby=user">'.&mt('Username').'</a>');  
     } else {  
  $r->print('<a href = "?sortedby=revuser">'.&mt('Username').'</a>');  
     }  
     $r->print('</th><th>');  
     if ($ENV{'form.sortedby'} eq "revdomain") {  
  $r->print('<a href = "?sortedby=domain">'.&mt('Domain').'</a>');  
     } else {  
  $r->print('<a href = "?sortedby=revdomain">'.&mt('Domain').'</a>');  
     }  
     $r->print('</th><th>');  
     if ($ENV{'form.sortedby'} eq "revsubject") {  
  $r->print('<a href = "?sortedby=subject">'.&mt('Subject').'</a>');  
     } else {  
     $r->print('<a href = "?sortedby=revsubject">'.&mt('Subject').'</a>');  
     }  
     $r->print('</th><th>');  
     if ($ENV{'form.sortedby'} eq "revstatus") {  
  $r->print('<a href = "?sortedby=status">'.&mt('Status').'</th>');  
     } else {  
       $r->print('<a href = "?sortedby=revstatus">'.&mt('Status').'</th>');  
     }  
     $r->print('</tr>');  
     my @temp=sortedmessages();  
     foreach (@temp){  
  my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$origID)= @$_;  
  if (($status ne 'deleted') && defined($sendtime) && $sendtime!~/error/) {  
     if ($status eq 'new') {  
  $r->print('<tr bgcolor="#FFBB77">');  
     } elsif ($status eq 'read') {  
  $r->print('<tr bgcolor="#BBBB77">');  
     } elsif ($status eq 'replied') {  
  $r->print('<tr bgcolor="#AAAA88">');   
     } else {  
  $r->print('<tr bgcolor="#99BBBB">');  
     }  
     $r->print('<td><a href="/adm/email?display='.$origID.$sqs.   
       '">'.&mt('Open').'</a></td><td><a href="/adm/email?markdel='.$origID.$sqs.  
       '">'.&mt('Delete').'</a><input type=checkbox name="delmark_'.$origID.'"></td>'.  
       '<td>'.&Apache::lonlocal::locallocaltime($sendtime).'</td><td>'.  
       $fromname.'</td><td>'.$fromdomain.'</td><td>'.  
       &Apache::lonnet::unescape($shortsubj).'</td><td>'.  
                       $status.'</td></tr>');  
  }  
     }     
     $r->print('</table><p>'.  
               '<a href="javascript:checkall()">'.&mt('Check All').'</a>&nbsp;'.  
               '<a href="javascript:uncheckall()">'.&mt('Uncheck All').'</a><p>'.  
       '<input type="hidden" name="sortedby" value="'.$ENV{'form.sortedby'}.'" />'.  
               '<input type=submit name="markeddel" value="'.&mt('Delete Checked').'">'.  
               '</form></body></html>');  
 }  
   
 # ============================================================== Compose output  
   
 sub compout {  
     my ($r,$forwarding,$broadcast)=@_;  
     &printheader($r,'/adm/email?compose=upload',  
      'Distribute from uploaded file');  
     my $dispcrit='';  
     my $dissub='';  
     my $dismsg='';  
     my $func=&mt('Send New');  
     my %lt=&Apache::lonlocal::texthash('us' => 'Username',  
        'do' => 'Domain',  
        'ad' => 'Additional Recipients',  
        'sb' => 'Subject',  
        'ca' => 'Cancel',  
        'ma' => 'Mail');  
   
     if (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {  
  my $crithelp = Apache::loncommon::help_open_topic("Course_Critical_Message");  
          $dispcrit=  
  '<input type="checkbox" name="critmsg"> '.&mt('Send as critical message').' ' . $crithelp .   
  '<br>'.  
  '<input type="checkbox" name="sendbck"> '.&mt('Send as critical message').'  ' .  
  &mt('and return receipt') . $crithelp . '<p>';  
       }  
     if ($forwarding) {  
        $dispcrit.='<input type="hidden" name="forwid" value="'.  
    $forwarding.'">';  
        $func=&mt('Forward');  
       my %message=&Apache::lonnet::get('nohist_email',[$forwarding]);  
       my %content=&unpackagemsg($message{$forwarding});  
   
        $dissub=&mt('Forwarding').': '.$content{'subject'};  
        $dismsg=&mt('Forwarded message from').' '.  
    $content{'sendername'}.' '.&mt('at').' '.$content{'senderdomain'};  
     }  
     my $defdom=$ENV{'user.domain'};  
     if ($ENV{'form.recdom'}) { $defdom=$ENV{'form.recdom'}; }  
       $r->print(  
                 '<form action="/adm/email"  name="compemail" method="post"'.  
                 ' enctype="multipart/form-data">'."\n".  
                 '<input type="hidden" name="sendmail" value="on">'."\n".  
                 '<table>');  
     unless (($broadcast eq 'group') || ($broadcast eq 'upload')) {  
         my $domform = &Apache::loncommon::select_dom_form($defdom,'recdomain');  
         my $selectlink=&Apache::loncommon::selectstudent_link  
     ('compemail','recuname','recdomain');  
        $r->print(<<"ENDREC");  
 <table>  
 <tr><td>$lt{'us'}:</td><td><input type="text" size="12" name="recuname" value="$ENV{'form.recname'}"></td><td rowspan="2">$selectlink</td></tr>  
 <tr><td>$lt{'do'}:</td>  
 <td>$domform</td></tr>  
 ENDREC  
     }  
     my $latexHelp = Apache::loncommon::helpLatexCheatsheet();  
     if ($broadcast ne 'upload') {  
        $r->print(<<"ENDCOMP");  
 <tr><td>$lt{'ad'}<br /><tt>username\@domain,username\@domain, ...  
 </tt></td><td>  
 <input type="text" size="50" name="additionalrec" /></td></tr>  
 <tr><td>$lt{'sb'}:</td><td><input type="text" size="50" name="subject" value="$dissub" />  
 </td></tr></table>  
 $latexHelp  
 <textarea name="message" cols="80" rows="10" wrap="hard">$dismsg  
 </textarea></p><br />  
 $dispcrit  
 <input type="submit" name="send" value="$func $lt{'ma'}" />  
 <input type="submit" name="cancel" value="$lt{'ca'}" />  
 ENDCOMP  
     } else { # $broadcast is 'upload'  
  $r->print(<<ENDUPLOAD);  
 <input type="hidden" name="sendmode" value="upload" />  
 <input type="hidden" name="send" value="on" />  
 <h3>Generate messages from a file</h3>  
 <p>  
 Subject: <input type="text" size="50" name="subject" />  
 </p>  
 <p>General message text<br />  
 <textarea name="message" cols="60" rows="10" wrap="hard">$dismsg  
 </textarea></p>  
 <p>  
 The file format for the uploaded portion of the message is:  
 <pre>  
 username1\@domain1: text  
 username2\@domain2: text  
 username3\@domain1: text  
 </pre>  
 </p>  
 <p>  
 The messages will be assembled from all lines with the respective   
 <tt>username\@domain</tt>, and appended to the general message text.</p>  
 <p>  
 <input type="file" name="upfile" size="40" /></p><p>  
 $dispcrit  
 <input type="submit" value="Upload and send" /></p>  
 ENDUPLOAD  
     }  
     if ($broadcast eq 'group') {  
        &discourse;  
     }  
     $r->print('</form>');  
 }  
   
 # ---------------------------------------------------- Display all face to face  
   
 sub disfacetoface {  
     my ($r,$user,$domain)=@_;  
     unless ($ENV{'request.course.id'}) { return; }  
     unless (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {  
  return;  
     }  
     my %records=&Apache::lonnet::dump('nohist_email',  
  $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},  
  $ENV{'course.'.$ENV{'request.course.id'}.'.num'},  
                          '%255b'.$user.'%253a'.$domain.'%255d');  
     my $result='';  
     foreach (sort keys %records) {  
         my %content=&unpackagemsg($records{$_});  
         next if ($content{'senderdomain'} eq '');  
         $content{'message'}=~s/\n/\<br\>/g;  
         if ($content{'subject'}=~/^Record/) {  
     $result.='<h3>'.&mt('Record').'</h3>';  
         } else {  
             $result.='<h3>'.&mt('Sent Message').'</h3>';  
             %content=&unpackagemsg($content{'message'});  
             $content{'message'}=  
                 '<b>Subject: '.$content{'subject'}.'</b><br />'.  
  $content{'message'};  
         }  
         $result.=&mt('By').': <b>'.  
 &Apache::loncommon::aboutmewrapper(  
  &Apache::loncommon::plainname($content{'sendername'},$content{'senderdomain'}),$content{'sendername'},$content{'senderdomain'}).'</b> ('.  
 $content{'sendername'}.'@'.  
             $content{'senderdomain'}.') '.$content{'time'}.  
             '<br><blockquote>'.  
               &Apache::lontexconvert::msgtexconverted($content{'message'}).  
       '</blockquote>';  
      }  
     # Check to see if there were any messages.  
     if ($result eq '') {  
         $r->print("<p><b>No notes, face-to-face discussion records, or critical messages in this course.</b></p>");  
     } else {      } else {
        $r->print($result);          return (undef,$status);
     }      }
 }  }
   
 # ---------------------------------------------------------------- Face to face  
   
 sub facetoface {  sub foldersuffix {
     my ($r,$stage)=@_;      my $folder=shift;
     unless (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {      unless ($folder) { return ''; }
  return;      my $suffix;
     }      my %folderhash = &get_user_folders($folder);
     &printheader($r,      if (ref($folderhash{$folder}) eq 'HASH') {
  '/adm/email?recordftf=query',          $suffix = '_'.&escape($folderhash{$folder}{'id'});
  "User Notes, Face-to-Face, Critical Messages");      } else {
 # from query string          $suffix = '_'.&escape($folder);
   
     if ($ENV{'form.recname'}) { $ENV{'form.recuname'}=$ENV{'form.recname'}; }  
     if ($ENV{'form.recdom'}) { $ENV{'form.recdomain'}=$ENV{'form.recdom'}; }  
   
     my $defdom=$ENV{'user.domain'};  
 # already filled in  
     if ($ENV{'form.recdomain'}) { $defdom=$ENV{'form.recdomain'}; }  
 # generate output  
     my $domform = &Apache::loncommon::select_dom_form($defdom,'recdomain');  
     my $stdbrws = &Apache::loncommon::selectstudent_link  
  ('stdselect','recuname','recdomain');  
     my %lt=&Apache::lonlocal::texthash('user' => 'Username',  
        'dom' => 'Domain',  
        'head' => 'User Notes, Records of Face-To-Face Discussions, and Critical Messages in Course',  
        'subm' => 'Retrieve discussion and message records',  
        'newr' => 'New Record (record is visible to course faculty and staff)',  
        'post' => 'Post this Record');  
     $r->print(<<"ENDTREC");  
 <h3>$lt{'head'}</h3>  
 <form method="post" action="/adm/email" name="stdselect">  
 <input type="hidden" name="recordftf" value="retrieve" />  
 <table>  
 <tr><td>$lt{'user'}:</td><td><input type="text" size="12" name="recuname" value="$ENV{'form.recuname'}" /></td>  
 <td rowspan="2">  
 $stdbrws  
 <input type="submit" value="$lt{'subm'}" /></td>  
 </tr>  
 <tr><td>$lt{'dom'}:</td>  
 <td>$domform</td></tr>  
 </table>  
 </form>  
 ENDTREC  
     if (($stage ne 'query') &&  
         ($ENV{'form.recdomain'}) && ($ENV{'form.recuname'})) {  
         chomp($ENV{'form.newrecord'});  
         if ($ENV{'form.newrecord'}) {  
            &user_normal_msg_raw(  
             $ENV{'course.'.$ENV{'request.course.id'}.'.num'},  
             $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},  
             &mt('Record').  
      ' ['.$ENV{'form.recuname'}.':'.$ENV{'form.recdomain'}.']',  
     $ENV{'form.newrecord'});  
         }  
         $r->print('<h3>'.&Apache::loncommon::plainname($ENV{'form.recuname'},  
      $ENV{'form.recdomain'}).'</h3>');  
         &disfacetoface($r,$ENV{'form.recuname'},$ENV{'form.recdomain'});  
  $r->print(<<ENDRHEAD);  
 <form method="post" action="/adm/email">  
 <input name="recdomain" value="$ENV{'form.recdomain'}" type="hidden" />  
 <input name="recuname" value="$ENV{'form.recuname'}" type="hidden" />  
 ENDRHEAD  
         $r->print(<<ENDBFORM);  
 <hr />$lt{'newr'}<br />  
 <textarea name="newrecord" cols="80" rows="10" wrap="hard"></textarea>  
 <br />  
 <input type="hidden" name="recordftf" value="post" />  
 <input type="submit" value="$lt{'post'}" />  
 </form>  
 ENDBFORM  
     }  
 }  
   
 # ----------------------------------------------- Generate the text input field  
   
 sub textinputarea {  
     my ($r,$name,$quote)=@_;  
     $r->print('<textarea name="'.$name.  
       '" cols="80" rows="12" wrap="hard">'.$quote.'</textarea>');  
 }  
   
 # ----------------------------------------------------------- Display a message  
   
 sub displaymessage {  
     my ($r,$msgid)=@_;  
     &statuschange($msgid,'read');  
     my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
     my %content=&unpackagemsg($message{$msgid});  
 # info to generate "next" and "previous" buttons  
     my @messages=&sortedmessages();  
     my $counter=0;  
     $r->print('<pre>');  
     my $escmsgid=&Apache::lonnet::escape($msgid);  
     foreach (@messages) {  
  if ($_->[5] eq $escmsgid){  
     last;  
  }  
  $counter++;  
     }      }
     $r->print('</pre>');      return $suffix;
     my $number_of_messages = scalar(@messages); #subtract 1 for last index  
 # start output  
     &printheader($r,'/adm/email?display='.$msgid,'Display a Message','',$content{'baseurl'});  
     my %courseinfo=&Apache::lonnet::coursedescription($content{'courseid'});  
 # Functions  
     $r->print('<table border="2" width="100%"><tr bgcolor="#FFFFAA"><td>'.&mt('Functions').':</td>'.  
       '<td><a href="/adm/email?replyto='.&Apache::lonnet::escape($msgid).$sqs.  
       '"><b>'.&mt('Reply').'</b></a></td>'.  
       '<td><a href="/adm/email?forward='.&Apache::lonnet::escape($msgid).$sqs.  
       '"><b>'.&mt('Forward').'</b></a></td>'.  
       '<td><a href="/adm/email?markunread='.&Apache::lonnet::escape($msgid).$sqs.  
       '"><b>'.&mt('Mark Unread').'</b></a></td>'.  
       '<td><a href="/adm/email?markdel='.&Apache::lonnet::escape($msgid).$sqs.  
       '"><b>Delete</b></a></td>'.  
       '<td><a href="/adm/email?sortedby='.$ENV{'form.sortedby'}.  
       '"><b>'.&mt('Display all Messages').'</b></a></td>');  
     if ($counter > 0){  
  $r->print('<td><a href="/adm/email?display='.$messages[$counter-1]->[5].$sqs.  
   '"><b>'.&mt('Previous').'</b></a></td>');  
     }  
     if ($counter < $number_of_messages - 1){  
  $r->print('<td><a href="/adm/email?display='.$messages[$counter+1]->[5].$sqs.  
   '"><b>'.&mt('Next').'</b></a></td>');  
     }  
     $r->print('</tr></table>');  
     $r->print('<br /><b>'.&mt('Subject').':</b> '.$content{'subject'}.  
       '<br /><b>'.&mt('From').':</b> '.  
       &Apache::loncommon::aboutmewrapper(  
  &Apache::loncommon::plainname($content{'sendername'},$content{'senderdomain'}),  
  $content{'sendername'},$content{'senderdomain'}).' ('.  
       $content{'sendername'}.' at '.  
       $content{'senderdomain'}.') '.  
       ($content{'courseid'}?'<br /><b>'.&mt('Course').':</b> '.$courseinfo{'description'}.  
        ($content{'coursesec'}?' ('.&mt('Group/Section').': '.$content{'coursesec'}.')':''):'').  
       '<br /><b>'.&mt('Time').':</b> '.$content{'time'}.  
       '<p><pre>'.  
       &Apache::lontexconvert::msgtexconverted($content{'message'},1).  
       '</pre><hr />'.$content{'citation'}.'</p>');  
     return;     
 }  
   
 # ================================================================== The Header  
   
 sub header {  
     my ($r,$title,$baseurl)=@_;  
     $r->print('<html><head><title>Communication and Messages</title>');  
     if ($baseurl) {  
  $r->print("<base href=\"http://$ENV{'SERVER_NAME'}/$baseurl\" />");  
     }  
     $r->print(&Apache::loncommon::studentbrowser_javascript().'</head>'.  
       &Apache::loncommon::bodytag('Communication and Messages'));  
         $r->print(&Apache::lonhtmlcommon::breadcrumbs  
                   (undef,($title?$title:'Communication and Messages')));  
   
 }  
   
 # ---------------------------------------------------------------- Print header  
   
 sub printheader {  
     my ($r,$url,$desc,$title,$baseurl)=@_;  
     &Apache::lonhtmlcommon::add_breadcrumb  
  ({href=>$url,  
   text=>$desc});  
     &header($r,$title,$baseurl);  
 }  }
   
   
 # ===================================================================== Handler  sub get_user_folders {
       my ($folder) = @_;
 sub handler {      my %userfolders = 
     my $r=shift;            &Apache::lonnet::dump('email_folders',undef,undef,$folder);
       my $lock = "\0".'lock_counter'; # locks db while counter incremented
 # ----------------------------------------------------------- Set document type      my $counter = "\0".'idcount';   # used in suffix for email db files
           if (defined($userfolders{$lock})) {
     &Apache::loncommon::content_type($r,'text/html');          delete($userfolders{$lock});
     $r->send_http_header;      }
           if (defined($userfolders{$counter})) {
     return OK if $r->header_only;          delete($userfolders{$counter});
           }
 # --------------------------- Get query string for limited number of parameters      return %userfolders;
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},  }
         ['display','replyto','forward','markread','markdel','markunread',  
          'sendreply','compose','sendmail','critical','recname','recdom',  sub secapply {
          'recordftf','sortedby']);      my $rec=shift;
     $sqs='&sortedby='.$ENV{'form.sortedby'};      my $defaultflag=shift;
 # ------------------------------------------------------ They checked for email      $rec=~s/\s+//g;
     &Apache::lonnet::put('email_status',{'recnewemail'=>0});      unless ($rec =~ /\:/) {
           $rec=~s/\@/\:/g;
 # ----------------------------------------------------------------- Breadcrumbs      }
       my ($adr,$sections_or_groups)=($rec=~/^([^\(]+)\(([^\)]+)\)/);
     &Apache::lonhtmlcommon::clear_breadcrumbs();      if ($sections_or_groups) {
     &Apache::lonhtmlcommon::add_breadcrumb   foreach my $item (split(/\;/,$sections_or_groups)) {
         ({href=>"/adm/communicate",              if (($item eq $env{'request.course.sec'}) ||
           text=>"Communication/Messages",                  ($defaultflag && ($item eq '*'))) {
           faq=>12,bug=>'Communication Tools',});                  return $adr; 
               } elsif ($env{'request.course.groups'}) {
 # --------------------------------------------------------------- Render Output                  my @usersgroups = split(/:/,$env{'request.course.groups'});
                   if (grep(/^\Q$item\E$/,@usersgroups)) {
     if ($ENV{'form.display'}) {                      return $adr;
  &displaymessage($r,$ENV{'form.display'});                  }
     } elsif ($ENV{'form.replyto'}) {              } 
  &comprep($r,$ENV{'form.replyto'});          }
     } elsif ($ENV{'form.sendreply'}) {  
  if ($ENV{'form.send'}) {  
     my $msgid=$ENV{'form.sendreply'};  
     my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
     my %content=&unpackagemsg($message{$msgid},1);  
     &statuschange($msgid,'replied');  
     if ((($ENV{'form.critmsg'}) || ($ENV{'form.sendbck'})) &&   
  (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) {  
  $r->print(&mt('Sending critical message').': '.  
   &user_crit_msg($content{'sendername'},  
  $content{'senderdomain'},  
  &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}),  
  &Apache::lonfeedback::clear_out_html($ENV{'form.message'}),  
  $ENV{'form.sendbck'}));  
     } else {  
  $r->print(&mt('Sending').': '.&user_normal_msg($content{'sendername'},  
        $content{'senderdomain'},  
        &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}),  
        &Apache::lonfeedback::clear_out_html($ENV{'form.message'})));  
     }  
  }  
  if ($ENV{'form.displayedcrit'}) {  
     &discrit($r);  
  } else {  
     &disall($r);  
  }  
     } elsif ($ENV{'form.confirm'}) {  
  foreach (keys %ENV) {  
     if ($_=~/^form\.rec\_(.*)$/) {  
  $r->print('<b>Confirming Receipt:</b> '.  
   &user_crit_received($1).'<br>');  
     }  
     if ($_=~/^form\.reprec\_(.*)$/) {  
  my $msgid=$1;  
  $r->print('<b>Confirming Receipt:</b> '.  
   &user_crit_received($msgid).'<br>');  
  &comprep($r,$msgid);  
     }  
  }  
  &discrit($r);  
     } elsif ($ENV{'form.critical'}) {  
  &discrit($r);  
     } elsif ($ENV{'form.forward'}) {  
  &compout($r,$ENV{'form.forward'});  
     } elsif ($ENV{'form.markread'}) {  
     } elsif ($ENV{'form.markdel'}) {  
  &statuschange($ENV{'form.markdel'},'deleted');  
  &disall($r);  
     } elsif ($ENV{'form.markeddel'}) {  
  my $total=0;  
  foreach (keys %ENV) {  
     if ($_=~/^form\.delmark_(.*)$/) {  
  &statuschange(&Apache::lonnet::unescape($1),'deleted');  
  $total++;  
     }  
  }  
  $r->print('Deleted '.$total.' message(s)<p>');  
  &disall($r);  
     } elsif ($ENV{'form.markunread'}) {  
  &statuschange($ENV{'form.markunread'},'new');  
  &disall($r);  
     } elsif ($ENV{'form.compose'}) {  
  &compout($r,'',$ENV{'form.compose'});  
     } elsif ($ENV{'form.recordftf'}) {  
  &facetoface($r,$ENV{'form.recordftf'});  
     } elsif ($ENV{'form.sendmail'}) {  
  my $sendstatus='';  
  if ($ENV{'form.send'}) {  
     my %content=();  
     undef %content;  
     if ($ENV{'form.forwid'}) {  
  my $msgid=$ENV{'form.forwid'};  
  my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
  %content=&unpackagemsg($message{$msgid},1);  
  &statuschange($msgid,'forwarded');  
  $ENV{'form.message'}.="\n\n-- Forwarded message --\n\n".  
     $content{'message'};  
     }  
     my %toaddr=();  
     undef %toaddr;  
     if ($ENV{'form.sendmode'} eq 'group') {  
  foreach (keys %ENV) {  
     if ($_=~/^form\.send\_to\_\&\&\&[^\&]*\&\&\&\_(.+)$/) {  
  $toaddr{$1}='';  
     }  
  }  
     } elsif ($ENV{'form.sendmode'} eq 'upload') {  
  foreach (split(/[\n\r\f]+/,$ENV{'form.upfile'})) {  
     my ($rec,$txt)=split(/\s*\:\s*/,$_);  
     if ($txt) {  
  $rec=~s/\@/\:/;  
  $toaddr{$rec}.=$txt."\n";  
     }  
  }  
     } else {  
  $toaddr{$ENV{'form.recuname'}.':'.$ENV{'form.recdomain'}}='';  
     }  
     if ($ENV{'form.additionalrec'}) {  
  foreach (split(/\,/,$ENV{'form.additionalrec'})) {  
     my ($auname,$audom)=split(/\@/,$_);  
     $toaddr{$auname.':'.$audom}='';  
  }  
     }  
     foreach (keys %toaddr) {  
  my ($recuname,$recdomain)=split(/\:/,$_);  
  my $msgtxt=&Apache::lonfeedback::clear_out_html($ENV{'form.message'});  
  if ($toaddr{$_}) { $msgtxt.='<hr>'.$toaddr{$_}; }      
  if ((($ENV{'form.critmsg'}) || ($ENV{'form.sendbck'})) &&   
     (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) {  
     $r->print(&mt('Sending critical message').' ...');  
     $sendstatus.=' '.&user_crit_msg($recuname,$recdomain,  
     &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}),  
     $msgtxt,  
     $ENV{'form.sendbck'});  
  } else {  
     $r->print(&mt('Sending').' ...');  
     $sendstatus.=' '.&user_normal_msg($recuname,$recdomain,  
       &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}),  
       $msgtxt,  
       $content{'citation'});  
  }  
  $r->print('<br />');  
     }  
  }  
  if ($sendstatus=~/^(\s*(?:ok|con_delayed)\s*)*$/) {  
     $r->print('<br /><font color="green">'.&mt('Completed.').'</font>');  
     if ($ENV{'form.displayedcrit'}) {  
  &discrit($r);  
     } else {  
  &disall($r);  
     }  
  } else {  
     $r->print(  
       '<h2><font color="red">'.&mt('Could not deliver message').'</font></h2>'.  
       &mt('Please use the browser "Back" button and correct the recipient addresses')  
       );  
  }  
     } else {      } else {
  &disall($r);         return $rec;
     }      }
     $r->print('</body></html>');      return '';
     return OK;  
 }  }
 # ================================================= Main program, reset counter  
   
 BEGIN {  sub decide_receiver {
     $msgcount=0;      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<br />';
    $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').'<br />';
    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').'<br />';
    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').'<br />';
    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  1;
   
 =back  
   
 =cut  
   
 1;   
   
 __END__  __END__
   
   
   
   
   
   
   

Removed from v.1.91  
changed lines
  Added in v.1.251


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>