--- loncom/interface/lonmsg.pm 2002/12/27 14:59:42 1.44 +++ loncom/interface/lonmsg.pm 2003/08/12 20:12:05 1.60 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Routines for messaging # -# $Id: lonmsg.pm,v 1.44 2002/12/27 14:59:42 www Exp $ +# $Id: lonmsg.pm,v 1.60 2003/08/12 20:12:05 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -44,24 +44,92 @@ # 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 + +XLON-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: A message the recipient B +acknowlegde receipt of before they are allowed to continue using the +system, preventing a user from claiming they never got a message + +=item * B: 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: 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 + +Users can ask LON-CAPA to forward messages to conventional e-mail +addresses on their B 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 Apache::lonnet(); use vars qw($msgcount); -use HTML::TokeParser; +use HTML::TokeParser(); use Apache::Constants qw(:common); -use Apache::loncommon; -use Apache::lontexconvert; +use Apache::loncommon(); +use Apache::lontexconvert(); +use HTML::Entities(); +use Mail::Send; # ===================================================================== Package sub packagemsg { - my ($subject,$message,$citation)=@_; - $message=~s/\/\>\;/g; - $citation=~s/\/\>\;/g; - $subject=~s/\/\>\;/g; + my ($subject,$message,$citation,$baseurl,$attachmenturl)=@_; + $message =&HTML::Entities::encode($message); + $citation=&HTML::Entities::encode($citation); + $subject =&HTML::Entities::encode($subject); + #remove machine specification + $baseurl =~ s|^http://[^/]+/|/|; + $baseurl =&HTML::Entities::encode($baseurl); + #remove machine specification + $attachmenturl =~ s|^http://[^/]+/|/|; + $attachmenturl =&HTML::Entities::encode($attachmenturl); + my $now=time; $msgcount++; my $partsubj=$subject; @@ -69,8 +137,7 @@ sub packagemsg { my $msgid=&Apache::lonnet::escape( $now.':'.$partsubj.':'.$ENV{'user.name'}.':'. $ENV{'user.domain'}.':'.$msgcount.':'.$$); - return $msgid, - ''.$ENV{'user.name'}.''. + my $result=''.$ENV{'user.name'}.''. ''.$ENV{'user.domain'}.''. ''.$subject.''. ''. @@ -86,14 +153,23 @@ sub packagemsg { ''.$ENV{'request.role'}.''. ''.$ENV{'request.filename'}.''. ''.$msgid.''. - ''.$message.''. - ''.$citation.''; + ''.$message.''; + if (defined($citation)) { + $result.=''.$citation.''; + } + if (defined($baseurl)) { + $result.= ''.$baseurl.''; + } + if (defined($attachmenturl)) { + $result.= ''.$attachmenturl.''; + } + return $msgid,$result; } # ================================================== Unpack message into a hash sub unpackagemsg { - my $message=shift; + my ($message,$notoken)=@_; my %content=(); my $parser=HTML::TokeParser->new(\$message); my $token; @@ -104,6 +180,16 @@ sub unpackagemsg { $content{$entry}=$value; } } + if ($content{'attachmenturl'}) { + my ($fname,$ft)=($content{'attachmenturl'}=~/\/(\w+)\.(\w+)$/); + if ($notoken) { + $content{'message'}.='

Attachment: '.$fname.'.'.$ft.''; + } else { + $content{'message'}.='

Attachment: '.$fname.'.'.$ft.''; + } + } return %content; } @@ -119,6 +205,42 @@ sub unpackmsgid { return ($sendtime,$shortsubj,$fromname,$fromdomain,$status{$msgid}); } + +sub sendemail { + my ($to,$subject,$body)=@_; + $body= + "*** This is an automatic message generated by the LON-CAPA system.\n". + "*** Please do not reply to this address.\n\n".$body; + my $msg = new Mail::Send; + $msg->to($to); + $msg->subject('[LON-CAPA] '.$subject); + my $fh = $msg->open('smtp',Server => 'localhost'); + print $fh $body; + $fh->close; +} + +# ==================================================== Send notification emails + +sub sendnotification { + my ($to,$touname,$toudom,$subj,$crit)=@_; + my $sender=$ENV{'environment.firstname'}.' '.$ENV{'environment.lastname'}; + my $critical=($crit?' critical':''); + my $url='http://'. + $Apache::lonnet::hostname{&Apache::lonnet::homeserver($touname,$toudom)}. + '/adm/email?username='.$touname.'&domain='.$toudom; + my $body=(<: Sends message $message to the owner + of the resource with the URI $filename. + +=cut + sub author_res_msg { my ($filename,$message)=@_; unless ($message) { return 'empty'; } @@ -166,9 +295,23 @@ sub user_crit_msg_raw { 'put:'.$domain.':'.$user.':critical:'. &Apache::lonnet::escape($msgid).'='. &Apache::lonnet::escape($message),$homeserver); + if ($ENV{'request.course.id'}) { + &user_normal_msg_raw( + $ENV{'course.'.$ENV{'request.course.id'}.'.num'}, + $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, + 'Critical ['.$user.':'.$domain.']', + $message); + } } else { $status='no_host'; } +# Notifications + my %userenv = &Apache::lonnet::get('environment',['critnotification'], + $domain,$user); + if ($userenv{'critnotification'}) { + &sendnotification($userenv{'critnotification'},$user,$domain,$subject,1); + } +# Log this &Apache::lonnet::logthis( 'Sending critical email '.$msgid. ', log status: '. @@ -181,6 +324,14 @@ sub user_crit_msg_raw { # New routine that respects "forward" and calls old routine +=pod + +=item * B: Sends + a critical message $message to the $user at $domain. If $sendback is true, + a reciept will be sent to the current user when $user recieves the message. + +=cut + sub user_crit_msg { my ($user,$domain,$subject,$message,$sendback)=@_; my $status=''; @@ -205,7 +356,7 @@ sub user_crit_msg { sub user_crit_received { my $msgid=shift; my %message=&Apache::lonnet::get('critical',[$msgid]); - my %contents=&unpackagemsg($message{$msgid}); + my %contents=&unpackagemsg($message{$msgid},1); my $status='rec: '.($contents{'sendback'}? &user_normal_msg($contents{'sendername'},$contents{'senderdomain'}, 'Receipt: '.$ENV{'user.name'}.' at '.$ENV{'user.domain'}, @@ -229,14 +380,15 @@ sub user_crit_received { # ======================================================== Normal communication sub user_normal_msg_raw { - my ($user,$domain,$subject,$message,$citation)=@_; + my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl)=@_; # Check if allowed missing my $status=''; my $msgid='undefined'; unless (($message)&&($user)&&($domain)) { $status='empty'; }; my $homeserver=&Apache::lonnet::homeserver($user,$domain); if ($homeserver ne 'no_host') { - ($msgid,$message)=&packagemsg($subject,$message,$citation); + ($msgid,$message)=&packagemsg($subject,$message,$citation,$baseurl, + $attachmenturl); $status=&Apache::lonnet::critical( 'put:'.$domain.':'.$user.':nohist_email:'. &Apache::lonnet::escape($msgid).'='. @@ -246,6 +398,12 @@ sub user_normal_msg_raw { } else { $status='no_host'; } +# Notifications + my %userenv = &Apache::lonnet::get('environment',['notification'], + $domain,$user); + if ($userenv{'notification'}) { + &sendnotification($userenv{'notification'},$user,$domain,$subject,0); + } &Apache::lonnet::log($ENV{'user.domain'},$ENV{'user.name'}, $ENV{'user.home'}, 'Sending '.$msgid.' to '.$user.' at '.$domain.' with status: '.$status); @@ -254,8 +412,16 @@ sub user_normal_msg_raw { # New routine that respects "forward" and calls old routine +=pod + +=item * B: Sends a message to the + $user at $domain, with subject $subject and message $message. + +=cut + sub user_normal_msg { - my ($user,$domain,$subject,$message,$citation)=@_; + my ($user,$domain,$subject,$message,$citation,$baseurl,$attachmenturl)=@_; my $status=''; my %userenv = &Apache::lonnet::get('environment',['msgforward'], $domain,$user); @@ -265,11 +431,11 @@ sub user_normal_msg { my ($forwuser,$forwdomain)=split(/\:/,$_); $status.= &user_normal_msg_raw($forwuser,$forwdomain,$subject,$message, - $citation).' '; + $citation,$baseurl,$attachmenturl).' '; } } else { - $status= - &user_normal_msg_raw($user,$domain,$subject,$message,$citation); + $status=&user_normal_msg_raw($user,$domain,$subject,$message, + $citation,$baseurl,$attachmenturl); } return $status; } @@ -387,7 +553,8 @@ $content{'sendername'}.'@'. } # Check to see if there were any messages. if ($result eq '') { - $result = "

You have no critical messages.

"; + $result = "

You have no critical messages.

". + 'Select a course'; } else { $r->print($header); } @@ -400,11 +567,15 @@ $content{'sendername'}.'@'. sub comprep { my ($r,$msgid)=@_; my %message=&Apache::lonnet::get('nohist_email',[$msgid]); - my %content=&unpackagemsg($message{$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='Re: '.$content{'subject'}; my $dispcrit=''; if (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'})) { @@ -418,6 +589,7 @@ sub comprep { $r->print(<<"ENDREPLY");
+To: $torepl
Subject:

$dispcrit @@ -579,6 +754,49 @@ ENDUPLOAD $r->print('

'); } +# ---------------------------------------------------- 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/\/g; + if ($content{'subject'}=~/^Record/) { + $result.='

Record

'; + } else { + $result.='

Sent Message

'; + %content=&unpackagemsg($content{'message'}); + $content{'message'}= + 'Subject: '.$content{'subject'}.'
'. + $content{'message'}; + } + $result.='By: '. +&Apache::loncommon::aboutmewrapper( + &Apache::loncommon::plainname($content{'sendername'},$content{'senderdomain'}),$content{'sendername'},$content{'senderdomain'}).' ('. +$content{'sendername'}.'@'. + $content{'senderdomain'}.') '.$content{'time'}. + '
'. + &Apache::lontexconvert::msgtexconverted($content{'message'}). + '
'; + } + # Check to see if there were any messages. + if ($result eq '') { + $r->print("

No notes, face-to-face discussion records, or critical messages in this course.

"); + } else { + $r->print($result); + } +} + # ---------------------------------------------------------------- Face to face sub facetoface { @@ -586,16 +804,25 @@ sub facetoface { 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"); -

User Records of Face-To-Face Discussions and Messages in Course

-
+

User Notes, Records of Face-To-Face Discussions, and Critical Messages in Course

+ @@ -607,9 +834,15 @@ ENDTREC ($ENV{'form.recdomain'}) && ($ENV{'form.recuname'})) { chomp($ENV{'form.newrecord'}); if ($ENV{'form.newrecord'}) { - my $subject= - 'Record ['.$ENV{'form.recuname'}.':'.$ENV{'form.recdomain'}.']'; + &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('

'.&Apache::loncommon::plainname($ENV{'form.recuname'}, + $ENV{'form.recdomain'}).'

'); + &disfacetoface($r,$ENV{'form.recuname'},$ENV{'form.recdomain'}); $r->print(< @@ -618,6 +851,9 @@ ENDRHEAD $r->print(<New Record (record is visible to course faculty and staff)
+
+ + ENDBFORM } @@ -644,14 +880,23 @@ sub handler { # ------------------------------------------------------ They checked for email &Apache::lonnet::put('email_status',{'recnewemail'=>0}); # --------------------------------------------------------------- Render Output - - $r->print('EMail and Messaging'. - &Apache::loncommon::bodytag('EMail and Messages')); + if (!$ENV{'form.display'}) { + $r->print('EMail and Messaging'. + &Apache::loncommon::studentbrowser_javascript().''. + &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}); + $r->print('EMail and Messaging'); + if (defined($content{'baseurl'})) { + $r->print(""); + } + $r->print(&Apache::loncommon::studentbrowser_javascript(). + ''. + &Apache::loncommon::bodytag('EMail and Messages')); $r->print('Subject: '.$content{'subject'}. '
From: '. &Apache::loncommon::aboutmewrapper( @@ -678,21 +923,21 @@ $content{'sendername'},$content{'senderd } elsif ($ENV{'form.sendreply'}) { my $msgid=$ENV{'form.sendreply'}; my %message=&Apache::lonnet::get('nohist_email',[$msgid]); - my %content=&unpackagemsg($message{$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('Sending critical: '. &user_crit_msg($content{'sendername'}, $content{'senderdomain'}, - $ENV{'form.subject'}, - $ENV{'form.message'}, + &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}), + &Apache::lonfeedback::clear_out_html($ENV{'form.message'}), $ENV{'form.sendbck'})); } else { $r->print('Sending: '.&user_normal_msg($content{'sendername'}, $content{'senderdomain'}, - $ENV{'form.subject'}, - $ENV{'form.message'})); + &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}), + &Apache::lonfeedback::clear_out_html($ENV{'form.message'}))); } if ($ENV{'form.displayedcrit'}) { &discrit($r); @@ -744,7 +989,7 @@ $content{'sendername'},$content{'senderd if ($ENV{'form.forwid'}) { my $msgid=$ENV{'form.forwid'}; my %message=&Apache::lonnet::get('nohist_email',[$msgid]); - %content=&unpackagemsg($message{$msgid}); + %content=&unpackagemsg($message{$msgid},1); &statuschange($msgid,'forwarded'); $ENV{'form.message'}.="\n\n-- Forwarded message --\n\n". $content{'message'}; @@ -776,18 +1021,18 @@ $content{'sendername'},$content{'senderd } foreach (keys %toaddr) { my ($recuname,$recdomain)=split(/\:/,$_); - my $msgtxt=$ENV{'form.message'}; + my $msgtxt=&Apache::lonfeedback::clear_out_html($ENV{'form.message'}); if ($toaddr{$_}) { $msgtxt.='
'.$toaddr{$_}; } if ((($ENV{'form.critmsg'}) || ($ENV{'form.sendbck'})) && (&Apache::lonnet::allowed('srm',$ENV{'request.course.id'}))) { $r->print('Sending critical: '. &user_crit_msg($recuname,$recdomain, - $ENV{'form.subject'}, + &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}), $msgtxt, $ENV{'form.sendbck'})); } else { $r->print('Sending: '.&user_normal_msg($recuname,$recdomain, - $ENV{'form.subject'}, + &Apache::lonfeedback::clear_out_html($ENV{'form.subject'}), $msgtxt, $content{'citation'})); } @@ -811,7 +1056,14 @@ BEGIN { $msgcount=0; } -1; +=pod + +=back + +=cut + +1; + __END__
Username: +$stdbrws
Domain: