Diff for /loncom/interface/lonfeedback.pm between versions 1.26 and 1.307

version 1.26, 2002/04/03 18:47:39 version 1.307, 2011/12/10 22:54:34
Line 25 Line 25
 #  #
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 # (Internal Server Error Handler  ###
 #  
 # (Login Screen  
 # 5/21/99,5/22,5/25,5/26,5/31,6/2,6/10,7/12,7/14,  
 # 1/14/00,5/29,5/30,6/1,6/29,7/1,11/9 Gerd Kortemeyer)  
 #  
 # 3/1/1 Gerd Kortemeyer)  
 #  
 # 3/1,2/3,2/5,2/6,2/8 Gerd Kortemeyer  
 # 2/9 Guy Albertelli  
 # 2/10 Gerd Kortemeyer  
 # 2/13 Guy Albertelli  
 # 7/25 Gerd Kortemeyer  
 # 7/26 Guy Albertelli  
 # 7/26,8/10,10/1,11/5,11/6,12/27,12/29 Gerd Kortemeyer  
 # YEAR=2002  
 # 1/1,1/16 Gerd Kortemeyer  
 #  
   
 package Apache::lonfeedback;  package Apache::lonfeedback;
   
Line 50  use strict; Line 33  use strict;
 use Apache::Constants qw(:common);  use Apache::Constants qw(:common);
 use Apache::lonmsg();  use Apache::lonmsg();
 use Apache::loncommon();  use Apache::loncommon();
   use Apache::lontexconvert();
   use Apache::lonlocal; # must not have ()
   use Apache::lonnet;
   use Apache::lonhtmlcommon();
   use Apache::lonnavmaps;
   use Apache::lonenc();
   use Apache::lonrss();
   use HTML::LCParser();
   #use HTML::Tidy::libXML;
   use Apache::lonspeller();
   use Apache::longroup;
   use Cwd;
   use LONCAPA;
   
   sub discussion_open {
       my ($status,$symb)=@_;
   # Advanced roles can always discuss
       if ($env{'request.role.adv'}) { return 1; }
   # Get discussion closing date
       my $close=&Apache::lonnet::EXT('resource.0.discussend',$symb);
   # If it is defined and in the future, the instructor wants this discussion to be open
       if (defined($close) && $close ne '' && $close > time) {
           return 1;
       }
   # It was not explicitly open, check if the problem is available.
   # If the problem is not available, close the discussion 
       if (defined($status) &&
    !($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER'
     || $status eq 'OPEN')) {
    return 0;
       }
   # The problem is available, but check if the instructor explictly closed discussion
       if (defined($close) && $close ne '' && $close < time) {
    return 0;
       }
       return 1;
   }
   
 sub mail_screen {  sub discussion_visible {
   my ($r,$feedurl,$options) = @_;      my ($status)=@_;
   $r->print(<<ENDDOCUMENT);      if (not &discussion_open($status)) {
 <html>   my $hidden=&Apache::lonnet::EXT('resource.0.discusshide');
 <head>   if (lc($hidden) eq 'yes' or $hidden eq '' or !defined($hidden))  {
 <title>The LearningOnline Network with CAPA</title>      if (!$env{'request.role.adv'}) { return 0; }
 <meta http-equiv="pragma" content="no-cache"></meta>   }
 <script>      }
     function gosubmit() {      return 1;
         var rec=0;  }
         if (typeof(document.mailform.elements.author)!="undefined") {  
           if (document.mailform.elements.author.checked) {  sub list_discussion {
              rec=1;      my ($mode,$status,$ressymb,$imsextras,$group)=@_;
           }       unless ($ressymb) { $ressymb=&Apache::lonnet::symbread(); }
       unless ($ressymb) { return ''; }
       $ressymb=&wrap_symb($ressymb);
       my $outputtarget=$env{'form.grade_target'};
       if (defined($env{'form.export'})) {
    if($env{'form.export'}) {
               $outputtarget = 'export';
         }          }
         if (typeof(document.mailform.elements.question)!="undefined") {      }
           if (document.mailform.elements.question.checked) {      if (defined($imsextras)) {
              rec=1;          if ($$imsextras{'caller'} eq 'imsexport') {
           }               $outputtarget = 'export';
         }          }
         if (typeof(document.mailform.elements.course)!="undefined") {      }
           if (document.mailform.elements.course.checked) {      if (not &discussion_visible($status)) {
              rec=1;          if ($mode ne 'board') {
           }               &Apache::lonenc::check_encrypt(\$ressymb); 
               return '<br /><div class="LC_feedback_link">'.&send_message_link($ressymb)."</div>";
         }          }
         if (typeof(document.mailform.elements.policy)!="undefined") {      }
           if (document.mailform.elements.policy.checked) {      if ($group ne '' && $mode eq 'board') {
              rec=1;          if (&check_group_priv($group,'vgb') ne 'ok') {
           }               return '';
         }          }
         if (typeof(document.mailform.elements.discuss)!="undefined") {      }
           if (document.mailform.elements.discuss.checked) {  
              rec=1;      my ($blocked,$blocktext) = 
           }           &Apache::loncommon::blocking_status('boards');
       if ($blocked) {
           $blocktext = '<br /><div class="LC_feedback_link"><span class="LC_feedback_link">'.$blocktext."</span>";
           &Apache::lonenc::check_encrypt(\$ressymb);
           if ($mode ne 'board') {
               $blocktext.=&send_message_link($ressymb).'</div>';
           }else{
               $blocktext.="</div>";
           }
           return $blocktext; 
       }
   
       my @bgcols = ("LC_disc_old_item","LC_disc_new_item");
       my $discussiononly=0;
       if ($mode eq 'board') { $discussiononly=1; }
       unless ($env{'request.course.id'}) { return ''; }
       my $crs='/'.$env{'request.course.id'};
       my $cid=$env{'request.course.id'};
       if ($env{'request.course.sec'}) {
    $crs.='_'.$env{'request.course.sec'};
       }
       $crs=~s/\_/\//g;
       my $encsymb=&Apache::lonenc::check_encrypt($ressymb);
       my $viewgrades=(&Apache::lonnet::allowed('vgr',$crs)
     && ($ressymb=~/$LONCAPA::assess_re/));
       
       my %usernamesort = ();
       my %namesort =();
       my %subjectsort = ();
   
   # Get discussion display settings for this discussion
       my $lastkey = $ressymb.'_lastread';
       my $showkey = $ressymb.'_showonlyunread';
       my $markkey = $ressymb.'_showonlyunmark',
       my $visitkey = $ressymb.'_visit';
       my $ondispkey = $ressymb.'_markondisp';
       my $userpickkey = $ressymb.'_userpick';
       my $toggkey = $ressymb.'_readtoggle';
       my $readkey = $ressymb.'_read';
       $ressymb=$encsymb;
       my %dischash = &Apache::lonnet::get('nohist_'.$cid.'_discuss',[$lastkey,$showkey,$markkey,$visitkey,$ondispkey,$userpickkey,$toggkey,$readkey],$env{'user.domain'},$env{'user.name'});
       my %discinfo = ();
       my $showonlyunread = 0;
       my $showunmark = 0; 
       my $markondisp = 0;
       my $prevread = 0;
       my $previous = 0;
       my $visit = 0;
       my $newpostsflag = 0;
       my @posters = split(/\&/,$dischash{$userpickkey});
   
   # Retain identification of "NEW" posts identified in last display, if continuing 'previous' browsing of posts.
       &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['previous','sortposts','rolefilter','statusfilter','sectionpick','grouppick','totposters']);
       my $sortposts = $env{'form.sortposts'};
       my $statusfilter = $env{'form.statusfilter'};
       my @sectionpick = split(/,/,$env{'form.sectionpick'});
       my @grouppick   = split(/,/,$env{'form.grouppick'});
       my @rolefilter  = split(/,/,$env{'form.rolefilter'});
   
       my $totposters = $env{'form.totposters'};
       $previous = $env{'form.previous'};
       if ($previous > 0) {
           $prevread = $previous;
       } elsif (defined($dischash{$lastkey})) {
           unless ($dischash{$lastkey} eq '') {
               $prevread = $dischash{$lastkey};
           }
       }
   
       my $cdom = $env{'course.'.$cid.'.domain'};
       my $cnum = $env{'course.'.$cid.'.num'};
       my $crstype = &Apache::loncommon::course_type();
   
   # Get information about students and non-students in course for filtering display of posts
       my %roleshash = ();
       my %roleinfo = ();
       my ($classgroups,$studentgroups);
       if ($env{'form.rolefilter'}) {
           %roleshash = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
           foreach my $rolekey (keys(%roleshash)) {
               my ($role,$uname,$udom,$sec) = split(/:/,$rolekey);
               if ($role =~ /^cr/) {
                   $role = 'cr';
               }
               my ($end,$start) = split(/:/,$roleshash{$rolekey});
               my $now = time;
               my $status = 'Active';
               if (($now < $start) || ($end > 0 && $now > $end)) {
                   $status = 'Expired';
               }
               if ($uname && $udom) { 
                   push(@{$roleinfo{$uname.':'.$udom}}, $role.':'.$sec.':'.$status);
               }
           }
           my ($classlist,$keylist) =
                            &Apache::loncoursedata::get_classlist($cdom,$cnum);
           my $sec_index = &Apache::loncoursedata::CL_SECTION();
           my $status_index = &Apache::loncoursedata::CL_STATUS();
           while (my ($student,$data) = each %$classlist) {
               my ($section,$status) = ($data->[$sec_index],
                                    $data->[$status_index]);
               push(@{$roleinfo{$student}}, 'st:'.$section.':'.$status);
           }
    ($classgroups,$studentgroups) = 
       &Apache::loncoursedata::get_group_memberships($classlist,$keylist,
     $cdom,$cnum);
       }
   
   # Get discussion display default settings for user
       if ($env{'environment.discdisplay'} eq 'unread') {
           $showonlyunread = 1;
       }
       if ($env{'environment.discmarkread'} eq 'ondisp') {
           $markondisp = 1;
       }
   
   # Override user's default if user specified display setting for this discussion
       if (defined($dischash{$ondispkey})) {
           unless ($dischash{$ondispkey} eq '') {
               $markondisp = $dischash{$ondispkey};
           }
       }
       if ($markondisp) {
           $discinfo{$lastkey} = time;
       }
   
       if (defined($dischash{$showkey})) {
           unless ($dischash{$showkey} eq '') {
               $showonlyunread = $dischash{$showkey};
           }
       }
   
       if (defined($dischash{$markkey})) {
           unless ($dischash{$markkey} eq '') {
               $showunmark = $dischash{$markkey};
           }
       }
   
       if (defined($dischash{$visitkey})) {
           unless ($dischash{$visitkey} eq '') {
               $visit = $dischash{$visitkey};
           }
       }
       $visit ++;
   
       my $seeid;
       if (($group ne '') && ($mode eq 'board') && 
           ($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);
       }
       my @discussionitems=();
       my %shown = ();
       my @posteridentity=();
   
       my $current=0;
       my $visible=0;
       my @depth=();
       my @replies = ();
       my %alldiscussion=();
       my %imsitems=();
       my %imsfiles=();
       my %notshown = ();
       my %newitem = ();
       my $maxdepth=0;
       my %anonhash=();
       my $anoncnt=0;
   
       my $now = time;
       $discinfo{$visitkey} = $visit;
   
       &Apache::lonnet::put('nohist_'.$cid.'_discuss',\%discinfo,$env{'user.domain'},$env{'user.name'});
       &build_posting_display(\%usernamesort,\%subjectsort,\%namesort,\%notshown,\%newitem,\%dischash,\%shown,\%alldiscussion,\%imsitems,\%imsfiles,\%roleinfo,\@discussionitems,\@replies,\@depth,\@posters,\$maxdepth,\$visible,\$newpostsflag,\$current,$status,$viewgrades,$seeid,$prevread,$sortposts,$encsymb,$readkey,$showunmark,$showonlyunread,$totposters,\@rolefilter,\@sectionpick,\@grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,\%anonhash,$anoncnt,$group);
   
       my $discussion='';
       my $manifestfile;
       my $manifestok=0;
       my $tempexport;
       my $imsresources;
       my $copyresult;
   
       my $function = &Apache::loncommon::get_users_function();
       my $color = &Apache::loncommon::designparm($function.'.tabbg',
                                                       $env{'user.domain'});
       my %lt = &Apache::lonlocal::texthash(
           'cuse' => 'Current discussion settings',
           'allposts' => 'All posts',
           'unread' => 'New posts only',
           'unmark' => 'Unread only',
           'ondisp' => 'Once displayed',
           'onmark' => 'Once marked not NEW',
           'toggoff' => 'Off',
           'toggon' => 'On',
           'disa' => 'Posts to be displayed',
           'npce' => 'Posts cease to be marked "NEW"',
           'epcb' => 'Each post can be toggled read/unread', 
           'chgt' => 'Change',
           'disp' => 'Display',
           'nolo' => 'Not new',
           'togg' => 'Toggle read/unread',
           'aner' => 'An error occurred opening the manifest file.',
           'difo' => 'Discussion for',
           'aerr' => 'An error occurred opening the export file for posting',
           'aysu' => 'Are you sure you want to delete this post?',
           'dpwn' => 'Deleted posts will no longer be visible to you and other students',
           'bwco' => 'but will continue to be visible to your instructor',
           'depo' => 'Deleted posts will no longer be visible to you or anyone else.',
           'discussions' => 'DISCUSSIONS'
       );
   
       my $currdisp = $lt{'allposts'};
       my $currmark = $lt{'onmark'};
       my $currtogg = $lt{'toggoff'};
       my $dispchange = $lt{'unread'};
       my $markchange = $lt{'ondisp'};
       my $toggchange = $lt{'toggon'};
       my $chglink = '/adm/feedback?modifydisp='.$ressymb;
       my $displinkA = 'onlyunread';
       my $displinkB = 'onlyunmark';
       my $marklink = 'markondisp';
       my $togglink = 'toggon';
   
       if ($markondisp) {
           $currmark = $lt{'ondisp'};
           $markchange = $lt{'onmark'};
           $marklink = 'markonread';
       }
   
       if ($showonlyunread) {
           $currdisp = $lt{'unread'};
           $dispchange = $lt{'allposts'};
           $displinkA = 'allposts';
       }
   
       if ($showunmark) {
           $currdisp = $lt{'unmark'};
           $dispchange = $lt{'unmark'};
           $displinkA='allposts';
           $displinkB='onlyunread';
           $showonlyunread = 0;
       }
   
       if ($dischash{$toggkey}) {
           $currtogg = $lt{'toggon'};
           $toggchange = $lt{'toggoff'};
           $togglink = 'toggoff';
       } 
      
       $chglink .= '&amp;changes='.$displinkA.'_'.$displinkB.'_'.$marklink.'_'.$togglink;
   
       if ($newpostsflag) {
           $chglink .= '&amp;previous='.$prevread;
       }
       $chglink.=&group_args($group);
   
       if ($visible) {
   # Print the discusssion
           if ($outputtarget eq 'tex') {
               $discussion.='<tex>{\tiny \vskip 0 mm\noindent\makebox[2 cm][b]{\hrulefill}'.
                            '\textbf{'.$lt{'discussions'}.'}\makebox[2 cm][b]{\hrulefill}\vskip 0 mm'.
                            '\noindent\textbf{'.$lt{'disa'}.'}: \textit{'.$currdisp.'}\vskip 0 mm'.
                            '\noindent\textbf{'.$lt{'npce'}.'}: \textit{'.$currmark.'}}</tex>';
           } elsif ($outputtarget eq 'export') {
   # Create temporary directory if this is an export
               my $now = time;
               if ((defined($imsextras)) && ($$imsextras{'caller'} eq 'imsexport')) {
                   $tempexport = $$imsextras{'tempexport'};
                   if (!-e $tempexport) {
                       mkdir($tempexport,0700);
                   }
                   $tempexport .= '/'.$$imsextras{'count'};
                   if (!-e $tempexport) {
                       mkdir($tempexport,0700);
                   }
               } else {
                   $tempexport = $Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/ims_exports';
                   if (!-e $tempexport) {
                       mkdir($tempexport,0700);
                   }
                   $tempexport .= '/'.$now;
                   if (!-e $tempexport) {
                       mkdir($tempexport,0700);
                   }
                   $tempexport .= '/'.$env{'user.domain'}.'_'.$env{'user.name'};
               }
               if (!-e $tempexport) {
                   mkdir($tempexport,0700);
               }
   # open manifest file
               my $manifest = '/imsmanifest.xml';
               my $manifestfilename = $tempexport.$manifest;
               if ($manifestfile = Apache::File->new('>'.$manifestfilename)) {
                   $manifestok=1;
                   print $manifestfile qq|<?xml version="1.0" encoding="UTF-8"?>
   <manifest xmlns="http://www.imsglobal.org/xsd/imscp_v1p1" xmlns:imsmd="http://www.imsglobal.org/xsd/imsmd_v1p2
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance
   identifier="MANIFEST-$ressymb" xsi:schemaLocation="http://www.imsglobal.org/xsd/imscp_v1p1 
   imscp_v1p1.xsd http://www.imsglobal.org/xsd/imsmd_v1p2 imsmd_v1p2p2.xsd">
     <organizations default="$ressymb">
       <organization identifier="$ressymb">
         <title>$lt{'difo'} $ressymb</title>\n|;
               } else {
                   $discussion .= $lt{'aner'}.'<br />';
               }
    } else {
               my $colspan=$maxdepth+1;
               $discussion.= &Apache::lonhtmlcommon::scripttag(qq|
      function verifydelete (caller,symb,idx,newflag,previous,groupparm) {
          var symbparm = symb+':::'+idx
          var prevparm = ""
          if (newflag == 1) {
              prevparm = "&amp;previous="+previous
          }
          if (caller == 'studentdelete') {
              if (confirm("$lt{'aysu'}\\n$lt{'dpwn'},\\n$lt{'bwco'}")) {
                  document.location.href = "/adm/feedback?hide="+symbparm+prevparm+groupparm
              }
          } else {
              if (caller == 'seeiddelete') {
                  if (confirm("$lt{'aysu'}\\n$lt{'depo'}")) {
                      document.location.href = "/adm/feedback?deldisc="+symbparm+prevparm+groupparm
                  }
              }
          }
      }
               |);
       $discussion.='<form name="readchoices" method="post" action="/adm/feedback?chgreads='.$ressymb.'" >'.
                            "\n".'<table class="LC_discussion">';
               $discussion .= &action_links_bar($colspan,$ressymb,$visible,
                                                $newpostsflag,$group,
                                                $prevread,$markondisp);
               my $escsymb=&escape($ressymb);
               my $numhidden = keys(%notshown);
               if ($numhidden > 0) {
                   my $colspan = $maxdepth+1;
                   $discussion.="\n".'<tr><td bgcolor="#CCCCCC" colspan="'.$colspan.'">'.
                            '<a href="/adm/feedback?allposts=1&amp;symb='.$escsymb;
                   if ($newpostsflag) {
                       $discussion .= '&amp;previous='.$prevread;
                   }
    $discussion .= &group_args($group);
                   $discussion .= '">'.&mt('Show all posts').'</a> '.&mt('to display').' '.
                            $numhidden.' ';
                   if ($showunmark) {
                       $discussion .= &mt('posts previously marked read');
                   } else {
                       $discussion .= &mt('previously viewed posts');
                   }
                   $discussion .= '<br/></td></tr>';
               }
           }
   
   # Choose sort mechanism
           my @showposts = ();
           if ($sortposts eq 'descdate') {
               @showposts = (sort { $b <=> $a } keys(%alldiscussion));
           } elsif ($sortposts eq 'thread') {
               @showposts = (sort { $a <=> $b } keys(%alldiscussion));
           } elsif ($sortposts eq 'subject') {
               foreach my $key (sort(keys(%subjectsort))) {
                   push(@showposts, @{$subjectsort{$key}});
               }
           } elsif ($sortposts eq 'username') {
               foreach my $domain (sort(keys(%usernamesort))) {
                   foreach my $key (sort(keys(%{$usernamesort{$domain}}))) {
                       push(@showposts, @{$usernamesort{$domain}{$key}});
                   }
               }
           } elsif ($sortposts eq 'lastfirst') {
               foreach my $last (sort(keys(%namesort))) {
                    foreach my $key (sort(keys(%{$namesort{$last}}))) {
                        push(@showposts, @{$namesort{$last}{$key}});
                    }
               }
           } else {
               @showposts =  (sort { $a <=> $b } keys(%alldiscussion));
           }
           my $currdepth = 0;
           my $firstidx = $alldiscussion{$showposts[0]};
           foreach my $post (@showposts) {
               unless (($sortposts eq 'thread') || (($sortposts eq '') && ($env{'environment.threadeddiscussion'})) || ($outputtarget eq 'export')) {
                   $alldiscussion{$post} = $post;
               }
               unless ( ($notshown{$alldiscussion{$post}} eq '1') || ($shown{$alldiscussion{$post}} == 0) ) {
                   if ($outputtarget ne 'tex' && $outputtarget ne 'export') {
       $discussion.="\n<tr>";
    }
           my $thisdepth=$depth[$alldiscussion{$post}];
                   if ($outputtarget ne 'tex' && $outputtarget ne 'export') {
       for (1..$thisdepth) {
    $discussion.='<td>&nbsp;&nbsp;&nbsp;</td>';
       }
    }
           my $colspan=$maxdepth-$thisdepth+1;
                   if ($outputtarget eq 'tex') {
       #cleanup block
       $discussionitems[$alldiscussion{$post}]=~s/<table([^>]*)>/<table TeXwidth="90 mm">/;
       $discussionitems[$alldiscussion{$post}]=~s/<tr([^>]*)><td([^>]*)>/<tr><td TeXwidth="20 mm" align="left">/;
                       my $threadinsert='';
                       if ($thisdepth > 0) {
    $threadinsert='<br /><strong>Reply: '.$thisdepth.'</strong>';
       }
       $discussionitems[$alldiscussion{$post}]=~s/<\/td><td([^>]*)>/$threadinsert<\/td><td TeXwidth="65 mm" align="left">/;
                       $discussionitems[$alldiscussion{$post}]=~s/(<b>|<\/b>|<\/a>|<a([^>]+)>)//g;
   
       $discussionitems[$alldiscussion{$post}]='<tex>\vskip 0 mm\noindent\makebox[2 cm][b]{\hrulefill}</tex>'.$discussionitems[$alldiscussion{$post}];
       $discussion.=$discussionitems[$alldiscussion{$post}];
    } elsif ($outputtarget eq 'export') {
                       my $postfilename = $alldiscussion{$post}.'-'.$imsitems{$alldiscussion{$post}}{'timestamp'}.'.html';
                       if ($manifestok) {
                           if (($depth[$alldiscussion{$post}] <= $currdepth) && ($alldiscussion{$post} != $firstidx)) {
                               print $manifestfile '  </item>'."\n";
                           }
                           $currdepth = $depth[$alldiscussion{$post}];
                           print $manifestfile "\n". 
         '<item identifier="ITEM-'.$ressymb.'-'.$alldiscussion{$post}.'" isvisible="'.
           $imsitems{$alldiscussion{$post}}{'isvisible'}.'" identifieref="RES-'.$ressymb.'-'.$alldiscussion{$post}.'">'.
           '<title>'.$imsitems{$alldiscussion{$post}}{'title'}.'</title></item>';
                           $imsresources .= "\n".
       '<resource identifier="RES-'.$ressymb.'-'.$alldiscussion{$post}.'" type="webcontent" href="'.$postfilename.'">'."\n".
         '<file href="'.$postfilename.'">'."\n".
         $imsfiles{$alldiscussion{$post}}{$imsitems{$alldiscussion{$post}}{'currversion'}}.'</file>'."\n".
       '</resource>';
                       }
                       my $postingfile;
                       my $postingfilename = $tempexport.'/'.$postfilename;
                       if ($postingfile = Apache::File->new('>'.$postingfilename)) {
                           print $postingfile '<html><head><title>Discussion Post</title></head><body>'.
                                              $imsitems{$alldiscussion{$post}}{'title'}.' '.
                                              $imsitems{$alldiscussion{$post}}{'sender'}.
                                              $imsitems{$alldiscussion{$post}}{'timestamp'}.'<br /><br />'.
                                              $imsitems{$alldiscussion{$post}}{'message'}.'<br />'.
                                              $imsitems{$alldiscussion{$post}}{'attach'}.'</body></html>'."\n"; 
                           close($postingfile);
                       } else {
                           $discussion .= $lt{'aerr'}.' '.$alldiscussion{$post}.'<br />';
                       }
                       $copyresult.=&replicate_attachments($imsitems{$alldiscussion{$post}}{'allattachments'},$tempexport);
                   } else {
                       $discussion.='<td class="'.$bgcols[$newitem{$alldiscussion{$post}}].
                          '" colspan="'.$colspan.'">'. $discussionitems[$alldiscussion{$post}].
                          '</td></tr>';
                   }
       }
           }
    unless ($outputtarget eq 'tex' || $outputtarget eq 'export') {
               my $colspan=$maxdepth+1;
               $discussion .= <<END;
               <tr bgcolor="#FFFFFF">
                <td colspan="$colspan" class="LC_disc_action_links_bar">
                 <table class="LC_disc_action_table">
                  <tr>
                   <td align="left">
                    <table border="0" cellpadding="0" cellspacing="4">
                     <tr>
                      <td>
                       <font size="-1"><b>$lt{'cuse'}</b>:</font></td>
                      <td>&nbsp;</td>
                      <td><font size="-1">
   END
               if ($newpostsflag) {
                   $discussion .= 
                      '1.&nbsp;'.$lt{'disp'}.'&nbsp;-&nbsp;<i>'.$currdisp.'</i>&nbsp;&nbsp;2.&nbsp;'.$lt{'nolo'}.'&nbsp;-&nbsp;<i>'.$currmark.'</i>';
                   if ($dischash{$toggkey}) {
                      $discussion .= '&nbsp;&nbsp;3.&nbsp;'.$lt{'togg'}.'&nbsp;-&nbsp;<i>'.$currtogg.'</i>';
                   }
               } else {
                   if ($dischash{$toggkey}) {
                      $discussion .= '1.&nbsp;'.$lt{'disp'}.'&nbsp;-&nbsp;<i>'.$currdisp.'</i>&nbsp;2.&nbsp;'.$lt{'togg'}.'&nbsp;-&nbsp;<i>'.$currtogg.'</i>';
                   } else {
                       $discussion .=
                            $lt{'disp'}.'&nbsp;-&nbsp;<i>'.$currdisp.'</i>';
                   }
               }
               $discussion .= <<END;
                      </font></td>
                      <td>&nbsp;</td>
                      <td align="left">
                       <font size="-1"><b><a href="$chglink">$lt{'chgt'}</a>?</b></font>
                      </td>
                     </tr>
                    </table>
                   </td>
   END
               if ($sortposts) {
                   my %sort_types = ();
                   my %role_types = ();
                   my %status_types = ();
                   &sort_filter_names(\%sort_types,\%role_types,\%status_types,$crstype);
   
                   $discussion .= '<td><font size="-1"><b>'.&mt('Sorted by').'</b>: '.$sort_types{$sortposts}.'<br />';
                   if (defined($env{'form.totposters'})) {
                       $discussion .= &mt('Posts by').':';
                       if ($totposters > 0) {
                           foreach my $poster (@posters) {
                               $discussion .= ' '.$poster.',';
                           }
                           $discussion =~ s/,$//;
                       } else {
                           $discussion .= &mt('None selected');
                       }
                   } else {
                       my $filterchoice ='';
                       if (@sectionpick > 0) {
                           $filterchoice = '<i>'.&mt('sections').'</i>-&nbsp;'.$env{'form.sectionpick'};
                           $filterchoice .= '&nbsp;&nbsp;&nbsp; ';
                       }
                       if (@grouppick > 0) {
                           $filterchoice = '<i>'.&mt('groups').'</i>-&nbsp;'.$env{'form.grouppick'};
                           $filterchoice .= '&nbsp;&nbsp;&nbsp; ';
                       }
                       if (@rolefilter > 0) {
                           $filterchoice .= '<i>'.&mt('roles').'</i>-';
                           foreach my $role (@rolefilter) {
                               $filterchoice .= '&nbsp;'.$role_types{$role}.',';
                           }
                           $filterchoice =~ s/,$//;
                           $filterchoice .= '<br />'.('&nbsp;' x8);
                       }
                       if ($statusfilter) {
                           $filterchoice .= '<i>'.&mt('status').'</i>-&nbsp;'.$status_types{$statusfilter};
                       }
                       if ($filterchoice) {
                           $discussion .= '<b>'.&mt('Filters').'</b>:&nbsp;'.$filterchoice;
                       }
                       $discussion .= '</font></td>';
                   }
               }
               if ($dischash{$toggkey}) {
                   my $storebutton = &mt('Save read/unread changes');
                   $discussion.='<td align="right">'.
                 '<input type="hidden" name="discsymb" value="'.$ressymb.'" />'."\n".
                 '<input type="button" name="readoptions" value="'.$storebutton.'"'.
                 ' onclick="this.form.submit();" />'."\n".
                 '</td>';
               }
               $discussion .= (<<END);
                  </tr>
                 </table>
                </td>
               </tr>
   END
               $discussion .= &action_links_bar($colspan,$ressymb,$visible,
                                                $newpostsflag,$group,
                                                $prevread,$markondisp);
               $discussion .= "
              </table>
              <br /><br /></form>\n";
           } 
           if ($outputtarget eq 'export') {
               if ($manifestok) {
                   while ($currdepth > 0) {
                       print $manifestfile "    </item>\n";
                       $currdepth --;
                   }
                   print $manifestfile qq|
       </organization>
     </organizations>
     <resources>
       $imsresources
     </resources>
   </manifest>
                   |;
                   close($manifestfile);
                   if ((defined($imsextras)) && ($$imsextras{'caller'} eq 'imsexport')) {
                       $discussion = $copyresult;
                   } else {
   
   #Create zip file in prtspool
   
                       my $imszipfile = '/prtspool/'.
                       $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
                            time.'_'.rand(1000000000).'.zip';
                       my $cwd = &getcwd(); 
                       my $imszip = '/home/httpd/'.$imszipfile;
                       chdir $tempexport;
                       open(OUTPUT, "zip -r $imszip *  2> /dev/null |");
                       close(OUTPUT);
                       chdir $cwd;
                       $discussion .= &mt('Download the zip file from [_1]Discussion Posting Archive','<a href="'.$imszipfile.'">').'</a><br />';
                       if ($copyresult) {
                           $discussion .= &mt('The following errors occurred during export').' - <br />'.$copyresult;
                       }
                   }
               } else {
                   $discussion .= '<br />'.&mt('Unfortunately you will not be able to retrieve an archive of the discussion posts at this time, because there was a problem creating a manifest file.').'<br />';
               }
               return $discussion;
           }
       }
       if ($discussiononly) {
           my $now = time;
           my $attachnum = 0;
           my $currnewattach = [];
           my $currdelold = [];
           my $comment = '';
           my $subject = '';
           if ($env{'form.origpage'}) {
               &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['addnewattach','deloldattach','delnewattach','timestamp','idx','subject','comment']);
               $subject = &unescape($env{'form.subject'});
               $comment = &unescape($env{'form.comment'});
               my @keepold = ();
               &process_attachments($currnewattach,$currdelold,\@keepold);
               if (@{$currnewattach} > 0) {
                   $attachnum += @{$currnewattach};
               }
           }
    if ((&discussion_open($status)) && ($outputtarget ne 'tex')) {
               if (($group ne '') && ($mode eq 'board')) {  
                   if ((&check_group_priv($group,'pgd') eq 'ok') && 
                      ($ressymb =~ m{^bulletin___\d+___adm/wrapper/adm/\Q$cdom\E/\Q$cnum\E/\d+/bulletinboard$})) {
                       $discussion .=
    &postingform_display($mode,$ressymb,$now,$subject,
        $comment,$outputtarget,$attachnum,
        $currnewattach,$currdelold,
        $group,$crstype);
                   }
               } else {
                   if (&Apache::lonnet::allowed('pch',$env{'request.course.id'}.
                       ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
   
               $discussion.= 
           &postingform_display($mode,$ressymb,$now,$subject,
        $comment,$outputtarget,$attachnum,
        $currnewattach,$currdelold,'',$crstype);
                   } else {
                       $discussion.= '<span class="LC_feedback_link">'.
                                     &mt('This discussion is closed.').'</span>';
                   }
               }
    }
           if (!(&discussion_open($status)) && ($outputtarget ne 'tex')) {
               $discussion.= '<span class="LC_feedback_link">'.
                             &mt('This discussion is closed.').'</span>';
           }
       } elsif ($outputtarget ne 'tex') {
           $discussion.='<div class="LC_feedback_link">';
           if (&discussion_open($status) &&
               &Apache::lonnet::allowed('pch',
              $env{'request.course.id'}.
           ($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''))) {
               $discussion.= &send_feedback_link($ressymb);
               if ($env{'request.role.adv'}) {
                   my $close = &Apache::lonnet::EXT('resource.0.discussend',$ressymb);
                   if (defined($close) && $close ne '' && $close < time) {
                       $discussion .= '&nbsp;'.&mt('(Closed for [_1] roles)',
                                                   &Apache::lonnet::plaintext('st',$crstype));
                   }
               }
    } else {
               $discussion.= '<span class="LC_feedback_link">'.&mt('This discussion is closed.').'</span>';
           }
    $discussion.= &send_message_link($ressymb).'</div>';
       }
       return $discussion;
   }
   
   
   sub discussion_link {
      my ($ressymb,$linktext,$cmd,$item,$flag,$prev,$adds)=@_;
      my $link='/adm/feedback?inhibitmenu=yes&modal=yes&'.$cmd.'='.&escape($ressymb).':::'.$item;
      if ($flag) { $link .= '&amp;previous='.$prev; }
      if ($adds) { $link .= $adds; }
      return &Apache::loncommon::modal_link($link,$linktext,600,400);
   }
   
   
   sub send_feedback_link {
       my ($ressymb) = @_;
       return '<span class="LC_feedback_link">'.
                    &discussion_link($ressymb,
                       '<img alt="" class="LC_noBorder" src="'.
                       &Apache::loncommon::lonhttpdurl('/adm/lonMisc/chat.gif').
                       '" border="0" /><span class="LC_menubuttons_inline_text">'.&mt('Post Discussion').'</span>',
                       'replydisc').
              '</span>';
   }
   
   sub send_message_link {
       my ($ressymb) = @_;
       my $output = '<span class="LC_message_link">'.
                    '  <a href="/adm/feedback?sendmessageonly=1&amp;symb='.
                    &escape($ressymb).'"><img alt="" class="LC_noBorder" src="'.
                    &Apache::loncommon::lonhttpdurl('/res/adm/pages/feedback.png').
                    '" border="0" /><span class="LC_menubuttons_inline_text">'.&mt('Send Feedback').'</span></a></span>';
       return $output;
   }
   
   sub action_links_bar {
       my ($colspan,$ressymb,$visible,$newpostsflag,$group,$prevread,$markondisp) = @_;
       my $discussion = '<tr><td class="LC_disc_action_links_bar" colspan="'.$colspan.'">'.
                        '<table width="100%" class="LC_disc_action_table"><tr>'.
                        '<td class="LC_disc_action_left">';
       my $escsymb=&escape($ressymb);
       if ($visible) {
           $discussion .= '<a href="/adm/feedback?cmd=threadedon&amp;symb='.$escsymb;
           if ($newpostsflag) {
               $discussion .= '&amp;previous='.$prevread;
           }
           $discussion .= &group_args($group);
           $discussion .='">'.&mt('Threaded View').'</a>&nbsp;&nbsp;'.
                         '<a href="/adm/feedback?cmd=threadedoff&amp;symb='.$escsymb;
           if ($newpostsflag) {
               $discussion .= '&amp;previous='.$prevread;
           }
           $discussion .= &group_args($group);
           $discussion .='">'.&mt('Chronological View').'</a>&nbsp;&nbsp;
                          <a href="/adm/feedback?cmd=sortfilter&amp;symb='.$escsymb;
           if ($newpostsflag) {
               $discussion .= '&amp;previous='.$prevread;
           }
           $discussion .= &group_args($group);
           $discussion .='">'.&mt('Sorting/Filtering options').'</a>'.('&nbsp;' x2);
       }
       $discussion .='<a href="/adm/feedback?export='.$escsymb;
       if ($newpostsflag) {
           $discussion .= '&amp;previous='.$prevread;
       }
       $discussion .= &group_args($group);
       $discussion .= '">'.&mt('Export').'?</a>&nbsp;&nbsp;</td>';
       if ($newpostsflag) {
           if (!$markondisp) {
               $discussion .='<td class="LC_disc_action_right"><a href="/adm/preferences?action=changediscussions';
               $discussion .= &group_args($group);
               $discussion .= '">'.
                              &mt('Preferences on what is marked as NEW').
                              '</a><br /><a href="/adm/feedback?markread=1&amp;symb='.$escsymb;
               $discussion .= &group_args($group);
               $discussion .= '">'.&mt('Mark NEW posts no longer new').'</a></td>';
           } else {
               $discussion .= '<td>&nbsp;</td>';
           }
       } else {
           $discussion .= '<td>&nbsp;</td>';
       }
       $discussion .= '</tr></table></td></tr>';
       return $discussion;
   }
   
   sub postingform_display {
       my ($mode,$ressymb,$now,$subject,$comment,$outputtarget,$attachnum,
           $currnewattach,$currdelold,$group,$crstype) = @_;
       my $newattachmsg;
       my %lt = &Apache::lonlocal::texthash(
                 'note' => 'Note: in anonymous discussion, your name is visible only to course faculty',
                'title' => 'Title',
                'podi' => 'Post Discussion',
                'poan' => 'Post Anonymous Discussion',
                'newa' => 'New attachments',
       );
       if ($crstype eq 'Community') {
           $lt{'note'} = &mt('Note: in anonymous discussion, your name is visible only to community facilitators');
       }
       my ($postingform,$textareaclass);
       if (&Apache::lonhtmlcommon::htmlareabrowser()) {
           $postingform = &Apache::lonhtmlcommon::htmlareaselectactive();
           $textareaclass = 'class="LC_richDefaultOff"';
           if ($env{'request.course.id'}) {
               unless (($env{'course.'.$env{'request.course.id'}.'.allow_limited_html_in_feedback'} =~ /^\s*yes\s*$/i) || ($env{'form.sendmessageonly'})) {
                   undef($textareaclass);
               }
           }
       }
       $postingform .= (<<ENDDISCUSS);
   <form action="/adm/feedback" method="post" name="mailform" enctype="multipart/form-data"> <input type="submit" name="discuss" value="$lt{'podi'}" />
   <input type="submit" name="anondiscuss" value="$lt{'poan'}" /> <input type="hidden" name="symb" value="$ressymb" />
   <input type="hidden" name="sendit" value="true" />
   <input type="hidden" name="timestamp" value="$now" />
   <br /><a name="newpost"></a>
   <font size="1">$lt{'note'}</font><br />
   <b>$lt{'title'}:</b>&nbsp;<input type="text" name="subject" value="$subject" size="30" /><br /><br />
   <textarea name="comment" cols="80" rows="14" id="comment" $textareaclass>$comment</textarea>
   ENDDISCUSS
       if ($env{'form.origpage'}) {
           $postingform .= '<input type="hidden" name="origpage" value="'.
                           $env{'form.origpage'}.'" />'."\n";
           foreach my $att (@{$currnewattach}) {
               $postingform .= '<input type="hidden" name="currnewattach" '.
                               'value="'.$att.'" />'."\n";
           }
       }
       if (exists($env{'form.ref'})) {
           $postingform .= '<input type="hidden" name="ref" value="'.
                           $env{'form.ref'}.'" />';
       }
       if ($group ne '') {
           $postingform .='<input type="hidden" name="group" value="'.$group.'" />';
       }
       my $blockblog = &Apache::loncommon::blocking_status('blogs');
       if (!$blockblog) {
           $postingform .= &add_blog_checkbox($crstype);
       }
       $postingform .= "</form>\n";
       $postingform .= &generate_attachments_button('',$attachnum,$ressymb,
                                                        $now,$currnewattach,
                                                        $currdelold,'',$mode,
                                                        $blockblog);
       if ((ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)) {
           $newattachmsg = '<br /><b>'.$lt{'newa'}.'</b><br />';
           if (@{$currnewattach} > 1) {
                $newattachmsg .= '<ol>';
                foreach my $item (@{$currnewattach}) {
                   $item =~ m#.*/([^/]+)$#;
                   $newattachmsg .= '<li><a href="'.$item.'">'.$1.'</a></li>'."\n";
                }
                $newattachmsg .= '</ol>'."\n";
            } else {
                $$currnewattach[0] =~ m#.*/([^/]+)$#;
                $newattachmsg .= '<a href="'.$$currnewattach[0].'">'.$1.'</a><br />'."\n";
            }
       }
       $postingform .= $newattachmsg;
       $postingform .= &generate_preview_button();
       return $postingform;
   }
   
   sub build_posting_display {
       my ($usernamesort,$subjectsort,$namesort,$notshown,$newitem,$dischash,$shown,$alldiscussion,$imsitems,$imsfiles,$roleinfo,$discussionitems,$replies,$depth,$posters,$maxdepth,$visible,$newpostsflag,$current,$status,$viewgrades,$seeid,$prevread,$sortposts,$ressymb,$readkey,$showunmark,$showonlyunread,$totposters,$rolefilter,$sectionpick,$grouppick,$classgroups,$statusfilter,$toggkey,$outputtarget,$anonhash,$anoncnt,$group) = @_;
       my @original=();
       my @index=();
       my $skip_group_check = 0;
       my $symb=&Apache::lonenc::check_decrypt($ressymb);
       my $escsymb=&escape($ressymb);
       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 $see_anonymous = 
    &Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:''));
   
       if ((@{$grouppick} == 0) || (grep(/^all$/,@{$grouppick}))) {
           $skip_group_check = 1;
       }
       my (%deletions,%hiddens);
       if ($contrib{'deleted'}) {
          my $deleted = $contrib{'deleted'};
          $deleted =~ s/^\.//;
          $deleted =~ s/\.$//;
          %deletions = map { $_ => 1 } (split(/\.\./,$deleted));
       }
       if ($contrib{'hidden'}) {
          my $hidden = $contrib{'hidden'};
          $hidden =~ s/^\.//;
          $hidden =~ s/\.$//;
          %hiddens = map { $_ => 1 } (split(/\.\./,$hidden));
       }
       if ($contrib{'version'}) {
           my $oldest = $contrib{'1:timestamp'};
           if ($prevread eq '0') {
               $prevread = $oldest-1;
           }
           my ($skiptest,$rolematch,$roleregexp,$secregexp,$statusregexp);
           if ($sortposts) {
               ($skiptest,$roleregexp,$secregexp,$statusregexp) = 
                        &filter_regexp($rolefilter,$sectionpick,$statusfilter);
               $rolematch = $roleregexp.':'.$secregexp.':'.$statusregexp;
           } 
    for (my $id=1;$id<=$contrib{'version'};$id++) {
       my $idx=$id;
               next if ($contrib{$idx.':deleted'});
               next if ($contrib{$idx.':hidden'});
               my $posttime = $contrib{$idx.':timestamp'};
               if ($prevread <= $posttime) {
                   $$newpostsflag = 1;
               }
               my $studenthidden=($contrib{'studenthidden'}=~/\.$idx\./);
       my $origindex='0.';
               my $numoldver=0;
       if ($contrib{$idx.':replyto'}) {
                   if ( (($env{'environment.threadeddiscussion'}) && ($sortposts eq '')) || ($sortposts eq 'thread') || ($outputtarget eq 'export')) {
   # this is a follow-up message
       $original[$idx]=$original[$contrib{$idx.':replyto'}];
       $$depth[$idx]=$$depth[$contrib{$idx.':replyto'}]+1;
       $origindex=$index[$contrib{$idx.':replyto'}];
       if ($$depth[$idx]>$$maxdepth) { $$maxdepth=$$depth[$idx]; }
                   } else {
                       $original[$idx]=0;
                       $$depth[$idx]=0;
                   }
       } else {
   # this is an original message
    $original[$idx]=0;
    $$depth[$idx]=0;
       }
       if ($$replies[$$depth[$idx]]) {
    $$replies[$$depth[$idx]]++;
       } else {
    $$replies[$$depth[$idx]]=1;
       }
               unless ((($hiddens{$idx}) && (!$seeid)) || ($deletions{$idx})) {
    $$visible++;
                   if ($contrib{$idx.':history'}) {
                       if ($contrib{$idx.':history'} =~ /:/) {
                           my @oldversions = split(/:/,$contrib{$idx.':history'});
                           $numoldver = @oldversions;
                       } else {
                           $numoldver = 1;
                       } 
                   }
                   $$current = $numoldver;
    my %messages = ();
                   my %subjects = ();
                   my %attachtxt = ();
                   my %allattachments = ();
                   my ($screenname,$plainname);
                   my $sender = &mt('Anonymous');
   # Anonymous users getting number within a discussion
   # Since idx is in static order, this should give the same sequence every time. 
    my $key=$contrib{$idx.':sendername'}.'@'.$contrib{$idx.':senderdomain'};
    unless ($$anonhash{$key}) {
                       $anoncnt++;
       $$anonhash{$key}=&mt('Anonymous').' '.$anoncnt;
    }
                   my ($message,$subject,$vgrlink,$ctlink);
                   &get_post_contents(\%contrib,$idx,$seeid,$outputtarget,\%messages,\%subjects,\%allattachments,\%attachtxt,$imsfiles,\$screenname,\$plainname,$numoldver);
   
   
   # Set up for sorting by subject
                   unless ($outputtarget eq 'export') {
                       $message=$messages{$numoldver};
                       $message.=$attachtxt{$numoldver};
                       $subject=$subjects{$numoldver};
                       if ($message) {
             if ($hiddens{$idx}) {
       $message='<font color="#888888">'.$message.'</font>';
                               if ($studenthidden) {
                                   $message .='<br /><br />Deleted by poster (student).';
                               }
           }
   
                           if ($subject eq '') {
                              if (defined($$subjectsort{'__No subject'})) {
                                  push(@{$$subjectsort{'__No subject'}}, $idx);
                              } else {
                                  @{$$subjectsort{'__No subject'}} = ("$idx");
                              }
                           } else {
                               if (defined($$subjectsort{$subject})) {
                                  push(@{$$subjectsort{$subject}}, $idx);
                               } else {
                                  @{$$subjectsort{$subject}} = ("$idx");
                               }
                           }
           if (!$contrib{$idx.':anonymous'} || $see_anonymous) {
       $sender=&Apache::loncommon::aboutmewrapper(
    $plainname,
    $contrib{$idx.':sendername'},
    $contrib{$idx.':senderdomain'}).' ('.
    $contrib{$idx.':sendername'}.':'.
    $contrib{$idx.':senderdomain'}.')';
                               $sender = '<b>'.$sender.'</b>';
       if ($contrib{$idx.':anonymous'}) {
           $sender.=' <font color="red"><b>['.$$anonhash{$key}.']</b></font> '.
       $screenname;
       }
       if ($see_anonymous) {
    $sender.=&Apache::loncommon::student_image_tag($contrib{$idx.':senderdomain'},$contrib{$idx.':sendername'});
       }
   # Set up for sorting by domain, then username
                               unless (defined($$usernamesort{$contrib{$idx.':senderdomain'}})) {
                                   %{$$usernamesort{$contrib{$idx.':senderdomain'}}} = ();
                               }
                               if (defined($$usernamesort{$contrib{$idx.':senderdomain'}}{$contrib{$idx.':sendername'}})) {
                                   push(@{$$usernamesort{$contrib{$idx.':senderdomain'}}{$contrib{$idx.':sendername'}}}, $idx);
                               } else {
                                   @{$$usernamesort{$contrib{$idx.':senderdomain'}}{$contrib{$idx.':sendername'}}} = ("$idx");
                               }
   # Set up for sorting by last name, then first name
                               my %names = &Apache::lonnet::get('environment',
                                    ['firstname','lastname'],$contrib{$idx.':senderdomain'},
                                     ,$contrib{$idx.':sendername'});
                               my $lastname = $names{'lastname'};
                               my $firstname = $names{'firstname'};
                               if ($lastname eq '') {
                                   $lastname = '_';
                               }
                               if ($firstname eq '') {
                                   $firstname = '_';
                               }
                               unless (defined($$namesort{$lastname})) {
                                   %{$$namesort{$lastname}} = ();
                               }
                               if (defined($$namesort{$lastname}{$firstname})) {
                                   push(@{$$namesort{$lastname}{$firstname}}, $idx);
                               } else {
                                   @{$$namesort{$lastname}{$firstname}} = ("$idx");
                               }
                               if ($outputtarget ne 'tex') {
                                   if (&editing_allowed($escsymb.':::'.$idx,$group)) {
                                       if (($env{'user.domain'} eq $contrib{$idx.':senderdomain'}) && ($env{'user.name'} eq $contrib{$idx.':sendername'})) {
                                           $sender.=' <a href="/adm/feedback?editdisc='.
                                                $escsymb.':::'.$idx;
                                           if ($$newpostsflag) {
                                               $sender .= '&amp;previous='.$prevread;
                                           }
           $sender .= &group_args($group);
                                           $sender .= '">'.&mt('Edit').'</a>';
                                       
                                           unless ($seeid) {
                                               my $grpargs = &group_args($group);
                                               $sender.=" <a href=\"javascript:verifydelete('studentdelete','$escsymb','$idx','$$newpostsflag','$prevread','$grpargs')";
                                               $sender .= '">'.&mt('Delete').'</a>';
                                           }
                                       }
                                   }
           if ($seeid) {
               if ($hiddens{$idx}) {
                                           unless ($studenthidden) {
                       $sender.=' <a href="/adm/feedback?unhide='.
                        $escsymb.':::'.$idx;
                                               if ($$newpostsflag) {
                                                   $sender .= '&amp;previous='.$prevread;
                                               }
                                               $sender .= '">'.&mt('Make Visible').'</a>';
                                           }
               } else {
           $sender.=' <a href="/adm/feedback?hide='.
                    $escsymb.':::'.$idx;
                                           if ($$newpostsflag) {
                                               $sender .= '&amp;previous='.$prevread;
                                           }
           $sender .= &group_args($group);
                                           $sender .= '">'.&mt('Hide').'</a>';
               }
                                       my $grpargs = &group_args($group);
               $sender.= 
                                           " <a href=\"javascript:verifydelete('seeiddelete','$escsymb','$idx','$$newpostsflag','$prevread','$grpargs')\">";
                                       $sender .= &mt('Delete').'</a>';
                                   }
                               }
                           } else {
       if ($screenname) {
           $sender='<i>'.$screenname.'</i>';
       } else {
    $sender='<i>'.$$anonhash{$key}.'</i>';
       }
                               $sender = '<b>'.$sender.'</b>';
   # Set up for sorting by domain, then username for anonymous
                               unless (defined($$usernamesort{'__anon'})) {
                                   %{$$usernamesort{'__anon'}} = ();
                               }
                               if (defined($$usernamesort{'__anon'}{'__anon'})) {
                                   push(@{$$usernamesort{'__anon'}{'__anon'}}, $idx);
                               } else {
                                   @{$$usernamesort{'__anon'}{'__anon'}} = ("$idx");
                               }
   # Set up for sorting by last name, then first name for anonymous
                               unless (defined($$namesort{'__anon'})) {
                                   %{$$namesort{'__anon'}} = ();
                               }
                               if (defined($$namesort{'__anon'}{'__anon'})) {
                                   push(@{$$namesort{'__anon'}{'__anon'}}, $idx);
                               } else {
                                   @{$$namesort{'__anon'}{'__anon'}} = ("$idx");
                               }
           }
                           if ($outputtarget ne 'tex') {
               if (&discussion_open($status)) {
                                   if (($group ne '') && 
                                       (&check_group_priv($group,'pgd') eq 'ok')) {
                                        $sender.=' '.
                                            &discussion_link($symb,&mt('Reply'),'replydisc',$idx,$$newpostsflag,$prevread,&group_args($group));
                                   } elsif (&Apache::lonnet::allowed('pch', 
        $env{'request.course.id'}.
        ($env{'request.course.sec'}?'/'.
                                         $env{'request.course.sec'}:''))) {
                $sender.=' '.
                                            &discussion_link($symb,&mt('Reply'),'replydisc',$idx,$$newpostsflag,$prevread);
                                   }
                               }
               if ($viewgrades) {
           $vgrlink=&Apache::loncommon::submlink(&mt('Submissions'),
                                   $contrib{$idx.':sendername'},$contrib{$idx.':senderdomain'},$ressymb);
               }
                               if ($$dischash{$readkey}=~/\.$idx\./) { 
                                   $ctlink = '<label><b>'.&mt('Mark unread').'?</b>&nbsp;<input type="checkbox" name="postunread_'.$idx.'" /></label>';
                               } else {
                                   $ctlink = '<label><b>'.&mt('Mark read').'?</b>&nbsp;<input type="checkbox" name="postread_'.$idx.'" /></label>';
                               }
                           }
                       }
   #figure out at what position this needs to print
                   }
                   if ($outputtarget eq 'export' || $message) {
       my $thisindex=$idx;
       if ( (($env{'environment.threadeddiscussion'}) && ($sortposts eq '')) || ($sortposts eq 'thread') || ($outputtarget eq 'export')) {
    $thisindex=$origindex.substr('00'.$$replies[$$depth[$idx]],-2,2);
       }
       $$alldiscussion{$thisindex}=$idx;
                       $$shown{$idx} = 0;
                       $index[$idx]=$thisindex;
                   }
                   if ($outputtarget eq 'export') {
                       %{$$imsitems{$idx}} = ();
                       $$imsitems{$idx}{'isvisible'}='true';
                       if ($hiddens{$idx}) {
                           $$imsitems{$idx}{'isvisible'}='false';
                       }
                       $$imsitems{$idx}{'title'}=$subjects{$numoldver};
                       $$imsitems{$idx}{'message'}=$messages{$numoldver};
                       $$imsitems{$idx}{'attach'}=$attachtxt{$numoldver};
                       $$imsitems{$idx}{'timestamp'}=$contrib{$idx.':timestamp'};
                       $$imsitems{$idx}{'sender'}=$plainname.' ('.
                                            $contrib{$idx.':sendername'}.':'.
                                            $contrib{$idx.':senderdomain'}.')';
                       $$imsitems{$idx}{'isanonymous'}='false';
                       if ($contrib{$idx.':anonymous'}) {
                           $$imsitems{$idx}{'isanonymous'}='true';
                       }
                       $$imsitems{$idx}{'currversion'}=$numoldver;
                       %{$$imsitems{$idx}{'allattachments'}}=%allattachments;
                       unless ($messages{$numoldver} eq '' && $attachtxt{$numoldver} eq '') {
                           $$shown{$idx} = 1;
                       }
                   } else {
                       if ($message) {
                           my $spansize = 2;
                           if ($showonlyunread && $prevread > $posttime) {
                               $$notshown{$idx} = 1;
                           } elsif ($showunmark && $$dischash{$readkey}=~/\.$idx\./) {
                               $$notshown{$idx} = 1;
                           } else {
   # apply filters
                               my $uname = $contrib{$idx.':sendername'};
                               my $udom = $contrib{$idx.':senderdomain'};
                               my $poster = $uname.':'.$udom;
                               if ($env{'form.totposters'} ne '') {
                                   if ($totposters == 0) {
                                       $$shown{$idx} = 0;
                                   } elsif ($totposters > 0) {
                                       if (grep/^$poster$/,@{$posters}) {
                                           $$shown{$idx} = 1;
                                       }
                                   }
                               } elsif ($sortposts) {
                                   if ($skiptest) {
                                       $$shown{$idx} = 1;
                                   } else {
                                       foreach my $role (@{$$roleinfo{$poster}}) {
                                           if ($role =~ /^cc:/) {
                                               my $cc_regexp = $roleregexp.':[^:]*:'.$statusregexp;
                                               if ($role =~ /$cc_regexp/) {
                                                   $$shown{$idx} = 1;
                                                   last;
                                               }
                                           } elsif ($role =~ /^$rolematch$/) {
                                               $$shown{$idx} = 1;
                                               last;
                                           }
                                       }
                                   }
                                   if ($$shown{$idx} && !$skip_group_check) {
                                       my $showflag = 0;
                                       if (ref($$classgroups{$poster}{active}) eq 'HASH') {
                                           foreach my $grp (@{$grouppick}) {
                                               if (grep/^\Q$grp\E$/,
                                    keys(%{$$classgroups{$poster}{active}})) {
                                                   $showflag = 1;
                                                   last;
                                               }
                                           }
                                       }
                                       if ($showflag) {
                                           $$shown{$idx} = 1;
                                       } else {
                                           $$shown{$idx} = 0;
                                       }
                                   }
                               } else {
                                   $$shown{$idx} = 1;
                               }
                           }
                           unless ($$notshown{$idx} == 1) {
                               if ($prevread > 0 && $prevread <= $posttime) {
                                   $$newitem{$idx} = 1;
                                   $$discussionitems[$idx] .= '
                                     <table border="0" width="100%">
                                     <tr><td align="left"><font color="#FF0000"><b>'.&mt('NEW').'&nbsp;</b></font></td>';
                               } else {
                                   $$newitem{$idx} = 0;
                                   $$discussionitems[$idx] .= '
                                     <table border="0" width="100%">
                                     <tr><td align="left">&nbsp;</td>';
                               }
                               $$discussionitems[$idx] .= '<td align="left">&nbsp;&nbsp;'.
                                   '<b>'.$subject.'</b>&nbsp;&nbsp;'.
                                   $sender.' '.$vgrlink.' ('.
                                   &Apache::lonlocal::locallocaltime($posttime).')</td>';
                               if ($$dischash{$toggkey}) {
                                   $$discussionitems[$idx].='<td align="right">&nbsp;&nbsp;'.
                                     $ctlink.'</td>';
                               }
                               $$discussionitems[$idx].= '</tr></table><blockquote>'.
                                       $message.'</blockquote>';
                               if ($contrib{$idx.':history'}) {
                                   my @postversions = ();
                                   $$discussionitems[$idx] .= &mt('This post has been edited by the author.');
                                   if ($seeid) {
                                       $$discussionitems[$idx] .= '&nbsp;&nbsp;<a href="/adm/feedback?allversions='.$escsymb.':::'.$idx;
       $$discussionitems[$idx] .= &group_args($group);
                                       $$discussionitems[$idx] .= '">'.&mt('Display all versions').'</a>';
                                   }
                                   $$discussionitems[$idx].='<br/>'.&mt('Earlier version(s) were posted on: ');
                                   if ($contrib{$idx.':history'} =~ m/:/) {
                                       @postversions = split(/:/,$contrib{$idx.':history'});
                                   } else {
                                       @postversions = ("$contrib{$idx.':history'}");
                                   }
                                   for (my $i=0; $i<@postversions; $i++) {
                                       my $version = $i+1;
                                       $$discussionitems[$idx] .= '<b>'.$version.'.</b> - '.&Apache::lonlocal::locallocaltime($postversions[$i]).'  ';
                                   }
                               }
                           }
                       }
                   }
               }
    }
       }
   }
   
   sub filter_regexp {
       my ($rolefilter,$sectionpick,$statusfilter) = @_;
       my ($roleregexp,$secregexp,$statusregexp);
       my $skiptest = 1;
       if (@{$rolefilter} > 0) {
           my @okrolefilter = ();
           foreach my $role (@{$rolefilter}) {
               unless ($role eq '') {
                   push(@okrolefilter, $role);
               }
           }
           if (@okrolefilter > 0) {
               if (grep/^all$/,@okrolefilter) {
                   $roleregexp='[^:]+';
               } else {
                   if (@okrolefilter == 1) {
                       $roleregexp=$okrolefilter[0];
                   } else {
                       $roleregexp='('.join('|',@okrolefilter).')';
                   }
                   $skiptest = 0;
               }
           }
       }
       if (@{$sectionpick} > 0) {
           my @oksectionpick = ();
           foreach my $sec (@{$sectionpick}) {
               unless ($sec eq '') {
                    push(@oksectionpick, $sec);
               }
           }
           if ((@oksectionpick > 0) && (!grep/^all$/,@oksectionpick)) {
               if (@oksectionpick == 1) {
                   $secregexp = $oksectionpick[0];
               } else {
                   $secregexp .= '('.join('|',@oksectionpick).')';
               }
               $skiptest = 0;
           } else {
               $secregexp .= '[^:]*';
           }
       }
   
       if (defined($statusfilter) && $statusfilter ne '') {
           if ($statusfilter eq 'all') {
               $statusregexp = '[^:]+';
           } else {
               $statusregexp = $statusfilter;
               $skiptest = 0;
           }
       }
       return ($skiptest,$roleregexp,$secregexp,$statusregexp);
   }
   
   
   sub get_post_contents {
       my ($contrib,$idx,$seeid,$type,$messages,$subjects,$allattachments,$attachtxt,$imsfiles,$screenname,$plainname,$numver) = @_;
       my $discussion = '';
       my $start=$numver;
       my $end=$numver + 1;
       %{$$imsfiles{$idx}}=();
       if ($type eq 'allversions') {
          unless($seeid) {
              $discussion=&mt('You do not have privileges to view all versions of posts.').' '.&mt('Please select a different role.');
              return $discussion;
          } 
       }
   #    $$screenname=&Apache::loncommon::screenname(
   #                                        $$contrib{$idx.':sendername'},
   #                                        $$contrib{$idx.':senderdomain'});
       $$plainname=&Apache::loncommon::nickname(
                                           $$contrib{$idx.':sendername'},
                                           $$contrib{$idx.':senderdomain'});
       $$screenname=$$contrib{$idx.':screenname'};
   
       my $sender=&Apache::loncommon::aboutmewrapper(
                                    $$plainname,
                                    $$contrib{$idx.':sendername'},
                                    $$contrib{$idx.':senderdomain'}).' ('.
                                    $$contrib{$idx.':sendername'}.':'.
                                    $$contrib{$idx.':senderdomain'}.')';
       my $attachmenturls = $$contrib{$idx.':attachmenturl'};
       my @postversions = ();
       if ($type eq 'allversions' || $type eq 'export') {
           $start = 0;
           if ($$contrib{$idx.':history'}) {
       @postversions = split(/:/,$$contrib{$idx.':history'});
           }
           &get_post_versions($messages,$$contrib{$idx.':message'},1);
           &get_post_versions($subjects,$$contrib{$idx.':subject'},1);
           push(@postversions,$$contrib{$idx.':timestamp'});
           $end = @postversions;
       } else {
           &get_post_versions($messages,$$contrib{$idx.':message'},1,$numver);
           &get_post_versions($subjects,$$contrib{$idx.':subject'},1,$numver);
       }
   
       if ($$contrib{$idx.':anonymous'}) {
           $sender.=' ['.&mt('anonymous').'] '.$$screenname;
       }
       if ($type eq 'allversions') {
           $discussion=('<b>'.$sender.'</b><br /><ul>');
       }
       for (my $i=$start; $i<$end; $i++) {
           my ($timesent,$attachmsg);
           my %currattach = ();
           $timesent = &Apache::lonlocal::locallocaltime($postversions[$i]);
           unless (&contains_block_html($messages->{$i})) {
               &newline_to_br(\$messages->{$i});
           }
           $$messages{$i}=&Apache::lontexconvert::msgtexconverted($$messages{$i});
           $$subjects{$i}=~s/\n/\<br \/\>/g;
           $$subjects{$i}=&Apache::lontexconvert::msgtexconverted($$subjects{$i});
           if ($attachmenturls) {
               &extract_attachments($attachmenturls,$idx,$i,\$attachmsg,$allattachments,\%currattach);
           }
           if ($type eq 'export') {
               $$imsfiles{$idx}{$i} = '';
               if ($attachmsg) {
                   $$attachtxt{$i} = '<br />'.&mt('Attachments').':<br />';
                   foreach my $key (sort(keys(%currattach))) {
                       if ($$allattachments{$key}{'filename'} =~ m-^/uploaded/([^/]+/[^/]+)(/feedback)?(/?\d*)/([^/]+)$-) {
                           my $fname = $1.$3.'/'.$4;
                           $$imsfiles{$idx}{$i} .= '<file href="'.$fname.'" />'."\n";
                           $$attachtxt{$i}.= '<a href="'.$fname.'">'.$4.'</a><br />';
                       }
                   }
               }
           } else {
               if ($attachmsg) {
                   $$attachtxt{$i} = '<br />'.&mt('Attachments').':'.$attachmsg.'<br />';
               } else {
                   $$attachtxt{$i} = '';
               }
           }
           if ($type eq 'allversions') {
               $discussion.= <<"END";
   <li><b>$$subjects{$i}</b>, $timesent<br />
   $$messages{$i}<br />
   $$attachtxt{$i}</li>
   END
         }          }
         if (typeof(document.mailform.elements.anondiscuss)!="undefined") {      }
           if (document.mailform.elements.anondiscuss.checked) {      if ($type eq 'allversions') {
           $discussion.='</ul>';
           return $discussion;
       } else {
           return;
       }
   }
   
   sub replicate_attachments {
       my ($attachrefs,$tempexport) = @_;
       my $response;
       foreach my $id (keys(%{$attachrefs})) {
           if ($$attachrefs{$id}{'filename'} =~ m-^/uploaded/([^/]+)/([^/]+)(/feedback)?(/?\d*)/([^/]+)$-) {
               my $path = $tempexport;
               my $tail = $1.'/'.$2.$4;
               my @extras = split(/\//,$tail);
               my $destination = $tempexport.'/'.$1.'/'.$2.$4.'/'.$5;
               if (!-e $destination) {
                   my $i= 0;
                   while ($i<@extras) {
                       $path .= '/'.$extras[$i];
                       if (!-e $path) {
                           mkdir($path,0700);
                       }
                       $i ++;
                   }
                   my ($content,$rtncode);
                   my $uploadreply = &Apache::lonnet::getuploaded('GET',$$attachrefs{$id}{'filename'},$1,$2,$content,$rtncode);
                   if ($uploadreply eq 'ok') {
                       my $attachcopy;
                       if ($attachcopy = Apache::File->new('>'.$destination)) {
                           print $attachcopy $content;
                           close($attachcopy);
                       } else {
                           $response .= &mt('Error copying file attachment - [_1] to IMS package',$5).': '.$!.'<br />'."\n";
                       }
                   } else {
                       &Apache::lonnet::logthis("Replication of attachment failed when building IMS export of discussion posts - domain: $1, course: $2, file: $$attachrefs{$id}{'filename'} -error: $rtncode");
                       $response .= &mt('Error copying file attachment - [_1] to IMS package: ',$5).$rtncode.'<br />'."\n";
                   }
               }
           }
       }
       return $response;
   }
   
   sub mail_screen {
     my ($r,$feedurl,$options,$caller_symb,$attachmaxtext) = @_;
     if (exists($env{'form.origpage'})) {
         &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['subject','comment','currnewattach','addnewattach','deloldattach','delnewattach','timestamp','idx','anondiscuss','discuss','blog','group','ref']);
     }
   
     my %lt = &Apache::lonlocal::texthash(
               'myqu' => 'My question/comment/feedback:',
               'title' => 'Title',
               'reta' => 'Retained attachments',
               'atta' => 'Attachment',
              );
     my $restitle = &get_resource_title($caller_symb,$feedurl);
     my $quote='';
     my $subject = '';
     my $comment = '';
     my $prevtag = '';
     my $parentmsg = '';
     my ($symb,$idx,$attachmenturls);
     my $numoldver = 0;
     my $attachmsg = '';
     my $newattachmsg = '';
     my @currnewattach = ();
     my @currdelold = ();
     my @keepold = ();
     my %attachments = ();
     my %currattach = ();
     my $attachnum = 0;
     my $anonchk = (<<END);
     function anonchk() {
         for (var i=0; i < document.mailform.discuss.length; i++) {
     if (document.mailform.discuss[i].checked) {
         document.attachment.discuss.value = 
     document.mailform.discuss[i].value;
     }
         }
         if (document.mailform.blog.checked) {
     document.attachment.blog.value = 1;
         }
        return
      }
   END
     my $anonscript;
     if (exists($env{'form.origpage'})) {
         $anonscript = (<<END);
     function setposttype() {
         var disc = "$env{'form.discuss'}";
         for (var i=0; i < document.mailform.discuss.length; i++) {
     if (disc == document.mailform.discuss[i].value) {
         document.mailform.discuss[i].checked = 1;
     }
         }
         var blog = "$env{'form.blog'}";
         if (blog == 1) {
             document.mailform.blog.checked=1;
         }
         return
     }
   END
     } else {
         $anonscript = (<<END);
     function setposttype() {
         return
     }
   END
     }
     if (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
         if ($env{'form.replydisc'}) {
             ($symb,$idx)=split(/\:\:\:/,$env{'form.replydisc'});
         } else {
             ($symb,$idx)=split(/\:\:\:/,$env{'form.editdisc'});
         }
         my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
      $env{'course.'.$env{'request.course.id'}.'.domain'},
      $env{'course.'.$env{'request.course.id'}.'.num'});
         unless (($contrib{'hidden'}=~/\.$idx\./) || ($contrib{'deleted'}=~/\.$idx\./)) {
             if ($contrib{$idx.':history'}) {
                 if ($contrib{$idx.':history'} =~ /:/) {
                     my @oldversions = split(/:/,$contrib{$idx.':history'});
                     $numoldver = @oldversions;
                 } else {
                     $numoldver = 1;
                 }
             }
             if ($env{'form.replydisc'}) {
                 if ($contrib{$idx.':history'}) {
                     if ($contrib{$idx.':history'} =~ /:/) {
                         my @oldversions = split(/:/,$contrib{$idx.':history'});
                         $numoldver = @oldversions;
                     } else {
                         $numoldver = 1;
                     }
                 }
                 if ($idx > 0) {
                     my %msgversions = ();
                     &get_post_versions(\%msgversions,$contrib{$idx.':message'},0,$numoldver);
                     $quote = $msgversions{$numoldver};
                 }
                 if ($idx > 0) {
                     my %subversions = ();
                     &get_post_versions(\%subversions,$contrib{$idx.':subject'},1,$numoldver);
                     $subject = &mt('Re: ').$subversions{$numoldver};
                 }
                 $subject = &HTML::Entities::encode($subject,'<>&"');
             } else {
                 $attachmenturls = $contrib{$idx.':attachmenturl'};
                 if ($idx > 0) {
                     my %msgversions = ();
                     &get_post_versions(\%msgversions,$contrib{$idx.':message'},0,$numoldver);
                     $comment = $msgversions{$numoldver};
                     my %subversions = ();
                     &get_post_versions(\%subversions,$contrib{$idx.':subject'},0,$numoldver);
                     $subject = $subversions{$numoldver}; 
                 }
                 if (defined($contrib{$idx.':replyto'})) {
                     $parentmsg = $contrib{$idx.':replyto'};
                 }
                 unless (exists($env{'form.origpage'})) {
                     my $anonflag = 'nonanon';
                     if ($contrib{$idx.':anonymous'}) {
                         $anonflag = 'anon';
                     }
                     $anonscript = (<<END);
     function setposttype () {
         var currtype = "$anonflag";
         for (var i=0; i<document.mailform.discuss.length; i++) {
     if (document.mailform.elements.discuss[i].value == currtype ) {
         document.mailform.elements.discuss[i].checked=1;
     } 
         }
         return
     }
   END
                 }
             }
         }
         if ($env{'form.previous'}) {
             $prevtag = '<input type="hidden" name="previous" value="'.$env{'form.previous'}.'" />';
         }
     }
   
     if ($env{'form.origpage'}) {
         $subject = &unescape($env{'form.subject'});
         $comment = &unescape($env{'form.comment'});
         &process_attachments(\@currnewattach,\@currdelold,\@keepold);
     }
     my $latexHelp=&Apache::loncommon::helpLatexCheatsheet(undef,undef,1);
     my $send=&mt('Send');
     my $alert = &mt('Please select a feedback type.');
     my $js= <<END;
   <script type="text/javascript">
   //<!--
       function gosubmit() {
           var rec=0;
           if (typeof(document.mailform.elements.discuss)!="undefined") {
       if (typeof(document.mailform.elements.discuss.length) == "undefined") {
    if (document.mailform.elements.discuss.checked ) {
       rec=1;
    }
       } else {
    for (var i=0; i<document.mailform.elements.discuss.length; i++) {
       if (document.mailform.elements.discuss[i].checked ) {
    rec=1;
       } 
    }
       }
    }
           if (typeof(document.mailform.elements.blog)!="undefined") {
             if (document.mailform.elements.blog.checked) {
              rec=1;               rec=1;
           }             } 
         }          }
   
         if (rec) {          if (rec) {
               if (typeof(document.mailform.onsubmit)=='function') {
    document.mailform.onsubmit();
       }
     document.mailform.submit();      document.mailform.submit();
         } else {          } else {
             alert('Please check a feedback type.');              alert('$alert');
  }   }
     }      }
       $anonchk
       $anonscript
   //-->
 </script>  </script>
 </head>  END
 <body bgcolor="#FFFFFF" onLoad="window.focus();">  
 <img align=right src=/adm/lonIcons/lonlogos.gif>    my ($textareaheader,$textareaclass);
 <h1>Feedback</h1>    if (&Apache::lonhtmlcommon::htmlareabrowser()) {
 <h2><tt>$feedurl</tt></h2>        $textareaheader = &Apache::lonhtmlcommon::htmlareaselectactive();
 <form action="/adm/feedback" method=post name=mailform>        $textareaclass = 'class="LC_richDefaultOff"';
 <input type=hidden name=postdata value="$feedurl">        if ($env{'request.course.id'}) {
 Please check at least one of the following feedback types:            unless (($env{'course.'.$env{'request.course.id'}.'.allow_limited_html_in_feedback'} =~ /^\s*yes\s*$/i) || ($env{'form.sendmessageonly'})) {
 $options<hr>                undef($textareaclass);
 My question/comment/feedback:<p>            }
 <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>    # Breadcrumbs
     my $brcrum = [{'href' => '',
                    'text' => 'Resource Feedback and Discussion'}];
   
     my %onload = ('onload' => 'window.focus();setposttype();');
     my %parms=('add_entries' => \%onload);
     if ($env{'form.modal'} ne 'yes') { 'bread_crumbs' => $brcrum }
     my $start_page=
         &Apache::loncommon::start_page('Resource Feedback and Discussion',$js,\%parms);
   
     if ($quote ne '') {
         $quote = &HTML::Entities::decode($quote);
         unless (&contains_block_html($quote)) {
             &newline_to_br(\$quote);
         }
         $quote='<blockquote>'.&Apache::lontexconvert::msgtexconverted($quote).'</blockquote>';
     }
   
     $r->print(<<END);
   $start_page
   <h2><tt>$restitle</tt></h2>
   <form action="/adm/feedback" method="post" name="mailform"
   enctype="multipart/form-data">
   $prevtag
   <input type="hidden" name="postdata" value="$feedurl" />
   <input type="hidden" name="modal" value="$env{'form.modal'}" />
   END
     if ($env{'form.replydisc'}) {
         $r->print(<<END);
   <input type="hidden" name="replydisc" value="$env{'form.replydisc'}" />
   END
     } elsif ($env{'form.editdisc'}) {
        $r->print(<<END);
   <input type="hidden" name="editdisc" value="$env{'form.editdisc'}" />
   <input type="hidden" name="parentmsg" value="$parentmsg" />
   END
     }
     $r->print(<<END);
   $options<hr />
   $quote
   <p>$lt{'myqu'}
   $textareaheader
   </p>
   <p>
   $latexHelp
   </p>
   <p>
   $lt{'title'}: <input type="text" name="subject" size="30" value="$subject" /></p>
   <p>
   <textarea name="comment" id="comment" cols="60" rows="10" $textareaclass>$comment
   </textarea></p>
   END
       if ( ($env{'form.editdisc'}) || ($env{'form.replydisc'}) ) {
           if ($env{'form.origpage'}) {
               foreach my $attach (@currnewattach) {
                   $r->print('<input type="hidden" name="currnewattach" value="'.$attach.'" />'."\n");
               }
               foreach my $oldatt (@currdelold) {
                   $r->print('<input type="hidden" name="deloldattach" value="'.$oldatt.'" />'."\n");
               }
           }
           if ($env{'form.editdisc'}) {
               if ($attachmenturls) {
                   &extract_attachments($attachmenturls,$idx,$numoldver,\$attachmsg,\%attachments,\%currattach,\@currdelold);
                   $attachnum = scalar(keys(%currattach));
                   foreach my $key (keys(%currattach)) {
                       $r->print('<input type="hidden" name="keepold" value="'.$key.'" />'."\n");
                   }
               }
           }
       } else {
           $r->print(<<END);
   <p>
   $lt{'atta'} $attachmaxtext: <input type="file" name="attachment" />
   </p>
   END
       }
       if (exists($env{'form.group'})) {
           $r->print('<input type="hidden" name="group" value="'.$env{'form.group'}.'" />');
       }
       if (exists($env{'form.ref'})) {
           $r->print('<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />');
       }
       $r->print(<<END);
   <p>
   <input type="hidden" name="sendit" value="1" />
   <input type="button" value="$send" onclick='gosubmit();' />
   </p>
 </form>  </form>
 </body>  END
 </html>      if ($env{'form.editdisc'} || $env{'form.replydisc'}) {
 ENDDOCUMENT          my $now = time;
           my $ressymb = $symb;
           &Apache::lonenc::check_encrypt(\$ressymb);
           my $postidx = '';
           if ($env{'form.editdisc'}) {
               $postidx = $idx;
           }
           if (@currnewattach > 0) {
               $attachnum += @currnewattach;
           }
           my $blockblog = &Apache::loncommon::blocking_status('blogs');
           $r->print(&generate_attachments_button($postidx,$attachnum,$ressymb,$now,\@currnewattach,\@currdelold,$numoldver,'',$blockblog));
           if ($attachnum > 0) {
               if (@currnewattach > 0) {
                   $newattachmsg .= '<br /><b>'.&mt('New attachments').'</b><br />';
                   if (@currnewattach > 1) {
                       $newattachmsg .= '<ol>';
                       foreach my $item (@currnewattach) {
                           $item =~ m#.*/([^/]+)$#;
                           $newattachmsg .= '<li><a href="'.$item.'">'.$1.'</a></li>'."\n";
                       }
                       $newattachmsg .= '</ol>'."\n";
                   } else {
                       $currnewattach[0] =~ m#.*/([^/]+)$#;
                       $newattachmsg .= '<a href="'.$currnewattach[0].'">'.$1.'</a><br />'."\n";
                   }
               }
               if ($attachmsg) {
                   $r->print("<br /><b>$lt{'reta'}</b>:$attachmsg<br />\n");
               }
               if ($newattachmsg) {
                   $r->print("$newattachmsg<br />");
               }
           }
       }
       $r->print(&generate_preview_button().
         &Apache::loncommon::end_page());
   
   }
   
   sub print_display_options {
       my ($r,$symb,$previous,$dispchgA,$dispchgB,$markchg,$toggchg,$feedurl) = @_;
       &Apache::loncommon::content_type($r,'text/html');
       $r->send_http_header;
   
       my $function = &Apache::loncommon::get_users_function();
       my $tabcolor = &Apache::loncommon::designparm($function.'.tabbg',
                                                       $env{'user.domain'});
   
       my %lt = &Apache::lonlocal::texthash(
           'pref' => 'Display Preference',
           'curr' => 'Current setting ',
           'actn' => 'Action',
           'deff' => 'Default for all discussions',
           'prca' => 'Preferences can be set for this discussion that determine ....',
           'whpo' => 'Which posts are displayed when you display this discussion board or resource, and',
           'unwh' => 'Under what circumstances posts are identified as "NEW", and',
           'wipa' => 'Whether individual posts can be marked as read/unread',
           'allposts' => 'All posts',
           'unread' => 'New posts only',
           'unmark' => 'Posts not marked read',
           'ondisp' => 'Once displayed',
           'onmark' => 'Once marked not NEW',
           'toggon' => 'Shown',
           'toggoff' => 'Not shown',
           'disa' => 'Posts displayed?',
           'npmr' => 'New posts cease to be identified as "NEW"?',
           'dotm' => 'Option to mark each post as read/unread?',  
           'chgt' => 'Change to ',
           'mkdf' => 'Set to ',
           'yhni' => 'You have not indicated that you wish to change any of the discussion settings',
           'ywbr' => 'You will be returned to the previous page if you click OK.'
       );
   
       my $dispchangeA = $lt{'unread'};
       my $dispchangeB = $lt{'unmark'};
       my $markchange = $lt{'ondisp'};
       my $toggchange = $lt{'toggon'};
       my $currdisp = $lt{'allposts'};
       my $currmark = $lt{'onmark'};
       my $discdisp = 'allposts';
       my $discmark = 'onmark';
       my $currtogg = $lt{'toggoff'};
       my $disctogg = 'toggoff';
                                                                                         
       if ($dispchgA eq 'allposts') {
           $dispchangeA = $lt{'allposts'};
           $currdisp = $lt{'unread'};
           $discdisp = 'unread';
       }
   
       if ($markchg eq 'markonread') {
           $markchange = $lt{'onmark'};
           $currmark = $lt{'ondisp'};
           $discmark = 'ondisp';
       }
   
       if ($dispchgB eq 'onlyunread') {
           $dispchangeB = $lt{'unread'};
           $currdisp = $lt{'unmark'};
           $discdisp = 'unmark';
       }
       if ($toggchg eq 'toggoff') {
           $toggchange = $lt{'toggoff'};
           $currtogg = $lt{'toggon'};
           $disctogg = 'toggon';
       }
   
       my $js = <<END;
   <script type="text/javascript">
   function discdispChk(caller) {
       var disctogg = '$toggchg'
       if (caller == 0) {
           if (document.modifydisp.discdisp[0].checked == true) {
               if (document.modifydisp.discdisp[1].checked == true) {
                   document.modifydisp.discdisp[1].checked = false
               }
           }
       }
       if (caller == 1) {
           if (document.modifydisp.discdisp[1].checked == true) {
               if (document.modifydisp.discdisp[0].checked == true) {
                   document.modifydisp.discdisp[0].checked = false
               }
               if (disctogg == 'toggon') {
                   document.modifydisp.disctogg.checked = true
               }
               if (disctogg == 'toggoff') {
                   document.modifydisp.disctogg.checked = false
               }
           }
       }
       if (caller == 2) {
           var dispchgB = '$dispchgB'
           if (disctogg == 'toggoff') {
               if (document.modifydisp.disctogg.checked == true) {
                   if (dispchgB == 'onlyunmark') {
                       document.modifydisp.discdisp[1].checked = false
                   }
               }
           }
       }  
   }
   
   function setDisp() {
       var prev = "$previous"
       var chktotal = 0
       if (document.modifydisp.discdisp[0].checked == true) {
           document.modifydisp.$dispchgA.value = "$symb"
           chktotal ++
       }
       if (document.modifydisp.discdisp[1].checked == true) {
           document.modifydisp.$dispchgB.value = "$symb"
           chktotal ++
       }
       if (document.modifydisp.discmark.checked == true) {
           document.modifydisp.$markchg.value = "$symb"
           chktotal ++
       }
       if (document.modifydisp.disctogg.checked == true) {
           document.modifydisp.$toggchg.value = "$symb"
           chktotal ++
       }
       if (chktotal > 0) { 
           document.modifydisp.submit()
       } else {
           if(confirm("$lt{'yhni'}. \\n$lt{'ywbr'}"))      {
               if (prev > 0) {
                   location.href = "$feedurl?previous=$previous"
               } else {
                   location.href = "$feedurl"
               }
           }
       }
   }
   </script>
   END
   
   
       my $start_page =
    &Apache::loncommon::start_page('Discussion display options',$js);
       my $end_page =
    &Apache::loncommon::end_page();
       $r->print(<<END);
   $start_page
   <form name="modifydisp" method="post" action="/adm/feedback">
   $lt{'sdpf'}<br/> $lt{'prca'}  <ol><li>$lt{'whpo'}</li><li>$lt{'unwh'}</li><li>$lt{'wipa'}</li></ol>
   <br />
   END
       $r->print(&Apache::loncommon::start_data_table());
       $r->print(<<END);
          <tr>
           <th>$lt{'pref'}</td>
           <th>$lt{'curr'}</td>
           <th>$lt{'actn'}?</td>
          </tr>
   END
       $r->print(&Apache::loncommon::start_data_table_row());
       $r->print(<<END);
          <td>$lt{'disa'}</td>
          <td>$lt{$discdisp}</td>
          <td><label><input type="checkbox" name="discdisp" onclick="discdispChk('0')" />&nbsp;$lt{'chgt'} "$dispchangeA"</label>
              <br />
              <label><input type="checkbox" name="discdisp" onclick="discdispChk('1')" />&nbsp;$lt{'chgt'} "$dispchangeB"</label>
          </td>
   END
       $r->print(&Apache::loncommon::end_data_table_row());
       $r->print(&Apache::loncommon::start_data_table_row());
       $r->print(<<END);
          <td>$lt{'npmr'}</td>
          <td>$lt{$discmark}</td>
          <td><label><input type="checkbox" name="discmark" />$lt{'chgt'} "$markchange"</label></td>
   END
       $r->print(&Apache::loncommon::end_data_table_row());
       $r->print(&Apache::loncommon::start_data_table_row());
       $r->print(<<END);
          <td>$lt{'dotm'}</td>
          <td>$lt{$disctogg}</td>
          <td><label><input type="checkbox" name="disctogg" onclick="discdispChk('2')" />$lt{'chgt'} "$toggchange"</label></td>
   END
       my $save = &mt('Save');
       $r->print(&Apache::loncommon::end_data_table_row());
       $r->print(&Apache::loncommon::end_data_table());
       $r->print(<<END);
   <br />
   <br />
   <input type="hidden" name="symb" value="$symb" />
   <input type="hidden" name="previous" value="$previous" />
   <input type="hidden" name="$dispchgA" value="" />
   <input type="hidden" name="$dispchgB" value="" />
   <input type="hidden" name="$markchg" value="" />
   <input type="hidden" name="$toggchg" value="" />
   <input type="button" name="sub" value="$save" onclick="javascript:setDisp()" />
   END
       if (exists($env{'form.group'})) {
           $r->print('<input type="hidden" name="group" value="'.$env{'form.group'}.'" />');
       }
       if (exists($env{'form.ref'})) {
           $r->print('<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />');
       }
       $r->print("
   <br />
   <br />
   </form>
   $end_page
       ");
       return;
   }
   
   sub print_sortfilter_options {
       my ($r,$symb,$previous,$feedurl) = @_;
   
       &Apache::loncommon::content_type($r,'text/html');
       $r->send_http_header;
   
       &Apache::lonenc::check_encrypt(\$symb);
       my @sections;
       my $section_sel = '';
       my $numvisible = 5;
       my @groups;
       my $group_sel = '';
       my $numgroupvis = 5;
       my %sectioncount = &Apache::loncommon::get_sections();
       my @courseroles = qw(st ad ep ta in);
       my $crstype = &Apache::loncommon::course_type();
       my $ccrole = 'cc';
       if ($crstype eq 'Community') {
           $ccrole = 'co';
       }
       push(@courseroles,$ccrole);
       
       if ($env{'request.course.sec'} !~ /^\s*$/) {  #Restrict section choice to current section 
           @sections = ('all',$env{'request.course.sec'});
           $numvisible = 2;
       } else {
           @sections = sort {$a cmp $b} keys(%sectioncount);
           if (scalar(@sections) < 4) {
               $numvisible = scalar(@sections) + 1;
           }
           unshift(@sections,'all'); # Put 'all' at the front of the list
   
       }
       foreach my $sec (@sections) {
           $section_sel .= "  <option value=\"$sec\">$sec</option>\n";
       }
   
       if (&check_group_priv() eq 'ok') {
           my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
           my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; 
           my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
           @groups = sort {$a cmp $b} keys(%curr_groups);
           if (scalar(@groups) < 4) {
               $numgroupvis = scalar(@groups) + 1;
           }
           unshift(@groups,'all'); # Put 'all' at the front of the list
       } else { 
           my @coursegroups = split(/:/,$env{'request.course.groups'});
           if (@coursegroups > 0) {
               @coursegroups = sort {$a cmp $b} @coursegroups;
               @groups = ('all',@coursegroups);
               if (scalar(@groups) < 4) {
                   $numgroupvis = scalar(@groups) + 1;
               }
           } else {
               @groups = ('all');
               $numgroupvis = 1;
           }
       }
       foreach my $group (@groups) {
           $group_sel .= "  <option value=\"$group\">$group</option>\n";
       }
   
       my $function = &Apache::loncommon::get_users_function();
       my $tabcolor = &Apache::loncommon::designparm($function.'.tabbg',
                                                       $env{'user.domain'});
       my %lt = &Apache::lonlocal::texthash(
           'diop' => 'Display Options',
           'curr' => 'Current setting ',
           'actn' => 'Action',
           'prca' => 'Set options that control the sort order of posts, and/or which posts are displayed.',
           'soor' => 'Sort order',
           'spur' => 'Specific user roles',
           'sprs' => 'Specific role status',
           'spse' => 'Specific sections',
           'spgr' => 'Specific groups',
           'psub' => 'Pick specific users (by name)',
           'shal' => 'Show a list of current posters',
           'stor' => 'Save changes',
       );
   
       my %sort_types = ();
       my %role_types = ();
       my %status_types = ();
       &sort_filter_names(\%sort_types,\%role_types,\%status_types,$crstype);
   
       my $js = <<END;
   <script type="text/javascript">
   function verifyFilter() {
       var rolenum = 0
       for (var i=0; i<document.modifyshown.rolefilter.length; i++) {
           if (document.modifyshown.rolefilter.options[i].selected == true) {
               rolenum ++
           }
       }
       if (rolenum == 0) {
           document.modifyshown.rolefilter.options[0].selected = true
       }
   
       var secnum = 0
       for (var i=0; i<document.modifyshown.sectionpick.length; i++) {
           if (document.modifyshown.sectionpick.options[i].selected == true) {
               secnum ++
           }
       }
       if (secnum == 0) {
           document.modifyshown.sectionpick.options[0].selected = true
       }
   
       var grpnum = 0
       for (var i=0; i<document.modifyshown.grouppick.length; i++) {
           if (document.modifyshown.grouppick.options[i].selected == true) {
               grpnum ++
           }
       }
       if (grpnum == 0) {
           document.modifyshown.grouppick.options[0].selected = true
       }
   
       document.modifyshown.submit();
   }
   </script>
   END
   
       my $start_page=
    &Apache::loncommon::start_page('Discussion options',$js);
       my $end_page=
    &Apache::loncommon::end_page();
   
       $r->print(<<END);
   $start_page
   <form name="modifyshown" method="post" action="/adm/feedback">
   <b>$lt{'diso'}</b><br/> $lt{'prca'}
   <br /><br />
   <table border="0">
    <tr>
     <th>$lt{'soor'}</th>
     <th>&nbsp;</th>
     <th>$lt{'sprs'}</th>
     <th>&nbsp;</th>
     <th>$lt{'spur'}</th>
     <th>&nbsp;</th>
     <th>$lt{'spse'}</th>
     <th>&nbsp;</th>
     <th>$lt{'spgr'}</th>
     <th>&nbsp;</th>
     <th>$lt{'psub'}</th>
    </tr>
    <tr>
     <td align="center" valign="top">
      <select name="sortposts">
       <option value="ascdate" selected="selected">$sort_types{'ascdate'}</option>
       <option value="descdate">$sort_types{'descdate'}</option>
       <option value="thread">$sort_types{'thread'}</option>
       <option value="subject">$sort_types{'subject'}</option>
       <option value="username">$sort_types{'username'}</option>
       <option value="lastfirst">$sort_types{'lastfirst'}</option>
      </select>
     </td>
     <td>&nbsp;</td>
     <td align="center" valign="top">
      <select name="statusfilter">
       <option value="all" selected="selected">$status_types{'all'}</option>
       <option value="Active">$status_types{'Active'}</option>
       <option value="Expired">$status_types{'Expired'}</option>
       <option value="Future">$status_types{'Future'}</option>
      </select>
     </td>
     <td>&nbsp;</td>
     <td align="center" valign="top">
      <select name="rolefilter" multiple="multiple" size="5">
       <option value="all">$role_types{'all'}</option>
       <option value="st">$role_types{'st'}</option>
       <option value="$ccrole">$role_types{$ccrole}</option>
       <option value="in">$role_types{'in'}</option>
       <option value="ta">$role_types{'ta'}</option>
       <option value="ep">$role_types{'ep'}</option>
       <option value="ad">$role_types{'ad'}</option>
       <option value="cr">$role_types{'cr'}</option>
      </select>
     </td>
     <td>&nbsp;</td>
     <td align="center" valign="top">
      <select name="sectionpick" multiple="multiple" size="$numvisible">
       $section_sel
      </select>
     </td>
     <td>&nbsp;</td>
     <td align="center" valign="top">
      <select name="grouppick" multiple="multiple" size="$numvisible">
       $group_sel
      </select>
     </td>
     <td>&nbsp;</td>
     <td valign="top"><label><input type="checkbox" name="posterlist" value="$symb" />$lt{'shal'}</label></td>
    </tr>
   </table>
   <br />
   <br />
   <input type="hidden" name="previous" value="$previous" />
   <input type="hidden" name="applysort" value="$symb" />
   <input type="button" name="sub" value="$lt{'stor'}" onclick="verifyFilter()" />
   END
       if (exists($env{'form.group'})) {
           $r->print('<input type="hidden" name="group" value="'.$env{'form.group'}.'" />');
       }
       if (exists($env{'form.ref'})) {
           $r->print('<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />');
       }
       $r->print("
   <br />
   <br />
   </form>
   $end_page
   ");
   }
   
   sub print_showposters {
       my ($r,$symb,$previous,$feedurl,$sortposts) = @_;
   
       &Apache::loncommon::content_type($r,'text/html');
       $r->send_http_header;
   
       &Apache::lonenc::check_encrypt(\$symb);
       my $crs='/'.$env{'request.course.id'};
       if ($env{'request.course.sec'}) {
           $crs.='_'.$env{'request.course.sec'};
       }
       $crs=~s/\_/\//g;
       my $seeid;
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my $group = $env{'form.group'};
       my $ressymb = &wrap_symb($symb);
       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);
       }
       my %contrib=&Apache::lonnet::restore($symb,$env{'request.course.id'},
                                            $cdom,$cnum);
       my %namesort = ();
       my %postcounts = ();
   
       my %lt = &Apache::lonlocal::texthash(
                                             sele => 'Select',
                                             full => 'Fullname',
                                             usdo => 'Username:domain',
                                             post => 'Posts',
                                           );
       if ($contrib{'version'}) {
           for (my $idx=1;$idx<=$contrib{'version'};$idx++) {
               my $hidden=($contrib{'hidden'}=~/\.$idx\./);
               my $deleted=($contrib{'deleted'}=~/\.$idx\./);
               unless ((($hidden) && (!$seeid)) || ($deleted)) {
                   if ((!$contrib{$idx.':anonymous'}) || (&Apache::lonnet::allowed('rin',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')))) {
                       my %names = &Apache::lonnet::get('environment',['firstname','lastname'],$contrib{$idx.':senderdomain'},$contrib{$idx.':sendername'});
                       my $lastname = $names{'lastname'};
                       my $firstname = $names{'firstname'};
                       if ($lastname eq '') {
                           $lastname = '_';
                       }
                       if ($firstname eq '') {
                           $firstname = '_';
                       }
                       unless (defined($namesort{$lastname})) {
                           %{$namesort{$lastname}} = ();
                       }
                       my $poster =  $contrib{$idx.':sendername'}.':'.$contrib{$idx.':senderdomain'};
                       $postcounts{$poster} ++;
                       if (defined($namesort{$lastname}{$firstname})) {
                           if (!grep/^$poster$/,@{$namesort{$lastname}{$firstname}}) {
                               push(@{$namesort{$lastname}{$firstname}}, $poster);
                           }
                       } else {
                           @{$namesort{$lastname}{$firstname}} = ("$poster");
                       }
                   }
               }
           }
       }
   
       my $start_page = &Apache::loncommon::start_page('Discussion options');
       my $table_start =&Apache::loncommon::start_data_table();
       $r->print(<<END);
   $start_page
    <form name="pickpostersform" method="post">
    <br />
       $table_start
         <tr>
          <th>#</th>
          <th>$lt{'sele'}</th>
          <th>$lt{'full'} <font color="#999999">($lt{'usdo'})</font></th>
          <th>$lt{'post'}</th>
         </tr>
   END
       my $count = 0;
       foreach my $last (sort(keys(%namesort))) {
           foreach my $first (sort(keys(%{$namesort{$last}}))) {
               foreach my $user (sort(@{$namesort{$last}{$first}})) {
                   my ($uname,$udom) = split(/:/,$user);
                   if (!$uname || !$udom) { 
                       next;
                   } else {
                       $count ++;
                       $r->print(&Apache::loncommon::start_data_table_row().
         '<td align="right">'.$count.'</td>
                                  <td align="center"><label><input name="stuinfo" type="checkbox" value="'.$user.'" /></td>
                                  <td>'.$last.', '.$first.' ('.$uname.':'.$udom.')</label></td>
                                  <td>'.$postcounts{$user}.'</td>'.
         &Apache::loncommon::end_data_table_row());
                   }
               }
           }
       }
       $r->print(&Apache::loncommon::end_data_table());
       my $end_page   = &Apache::loncommon::end_page();
       $r->print(<<END);
   <br />
   <input type="hidden" name="sortposts" value="$sortposts" />
   <input type="hidden" name="userpick" value="$symb" />
   <input type="button" name="store" value="Display posts" onclick="javascript:document.pickpostersform.submit()" />
   </form>
   $end_page
   END
   }
   
   sub get_post_versions {
       my ($versions,$incoming,$htmldecode,$numver) = @_;
       if ($incoming =~ /^<version num="0">/) {
           my $p = HTML::LCParser->new(\$incoming);
           my $done = 0; 
   
           while ( (my $token = $p->get_tag("version")) && (!$done)) {
               my $num = $token->[1]{num};
               my $text = $p->get_text("/version");
               if (defined($numver)) {
                   if ($num == $numver) {
                       if ($htmldecode) {
                           $text = &HTML::Entities::decode($text);
                       }
                       $$versions{$numver}=$text;
                       $done = 1;
                   }
               } else {
                   if ($htmldecode) {
                       $text = &HTML::Entities::decode($text);
                   }
                   $$versions{$num}=$text;
               }
           }
       } else {
           if (!defined($numver)) {
               $numver = 0;
           }
           if ($htmldecode) {
               $$versions{$numver} = $incoming;
           } else {
               $$versions{$numver} = &HTML::Entities::encode($incoming,'<>&"');
           }
       }
       return;
   }
   
   sub get_post_attachments {
       my ($attachments,$attachmenturls) = @_;
       my $num;
       if ($attachmenturls =~ m/^<attachment id="0">/) {
           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 {  sub fail_redirect {
   my ($r,$feedurl) = @_;    my ($r,$feedurl) = @_;
   $r->print (<<ENDFAILREDIR);    if ($feedurl=~/^\/adm\//) { $feedurl.='?register=1' };
 <head><title>Feedback not sent</title>    my %lt = &Apache::lonlocal::texthash(
 <meta http-equiv="pragma" content="no-cache"></meta>                   'sorr' => 'Sorry, no recipients ...',
 <meta HTTP-EQUIV="Refresh" CONTENT="2; url=$feedurl">    );
 </head>    my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
 <html>    $r->print(&Apache::loncommon::start_page('Feedback not sent',undef,
 <body bgcolor="#FFFFFF">     {'redirect'  => [2,$feedurl],
 <img align=right src=/adm/lonIcons/lonlogos.gif>      'only_body' => 1,}));
 <b>Sorry, no recipients  ...</b>    $r->print(<<ENDFAILREDIR);
 </body>  <img align="right" src="$logo" />
 </html>  <b>$lt{'sorr'}</b>
 ENDFAILREDIR  ENDFAILREDIR
     $r->print(&Apache::loncommon::end_page());
 }  }
   
 sub redirect_back {  sub redirect_back {
   my ($r,$feedurl,$typestyle,$sendsomething,$status) = @_;    my ($r,$feedurl,$typestyle,$sendsomething,$sendposts,$blog,$status,$previous,$sort,$rolefilter,$statusfilter,$sectionpick,$grouppick,$numpicks,$group,$toolarge) = @_;
   $r->print (<<ENDREDIR);    my $sorttag = '';
 <head>    my $roletag = '';
 <title>Feedback sent</title>    my $statustag = '';
 <meta http-equiv="pragma" content="no-cache"></meta>    my $sectag = '';
 <meta HTTP-EQUIV="Refresh" CONTENT="2; url=$feedurl">    my $grptag = '';
 </head>    my $userpicktag = '';
 <html>    my $qrystr = '';
 <body bgcolor="#FFFFFF">    my $prevtag = '';
 <img align=right src=/adm/lonIcons/lonlogos.gif>  
     &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 .= '&amp;'.$qrystr;
         } else {
             $feedurl .= '?'.$qrystr;
         }
         $prevtag = '<input type="hidden" name="previous" value="'.$previous.'" />';
     }
     if (defined($sort)) {
         my $sortqry = 'sortposts='.$sort;
         if (($feedurl =~ /\?register=1/) || ($feedurl =~ /\?previous=/)) {
             $feedurl .= '&amp;'.$sortqry;
         } else {
             $feedurl .= '?'.$sortqry;
         }
         $sorttag = '<input type="hidden" name="sortposts" value="'.$sort.'" />';
         if (defined($numpicks)) {
             my $userpickqry = 'totposters='.$numpicks;
             $feedurl .= '&amp;'.$userpickqry;
             $userpicktag = '<input type="hidden" name="totposters" value="'.$numpicks.'" />';
         } else {
             if (ref($sectionpick) eq 'ARRAY') {
                 $feedurl .= '&amp;sectionpick=';
                 $sectag .=  '<input type="hidden" name="sectionpick" value="';
                 foreach my $sec (@{$sectionpick}) {
                     $feedurl .= $sec.',';
                     $sectag .= $sec.',';
                 }
                 $feedurl =~ s/,$//;
                 $sectag =~ s/,$//;
                 $sectag .= '" />';
             } else {
                 $feedurl .= '&amp;sectionpick='.$sectionpick;
                 $sectag = '<input type="hidden" name="sectionpick" value="'.$sectionpick.'" />';
             }
             if (ref($grouppick) eq 'ARRAY') {
                 $feedurl .= '&amp;grouppick=';
                 $sectag .=  '<input type="hidden" name="grouppick" value="';
                 foreach my $grp (@{$grouppick}) {
                     $feedurl .= $grp.',';
                     $grptag .= $grp.',';
                 }
                 $feedurl =~ s/,$//;
                 $grptag =~ s/,$//;
                 $grptag .= '" />';
             } else {
                 $feedurl .= '&amp;grouppick='.$grouppick;
                 $grptag = '<input type="hidden" name="grouppick" value="'.$grouppick.'" />';
             }
             if (ref($rolefilter) eq 'ARRAY') {
                 $feedurl .= '&amp;rolefilter=';
                 $roletag .=  '<input type="hidden" name="rolefilter" value="';
                 foreach my $role (@{$rolefilter}) {
                     $feedurl .= $role.',';
                     $roletag .= $role.',';
                 }
                 $feedurl =~ s/,$//;
                 $roletag =~ s/,$//;
                 $roletag .= '" />';
             } else {
                 $feedurl .= '&amp;rolefilter='.$rolefilter;
                 $roletag = '<input type="hidden" name="rolefilter" value="'.$rolefilter.'" />';
             }
             $feedurl .= '&amp;statusfilter='.$statusfilter;
             $statustag ='<input type="hidden" name="statusfilter" value="'.$statusfilter.'" />';
         }
     }
     my $grouptag;
     if ($group ne '') {
         $grouptag = '<input type="hidden" name="group" value="'.$group.'" />';
         my $refarg;
         if (exists($env{'form.ref'})) {
             $refarg = '&amp;ref='.$env{'form.ref'};
             $grouptag .= '<input type="hidden" name="ref" value="'.$env{'form.ref'}.'" />';
         }
         if ($feedurl =~ /\?/) {
             $feedurl .= '&amp;group='.$group.$refarg;
         } else {
             $feedurl .= '?group='.$group.$refarg;
         }
     } 
     &Apache::lonenc::check_encrypt(\$feedurl);
     my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
     my %parms=('only_body'   => 1);
     if ($env{'form.modal'}) {
         $parms{'add_entries'}={'onLoad' => 'document.forms.reldt.submit()'};
     } else {
         $parms{'redirect'}=[0,$feedurl];
     }
     my $start_page=
         &Apache::loncommon::start_page('Feedback sent',undef,\%parms);
     my $end_page = &Apache::loncommon::end_page();
     $r->print(<<ENDREDIR);
   $start_page
   <img align="right" src="$logo" />
 $typestyle  $typestyle
 <b>Sent $sendsomething message(s).</b>  <b>Sent $sendsomething message(s), and $sendposts post(s).</b>
 <font color=red>$status</font>  $blog
 </body>  $toolarge
 </html>  <font color="red">$status</font>
   <form name="reldt" action="$feedurl" target="loncapaclient">
   $prevtag
   $sorttag
   $statustag
   $roletag
   $sectag
   $grptag
   $userpicktag
   $grouptag
   </form>
   $end_page
 ENDREDIR  ENDREDIR
 }  }
   
 sub no_redirect_back {  sub no_redirect_back {
   my ($r,$feedurl) = @_;    my ($r,$feedurl) = @_;
   $r->print (<<ENDNOREDIR);    my $nofeed=&mt('Sorry, no feedback possible on this resource ...');
 <head><title>Feedback not sent</title>   
 <meta http-equiv="pragma" content="no-cache"></meta>    my %onload;
 ENDNOREDIR  
     my %body_options = ('only_body'   => 1,
         'bgcolor'     => '#FFFFFF',
         'add_entries' => \%onload,);
   
   if ($feedurl!~/^\/adm\/feedback/) {     if ($feedurl !~ m{^/adm/feedback}) { 
     $r->print('<meta HTTP-EQUIV="Refresh" CONTENT="2; url='.$feedurl.'">');        $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 (<<ENDNOREDIRTWO);    $r->print (<<ENDNOREDIRTWO);
 </head>  $start_page
 <html>  <img align="right" src="$logo" />
 <body bgcolor="#FFFFFF">  <b>$nofeed</b>
 <img align=right src=/adm/lonIcons/lonlogos.gif>  <br />
 <b>Sorry, no feedback possible on this resource  ...</b>  $end_page
 </body>  
 </html>  
 ENDNOREDIRTWO  ENDNOREDIRTWO
 }  }
   
 sub screen_header {  sub screen_header {
   my ($feedurl) = @_;      my ($feedurl,$symb,$group) = @_;
   my $options='';      my $crscontent = &mt('Question/Comment/Feedback about course content');
   if (($feedurl=~/^\/res/) && ($feedurl!~/^\/res\/adm/)) {      my $crspolicy = &mt('Question/Comment/Feedback about course policy');
     $options=       my $contribdisc = &mt('Contribution to course discussion of resource');
       '<p><input type=checkbox name=author> Feedback to resource author';      my $anoncontrib = &mt('Anonymous contribution to course discussion of resource');
   }      my $namevis = &mt('name only visible to course faculty');
   if ($ENV{'course.'.$ENV{'request.course.id'}.'.question.email'}) {      my $crstype;
     $options.=      if ($env{'request.course.id'}) {
     '<br><input type=checkbox name=question> Question about resource content';          $crstype = &Apache::loncommon::course_type();
   }          if ($crstype eq 'Community') {
   if ($ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'}) {              $crscontent = &mt('Question/Comment/Feedback about community content');
     $options.=              $crspolicy = &mt('Question/Comment/Feedback about community policy');
       '<br><input type=checkbox name=course> '.              $contribdisc = &mt('Contribution to community discussion of resource');
  'Question/Comment/Feedback about course content';              $anoncontrib = &mt('Anonymous contribution to community discussion of resource');
   }              $namevis = &mt('name only visible to community facilitators');
   if ($ENV{'course.'.$ENV{'request.course.id'}.'.policy.email'}) {          }
     $options.=      }
       '<br><input type=checkbox name=policy> '.      my $msgoptions='';
  'Question/Comment/Feedback about course policy';      my $discussoptions='';
   }      unless (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
    if (($feedurl=~/^\/res\//) && ($feedurl!~/^\/res\/adm/) && ($env{'user.adv'})) {
   if ($ENV{'request.course.id'}) {      $msgoptions= 
       if (&Apache::lonnet::allowed('pch',   '<p><label><input type="radio" name="discuss" value="author" /> '.
         $ENV{'request.course.id'}.   &mt('Feedback to resource author').'</label></p>';
         ($ENV{'request.course.sec'}?'/'.$ENV{'request.course.sec'}:''))) {   }
     $options.='<br><input type=checkbox name=discuss> '.          my %optionhash=();
  '<b>Contribution to course discussion of resource</b>';          foreach my $type ('question','comment','policy') {
     $options.='<br><input type=checkbox name=anondiscuss> '.      $optionhash{$type}=$env{'course.'.$env{'request.course.id'}.'.'.$type.'.email.text'};
  '<b>Anonymous contribution to course discussion of resource</b>'.   }
         ' (name only visible to course faculty)';   if (&feedback_available(1)) {
       }      $msgoptions.=
   }   '<p><label><input type="radio" name="discuss" value="question" /> '.
   return $options;   ($optionhash{'question'}?$optionhash{'question'}:&mt('Question about resource content')).'</label></p>';
    }
    if (&feedback_available(0,1)) {
       $msgoptions.=
    '<p><label><input type="radio" name="discuss" value="course" /> '.
    ($optionhash{'comment'}?$optionhash{'comment'}:$crscontent).
    '</label></p>';
    }
    if (&feedback_available(0,0,1)) {
       $msgoptions.=
    '<p><label><input type="radio" name="discuss" value="policy" /> '.
    ($optionhash{'policy'}?$optionhash{'policy'}:$crspolicy).
    '</label></p>';
    }
       }
       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='<label><input type="radio" name="discuss" value="nonanon" checked="checked" /> '.
    $contribdisc.
           '</label><br /><label><input type="radio" name="discuss" value="anon" /> '.
    $anoncontrib.
    ' <i>('.$namevis.')</i></label> '.
    '<a href="/adm/preferences?action=changescreenname">'.&mt('Change Screenname').'</a>';
               my $blockblog = &Apache::loncommon::blocking_status('blogs');
               if (!$blockblog) {
                   $discussoptions.= &add_blog_checkbox($crstype);
               }
           }
       }
       if ($msgoptions) {
           $msgoptions='<h2><img src="'.&Apache::loncommon::lonhttpdurl('/res/adm/pages/feedback.png').'" />'
                      .' '.&mt('Send Feedback').'</h2><p>'.&Apache::lonhtmlcommon::coursepreflink(&mt('Feedback Settings'),'feedback').'</p>'
                      .$msgoptions;
       }
       if ($discussoptions) { 
    $discussoptions='<h2><img src="'.&Apache::loncommon::lonhttpdurl('/adm/lonMisc/chat.gif').'" alt="" />'
                           .' '.&mt('Discussion Contributions').'</h2><p>'.&Apache::lonhtmlcommon::coursepreflink(&mt('Discussion Settings'),'discussion').'</p>'
                           .$discussoptions;
       }
       return $msgoptions.$discussoptions;
 }  }
   
 sub resource_output {  sub resource_output {
   my ($feedurl) = @_;    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/\<\/body\>//gi;    $usersaw=~s/\<\/body\>//gi;
   $usersaw=~s/\<html\>//gi;    $usersaw=~s/\<html\>//gi;
Line 226  sub resource_output { Line 2734  sub resource_output {
 }  }
   
 sub clear_out_html {  sub clear_out_html {
   my $message=$ENV{'form.comment'};    my ($message,$override)=@_;
   $message=~s/\</\&lt\;/g;  # Always allow the <m>-tag
   $message=~s/\>/\&gt\;/g;    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 <B> <I> <P> <A> <LI> <OL> <UL> <EM> <BR> <TT> <STRONG> 
         # <BLOCKQUOTE> <DIV .*> <DIV> <IMG> <M> <SPAN> <H1> <H2> <H3> <H4> <SUB>
         # <SUP> <TABLE> <TR> <TD> <TH> <TBODY>
         %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, PRE=>1, DIV=>1, IMG=>1,
        M=>1, CHEM=>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":"\&lt;$1"}/ge;
     $message =~ s/(\<?\s*(\w+)[^\<\>]*)\>/
     {($html{uc($2)}&&(length($1)<1000))?"$1\>":"$1\&gt;"}/ge;
   return $message;    return $message;
 }  }
   
 sub assemble_email {  sub assemble_email {
   my ($feedurl,$message,$prevattempts,$usersaw)=@_;    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)',
     );
     if (&Apache::loncommon::course_type() eq 'Community') {
         $lt{'prev'} = &mt('Previous attempts of member (if applicable)');
     }
   my $email=<<"ENDEMAIL";    my $email=<<"ENDEMAIL";
 Refers to <a href="$feedurl">$feedurl</a>  
   
 $message  $message
 ENDEMAIL  ENDEMAIL
     my $citations=<<"ENDCITE";      my $citations=<<"ENDCITE";
 <h2>Previous attempts of student (if applicable)</h2>  <h2>$lt{'prev'}</h2>
 $prevattempts  $prevattempts
 <p><hr>  <br /><hr />
 <h2>Original screen output (if applicable)</h2>  <h2>$lt{'orig'}</h2>
 $usersaw  $usersaw
   <h2>$lt{'corr'}</h2>
   $useranswer
 ENDCITE  ENDCITE
   return ($email,$citations);    return ($email,$citations);
 }  }
   
 sub decide_receiver {  
   my ($feedurl) = @_;  sub feedback_available {
   my $typestyle='';      my ($question,$course,$policy)=@_;
   my %to=();      my ($typestyle,%to)=&Apache::lonmsg::decide_receiver('',0,$question,
   if ($ENV{'form.author'}) {   $course,$policy);
     $typestyle.='Submitting as Author Feedback<br>';      return scalar(%to);
     $feedurl=~/^\/res\/(\w+)\/(\w+)\//;  
     $to{$2.':'.$1}=1;  
   }  
   if ($ENV{'form.question'}) {  
     $typestyle.='Submitting as Question<br>';  
     foreach (split(/\,/,  
    $ENV{'course.'.$ENV{'request.course.id'}.'.question.email'})  
      ) {  
       $to{$_}=1;  
     }   
   }  
   if ($ENV{'form.course'}) {  
     $typestyle.='Submitting as Comment<br>';  
     foreach (split(/\,/,  
    $ENV{'course.'.$ENV{'request.course.id'}.'.comment.email'})  
      ) {  
       $to{$_}=1;  
     }   
   }  
   if ($ENV{'form.policy'}) {  
     $typestyle.='Submitting as Policy Feedback<br>';  
     foreach (split(/\,/,  
    $ENV{'course.'.$ENV{'request.course.id'}.'.policy.email'})  
      ) {  
       $to{$_}=1;  
     }   
   }  
   return ($typestyle,%to);  
 }  }
   
 sub send_msg {  sub send_msg {
   my ($feedurl,$email,$citations,%to)=@_;      my ($title,$feedurl,$email,$citations,$attachmenturl,$symb,%to)=@_;
   my $status='';      my $status='';
   my $sendsomething=0;      my $sendsomething=0;
   foreach (keys %to) {      my $restitle = &get_resource_title($symb,$feedurl);
     if ($_) {      if ($title=~/^Error/) { $title=&mt('Feedback').': '.$title; }
       my $declutter=&Apache::lonnet::declutter($feedurl);      unless ($title=~/\w/) { $title=&mt('Feedback'); }
       unless (&Apache::lonmsg::user_normal_msg(split(/\:/,$_),      foreach my $key (keys(%to)) {
                'Feedback ['.$declutter.']',$email,$citations) eq 'ok') {   if ($key) {
  $status.='<br>Error sending message to '.$_.'<br>';      my ($user,$domain) = split(/\:/,$key,2);
       } else {      if (!defined($user)) {
  $sendsomething++;   $status.='<br />'.&mt('Error sending message to [_1], no user specified.',$key);
       }      } elsif (!defined($domain)) {
    $status.='<br />'.&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.='<br />'.&mt('Error sending message to').' '.$key.'<br />';
    } else {
       $sendsomething++;
    }
       }
    }
     }      }
   }  
   
     my %record=&Apache::lonnet::restore('_feedback');      my %record=&Apache::lonnet::restore('_feedback');
     my ($temp)=keys %record;      my ($temp)=keys(%record);
     unless ($temp=~/^error\:/) {      unless ($temp=~/^error\:/) {
        my %newrecord=();   my %newrecord=();
        $newrecord{'resource'}=$feedurl;   $newrecord{'resource'}=$feedurl;
        $newrecord{'subnumber'}=$record{'subnumber'}+1;   $newrecord{'subnumber'}=$record{'subnumber'}+1;
        unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') {   unless (&Apache::lonnet::cstore(\%newrecord,'_feedback') eq 'ok') {
    $status.='<br>Not registered<br>';      $status.='<br />'.&mt('Not registered').'<br />';
        }   }
     }      }
              
   return ($status,$sendsomething);      return ($status,$sendsomething);
 }  }
   
 sub adddiscuss {  sub adddiscuss {
     my ($symb,$email,$anon)=@_;      my ($symb,$email,$anon,$attachmenturl,$subject,$group)=@_;
     my $status='';      my $status='';
     if (&Apache::lonnet::allowed('pch',$ENV{'request.course.id'}.      my $realsymb;
         ($ENV{'request.course.sec'}?'/'.$ENV{'request.course.sec'}:''))) {      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,      my %contrib=('message'      => $email,
                  'sendername'   => $ENV{'user.name'},                   'sendername'   => $env{'user.name'},
                  'senderdomain' => $ENV{'user.domain'},                   'senderdomain' => $env{'user.domain'},
                  'screenname'   => $ENV{'environment.screenname'},                   'screenname'   => $env{'environment.screenname'},
                  'plainname'    => $ENV{'environment.firstname'}.' '.                   'plainname'    => $env{'environment.firstname'}.' '.
                    $ENV{'environment.middlename'}.' '.                     $env{'environment.middlename'}.' '.
                                    $ENV{'environment.lastname'}.' '.                                     $env{'environment.lastname'}.' '.
                                    $ENV{'enrironment.generation'});                                     $env{'enrironment.generation'},
                    'attachmenturl'=> $attachmenturl,
                    'subject'      => $subject);
       if ($env{'form.replydisc'}) {
    $contrib{'replyto'}=(split(/\:\:\:/,$env{'form.replydisc'}))[1];
       }
     if ($anon) {      if ($anon) {
  $contrib{'anonymous'}='true';   $contrib{'anonymous'}='true';
     }      }
     if (($symb) && ($email)) {      if (($symb) && ($email)) {
        $status='Adding to class discussion'.($anon?' (anonymous)':'').': '.          my $now = time;
         &Apache::lonnet::store(\%contrib,$symb,$ENV{'request.course.id'},          if ($env{'form.editdisc'}) {
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},              $contrib{'ip'}=$ENV{'REMOTE_ADDR'};
      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});              $contrib{'host'}=$Apache::lonnet::perlvar{'lonHostID'};
         my %storenewentry=($symb => time);              $contrib{'timestamp'} = $now;
         $status.='<br>Updating discussion time: '.              $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'} =~ /^<version num="0">/) {
                       $contrib{'subject'} = '<version num="'.$numnewver.'">'.&HTML::Entities::encode($contrib{'subject'},'<>&"').'</version>';
                       $contrib{'subject'} = $oldcontrib{$oldidx.':subject'}.$contrib{'subject'};
                   } else {
                       $contrib{'subject'} = '<version num="0">'.&HTML::Entities::encode($oldcontrib{$oldidx.':subject'},'<>&"').'</version><version num="1">'.&HTML::Entities::encode($contrib{'subject'},'<>&"').'</version>';
                   }
               } 
               if (defined($oldcontrib{$oldidx.':message'})) {
                   if ($oldcontrib{$oldidx.':message'} =~ /^<version num="0">/) {
                       $contrib{'message'} = '<version num="'.$numnewver.'">'.&HTML::Entities::encode($contrib{'message'},'<>&"').'</version>';
                       $contrib{'message'} = $oldcontrib{$oldidx.':message'}.$contrib{'message'};
                   } else {
                       $contrib{'message'} = '<version num="0">'.&HTML::Entities::encode($oldcontrib{$oldidx.':message'},'<>&"').'</version><version num="1">'.&HTML::Entities::encode($contrib{'message'},'<>&"').'</version>';
                   }
               }
               $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.='<br />'.&mt('Updating discussion time').': '.
         &Apache::lonnet::put('discussiontimes',\%storenewentry,          &Apache::lonnet::put('discussiontimes',\%storenewentry,
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},                       $env{'course.'.$env{'request.course.id'}.'.domain'},
      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});       $env{'course.'.$env{'request.course.id'}.'.num'});
     }      }
     my %record=&Apache::lonnet::restore('_discussion');      my %record=&Apache::lonnet::restore('_discussion');
     my ($temp)=keys %record;      my ($temp)=keys(%record);
     unless ($temp=~/^error\:/) {      unless ($temp=~/^error\:/) {
        my %newrecord=();         my %newrecord=();
        $newrecord{'resource'}=$symb;         $newrecord{'resource'}=$symb;
        $newrecord{'subnumber'}=$record{'subnumber'}+1;         $newrecord{'subnumber'}=$record{'subnumber'}+1;
        $status.='<br>Registering: '.         $status.='<br />'.&mt('Registering').': '.
                &Apache::lonnet::cstore(\%newrecord,'_discussion');                 &Apache::lonnet::cstore(\%newrecord,'_discussion');
     }      }
     } else {      } else {
  $status.='Failed.';   $status.='Failed.';
     }      }
     return $status.'<br>';         return $status.'<br />';   
 }  }
   
 sub handler {  sub get_discussion_info {
   my $r = shift;      my ($idx,%contrib) = @_;
   if ($r->header_only) {      my $changelast = 0;
      $r->content_type('text/html');      my $count = 0;
      $r->send_http_header;      my $hiddenflag = 0;
      return OK;      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);
   }
   
 # --------------------------- Get query string for limited number of parameters  # ----------------------------------------------------------- Preview function
   
     foreach (split(/&/,$ENV{'QUERY_STRING'})) {  sub show_preview {
        my ($name, $value) = split(/=/,$_);      my ($r) = @_;
        $value =~ tr/+/ /;      &Apache::loncommon::content_type($r,'text/html');
        $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;      $r->send_http_header;
        if (($name eq 'hide') || ($name eq 'unhide') || ($name eq 'postdata')) {      my $start_page=
            unless ($ENV{'form.'.$name}) {   &Apache::loncommon::start_page('Preview',undef,
               $ENV{'form.'.$name}=$value;         {'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'});
       $subject=~s/\n/\<br \/\>/g;
       $subject=&Apache::lontexconvert::msgtexconverted($subject);
   
       my $end_page = &Apache::loncommon::end_page();
   
       $r->print($start_page
                .'<h1>'.&mt('Preview').'</h1>'
                .&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 tidy_html {
    my ($message)=@_;
   # my $tidy = HTML::Tidy::libXML->new();
   # my $xhtml = $tidy->clean($message, 'utf-8', 1);
   # $xhtml =~ m/<body>(.*)<\/body>/is;
   # my $clean = $1;
   # # remove any empty block-level tags
   # $clean =~ s/<(table|p|div|tbody|blockquote|m|pre|algebra|center|ol|ul|span|h1|h2|h3|h4|h5|h6)\s*\/>//i;
   # $message=$clean;
    return $message;
   }
   
   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/\<br \/\>/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;
   }
   
   if (($ENV{'form.hide'}) || ($ENV{'form.unhide'})) {  sub generate_preview_button {
 # ----------------------------------------------------------------- Hide/unhide      my ($formname,$fieldname)=@_;
     $r->content_type('text/html');      unless ($formname) { $formname='mailform'; }
     $r->send_http_header;      unless ($fieldname) { $fieldname='comment'; }
       my $pre=&mt("Show Preview and Check Spelling");
       return(<<ENDPREVIEW);
   <br />
   <form name="preview" action="/adm/feedback?preview=1" method="post" target="preview">
   <input type="hidden" name="subject" />
   <input type="hidden" name="comment" />
   <input type="button" value="$pre"
   onclick="if (typeof(document.$formname.onsubmit)=='function') {document.$formname.onsubmit();};this.form.comment.value=document.$formname.$fieldname.value;this.form.subject.value=document.$formname.subject.value;this.form.submit();" />
   </form>
   ENDPREVIEW
   }
   
     my $entry=$ENV{'form.hide'}?$ENV{'form.hide'}:$ENV{'form.unhide'};  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 = <<END;
   <script type="text/javascript">
    function setAction () {
      document.modattachments.action = document.modattachments.origpage.value;
      document.modattachments.submit();
    }
   </script> 
   END
   
       # Breadcrumbs
       my $brcrum = [{'href' => '',
                      'text' => 'Discussion Post Attachments'}];
       my %parms=();
       if ($env{'form.modal'} ne 'yes') { 'bread_crumbs' => $brcrum }
   
       my $start_page = 
    &Apache::loncommon::start_page('Discussion Post Attachments',$js,\%parms);
   
       my $orig_subject = &unescape($env{'form.subject'});
       my $subject=&clear_out_html($orig_subject);
       $subject=~s/\n/\<br \/\>/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 ($symb,$idx)=split(/\:\:\:/,$entry);      my $end_page = 
     my ($map,$ind,$url)=split(/\_\_\_/,$symb);   &Apache::loncommon::end_page();
          
       $r->print(<<END);
   $start_page
   $toolarge
   <form name="modattachments" method="post" enctype="multipart/form-data" action="/adm/feedback?attach=$symb">
    <br />
    <table class="LC_data_table">
     <tr>
      <td colspan="2">
       <b>Subject:</b> <b>$subject</b><br /><br />
   END
       if ($idx) {
           if ($attachmenturls) {
               my @currold = keys(%currattach);
               if (@currold > 0) {
                   $r->print($lt{'thfo'}.'<br />'.$lt{'chth'}.'<br />'."\n");  
                   foreach my $id (@currold) {
                       my $attachurl = &HTML::Entities::decode($attachments{$id}{'filename'}); 
                       $attachurl =~ m#/([^/]+)$#;
                       $r->print('<label><input type="checkbox" name="deloldattach" value="'.$id.'" />&nbsp;'.$1.'</label><br />'."\n");
                   }
                   $r->print("<br />");
               }
           }
       }
       if ((ref($currnewattach) eq 'ARRAY') && (@{$currnewattach} > 0)) {
           $r->print($lt{'thef'}.'<br />'.$lt{'chth'}.'<br />'."\n");
           foreach my $attach (@{$currnewattach}) {
               $attach =~ m#/([^/]+)$#;
               $r->print('<label><input type="checkbox" name="delnewattach" value="'.$attach.'" />&nbsp;'.$1.'</label><br />'."\n");
           }
           $r->print("<br />"); 
       }
       $r->print(<<END);
      </td></tr>
      <tr>
       <td>
      $lt{'adda'}</td><td><input type="file" name="addnewattach" /><input type="button" name="upload" value="Upload" onclick="this.form.submit()" />    
      </td>
     </tr>
     <tr>
      <td colspan="2">$attachmaxtext</td>
     </tr>
    </table>
   <input type="hidden" name="subject" value="$env{'form.subject'}" />
   <input type="hidden" name="comment" value="$env{'form.comment'}" />
   <input type="hidden" name="timestamp" value="$env{'form.timestamp'}" />
   <input type="hidden" name="idx" value="$env{'form.idx'}" />
   <input type="hidden" name="numoldver" value="$env{'form.numoldver'}" />
   <input type="hidden" name="origpage" value="$env{'form.origpage'}" />
   <input type="hidden" name="blog" value="$env{'form.blog'}" />
   <input type="hidden" name="discuss" value="$env{'form.discuss'}" />
   END
       foreach my $item (@{$currnewattach}) {
           $r->print('<input type="hidden" name="currnewattach" value="'.$item.'" />'."\n");
       }
       foreach my $item (@{$currdelold}) {
           $r->print('<input type="hidden" name="deloldattach" value="'.$item.'" />'."\n");
       }
       $r->print(<<END);
    <input type="button" name="rtntoedit" value="$lt{'stch'}" onclick="setAction()" />
   </form>
   $end_page
   END
       return;
   }
   
     my %contrib=&Apache::lonnet::restore($symb,$ENV{'request.course.id'},  sub process_attachments {
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},      my ($currnewattach,$currdelold,$keepold) = @_;
      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});  
   
               @{$currnewattach}=
     my $currenthidden=$contrib{'hidden'};   &Apache::loncommon::get_env_multiple('form.currnewattach');
           @{$currdelold}=
     if ($ENV{'form.hide'}) {   &Apache::loncommon::get_env_multiple('form.deloldattach');
  $currenthidden.='.'.$idx.'.';      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 = (<<END);
   <br />
   <form name="attachment" action="/adm/feedback?attach=$ressymb" method="post">
   $lt{'clic'}:&nbsp;<input type="button" value="$att"
   onclick="if (typeof(document.mailform.onsubmit)=='function') {document.mailform.onsubmit();};this.form.comment.value=escape(document.mailform.comment.value);this.form.subject.value=escape(document.mailform.subject.value);
   END
       if (!$blockblog) {
           $response .= 'setblogvalue();';
       }
       unless ($mode eq 'board') {
           $response .= 'javascript:anonchk();';
       }
       $response .= (<<ENDATTACH);
   this.form.submit();" />
   <input type="hidden" name="origpage" value="$origpage" />
   <input type="hidden" name="idx" value="$idx" />
   <input type="hidden" name="timestamp" value="$now" />
   <input type="hidden" name="subject" />
   <input type="hidden" name="comment" />
   <input type="hidden" name="blog" value="0" />
   <input type="hidden" name="discuss" value="0" />
   <input type="hidden" name="numoldver" value="$numoldver" />
   ENDATTACH
       if (defined($deloldattach)) {
           if (@{$deloldattach} > 0) {
               foreach my $delatt (@{$deloldattach}) {
                   $response .= '<input type="hidden" name="deloldattach" value="'.$delatt.'" />'."\n";
               }
           }
       }
       if (defined($currnewattach)) {
           if (@{$currnewattach} > 0) {
               foreach my $attach (@{$currnewattach}) {
                   $response .= '<input type="hidden" name="currnewattach" value="'.$attach.'" />'."\n";
               }
           }
       }
       $response .= '</form>';
       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/^<attachment id="0">/) {
               $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
           } else {
               $attachurl = $$attachments{$id}{'filename'};
           }
           $attachurl=~m|/([^/]+)$|;
           $$message.='<br /><a href="'.$attachurl.'"><tt>'.
           $1.'</tt></a><br />';
           &Apache::lonnet::allowuploaded('/adm/feedback',
                                  $attachurl);
       } elsif (@attached > 1) {
           $$message.='<ol>';
           foreach my $attach (@attached) {
               my $id = $attach;
               my $attachurl = &HTML::Entities::decode($$attachments{$id}{'filename'});
               my ($fname)
                 =($attachurl=~m|/([^/]+)$|);
               $$message .= '<li><a href="'.$attachurl.
                 '"><tt>'.
                 $fname.'</tt></a></li>';
               &Apache::lonnet::allowuploaded('/adm/feedback',
                                $attachurl);
           }
           $$message .= '</ol>';
       }
   }
   
   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/^<attachment id="0">/) {
                   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 .= '<attachment id="'.$num.'"><filename>'.$attachments{$num}{'filename'}.'</filename>';
                       foreach my $item (sort {$a <=> $b} keys(%{$attachments{$num}})) {
                           unless ($item eq 'filename') {
                               $newattachmenturl .= '<post id="'.$item.'">'.$attachments{$num}{$item}.'</post>';
                           }
                       }
                       if (grep/^$num$/,@{$keepold}) {
                           $newattachmenturl .= '<post id="'.$currver.'">'.$attachments{$num}{$prevver}.'</post>';
                       }
                       $newattachmenturl .= '</attachment>';
                   }
               } else {
                   $newattachmenturl = '<attachment id="0"><filename>'.&HTML::Entities::encode($oldattachmenturl).'</filename><post id="0">n</post>';
                   unless (grep/^0$/,@{$keepold}) {
                       $newattachmenturl .= '<post id="1">n</post>';
                   }
                   $newattachmenturl .= '</attachment>';
                   $startnum ++;
               }
           }
       }
       for (my $i=0; $i<@{$currnewattach}; $i++) {
           my $attachnum = $startnum + $i;
           $newattachmenturl .= '<attachment id="'.$attachnum.'"><filename>'.&HTML::Entities::encode($$currnewattach[$i]).'</filename><post id="'.$currver.'">n</post></attachment>';
       }
       return $newattachmenturl; 
   }
   
   sub add_blog_checkbox {
       my ($crstype) = @_;
       my $checkstatus;
       if ($env{'form.blog'}) {
           $checkstatus = 'checked="checked"';
       }
       my $output = '
   <script type="text/javascript">
   function setblogvalue() {
       if (document.mailform.blog.checked) {
           document.attachment.blog.value = 1;
     } else {      } else {
         $currenthidden=~s/\.$idx\.//g;          document.attachment.blog.value = 0;
     }      }
     my %newhash=('hidden' => $currenthidden);  }
   </script><br />
   <label><input type="checkbox" name="blog" '.$checkstatus.' /> ';
       if ($crstype eq 'Community') {
           $output .= &mt('Add to my public community blog');
       } else {
           $output .= &mt('Add to my public course blog');
       } 
       $output .= '</label><br />'."\n";
       return $output;
   }
   
     &Apache::lonnet::store(\%newhash,$symb,$ENV{'request.course.id'},  sub has_discussion {
                      $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},      my $resourcesref = shift;
      $ENV{'course.'.$ENV{'request.course.id'}.'.num'});      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;
     }
   
     &redirect_back($r,'/res/'.$url,'Changed discussion status<p>','0');  # --------------------------- Get query string for limited number of parameters
   
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
            ['modal','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').'<br />',                     '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').'<br />',
        '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;
   
         # Breadcrumbs
         my $brcrum = [{'href' => '',
                        'text' => 'Discussion Post Versions'}];
               
   } else {        my %parms=();
 # ------------------------------------------------------------- Normal feedback        if ($env{'form.modal'} ne 'yes') { 'bread_crumbs' => $brcrum }
   my $feedurl=$ENV{'form.postdata'};  
   $feedurl=~s/^http\:\/\///;        $r->print(&Apache::loncommon::start_page('Discussion Post Versions',undef,\%parms));
   $feedurl=~s/^$ENV{'SERVER_NAME'}//;  
   $feedurl=~s/^$ENV{'HTTP_HOST'}//;        my $crs='/'.$env{'request.course.id'};
         if ($env{'request.course.sec'}) {
   my $symb=&Apache::lonnet::symbread($feedurl);            $crs.='_'.$env{'request.course.sec'};
   my $goahead=1;        }
   if ($feedurl=~/\.(problem|exam|quiz|assess|survey|form)$/) {        $crs=~s|_|/|g;
       unless ($symb) { $goahead=0; }        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').'<br />','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').'<br />','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 = "<b>$lt{'mnpa'} $numitems $lt{'robb'}</b>";
         if ($numitems > 0) {
             &Apache::lonnet::put('nohist_'.$env{'request.course.id'}.'_discuss',
        \%discinfo,$env{'user.domain'},$env{'user.name'});
         } else {
             $textline = "<b>$lt{'twnp'}</b>";
         }
         &Apache::loncommon::content_type($r,'text/html');
         $r->send_http_header;
         my $logo=&Apache::loncommon::lonhttpdurl('/adm/lonIcons/lonlogos.gif');
         my %onload;
   
         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 (<<ENDREDIR);
   $start_page
   <img align="right" src="$logo" />
   $textline
   <form name="reldt" action="$feedurl" target="loncapaclient">
   </form>
   <br />
   $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').'<br />',
        '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 ($goahead) {        if ($env{'form.hide'} && !$seeid && !(&editing_allowed($env{'form.hide'},$env{'form.group'}))) {
             &redirect_back($r,$feedurl,&mt('Deletion not permitted').'<br />',                 '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').'<br />',
        '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').'<br />',
        '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').'<br />',
        '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 = '<p><span class="LC_warning">'.&mt('Attachment not included - exceeded permitted length').'</span><br /></p>';
             }
         }
         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 =~ /$LONCAPA::assess_re/) {
             $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=~/$LONCAPA::assess_re/) {
     unless ($symb) { $goahead=0; }
         }
         if (!$goahead) {
             # Ambiguous Problem Resource
     $r->internal_redirect('/adm/ambiguous');
     return OK;
         }
 # Go ahead with feedback, no ambiguous reference  # Go ahead with feedback, no ambiguous reference
     $r->content_type('text/html');        unless (
     $r->send_http_header;    (
        ($feedurl=~m:^/res:) && ($feedurl!~m:^/res/adm:)
     if (     ) 
       (    || 
        ($feedurl=~m:^/res:) && ($feedurl!~m:^/res/adm:)    ($env{'request.course.id'} && ($feedurl!~m:^/adm:))
       )     ||
       ||     ($env{'request.course.id'} && ($symb=~/^bulletin\_\_\_/))
       ($ENV{'request.course.id'} && ($feedurl!~m:^/adm:))    ) {
      ) {    &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  # --------------------------------------------------- Print login screen header
     unless ($ENV{'form.sendit'}) {        unless ($env{'form.sendit'}) {
       my $options=&screen_header($feedurl);            &Apache::lonenc::check_encrypt(\$feedurl);
       if ($options) {    &Apache::loncommon::content_type($r,'text/html');
  &mail_screen($r,$feedurl,$options);    $r->send_http_header;
       } else {            if (($env{'form.replydisc'}) || ($env{'form.editdisc'})) {
  &fail_redirect($r,$feedurl);                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;
       }        }
     } else {  
               
 # Get previous user input  # Get previous user input
       my $prevattempts=&Apache::loncommon::get_previous_attempt(        my $prevattempts=&Apache::loncommon::get_previous_attempt(
             $symb,$ENV{'user.name'},$ENV{'user.domain'},                                     $symb,$env{'user.name'},$env{'user.domain'},
             $ENV{'request.course.id'});     $env{'request.course.id'});
   
 # Get output from resource  # Get output from resource
         &Apache::lonenc::check_encrypt(\$feedurl);
       my $usersaw=&resource_output($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 = '<p><span class="LC_warning">'.&mt('Attachment not included - exceeded permitted length').'</span><br /></p>';
             }
         }
 # Filter HTML out of message (could be nasty)  # Filter HTML out of message (could be nasty)
       my $message=&clear_out_html;        my $override;
         if ($env{'form.discuss'} =~ /^(?:author|question|course|policy)$/) {
             $override = 1;
         }
         my $message=&clear_out_html($env{'form.comment'},$override);
   
 # Assemble email  # Assemble email
       my ($email,$citations)=&assemble_email($feedurl,$message,$prevattempts,        my ($email,$citations)=&assemble_email($message,$prevattempts,
           $usersaw);       $usersaw,$useranswer);
    
 # Who gets this?  # Who gets this?
       my ($typestyle,%to) = &decide_receiver($feedurl);        my ($typestyle,%to) = &Apache::lonmsg::decide_receiver($feedurl);
   
 # Actually send mail  # Actually send mail
       my ($status,$numsent)=&send_msg($feedurl,$email,$citations,%to);        my ($status,$numsent)=&send_msg(&clear_out_html($env{'form.subject'}),
         $feedurl,$email,$citations,
         $attachmenturl,$usersymb,%to);
   
 # Discussion? Store that.  # Discussion? Store that.
         my $numpost=0;
       if ($ENV{'form.discuss'}) {        if (  ($env{'form.discuss'} ne ''
   $typestyle.=&adddiscuss($symb,$message);       && $env{'form.discuss'} !~ /^(?:author|question|course|policy)/)
      || $env{'form.anondiscuss'} ne '') {
     my $subject = &clear_out_html($env{'form.subject'});
     my $anonmode=($env{'form.discuss'} eq 'anon' || $env{'form.anondiscuss'} );
     $typestyle.=&adddiscuss($symb,$message,$anonmode,$attachmenturl,
     $subject,$group);
     $numpost++;
       }        }
   
       if ($ENV{'form.anondiscuss'}) {  # Add to blog?
   $typestyle.=&adddiscuss($symb,$message,1);  
         my $blog='';
         if ($env{'form.blog'}) {
     my $subject = &clear_out_html($env{'form.subject'});
     $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='<br />'.&mt('Added to my community blog').'<br />';
             } else {
         $blog='<br />'.&mt('Added to my course blog').'<br />';
             }
       }        }
     
   # 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.'<br /><br /><a href="javascript:history.go(-1)">'.
         &mt('Back to previous page').
         &Apache::loncommon::end_page();
   } 
   
 # Receipt screen and redirect back to where came from  sub wrap_symb {
       &redirect_back($r,$feedurl,$typestyle,$numsent,$status);      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|;
     }      }
    } else {      my $feedurl = &get_feedurl($symb);
 # Unable to give feedback      return ($symb,$feedurl);
     &no_redirect_back($r,$feedurl);  }
    }  
   } else {  sub editing_allowed {
 # Ambiguous Problem Resource      my ($postid,$group) = @_;
     $r->internal_redirect('/adm/ambiguous');      $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 = '&amp;group='.$group;
       if (exists($env{'form.ref'})) {
           $extra_args .= '&amp;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;
 }  }
   return OK;  
 }   
   
 1;  1;
 __END__  __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 tidy_html()
   
   =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

Removed from v.1.26  
changed lines
  Added in v.1.307


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.