Diff for /loncom/interface/lonmsg.pm between versions 1.71 and 1.191

version 1.71, 2003/12/05 22:14:15 version 1.191, 2006/12/24 22:13:19
Line 25 Line 25
 #  #
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 #  
 # (Routines to control the menu  
 #  
 # (TeX Conversion Module  
 #  
 # 05/29/00,05/30 Gerd Kortemeyer)  
 #  
 # 10/05 Gerd Kortemeyer)  
 #  
 # 10/19,10/20,10/30,  
 # 02/06/01 Gerd Kortemeyer  
 # 07/27 Guy Albertelli  
 # 07/27,07/28,07/30,08/03,08/06,08/08,08/09,08/10,8/13,8/15,  
 # 10/1,11/5 Gerd Kortemeyer  
 # YEAR=2002  
 # 1/1,3/18 Gerd Kortemeyer  
 #  
 package Apache::lonmsg;  
   
 =pod  
   
 =head1 NAME  
   
 Apache::lonmsg: supports internal messaging  
   
 =head1 SYNOPSIS  
   
 lonmsg provides routines for sending messages, receiving messages, and  
 a handler to allow users to read, send, and delete messages.  
   
 =head1 OVERVIEW  
   
 =head2 Messaging Overview  
   
 X<messages>LON-CAPA provides an internal messaging system similar to  
 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  package Apache::lonmsg;
 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  
   
 Users can ask LON-CAPA to forward messages to conventional e-mail  
 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  
 it is likely you will not need to programmatically read messages,  
 since lonmsg already implements that functionality.  
   
 =head1 FUNCTIONS  
   
 =over 4  
   
 =cut  
   
 use strict;  use strict;
 use Apache::lonnet();  use Apache::lonnet;
 use vars qw($msgcount);  
 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 Mail::Send;
   use LONCAPA qw(:DEFAULT :match);
   
 # Querystring component with sorting type  {
 my $sqs;      my $uniq;
       sub get_uniq {
    $uniq++;
    return $uniq;
       }
   }
   
 # ===================================================================== Package  # ===================================================================== Package
   
 sub packagemsg {  sub packagemsg {
     my ($subject,$message,$citation,$baseurl,$attachmenturl)=@_;      my ($subject,$message,$citation,$baseurl,$attachmenturl,
     $message =&HTML::Entities::encode($message);   $recuser,$recdomain,$msgid,$type,$crsmsgid,$symb,$error)=@_;
     $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|^http://[^/]+/|/|;
     $baseurl =&HTML::Entities::encode($baseurl);      $baseurl =&HTML::Entities::encode($baseurl,'<>&"');
     #remove machine specification      #remove machine specification
     $attachmenturl =~ s|^http://[^/]+/|/|;      $attachmenturl =~ s|^http://[^/]+/|/|;
     $attachmenturl =&HTML::Entities::encode($attachmenturl);      $attachmenturl =&HTML::Entities::encode($attachmenturl,'<>&"');
       my $course_context;
       if (defined($env{'form.replyid'})) {
           my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid)=
                      split(/\:/,&unescape($env{'form.replyid'}));
           $course_context = $origcid;
       }
       foreach my $key (keys(%env)) {
           if ($key=~/^form\.(rep)?rec\_(.*)$/) {
               my ($sendtime,$shortsubj,$fromname,$fromdomain,$count,$origcid) =
                                       split(/\:/,&unescape($2));
               $course_context = $origcid;
               last;
           }
       }
       unless(defined($course_context)) {
           $course_context = $env{'request.course.id'};
       }
     my $now=time;      my $now=time;
     $msgcount++;      my $msgcount = &get_uniq();
     my $partsubj=$subject;      unless(defined($msgid)) {
     $partsubj=&Apache::lonnet::escape($partsubj);          $msgid = &buildmsgid($now,$subject,$env{'user.name'},$env{'user.domain'},
     my $msgid=&Apache::lonnet::escape(                             $msgcount,$course_context,$symb,$error,$$);
            $now.':'.$partsubj.':'.$ENV{'user.name'}.':'.      }
            $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>'.$ENV{'REMOTE_ADDR'}.'</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>'.
    '<role>'.$ENV{'request.role'}.'</role>'.     '<coursesec>'.$env{'request.course.sec'}.'</coursesec>'.
    '<resource>'.$ENV{'request.filename'}.'</resource>'.     '<role>'.$env{'request.role'}.'</role>'.
            '<msgid>'.$msgid.'</msgid>'.     '<resource>'.$env{'request.filename'}.'</resource>'.
    '<message>'.$message.'</message>';             '<msgid>'.$msgid.'</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 .= '<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 167  sub packagemsg { Line 134  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 (defined($course_context)) {
               if ($course_context eq $env{'request.course.id'}) {
                   my $resource_title = &Apache::lonnet::gettitle($symb);
                   if (defined($resource_title)) {
                       $result .= '<resource_title>'.$resource_title.'</resource_title>';
                   }
               }
           }
       }
       return ($msgid,$result);
 }  }
   
 # ================================================== Unpack message into a hash  # ================================================== Unpack message into a hash
Line 181  sub unpackagemsg { Line 159  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 (!exists($content{'recuser'})) { $content{'recuser'} = []; }
     if ($content{'attachmenturl'}) {      if ($content{'attachmenturl'}) {
        my ($fname,$ft)=($content{'attachmenturl'}=~/\/(\w+)\.(\w+)$/);         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;
Line 199  sub unpackagemsg { Line 188  sub unpackagemsg {
   
 # ======================================================= Get info out of msgid  # ======================================================= Get info out of msgid
   
   sub buildmsgid {
       my ($now,$subject,$uname,$udom,$msgcount,$course_context,$symb,$error,$pid) = @_;
       $subject=&escape($subject);
       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)=@_;
     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}=''; }      $shortsubj = &unescape($shortsubj);
     unless ($status{$msgid}) { $status{$msgid}='new'; }      $shortsubj = &HTML::Entities::decode($shortsubj);
     return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid});      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 {  sub sendemail {
     my ($to,$subject,$body)=@_;      my ($to,$subject,$body)=@_;
       my %senderemails=&Apache::loncommon::getemails();
       my $senderaddress='';
       foreach my $type ('notification','permanentemail','critnotification') {
    if ($senderemails{$type}) {
       $senderaddress=$senderemails{$type};
    }
       }
     $body=      $body=
     "*** ".&mt('This is an automatic message generated by the LON-CAPA system.')."\n".      "*** ".&mt('This is an automatic message generated by the LON-CAPA system.')."\n".
     "*** ".&mt('Please do not reply to this address.')."\n\n".$body;      "*** ".($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;      my $msg = new Mail::Send;
     $msg->to($to);      $msg->to($to);
     $msg->subject('[LON-CAPA] '.$subject);      $msg->subject('[LON-CAPA] '.$subject);
     if (my $fh = $msg->open('smtp',Server => 'localhost')) {      if ($senderaddress) { $msg->add('Reply-to',$senderaddress); $msg->add('From',$senderaddress); }
       if (my $fh = $msg->open()) {
  print $fh $body;   print $fh $body;
  $fh->close;   $fh->close;
     }      }
Line 227  sub sendemail { Line 244  sub sendemail {
 # ==================================================== Send notification emails  # ==================================================== Send notification emails
   
 sub sendnotification {  sub sendnotification {
     my ($to,$touname,$toudom,$subj,$crit)=@_;      my ($to,$touname,$toudom,$subj,$crit,$text)=@_;
     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':'');
       $text=~s/\&lt\;/\</gs;
       $text=~s/\&gt\;/\>/gs;
       $text=~s/\<\/*[^\>]+\>//gs;
     my $url='http://'.      my $url='http://'.
       $Apache::lonnet::hostname{&Apache::lonnet::homeserver($touname,$toudom)}.        $Apache::lonnet::hostname{&Apache::lonnet::homeserver($touname,$toudom)}.
       '/adm/email?username='.$touname.'&domain='.$toudom;        '/adm/email?username='.$touname.'&domain='.$toudom;
Line 238  You received a$critical message from $se Line 261  You received a$critical message from $se
   
  $subj   $subj
   
   === Excerpt ============================================================
   $text
   ========================================================================
   
 Use  Use
   
  $url   $url
   
 to access this message.  to access the full message.
 ENDMSG  ENDMSG
     &sendemail($to,'New'.$critical.' message from '.$sender,$body);      &sendemail($to,'New'.$critical.' message from '.$sender,$body);
 }  }
 # ============================================================= Check for email  # ============================================================= 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);
         if ($what{'recnewemail'}>0) { return 1; }          if ($what{'recnewemail'}>0) { return 1; }
Line 270  sub author_res_msg { Line 297  sub author_res_msg {
     my ($filename,$message)=@_;      my ($filename,$message)=@_;
     unless ($message) { return 'empty'; }      unless ($message) { return 'empty'; }
     $filename=&Apache::lonnet::declutter($filename);      $filename=&Apache::lonnet::declutter($filename);
     my ($domain,$author)=split(/\//,$filename);      my ($domain,$author,@dummy)=split(/\//,$filename);
     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("Error: [$filename]",$message);         ($msgid,$message)=&packagemsg($filename,$message);
        #FIXME this should be nohist_res_msg, we need to provide an interface   
        #      to this hash BUG#2444  
        #return &Apache::lonnet::reply('put:'.$domain.':'.$author.  
        #  ':nohist_res_msg:'.  
        #   &Apache::lonnet::escape($filename.'_'.$id).'='.  
        #   &Apache::lonnet::escape($message),$homeserver);  
        return &Apache::lonnet::reply('put:'.$domain.':'.$author.         return &Apache::lonnet::reply('put:'.$domain.':'.$author.
    ':nohist_email:'.           ':nohist_res_msgs:'.
            &Apache::lonnet::escape($msgid).'='.            &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 {
       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.='<p><img src="/adm/lonMisc/bomb.gif" /><b>'.
    $content{'time'}.'</b>: '.$content{'message'}.
    '<br /></p>';
    }
       } 
       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);
       return $result;
   }
   
 # ================================================== Critical message to a user  # ================================================== Critical message to a user
   
 sub user_crit_msg_raw {  sub user_crit_msg_raw {
     my ($user,$domain,$subject,$message,$sendback)=@_;      my ($user,$domain,$subject,$message,$sendback,$toperm,$sentmessage)=@_;
 # 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);
        if ($sendback) { $message.='<sendback>true</sendback>'; }         if ($sendback) { $packed_message.='<sendback>true</sendback>'; }
        $status=&Apache::lonnet::critical(         $status=&Apache::lonnet::critical(
            'put:'.$domain.':'.$user.':critical:'.             'put:'.$domain.':'.$user.':critical:'.
            &Apache::lonnet::escape($msgid).'='.             &escape($msgid).'='.
            &Apache::lonnet::escape($message),$homeserver);             &escape($packed_message),$homeserver);
        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'},          (undef,my $packed_message_no_citation) =
             'Critical ['.$user.':'.$domain.']',              &packagemsg($subject,$message,undef,undef,undef,$user,$domain,
     $message);                          $msgid);
        }          $status .= &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);  
     if ($userenv{'critnotification'}) {      if ($userenv{'critnotification'}) {
       &sendnotification($userenv{'critnotification'},$user,$domain,$subject,1);        &sendnotification($userenv{'critnotification'},$user,$domain,$subject,1,
    $text);
       }
       if ($toperm && $userenv{'permanentemail'}) {
         &sendnotification($userenv{'permanentemail'},$user,$domain,$subject,1,
    $text);
     }      }
 # Log this  # Log this
     &Apache::lonnet::logthis(      &Apache::lonnet::logthis(
       'Sending critical email '.$msgid.        'Sending critical email '.$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));
     return $status;      return $status;
Line 341  sub user_crit_msg_raw { Line 442  sub user_crit_msg_raw {
     a critical message $message to the $user at $domain. If $sendback is true,      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.      a reciept will be sent to the current user when $user recieves the message.
   
       Additionally it will check if the user has a Forwarding address
       set, and send the message to that address instead
   
       returns 
         - in array context a list of results for each message that was sent
         - in scalar context a space seperated list of results for each 
              message sent
   
 =cut  =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='';      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));
        }         }
     } 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));
     }      }
     return $status;      if (wantarray) {
    return @status;
       }
       return join(' ',@status);
 }  }
   
 # =================================================== Critical message received  # =================================================== Critical message received
Line 370  sub user_crit_received { Line 484  sub user_crit_received {
     my %contents=&unpackagemsg($message{$msgid},1);      my %contents=&unpackagemsg($message{$msgid},1);
     my $status='rec: '.($contents{'sendback'}?      my $status='rec: '.($contents{'sendback'}?
      &user_normal_msg($contents{'sendername'},$contents{'senderdomain'},       &user_normal_msg($contents{'sendername'},$contents{'senderdomain'},
                      &mt('Receipt').': '.$ENV{'user.name'}.' at '.$ENV{'user.domain'},                       &mt('Receipt').': '.$env{'user.name'}.' '.&mt('at').' '.$env{'user.domain'}.', '.$contents{'subject'},
                      &mt('User').' '.$ENV{'user.name'}.' '.&mt('at').' '.$ENV{'user.domain'}.                       &mt('User').' '.$env{'user.name'}.' '.&mt('at').' '.$env{'user.domain'}.
                      ' acknowledged receipt of message'."\n".'   "'.                       ' acknowledged receipt of message'."\n".'   "'.
                      $contents{'subject'}.'"'."\n".&mt('dated').' '.                       $contents{'subject'}.'"'."\n".&mt('dated').' '.
                      $contents{'time'}.".\n"                       $contents{'time'}.".\n"
Line 381  sub user_crit_received { Line 495  sub user_crit_received {
      '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;
Line 391  sub user_crit_received { Line 505  sub user_crit_received {
 # ======================================================== Normal communication  # ======================================================== 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)=@_;
 # 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,
                                        $attachmenturl,$user,$domain,$currid,
                                        undef,$crsmsgid,$symb,$error);
   
   # Store in user folder
        $status=&Apache::lonnet::critical(         $status=&Apache::lonnet::critical(
            'put:'.$domain.':'.$user.':nohist_email:'.             'put:'.$domain.':'.$user.':nohist_email:'.
            &Apache::lonnet::escape($msgid).'='.             &escape($msgid).'='.
            &Apache::lonnet::escape($message),$homeserver);             &escape($packed_message),$homeserver);
   # Save new message received time
        &Apache::lonnet::put         &Apache::lonnet::put
                          ('email_status',{'recnewemail'=>time},$domain,$user);                           ('email_status',{'recnewemail'=>time},$domain,$user);
   # Into sent-mail folder unless a broadcast message or critical message
          unless (($env{'request.course.id'}) && 
                  (($env{'form.sendmode'} eq 'group')  || 
                  (($env{'form.critmsg'}) || ($env{'form.sendbck'})) &&
                  (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
    || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
       '/'.$env{'request.course.sec'})))) {
              (undef,my $packed_message_no_citation) =
                  &packagemsg($subject,$message,undef,$baseurl,$attachmenturl,
                              $user,$domain,$currid,undef,$crsmsgid,$symb,$error);
              $status .= &store_sent_mail($msgid,$packed_message_no_citation);
          }
     } else {      } else {
        $status='no_host';         $status='no_host';
     }      }
       if (defined($newid)) {
           $$newid = $msgid;
       }
       if (defined($sentmessage)) {
           $$sentmessage = $packed_message;
       }
   
 # Notifications  # Notifications
     my %userenv = &Apache::lonnet::get('environment',['notification'],      my %userenv = &Apache::lonnet::get('environment',['notification',
                                                         'permanentemail'],
                                        $domain,$user);                                         $domain,$user);
     if ($userenv{'notification'}) {      if ($userenv{'notification'}) {
  &sendnotification($userenv{'notification'},$user,$domain,$subject,0);   &sendnotification($userenv{'notification'},$user,$domain,$subject,0,
     $text);
     }      }
     &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'},      if ($toperm && $userenv{'permanentemail'}) {
                          $ENV{'user.home'},   &sendnotification($userenv{'permanentemail'},$user,$domain,$subject,0,
     $text);
       }
       &Apache::lonnet::log($env{'user.domain'},$env{'user.name'},
                            $env{'user.home'},
       'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status);        'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status);
     return $status;      return $status;
 }  }
Line 425  sub user_normal_msg_raw { Line 572  sub user_normal_msg_raw {
   
 =pod  =pod
   
 =item * B<user_normal_msg($user, $domain, $subject, $message,  =item * B<user_normal_msg($user, $domain, $subject, $message, $citation,
     $citation, $baseurl, $attachmenturl)>: Sends a message to the         $baseurl, $attachmenturl, $toperm, $sentmessage, $symb, $restitle, $error)>:
     $user at $domain, with subject $subject and message $message.   Sends a message to the  $user at $domain, with subject $subject and message $message.
   
 =cut  =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,
    $toperm,$sentmessage,$symb,$restitle,$error)=@_;
     my $status='';      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 (split(/\,/,$msgforward)) {
  my ($forwuser,$forwdomain)=split(/\:/,$_);      my ($forwuser,$forwdomain)=split(/\:/,$_);
          $status.=      $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,$restitle,$error).' ';
     } else {           }
       } else {
  $status=&user_normal_msg_raw($user,$domain,$subject,$message,   $status=&user_normal_msg_raw($user,$domain,$subject,$message,
      $citation,$baseurl,$attachmenturl);       $citation,$baseurl,$attachmenturl,$toperm,
        undef,undef,$sentmessage,undef,$symb,$restitle,$error);
     }      }
     return $status;      return $status;
 }  }
   
   sub store_sent_mail {
 # =============================================================== Status Change      my ($msgid,$message) = @_;
       my $status =' '.&Apache::lonnet::critical(
 sub statuschange {                 'put:'.$env{'user.domain'}.':'.$env{'user.name'}.
     my ($msgid,$newstatus)=@_;                                            ':nohist_email_sent:'.
     my %status=&Apache::lonnet::get('email_status',[$msgid]);                 &escape($msgid).'='.
     if ($status{$msgid}=~/^error\:/) { $status{$msgid}=''; }                 &escape($message),$env{'user.home'});
     unless ($status{$msgid}) { $status{$msgid}='new'; }      return $status;
     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  # =============================================================== Folder suffix
   
 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;  
             }  
         }  
     }  
   
     function checksec() {  
  for (i=0; i<document.forms.compemail.elements.length; i++) {  
             if   
           (document.forms.compemail.elements[i].name.indexOf  
            ('send_to_&&&'+document.forms.compemail.chksec.value)==0) {  
       document.forms.compemail.elements[i].checked=true;  
             }  
         }  
     }  
   
     function uncheckall() {  sub foldersuffix {
  for (i=0; i<document.forms.compemail.elements.length; i++) {      my $folder=shift;
             if       unless ($folder) { return ''; }
           (document.forms.compemail.elements[i].name.indexOf('send_to_')==0) {      my $suffix;
       document.forms.compemail.elements[i].checked=false;      my %folderhash = &get_user_folders($folder);
             }      if (ref($folderhash{$folder}) eq 'HASH') {
         }          $suffix = '_'.&escape($folderhash{$folder}{'id'});
     }  
 </script>  
 <input type=button onClick="checkall()" value="$lt{'cfa'}">&nbsp;  
 <input type=button onClick="checksec()" value="$lt{'cfs'}">  
 <input type=text size=5 name=chksec>&nbsp;  
 <input type=button onClick="uncheckall()" value="$lt{'cfn'}">  
 <p>  
 ENDDISHEADER  
     my %coursepersonnel=  
        &Apache::lonnet::get_course_adv_roles();  
     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 discrit {  
     my $r=shift;  
     my $header = '<h1><font color=red>'.&mt('Critical Messages').'</font></h1>'.  
         '<form action=/adm/email method=post>'.  
         '<input type=hidden name=confirm value=true>';  
     my %what=&Apache::lonnet::dump('critical');  
     my $result = '';  
     foreach (sort keys %what) {  
         my %content=&unpackagemsg($what{$_});  
         next if ($content{'senderdomain'} eq '');  
         $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>'.  
             '<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 {      } else {
         $r->print($header);          $suffix = '_'.&escape($folder);
     }      }
     $r->print($result);      return $suffix;
     $r->print('<input type=hidden name="displayedcrit" value="true"></form>');  
 }  }
   
 # =============================================================== Compose reply  # ========================================================= User-defined folders 
   
 sub comprep {  
     my ($r,$msgid)=@_;  
       my %message=&Apache::lonnet::get('nohist_email',[$msgid]);  
       my %content=&unpackagemsg($message{$msgid},1);  
       my $quotemsg='> '.$content{'message'};  
       $quotemsg=~s/\r/\n/g;  
       $quotemsg=~s/\f/\n/g;  
       $quotemsg=~s/\n+/\n\> /g;  
       my $torepl=&Apache::loncommon::aboutmewrapper(  
  &Apache::loncommon::plainname($content{'sendername'},$content{'senderdomain'}),$content{'sendername'},$content{'senderdomain'}).' ('.  
 $content{'sendername'}.'@'.  
             $content{'senderdomain'}.')';  
       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() {  sub get_user_folders {
  for (i=0; i<document.forms.disall.elements.length; i++) {      my ($folder) = @_;
             if       my %userfolders = 
           (document.forms.disall.elements[i].name.indexOf('delmark_')==0) {            &Apache::lonnet::dump('email_folders',undef,undef,$folder);
       document.forms.disall.elements[i].checked=false;      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});
 </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 (defined($userfolders{$counter})) {
     if ($ENV{'form.sortedby'} eq "revuser") {          delete($userfolders{$counter});
  $r->print('<a href = "?sortedby=user">'.&mt('Username').'</a>');  
     } else {  
  $r->print('<a href = "?sortedby=revuser">'.&mt('Username').'</a>');  
     }      }
     $r->print('</th><th>');      return %userfolders;
     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)=@_;  
       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>  
 <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=20><p>  
 $dispcrit  
 <input type=submit value="Upload and send">  
 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 {  
        $r->print($result);  
     }  
 }  
   
 # ---------------------------------------------------------------- Face to face  
   
 sub facetoface {  
     my ($r,$stage)=@_;  
     unless (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) {  
  return;  
     }  
 # from query string  
     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');  
     $r->print(<<"ENDTREC");  
 <h3>User Notes, Records of Face-To-Face Discussions, and Critical Messages in Course</h3>  
 <form method="post" action="/adm/email" name="stdselect">  
 <input type="hidden" name="recordftf" value="retrieve" />  
 <table>  
 <tr><td>Username:</td><td><input type=text size=12 name=recuname value="$ENV{'form.recuname'}"></td>  
 <td rowspan="2">  
 $stdbrws  
 <input type="submit" value="Retrieve discussion and message records"></td>  
 </tr>  
 <tr><td>Domain:</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'},  
             '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 />New Record (record is visible to course faculty and staff)<br />  
 <textarea name="newrecord" cols="80" rows="10" wrap="hard"></textarea>  
 <br />  
 <input type="hidden" name="recordftf" value="post" />  
 <input type="submit" value="Post this record" />  
 </form>  
 ENDBFORM  
     }  
 }  
   
 # ===================================================================== Handler  
   
 sub handler {  
     my $r=shift;  
   
 # ----------------------------------------------------------- Set document type  
   
   &Apache::loncommon::content_type($r,'text/html');  
   $r->send_http_header;  
   
   return OK if $r->header_only;  
   
 # --------------------------- Get query string for limited number of parameters  
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},  
         ['display','replyto','forward','markread','markdel','markunread',  
          'sendreply','compose','sendmail','critical','recname','recdom',  
          'recordftf','sortedby']);  
     $sqs='&sortedby='.$ENV{'form.sortedby'};  
 # ------------------------------------------------------ They checked for email  
   &Apache::lonnet::put('email_status',{'recnewemail'=>0});  
 # --------------------------------------------------------------- Render Output  
   if (!$ENV{'form.display'}) {  
       $r->print('<html><head><title>EMail and Messaging</title>'.  
  &Apache::loncommon::studentbrowser_javascript().'</head>'.  
  &Apache::loncommon::bodytag('EMail and Messages'));  
   }  
   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});  
 # 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>');  
       my $number_of_messages = scalar(@messages); #subtract 1 for last index  
 # start output  
       $r->print('<html><head><title>EMail and Messaging</title>');  
       if (defined($content{'baseurl'})) {  
   $r->print("<base href=\"http://$ENV{'SERVER_NAME'}/$content{'baseurl'}\" />");  
       }  
       $r->print(&Apache::loncommon::studentbrowser_javascript().  
  '</head>'.  
  &Apache::loncommon::bodytag('EMail and Messages'));  
       $r->print('<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'}.') '.  
              '<br><b>'.&mt('Time').':</b> '.$content{'time'}.'<p>'.  
              '<table border=2><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><p><pre>'.  
              &Apache::lontexconvert::msgtexconverted($content{'message'}).  
              '</pre><hr>'.$content{'citation'});  
   } 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*)*$/) {  
   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 {  
       &disall($r);  
   }  
   $r->print('</body></html>');  
   return OK;  
   
 }  }
 # ================================================= Main program, reset counter  
   
 BEGIN {  
     $msgcount=0;  
 }  
   
 =pod  
   
 =back  
   
 =cut  
   
 1;   
   
   1;
 __END__  __END__
   
   
   
   
   
   
   

Removed from v.1.71  
changed lines
  Added in v.1.191


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