--- loncom/interface/londocs.pm	2017/05/19 20:00:45	1.484.2.72
+++ loncom/interface/londocs.pm	2018/09/06 02:52:33	1.484.2.78
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Documents
 #
-# $Id: londocs.pm,v 1.484.2.72 2017/05/19 20:00:45 raeburn Exp $
+# $Id: londocs.pm,v 1.484.2.78 2018/09/06 02:52:33 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -1203,9 +1203,7 @@ sub print_paste_buffer {
                     if (($srcdom ne $coursedom) || ($srcnum ne $coursenum)) {
                         $othercourse = 1;
                         if ($env{"user.priv.cm./$srcdom/$srcnum"} =~ /\Q:mdc&F\E/) {
-                            if ($canpaste) {
-                                $othercrs = '<br />'.&mt('(from another course)');
-                            }
+                            $othercrs = '<br />'.&mt('(from another course)');
                         } else {
                             $canpaste = 0;
                             $nopaste = &mt('Paste from another course unavailable.'); 
@@ -1219,10 +1217,20 @@ sub print_paste_buffer {
                         }
                         $is_uploaded_map = 1;
                     }
+                } elsif ($url =~ m{^/adm/($match_domain)/($match_username)/\d+/(bulletinboard|smppg)$}) {
+                    if ($cid ne $env{'request.course.id'}) {
+                        my ($srcdom,$srcnum) = split(/_/,$cid);
+                        if ($env{"user.priv.cm./$srcdom/$srcnum"} =~ /\Q:mdc&F\E/) {
+                            $othercrs = '<br />'.&mt('(from another course)');
+                        } else {
+                            $canpaste = 0;
+                            $nopaste = &mt('Paste from another course unavailable.');
+                        }
+                    }
+                }
+                if ($canpaste) {
+                    push(@pasteable,$suffix);
                 }
-            }
-            if ($canpaste) {
-               push(@pasteable,$suffix);
             }
             my $buffer;
             if ($is_external) {
@@ -1512,6 +1520,7 @@ sub do_paste_from_buffer {
 
     foreach my $suffix (@topaste) {
         my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url_'.$suffix});
+        my $cid=&LONCAPA::map::qtescape($env{'docs.markedcopy_crs_'.$suffix});
 # Supplemental content may only include certain types of content
 # Early out if pasted content is not supported in Supplemental area
         if ($folder =~ /^supplemental/) {
@@ -1533,8 +1542,19 @@ sub do_paste_from_buffer {
             }
             $srcdom{$suffix} = $srcd;
             $srcnum{$suffix} = $srcn;
+        } elsif ($url =~ m{^/adm/$match_domain/$match_username/\d+/(bulletinboard|smppg)$}) {
+            my ($srcd,$srcn) = split(/_/,$cid);
+# When paste buffer was populated using an active role in a different course
+# check for mdc privilege in the course from which the resource was pasted
+            if (($srcd ne $coursedom) || ($srcn ne $coursenum)) {
+                unless ($env{"user.priv.cm./$srcd/$srcn"} =~ /\Q:mdc&F\E/) {
+                    $notincrs{$suffix} = 1;
+                    next;
+                }
+            }
+            $srcdom{$suffix} = $srcd;
+            $srcnum{$suffix} = $srcn;
         }
-
         push(@dopaste,$suffix);
         if ($url=~/\.(page|sequence)$/) {
             $is_map{$suffix} = 1; 
@@ -1632,6 +1652,7 @@ sub do_paste_from_buffer {
         }
         my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url_'.$suffix});
         my $title=&LONCAPA::map::qtescape($env{'docs.markedcopy_title_'.$suffix});
+        my $cid=&LONCAPA::map::qtescape($env{'docs.markedcopy_crs_'.$suffix});
         my $oldurl = $url;
         if ($is_map{$suffix}) {
 # If pasting a map, check if map contains other maps
@@ -1692,13 +1713,21 @@ sub do_paste_from_buffer {
         }
         if ($url=~ m{/(bulletinboard|smppg)$}) {
             my $prefix = $1;
+            my $fromothercrs;
             #need to copy the db contents to a new one, unless this is a move.
             my %info = (
                          src  => $url,
                          cdom => $coursedom,
                          cnum => $coursenum,
-            );
-            unless ($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') {
+                       );
+            if (($srcdom{$suffix} =~ /^$match_domain$/) && ($srcnum{$suffix} =~ /^$match_courseid$/)) {
+                unless (($srcdom{$suffix} eq $coursedom) && ($srcnum{$suffix} eq $coursenum)) {
+                    $fromothercrs = 1;
+                    $info{'cdom'} = $srcdom{$suffix};
+                    $info{'cnum'} = $srcnum{$suffix};
+                }
+            }
+            unless (($env{'form.docs.markedcopy_options_'.$suffix} eq 'move') && (!$fromothercrs)) {
                 my (%lockerr,$msg); 
                 my ($newurl,$result,$errtext) =
                     &dbcopy(\%info,$coursedom,$coursenum,\%lockerr);
@@ -2434,7 +2463,8 @@ sub apply_fixups {
                     }
                 }
             }
-            for (my $i=0; $i<@LONCAPA::map::order; $i++) {
+            my $total = scalar(@LONCAPA::map::order) - 1;
+            for (my $i=$total; $i>=0; $i--) {
                 my $idx = $LONCAPA::map::order[$i];
                 if (defined($LONCAPA::map::resources[$idx])) {
                     my $changed;
@@ -2444,7 +2474,7 @@ sub apply_fixups {
                         splice(@LONCAPA::map::order,$i,1);
                         if (ref($currparam{$idx}) eq 'ARRAY') {
                             foreach my $name (@{$currparam{$idx}}) {
-                                &LONCAPA::map::delparameter($idx,'parameter_'.$name);
+                                &LONCAPA::map::delparameter($idx,$name);
                             }
                         }
                         next;
@@ -2486,7 +2516,7 @@ sub apply_fixups {
             foreach my $idx (keys(%remparam)) {
                 if (ref($remparam{$idx}) eq 'ARRAY') {
                     foreach my $name (@{$remparam{$idx}}) {   
-                        &LONCAPA::map::delparameter($idx,'parameter_'.$name);
+                        &LONCAPA::map::delparameter($idx,$name);
                     }
                 }
             }
@@ -3746,7 +3776,7 @@ END
                     }
                 }
                 if ($url ne '') {
-                    $url.=(($url=~/\?/)?'&':'?').'symb='.&HTML::Entities::encode($shownsymb,'"<>&');
+                    $url.=(($url=~/\?/)?'&':'?').'symb='.&escape($shownsymb);
                 }
             } elsif (!$env{'request.role.adv'}) {
                 my $checkencrypt;
@@ -4183,7 +4213,7 @@ sub untiehash {
 
 
 sub checkonthis {
-    my ($r,$url,$level,$title)=@_;
+    my ($r,$url,$level,$title,$checkstale)=@_;
     $url=&unescape($url);
     $alreadyseen{$url}=1;
     $r->rflush();
@@ -4198,10 +4228,22 @@ sub checkonthis {
        $r->print('<a href="'.$url.'" target="cat">'.
 		 ($title?$title:$url).'</a> ');
        if ($url=~/^\/res\//) {
+          my $updated;
+          if (($checkstale) && ($url !~ m{^/res/lib/templates/}) &&
+              ($url !~ /\.\d+\.\w+$/)) {
+              $updated = &Apache::lonnet::remove_stale_resfile($url);
+          }
 	  my $result=&Apache::lonnet::repcopy(
                               &Apache::lonnet::filelocation('',$url));
           if ($result eq 'ok') {
              $r->print('<span class="LC_success">'.&mt('ok').'</span>');
+             if ($updated) {
+                 $r->print('<br />');
+                 for (my $i=0;$i<=$level*5;$i++) {
+                     $r->print('&nbsp;');
+                 }
+                 $r->print('- '.&mt('Outdated copy removed'));
+             }
              $r->rflush();
              &Apache::lonnet::countacc($url);
              $url=~/\.(\w+)$/;
@@ -4235,7 +4277,7 @@ sub checkonthis {
                 &Apache::lonnet::metadata($url,'dependencies');
              foreach my $dep (split(/\,/,$dependencies)) {
 		 if (($dep=~/^\/res\//) && (!$alreadyseen{$dep})) {
-                    &checkonthis($r,$dep,$level+1);
+                    &checkonthis($r,$dep,$level+1,'',$checkstale);
                  }
              }
           } elsif ($result eq 'unavailable') {
@@ -4249,6 +4291,9 @@ sub checkonthis {
           } else {
              $r->print('<span class="LC_error">'.&mt('access denied').'</span>');
           }
+          if (($updated) && ($result ne 'ok')) {
+              $r->print('<br />'.&mt('Outdated copy removed'));
+          }
        }
     }
 }
@@ -4301,9 +4346,29 @@ sub list_symbs {
     $r->print(&endContentScreen());
 }
 
+sub contentverifyform {
+    my ($r) = @_;
+    my $crstype = &Apache::loncommon::course_type();
+    $r->print(&Apache::loncommon::start_page('Verify '.$crstype.' Content'));
+    $r->print(&Apache::lonhtmlcommon::breadcrumbs('Verify '.$crstype.' Content'));
+    $r->print(&startContentScreen('tools'));
+    $r->print('<h4 class="LC_info">'.&mt($crstype.' content verification').'</h4>');
+    $r->print('<form method="post" action="/adm/coursedocs"><p>'.
+              &mt('Include a check if files copied from elsewhere are up to date (will increase verification time)?').
+              '&nbsp;<span class="LC_nobreak">'.
+              '<label><input type="radio" name="checkstale" value="0" checked="checked" />'.
+              &mt('No').'</label>'.('&nbsp;'x2).
+              '<label><input type="radio" name="checkstale" value="1" />'.
+              &mt('Yes').'</label></span></p><p>'.
+              '<input type="submit" value="'.&mt('Verify content').' "/>'.
+              '<input type="hidden" value="1" name="tools" />'.
+              '<input type="hidden" value="1" name="verify" /></p></form>');
+    $r->print(&endContentScreen());
+    return;
+}
 
 sub verifycontent {
-    my ($r) = @_;
+    my ($r,$checkstale) = @_;
     my $crstype = &Apache::loncommon::course_type();
     $r->print(&Apache::loncommon::start_page('Verify '.$crstype.' Content'));
     $r->print(&Apache::lonhtmlcommon::breadcrumbs('Verify '.$crstype.' Content'));
@@ -4324,7 +4389,7 @@ sub verifycontent {
 	   }
        }
        if (($key=~/^src\_(.+)$/) && (!$alreadyseen{&unescape($hash{$key})})) {
-           &checkonthis($r,$hash{$key},0,$hash{'title_'.$1});
+           &checkonthis($r,$hash{$key},0,$hash{'title_'.$1},$checkstale);
        }
    }
    &untiehash();
@@ -4791,9 +4856,25 @@ sub handler {
         $disabled = ' disabled="disabled"';
     }
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['inhibitmenu']);
+    if ($env{'form.inhibitmenu'}) {
+        unless ($env{'form.inhibitmenu'} eq 'yes') {
+            delete($env{'form.inhibitmenu'});
+        }
+    }
+
   if ($allowed && $env{'form.verify'}) {
       &init_breadcrumbs('verify','Verify Content','Docs_Verify_Content');
-      &verifycontent($r);
+      if (!$canedit) {
+          &verifycontent($r);
+      } elsif (($env{'form.checkstale'} ne '') && ($env{'form.checkstale'} =~ /^\d$/)) {
+          &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/coursedocs?tools=1&verify=1&checkstale=$env{'form.checkstale'}",
+                                                  text=>'Results',
+                                                  faq=>273,
+                                                  bug=>'Instructor Interface'});
+          &verifycontent($r,$env{'form.checkstale'});
+      } else {
+          &contentverifyform($r);
+      }
   } elsif ($allowed && $env{'form.listsymbs'}) {
       &init_breadcrumbs('listsymbs','List Content IDs');
       &list_symbs($r);
@@ -4824,6 +4905,26 @@ sub handler {
                                              'forcesupplement','forcestandard',
                                              'tools','symb','command','supppath']);
 
+    foreach my $item ('forcesupplement','forcestandard','tools') {
+        next if ($env{'form.'.$item} eq '');
+        unless ($env{'form.'.$item} eq '1') {
+            delete($env{'form.'.$item});
+        }
+    }
+
+    if ($env{'form.command'}) {
+        unless ($env{'form.command'} =~ /^(direct|directnav|editdocs|editsupp|contents|home)$/) {
+            delete($env{'form.command'});
+        }
+    }
+
+    if ($env{'form.symb'}) {
+        my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($env{'form.symb'});
+        unless (($id =~ /^\d+$/) && (&Apache::lonnet::is_on_map($resurl))) {
+            delete($env{'form.symb'});
+        }
+    }
+
 # standard=1: this is a "new-style" course with an uploaded map as top level
 # standard=2: this is a "old-style" course, and there is nothing we can do
 
@@ -4846,6 +4947,38 @@ sub handler {
     my $toolsflag=0;
     if ($env{'form.tools'}) { $toolsflag=1; }
 
+    if ($env{'form.folderpath'} ne '') {
+        my @items = split(/\&/,$env{'form.folderpath'});
+        my $badpath;
+        for (my $i=0; $i<@items; $i++) {
+            my $odd = $i%2;
+            if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) {
+                $badpath = 1;
+            } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) {
+                $badpath = 1;
+            }
+            last if ($badpath);
+        }
+        if ($badpath) {
+            delete($env{'form.folderpath'});
+        }
+    }
+
+    if ($env{'form.supppath'} ne '') {
+        my @items = split(/\&/,$env{'form.supppath'});
+        my $badpath;
+        for (my $i=0; $i<@items; $i++) {
+            my $odd = $i%2;
+            if ((!$odd) && ($items[$i] !~ /^supplemental(|_\d+)$/)) {
+                $badpath = 1;
+            }
+            last if ($badpath);
+        }
+        if ($badpath) {
+            delete($env{'form.supppath'});
+        }
+    }
+
     my $script='';
     my $showdoc=0;
     my $addentries = {};
@@ -5112,6 +5245,7 @@ sub handler {
 	  undef($hadchanges);
           $uploadphase = &process_file_upload(\$upload_output,$coursenum,$coursedom,
                                               \%allfiles,\%codebase,$context,$crstype);
+          undef($navmap);
 	  if ($hadchanges) {
 	      &mark_hash_old();
 	  }
@@ -5633,7 +5767,9 @@ unless ($container eq 'page') {
              $r->print('<p><span class="LC_error">'.$error.'</span></p>');
           }
           if ($hadchanges) {
-             &mark_hash_old();
+             unless (&is_hash_old()) {
+                 &mark_hash_old();
+             }
           }
 
           &changewarning($r,'');
@@ -5817,6 +5953,7 @@ my %suporderhash = (
 sub embedded_form_elems {
     my ($phase,$primaryurl,$newidx) = @_;
     my $folderpath = &HTML::Entities::encode($env{'form.folderpath'},'<>&"');
+    $newidx =~s /\D+//g;
     return <<STATE;
     <input type="hidden" name="folderpath" value="$folderpath" />
     <input type="hidden" name="cmd" value="upload_embedded" />
@@ -5837,7 +5974,11 @@ sub embedded_destination {
     } elsif ($folder =~ /^(default|supplemental)_(\d+)$/) {
         $destination .=  $2.'/';
     }
-    $destination .= $env{'form.newidx'};
+    my $newidx = $env{'form.newidx'};
+    $newidx =~s /\D+//g;
+    if ($newidx) {
+        $destination .= $newidx;
+    }
     my $dir_root = '/userfiles';
     return ($destination,$dir_root);
 }
@@ -5863,6 +6004,9 @@ sub decompression_info {
     }
     unshift(@hiddens,$pathitem);
     foreach my $item (@hiddens) {
+        if ($item eq 'newidx') {
+            next if ($env{'form.'.$item} =~ /\D/);
+        }
         if ($env{'form.'.$item}) {
             $hiddenelem .= '<input type="hidden" name="'.$item.'" value="'.
                            &HTML::Entities::encode($env{'form.'.$item},'<>&"').'" />'."\n";
@@ -7424,7 +7568,9 @@ check on this
 
 Verify Content
 
-=item devalidateversioncache() & checkversions()
+=item devalidateversioncache()
+
+=item checkversions()
 
 Check Versions