/) {
+ my $p = HTML::LCParser->new(\$attachmenturls);
+ while (my $token = $p->get_tag("attachment","filename","post")) {
+ if ($token->[0] eq "attachment") {
+ $num = $token->[1]{id};
+ %{$$attachments{$num}} =();
+ } elsif ($token->[0] eq "filename") {
+ $$attachments{$num}{'filename'} = $p->get_text("/filename");
+ } elsif ($token->[0] eq "post") {
+ my $id = $token->[1]{id};
+ $$attachments{$num}{$id} = $p->get_text("/post");
+ }
+ }
+ } else {
+ %{$$attachments{'0'}} = ();
+ $$attachments{'0'}{'filename'} = $attachmenturls;
+ $$attachments{'0'}{'0'} = 'n';
+ }
+
+ return;
+}
+
+sub fail_redirect {
+ my ($r,$feedurl) = @_;
+ if ($feedurl=~/^\/adm\//) { $feedurl.='?register=1' };
+ my %lt = &Apache::lonlocal::texthash(
+ 'sorr' => 'Sorry, no recipients ...',
+ );
+ my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
+ $r->print(&Apache::loncommon::start_page('Feedback not sent',undef,
+ {'redirect' => [2,$feedurl],
+ 'only_body' => 1,}));
+ $r->print(<
+$lt{'sorr'}
+ENDFAILREDIR
+ $r->print(&Apache::loncommon::end_page());
+}
+
+sub redirect_back {
+ my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$blog,$status,$previous,$sort,$rolefilter,$statusfilter,$sectionpick,$grouppick,$numpicks,$group,$toolarge) = @_;
+ my $sorttag = '';
+ my $roletag = '';
+ my $statustag = '';
+ my $sectag = '';
+ my $grptag = '';
+ my $userpicktag = '';
+ my $qrystr = '';
+ my $prevtag = '';
+
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ &dewrapper(\$feedurl);
+ if ($feedurl=~/^\/adm\//) { $feedurl.='?register=1' };
+ if ($previous > 0) {
+ $qrystr = 'previous='.$previous;
+ if ($feedurl =~ /\?register=1/) {
+ $feedurl .= '&'.$qrystr;
+ } else {
+ $feedurl .= '?'.$qrystr;
+ }
+ $prevtag = '';
+ }
+ if (defined($sort)) {
+ my $sortqry = 'sortposts='.$sort;
+ if (($feedurl =~ /\?register=1/) || ($feedurl =~ /\?previous=/)) {
+ $feedurl .= '&'.$sortqry;
+ } else {
+ $feedurl .= '?'.$sortqry;
+ }
+ $sorttag = '';
+ if (defined($numpicks)) {
+ my $userpickqry = 'totposters='.$numpicks;
+ $feedurl .= '&'.$userpickqry;
+ $userpicktag = '';
+ } else {
+ if (ref($sectionpick) eq 'ARRAY') {
+ $feedurl .= '§ionpick=';
+ $sectag .= '';
+ } else {
+ $feedurl .= '§ionpick='.$sectionpick;
+ $sectag = '';
+ }
+ if (ref($grouppick) eq 'ARRAY') {
+ $feedurl .= '&grouppick=';
+ $sectag .= '';
+ } else {
+ $feedurl .= '&grouppick='.$grouppick;
+ $grptag = '';
+ }
+ if (ref($rolefilter) eq 'ARRAY') {
+ $feedurl .= '&rolefilter=';
+ $roletag .= '';
+ } else {
+ $feedurl .= '&rolefilter='.$rolefilter;
+ $roletag = '';
+ }
+ $feedurl .= '&statusfilter='.$statusfilter;
+ $statustag ='';
+ }
+ }
+ my $grouptag;
+ if ($group ne '') {
+ $grouptag = '';
+ my $refarg;
+ if (exists($env{'form.ref'})) {
+ $refarg = '&ref='.$env{'form.ref'};
+ $grouptag .= '';
+ }
+ if ($feedurl =~ /\?/) {
+ $feedurl .= '&group='.$group.$refarg;
+ } else {
+ $feedurl .= '?group='.$group.$refarg;
+ }
+ }
+ &Apache::lonenc::check_encrypt(\$feedurl);
+ my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
+ my %onload;
+ if ($env{'environment.remote'} ne 'off') {
+ $onload{'onload'} =
+ "if (window.name!='loncapaclient') { this.document.reldt.submit(); self.window.close(); }";
+ }
+ my $start_page=
+ &Apache::loncommon::start_page('Feedback sent',undef,
+ {'redirect' => [0,$feedurl],
+ 'only_body' => 1,
+ 'add_entries' => \%onload});
+ my $end_page = &Apache::loncommon::end_page();
+ $r->print(<
+$typestyle
+Sent $sendsomething message(s), and $sendposts post(s).
+$blog
+$toolarge
+$status
+
+$end_page
+ENDREDIR
+}
+
+sub no_redirect_back {
+ my ($r,$feedurl) = @_;
+ my $nofeed=&mt('Sorry, no feedback possible on this resource ...');
+
+ my %onload;
+ if ($env{'environment.remote'} ne 'off') {
+ $onload{'onload'} =
+ "if (window.name!='loncapaclient') { self.window.close(); }";
+ }
+
+ my %body_options = ('only_body' => 1,
+ 'bgcolor' => '#FFFFFF',
+ 'add_entries' => \%onload,);
+
+ if ($feedurl !~ m{^/adm/feedback}) {
+ $body_options{'rediect'} = [2,$feedurl];
+ }
+ my $start_page=
+ &Apache::loncommon::start_page('Feedback not sent',undef,
+ \%body_options);
+
+ my $end_page = &Apache::loncommon::end_page();
+
+ &Apache::lonenc::check_encrypt(\$feedurl);
+ my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
+ $r->print (<
+$nofeed
+
+$end_page
+ENDNOREDIRTWO
+}
+
+sub screen_header {
+ my ($feedurl,$symb,$group) = @_;
+ my $crscontent = &mt('Question/Comment/Feedback about course content');
+ my $crspolicy = &mt('Question/Comment/Feedback about course policy');
+ my $contribdisc = &mt('Contribution to course discussion of resource');
+ my $anoncontrib = &mt('Anonymous contribution to course discussion of resource');
+ my $namevis = &mt('name only visible to course faculty');
+ my $crstype;
+ if ($env{'request.course.id'}) {
+ $crstype = &Apache::loncommon::course_type();
+ if ($crstype eq 'Community') {
+ $crscontent = &mt('Question/Comment/Feedback about community content');
+ $crspolicy = &mt('Question/Comment/Feedback about community policy');
+ $contribdisc = &mt('Contribution to community discussion of resource');
+ $anoncontrib = &mt('Anonymous contribution to community discussion of resource');
+ $namevis = &mt('name only visible to community facilitators');
+ }
+ }
+ my $msgoptions='';
+ my $discussoptions='';
+ unless (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
+ if (($feedurl=~/^\/res\//) && ($feedurl!~/^\/res\/adm/) && ($env{'user.adv'})) {
+ $msgoptions=
+ '';
+ }
+ my %optionhash=();
+ foreach my $type ('question','comment','policy') {
+ $optionhash{$type}=$env{'course.'.$env{'request.course.id'}.'.'.$type.'.email.text'};
+ }
+ if (&feedback_available(1)) {
+ $msgoptions.=
+ '';
+ }
+ if (&feedback_available(0,1)) {
+ $msgoptions.=
+ '';
+ }
+ if (&feedback_available(0,0,1)) {
+ $msgoptions.=
+ '';
+ }
+ }
+ if (($env{'request.course.id'}) && (!$env{'form.sendmessageonly'})) {
+ my ($blocked,$blocktext) = &Apache::loncommon::blocking_status('boards');
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $realsymb = $symb;
+ if ($symb=~/^bulletin___/) {
+ my $filename=(&Apache::lonnet::decode_symb($symb))[2];
+ $filename=~s|^adm/wrapper/||;
+ $realsymb=&Apache::lonnet::symbread($filename);
+ }
+ if (!$blocked && &discussion_open(undef,$realsymb) &&
+ (&Apache::lonnet::allowed('pch',
+ $env{'request.course.id'}.
+ ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')) ||
+ (($group ne '') && ($symb =~ m{^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$}) && (&check_group_priv($group,'pgd') eq 'ok')))) {
+ $discussoptions='
'.
+
+ ''.&mt('Change Screenname').'';
+ my $blockblog = &Apache::loncommon::blocking_status('blogs');
+ if (!$blockblog) {
+ $discussoptions.= &add_blog_checkbox($crstype);
+ }
+ }
+ }
+ if ($msgoptions) {
+ $msgoptions=''
+ .' '.&mt('Send Feedback').'
'.&Apache::lonhtmlcommon::coursepreflink(&mt('Feedback Settings'),'feedback').'
'
+ .$msgoptions;
+ }
+ if ($discussoptions) {
+ $discussoptions=''
+ .' '.&mt('Discussion Contributions').'
'.&Apache::lonhtmlcommon::coursepreflink(&mt('Discussion Settings'),'discussion').'
'
+ .$discussoptions;
+ }
+ return $msgoptions.$discussoptions;
+}
+
+sub resource_output {
+ my ($feedurl) = @_;
+ my $usersaw=&Apache::lonnet::ssi_body($feedurl);
+ $usersaw=~s/\]*\>//gi;
+ $usersaw=~s/\<\/body\>//gi;
+ $usersaw=~s/\//gi;
+ $usersaw=~s/\<\/html\>//gi;
+ $usersaw=~s/\//gi;
+ $usersaw=~s/\<\/head\>//gi;
+ $usersaw=~s/action\s*\=/would_be_action\=/gi;
+ return $usersaw;
+}
+
+sub clear_out_html {
+ my ($message,$override,$ignore_htmlarea)=@_;
+ if (!$ignore_htmlarea
+ && !&Apache::lonhtmlcommon::htmlareablocked()) { return $message; }
+# Always allow the -tag
+ my %html=(M=>1);
+# Check if more is allowed
+ my $cid=$env{'request.course.id'};
+ if (($env{"course.$cid.allow_limited_html_in_feedback"} =~ m/yes/i) ||
+ ($override)) {
+ # allows
+ #
+ # | |
+ %html=(B=>1, I=>1, P=>1, A=>1, LI=>1, OL=>1, UL=>1, EM=>1,
+ BR=>1, TT=>1, STRONG=>1, BLOCKQUOTE=>1, DIV=>1, IMG=>1,
+ M=>1, ALGEBRA=>1, SUB=>1, SUP=>1, SPAN=>1,
+ H1=>1, H2=>1, H3=>1, H4=>1, H5=>1, H6=>1,
+ TABLE=>1, TR=>1, TD=>1, TH=>1, TBODY=>1);
+
+ }
+# Do the substitution of everything that is not explicitly allowed
+ $message =~ s/\<(\/?\s*(\w+)[^\>\<]*)/
+ {($html{uc($2)}&&(length($1)<1000))?"\<$1":"\<$1"}/ge;
+ $message =~ s/(\\s*(\w+)[^\<\>]*)\>/
+ {($html{uc($2)}&&(length($1)<1000))?"$1\>":"$1\>"}/ge;
+ return $message;
+}
+
+sub assemble_email {
+ my ($message,$prevattempts,$usersaw,$useranswer)=@_;
+ my %lt = &Apache::lonlocal::texthash(
+ 'prev' => 'Previous attempts of student (if applicable)',
+ 'orig' => 'Original screen output (if applicable)',
+ 'corr' => 'Correct Answer(s) (if applicable)',
+ );
+ my $email=<<"ENDEMAIL";
+$message
+ENDEMAIL
+ my $citations=<<"ENDCITE";
+$lt{'prev'}
+$prevattempts
+
+$lt{'orig'}
+$usersaw
+$lt{'corr'}
+$useranswer
+ENDCITE
+ return ($email,$citations);
+}
+
+
+sub feedback_available {
+ my ($question,$course,$policy)=@_;
+ my ($typestyle,%to)=&Apache::lonmsg::decide_receiver('',0,$question,
+ $course,$policy);
+ return scalar(%to);
+}
+
+sub send_msg {
+ my ($title,$feedurl,$email,$citations,$attachmenturl,$symb,%to)=@_;
+ my $status='';
+ my $sendsomething=0;
+ my $restitle = &get_resource_title($symb,$feedurl);
+ if ($title=~/^Error/) { $title=&mt('Feedback').': '.$title; }
+ unless ($title=~/\w/) { $title=&mt('Feedback'); }
+ foreach my $key (keys(%to)) {
+ if ($key) {
+ my ($user,$domain) = split(/\:/,$key,2);
+ if (!defined($user)) {
+ $status.='
'.&mt('Error sending message to [_1], no user specified.',$key);
+ } elsif (!defined($domain)) {
+ $status.='
'.&mt('Error sending message to [_1], no domain specified.',$key);
+ } else {
+ unless (&Apache::lonmsg::user_normal_msg($user,$domain,
+ $title.' ['.$restitle.']',$email,$citations,$feedurl,
+ $attachmenturl,undef,undef,$symb,$restitle)=~/ok/) {
+ $status.='
'.&mt('Error sending message to').' '.$key.'
';
+ } else {
+ $sendsomething++;
+ }
+ }
+ }
+ }
+
+
+ my %record=&Apache::lonnet::restore('_feedback');
+ my ($temp)=keys(%record);
+ unless ($temp=~/^error\:/) {
+ my %newrecord=();
+ $newrecord{'resource'}=$feedurl;
+ $newrecord{'subnumber'}=$record{'subnumber'}+1;
+ unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') {
+ $status.='
'.&mt('Not registered').'
';
+ }
+ }
+
+ return ($status,$sendsomething);
+}
+
+sub adddiscuss {
+ my ($symb,$email,$anon,$attachmenturl,$subject,$group)=@_;
+ my $status='';
+ my $realsymb;
+ if ($symb=~/^bulletin___/) {
+ my $filename=(&Apache::lonnet::decode_symb($symb))[2];
+ $filename=~s|^adm/wrapper/||;
+ $realsymb=&Apache::lonnet::symbread($filename);
+ }
+ my ($cnum,$cdom);
+ if ($env{'request.course.id'}) {
+ $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ }
+ if (&discussion_open(undef,$realsymb) &&
+ (&Apache::lonnet::allowed('pch',$env{'request.course.id'}.
+ ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')) ||
+ (($group ne '') && (&check_group_priv($group,'pgd') eq 'ok') &&
+ ($symb =~ m{^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$})))) {
+
+ my %contrib=('message' => $email,
+ 'sendername' => $env{'user.name'},
+ 'senderdomain' => $env{'user.domain'},
+ 'screenname' => $env{'environment.screenname'},
+ 'plainname' => $env{'environment.firstname'}.' '.
+ $env{'environment.middlename'}.' '.
+ $env{'environment.lastname'}.' '.
+ $env{'enrironment.generation'},
+ 'attachmenturl'=> $attachmenturl,
+ 'subject' => $subject);
+ if ($env{'form.replydisc'}) {
+ $contrib{'replyto'}=(split(/\:\:\:/,$env{'form.replydisc'}))[1];
+ }
+ if ($anon) {
+ $contrib{'anonymous'}='true';
+ }
+ if (($symb) && ($email)) {
+ my $now = time;
+ if ($env{'form.editdisc'}) {
+ $contrib{'ip'}=$ENV{'REMOTE_ADDR'};
+ $contrib{'host'}=$Apache::lonnet::perlvar{'lonHostID'};
+ $contrib{'timestamp'} = $now;
+ $contrib{'history'} = '';
+ my $numoldver = 0;
+ my ($oldsymb,$oldidx)=split(/\:\:\:/,$env{'form.editdisc'});
+ &Apache::lonenc::check_decrypt(\$oldsymb);
+ $oldsymb=~s|(bulletin___\d+___)adm/wrapper/|$1|;
+# get timestamp for last post and history
+ my %oldcontrib=&Apache::lonnet::restore($oldsymb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ if (defined($oldcontrib{$oldidx.':replyto'})) {
+ $contrib{'replyto'} = $oldcontrib{$oldidx.':replyto'};
+ }
+ if (defined($oldcontrib{$oldidx.':history'})) {
+ if ($oldcontrib{$oldidx.':history'} =~ /:/) {
+ my @oldversions = split(/:/,$oldcontrib{$oldidx.':history'});
+ $numoldver = @oldversions;
+ } else {
+ $numoldver = 1;
+ }
+ $contrib{'history'} = $oldcontrib{$oldidx.':history'}.':';
+ }
+ my $numnewver = $numoldver + 1;
+ if (defined($oldcontrib{$oldidx.':subject'})) {
+ if ($oldcontrib{$oldidx.':subject'} =~ /^/) {
+ $contrib{'subject'} = ''.&HTML::Entities::encode($contrib{'subject'},'<>&"').'';
+ $contrib{'subject'} = $oldcontrib{$oldidx.':subject'}.$contrib{'subject'};
+ } else {
+ $contrib{'subject'} = ''.&HTML::Entities::encode($oldcontrib{$oldidx.':subject'},'<>&"').''.&HTML::Entities::encode($contrib{'subject'},'<>&"').'';
+ }
+ }
+ if (defined($oldcontrib{$oldidx.':message'})) {
+ if ($oldcontrib{$oldidx.':message'} =~ /^/) {
+ $contrib{'message'} = ''.&HTML::Entities::encode($contrib{'message'},'<>&"').'';
+ $contrib{'message'} = $oldcontrib{$oldidx.':message'}.$contrib{'message'};
+ } else {
+ $contrib{'message'} = ''.&HTML::Entities::encode($oldcontrib{$oldidx.':message'},'<>&"').''.&HTML::Entities::encode($contrib{'message'},'<>&"').'';
+ }
+ }
+ $contrib{'history'} .= $oldcontrib{$oldidx.':timestamp'};
+ my $put_reply = &Apache::lonnet::putstore($env{'request.course.id'},
+ $oldsymb,$oldidx,\%contrib,
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ $status='Editing class discussion'.($anon?' (anonymous)':'');
+ } else {
+ $status='Adding to class discussion'.($anon?' (anonymous)':'').': '.
+ &Apache::lonnet::store(\%contrib,$symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ }
+ my %storenewentry=($symb => $now);
+ $status.='
'.&mt('Updating discussion time').': '.
+ &Apache::lonnet::put('discussiontimes',\%storenewentry,
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ }
+ my %record=&Apache::lonnet::restore('_discussion');
+ my ($temp)=keys(%record);
+ unless ($temp=~/^error\:/) {
+ my %newrecord=();
+ $newrecord{'resource'}=$symb;
+ $newrecord{'subnumber'}=$record{'subnumber'}+1;
+ $status.='
'.&mt('Registering').': '.
+ &Apache::lonnet::cstore(\%newrecord,'_discussion');
+ }
+ } else {
+ $status.='Failed.';
+ }
+ return $status.'
';
+}
+
+sub get_discussion_info {
+ my ($idx,%contrib) = @_;
+ my $changelast = 0;
+ my $count = 0;
+ my $hiddenflag = 0;
+ my $deletedflag = 0;
+ my ($hidden,$deleted,%info,$newlastdisc);
+ my $version = $contrib{'version'};
+ if ($version) {
+ for (my $id=$version; $id>0; $id--) {
+ my $vkeys=$contrib{$id.':keys'};
+ my @keys=split(/:/,$vkeys);
+ if (grep(/^hidden$/,@keys)) {
+ if (!$hiddenflag) {
+ $hidden = $contrib{$id.':hidden'};
+ $hiddenflag = 1;
+ }
+ } elsif (grep(/^deleted$/,@keys)) {
+ if (!$deletedflag) {
+ $deleted = $contrib{$id.':deleted'};
+ $deletedflag = 1;
+ }
+ } else {
+ if (($hidden !~/\.$id\./) && ($deleted !~/\.$id\./)) {
+ $count++;
+ $info{$count}{'id'} = $id;
+ $info{$count}{'timestamp'}=$contrib{$id.':timestamp'};
+ }
+ }
+ }
+ if ($info{'1'}{'id'} == $idx) {
+ $changelast = 1;
+ if ($count > 1) {
+ $newlastdisc = $info{'2'}{'timestamp'};
+ } else {
+ $newlastdisc = 0;
+ }
+ }
+ }
+ return ($changelast,$newlastdisc);
+}
+
+# ----------------------------------------------------------- Preview function
+
+sub show_preview {
+ my ($r) = @_;
+ &Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
- return OK if $r->header_only;
+ my $start_page=
+ &Apache::loncommon::start_page('Preview',undef,
+ {'only_body' => 1,});
+
+ my $message=&clear_out_html($env{'form.comment'});
+ &newline_to_br(\$message);
+ $message=&Apache::lonspeller::markeduptext($message);
+ $message=&Apache::lontexconvert::msgtexconverted($message);
+ my $subject=&clear_out_html($env{'form.subject'},undef,1);
+ $subject=~s/\n/\
/g;
+ $subject=&Apache::lontexconvert::msgtexconverted($subject);
+
+ my $end_page = &Apache::loncommon::end_page();
+
+ $r->print($start_page
+ .''.&mt('Preview').'
'
+ .&Apache::lonhtmlcommon::start_pick_box()
+ .&Apache::lonhtmlcommon::row_title(&mt('Subject'))
+ .$subject
+ .&Apache::lonhtmlcommon::row_closure()
+ .&Apache::lonhtmlcommon::row_title(&mt('Message'))
+ .$message
+ .&Apache::lonhtmlcommon::row_closure(1)
+ .&Apache::lonhtmlcommon::end_pick_box()
+ .$end_page
+ );
+}
+
+sub contains_block_html {
+ my ($message)=@_;
+ return ($message =~ m{
+ <(br|h1|h2|h3|h4|h5|h6|p|ol|ul|table|pre|address|blockquote|center|div)
+ \s*
+ (\w+\=['"]\w+['"])*
+ \s*
+ (
+ \s*/>|
+ >.*\1\s*>
+ )}xs
+ );
+}
+
+sub newline_to_br {
+ my ($message)=@_;
+ my $newmessage;
+ my $parser=HTML::LCParser->new($message);
+ while (my $token=$parser->get_token()) {
+ if ($token->[0] eq 'T') {
+ my $text=$token->[1];
+ $text=~s/\n/\
/g;
+ $newmessage.=$text;
+ } elsif ($token->[0] eq 'D' || $token->[0] eq 'C') {
+ $newmessage.=$token->[1];
+ } elsif ($token->[0] eq 'PI' || $token->[0] eq 'E') {
+ $newmessage.=$token->[2];
+ } elsif ($token->[0] eq 'S') {
+ $newmessage.=$token->[4];
+ }
+
+ }
+ $$message=$newmessage;
+}
+
+sub generate_preview_button {
+ my ($formname,$fieldname)=@_;
+ unless ($formname) { $formname='mailform'; }
+ unless ($fieldname) { $fieldname='comment'; }
+ my $pre=&mt("Show Preview and Check Spelling");
+ return(<
+
+ENDPREVIEW
+}
+
+sub modify_attachments {
+ my ($r,$currnewattach,$currdelold,$symb,$idx,$attachmenturls,
+ $attachmaxtext,$toolarge)=@_;
+
+ my %lt = &Apache::lonlocal::texthash(
+ 'subj' => 'Subject',
+ 'thfo' => 'The following attachments were part of the most recent saved version of this posting.',
+ 'chth' => 'Check the checkboxes for any you wish to remove.',
+ 'thef' => 'The following attachments have been uploaded for inclusion with this posting.',
+ 'adda' => 'Add a new attachment to this post.',
+ 'stch' => 'Save Changes',
+ );
+ my $js = <
+ function setAction () {
+ document.modattachments.action = document.modattachments.origpage.value;
+ document.modattachments.submit();
+ }
+
+END
+
+ my $start_page =
+ &Apache::loncommon::start_page('Discussion Post Attachments',$js);
+
+ my $orig_subject = &unescape($env{'form.subject'});
+ my $subject=&clear_out_html($orig_subject,undef,1);
+ $subject=~s/\n/\
/g;
+ $subject=&Apache::lontexconvert::msgtexconverted($subject);
+ my $timestamp=$env{'form.timestamp'};
+ my $numoldver=$env{'form.numoldver'};
+
+ my $msg = '';
+ my %attachments = ();
+ my %currattach = ();
+ if ($idx) {
+ &extract_attachments($attachmenturls,$idx,$numoldver,\$msg,\%attachments,\%currattach,$currdelold);
+ }
+ &Apache::lonenc::check_encrypt(\$symb);
+
+ my $end_page =
+ &Apache::loncommon::end_page();
+
+ $r->print(<
+
+
+
+
+
+
+
+
+
+
+END
+ foreach my $item (@{$currnewattach}) {
+ $r->print(''."\n");
+ }
+ foreach my $item (@{$currdelold}) {
+ $r->print(''."\n");
+ }
+ $r->print(<
+
+$end_page
+END
+ return;
+}
+
+sub process_attachments {
+ my ($currnewattach,$currdelold,$keepold) = @_;
+
+ @{$currnewattach}=
+ &Apache::loncommon::get_env_multiple('form.currnewattach');
+ @{$currdelold}=
+ &Apache::loncommon::get_env_multiple('form.deloldattach');
+ if (exists($env{'form.delnewattach'})) {
+ my @currdelnew =
+ &Apache::loncommon::get_env_multiple('form.delnewattach');
+ my @currnew = ();
+ foreach my $newone (@{$currnewattach}) {
+ my $delflag = 0;
+ foreach my $item (@currdelnew) {
+ if ($newone eq $item) {
+ $delflag = 1;
+ last;
+ }
+ }
+ unless ($delflag) {
+ push(@currnew, $newone);
+ }
+ }
+ @{$currnewattach} = @currnew;
+ }
+ @{$keepold} = &Apache::loncommon::get_env_multiple('form.keepold');
+}
+
+sub generate_attachments_button {
+ my ($idx,$attachnum,$ressymb,$now,$currnewattach,$deloldattach,
+ $numoldver,$mode,$blockblog) = @_;
+ my $origpage = $ENV{'REQUEST_URI'};
+ my $att=$attachnum.' '.&mt("attachments");
+ my %lt = &Apache::lonlocal::texthash(
+ 'clic' => 'Click to add/remove attachments',
+ );
+ my $response = (<
+';
+ return $response;
+}
+
+sub extract_attachments {
+ my ($attachmenturls,$idx,$numoldver,$message,$attachments,$currattach,$currdelold) = @_;
+ %{$attachments}=();
+ &get_post_attachments($attachments,$attachmenturls);
+ foreach my $id (sort(keys(%{$attachments}))) {
+ if (exists($$attachments{$id}{$numoldver})) {
+ if (defined($currdelold)) {
+ if (@{$currdelold} > 0) {
+ unless (grep/^$id$/,@{$currdelold}) {
+ $$currattach{$id} = $$attachments{$id}{$numoldver};
+ }
+ } else {
+ $$currattach{$id} = $$attachments{$id}{$numoldver};
+ }
+ } else {
+ $$currattach{$id} = $$attachments{$id}{$numoldver};
+ }
+ }
+ }
+ my @attached = (sort { $a <=> $b } keys(%{$currattach}));
+ if (@attached == 1) {
+ my $id = $attached[0];
+ my $attachurl;
+ if ($attachmenturls =~ m/^/) {
+ $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
+ } else {
+ $attachurl = $$attachments{$id}{'filename'};
+ }
+ $attachurl=~m|/([^/]+)$|;
+ $$message.='
'.
+ $1.'
';
+ &Apache::lonnet::allowuploaded('/adm/feedback',
+ $attachurl);
+ } elsif (@attached > 1) {
+ $$message.='';
+ foreach my $attach (@attached) {
+ my $id = $attach;
+ my $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
+ my ($fname)
+ =($attachurl=~m|/([^/]+)$|);
+ $$message .= '- '.
+ $fname.'
';
+ &Apache::lonnet::allowuploaded('/adm/feedback',
+ $attachurl);
+ }
+ $$message .= '
';
+ }
+}
+
+sub construct_attachmenturl {
+ my ($currnewattach,$keepold,$symb,$idx)=@_;
+ my $oldattachmenturl;
+ my $newattachmenturl;
+ my $startnum = 0;
+ my $currver = 0;
+ if (($env{'form.editdisc'}) && ($idx)) {
+ my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ $oldattachmenturl = $contrib{$idx.':attachmenturl'};
+ if ($contrib{$idx.':history'}) {
+ if ($contrib{$idx.':history'} =~ /:/) {
+ my @oldversions = split(/:/,$contrib{$idx.':history'});
+ $currver = 1 + scalar(@oldversions);
+ } else {
+ $currver = 2;
+ }
+ } else {
+ $currver = 1;
+ }
+ if ($oldattachmenturl) {
+ if ($oldattachmenturl =~ m/^/) {
+ my %attachments = ();
+ my $prevver = $currver-1;
+ &get_post_attachments(\%attachments,$oldattachmenturl);
+ my $numattach = scalar(keys(%attachments));
+ $startnum += $numattach;
+ foreach my $num (sort {$a <=> $b} keys(%attachments)) {
+ $newattachmenturl .= ''.$attachments{$num}{'filename'}.'';
+ foreach my $item (sort {$a <=> $b} keys(%{$attachments{$num}})) {
+ unless ($item eq 'filename') {
+ $newattachmenturl .= ''.$attachments{$num}{$item}.'';
+ }
+ }
+ if (grep/^$num$/,@{$keepold}) {
+ $newattachmenturl .= ''.$attachments{$num}{$prevver}.'';
+ }
+ $newattachmenturl .= '';
+ }
+ } else {
+ $newattachmenturl = ''.&HTML::Entities::encode($oldattachmenturl).'n';
+ unless (grep/^0$/,@{$keepold}) {
+ $newattachmenturl .= 'n';
+ }
+ $newattachmenturl .= '';
+ $startnum ++;
+ }
+ }
+ }
+ for (my $i=0; $i<@{$currnewattach}; $i++) {
+ my $attachnum = $startnum + $i;
+ $newattachmenturl .= ''.&HTML::Entities::encode($$currnewattach[$i]).'n';
+ }
+ return $newattachmenturl;
+}
+
+sub add_blog_checkbox {
+ my ($crstype) = @_;
+ my $checkstatus;
+ if ($env{'form.blog'}) {
+ $checkstatus = 'checked="checked"';
+ }
+ my $output = '
+
+
'."\n";
+ return $output;
+}
+
+sub has_discussion {
+ my $resourcesref = shift;
+ my $navmap = Apache::lonnavmaps::navmap->new();
+ if (defined($navmap)) {
+ my @allres=$navmap->retrieveResources();
+ foreach my $resource (@allres) {
+ if ($resource->hasDiscussion()) {
+ my $ressymb = $resource->wrap_symb();
+ if (ref($resourcesref) eq 'ARRAY') {
+ push(@{$resourcesref}, $ressymb);
+ }
+ }
+ }
+ } else {
+ &Apache::lonnet::logthis('Has discussion check failed - could not create navmap object.');
+ }
+ return;
+}
+
+sub sort_filter_names {
+ my ($sort_types,$role_types,$status_types,$crstype) = @_;
+ if (ref($sort_types) eq 'HASH') {
+ %{$sort_types} = (
+ ascdate => 'Date order - oldest first',
+ descdate => 'Date order - newest first',
+ thread => 'Threaded',
+ subject => 'By subject',
+ username => 'By domain and username',
+ lastfirst => 'By last name, first name'
+ );
+ }
+ my @courseroles = qw(st in ta ep ad);
+ if ($crstype eq 'Community') {
+ push(@courseroles,'co');
+ } else {
+ push(@courseroles,'cc');
+ }
+ if (ref($role_types) eq 'HASH') {
+ foreach my $role (@courseroles) {
+ $role_types->{$role} = &Apache::lonnet::plaintext($role,$crstype);
+ }
+ $role_types->{'all'} = 'All roles';
+ $role_types->{'cr'} = 'Custom role';
+ }
+ if (ref($status_types) eq 'HASH') {
+ %{$status_types} = (
+ all => 'Roles of any status',
+ Active => 'Only active roles',
+ Expired => 'Only past roles',
+ Future => 'Only future roles',
+ );
+ }
+}
+
+sub handler {
+ my $r = shift;
+ if ($r->header_only) {
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ return OK;
+ }
+
+# --------------------------- Get query string for limited number of parameters
+
+ &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+ ['hide','unhide','deldisc','postdata','preview','replydisc','editdisc','cmd','symb','onlyunread','allposts','onlyunmark','previous','markread','markonread','markondisp','toggoff','toggon','modifydisp','changes','navtime','navmaps','navurl','sortposts','applysort','rolefilter','statusfilter','sectionpick','groupick','posterlist','userpick','attach','origpage','currnewattach','deloldattach','keepold','allversions','export','sendmessageonly','group','ref']);
+ my $group = $env{'form.group'};
+ my %attachmax = (
+ text => &mt('(128 KB max size)'),
+ num => 131072,
+ );
+ if ($env{'form.editdisc'}) {
+ if (!(&editing_allowed($env{'form.editdisc'},$env{'form.group'}))) {
+ my $symb=(split(/\:\:\:/,$env{'form.editdisc'}))[0];
+ my ($map,$id,$url)=&Apache::lonnet::decode_symb($symb);
+ my $feedurl=&Apache::lonnet::clutter($url);
+ &redirect_back($r,$feedurl,&mt('Editing not permitted').'
', '0','0','','',$env{'form.previous'},undef,undef,undef,
+ undef,undef,undef,$group);
+ return OK;
+ }
+ }
+ if ($env{'form.discsymb'}) {
+ my ($symb,$feedurl) = &get_feedurl_and_clean_symb($env{'form.discsymb'});
+ my $readkey = $symb.'_read';
+ my $chgcount = 0;
+ my %readinghash = &Apache::lonnet::get('nohist_'.$env{'request.course.id'}.'_discuss',[$readkey],$env{'user.domain'},$env{'user.name'});
+ foreach my $key (keys(%env)) {
+ if ($key =~ m/^form\.postunread_(\d+)/) {
+ if ($readinghash{$readkey} =~ /\.$1\./) {
+ $readinghash{$readkey} =~ s/\.$1\.//;
+ $chgcount ++;
+ }
+ } elsif ($key =~ m/^form\.postread_(\d+)/) {
+ unless ($readinghash{$readkey} =~ /\.$1\./) {
+ $readinghash{$readkey} .= '.'.$1.'.';
+ $chgcount ++;
+ }
+ }
+ }
+ if ($chgcount > 0) {
+ &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
+ \%readinghash,$env{'user.domain'},$env{'user.name'});
+ }
+ &redirect_back($r,$feedurl,&mt('Marked postings read/unread').'
',
+ '0','0','','',$env{'form.previous'},'','','',
+ undef,undef,undef,$group);
+ return OK;
+ }
+ if ($env{'form.allversions'}) {
+ &Apache::loncommon::content_type($r,'text/html');
+ &Apache::loncommon::no_cache($r);
+ $r->send_http_header;
+ $r->print(&Apache::loncommon::start_page('Discussion Post Versions'));
+
+ my $crs='/'.$env{'request.course.id'};
+ if ($env{'request.course.sec'}) {
+ $crs.='_'.$env{'request.course.sec'};
+ }
+ $crs=~s|_|/|g;
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my ($symb,$idx)=split(/\:\:\:/,$env{'form.allversions'});
+ ($symb)=&get_feedurl_and_clean_symb($symb);
+ my $ressymb = &wrap_symb($symb);
+ my $group = $env{'form.group'};
+ my $seeid;
+ if (($group ne '') && (($ressymb =~ m|^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$|))) {
+ if (&check_group_priv($group,'dgp') eq 'ok') {
+ $seeid = 1;
+ }
+ } else {
+ $seeid = &Apache::lonnet::allowed('rin',$crs);
+ }
+ if ($idx > 0) {
+ my %messages = ();
+ my %subjects = ();
+ my %attachmsgs = ();
+ my %allattachments = ();
+ my %imsfiles = ();
+ my ($screenname,$plainname);
+ my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ $r->print(&get_post_contents(\%contrib,$idx,$seeid,'allversions',\%messages,\%subjects,\%allattachments,\%attachmsgs,\%imsfiles,\$screenname,\$plainname));
+ }
+ $r->print(&Apache::loncommon::end_page());
+ return OK;
+ }
+ if ($env{'form.posterlist'}) {
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.applysort'});
+ &print_showposters($r,$symb,$env{'form.previous'},$feedurl,
+ $env{'form.sortposts'});
+ return OK;
+ }
+ if ($env{'form.userpick'}) {
+ my @posters = &Apache::loncommon::get_env_multiple('form.stuinfo');
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.userpick'});
+ my $numpicks = @posters;
+ my %discinfo;
+ $discinfo{$symb.'_userpick'} = join('&',@posters);
+ &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
+ \%discinfo,$env{'user.domain'},$env{'user.name'});
+ &redirect_back($r,$feedurl,&mt('Changed sort/filter').'
','0','0','',
+ '',$env{'form.previous'},$env{'form.sortposts'},'','','',
+ '',$numpicks,$group);
+ return OK;
+ }
+ if ($env{'form.applysort'}) {
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.applysort'});
+ &redirect_back($r,$feedurl,&mt('Changed sort/filter').'
','0','0','',
+ '',$env{'form.previous'},$env{'form.sortposts'},
+ $env{'form.rolefilter'},$env{'form.statusfilter'},
+ $env{'form.sectionpick'},$env{'form.grouppick'},
+ undef,$group);
+ return OK;
+ } elsif ($env{'form.cmd'} eq 'sortfilter') {
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'});
+ &print_sortfilter_options($r,$symb,$env{'form.previous'},$feedurl);
+ return OK;
+ } elsif ($env{'form.navtime'}) {
+ my %discinfo = ();
+ my @resources = ();
+ if (defined($env{'form.navmaps'})) {
+ if ($env{'form.navmaps'} =~ /:/) {
+ @resources = split(/:/,$env{'form.navmaps'});
+ } else {
+ @resources = ("$env{'form.navmaps'}");
+ }
+ } else {
+ &has_discussion(\@resources);
+ }
+ my $numitems = @resources;
+ my $feedurl = '/adm/navmaps';
+ if ($env{'form.navurl'}) { $feedurl .= '?'.$env{'form.navurl'}; }
+ my %lt = &Apache::lonlocal::texthash(
+ 'mnpa' => 'Marked "New" posts as read in a total of',
+ 'robb' => 'resources/bulletin boards.',
+ 'twnp' => 'There are currently no resources or discussion boards with unread discussion postings.'
+ );
+ foreach my $res (@resources) {
+ my $ressymb=$res;
+ &Apache::lonenc::check_decrypt(\$ressymb);
+ my $lastkey = $ressymb.'_lastread';
+ $discinfo{$lastkey} = $env{'form.navtime'};
+ }
+ my $textline = "$lt{'mnpa'} $numitems $lt{'robb'}";
+ if ($numitems > 0) {
+ &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
+ \%discinfo,$env{'user.domain'},$env{'user.name'});
+ } else {
+ $textline = "$lt{'twnp'}";
+ }
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
+ my %onload;
+ if ($env{'environment.remote'} ne 'off') {
+ $onload{'onload'} =
+ "if (window.name!='loncapaclient') { this.document.reldt.submit(); self.window.close(); }";
+ }
+
+ my $start_page=
+ &Apache::loncommon::start_page('New posts marked as read',undef,
+ {'redirect' => [2,$feedurl],
+ 'only_body' => 1,
+ 'add_entries' => \%onload});
+ my $end_page = &Apache::loncommon::end_page();
+ $r->print (<
+$textline
+
+
+$end_page
+ENDREDIR
+ return OK;
+ } elsif ($env{'form.modifydisp'}) {
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.modifydisp'});
+ my ($dispchgA,$dispchgB,$markchg,$toggchg) =
+ split(/_/,$env{'form.changes'});
+ &print_display_options($r,$symb,$env{'form.previous'},$dispchgA,
+ $dispchgB,$markchg,$toggchg,$feedurl);
+ return OK;
+ } elsif ($env{'form.markondisp'} || $env{'form.markonread'} ||
+ $env{'form.allposts'} || $env{'form.onlyunread'} ||
+ $env{'form.onlyunmark'} || $env{'form.toggoff'} ||
+ $env{'form.toggon'} || $env{'form.markread'}) {
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'});
+ my %discinfo;
+# ------------------------ Modify setting for read/unread toggle for each post
+ if ($env{'form.toggoff'}) { $discinfo{$symb.'_readtoggle'}=0; }
+ if ($env{'form.toggon'}) { $discinfo{$symb.'_readtoggle'}=1; }
+# --------- Modify setting for identification of 'NEW' posts in this discussion
+ if ($env{'form.markondisp'}) {
+ $discinfo{$symb.'_lastread'} = time;
+ $discinfo{$symb.'_markondisp'} = 1;
+ }
+ if ($env{'form.markonread'}) {
+ if ( $env{'form.previous'} > 0 ) {
+ $discinfo{$symb.'_lastread'} = $env{'form.previous'};
+ }
+ $discinfo{$symb.'_markondisp'} = 0;
+ }
+# --------------------------------- Modify display setting for this discussion
+ if ($env{'form.allposts'}) {
+ $discinfo{$symb.'_showonlyunread'} = 0;
+ $discinfo{$symb.'_showonlyunmark'} = 0;
+ }
+ if ($env{'form.onlyunread'}) { $discinfo{$symb.'_showonlyunread'} = 1; }
+ if ($env{'form.onlyunmark'}) { $discinfo{$symb.'_showonlyunmark'} = 1; }
+# ----------------------------------------------------- Mark new posts not NEW
+ if ($env{'form.markread'}) { $discinfo{$symb.'_lastread'} = time; }
+ &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
+ \%discinfo,$env{'user.domain'},$env{'user.name'});
+ my $previous=$env{'form.previous'};
+ if ($env{'form.markondisp'}) { $previous=undef; }
+ &redirect_back($r,$feedurl,&mt('Changed display status').'
',
+ '0','0','','',$previous,'','','','','','',$group);
+ return OK;
+ } elsif (($env{'form.hide'}) || ($env{'form.unhide'})) {
+# ----------------------------------------------------------------- Hide/unhide
+ my $entry=$env{'form.hide'}?$env{'form.hide'}:$env{'form.unhide'};
+ my ($symb,$idx)=split(/\:\:\:/,$entry);
+ ($symb,my $feedurl)=&get_feedurl_and_clean_symb($symb);
+
+ my $crs='/'.$env{'request.course.id'};
+ if ($env{'request.course.sec'}) {
+ $crs.='_'.$env{'request.course.sec'};
+ }
+ $crs=~s/\_/\//g;
+ my $seeid=&Apache::lonnet::allowed('rin',$crs);
+
+ if ($env{'form.hide'} && !$seeid && !(&editing_allowed($env{'form.hide'},$env{'form.group'}))) {
+ &redirect_back($r,$feedurl,&mt('Deletion not permitted').'
', '0','0','','',$env{'form.previous'},'','','','',
+ undef,undef,$group,);
+ return OK;
+ }
+
+ my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+
+ my $currenthidden=$contrib{'hidden'};
+ my $currentstudenthidden=$contrib{'studenthidden'};
+
+ if ($env{'form.hide'}) {
+ $currenthidden.='.'.$idx.'.';
+ unless ($seeid) {
+ $currentstudenthidden.='.'.$idx.'.';
+ }
+ } else {
+ $currenthidden=~s/\.$idx\.//g;
+ }
+ my %newhash=('hidden' => $currenthidden);
+ if ( ($env{'form.hide'}) && (!$seeid) ) {
+ $newhash{'studenthidden'} = $currentstudenthidden;
+ }
+ if ($env{'form.hide'}) {
+ my $changelast = 0;
+ my $newlast;
+ ($changelast,$newlast) = &get_discussion_info($idx,%contrib);
+ if ($changelast) {
+ &Apache::lonnet::put('discussiontimes',{$symb => $newlast},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ }
+ }
+ &Apache::lonnet::store(\%newhash,$symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+
+ &redirect_back($r,$feedurl,&mt('Changed discussion status').'
',
+ '0','0','','',$env{'form.previous'},undef,undef,undef,
+ undef,undef,undef,$group);
+ return OK;
+ } elsif ($env{'form.cmd'}=~/^(threadedoff|threadedon)$/) {
+ my ($symb,$feedurl)=&get_feedurl_and_clean_symb($env{'form.symb'});
+ if ($env{'form.cmd'} eq 'threadedon') {
+ &Apache::lonnet::put('environment',{'threadeddiscussion' => 'on'});
+ &Apache::lonnet::appenv({'environment.threadeddiscussion' => 'on'});
+ } else {
+ &Apache::lonnet::del('environment',['threadeddiscussion']);
+ &Apache::lonnet::delenv('environment.threadeddiscussion');
+ }
+ &redirect_back($r,$feedurl,&mt('Changed discussion view mode').'
',
+ '0','0','','',$env{'form.previous'},undef,undef,undef,
+ undef,undef,undef,$group);
+ return OK;
+ } elsif ($env{'form.deldisc'}) {
+# --------------------------------------------------------------- Hide for good
+ my ($symb,$idx)=split(/\:\:\:/,$env{'form.deldisc'});
+ ($symb,my $feedurl)=&get_feedurl_and_clean_symb($symb);
+ my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ my ($changelast,$newlast) = &get_discussion_info($idx,%contrib);
+ if ($changelast) {
+ &Apache::lonnet::put('discussiontimes',{$symb => $newlast},
+ $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'});
+ }
+ my %newhash=('deleted' => $contrib{'deleted'}.".$idx.");
+ &Apache::lonnet::store(\%newhash,$symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ &redirect_back($r,$feedurl,&mt('Changed discussion status').'
',
+ '0','0','','',$env{'form.previous'},undef,undef,undef,
+ undef,undef,undef,$group);
+ return OK;
+ } elsif ($env{'form.preview'}) {
+# -------------------------------------------------------- User wants a preview
+ &show_preview($r);
+ return OK;
+ } elsif ($env{'form.attach'}) {
+# -------------------------------------------------------- Work on attachments
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['subject','comment','addnewattach','delnewattach','timestamp','numoldver','idx','discuss','blog']);
+ my (@currnewattach,@currdelold,@keepold,$toolarge);
+ &process_attachments(\@currnewattach,\@currdelold,\@keepold);
+ if (exists($env{'form.addnewattach.filename'})) {
+ if (length($env{'form.addnewattach'})<=$attachmax{'num'}) {
+ my $subdir = 'feedback/'.$env{'form.timestamp'};
+ my $newattachment=&Apache::lonnet::userfileupload('addnewattach',undef,$subdir);
+ push(@currnewattach, $newattachment);
+ } else {
+ $toolarge = ''.&mt('Attachment not included - exceeded permitted length').'
';
+ }
+ }
+ my $attachmenturls;
+ my ($symb) = &get_feedurl_and_clean_symb($env{'form.attach'});
+ my $idx = $env{'form.idx'};
+ if ($idx) {
+ my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
+ $env{'course.'.$env{'request.course.id'}.'.domain'},
+ $env{'course.'.$env{'request.course.id'}.'.num'});
+ $attachmenturls = $contrib{$idx.':attachmenturl'};
+ }
+ &modify_attachments($r,\@currnewattach,\@currdelold,$symb,$idx,
+ $attachmenturls,$attachmax{'text'},$toolarge);
+ return OK;
+ } elsif ($env{'form.export'}) {
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ my ($symb,$feedurl) = &get_feedurl_and_clean_symb($env{'form.export'});
+ my $mode='board';
+ my $status='OPEN';
+ my $previous=$env{'form.previous'};
+ if ($feedurl =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/) {
+ $mode='problem';
+ $status=$Apache::inputtags::status[-1];
+ }
+ my $discussion = &list_discussion($mode,$status,$symb);
+ my $start_page =
+ &Apache::loncommon::start_page('Resource Feedback and Discussion');
+ my $end_page =
+ &Apache::loncommon::end_page();
+ $r->print($start_page.$discussion.$end_page);
+ return OK;
+ } else {
+# ------------------------------------------------------------- Normal feedback
+ my $feedurl=$env{'form.postdata'};
+ $feedurl=~s/^https?\:\/\///;
+ $feedurl=~s/^$ENV{'SERVER_NAME'}//;
+ $feedurl=~s/^$ENV{'HTTP_HOST'}//;
+ $feedurl=~s/\?.+$//;
+
+ my $symb;
+ if ($env{'form.replydisc'}) {
+ $symb=(split(/\:\:\:/,$env{'form.replydisc'}))[0];
+ } elsif ($env{'form.editdisc'}) {
+ $symb=(split(/\:\:\:/,$env{'form.editdisc'}))[0];
+ } elsif ($env{'form.origpage'}) {
+ $symb="";
+ } else {
+ $symb=&Apache::lonnet::symbread($feedurl);
+ }
+ unless ($symb) {
+ $symb=$env{'form.symb'};
+ }
+ if (defined($symb)) {
+ ($symb,$feedurl)=&get_feedurl_and_clean_symb($symb);
+ } else {
+ # backward compatibility (bulletin boards used to be 'wrapped')
+ &Apache::lonenc::check_decrypt(\$feedurl);
+ &dewrapper(\$feedurl);
+ }
+ my $goahead=1;
+ if ($feedurl=~/\.(problem|exam|quiz|assess|survey|form|task)$/) {
+ unless ($symb) { $goahead=0; }
+ }
+ if (!$goahead) {
+ # Ambiguous Problem Resource
+ $r->internal_redirect('/adm/ambiguous');
+ return OK;
+ }
+# Go ahead with feedback, no ambiguous reference
+ unless (
+ (
+ ($feedurl=~m:^/res:) && ($feedurl!~m:^/res/adm:)
+ )
+ ||
+ ($env{'request.course.id'} && ($feedurl!~m:^/adm:))
+ ||
+ ($env{'request.course.id'} && ($symb=~/^bulletin\_\_\_/))
+ ) {
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+# Unable to give feedback
+ &Apache::lonenc::check_encrypt(\$feedurl);
+ &no_redirect_back($r,$feedurl);
+ return OK;
+ }
# --------------------------------------------------- Print login screen header
- $r->print(<
-
-The LearningOnline Network with CAPA
-
-
-Feedback
-
-
-
-ENDDOCUMENT
- return OK;
+ unless ($env{'form.sendit'}) {
+ &Apache::lonenc::check_encrypt(\$feedurl);
+ &Apache::loncommon::content_type($r,'text/html');
+ $r->send_http_header;
+ if (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
+ my ($blocked,$blocktext) =
+ &Apache::loncommon::blocking_status('boards');
+ if ($blocked) {
+ $r->print(&blocked_reply_or_edit($blocktext));
+ return OK;
+ }
+ }
+ my $options=&screen_header($feedurl,$symb,$group);
+ if ($options) {
+ &mail_screen($r,$feedurl,$options,$symb,$attachmax{'text'});
+ } else {
+ &fail_redirect($r,$feedurl);
+ }
+ return OK;
+ }
+
+# Get previous user input
+ my $prevattempts=&Apache::loncommon::get_previous_attempt(
+ $symb,$env{'user.name'},$env{'user.domain'},
+ $env{'request.course.id'});
+
+# Get output from resource
+ &Apache::lonenc::check_encrypt(\$feedurl);
+ my $usersaw=&resource_output($feedurl);
+
+# Get resource answer (need to allow student to view grades for this to work)
+ &Apache::lonnet::appenv({'allowed.vgr'=>'F'});
+ my $usersymb = &Apache::lonenc::check_encrypt($symb);
+ my $useranswer=
+ &Apache::loncommon::get_student_answers(
+ $usersymb,$env{'user.name'},$env{'user.domain'},
+ $env{'request.course.id'});
+ &Apache::lonnet::delenv('allowed.vgr');
+# Get attachments, if any, and not too large
+ my $attachmenturl='';
+ my $toolarge='';
+ if (($env{'form.origpage'}) || ($env{'form.editdisc'}) ||
+ ($env{'form.replydisc'})) {
+ my ($symb,$idx);
+ if ($env{'form.replydisc'}) {
+ ($symb,$idx)=split(/\:\:\:/,$env{'form.replydisc'});
+ } elsif ($env{'form.editdisc'}) {
+ ($symb,$idx)=split(/\:\:\:/,$env{'form.editdisc'});
+ } elsif ($env{'form.origpage'}) {
+ $symb = $env{'form.symb'};
+ }
+ &Apache::lonenc::check_decrypt(\$symb);
+ my @currnewattach = ();
+ my @deloldattach = ();
+ my @keepold = ();
+ &process_attachments(\@currnewattach,\@deloldattach,\@keepold);
+ $symb=~s|(bulletin___\d+___)adm/wrapper/|$1|;
+ $attachmenturl=&construct_attachmenturl(\@currnewattach,\@keepold,$symb,$idx);
+ } elsif ($env{'form.attachment.filename'}) {
+ if (length($env{'form.attachment'})<=$attachmax{'num'}) {
+ my $now = time;
+ my $subdir = 'feedback/'.$now;
+ $attachmenturl=&Apache::lonnet::userfileupload('attachment',undef,$subdir);
+ } else {
+ $toolarge = ''.&mt('Attachment not included - exceeded permitted length').'
';
+ }
+ }
+# Filter HTML out of message (could be nasty)
+ my $message=&clear_out_html($env{'form.comment'});
+
+# Assemble email
+ my ($email,$citations)=&assemble_email($message,$prevattempts,
+ $usersaw,$useranswer);
+
+# Who gets this?
+ my ($typestyle,%to) = &Apache::lonmsg::decide_receiver($feedurl);
+
+# Actually send mail
+ my ($status,$numsent)=&send_msg(&clear_out_html($env{'form.subject'},
+ undef,1),
+ $feedurl,$email,$citations,
+ $attachmenturl,$usersymb,%to);
+
+# Discussion? Store that.
+ my $numpost=0;
+ if ( ($env{'form.discuss'} ne ''
+ && $env{'form.discuss'} !~ /^(?:author|question|course|policy)/)
+ || $env{'form.anondiscuss'} ne '') {
+ my $subject = &clear_out_html($env{'form.subject'},undef,1);
+ my $anonmode=($env{'form.discuss'} eq 'anon' || $env{'form.anondiscuss'} );
+ $typestyle.=&adddiscuss($symb,$message,$anonmode,$attachmenturl,
+ $subject,$group);
+ $numpost++;
+ }
+
+# Add to blog?
+
+ my $blog='';
+ if ($env{'form.blog'}) {
+ my $subject = &clear_out_html($env{'form.subject'},undef,1);
+ $status.=&Apache::lonrss::addentry($env{'user.name'},
+ $env{'user.domain'},
+ 'CourseBlog_'.$env{'request.course.id'},
+ $subject,$message,$feedurl,'public');
+ if (&Apache::loncommon::course_type() eq 'Community') {
+ $blog='
'.&mt('Added to my community blog').'
';
+ } else {
+ $blog='
'.&mt('Added to my course blog').'
';
+ }
+ }
+
+# Receipt screen and redirect back to where came from
+ &redirect_back($r,$feedurl,$typestyle,$numsent,$numpost,$blog,$status,$env{'form.previous'},undef,undef,undef,undef,undef,undef,$group,$toolarge);
+ }
+ return OK;
+}
+
+sub blocked_reply_or_edit {
+ my ($blocktext) = @_;
+ return
+ &Apache::loncommon::start_page('Resource Feedback and Discussion').
+ $blocktext.'
'.
+ &mt('Back to previous page').
+ &Apache::loncommon::end_page();
}
+sub wrap_symb {
+ my ($ressymb)=@_;
+ if ($ressymb =~ /bulletin___\d+___/) {
+ unless ($ressymb =~ m|bulletin___\d+___adm/wrapper|) {
+ $ressymb=~s|(bulletin___\d+___)|$1adm/wrapper|;
+ }
+ }
+ return $ressymb;
+}
+sub dewrapper {
+ my ($feedurl)=@_;
+ if ($$feedurl=~m|^/adm/wrapper/adm/.*/bulletinboard$|) {
+ $$feedurl=~s|^/adm/wrapper||;
+ }
+}
+
+sub get_feedurl {
+ my ($symb)=@_;
+ my ($map,$ind,$url)=&Apache::lonnet::decode_symb($symb);
+ my $feedurl = &Apache::lonnet::clutter($url);
+ &dewrapper(\$feedurl);
+ return $feedurl;
+}
+
+sub get_feedurl_and_clean_symb {
+ my ($symb)=@_;
+ &Apache::lonenc::check_decrypt(\$symb);
+# backward compatibility (bulletin boards used to be 'wrapped')
+ unless ($symb =~ m|bulletin___\d+___adm/wrapper|) {
+ $symb=~s|(bulletin___\d+___)|$1adm/wrapper|;
+ }
+ my $feedurl = &get_feedurl($symb);
+ return ($symb,$feedurl);
+}
+
+sub editing_allowed {
+ my ($postid,$group) = @_;
+ $postid = &unescape($postid);
+ my $can_edit = 0;
+ if ($group ne '') {
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ if ($postid =~ m|^bulletin___\d+___adm/wrapper(/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard)|) {
+ if (&check_group_priv($group,'egp') eq 'ok') {
+ $can_edit = 1;
+ }
+ return $can_edit;
+ }
+ }
+ my $cid = $env{'request.course.id'};
+ my $role = (split(/\./,$env{'request.role'}))[0];
+ my $section = $env{'request.course.sec'};
+ my $allow_editing_config =
+ $env{'course.'.$cid.'.allow_discussion_post_editing'};
+ if ($allow_editing_config =~ m/^\s*yes\s*$/i) {
+ $can_edit = 1;
+ } else {
+ foreach my $editor (split(/,/,$allow_editing_config)) {
+ my ($editor_role,$editor_sec) = split(/:/,$editor);
+ if ($editor_role eq $role
+ && defined($editor_sec)
+ && defined($section)
+ && $editor_sec eq $section) {
+ $can_edit = 1;
+ last;
+ }
+ if ($editor_role eq $role
+ && !defined($editor_sec)) {
+ $can_edit = 1;
+ }
+ }
+ }
+ return $can_edit;
+}
+
+sub check_group_priv {
+ my ($group,$grp_priv) = @_;
+ foreach my $priv ('mdg','vcg') {
+ my $checkcourse = $env{'request.course.id'}.
+ ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'');
+ if (&Apache::lonnet::allowed($priv,$checkcourse)) {
+ return 'ok';
+ }
+ }
+ if ($grp_priv && $group ne '') {
+ if (&Apache::lonnet::allowed($grp_priv,$env{'request.course.id'}.'/'.$group)) {
+ return 'ok';
+ }
+ }
+ return '';
+}
+
+sub group_args {
+ my ($group) = @_;
+ if ($group eq '') { return ''; }
+ my $extra_args = '&group='.$group;
+ if (exists($env{'form.ref'})) {
+ $extra_args .= '&ref='.$env{'form.ref'};
+ }
+ return $extra_args;
+}
+
+sub get_resource_title {
+ my ($symb,$feedurl) = @_;
+ my ($restitle,$plainurl);
+ if (defined($symb)) {
+ my $plain_symb = &Apache::lonenc::check_decrypt($symb);
+ $restitle = &Apache::lonnet::gettitle($plain_symb);
+ }
+ if (defined($feedurl)) {
+ $plainurl = &Apache::lonenc::check_decrypt($feedurl);
+ }
+ if (!defined($restitle)) {
+ if (defined($feedurl)) {
+ $restitle = &Apache::lonnet::gettitle($plainurl);
+ }
+ }
+ if ($plainurl ne $feedurl) {
+ my ($plain_filename) = ($plainurl =~ m-/([^/]+)$-);
+ if ($plain_filename eq $restitle) {
+ $restitle = &mt('Untitled resource');
+ }
+ }
+ if ($restitle eq '') {
+ $restitle = &mt('Untitled resource');
+ }
+ return $restitle;
+}
+
1;
__END__
+
+
+=pod
+
+=head1 NAME
+
+Apache::lonfeedback.pm
+
+=head1 SYNOPSIS
+
+Handles feedback from students to instructors and system administrators.
+
+Provides a screenshot of the current resource, as well as previous attempts if the resource was a homework.
+
+Used by lonmsg.pm.
+
+This is part of the LearningOnline Network with CAPA project
+described at http://www.lon-capa.org.
+
+=head1 OVERVIEW
+
+None
+
+=head1 SUBROUTINES
+
+=over
+
+=item discussion_open()
+
+=item discussion_visible()
+
+=item list_discussion()
+
+=item send_feedback_link()
+
+=item send_message_link()
+
+=item action_links_bar()
+
+=item postingform_display()
+
+=item build_posting_display
+
+=item filter_regexp()
+
+=item get_post_contents()
+
+=item replicate_attachments()
+
+=item mail_screen()
+
+=item print_display_options()
+
+=item print_sortfilter_options()
+
+=item print_showposters()
+
+=item get_post_versions()
+
+=item get_post_attachments()
+
+=item fail_redirect()
+
+=item redirect_back()
+
+=item no_redirect_back()
+
+=item screen_header()
+
+=item resource_output()
+
+=item clear_out_html()
+
+=item assemble_email()
+
+=item feedback_available()
+
+=item send_msg()
+
+=item adddiscuss()
+
+=item get_discussion_info()
+
+=item show_preview()
+
+=item newline_to_br()
+
+=item generate_preview_button()
+
+=item modify_attachments()
+
+=item process_attachments()
+
+=item generate_attachments_button()
+
+=item extract_attachments()
+
+=item construct_attachmenturl()
+
+=item add_blog_checkbox()
+
+=item has_discussion()
+
+=item sort_filter_names()
+
+=item handler()
+
+=item blocked_reply_or_edit()
+
+=item wrap_symb()
+
+=item dewrapper()
+
+=item get_feedurl()
+
+=item get_feedurl_and_clean_symb()
+
+=item editing_allowed()
+
+=item check_group_priv()
+
+=item group_args()
+
+=item get_resource_title()
+
+=back
+
+=cut