Diff for /loncom/interface/londocs.pm between versions 1.303 and 1.325.4.2

version 1.303, 2008/01/20 05:46:34 version 1.325.4.2, 2009/09/24 22:15:27
Line 26 Line 26
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
   
   
   
 package Apache::londocs;  package Apache::londocs;
   
 use strict;  use strict;
Line 42  use HTML::Entities; Line 44  use HTML::Entities;
 use GDBM_File;  use GDBM_File;
 use Apache::lonlocal;  use Apache::lonlocal;
 use Cwd;  use Cwd;
   use Apache::londocsgci();
 use LONCAPA qw(:DEFAULT :match);  use LONCAPA qw(:DEFAULT :match);
   
 my $iconpath;  my $iconpath;
Line 53  my %alreadyseen=(); Line 56  my %alreadyseen=();
   
 my $hadchanges;  my $hadchanges;
   
 # Available help topics  
   
 my %help=();  my %help=();
   
 # Mapread read maps into LONCAPA::map:: global arrays   
 # @order and @resources, determines status  
 # sets @order - pointer to resources in right order  
 # sets @resources - array with the resources with correct idx  
 #  
   
 sub mapread {  sub mapread {
     my ($coursenum,$coursedom,$map)=@_;      my ($coursenum,$coursedom,$map)=@_;
Line 81  sub storemap { Line 79  sub storemap {
     return ($errtext,0);      return ($errtext,0);
 }  }
   
 # ----------------------------------------- Return hash with valid author names  
   
 sub authorhosts {  sub authorhosts {
     my %outhash=();      my %outhash=();
     my $home=0;      my $home=0;
     my $other=0;      my $other=0;
     foreach (keys %env) {      foreach my $key (keys(%env)) {
  if ($_=~/^user\.role\.(au|ca)\.(.+)$/) {   if ($key=~/^user\.role\.(au|ca)\.(.+)$/) {
     my $role=$1;      my $role=$1;
     my $realm=$2;      my $realm=$2;
     my ($start,$end)=split(/\./,$env{$_});      my ($start,$end)=split(/\./,$env{$key});
     if (($start) && ($start>time)) { next; }      if (($start) && ($start>time)) { next; }
     if (($end) && (time>$end)) { next; }      if (($end) && (time>$end)) { next; }
     my $ca; my $cd;      my ($ca,$cd);
     if ($1 eq 'au') {      if ($1 eq 'au') {
  $ca=$env{'user.name'};   $ca=$env{'user.name'};
  $cd=$env{'user.domain'};   $cd=$env{'user.domain'};
Line 116  sub authorhosts { Line 114  sub authorhosts {
     }      }
     return ($home,$other,%outhash);      return ($home,$other,%outhash);
 }  }
 # ------------------------------------------------------ Generate "dump" button  
   
 sub dumpbutton {  sub dumpbutton {
     my ($home,$other,%outhash)=&authorhosts();      my ($home,$other,%outhash)=&authorhosts();
Line 141  sub clean { Line 139  sub clean {
     $title=~s/[^\w\/\!\$\%\^\*\-\_\=\+\;\:\,\\\|\`\~]+/\_/gs;      $title=~s/[^\w\/\!\$\%\^\*\-\_\=\+\;\:\,\\\|\`\~]+/\_/gs;
     return $title;      return $title;
 }  }
 # -------------------------------------------------------- Actually dump course  
   
   
 sub dumpcourse {  sub dumpcourse {
     my ($r) = @_;      my ($r) = @_;
Line 161  sub dumpcourse { Line 160  sub dumpcourse {
  my $title=$env{'form.authorfolder'};   my $title=$env{'form.authorfolder'};
  $title=&clean($title);   $title=&clean($title);
  my %replacehash=();   my %replacehash=();
  foreach (keys %env) {   foreach my $key (keys(%env)) {
     if ($_=~/^form\.namefor\_(.+)/) {      if ($key=~/^form\.namefor\_(.+)/) {
  $replacehash{$1}=$env{$_};   $replacehash{$1}=$env{$key};
     }      }
  }   }
  my $crs='/uploaded/'.$env{'request.course.id'}.'/';   my $crs='/uploaded/'.$env{'request.course.id'}.'/';
  $crs=~s/\_/\//g;   $crs=~s/\_/\//g;
  foreach (keys %replacehash) {   foreach my $item (keys(%replacehash)) {
     my $newfilename=$title.'/'.$replacehash{$_};      my $newfilename=$title.'/'.$replacehash{$item};
     $newfilename=~s/\.(\w+)$//;      $newfilename=~s/\.(\w+)$//;
     my $ext=$1;      my $ext=$1;
     $newfilename=&clean($newfilename);      $newfilename=&clean($newfilename);
Line 184  sub dumpcourse { Line 183  sub dumpcourse {
     unless(mkdir($makepath,0777)) { $fail=1; }       unless(mkdir($makepath,0777)) { $fail=1; } 
  }   }
     }      }
     $r->print('<br /><tt>'.$_.'</tt> => <tt>'.$newfilename.'</tt>: ');      $r->print('<br /><tt>'.$item.'</tt> => <tt>'.$newfilename.'</tt>: ');
     if (my $fh=Apache::File->new('>'.$path.'/'.$newfilename)) {      if (my $fh=Apache::File->new('>'.$path.'/'.$newfilename)) {
  if ($_=~/\.(sequence|page|html|htm|xml|xhtml)$/) {   if ($item=~/\.(sequence|page|html|htm|xml|xhtml)$/) {
     print $fh &Apache::lonclonecourse::rewritefile(      print $fh &Apache::lonclonecourse::rewritefile(
          &Apache::lonclonecourse::readfile($env{'request.course.id'},$_),           &Apache::lonclonecourse::readfile($env{'request.course.id'},$item),
      (%replacehash,$crs => '')       (%replacehash,$crs => '')
     );      );
  } else {   } else {
     print $fh      print $fh
          &Apache::lonclonecourse::readfile($env{'request.course.id'},$_);           &Apache::lonclonecourse::readfile($env{'request.course.id'},$item);
        }         }
  $fh->close();   $fh->close();
     } else {      } else {
Line 211  sub dumpcourse { Line 210  sub dumpcourse {
     $r->print(      $r->print(
       '<h3>'.&mt('Select the Construction Space').'</h3><select name="authorspace">');        '<h3>'.&mt('Select the Construction Space').'</h3><select name="authorspace">');
  }   }
  foreach (sort keys %outhash) {   foreach my $key (sort(keys(%outhash))) {
     if ($_=~/^home_(.+)$/) {      if ($key=~/^home_(.+)$/) {
  if ($home==1) {   if ($home==1) {
     $r->print(      $r->print(
   '<input type="hidden" name="authorspace" value="'.$1.'" />');    '<input type="hidden" name="authorspace" value="'.$1.'" />');
Line 228  sub dumpcourse { Line 227  sub dumpcourse {
  my $title=$origcrsdata{'description'};   my $title=$origcrsdata{'description'};
  $title=~s/[\/\s]+/\_/gs;   $title=~s/[\/\s]+/\_/gs;
  $title=&clean($title);   $title=&clean($title);
  $r->print('<h3>'.&mt('Folder in Construction Space').'</h3><input type="text" size="50" name="authorfolder" value="'.$title.'" /><br />');   $r->print('<h3>'.&mt('Folder in Construction Space').'</h3>'
                    .'<input type="text" size="50" name="authorfolder" value="'.$title.'" /><br />');
  &tiehash();   &tiehash();
  $r->print('<h3>'.&mt('Filenames in Construction Space').'</h3><table border="2"><tr><th>'.&mt('Internal Filename').'</th><th>'.&mt('Title').'</th><th>'.&mt('Save as ...').'</th></tr>');   $r->print('<h3>'.&mt('Filenames in Construction Space').'</h3>'
  foreach (&Apache::lonclonecourse::crsdirlist($origcrsid,'userfiles')) {                   .&Apache::loncommon::start_data_table()
     $r->print('<tr><td>'.$_.'</td>');                   .&Apache::loncommon::start_data_table_header_row()
     my ($ext)=($_=~/\.(\w+)$/);                   .'<th>'.&mt('Internal Filename').'</th>'
                    .'<th>'.&mt('Title').'</th>'
                    .'<th>'.&mt('Save as ...').'</th>'
                    .&Apache::loncommon::end_data_table_header_row());
    foreach my $file (&Apache::lonclonecourse::crsdirlist($origcrsid,'userfiles')) {
       $r->print(&Apache::loncommon::start_data_table_row()
                        .'<td>'.$file.'</td>');
       my ($ext)=($file=~/\.(\w+)$/);
     my $title=$hash{'title_'.$hash{      my $title=$hash{'title_'.$hash{
  'ids_/uploaded/'.$origcrsdata{'domain'}.'/'.$origcrsdata{'num'}.'/'.$_}};   'ids_/uploaded/'.$origcrsdata{'domain'}.'/'.$origcrsdata{'num'}.'/'.$file}};
     $r->print('<td>'.($title?$title:'&nbsp;').'</td>');      $r->print('<td>'.($title?$title:'&nbsp;').'</td>');
     if (!$title) {      if (!$title) {
  $title=$_;   $title=$file;
     } else {      } else {
  $title=~s|/|_|g;   $title=~s|/|_|g;
     }      }
     $title=~s/\.(\w+)$//;      $title=~s/\.(\w+)$//;
     $title=&clean($title);      $title=&clean($title);
     $title.='.'.$ext;      $title.='.'.$ext;
     $r->print("\n<td><input type='text' size='60' name='namefor_".$_."' value='".$title."' /></td></tr>\n");      $r->print("\n<td><input type='text' size='60' name='namefor_".$file."' value='".$title."' /></td>"
                        .&Apache::loncommon::end_data_table_row());
  }   }
  $r->print("</table>\n");   $r->print(&Apache::loncommon::end_data_table());
  &untiehash();   &untiehash();
  $r->print(   $r->print(
   '<p><input type="submit" name="dumpcourse" value="'.&mt('Dump [_1] DOCS',$type).'" /></p></form>');    '<p><input type="submit" name="dumpcourse" value="'.&mt("Dump $type DOCS").'" /></p></form>');
     }      }
 }  }
   
 # ------------------------------------------------------ Generate "export" button  
   
 sub exportbutton {  sub exportbutton {
     my $type = &Apache::loncommon::course_type();      my $type = &Apache::loncommon::course_type();
Line 264  sub exportbutton { Line 272  sub exportbutton {
     &Apache::loncommon::help_open_topic('Docs_Export_Course_Docs').'</div>';      &Apache::loncommon::help_open_topic('Docs_Export_Course_Docs').'</div>';
 }  }
   
   
   
 sub exportcourse {  sub exportcourse {
     my $r=shift;      my $r=shift;
     my $type = &Apache::loncommon::course_type();      my $type = &Apache::loncommon::course_type();
     my %discussiontime = &Apache::lonnet::dump('discussiontimes',      my %discussiontime = &Apache::lonnet::dump('discussiontimes',
                                                $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'});                                                 $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'});
     my $numdisc = keys %discussiontime;      my $numdisc = keys(%discussiontime);
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
       if (!defined($navmap)) {
           $r->print(&Apache::loncommon::start_page('Export '.lc($type).' to IMS content package').
                     '<h2>IMS Export Failed</h2>'.
                     '<div class="LC_error">'.
                     &mt('Unable to retrieve information about course contents').
                     '</div><a href="/adm/coursedocs">'.&mt('Return to Course Editor').'</a>');
           &Apache::lonnet::logthis('IMS export failed - could not create navmap object in '.lc($type).':'.$env{'request.course.id'});
           return;
       }
     my $it=$navmap->getIterator(undef,undef,undef,1,undef,undef);      my $it=$navmap->getIterator(undef,undef,undef,1,undef,undef);
     my $curRes;      my $curRes;
     my $outcome;      my $outcome;
Line 309  sub exportcourse { Line 328  sub exportcourse {
                 chdir $cwd;                  chdir $cwd;
                 $outcome .= &mt('Download the zip file from <a href="[_1]">IMS '.lc($type).' archive</a><br />',$imszipfile,);                  $outcome .= &mt('Download the zip file from <a href="[_1]">IMS '.lc($type).' archive</a><br />',$imszipfile,);
                 if ($copyresult) {                  if ($copyresult) {
                     $outcome .= 'The following errors occurred during export - '.$copyresult;                      $outcome .= &mt('The following errors occurred during export - [_1]',$copyresult);
                 }                  }
             } else {              } else {
                 $outcome = '<br />Unfortunately you will not be able to retrieve an IMS archive of this posts at this time, because there was a problem creating a manifest file.<br />';                  $outcome = '<br />'.&mt('Unfortunately you will not be able to retrieve an IMS archive of this posts at this time, because there was a problem creating a manifest file.').'<br />';
             }              }
         }          }
         $r->print(&Apache::loncommon::start_page('Export '.lc($type).' to IMS content package'));          $r->print(&Apache::loncommon::start_page('Export '.lc($type).' to IMS content package'));
Line 383  sub exportcourse { Line 402  sub exportcourse {
                     $display .= '<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" /><img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" />'."\n";                      $display .= '<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" /><img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" />'."\n";
                 }                  }
                 if ($curRes->is_sequence()) {                  if ($curRes->is_sequence()) {
                     $display .= '<img src="/adm/lonIcons/navmap.folder.open.gif">&nbsp;'."\n";                      $display .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" />&nbsp;'."\n";
                     $lastcontainer = $count + $startcount + $boards;                      $lastcontainer = $count + $startcount + $boards;
                 } elsif ($curRes->is_page()) {                  } elsif ($curRes->is_page()) {
                     $display .= '<img src="/adm/lonIcons/navmap.page.open.gif">&nbsp;'."\n";                      $display .= '<img src="/adm/lonIcons/navmap.page.open.gif" alt="" />&nbsp;'."\n";
                     $lastcontainer = $count + $startcount + $boards;                      $lastcontainer = $count + $startcount + $boards;
                 }                  }
                 my $currelem = $count+$boards+$startcount;                  my $currelem = $count+$boards+$startcount;
Line 439  function containerCheck(item) { Line 458  function containerCheck(item) {
     }      }
         |;          |;
   
         foreach my $container (sort { $a <=> $b } keys %children) {          foreach my $container (sort { $a <=> $b } (keys(%children))) {
             my @contents = split/:/,$children{$container};              my @contents = split(/:/,$children{$container});
             for (my $i=0; $i<@contents; $i ++) {              for (my $i=0; $i<@contents; $i ++) {
                 $scripttag .= '    parents['.$container.']['.$i.'] = '.$contents[$i]."\n";                  $scripttag .= '    parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
             }              }
Line 460  function containerCheck(item) { Line 479  function containerCheck(item) {
  $scripttag));   $scripttag));
  $r->print(&Apache::lonhtmlcommon::breadcrumbs('Export '.lc($type).' to IMS content package'));   $r->print(&Apache::lonhtmlcommon::breadcrumbs('Export '.lc($type).' to IMS content package'));
  $r->print($display.'</table>'.   $r->print($display.'</table>'.
                   '<p><input type="hidden" name="finishexport" value="1">'.                    '<p><input type="hidden" name="finishexport" value="1" />'.
                   '<input type="submit" name="exportcourse" value="'.                    '<input type="submit" name="exportcourse" value="'.
                   &mt('Export '.$type.' DOCS').'" /></p></form>'.                    &mt('Export '.$type.' DOCS').'" /></p></form>');
   &Apache::loncommon::end_page());  
     }      }
 }  }
   
Line 548  sub build_package { Line 566  sub build_package {
             if ($curRes->is_sequence() || $curRes->is_page()) {              if ($curRes->is_sequence() || $curRes->is_page()) {
                 $lastcontainer = $count;                  $lastcontainer = $count;
             }              }
             if (grep/^$count$/,@$exportitems) {              if (grep(/^$count$/,@$exportitems)) {
                 &get_dependencies($exportitems,\%parent,$depth,\@dependencies);                  &get_dependencies($exportitems,\%parent,$depth,\@dependencies);
             }              }
         }          }
Line 572  sub build_package { Line 590  sub build_package {
   
         if (ref($curRes)) {          if (ref($curRes)) {
             $count ++;              $count ++;
             if ((grep/^$count$/,@$exportitems) || (grep/^$count$/,@dependencies)) {              if ((grep(/^$count$/,@$exportitems)) || (grep(/^$count$/,@dependencies))) {
                 my $symb = $curRes->symb();                  my $symb = $curRes->symb();
                 my $isvisible = 'true';                  my $isvisible = 'true';
                 my $resourceref;                  my $resourceref;
Line 606  sub build_package { Line 624  sub build_package {
                      '   <resource identifier="RES-'.$env{'request.course.id'}.'-'.$count.                       '   <resource identifier="RES-'.$env{'request.course.id'}.'-'.$count.
                      '" type="webcontent" href="'.$content_file.'">'."\n".                       '" type="webcontent" href="'.$content_file.'">'."\n".
                      '       <file href="'.$content_file.'" />'."\n";                       '       <file href="'.$content_file.'" />'."\n";
                         foreach (@hrefs) {                          foreach my $item (@hrefs) {
                             $imsresources .=                              $imsresources .=
                      '        <file href="'.$_.'" />'."\n";                       '        <file href="'.$item.'" />'."\n";
                         }                          }
                         if (grep/^$count$/,@$discussions) {                          if (grep(/^$count$/,@$discussions)) {
                             my $ressymb = $symb;                              my $ressymb = $symb;
                             my $mode;                              my $mode;
                             if ($ressymb =~ m|adm/($match_domain)/($match_username)/(\d+)/bulletinboard$|) {                              if ($ressymb =~ m|adm/($match_domain)/($match_username)/(\d+)/bulletinboard$|) {
Line 651  sub build_package { Line 669  sub build_package {
 sub get_dependencies {  sub get_dependencies {
     my ($exportitems,$parent,$depth,$dependencies) = @_;      my ($exportitems,$parent,$depth,$dependencies) = @_;
     if ($depth > 1) {      if ($depth > 1) {
         if ((!grep/^$$parent{$depth}$/,@$exportitems) && (!grep/^$$parent{$depth}$/,@$dependencies)) {          if ((!grep(/^$$parent{$depth}$/,@$exportitems)) && (!grep(/^$$parent{$depth}$/,@$dependencies))) {
             push @$dependencies, $$parent{$depth};              push(@{$dependencies},$$parent{$depth});
             if ($depth > 2) {              if ($depth > 2) {
                 &get_dependencies($exportitems,$parent,$depth-1,$dependencies);                  &get_dependencies($exportitems,$parent,$depth-1,$dependencies);
             }              }
Line 818  sub extract_media { Line 836  sub extract_media {
         $dirpath = $url;          $dirpath = $url;
         $container = '';          $container = '';
     }      }
     &Apache::lonnet::extract_embedded_items(undef,undef,\%allfiles,\%codebase,$content);      &Apache::lonnet::extract_embedded_items(undef,\%allfiles,\%codebase,$content);
     foreach my $embed_file (keys(%allfiles)) {      foreach my $embed_file (keys(%allfiles)) {
         my $filename;          my $filename;
         if ($embed_file =~ m#([^/]+)$#) {          if ($embed_file =~ m#([^/]+)$#) {
Line 858  sub extract_media { Line 876  sub extract_media {
             my $copiedfile;              my $copiedfile;
             if ($copiedfile = Apache::File->new('>'.$destination)) {              if ($copiedfile = Apache::File->new('>'.$destination)) {
                 print $copiedfile $embed_content;                  print $copiedfile $embed_content;
                 push @{$href}, 'resources/'.$count.'/res/'.$filename;                  push(@{$href},'resources/'.$count.'/res/'.$filename);
                 my $attrib_regexp = '';                  my $attrib_regexp = '';
                 if (@{$allfiles{$embed_file}} > 1) {                  if (@{$allfiles{$embed_file}} > 1) {
                     $attrib_regexp = join('|',@{$allfiles{$embed_file}});                      $attrib_regexp = join('|',@{$allfiles{$embed_file}});
Line 902  sub store_template { Line 920  sub store_template {
     }      }
 }  }
   
 # Imports the given (name, url) resources into the course  
 # coursenum, coursedom, and folder must precede the list  
 sub group_import {  sub group_import {
     my ($coursenum, $coursedom, $folder, $container, $caller, @files) = @_;      my ($coursenum, $coursedom, $folder, $container, $caller, @files) = @_;
   
Line 924  sub group_import { Line 941  sub group_import {
             my $result=&Apache::lonnet::finishuserfileupload($coursenum,$coursedom,              my $result=&Apache::lonnet::finishuserfileupload($coursenum,$coursedom,
                                                 'output',$1.$2);                                                  'output',$1.$2);
             if ($result != m|^/uploaded/|) {              if ($result != m|^/uploaded/|) {
                 $errtext.='Map not saved: A network error occured when trying to save the new map. ';                  $errtext.='Map not saved: A network error occurred when trying to save the new map. ';
                 $fatal = 2;                  $fatal = 2;
             }              }
             if ($fatal) {              if ($fatal) {
Line 949  sub group_import { Line 966  sub group_import {
 }  }
   
 sub breadcrumbs {  sub breadcrumbs {
     my ($where)=@_;      my ($where,$allowed,$type)=@_;
     &Apache::lonhtmlcommon::clear_breadcrumbs();      &Apache::lonhtmlcommon::clear_breadcrumbs();
     my (@folders);      my (@folders);
     if ($env{'form.pagepath'}) {      if ($env{'form.pagepath'}) {
Line 966  sub breadcrumbs { Line 983  sub breadcrumbs {
     my $is_random_order=0;      my $is_random_order=0;
     while (@folders) {      while (@folders) {
  my $folder=shift(@folders);   my $folder=shift(@folders);
  my $foldername=shift(@folders);      my $foldername=shift(@folders);
  if ($folderpath) {$folderpath.='&';}   if ($folderpath) {$folderpath.='&';}
  $folderpath.=$folder.'&'.$foldername;   $folderpath.=$folder.'&'.$foldername;
  my $url='/adm/coursedocs?folderpath='.   my $url='/adm/coursedocs?folderpath='.
Line 982  sub breadcrumbs { Line 999  sub breadcrumbs {
             if ($2) { $ishidden=1; }              if ($2) { $ishidden=1; }
             if ($3) { $isencrypted=1; }              if ($3) { $isencrypted=1; }
     if ($4 ne '') { $is_random_order = 1; }      if ($4 ne '') { $is_random_order = 1; }
               if ($folder eq 'supplemental') {
                   if ($allowed) {
                       $name = &mt('Supplemental '.$type.' Documents');
                   } else {
                       $name = &mt($type.' Documents');
                   }
               }
     &Apache::lonhtmlcommon::add_breadcrumb(      &Apache::lonhtmlcommon::add_breadcrumb(
       {'href'=>$url.$cpinfo,        {'href'=>$url.$cpinfo,
        'title'=>$name,         'title'=>$name,
Line 1031  sub log_docs { Line 1055  sub log_docs {
                        'currentfolder' => $env{'form.folder'});                         'currentfolder' => $env{'form.folder'});
         if ($parmidx) {          if ($parmidx) {
            $storehash{'parameter_res'}=$oldresources[$parmidx];             $storehash{'parameter_res'}=$oldresources[$parmidx];
            foreach my $parm (keys %parmaction) {             foreach my $parm (keys(%parmaction)) {
               $storehash{'parameter_action_'.$parm}=$parmaction{$parm};                $storehash{'parameter_action_'.$parm}=$parmaction{$parm};
               $storehash{'parameter_value_'.$parm}=$parmvalue{$parm};                $storehash{'parameter_value_'.$parm}=$parmvalue{$parm};
            }             }
Line 1058  sub log_docs { Line 1082  sub log_docs {
 }  }
   
   
 #  
 # Docs Change Log  
 #  
 sub docs_change_log {  sub docs_change_log {
     my ($r)=@_;      my ($r)=@_;
     my $folder=$env{'form.folder'};      my $folder=$env{'form.folder'};
Line 1201  sub update_paste_buffer { Line 1225  sub update_paste_buffer {
 # Mark for copying  # Mark for copying
     my ($title,$url)=split(':',$LONCAPA::map::resources[$LONCAPA::map::order[$env{'form.markcopy'}]]);      my ($title,$url)=split(':',$LONCAPA::map::resources[$LONCAPA::map::order[$env{'form.markcopy'}]]);
     if (&is_supplemental_title($title)) {      if (&is_supplemental_title($title)) {
           &Apache::lonnet::appenv({'docs.markedcopy_supplemental' => $title});
  ($title) = &parse_supplemental_title($title);   ($title) = &parse_supplemental_title($title);
       } elsif ($env{'docs.markedcopy_supplemental'}) {
           &Apache::lonnet::delenv('docs.markedcopy_supplemental');
     }      }
     $url=~s{http(&colon;|:)//https(&colon;|:)//}{https$2//};      $url=~s{http(&colon;|:)//https(&colon;|:)//}{https$2//};
   
     &Apache::lonnet::appenv('docs.markedcopy_title' => $title,      &Apache::lonnet::appenv({'docs.markedcopy_title' => $title,
     'docs.markedcopy_url'   => $url);      'docs.markedcopy_url'   => $url});
     delete($env{'form.markcopy'});      delete($env{'form.markcopy'});
 }  }
   
Line 1250  ENDPASTE Line 1277  ENDPASTE
 }  }
   
 sub do_paste_from_buffer {  sub do_paste_from_buffer {
     my ($coursenum,$coursedom) = @_;      my ($coursenum,$coursedom,$folder) = @_;
   
     return 0 if (!$env{'form.pastemarked'});      if (!$env{'form.pastemarked'}) {
           return;
       }
   
 # paste resource to end of list  # paste resource to end of list
     my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url'});      my $url=&LONCAPA::map::qtescape($env{'docs.markedcopy_url'});
Line 1260  sub do_paste_from_buffer { Line 1289  sub do_paste_from_buffer {
 # Maps need to be copied first  # Maps need to be copied first
     if (($url=~/\.(page|sequence)$/) && ($url=~/^\/uploaded\//)) {      if (($url=~/\.(page|sequence)$/) && ($url=~/^\/uploaded\//)) {
  $title=&mt('Copy of').' '.$title;   $title=&mt('Copy of').' '.$title;
  my $newid=$$.time;   my $newid=$$.int(rand(100)).time;
  $url=~/^(.+)\.(\w+)$/;   my ($oldid,$ext) = ($url=~/^(.+)\.(\w+)$/);
  my $newurl=$1.$newid.'.'.$2;          if ($oldid =~ m{^(/uploaded/\Q$coursedom\E/\Q$coursenum\E/)(\D+)(\d+)$}) {
               my $path = $1;
               my $prefix = $2;
               my $ancestor = $3;
               if (length($ancestor) > 10) {
                   $ancestor = substr($ancestor,-10,10);
               }
               $oldid = $path.$prefix.$ancestor;
           }
           my $counter = 0;
           my $newurl=$oldid.$newid.'.'.$ext;
           my $is_unique = &uniqueness_check($newurl);
           while (!$is_unique && $counter < 100) {
               $counter ++;
               $newid ++;
               $newurl = $oldid.$newid;
               $is_unique = &uniqueness_check($newurl);
           }
           if (!$is_unique) {
               if ($url=~/\.page$/) {
                   return &mt('Paste failed: an error occurred creating a unique URL for the composite page');
               } else {
                   return &mt('Paste failed: an error occurred creating a unique URL for the folder');
               }
           }
  my $storefn=$newurl;   my $storefn=$newurl;
  $storefn=~s{^/\w+/$match_domain/$match_username/}{};   $storefn=~s{^/\w+/$match_domain/$match_username/}{};
  &Apache::lonclonecourse::writefile($env{'request.course.id'},$storefn,   my $paste_map_result =
    &Apache::lonnet::getfile($url));              &Apache::lonclonecourse::writefile($env{'request.course.id'},$storefn,
          &Apache::lonnet::getfile($url));
           if ($paste_map_result eq '/adm/notfound.html') {
               if ($url=~/\.page$/) {
                   return &mt('Paste failed: an error occurred saving the composite page');
               } else {
                   return &mt('Paste failed: an error occurred saving the folder');
               }
           }
  $url = $newurl;   $url = $newurl;
     }      }
 # published maps can only exists once, so remove it from paste buffer when done  # published maps can only exists once, so remove it from paste buffer when done
     if (($url=~/\.(page|sequence)$/) && ($url=~m {^/res/})) {      if (($url=~/\.(page|sequence)$/) && ($url=~m {^/res/})) {
  &Apache::lonnet::delenv('docs\\.markedcopy');   &Apache::lonnet::delenv('docs.markedcopy');
     }      }
     if ($url=~ m{/smppg$}) {      if ($url=~ m{/smppg$}) {
  my $db_name = &Apache::lonsimplepage::get_db_name($url);   my $db_name = &Apache::lonsimplepage::get_db_name($url);
Line 1292  sub do_paste_from_buffer { Line 1353  sub do_paste_from_buffer {
     $url       = &LONCAPA::map::qtunescape($url);      $url       = &LONCAPA::map::qtunescape($url);
 # Now insert the URL at the bottom  # Now insert the URL at the bottom
     my $newidx = &LONCAPA::map::getresidx($url);      my $newidx = &LONCAPA::map::getresidx($url);
       if ($env{'docs.markedcopy_supplemental'}) {
           if ($folder =~ /^supplemental/) {
               $title = $env{'docs.markedcopy_supplemental'};
           } else {
               (undef,undef,$title) = 
                   &parse_supplemental_title($env{'docs.markedcopy_supplemental'});
           }
       } else {
           if ($folder=~/^supplemental/) {
              $title=time.'___&&&___'.$env{'user.name'}.'___&&&___'.
                     $env{'user.domain'}.'___&&&___'.$title;
           }
       }
   
     $LONCAPA::map::resources[$newidx]= $title.':'.$url.':'.$ext.':normal:res';      $LONCAPA::map::resources[$newidx]= $title.':'.$url.':'.$ext.':normal:res';
     push(@LONCAPA::map::order, $newidx);      push(@LONCAPA::map::order, $newidx);
       return 'ok';
 # Store the result  # Store the result
 }  }
   
   sub uniqueness_check {
       my ($newurl) = @_;
       my $unique = 1;
       foreach my $res (@LONCAPA::map::order) {
           my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$res]);
           $url=&LONCAPA::map::qtescape($url);
           if ($newurl eq $url) {
               $unique = 0;
               last;    
           }
       }
       return $unique;
   }
   
 my %parameter_type = ( 'randompick'     => 'int_pos',  my %parameter_type = ( 'randompick'     => 'int_pos',
        'hiddenresource' => 'string_yesno',         'hiddenresource' => 'string_yesno',
        'encrypturl'     => 'string_yesno',         'encrypturl'     => 'string_yesno',
Line 1370  sub handle_edit_cmd { Line 1460  sub handle_edit_cmd {
 }  }
   
 sub editor {  sub editor {
     my ($r,$coursenum,$coursedom,$folder,$allowed,$upload_output,$which)=@_;      my ($r,$coursenum,$coursedom,$folder,$allowed,$upload_output,$type)=@_;
   
     my $container= ($env{'form.pagepath'}) ? 'page'      my $container= ($env{'form.pagepath'}) ? 'page'
                            : 'sequence';                             : 'sequence';
Line 1387  sub editor { Line 1477  sub editor {
     }      }
           
     my ($breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain,$is_random_order)=      my ($breadcrumbtrail,$randompick,$ishidden,$isencrypted,$plain,$is_random_order)=
  &breadcrumbs($folder);   &breadcrumbs($folder,$allowed,$type);
     $r->print($breadcrumbtrail);      $r->print($breadcrumbtrail);
           
 # ------------------------------------------------------------ Process commands  # ------------------------------------------------------------ Process commands
Line 1412  sub editor { Line 1502  sub editor {
  }   }
           
  if ($env{'form.pastemarked'}) {   if ($env{'form.pastemarked'}) {
     &do_paste_from_buffer($coursenum,$coursedom);              my $paste_res = 
     ($errtext,$fatal) = &storemap($coursenum,$coursedom,$folder.'.'.$container);                  &do_paste_from_buffer($coursenum,$coursedom,$folder);
     return $errtext if ($fatal);              if ($paste_res eq 'ok') {
                   ($errtext,$fatal) = &storemap($coursenum,$coursedom,$folder.'.'.$container);
                   return $errtext if ($fatal);
               } elsif ($paste_res ne '') {
                   $r->print('<p><span class="LC_error">'.$paste_res.'</span></p>');
               }
  }   }
   
  $r->print($upload_output);   $r->print($upload_output);
Line 1426  sub editor { Line 1521  sub editor {
 # Group import/search  # Group import/search
  if ($env{'form.importdetail'}) {   if ($env{'form.importdetail'}) {
     my @imports;      my @imports;
     foreach (split(/\&/,$env{'form.importdetail'})) {      foreach my $item (split(/\&/,$env{'form.importdetail'})) {
  if (defined($_)) {   if (defined($item)) {
     my ($name,$url,$residx)=      my ($name,$url,$residx)=
  map {&unescape($_)} split(/\=/,$_);   map {&unescape($_)} split(/\=/,$item);
     push(@imports, [$name, $url, $residx]);      push(@imports, [$name, $url, $residx]);
  }   }
     }      }
Line 1489  sub editor { Line 1584  sub editor {
  $r->print('<tr><td>'.&mt('Currently no documents.').'</td></tr>');   $r->print('<tr><td>'.&mt('Currently no documents.').'</td></tr>');
     }      }
     $r->print("\n</table>\n");      $r->print("\n</table>\n");
           if ($allowed) {
     &print_paste_buffer($r,$container);          &print_paste_buffer($r,$container);
       }
     return;      return;
 }  }
   
Line 1571  sub process_file_upload { Line 1667  sub process_file_upload {
   
                     $$upload_output .=                       $$upload_output .= 
  'This file contains embedded multimedia objects, which need to be uploaded to LON-CAPA.<br />'.   'This file contains embedded multimedia objects, which need to be uploaded to LON-CAPA.<br />'.
  &ask_for_embedded_content('/adm/coursedocs',   &Apache::loncommon::ask_for_embedded_content(
   $state,$allfiles,$codebase);                              '/adm/coursedocs',$state,$allfiles,$codebase);
                 } else {                  } else {
                     $$upload_output .= 'No embedded items identified<br />';                      $$upload_output .= 'No embedded items identified<br />';
                 }                  }
Line 1582  sub process_file_upload { Line 1678  sub process_file_upload {
     return $phase_status;      return $phase_status;
 }  }
   
 sub ask_for_embedded_content {  
     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;  
     my $upload_output = '  
    <form name="upload_embedded" action="'.$actionurl.'"  
                   method="post" enctype="multipart/form-data">';  
     $upload_output .= $state;  
     $upload_output .= '<b>Upload embedded files</b>:<br />'.  
  &Apache::loncommon::start_data_table();  
   
     my $num = 0;  
     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%{$allfiles})) {  
  $upload_output .= &Apache::loncommon::start_data_table_row().  
     '<td>'.$embed_file.'</td><td>';  
  if ($args->{'ignore_remote_references'}  
     && $embed_file =~ m{^\w+://}) {  
     $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>';  
  } elsif ($args->{'error_on_invalid_names'}  
     && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {  
       
     $upload_output.='<span class="LC_warning">'.&mt("Invalid characters").'</span>';  
       
  } else {  
   
     $upload_output .='  
            <input name="embedded_item_'.$num.'" type="file" value="bob" />  
            <input name="embedded_orig_'.$num.'" type="hidden" value="'.&escape($embed_file).'" />';  
     my $attrib = join(':',@{$$allfiles{$embed_file}});  
     $upload_output .=  
  "\n\t\t".  
  '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.  
  $attrib.'" />';  
     if (exists($$codebase{$embed_file})) {  
  $upload_output .=   
     "\n\t\t".  
     '<input name="codebase_'.$num.'" type="hidden" value="'.  
     &escape($$codebase{$embed_file}).'" />';  
     }  
  }  
  $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row();  
  $num++;  
     }  
     $upload_output .= &Apache::loncommon::end_data_table().'<br />  
    <input type ="hidden" name="number_embedded_items" value="'.$num.'" />  
    <input type ="submit" value="'.&mt('Upload Listed Files').'" />  
    '.&mt('(only files for which a location has been provided will be uploaded)').'  
    </form>';  
     return $upload_output;  
 }  
   
 sub process_secondary_uploads {  sub process_secondary_uploads {
     my ($upload_output,$coursedom,$coursenum,$formname,$num,$newidx) = @_;      my ($upload_output,$coursedom,$coursenum,$formname,$num,$newidx) = @_;
     my $folder=$env{'form.folder'};      my $folder=$env{'form.folder'};
Line 1733  sub entryline { Line 1780  sub entryline {
  '<select name="newpos" onChange="this.form.submit()">';   '<select name="newpos" onChange="this.form.submit()">';
     for (my $i=1;$i<=$#LONCAPA::map::order+1;$i++) {      for (my $i=1;$i<=$#LONCAPA::map::order+1;$i++) {
  if ($i==$incindex) {   if ($i==$incindex) {
     $selectbox.='<option value="" selected="1">('.$i.')</option>';      $selectbox.='<option value="" selected="selected">('.$i.')</option>';
  } else {   } else {
     $selectbox.='<option value="'.$i.'">'.$i.'</option>';      $selectbox.='<option value="'.$i.'">'.$i.'</option>';
  }   }
Line 1754  sub entryline { Line 1801  sub entryline {
  # no copy for published maps   # no copy for published maps
  $nocopy = 1;   $nocopy = 1;
     } else {      } else {
  foreach (&Apache::lonsequence::attemptread(&Apache::lonnet::filelocation('',$url),1)) {   foreach my $item (&Apache::lonsequence::attemptread(&Apache::lonnet::filelocation('',$url),1)) {
     my ($title,$url,$ext,$type)=split(/\:/,$_);      my ($title,$url,$ext,$type)=split(/\:/,$item);
     if (($url=~/\.(page|sequence)/) && ($type ne 'zombie')) {      if (($url=~/\.(page|sequence)/) && ($type ne 'zombie')) {
  $nocopy=1;   $nocopy=1;
  last;   last;
Line 1857  END Line 1904  END
     }      }
           
     my $orig_url = $url;      my $orig_url = $url;
       $orig_url=~s{http(&colon;|:)//https(&colon;|:)//}{https$2//};
     my $external = ($url=~s{^http(|s)(&colon;|:)//}{/adm/wrapper/ext/});      my $external = ($url=~s{^http(|s)(&colon;|:)//}{/adm/wrapper/ext/});
     if ((!$isfolder) && ($residx) && ($folder!~/supplemental/) && (!$ispage)) {      if ((!$isfolder) && ($residx) && ($folder!~/supplemental/) && (!$ispage)) {
  my $symb=&Apache::lonnet::symbclean(   my $symb=&Apache::lonnet::symbclean(
Line 1919  END Line 1967  END
     my $ro_set=      my $ro_set=
     ((&LONCAPA::map::getparameter($orderidx,'parameter_randomorder'))[0]=~/^yes$/i?' checked="checked"':'');      ((&LONCAPA::map::getparameter($orderidx,'parameter_randomorder'))[0]=~/^yes$/i?' checked="checked"':'');
  $rand_order_text ='   $rand_order_text ='
 <nobr><label><input type="checkbox" name="randomorder_'.$orderidx.'" onClick="this.form.changeparms.value=\'randomorder\';this.form.submit()" '.$ro_set.' /> '.&mt('Random Order').' </label></nobr>';     <span class="LC_nobreak"><label><input type="checkbox" name="randomorder_'.$orderidx.'" onClick="this.form.changeparms.value=\'randomorder\';this.form.submit()" '.$ro_set.' /> '.&mt('Random Order').' </label></span>';   
     }      }
     if ($ispage) {      if ($ispage) {
         my $pagename=&escape($pagetitle);          my $pagename=&escape($pagetitle);
Line 1957  END Line 2005  END
        'hd' => 'Hidden',         'hd' => 'Hidden',
        'ec' => 'URL hidden');         'ec' => 'URL hidden');
  my $enctext=   my $enctext=
     ((&LONCAPA::map::getparameter($orderidx,'parameter_encrypturl'))[0]=~/^yes$/i?' checked="1"':'');      ((&LONCAPA::map::getparameter($orderidx,'parameter_encrypturl'))[0]=~/^yes$/i?' checked="checked"':'');
  my $hidtext=   my $hidtext=
     ((&LONCAPA::map::getparameter($orderidx,'parameter_hiddenresource'))[0]=~/^yes$/i?' checked="1"':'');      ((&LONCAPA::map::getparameter($orderidx,'parameter_hiddenresource'))[0]=~/^yes$/i?' checked="checked"':'');
  $line.=(<<ENDPARMS);   $line.=(<<ENDPARMS);
   <td class="LC_docs_entry_parameter">    <td class="LC_docs_entry_parameter">
     $form_start      $form_start
Line 1979  ENDPARMS Line 2027  ENDPARMS
     return $line;      return $line;
 }  }
   
 # ---------------------------------------------------------------- tie the hash  =pod
   
   =item tiehash()
   
   tie the hash
   
   =cut
   
 sub tiehash {  sub tiehash {
     my ($mode)=@_;      my ($mode)=@_;
Line 2005  sub untiehash { Line 2059  sub untiehash {
     return OK;      return OK;
 }  }
   
 # --------------------------------------------------------------- check on this  
   
   
 sub checkonthis {  sub checkonthis {
     my ($r,$url,$level,$title)=@_;      my ($r,$url,$level,$title)=@_;
Line 2014  sub checkonthis { Line 2069  sub checkonthis {
     $r->rflush();      $r->rflush();
     if (($url) && ($url!~/^\/uploaded\//) && ($url!~/\*$/)) {      if (($url) && ($url!~/^\/uploaded\//) && ($url!~/\*$/)) {
        $r->print("\n<br />");         $r->print("\n<br />");
          if ($level==0) {
              $r->print("<br />");
          }
        for (my $i=0;$i<=$level*5;$i++) {         for (my $i=0;$i<=$level*5;$i++) {
            $r->print('&nbsp;');             $r->print('&nbsp;');
        }         }
Line 2023  sub checkonthis { Line 2081  sub checkonthis {
   my $result=&Apache::lonnet::repcopy(    my $result=&Apache::lonnet::repcopy(
                               &Apache::lonnet::filelocation('',$url));                                &Apache::lonnet::filelocation('',$url));
           if ($result eq 'ok') {            if ($result eq 'ok') {
              $r->print('<font color="green">'.&mt('ok').'</font>');               $r->print('<span class="LC_success">'.&mt('ok').'</span>');
              $r->rflush();               $r->rflush();
              &Apache::lonnet::countacc($url);               &Apache::lonnet::countacc($url);
              $url=~/\.(\w+)$/;               $url=~/\.(\w+)$/;
Line 2033  sub checkonthis { Line 2091  sub checkonthis {
                  for (my $i=0;$i<=$level*5;$i++) {                   for (my $i=0;$i<=$level*5;$i++) {
                      $r->print('&nbsp;');                       $r->print('&nbsp;');
                  }                   }
                  $r->print('- '.&mt('Rendering').': ');                   $r->print('- '.&mt('Rendering:').' ');
  my ($errorcount,$warningcount)=split(/:/,   my ($errorcount,$warningcount)=split(/:/,
        &Apache::lonnet::ssi_body($url,         &Apache::lonnet::ssi_body($url,
        ('grade_target'=>'web',         ('grade_target'=>'web',
Line 2041  sub checkonthis { Line 2099  sub checkonthis {
                  if (($errorcount) ||                   if (($errorcount) ||
                      ($warningcount)) {                       ($warningcount)) {
      if ($errorcount) {       if ($errorcount) {
                         $r->print('<img src="/adm/lonMisc/bomb.gif" /><span class="LC_error">'.                          $r->print('<img src="/adm/lonMisc/bomb.gif" alt="" /><span class="LC_error">'.
   $errorcount.' '.                            &mt('[quant,_1,error]',$errorcount).'</span>');
   &mt('error(s)').'</span> ');  
                      }                       }
      if ($warningcount) {       if ($warningcount) {
                         $r->print('<span class="LC_warning">'.                          $r->print('<span class="LC_warning">'.
   $warningcount.' '.                            &mt('[quant,_1,warning]',$warningcount).'</span>');
   &mt('warning(s)').'</span>');  
                      }                       }
                  } else {                   } else {
                      $r->print('<span class="LC_success">'.&mt('ok').'</span>');                       $r->print('<span class="LC_success">'.&mt('ok').'</span>');
Line 2057  sub checkonthis { Line 2113  sub checkonthis {
              }               }
      my $dependencies=       my $dependencies=
                 &Apache::lonnet::metadata($url,'dependencies');                  &Apache::lonnet::metadata($url,'dependencies');
              foreach (split(/\,/,$dependencies)) {               foreach my $dep (split(/\,/,$dependencies)) {
  if (($_=~/^\/res\//) && (!$alreadyseen{$_})) {   if (($dep=~/^\/res\//) && (!$alreadyseen{$dep})) {
                     &checkonthis($r,$_,$level+1);                      &checkonthis($r,$dep,$level+1);
                  }                   }
              }               }
           } elsif ($result eq 'unavailable') {            } elsif ($result eq 'unavailable') {
              $r->print('<span class="LC_error">'.&mt('connection down').'</span>');               $r->print('<span class="LC_error">'.&mt('connection down').'</span>');
           } elsif ($result eq 'not_found') {            } elsif ($result eq 'not_found') {
       unless ($url=~/\$/) {        unless ($url=~/\$/) {
   $r->print('<span class="LC_error">'.&mt('not found').'</b></font>');    $r->print('<span class="LC_error">'.&mt('not found').'</b></span>');
       } else {        } else {
   $r->print('<span class="LC_unknown">'.&mt('unable to verify variable URL').'</span>');    $r->print('<span class="LC_error">'.&mt('unable to verify variable URL').'</span>');
       }        }
           } else {            } else {
              $r->print('<span class="LC_error">'.&mt('access denied').'</span>');               $r->print('<span class="LC_error">'.&mt('access denied').'</span>');
           }            }
       }         }
    }      }
 }  }
   
   
 #  
 # ----------------------------------------------------------------- List Symbs  =pod
 #   
   =item list_symbs()
   
   List Symbs
   
   =cut
   
 sub list_symbs {  sub list_symbs {
     my ($r) = @_;      my ($r) = @_;
   
       my $type = &Apache::loncommon::course_type();
     $r->print(&Apache::loncommon::start_page('Symb List'));      $r->print(&Apache::loncommon::start_page('Symb List'));
     $r->print(&Apache::lonhtmlcommon::breadcrumbs('Symb List'));      $r->print(&Apache::lonhtmlcommon::breadcrumbs('Symb List'));
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
     $r->print("<pre>\n");      if (!defined($navmap)) {
     foreach my $res ($navmap->retrieveResources()) {          $r->print('<h2>'.&mt('Retrieval of List Failed').'</h2>'.
  $r->print($res->compTitle()."\t".$res->symb()."\n");                    '<div class="LC_error">'.
                     &mt('Unable to retrieve information about course contents').
                     '</div>');
           &Apache::lonnet::logthis('Symb list failed - could not create navmap object in '.lc($type).':'.$env{'request.course.id'});
       } else {
           $r->print("<pre>\n");
           foreach my $res ($navmap->retrieveResources()) {
       $r->print($res->compTitle()."\t".$res->symb()."\n");
           }
           $r->print("\n</pre>\n");
     }      }
     $r->print("\n</pre>\n");  
     $r->print('<a href="/adm/coursedocs">'.&mt('Return to DOCS').'</a>');      $r->print('<a href="/adm/coursedocs">'.&mt('Return to DOCS').'</a>');
 }  }
   
   
 #  
 # -------------------------------------------------------------- Verify Content  
 #   
 sub verifycontent {  sub verifycontent {
     my ($r) = @_;      my ($r) = @_;
     my $type = &Apache::loncommon::course_type();      my $type = &Apache::loncommon::course_type();
Line 2110  sub verifycontent { Line 2178  sub verifycontent {
    undef %alreadyseen;     undef %alreadyseen;
    %alreadyseen=();     %alreadyseen=();
    &tiehash();     &tiehash();
    foreach (keys %hash) {     foreach my $key (keys(%hash)) {
        if ($hash{$_}=~/\.(page|sequence)$/) {         if ($hash{$key}=~/\.(page|sequence)$/) {
    if (($_=~/^src_/) && ($alreadyseen{&unescape($hash{$_})})) {     if (($key=~/^src_/) && ($alreadyseen{&unescape($hash{$key})})) {
        $r->print('<hr /><span class="LC_error">'.         $r->print('<hr /><span class="LC_error">'.
  &mt('The following sequence or page is included more than once in your '.$type.': ').   &mt('The following sequence or page is included more than once in your '.$type.': ').
  &unescape($hash{$_}).'</span><br />'.   &unescape($hash{$key}).'</span><br />'.
  &mt('Note that grading records for problems included in this sequence or folder will overlap.<hr />'));   &mt('Note that grading records for problems included in this sequence or folder will overlap.<hr />'));
    }     }
        }         }
        if (($_=~/^src\_(.+)$/) && (!$alreadyseen{&unescape($hash{$_})})) {         if (($key=~/^src\_(.+)$/) && (!$alreadyseen{&unescape($hash{$key})})) {
            &checkonthis($r,$hash{$_},0,$hash{'title_'.$1});             &checkonthis($r,$hash{$key},0,$hash{'title_'.$1});
        }         }
    }     }
    &untiehash();     &untiehash();
Line 2129  sub verifycontent { Line 2197  sub verifycontent {
 }  }
   
   
 # -------------------------------------------------------------- Check Versions  
   
 sub devalidateversioncache {  sub devalidateversioncache {
     my $src=shift;      my $src=shift;
     &Apache::lonnet::devalidate_cache_new('courseresversion',$env{'request.course.id'}.'_'.      &Apache::lonnet::devalidate_cache_new('courseresversion',$env{'request.course.id'}.'_'.
Line 2160  sub checkversions { Line 2226  sub checkversions {
     my %newsetversions=();      my %newsetversions=();
     if ($env{'form.setmostrecent'}) {      if ($env{'form.setmostrecent'}) {
  $haschanged=1;   $haschanged=1;
  foreach (keys %hash) {   foreach my $key (keys(%hash)) {
     if ($_=~/^ids\_(\/res\/.+)$/) {      if ($key=~/^ids\_(\/res\/.+)$/) {
  $newsetversions{$1}='mostrecent';   $newsetversions{$1}='mostrecent';
                 &devalidateversioncache($1);                  &devalidateversioncache($1);
     }      }
  }   }
     } elsif ($env{'form.setcurrent'}) {      } elsif ($env{'form.setcurrent'}) {
  $haschanged=1;   $haschanged=1;
  foreach (keys %hash) {   foreach my $key (keys(%hash)) {
     if ($_=~/^ids\_(\/res\/.+)$/) {      if ($key=~/^ids\_(\/res\/.+)$/) {
  my $getvers=&Apache::lonnet::getversion($1);   my $getvers=&Apache::lonnet::getversion($1);
  if ($getvers>0) {   if ($getvers>0) {
     $newsetversions{$1}=$getvers;      $newsetversions{$1}=$getvers;
Line 2179  sub checkversions { Line 2245  sub checkversions {
  }   }
     } elsif ($env{'form.setversions'}) {      } elsif ($env{'form.setversions'}) {
  $haschanged=1;   $haschanged=1;
  foreach (keys %env) {   foreach my $key (keys(%env)) {
     if ($_=~/^form\.set_version_(.+)$/) {      if ($key=~/^form\.set_version_(.+)$/) {
  my $src=$1;   my $src=$1;
  if (($env{$_}) && ($env{$_} ne $setversions{$src})) {   if (($env{$key}) && ($env{$key} ne $setversions{$src})) {
     $newsetversions{$src}=$env{$_};      $newsetversions{$src}=$env{$key};
     &devalidateversioncache($src);      &devalidateversioncache($src);
  }   }
     }      }
Line 2204  sub checkversions { Line 2270  sub checkversions {
 # show all documents  # show all documents
  $header=&mt('All Documents in '.$type);   $header=&mt('All Documents in '.$type);
  $allsel=1;   $allsel=1;
  foreach (keys %hash) {   foreach my $key (keys(%hash)) {
     if ($_=~/^ids\_(\/res\/.+)$/) {      if ($key=~/^ids\_(\/res\/.+)$/) {
  my $src=$1;   my $src=$1;
  $changes{$src}=1;   $changes{$src}=1;
     }      }
Line 2215  sub checkversions { Line 2281  sub checkversions {
  %changes=&Apache::lonnet::dump   %changes=&Apache::lonnet::dump
  ('versionupdate',$env{'course.'.$env{'request.course.id'}.'.domain'},   ('versionupdate',$env{'course.'.$env{'request.course.id'}.'.domain'},
                      $env{'course.'.$env{'request.course.id'}.'.num'});                       $env{'course.'.$env{'request.course.id'}.'.num'});
  my $firstkey=(keys %changes)[0];   my $firstkey=(keys(%changes))[0];
  unless ($firstkey=~/^error\:/) {   unless ($firstkey=~/^error\:/) {
     unless ($env{'form.timerange'}) {      unless ($env{'form.timerange'}) {
  $env{'form.timerange'}=604800;   $env{'form.timerange'}=604800;
Line 2279  sub checkversions { Line 2345  sub checkversions {
 <input type="submit" name="setversions" value="$lt{'sv'}" />  <input type="submit" name="setversions" value="$lt{'sv'}" />
 <table border="0">  <table border="0">
 ENDHEADERS  ENDHEADERS
     foreach (sort keys %changes) {      foreach my $key (sort(keys(%changes))) {
  if ($changes{$_}>$starttime) {   if ($changes{$key}>$starttime) {
     my ($root,$extension)=($_=~/^(.*)\.(\w+)$/);      my ($root,$extension)=($key=~/^(.*)\.(\w+)$/);
     my $currentversion=&Apache::lonnet::getversion($_);      my $currentversion=&Apache::lonnet::getversion($key);
     if ($currentversion<0) {      if ($currentversion<0) {
  $currentversion=&mt('Could not be determined.');   $currentversion=&mt('Could not be determined.');
     }      }
     my $linkurl=&Apache::lonnet::clutter($_);      my $linkurl=&Apache::lonnet::clutter($key);
     $r->print(      $r->print(
       '<tr><td colspan="5"><br /><br /><font size="+1"><b>'.        '<tr><td colspan="5"><br /><br /><font size="+1"><b>'.
       &Apache::lonnet::gettitle($linkurl).        &Apache::lonnet::gettitle($linkurl).
Line 2433  sub changewarning { Line 2499  sub changewarning {
 $help{'Caching'}.'</span></h3></form>'."\n\n");  $help{'Caching'}.'</span></h3></form>'."\n\n");
 }  }
   
 # =========================================== Breadcrumbs for special functions  
   
 sub init_breadcrumbs {  sub init_breadcrumbs {
     my ($form,$text)=@_;      my ($form,$text)=@_;
Line 2449  sub init_breadcrumbs { Line 2514  sub init_breadcrumbs {
     bug=>'Instructor Interface'});      bug=>'Instructor Interface'});
 }  }
   
 # ================================================================ Main Handler  
   
   
 sub handler {  sub handler {
     my $r = shift;      my $r = shift;
     &Apache::loncommon::content_type($r,'text/html');      &Apache::loncommon::content_type($r,'text/html');
Line 2457  sub handler { Line 2524  sub handler {
     return OK if $r->header_only;      return OK if $r->header_only;
     my $type = &Apache::loncommon::course_type();      my $type = &Apache::loncommon::course_type();
   
       my $coursedom=$env{'course.'.$env{'request.course.id'}.'.domain'};
       if ($coursedom eq 'gcitest') {
           my $allowed=&Apache::lonnet::allowed('mdc',$env{'request.course.id'});
           if ($allowed) {
               &concept_test_builder($r);
               return OK;
           }
       }
   
 # --------------------------------------------- Initialize help topics for this  # --------------------------------------------- Initialize help topics for this
     foreach ('Adding_Course_Doc','Main_Course_Documents',      foreach my $topic ('Adding_Course_Doc','Main_Course_Documents',
      'Adding_External_Resource','Navigate_Content',                 'Adding_External_Resource','Navigate_Content',
      'Adding_Folders','Docs_Overview', 'Load_Map',                 'Adding_Folders','Docs_Overview', 'Load_Map',
      'Supplemental','Score_Upload_Form','Adding_Pages',                 'Supplemental','Score_Upload_Form','Adding_Pages',
      'Importing_LON-CAPA_Resource','Uploading_From_Harddrive',                 'Importing_LON-CAPA_Resource','Uploading_From_Harddrive',
      'Check_Resource_Versions','Verify_Content') {                 'Check_Resource_Versions','Verify_Content') {
  $help{$_}=&Apache::loncommon::help_open_topic('Docs_'.$_);   $help{$topic}=&Apache::loncommon::help_open_topic('Docs_'.$topic);
     }      }
     # Composite help files      # Composite help files
     $help{'Syllabus'} = &Apache::loncommon::help_open_topic(      $help{'Syllabus'} = &Apache::loncommon::help_open_topic(
Line 2513  sub handler { Line 2589  sub handler {
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
     ['folderpath','pagepath',      ['folderpath','pagepath',
      'pagesymb']);       'pagesymb']);
   # No folderpath, no pagepath, see if we have something stored
       if ((!$env{'form.folderpath'}) && (!$env{'form.pagepath'})) {
           &Apache::loncommon::restore_course_settings('docs_folderpath',
                                                 {'folderpath' => 'scalar'});
       }
       if (!$env{'form.folderpath'}) {
           &Apache::loncommon::restore_course_settings('docs_folderpath',
                                                 {'pagepath' => 'scalar'});
       }
       if ($env{'form.pagepath'}) {
          $env{'form.folderpath'}='';
       }
       if ($env{'form.folderpath'} =~ /^supplemental_\d+/) {
           $env{'form.folderpath'} = 'supplemental&'.
                                     &escape(&mt('Supplemental '.$type.' Documents')).'&'.
                                     $env{'form.folderpath'};
       }
       &Apache::loncommon::store_course_settings('docs_folderpath',
                                                   {'pagepath' => 'scalar',
                                                    'folderpath' => 'scalar'});
     if ($env{'form.folderpath'}) {      if ($env{'form.folderpath'}) {
  my (@folderpath)=split('&',$env{'form.folderpath'});   my (@folderpath)=split('&',$env{'form.folderpath'});
  $env{'form.foldername'}=&unescape(pop(@folderpath));   $env{'form.foldername'}=&unescape(pop(@folderpath));
Line 2563  sub handler { Line 2659  sub handler {
     }      }
 # -------------------------------------------------------------------- Body tag  # -------------------------------------------------------------------- Body tag
     $script = '<script type="text/javascript">'."\n".$script."\n".'</script>';      $script = '<script type="text/javascript">'."\n".$script."\n".'</script>';
       my $brcrum = [{href=>"/adm/createuser",text=>"$type Documents"}];
     $r->print(&Apache::loncommon::start_page("$type Documents", $script,      $r->print(&Apache::loncommon::start_page("$type Documents", $script,
      {'force_register' => $showdoc,}).      {'force_register' => $showdoc,
                                        'bread_crumbs' => $brcrum}).
       &Apache::loncommon::help_open_menu('','',273,'RAT'));        &Apache::loncommon::help_open_menu('','',273,'RAT'));
       
   my %allfiles = ();    my %allfiles = ();
Line 2603  sub handler { Line 2701  sub handler {
                   }                    }
                   my @attributes = ();                    my @attributes = ();
                   if ($env{'form.embedded_attrib_'.$i} =~ /:/) {                    if ($env{'form.embedded_attrib_'.$i} =~ /:/) {
                       @attributes = split/:/,$env{'form.embedded_attrib_'.$i};                        @attributes = split(/:/,$env{'form.embedded_attrib_'.$i});
                   } else {                    } else {
                       @attributes = ($env{'form.embedded_attrib_'.$i});                        @attributes = ($env{'form.embedded_attrib_'.$i});
                   }                    }
                   foreach (@attributes) {                    foreach my $attr (@attributes) {
                       push(@{$attribs{$i}},&unescape($_));                        push(@{$attribs{$i}},&unescape($attr));
                   }                    }
                   if ($javacodebase) {                    if ($javacodebase) {
                       $codebase{$i} = $javacodebase;                        $codebase{$i} = $javacodebase;
Line 2626  sub handler { Line 2724  sub handler {
               my $updateflag = 0;                my $updateflag = 0;
               my $getstatus = &Apache::lonnet::getuploaded('GET',$primary_url,$coursedom,$coursenum,\$content,\$rtncode);                my $getstatus = &Apache::lonnet::getuploaded('GET',$primary_url,$coursedom,$coursenum,\$content,\$rtncode);
               if ($getstatus eq 'ok') {                if ($getstatus eq 'ok') {
                   foreach my $item (keys %newname) {                    foreach my $item (keys(%newname)) {
                       if ($newname{$item} ne $origname{$item}) {                        if ($newname{$item} ne $origname{$item}) {
                           my $attrib_regexp = '';                            my $attrib_regexp = '';
                           if (@{$attribs{$item}} > 1) {                            if (@{$attribs{$item}} > 1) {
Line 2675  sub handler { Line 2773  sub handler {
                 'extr' => 'External Resource',                  'extr' => 'External Resource',
                 'syll' => 'Syllabus',                  'syll' => 'Syllabus',
                 'navc' => 'Navigate Contents',                  'navc' => 'Navigate Contents',
                 'sipa' => 'Simple Page',                  'sipa' => 'Simple Course Page',
                 'sipr' => 'Simple Problem',                  'sipr' => 'Simple Problem',
                 'drbx' => 'Drop Box',                  'drbx' => 'Drop Box',
                 'scuf' => 'Score Upload Form',                  'scuf' => 'Score Upload Form',
                 'bull' => 'Bulletin Board',                  'bull' => 'Discussion Board',
                 'mypi' => 'My Personal Info',                  'mypi' => 'My Personal Information Page',
                 'grpo' => 'Group Files',                  'grpo' => 'Group Portfolios',
                 'rost' => 'Course Roster',                  'rost' => 'Course Roster',
  'abou' => 'About User',   'abou' => 'About User',
                 'imsf' => 'Import IMS package',                  'imsf' => 'Import IMS package',
Line 2774  ENDCOURSEVERIFY Line 2872  ENDCOURSEVERIFY
        }         }
        $hadchanges=0;         $hadchanges=0;
        my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,         my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,
    $upload_output);     $upload_output,$type);
        if ($error) {         if ($error) {
    $r->print('<p><span class="LC_error">'.$error.'</span></p>');     $r->print('<p><span class="LC_error">'.$error.'</span></p>');
        }         }
        if ($hadchanges) {         if ($hadchanges) {
    &mark_hash_old()     &mark_hash_old();
        }         }
        &changewarning($r,$postexec);         &changewarning($r,$postexec);
        my $folderseq='/uploaded/'.$coursedom.'/'.$coursenum.'/default_'.time.         my $folderseq='/uploaded/'.$coursedom.'/'.$coursenum.'/default_'.time.
Line 2811  $uploadtag Line 2909  $uploadtag
 <br />  <br />
 <span class="LC_nobreak">  <span class="LC_nobreak">
 <label>$lt{'parse'}?  <label>$lt{'parse'}?
 <input type="checkbox" name="parserflag" />  <input type="checkbox" name="parserflag" checked="checked" />
 </label>  </label>
 </span>  </span>
 <br />  <br />
Line 2891  value="$lt{'newp'}" />$help{'Adding_Page Line 2989  value="$lt{'newp'}" />$help{'Adding_Page
 <br /><form action="/adm/coursedocs" method="post" name="newsyl">  <br /><form action="/adm/coursedocs" method="post" name="newsyl">
 $uploadtag  $uploadtag
 <input type="hidden" name="importdetail"   <input type="hidden" name="importdetail" 
 value="Syllabus=/public/$coursedom/$coursenum/syllabus" />  value="$lt{'syll'}=/public/$coursedom/$coursenum/syllabus" />
 <span class="LC_nobreak">  <span class="LC_nobreak">
 <input name="newsyl" type="submit" value="$lt{'syll'}" />   <input name="newsyl" type="submit" value="$lt{'syll'}" /> 
  $help{'Syllabus'}   $help{'Syllabus'}
Line 2900  value="Syllabus=/public/$coursedom/$cour Line 2998  value="Syllabus=/public/$coursedom/$cour
 <br /><form action="/adm/coursedocs" method="post" name="newnav">  <br /><form action="/adm/coursedocs" method="post" name="newnav">
 $uploadtag  $uploadtag
 <input type="hidden" name="importdetail"   <input type="hidden" name="importdetail" 
 value="Navigate Content=/adm/navmaps" />  value="$lt{'navc'}=/adm/navmaps" />
 <span class="LC_nobreak">  <span class="LC_nobreak">
 <input name="newnav" type="submit" value="$lt{'navc'}" />  <input name="newnav" type="submit" value="$lt{'navc'}" />
 $help{'Navigate_Content'}  $help{'Navigate_Content'}
Line 2968  onClick="javascript:makeabout();" /> Line 3066  onClick="javascript:makeabout();" />
 <br /><form action="/adm/coursedocs" method="post" name="newgroupfiles">  <br /><form action="/adm/coursedocs" method="post" name="newgroupfiles">
 $uploadtag  $uploadtag
 <input type="hidden" name="importdetail"  <input type="hidden" name="importdetail"
 value="Group Files=/adm/$coursedom/$coursenum/aboutme" />  value="$lt{'grpo'}=/adm/$coursedom/$coursenum/aboutme" />
 <span class="LC_nobreak">  <span class="LC_nobreak">
 <input name="newgroupfiles" type="submit" value="$lt{'grpo'}" />  <input name="newgroupfiles" type="submit" value="$lt{'grpo'}" />
 $help{'Group Files'}  $help{'Group Files'}
Line 2977  $help{'Group Files'} Line 3075  $help{'Group Files'}
 <br /><form action="/adm/coursedocs" method="post" name="newroster">  <br /><form action="/adm/coursedocs" method="post" name="newroster">
 $uploadtag  $uploadtag
 <input type="hidden" name="importdetail"   <input type="hidden" name="importdetail" 
 value="Course Roster=/adm/viewclasslist" />  value="$lt{'rost'}=/adm/viewclasslist" />
 <span class="LC_nobreak">  <span class="LC_nobreak">
 <input name="newroster" type="submit" value="$lt{'rost'}" />  <input name="newroster" type="submit" value="$lt{'rost'}" />
 $help{'Course Roster'}  $help{'Course Roster'}
Line 3020  ENDBLOCK Line 3118  ENDBLOCK
    $folder='supplemental';     $folder='supplemental';
        }         }
        if ($folder =~ /^supplemental$/ &&         if ($folder =~ /^supplemental$/ &&
    $env{'form.folderpath'} =~ /^default\&/) {     (($env{'form.folderpath'} =~ /^default\&/) || ($env{'form.folderpath'} eq ''))) {
    $env{'form.folderpath'}='supplemental&'.            $env{'form.folderpath'} = 'supplemental&'.
        &escape(&mt('Supplemental '.$type.' Documents'));                                      &escape(&mt('Supplemental '.$type.' Documents'));
        }         }
        my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed);         my $error = &editor($r,$coursenum,$coursedom,$folder,$allowed,'',$type);
        if ($error) {         if ($error) {
    $r->print('<p><span class="LC_error">'.$error.'</span></p>');     $r->print('<p><span class="LC_error">'.$error.'</span></p>');
        }         }
Line 3051  ENDBLOCK Line 3149  ENDBLOCK
 </span>  </span>
 <br /><br />  <br /><br />
 $lt{'comment'}:<br />  $lt{'comment'}:<br />
 <textarea cols=50 rows=4 name='comment'>  <textarea cols="50" rows="4" name="comment">
 </textarea>  </textarea>
 <br />  <br />
 <input type="hidden" name="folderpath" value="$path" />  <input type="hidden" name="folderpath" value="$path" />
Line 3126  ENDSUPFORM Line 3224  ENDSUPFORM
  }   }
  $r->print(&Apache::loncommon::end_page());   $r->print(&Apache::loncommon::end_page());
  return OK;   return OK;
 }   }
   
   sub concept_test_builder {
       my ($r) = @_;
       $r->print(&Apache::loncommon::start_page('Assemble Test'));
       &Apache::londocsgci::setdefaults();
       if ($env{'form.phase'} eq 'storemap') {
             &Apache::londocsgci::evaluate();
             &Apache::londocsgci::store($r);
       }
       &Apache::londocsgci::load();
       &Apache::londocsgci::listresources($r);
       $r->print(&Apache::loncommon::end_page());
       return;
   }
   
 sub editing_js {  sub editing_js {
     my ($udom,$uname) = @_;      my ($udom,$uname) = @_;
Line 3137  sub editing_js { Line 3248  sub editing_js {
                                           t_mnf => 'New Folder',                                            t_mnf => 'New Folder',
                                           p_mnp => 'Name of New Page',                                            p_mnp => 'Name of New Page',
                                           t_mnp => 'New Page',                                            t_mnp => 'New Page',
                                           p_mxu => 'Listed Title for the Uploaded Score',                                            p_mxu => 'Title for the Uploaded Score',
                                           p_msp => 'Listed Title for the Page',                                            p_msp => 'Name of the Simple Course Page',
                                           p_msb => 'Listed Title for the Problem',                                            p_msb => 'Title for the Problem',
                                           p_mdb => 'Listed Title for the Drop Box',                                            p_mdb => 'Title for the Drop Box',
                                           p_mbb => 'Listed Title for the Bulletin Board',                                            p_mbb => 'Title for the Discussion Board',
                                           p_mab => "Enter user:domain for User's 'About Me' Page",                                            p_mab => "Enter user:domain for User's 'About Me' Page",
                                           p_mab2 => "About [_99]",                                            p_mab2 => "About [_99]",
                                           p_mab_alrt1 => 'Not a valid user:domain',                                            p_mab_alrt1 => 'Not a valid user:domain',
Line 3334  ENDNEWSCRIPT Line 3445  ENDNEWSCRIPT
 }  }
 1;  1;
 __END__  __END__
   
   
   =head1 NAME
   
   Apache::londocs.pm
   
   =head1 SYNOPSIS
   
   This is part of the LearningOnline Network with CAPA project
   described at http://www.lon-capa.org.
   
   =head1 SUBROUTINES
   
   =over
   
   =item %help=()
   
   Available help topics
   
   =item mapread()
   
   Mapread read maps into LONCAPA::map:: global arrays 
   @order and @resources, determines status
   sets @order - pointer to resources in right order
   sets @resources - array with the resources with correct idx
   
   =item authorhosts()
   
   Return hash with valid author names
   
   =item dumpbutton()
   
   Generate "dump" button
   
   =item clean()
   
   =item dumpcourse()
   
       Actually dump course
   
   
   =item exportbutton()
   
       Generate "export" button
   
   =item exportcourse()
   
   =item create_ims_store()
   
   =item build_package()
   
   =item get_dependencies()
   
   =item process_content()
   
   =item replicate_content()
   
   =item extract_media()
   
   =item store_template()
   
   =item group_import()
   
       Imports the given (name, url) resources into the course
       coursenum, coursedom, and folder must precede the list
   
   =item breadcrumbs()
   
   =item log_docs()
   
   =item docs_change_log()
   
   =item update_paste_buffer()
   
   =item print_paste_buffer()
   
   =item do_paste_from_buffer()
   
   =item update_parameter()
   
   =item handle_edit_cmd()
   
   =item editor()
   
   =item process_file_upload()
   
   =item process_secondary_uploads()
   
   =item is_supplemental_title()
   
   =item parse_supplemental_title()
   
   =item entryline()
   
   =item tiehash()
   
   =item untiehash()
   
   =item checkonthis()
   
   check on this
   
   =item verifycontent()
   
   Verify Content
   
   =item devalidateversioncache() & checkversions()
   
   Check Versions
   
   =item mark_hash_old()
   
   =item is_hash_old()
   
   =item changewarning()
   
   =item init_breadcrumbs()
   
   Breadcrumbs for special functions
   
   =back
   
   =cut

Removed from v.1.303  
changed lines
  Added in v.1.325.4.2


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>