--- loncom/interface/lonfeedback.pm	2002/01/16 15:38:38	1.23
+++ loncom/interface/lonfeedback.pm	2003/10/15 20:01:48	1.62
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Feedback
 #
-# $Id: lonfeedback.pm,v 1.23 2002/01/16 15:38:38 www Exp $
+# $Id: lonfeedback.pm,v 1.62 2003/10/15 20:01:48 www Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -50,9 +50,133 @@ use strict;
 use Apache::Constants qw(:common);
 use Apache::lonmsg();
 use Apache::loncommon();
+use Apache::lontexconvert();
+use Apache::lonlocal;
+
+sub list_discussion {
+    my ($mode,$status,$symb)=@_;
+    my $discussiononly=0;
+    if ($mode eq 'board') { $discussiononly=1; }
+    unless ($ENV{'request.course.id'}) { return ''; }
+    my $crs='/'.$ENV{'request.course.id'};
+    if ($ENV{'request.course.sec'}) {
+	$crs.='_'.$ENV{'request.course.sec'};
+    }                 
+    $crs=~s/\_/\//g;
+    unless ($symb) {
+	$symb=&Apache::lonnet::symbread();
+    }
+    unless ($symb) { return ''; }
+    my $seeid=&Apache::lonnet::allowed('rin',$crs);
+    my $viewgrades=&Apache::lonnet::allowed('vgr',$crs);
+    my $discussion='';
+    my %contrib=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},
+			  $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
+			  $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
+    if ($contrib{'version'}) {
+	unless ($discussiononly) {
+	    $discussion.=
+		'<address><hr />';
+	}
+	my $idx;
+	for ($idx=1;$idx<=$contrib{'version'};$idx++) {
+	    my $hidden=($contrib{'hidden'}=~/\.$idx\./);
+	    my $deleted=($contrib{'deleted'}=~/\.$idx\./);
+	    unless ((($hidden) && (!$seeid)) || ($deleted)) {
+		my $message=$contrib{$idx.':message'};
+		$message=~s/\n/\<br \/\>/g;
+		$message=&Apache::lontexconvert::msgtexconverted($message);
+		if ($contrib{$idx.':attachmenturl'}) {
+		    my ($fname,$ft)
+                        =($contrib{$idx.':attachmenturl'}=~/\/(\w+)\.(\w+)$/);
+		    $message.='<p>'.&mt('Attachment').': <a href="'.
+			&Apache::lonnet::tokenwrapper(
+                                             $contrib{$idx.':attachmenturl'}).
+			'"><tt>'.$fname.'.'.$ft.'</tt></a>';
+		}
+		if ($message) {
+		    if ($hidden) {
+			$message='<font color="#888888">'.$message.'</font>';
+		    }
+		    my $screenname=&Apache::loncommon::screenname(
+					    $contrib{$idx.':sendername'},
+					    $contrib{$idx.':senderdomain'});
+		    my $plainname=&Apache::loncommon::nickname(
+					    $contrib{$idx.':sendername'},
+					    $contrib{$idx.':senderdomain'});
+		    
+		    my $sender=&mt('Anonymous');
+		    if ((!$contrib{$idx.':anonymous'}) || ($seeid)) {
+			$sender=&Apache::loncommon::aboutmewrapper(
+					 $plainname,
+					 $contrib{$idx.':sendername'},
+					 $contrib{$idx.':senderdomain'}).' ('.
+					 $contrib{$idx.':sendername'}.' at '.
+					 $contrib{$idx.':senderdomain'}.')';
+			if ($contrib{$idx.':anonymous'}) {
+			    $sender.=' ['.&mt('anonymous').'] '.
+				$screenname;
+			}
+			if ($seeid) {
+			    if ($hidden) {
+				$sender.=' <a href="/adm/feedback?unhide='.
+				    $symb.':::'.$idx.'">'.&mt('Make Visible').'</a>';
+			    } else {
+				$sender.=' <a href="/adm/feedback?hide='.
+				    $symb.':::'.$idx.'">'.&mt('Hide').'</a>';
+			    }                     
+			    $sender.=' <a href="/adm/feedback?deldisc='.
+				$symb.':::'.$idx.'">'.&mt('Delete').'</a>';
+			}
+		    } else {
+			if ($screenname) {
+			    $sender='<i>'.$screenname.'</i>';
+			}
+		    }
+		    my $vgrlink;
+		    if ($viewgrades) {
+			$vgrlink=&Apache::loncommon::submlink('Submissions',
+            $contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'},$symb);
+		    }
+		    $discussion.='<p><b>'.$sender.'</b> '.$vgrlink.' ('.
+			localtime($contrib{$idx.':timestamp'}).
+			'):<blockquote>'.$message.
+			'</blockquote></p>';
+	        }
+	    } 
+	}
+	unless ($discussiononly) {
+	    $discussion.='</address>';
+	}
+    }
+    if ($discussiononly) {
+	$discussion.=(<<ENDDISCUSS);
+<form action="/adm/feedback" method="post" name="mailform" enctype="multipart/form-data">
+<input type="submit" name="discuss" value="Post Discussion" />
+<input type="submit" name="anondiscuss" value="Post Anonymous Discussion" />
+<input type="hidden" name="symb" value="$symb" />
+<input type="hidden" name="sendit" value="true" />
+<br />
+<font size="1">Note: in anonymous discussion, your name is visible only to
+course faculty</font><br />
+<textarea name=comment cols=60 rows=10 wrap=hard></textarea>
+<p>
+Attachment (128 KB max size): <input type="file" name="attachment" />
+</p>
+</form>
+ENDDISCUSS
+      $discussion.=&generate_preview_button();
+   }
+   return $discussion;
+}
 
 sub mail_screen {
   my ($r,$feedurl,$options) = @_;
+  my $bodytag=&Apache::loncommon::bodytag('Resource Feedback and Discussion',
+                                          '','onLoad="window.focus();"');
+  my $title=&Apache::lonnet::gettitle($feedurl);
+  if (!$title) { $title = $feedurl; }
+  my $latexHelp = Apache::loncommon::helpLatexCheatsheet();
   $r->print(<<ENDDOCUMENT);
 <html>
 <head>
@@ -100,23 +224,26 @@ sub mail_screen {
     }
 </script>
 </head>
-<body bgcolor="#FFFFFF" onLoad="window.focus();">
-<img align=right src=/adm/lonIcons/lonlogos.gif>
-<h1>Feedback</h1>
-<h2><tt>$feedurl</tt></h2>
-<form action="/adm/feedback" method=post name=mailform>
+$bodytag
+<h2><tt>$title</tt></h2>
+<form action="/adm/feedback" method="post" name="mailform"
+enctype="multipart/form-data">
 <input type=hidden name=postdata value="$feedurl">
 Please check at least one of the following feedback types:
 $options<hr>
 My question/comment/feedback:<p>
+$latexHelp
 <textarea name=comment cols=60 rows=10 wrap=hard>
 </textarea><p>
-<input type=hidden name=sendit value=1>
-<input type=button value="Send Feedback" onClick='gosubmit();'></input>
+Attachment (128 KB max size): <input type="file" name="attachment" />
+</p>
+<p>
+<input type="hidden" name="sendit" value="1" />
+<input type=button value="Send Feedback" onClick='gosubmit();' />
+</p>
 </form>
-</body>
-</html>
 ENDDOCUMENT
+$r->print(&generate_preview_button().'</body></html>');
 }
 
 sub fail_redirect {
@@ -136,7 +263,7 @@ ENDFAILREDIR
 }
 
 sub redirect_back {
-  my ($r,$feedurl,$typestyle,$sendsomething,$status) = @_;
+  my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$status) = @_;
   $r->print (<<ENDREDIR);
 <head>
 <title>Feedback sent</title>
@@ -144,11 +271,13 @@ sub redirect_back {
 <meta HTTP-EQUIV="Refresh" CONTENT="2; url=$feedurl">
 </head>
 <html>
-<body bgcolor="#FFFFFF">
+<body bgcolor="#FFFFFF" onLoad='if (window.name!="loncapaclient") { this.document.reldt.submit(); self.close(); }'>
 <img align=right src=/adm/lonIcons/lonlogos.gif>
 $typestyle
-<b>Sent $sendsomething message(s).</b>
+<b>Sent $sendsomething message(s), and $sendposts post(s).</b>
 <font color=red>$status</font>
+<form name="reldt" action="$feedurl" target="loncapaclient">
+</form>
 </body>
 </html>
 ENDREDIR
@@ -168,7 +297,7 @@ ENDNOREDIR
   $r->print (<<ENDNOREDIRTWO);
 </head>
 <html>
-<body bgcolor="#FFFFFF">
+<body bgcolor="#FFFFFF" onLoad='if (window.name!="loncapaclient") { self.close(); }'>
 <img align=right src=/adm/lonIcons/lonlogos.gif>
 <b>Sorry, no feedback possible on this resource  ...</b>
 </body>
@@ -178,43 +307,49 @@ ENDNOREDIRTWO
 
 sub screen_header {
   my ($feedurl) = @_;
-  my $options='';
-  if (($feedurl=~/^\/res/) && ($feedurl!~/^\/res\/adm/)) {
-    $options= 
-      '<p><input type=checkbox name=author> Feedback to resource author';
-  }
-  if ($ENV{'course.'.$ENV{'request.course.id'}.'.question.email'}) {
-    $options.=
-    '<br><input type=checkbox name=question> Question about resource content';
+  my $msgoptions='';
+  my $discussoptions='';
+  if (($feedurl=~/^\/res\//) && ($feedurl!~/^\/res\/adm/)) {
+    $msgoptions= 
+      '<p><input type=checkbox name=author> '.
+      &mt('Feedback to resource author');
+  }
+  if (&feedback_available(1)) {
+    $msgoptions.=
+    '<br><input type=checkbox name=question> '.
+    &mt('Question about resource content');
   }
-  if ($ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'}) {
-    $options.=
+  if (&feedback_available(0,1)) {
+    $msgoptions.=
       '<br><input type=checkbox name=course> '.
-	'Question/Comment/Feedback about course content';
+	&mt('Question/Comment/Feedback about course content');
   }
-  if ($ENV{'course.'.$ENV{'request.course.id'}.'.policy.email'}) {
-    $options.=
+  if (&feedback_available(0,0,1)) {
+    $msgoptions.=
       '<br><input type=checkbox name=policy> '.
-	'Question/Comment/Feedback about course policy';
+	&mt('Question/Comment/Feedback about course policy');
   }
 
   if ($ENV{'request.course.id'}) {
       if (&Apache::lonnet::allowed('pch',
         $ENV{'request.course.id'}.
         ($ENV{'request.course.sec'}?'/'.$ENV{'request.course.sec'}:''))) {
-    $options.='<br><input type=checkbox name=discuss> '.
-	'<b>Contribution to course discussion of resource</b>';
-    $options.='<br><input type=checkbox name=anondiscuss> '.
-	'<b>Anonymous contribution to course discussion of resource</b>'.
-        ' (name only visible to course faculty)';
+    $discussoptions='<input type="checkbox" name="discuss" onClick="this.form.anondiscuss.checked=false;"> '.
+	&mt('Contribution to course discussion of resource');
+    $discussoptions.='<br><input type="checkbox" name="anondiscuss" onClick="this.form.discuss.checked=false;"> '.
+	&mt('Anonymous contribution to course discussion of resource').
+        ' <i>('.&mt('name only visible to course faculty').')</i>';
       }
   }
-  return $options;
+  if ($msgoptions) { $msgoptions='<h2>'.&mt('Sending Messages').'</h2>'.$msgoptions; }
+  if ($discussoptions) { 
+     $discussoptions='<h2>'.&mt('Discussion Contributions').'</h2>'.$discussoptions; }
+  return $msgoptions.$discussoptions;
 }
 
 sub resource_output {
   my ($feedurl) = @_;
-  my $usersaw=&Apache::lonnet::ssi($feedurl);
+  my $usersaw=&Apache::lonnet::ssi_body($feedurl);
   $usersaw=~s/\<body[^\>]*\>//gi;
   $usersaw=~s/\<\/body\>//gi;
   $usersaw=~s/\<html\>//gi;
@@ -226,14 +361,29 @@ sub resource_output {
 }
 
 sub clear_out_html {
-  my $message=$ENV{'form.comment'};
-  $message=~s/\</\&lt\;/g;
-  $message=~s/\>/\&gt\;/g;
+  my ($message,$override)=@_;
+  my $cid=$ENV{'request.course.id'};
+  if (($ENV{"course.$cid.allow_limited_html_in_feedback"} =~ m/yes/i) ||
+      ($override)) {
+      # allows <B> <I> <P> <A> <LI> <OL> <UL> <EM> <BR> <TT> <STRONG> 
+      # <BLOCKQUOTE> <DIV .*> <DIV> <IMG>
+      my %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);
+
+      $message =~ s/\<(\/?\s*(\w+)[^\>\<]*)/
+	  {($html{uc($2)}&&(length($1)<1000))?"\<$1":"\&lt;$1"}/ge;
+      $message =~ s/(\<?\s*(\w+)[^\<\>]*)\>/
+	  {($html{uc($2)}&&(length($1)<1000))?"$1\>":"$1\&gt;"}/ge;
+  } else {
+      $message=~s/\</\&lt\;/g;
+      $message=~s/\>/\&gt\;/g;
+  }
   return $message;
 }
 
 sub assemble_email {
-  my ($feedurl,$message,$prevattempts,$usersaw)=@_;
+  my ($feedurl,$message,$prevattempts,$usersaw,$useranswer)=@_;
   my $email=<<"ENDEMAIL";
 Refers to <a href="$feedurl">$feedurl</a>
 
@@ -245,58 +395,96 @@ $prevattempts
 <p><hr>
 <h2>Original screen output (if applicable)</h2>
 $usersaw
+<h2>Correct Answer(s) (if applicable)</h2>
+$useranswer
 ENDCITE
   return ($email,$citations);
 }
 
+sub secapply {
+    my $rec=shift;
+    my $defaultflag=shift;
+    $rec=~s/\s+//g;
+    $rec=~s/\@/\:/g;
+    my ($adr,$sections)=($rec=~/^([^\(]+)\(([^\)]+)\)/);
+    if ($sections) {
+	foreach (split(/\;/,$sections)) {
+            if (($_ eq $ENV{'request.course.sec'}) ||
+                ($defaultflag && ($_ eq '*'))) {
+                return $adr; 
+            }
+        }
+    } else {
+       return $rec;
+    }
+    return '';
+}
+
 sub decide_receiver {
-  my ($feedurl) = @_;
+  my ($feedurl,$author,$question,$course,$policy,$defaultflag) = @_;
   my $typestyle='';
   my %to=();
-  if ($ENV{'form.author'}) {
+  if ($ENV{'form.author'}||$author) {
     $typestyle.='Submitting as Author Feedback<br>';
     $feedurl=~/^\/res\/(\w+)\/(\w+)\//;
     $to{$2.':'.$1}=1;
   }
-  if ($ENV{'form.question'}) {
+  if ($ENV{'form.question'}||$question) {
     $typestyle.='Submitting as Question<br>';
-    map {
-      $to{$_}=1;
-    } split(/\,/,
-	    $ENV{'course.'.$ENV{'request.course.id'}.'.question.email'});
+    foreach (split(/\,/,
+		   $ENV{'course.'.$ENV{'request.course.id'}.'.question.email'})
+	     ) {
+	my $rec=&secapply($_,$defaultflag);
+        if ($rec) { $to{$rec}=1; }
+    } 
   }
-  if ($ENV{'form.course'}) {
+  if ($ENV{'form.course'}||$course) {
     $typestyle.='Submitting as Comment<br>';
-    map {
-      $to{$_}=1;
-    } split(/\,/,
-	    $ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'});
+    foreach (split(/\,/,
+		   $ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'})
+	     ) {
+	my $rec=&secapply($_,$defaultflag);
+        if ($rec) { $to{$rec}=1; }
+    } 
   }
-  if ($ENV{'form.policy'}) {
+  if ($ENV{'form.policy'}||$policy) {
     $typestyle.='Submitting as Policy Feedback<br>';
-    map {
-      $to{$_}=1;
-    } split(/\,/,
-	    $ENV{'course.'.$ENV{'request.course.id'}.'.policy.email'});
+    foreach (split(/\,/,
+		   $ENV{'course.'.$ENV{'request.course.id'}.'.policy.email'})
+	     ) {
+	my $rec=&secapply($_,$defaultflag);
+        if ($rec) { $to{$rec}=1; }
+    } 
+  }
+  if ((scalar(%to) eq '0') && (!$defaultflag)) {
+     ($typestyle,%to)=
+	 &decide_receiver($feedurl,$author,$question,$course,$policy,1);
   }
   return ($typestyle,%to);
 }
 
+sub feedback_available {
+    my ($question,$course,$policy)=@_;
+    my ($typestyle,%to)=&decide_receiver('',0,$question,$course,$policy);
+    return scalar(%to);
+}
+
 sub send_msg {
-  my ($feedurl,$email,$citations,%to)=@_;
+  my ($feedurl,$email,$citations,$attachmenturl,%to)=@_;
   my $status='';
   my $sendsomething=0;
-  map {
+  foreach (keys %to) {
     if ($_) {
       my $declutter=&Apache::lonnet::declutter($feedurl);
       unless (&Apache::lonmsg::user_normal_msg(split(/\:/,$_),
-               'Feedback ['.$declutter.']',$email,$citations) eq 'ok') {
-	$status.='<br>Error sending message to '.$_.'<br>';
+               'Feedback ['.$declutter.']',$email,$citations,$feedurl,
+                $attachmenturl)=~/ok/) {
+	$status.='<br>'.&mt('Error sending message to').' '.$_.'<br>';
       } else {
 	$sendsomething++;
       }
     }
-  } keys %to;
+  }
 
     my %record=&Apache::lonnet::restore('_feedback');
     my ($temp)=keys %record;
@@ -305,7 +493,7 @@ sub send_msg {
        $newrecord{'resource'}=$feedurl;
        $newrecord{'subnumber'}=$record{'subnumber'}+1;
        unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') {
-	   $status.='<br>Not registered<br>';
+	   $status.='<br>'.&mt('Not registered').'<br>';
        }
     }
        
@@ -313,14 +501,20 @@ sub send_msg {
 }
 
 sub adddiscuss {
-    my ($symb,$email,$anon)=@_;
+    my ($symb,$email,$anon,$attachmenturl)=@_;
     my $status='';
     if (&Apache::lonnet::allowed('pch',$ENV{'request.course.id'}.
         ($ENV{'request.course.sec'}?'/'.$ENV{'request.course.sec'}:''))) {
 
     my %contrib=('message'      => $email,
                  'sendername'   => $ENV{'user.name'},
-                 'senderdomain' => $ENV{'user.domain'});
+                 'senderdomain' => $ENV{'user.domain'},
+                 'screenname'   => $ENV{'environment.screenname'},
+                 'plainname'    => $ENV{'environment.firstname'}.' '.
+		                   $ENV{'environment.middlename'}.' '.
+                                   $ENV{'environment.lastname'}.' '.
+                                   $ENV{'enrironment.generation'},
+                 'attachmenturl'=> $attachmenturl);
     if ($anon) {
 	$contrib{'anonymous'}='true';
     }
@@ -330,7 +524,7 @@ sub adddiscuss {
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
 		     $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
         my %storenewentry=($symb => time);
-        $status.='<br>Updating discussion time: '.
+        $status.='<br>'.&mt('Updating discussion time').': '.
         &Apache::lonnet::put('discussiontimes',\%storenewentry,
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
 		     $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
@@ -341,7 +535,7 @@ sub adddiscuss {
        my %newrecord=();
        $newrecord{'resource'}=$symb;
        $newrecord{'subnumber'}=$record{'subnumber'}+1;
-       $status.='<br>Registering: '.
+       $status.='<br>'.&mt('Registering').': '.
                &Apache::lonnet::cstore(\%newrecord,'_discussion');
     }
     } else {
@@ -350,6 +544,26 @@ sub adddiscuss {
     return $status.'<br>';   
 }
 
+# ----------------------------------------------------------- Preview function
+
+sub show_preview {
+    my $r=shift;
+    my $message=&clear_out_html($ENV{'form.comment'});
+    $message=~s/\n/\<br \/\>/g;
+    $message=&Apache::lontexconvert::msgtexconverted($message);
+    $r->print('<table border="2"><tr><td>'.
+       $message.'</td></tr></table>');
+}
+
+sub generate_preview_button {
+    return(<<ENDPREVIEW);
+<form name="preview" action="/adm/feedback?preview=1" method="post" target="preview">
+<input type="hidden" name="comment" />
+<input type="button" value="Show Preview"
+onClick="this.form.comment.value=document.mailform.comment.value;this.form.submit();" />
+</form>
+ENDPREVIEW
+}
 sub handler {
   my $r = shift;
   if ($r->header_only) {
@@ -360,16 +574,8 @@ sub handler {
 
 # --------------------------- Get query string for limited number of parameters
 
-    map {
-       my ($name, $value) = split(/=/,$_);
-       $value =~ tr/+/ /;
-       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
-       if (($name eq 'hide') || ($name eq 'unhide')) {
-           unless ($ENV{'form.'.$name}) {
-              $ENV{'form.'.$name}=$value;
-	   }
-       }
-    } (split(/&/,$ENV{'QUERY_STRING'}));
+   &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+                             ['hide','unhide','deldisc','postdata','preview']);
 
   if (($ENV{'form.hide'}) || ($ENV{'form.unhide'})) {
 # ----------------------------------------------------------------- Hide/unhide
@@ -379,7 +585,7 @@ sub handler {
     my $entry=$ENV{'form.hide'}?$ENV{'form.hide'}:$ENV{'form.unhide'};
 
     my ($symb,$idx)=split(/\:\:\:/,$entry);
-    my ($map,$ind,$url)=split(/\_\_\_/,$symb);
+    my ($map,$ind,$url)=&Apache::lonnet::decode_symb($symb);
 
     my %contrib=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
@@ -399,17 +605,54 @@ sub handler {
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
 		     $ENV{'course.'.$ENV{'request.course.id'}.'.num'});
 
-    &redirect_back($r,'/res/'.$url,'Changed discussion status<p>','0');
+    &redirect_back($r,&Apache::lonnet::clutter($url),
+       &mt('Changed discussion status').'<p>','0','0');
+  } elsif ($ENV{'form.deldisc'}) {
+# --------------------------------------------------------------- Hide for good
+    $r->content_type('text/html');
+    $r->send_http_header;
+
+    my $entry=$ENV{'form.deldisc'};
 
-      
+    my ($symb,$idx)=split(/\:\:\:/,$entry);
+    my ($map,$ind,$url)=&Apache::lonnet::decode_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 $currentdeleted=$contrib{'deleted'};
+    
+    $currentdeleted.='.'.$idx.'.';
+
+    my %newhash=('deleted' => $currentdeleted);
+
+    &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,&Apache::lonnet::clutter($url),
+       &mt('Changed discussion status').'<p>','0','0');
+  } elsif ($ENV{'form.preview'}) {
+# -------------------------------------------------------- User wants a preview
+      &show_preview($r);
   } else {
 # ------------------------------------------------------------- Normal feedback
   my $feedurl=$ENV{'form.postdata'};
   $feedurl=~s/^http\:\/\///;
   $feedurl=~s/^$ENV{'SERVER_NAME'}//;
   $feedurl=~s/^$ENV{'HTTP_HOST'}//;
+  $feedurl=~s/\?.+$//;
 
   my $symb=&Apache::lonnet::symbread($feedurl);
+  unless ($symb) {
+      $symb=$ENV{'form.symb'};
+      if ($symb) {
+	  my ($map,$id,$url)=&Apache::lonnet::decode_symb($symb);
+          $feedurl=&Apache::lonnet::clutter($url);
+      }
+  }
   my $goahead=1;
   if ($feedurl=~/\.(problem|exam|quiz|assess|survey|form)$/) {
       unless ($symb) { $goahead=0; }
@@ -426,6 +669,8 @@ sub handler {
       ) 
       || 
       ($ENV{'request.course.id'} && ($feedurl!~m:^/adm:))
+      ||
+      ($ENV{'request.course.id'} && ($symb=~/^bulletin\_\_\_/))
      ) {
 # --------------------------------------------------- Print login screen header
     unless ($ENV{'form.sendit'}) {
@@ -445,32 +690,49 @@ sub handler {
 # Get output from resource
       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 $useranswer=&Apache::loncommon::get_student_answers(
+                       $symb,$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='';
+      if ($ENV{'form.attachment.filename'}) {
+	  unless (length($ENV{'form.attachment'})>131072) {
+	      $attachmenturl=&Apache::lonnet::userfileupload('attachment');
+	  }
+      }
 # Filter HTML out of message (could be nasty)
-      my $message=&clear_out_html;
+      my $message=&clear_out_html($ENV{'form.comment'});
 
 # Assemble email
       my ($email,$citations)=&assemble_email($feedurl,$message,$prevattempts,
-          $usersaw);
-
+          $usersaw,$useranswer);
+ 
 # Who gets this?
       my ($typestyle,%to) = &decide_receiver($feedurl);
 
 # Actually send mail
-      my ($status,$numsent)=&send_msg($feedurl,$email,$citations,%to);
+      my ($status,$numsent)=&send_msg($feedurl,$email,$citations,
+          $attachmenturl,%to);
 
 # Discussion? Store that.
 
+      my $numpost=0;
       if ($ENV{'form.discuss'}) {
-	  $typestyle.=&adddiscuss($symb,$message);
+	  $typestyle.=&adddiscuss($symb,$message,0,$attachmenturl);
+	  $numpost++;
       }
 
       if ($ENV{'form.anondiscuss'}) {
-	  $typestyle.=&adddiscuss($symb,$message,1);
+	  $typestyle.=&adddiscuss($symb,$message,1,$attachmenturl);
+	  $numpost++;
       }
 
 
 # Receipt screen and redirect back to where came from
-      &redirect_back($r,$feedurl,$typestyle,$numsent,$status);
+      &redirect_back($r,$feedurl,$typestyle,$numsent,$numpost,$status);
 
     }
    } else {
@@ -479,7 +741,10 @@ sub handler {
    }
   } else {
 # Ambiguous Problem Resource
-    $r->internal_redirect('/adm/ambiguous');
+      if ( &Apache::lonnet::mod_perl_version() == 2 ) {
+	  &Apache::lonnet::cleanenv();
+      }
+      $r->internal_redirect('/adm/ambiguous');
   }
 }
   return OK;
@@ -487,7 +752,3 @@ sub handler {
 
 1;
 __END__
-
-
-
-