Diff for /loncom/publisher/lonpublisher.pm between versions 1.84 and 1.261

version 1.84, 2002/07/17 18:23:45 version 1.261, 2009/07/25 06:55:31
Line 25 Line 25
 #  #
 # http://www.lon-capa.org/  # http://www.lon-capa.org/
 #  #
 #   
 # (TeX Content Handler  
 #  
 # 05/29/00,05/30,10/11 Gerd Kortemeyer)  
 #  
 # 11/28,11/29,11/30,12/01,12/02,12/04,12/23 Gerd Kortemeyer  
 # 03/23 Guy Albertelli  
 # 03/24,03/29,04/03 Gerd Kortemeyer  
 # 04/16/2001 Scott Harrison  
 # 05/03,05/05,05/07 Gerd Kortemeyer  
 # 05/28/2001 Scott Harrison  
 # 06/23,08/07,08/11,8/13,8/17,8/18,8/24,9/26,10/16 Gerd Kortemeyer  
 # 12/04,12/05 Guy Albertelli  
 # 12/05 Gerd Kortemeyer  
 # 12/05 Guy Albertelli  
 # 12/06,12/07 Gerd Kortemeyer  
 # 12/15,12/16 Scott Harrison  
 # 12/25 Gerd Kortemeyer  
 # YEAR=2002  
 # 1/16,1/17 Scott Harrison  
 # 1/17 Gerd Kortemeyer  
 #  
 ###  ###
   
 ###############################################################################  ###############################################################################
Line 61 Line 39
 ##                                                                           ##  ##                                                                           ##
 ###############################################################################  ###############################################################################
   
   
   ######################################################################
   ######################################################################
   
   =pod 
   
   =head1 NAME
   
   lonpublisher - LON-CAPA publishing handler
   
   =head1 SYNOPSIS
   
   B<lonpublisher> is used by B<mod_perl> inside B<Apache>.  This is the
   invocation by F<loncapa_apache.conf>:
   
     <Location /adm/publish>
     PerlAccessHandler       Apache::lonacc
     SetHandler perl-script
     PerlHandler Apache::lonpublisher
     ErrorDocument     403 /adm/login
     ErrorDocument     404 /adm/notfound.html
     ErrorDocument     406 /adm/unauthorized.html
     ErrorDocument     500 /adm/errorhandler
     </Location>
   
   =head1 OVERVIEW
   
   Authors can only write-access the C</~authorname/> space. They can
   copy resources into the resource area through the publication step,
   and move them back through a recover step. Authors do not have direct
   write-access to their resource space.
   
   During the publication step, several events will be
   triggered. Metadata is gathered, where a wizard manages default
   entries on a hierarchical per-directory base: The wizard imports the
   metadata (including access privileges and royalty information) from
   the most recent published resource in the current directory, and if
   that is not available, from the next directory above, etc. The Network
   keeps all previous versions of a resource and makes them available by
   an explicit version number, which is inserted between the file name
   and extension, for example C<foo.2.html>, while the most recent
   version does not carry a version number (C<foo.html>). Servers
   subscribing to a changed resource are notified that a new version is
   available.
   
   =head1 DESCRIPTION
   
   B<lonpublisher> takes the proper steps to add resources to the LON-CAPA
   digital library.  This includes updating the metadata table in the
   LON-CAPA database.
   
   B<lonpublisher> is many things to many people.  
   
   This module publishes a file.  This involves gathering metadata,
   versioning the file, copying file from construction space to
   publication space, and copying metadata from construction space
   to publication space.
   
   =head2 SUBROUTINES
   
   Many of the undocumented subroutines implement various magical
   parsing shortcuts.
   
   =over 4
   
   =cut
   
   ######################################################################
   ######################################################################
   
   
 package Apache::lonpublisher;  package Apache::lonpublisher;
   
 # ------------------------------------------------- modules used by this module  # ------------------------------------------------- modules used by this module
Line 69  use Apache::File; Line 118  use Apache::File;
 use File::Copy;  use File::Copy;
 use Apache::Constants qw(:common :http :methods);  use Apache::Constants qw(:common :http :methods);
 use HTML::LCParser;  use HTML::LCParser;
   use HTML::Entities;
   use Encode::Encoder;
 use Apache::lonxml;  use Apache::lonxml;
 use Apache::lonhomework;  
 use Apache::loncacc;  use Apache::loncacc;
 use DBI;  use DBI;
 use Apache::lonnet();  use Apache::lonnet;
 use Apache::loncommon();  use Apache::loncommon();
   use Apache::lonhtmlcommon;
   use Apache::lonmysql;
   use Apache::lonlocal;
   use Apache::loncfile;
   use LONCAPA::lonmetadata;
   use Apache::lonmsg;
   use vars qw(%metadatafields %metadatakeys);
   use LONCAPA qw(:DEFAULT :match);
    
   
 my %addid;  my %addid;
 my %nokey;  my %nokey;
   
 my %metadatafields;  
 my %metadatakeys;  
   
 my $docroot;  my $docroot;
   
 my $cuname;  my $cuname;
 my $cudom;  my $cudom;
   
 # ----------------------------------------------- Evaluate string with metadata  my $registered_cleanup;
   my $modified_urls;
   
   my $lock;
   
   =pod
   
   =item B<metaeval>
   
   Evaluates a string that contains metadata.  This subroutine
   stores values inside I<%metadatafields> and I<%metadatakeys>.
   The hash key is a I<$unikey> corresponding to a unique id
   that is descriptive of the parser location inside the XML tree.
   
   Parameters:
   
   =over 4
   
   =item I<$metastring>
   
   A string that contains metadata.
   
   =back
   
   Returns:
   
   nothing
   
   =cut
   
   #########################################
   #########################################
   #
   # Modifies global %metadatafields %metadatakeys 
   #
   
 sub metaeval {  sub metaeval {
     my $metastring=shift;      my ($metastring,$prefix)=@_;
         
         my $parser=HTML::LCParser->new(\$metastring);      my $parser=HTML::LCParser->new(\$metastring);
         my $token;      my $token;
         while ($token=$parser->get_token) {      while ($token=$parser->get_token) {
            if ($token->[0] eq 'S') {   if ($token->[0] eq 'S') {
       my $entry=$token->[1];      my $entry=$token->[1];
               my $unikey=$entry;      my $unikey=$entry;
               if (defined($token->[2]->{'package'})) {       next if ($entry =~ m/^(?:parameter|stores)_/);
                   $unikey.='_package_'.$token->[2]->{'package'};      if (defined($token->[2]->{'package'})) { 
               }    $unikey.="\0package\0".$token->[2]->{'package'};
               if (defined($token->[2]->{'part'})) {       } 
                  $unikey.='_'.$token->[2]->{'part'};       if (defined($token->[2]->{'part'})) { 
       }   $unikey.="\0".$token->[2]->{'part'}; 
               if (defined($token->[2]->{'id'})) {       }
                   $unikey.='_'.$token->[2]->{'id'};      if (defined($token->[2]->{'id'})) { 
               }    $unikey.="\0".$token->[2]->{'id'};
               if (defined($token->[2]->{'name'})) {       } 
                  $unikey.='_'.$token->[2]->{'name'};       if (defined($token->[2]->{'name'})) { 
       }   $unikey.="\0".$token->[2]->{'name'}; 
               foreach (@{$token->[3]}) {      }
   $metadatafields{$unikey.'.'.$_}=$token->[2]->{$_};      foreach (@{$token->[3]}) {
                   if ($metadatakeys{$unikey}) {   $metadatafields{$unikey.'.'.$_}=$token->[2]->{$_};
       $metadatakeys{$unikey}.=','.$_;   if ($metadatakeys{$unikey}) {
                   } else {      $metadatakeys{$unikey}.=','.$_;
                       $metadatakeys{$unikey}=$_;   } else {
                   }      $metadatakeys{$unikey}=$_;
               }   }
               if ($metadatafields{$unikey}) {      }
   my $newentry=$parser->get_text('/'.$entry);      my $newentry=$parser->get_text('/'.$entry);
                   unless (($metadatafields{$unikey}=~/$newentry/) ||      if (($entry eq 'customdistributionfile') ||
                           ($newentry eq '')) {   ($entry eq 'sourcerights')) {
                      $metadatafields{$unikey}.=', '.$newentry;   $newentry=~s/^\s*//;
   }   if ($newentry !~m|^/res|) { $newentry=$prefix.$newentry; }
       } else {      }
                  $metadatafields{$unikey}=$parser->get_text('/'.$entry);  # actually store
               }      if ( $entry eq 'rule' && exists($metadatafields{$unikey})) {
           }   $metadatafields{$unikey}.=','.$newentry;
        }      } else {
    $metadatafields{$unikey}=$newentry;
       }
    }
       }
 }  }
   
 # -------------------------------------------------------- Read a metadata file  #########################################
   #########################################
   
   =pod
   
   =item B<metaread>
   
   Read a metadata file
   
   Parameters:
   
   =over
   
   =item I<$logfile>
   
   File output stream to output errors and warnings to.
   
   =item I<$fn>
   
   File name (including path).
   
   =back
   
   Returns:
   
   =over 4
   
   =item Scalar string (if successful)
   
   XHTML text that indicates successful reading of the metadata.
   
   =back
   
   =cut
   
   #########################################
   #########################################
 sub metaread {  sub metaread {
     my ($logfile,$fn)=@_;      my ($logfile,$fn,$prefix)=@_;
     unless (-e $fn) {      unless (-e $fn) {
  print $logfile 'No file '.$fn."\n";   print($logfile 'No file '.$fn."\n");
         return '<br><b>No file:</b> <tt>'.$fn.'</tt>';          return '<div><b>'
                 .&mt('No file: [_1]'
                     ,'</b> <tt>'.&Apache::loncfile::display($fn).'</tt></div>');
     }      }
     print $logfile 'Processing '.$fn."\n";      print($logfile 'Processing '.$fn."\n");
     my $metastring;      my $metastring;
     {      {
      my $metafh=Apache::File->new($fn);   my $metafh=Apache::File->new($fn);
      $metastring=join('',<$metafh>);   $metastring=join('',<$metafh>);
     }      }
     &metaeval($metastring);      &metaeval($metastring,$prefix);
     return '<br><b>Processed file:</b> <tt>'.$fn.'</tt>';      return '<div><b>'
             .&mt('Processed file: [_1]'
                 ,'</b> <tt>'.&Apache::loncfile::display($fn).'</tt></div>');
 }  }
   
 # ---------------------------- convert 'time' format into a datetime sql format  #########################################
 sub sqltime {  #########################################
     my $timef=shift @_;  
     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =  sub coursedependencies {
  localtime($timef);      my $url=&Apache::lonnet::declutter(shift);
     $mon++; $year+=1900;      $url=~s/\.meta$//;
     return "$year-$mon-$mday $hour:$min:$sec";      my ($adomain,$aauthor)=($url=~ m{^($match_domain)/($match_username)/});
       my $regexp=quotemeta($url);
       $regexp='___'.$regexp.'___course';
       my %evaldata=&Apache::lonnet::dump('nohist_resevaldata',$adomain,
          $aauthor,$regexp);
       my %courses=();
       foreach (keys %evaldata) {
    if ($_=~/^([a-zA-Z0-9]+_[a-zA-Z0-9]+)___.+___course$/) {
       $courses{$1}=1;
           }
       }
       return %courses;
 }  }
   #########################################
   #########################################
   
   
   =pod
   
 # --------------------------------------------------------- Various form fields  =item Form-field-generating subroutines.
   
   For input parameters, these subroutines take in values
   such as I<$name>, I<$value> and other form field metadata.
   The output (scalar string that is returned) is an XHTML
   string which presents the form field (foreseeably inside
   <form></form> tags).
   
   =over 4
   
   =item B<textfield>
   
   =item B<hiddenfield>
   
   =item B<selectbox>
   
   =back
   
   =cut
   
   #########################################
   #########################################
 sub textfield {  sub textfield {
     my ($title,$name,$value)=@_;      my ($title,$name,$value,$noline)=@_;
     return "\n<p><b>$title:</b><br>".      $value=~s/^\s+//gs;
            '<input type=text name="'.$name.'" size=80 value="'.$value.'">';      $value=~s/\s+$//gs;
       $value=~s/\s+/ /gs;
       $title=&mt($title);
       $env{'form.'.$name}=$value;
       return "\n".&Apache::lonhtmlcommon::row_title($title)
              .'<input type="text" name="'.$name.'" size="80" value="'.$value.'" />'
              .&Apache::lonhtmlcommon::row_closure($noline);
   }
   
   sub text_with_browse_field {
       my ($title,$name,$value,$restriction,$noline)=@_;
       $value=~s/^\s+//gs;
       $value=~s/\s+$//gs;
       $value=~s/\s+/ /gs;
       $title=&mt($title);
       $env{'form.'.$name}=$value;
       return "\n".&Apache::lonhtmlcommon::row_title($title)
             .'<input type="text" name="'.$name.'" size="80" value="'.$value.'" />'
             .'<br />'
     .'<a href="javascript:openbrowser(\'pubform\',\''.$name.'\',\''.$restriction.'\');">'
             .&mt('Select')
             .'</a>&nbsp;'
     .'<a href="javascript:opensearcher(\'pubform\',\''.$name.'\');">'
             .&mt('Search')
             .'</a>'
             .&Apache::lonhtmlcommon::row_closure($noline);
 }  }
   
 sub hiddenfield {  sub hiddenfield {
     my ($name,$value)=@_;      my ($name,$value)=@_;
     return "\n".'<input type=hidden name="'.$name.'" value="'.$value.'">';      $env{'form.'.$name}=$value;
       return "\n".'<input type="hidden" name="'.$name.'" value="'.$value.'" />';
   }
   
   sub checkbox {
       my ($name,$text)=@_;
       return "\n<br /><label><input type='checkbox' name='$name' /> ".
    &mt($text)."</label>";
 }  }
   
 sub selectbox {  sub selectbox {
     my ($title,$name,$value,$functionref,@idlist)=@_;      my ($title,$name,$value,$functionref,@idlist)=@_;
     my $uctitle=uc($title);      $title=&mt($title);
     my $selout="\n<p><font color=\"#800000\" face=\"helvetica\"><b>$uctitle:".      $value=(split(/\s*,\s*/,$value))[-1];
  "</b></font><br />".'<select name="'.$name.'">';      if (defined($value)) {
    $env{'form.'.$name}=$value;
       } else {
    $env{'form.'.$name}=$idlist[0];
       }
       my $selout="\n".&Apache::lonhtmlcommon::row_title($title)
                 .'<select name="'.$name.'">';
     foreach (@idlist) {      foreach (@idlist) {
         $selout.='<option value=\''.$_.'\'';          $selout.='<option value="'.$_.'"';
         if ($_ eq $value) {          if ($_ eq $value) {
     $selout.=' selected>'.&{$functionref}($_).'</option>';      $selout.=' selected="selected"';
  }          }
         else {$selout.='>'.&{$functionref}($_).'</option>';}          $selout.='>'.&{$functionref}($_).'</option>';
     }      }
     return $selout.'</select>';      $selout.='</select>'.&Apache::lonhtmlcommon::row_closure();
       return $selout;
   }
   
   sub select_level_form {
       my ($value,$name)=@_;
       $env{'form.'.$name}=$value;
       if (!defined($value)) { $env{'form.'.$name}=0; }
       return  &Apache::loncommon::select_level_form($value,$name);
 }  }
   #########################################
   #########################################
   
 # -------------------------------------------------------- Publication Step One  =pod
   
   =item B<urlfixup>
   
   Fix up a url?  First step of publication
   
   =cut
   
   #########################################
   #########################################
 sub urlfixup {  sub urlfixup {
     my ($url,$target)=@_;      my ($url,$target)=@_;
     unless ($url) { return ''; }      unless ($url) { return ''; }
Line 194  sub urlfixup { Line 421  sub urlfixup {
     if ($url =~ /^mailto:/i) { return $url; }      if ($url =~ /^mailto:/i) { return $url; }
     #internal document links need no fixing      #internal document links need no fixing
     if ($url =~ /^\#/) { return $url; }       if ($url =~ /^\#/) { return $url; } 
     my ($host)=($url=~/(?:http\:\/\/)*([^\/]+)/);      my ($host)=($url=~m{(?:(?:http|https|ftp)://)*([^/]+)});
     foreach (values %Apache::lonnet::hostname) {      my @lonids = &Apache::lonnet::machine_ids($host);
  if ($_ eq $host) {      if (@lonids) {
     $url=~s/^http\:\/\///;   $url=~s{^(?:http|https|ftp)://}{};
             $url=~s/^$host//;   $url=~s/^\Q$host\E//;
         }  
     }      }
     if ($url=~/^http\:\/\//) { return $url; }      if ($url=~m{^(?:http|https|ftp)://}) { return $url; }
     $url=~s/\~$cuname/res\/$cudom\/$cuname/;      $url=~s{\Q~$cuname\E}{res/$cudom/$cuname};
     return $url;      return $url;
 }  }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<absoluteurl>
   
   Currently undocumented.
   
   =cut
   
   #########################################
   #########################################
 sub absoluteurl {  sub absoluteurl {
     my ($url,$target)=@_;      my ($url,$target)=@_;
     unless ($url) { return ''; }      unless ($url) { return ''; }
Line 217  sub absoluteurl { Line 455  sub absoluteurl {
     return $url;      return $url;
 }  }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<set_allow>
   
   Currently undocumented    
   
   =cut
   
   #########################################
   #########################################
 sub set_allow {  sub set_allow {
     my ($allow,$logfile,$target,$tag,$oldurl)=@_;      my ($allow,$logfile,$target,$tag,$oldurl)=@_;
     my $newurl=&urlfixup($oldurl,$target);      my $newurl=&urlfixup($oldurl,$target);
Line 228  sub set_allow { Line 479  sub set_allow {
     }      }
     if (($newurl !~ /^javascript:/i) &&      if (($newurl !~ /^javascript:/i) &&
  ($newurl !~ /^mailto:/i) &&   ($newurl !~ /^mailto:/i) &&
  ($newurl !~ /^http:/i) &&   ($newurl !~ /^(?:http|https|ftp):/i) &&
  ($newurl !~ /^\#/)) {   ($newurl !~ /^\#/)) {
  $$allow{&absoluteurl($newurl,$target)}=1;   $$allow{&absoluteurl($newurl,$target)}=1;
     }      }
     return $return_url      return $return_url;
   }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<get_subscribed_hosts>
   
   Currently undocumented    
   
   =cut
   
   #########################################
   #########################################
   sub get_subscribed_hosts {
       my ($target)=@_;
       my @subscribed;
       my $filename;
       $target=~/(.*)\/([^\/]+)$/;
       my $srcf=$2;
       opendir(DIR,$1);
       # cycle through listed files, subscriptions used to exist
       # as "filename.lonid"
       while ($filename=readdir(DIR)) {
    if ($filename=~/\Q$srcf\E\.($match_lonid)$/) {
       my $subhost=$1;
       if (($subhost ne 'meta' 
    && $subhost ne 'subscription' 
    && $subhost ne 'meta.subscription'
    && $subhost ne 'tmp') &&
                   ($subhost ne $Apache::lonnet::perlvar{'lonHostID'})) {
    push(@subscribed,$subhost);
       }
    }
       }
       closedir(DIR);
       my $sh;
       if ( $sh=Apache::File->new("$target.subscription") ) {
    while (my $subline=<$sh>) {
       if ($subline =~ /^($match_lonid):/) { 
                   if ($1 ne $Apache::lonnet::perlvar{'lonHostID'}) { 
                      push(@subscribed,$1);
           }
       }
    }
       }
       return @subscribed;
 }  }
   
   
   #########################################
   #########################################
   
   =pod
   
   =item B<get_max_ids_indices>
   
   Currently undocumented    
   
   =cut
   
   #########################################
   #########################################
   sub get_max_ids_indices {
       my ($content)=@_;
       my $maxindex=10;
       my $maxid=10;
       my $needsfixup=0;
       my $duplicateids=0;
   
       my %allids;
       my %duplicatedids;
   
       my $parser=HTML::LCParser->new($content);
       $parser->xml_mode(1);
       my $token;
       while ($token=$parser->get_token) {
    if ($token->[0] eq 'S') {
       my $counter;
       if ($counter=$addid{$token->[1]}) {
    if ($counter eq 'id') {
       if (defined($token->[2]->{'id'}) &&
    $token->[2]->{'id'} !~ /^\s*$/) {
    $maxid=($token->[2]->{'id'}>$maxid)?$token->[2]->{'id'}:$maxid;
    if (exists($allids{$token->[2]->{'id'}})) {
       $duplicateids=1;
       $duplicatedids{$token->[2]->{'id'}}=1;
    } else {
       $allids{$token->[2]->{'id'}}=1;
    }
       } else {
    $needsfixup=1;
       }
    } else {
       if (defined($token->[2]->{'index'}) &&
    $token->[2]->{'index'} !~ /^\s*$/) {
    $maxindex=($token->[2]->{'index'}>$maxindex)?$token->[2]->{'index'}:$maxindex;
       } else {
    $needsfixup=1;
       }
    }
       }
    }
       }
       return ($needsfixup,$maxid,$maxindex,$duplicateids,
       (keys(%duplicatedids)));
   }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<get_all_text_unbalanced>
   
   Currently undocumented    
   
   =cut
   
   #########################################
   #########################################
   sub get_all_text_unbalanced {
       #there is a copy of this in lonxml.pm
       my($tag,$pars)= @_;
       my $token;
       my $result='';
       $tag='<'.$tag.'>';
       while ($token = $$pars[-1]->get_token) {
    if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) {
       $result.=$token->[1];
    } elsif ($token->[0] eq 'PI') {
       $result.=$token->[2];
    } elsif ($token->[0] eq 'S') {
       $result.=$token->[4];
    } elsif ($token->[0] eq 'E')  {
       $result.=$token->[2];
    }
    if ($result =~ /\Q$tag\E/s) {
       ($result,my $redo)=$result =~ /(.*)\Q$tag\E(.*)/is;
       #&Apache::lonnet::logthis('Got a winner with leftovers ::'.$2);
       #&Apache::lonnet::logthis('Result is :'.$1);
       $redo=$tag.$redo;
       push (@$pars,HTML::LCParser->new(\$redo));
       $$pars[-1]->xml_mode('1');
       last;
    }
       }
       return $result
   }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<fix_ids_and_indices>
   
   Currently undocumented    
   
   =cut
   
   #########################################
   #########################################
   #Arguably this should all be done as a lonnet::ssi instead
   sub fix_ids_and_indices {
       my ($logfile,$source,$target)=@_;
   
       my %allow;
       my $content;
       {
    my $org=Apache::File->new($source);
    $content=join('',<$org>);
       }
   
       my ($needsfixup,$maxid,$maxindex,$duplicateids,@duplicatedids)=
    &get_max_ids_indices(\$content);
   
       print $logfile ("Got $needsfixup,$maxid,$maxindex,$duplicateids--".
      join(', ',@duplicatedids));
       if ($duplicateids) {
    print $logfile "Duplicate ID(s) exist, ".join(', ',@duplicatedids)."\n";
    my $outstring='<span class="LC_error">'.&mt('Unable to publish file, it contains duplicated ID(s), ID(s) need to be unique. The duplicated ID(s) are').': '.join(', ',@duplicatedids).'</span>';
    return ($outstring,1);
       }
       if ($needsfixup) {
    print $logfile "Needs ID and/or index fixup\n".
       "Max ID   : $maxid (min 10)\n".
                   "Max Index: $maxindex (min 10)\n";
       }
       my $outstring='';
       my $responsecounter=1;
       my @parser;
       $parser[0]=HTML::LCParser->new(\$content);
       $parser[-1]->xml_mode(1);
       my $token;
       while (@parser) {
    while ($token=$parser[-1]->get_token) {
       if ($token->[0] eq 'S') {
    my $counter;
    my $tag=$token->[1];
    my $lctag=lc($tag);
    if ($lctag eq 'allow') {
       $allow{$token->[2]->{'src'}}=1;
       next;
    }
    if ($lctag eq 'base') { next; }
                   if (($lctag eq 'part') || ($lctag eq 'problem')) {
                       $responsecounter=0;
                   }
                   if ($lctag=~/response$/) { $responsecounter++; }
                   if ($lctag eq 'import') { $responsecounter++; }
    my %parms=%{$token->[2]};
    $counter=$addid{$tag};
    if (!$counter) { $counter=$addid{$lctag}; }
    if ($counter) {
       if ($counter eq 'id') {
    unless (defined($parms{'id'}) &&
    $parms{'id'}!~/^\s*$/) {
       $maxid++;
       $parms{'id'}=$maxid;
       print $logfile 'ID(new) : '.$tag.':'.$maxid."\n";
    } else {
       print $logfile 'ID(kept): '.$tag.':'.$parms{'id'}."\n";
    }
       } elsif ($counter eq 'index') {
    unless (defined($parms{'index'}) &&
    $parms{'index'}!~/^\s*$/) {
       $maxindex++;
       $parms{'index'}=$maxindex;
       print $logfile 'Index: '.$tag.':'.$maxindex."\n";
    }
       }
    }
                   unless ($parms{'type'} eq 'zombie') {
       foreach my $type ('src','href','background','bgimg') {
    foreach my $key (keys(%parms)) {
       if ($key =~ /^$type$/i) {
    $parms{$key}=&set_allow(\%allow,$logfile,
    $target,$tag,
    $parms{$key});
       }
    }
       }
    }
    # probably a <randomlabel> image type <label>
    # or a <image> tag inside <imageresponse>
    if (($lctag eq 'label' && defined($parms{'description'}))
       ||
       ($lctag eq 'image')) {
       my $next_token=$parser[-1]->get_token();
       if ($next_token->[0] eq 'T') {
                           $next_token->[1] =~ s/[\n\r\f]+//g;
    $next_token->[1]=&set_allow(\%allow,$logfile,
       $target,$tag,
       $next_token->[1]);
       }
       $parser[-1]->unget_token($next_token);
    }
    if ($lctag eq 'applet') {
       my $codebase='';
       my $havecodebase=0;
       foreach my $key (keys(%parms)) {
    if (lc($key) eq 'codebase') { 
       $codebase=$parms{$key};
       $havecodebase=1; 
    }
       }
       if ($havecodebase) {
    my $oldcodebase=$codebase;
    unless ($oldcodebase=~/\/$/) {
       $oldcodebase.='/';
    }
    $codebase=&urlfixup($oldcodebase,$target);
    $codebase=~s/\/$//;    
    if ($codebase ne $oldcodebase) {
       $parms{'codebase'}=$codebase;
       print $logfile 'URL codebase: '.$tag.':'.
    $oldcodebase.' - '.
       $codebase."\n";
    }
    $allow{&absoluteurl($codebase,$target).'/*'}=1;
       } else {
    foreach my $key (keys(%parms)) {
       if ($key =~ /(archive|code|object)/i) {
    my $oldurl=$parms{$key};
    my $newurl=&urlfixup($oldurl,$target);
    $newurl=~s/\/[^\/]+$/\/\*/;
    print $logfile 'Allow: applet '.lc($key).':'.
       $oldurl.' allows '.$newurl."\n";
    $allow{&absoluteurl($newurl,$target)}=1;
       }
    }
       }
    }
    my $newparmstring='';
    my $endtag='';
    foreach (keys %parms) {
       if ($_ eq '/') {
    $endtag=' /';
       } else { 
    my $quote=($parms{$_}=~/\"/?"'":'"');
    $newparmstring.=' '.$_.'='.$quote.$parms{$_}.$quote;
       }
    }
    if (!$endtag) { if ($token->[4]=~m:/>$:) { $endtag=' /'; }; }
    $outstring.='<'.$tag.$newparmstring.$endtag.'>';
    if ($lctag eq 'm' || $lctag eq 'script' || $lctag eq 'answer' 
                       || $lctag eq 'display' || $lctag eq 'tex') {
       $outstring.=&get_all_text_unbalanced('/'.$lctag,\@parser);
    }
       } elsif ($token->[0] eq 'E') {
    if ($token->[2]) {
       unless ($token->[1] eq 'allow') {
    $outstring.='</'.$token->[1].'>';
       }
                   }
                   if ((($token->[1] eq 'part') || ($token->[1] eq 'problem'))
                       && (!$responsecounter)) {
                       my $outstring='<span class="LC_error">'.&mt('Found [_1] without responses. This resource cannot be published.',$token->[1]).'</span>';
                       return ($outstring,1);
                   }
       } else {
    $outstring.=$token->[1];
       }
    }
    pop(@parser);
       }
   
       if ($needsfixup) {
    print $logfile "End of ID and/or index fixup\n".
       "Max ID   : $maxid (min 10)\n".
    "Max Index: $maxindex (min 10)\n";
       } else {
    print $logfile "Does not need ID and/or index fixup\n";
       }
   
       return ($outstring,0,%allow);
   }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<store_metadata>
   
   Store the metadata in the metadata table in the loncapa database.
   Uses lonmysql to access the database.
   
   Inputs: \%metadata
   
   Returns: (error,status).  error is undef on success, status is undef on error.
   
   =cut
   
   #########################################
   #########################################
   sub store_metadata {
       my %metadata = @_;
       my $error;
       # Determine if the table exists
       my $status = &Apache::lonmysql::check_table('metadata');
       if (! defined($status)) {
           $error='<span class="LC_error">'
                 .&mt('WARNING: Cannot connect to database!')
                 .'</span>';
           &Apache::lonnet::logthis($error);
           return ($error,undef);
       }
       if ($status == 0) {
           # It would be nice to actually create the table....
           $error ='<span class="LC_error">'
                  .&mt('WARNING: The metadata table does not exist in the LON-CAPA database!')
                  .'</span>';
           &Apache::lonnet::logthis($error);
           return ($error,undef);
       }
       my $dbh = &Apache::lonmysql::get_dbh();
       if (($metadata{'obsolete'}) || ($metadata{'copyright'} eq 'priv')) {
           # remove this entry
    my $delitem = 'url = '.$dbh->quote($metadata{'url'});
    $status = &LONCAPA::lonmetadata::delete_metadata($dbh,undef,$delitem);
                                                          
       } else {
           $status = &LONCAPA::lonmetadata::update_metadata($dbh,undef,undef,
                                                            \%metadata);
       }
       if (defined($status) && $status ne '') {
           $error='<span class="LC_error">'
                 .&mt('Error occurred saving new values in metadata table in LON-CAPA database!')
                 .'</span>';
           &Apache::lonnet::logthis($error);
           &Apache::lonnet::logthis($status);
           return ($error,undef);
       }
       return (undef,'success');
   }
   
   
   # ========================================== Parse file for errors and warnings
   
   sub checkonthis {
       my ($r,$source)=@_;
       my $uri=&Apache::lonnet::hreflocation($source);
       $uri=~s/\/$//;
       my $result=&Apache::lonnet::ssi_body($uri,
    ('grade_target'=>'web',
     'return_only_error_and_warning_counts' => 1));
       my ($errorcount,$warningcount)=split(':',$result);
       if (($errorcount) || ($warningcount)) {
           $r->print('<h3>'.&mt('Warnings and Errors').'</h3>');
           $r->print('<tt>'.$uri.'</tt>:');
           $r->print('<ul>');
           if ($warningcount) {
               $r->print('<li><div class="LC_warning">'
                        .&mt('[quant,_1,warning]',$warningcount)
                        .'</div></li>');
           }
           if ($errorcount) {
               $r->print('<li><div class="LC_error">'
                        .&mt('[quant,_1,error]',$errorcount)
                        .' <img src="/adm/lonMisc/bomb.gif" />'
                        .'</div></li>');
           }
           $r->print('</ul>');
       } else {
    #$r->print('<font color="green">'.&mt('ok').'</font>');
       }
       $r->rflush();
       return ($warningcount,$errorcount);
   }
   
   # ============================================== Parse file itself for metadata
   #
   # parses a file with target meta, sets global %metadatafields %metadatakeys 
   
   sub parseformeta {
       my ($source,$style)=@_;
       my $allmeta='';
       if (($style eq 'ssi') || ($style eq 'prv')) {
    my $dir=$source;
    $dir=~s-/[^/]*$--;
    my $file=$source;
    $file=(split('/',$file))[-1];
           $source=&Apache::lonnet::hreflocation($dir,$file);
    $allmeta=&Apache::lonnet::ssi_body($source,('grade_target' => 'meta'));
           &metaeval($allmeta);
       }
       return $allmeta;
   }
   
   #########################################
   #########################################
   
   =pod
   
   =item B<publish>
   
   This is the workhorse function of this module.  This subroutine generates
   backup copies, performs any automatic processing (prior to publication,
   especially for rat and ssi files),
   
   Returns a 2 element array, the first is the string to be shown to the
   user, the second is an error code, either 1 (an error occurred) or 0
   (no error occurred)
   
   I<Additional documentation needed.>
   
   =cut
   
   #########################################
   #########################################
 sub publish {  sub publish {
   
     my ($source,$target,$style)=@_;      my ($source,$target,$style,$batch)=@_;
     my $logfile;      my $logfile;
     my $scrout='';      my $scrout='';
     my $allmeta='';      my $allmeta='';
     my $content='';      my $content='';
     my %allow=();      my %allow=();
     undef %allow;  
   
     unless ($logfile=Apache::File->new('>>'.$source.'.log')) {      unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
  return    return ('<span class="LC_error">'.&mt('No write permission to user directory, FAIL').'</span>',1);
          '<font color=red>No write permission to user directory, FAIL</font>';  
     }      }
     print $logfile       print $logfile 
 "\n\n================= Publish ".localtime()." Phase One  ================\n";  "\n\n================= Publish ".localtime()." Phase One  ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
   
     if (($style eq 'ssi') || ($style eq 'rat')) {      if (($style eq 'ssi') || ($style eq 'rat') || ($style eq 'prv')) {
 # ------------------------------------------------------- This needs processing  # ------------------------------------------------------- This needs processing
   
 # ----------------------------------------------------------------- Backup Copy  # ----------------------------------------------------------------- Backup Copy
Line 261  sub publish { Line 981  sub publish {
     print $logfile "Copied original file to ".$copyfile."\n";      print $logfile "Copied original file to ".$copyfile."\n";
         } else {          } else {
     print $logfile "Unable to write backup ".$copyfile.':'.$!."\n";      print $logfile "Unable to write backup ".$copyfile.':'.$!."\n";
           return "<font color=red>Failed to write backup copy, $!,FAIL</font>";      return ("<span class=\"LC_error\">".&mt("Failed to write backup copy, [_1], FAIL",$1)."</span>",1);
         }          }
 # ------------------------------------------------------------- IDs and indices  # ------------------------------------------------------------- IDs and indices
   
         my $maxindex=10;   my ($outstring,$error);
         my $maxid=10;   ($outstring,$error,%allow)=&fix_ids_and_indices($logfile,$source,
    $target);
         my $needsfixup=0;   if ($error) { return ($outstring,$error); }
   
         {  
           my $org=Apache::File->new($source);  
           $content=join('',<$org>);  
         }  
         {  
           my $parser=HTML::LCParser->new(\$content);  
           my $token;  
           while ($token=$parser->get_token) {  
               if ($token->[0] eq 'S') {  
                   my $counter;  
   if ($counter=$addid{$token->[1]}) {  
       if ($counter eq 'id') {  
   if (defined($token->[2]->{'id'})) {  
                              $maxid=  
        ($token->[2]->{'id'}>$maxid)?$token->[2]->{'id'}:$maxid;  
  } else {  
                              $needsfixup=1;  
                          }  
                       } else {  
    if (defined($token->[2]->{'index'})) {  
                              $maxindex=  
    ($token->[2]->{'index'}>$maxindex)?$token->[2]->{'index'}:$maxindex;  
   } else {  
                              $needsfixup=1;  
   }  
       }  
   }  
               }  
           }  
       }  
       if ($needsfixup) {  
           print $logfile "Needs ID and/or index fixup\n".  
         "Max ID   : $maxid (min 10)\n".  
                 "Max Index: $maxindex (min 10)\n";  
       }  
           my $outstring='';  
           my $parser=HTML::LCParser->new(\$content);  
           $parser->xml_mode(1);  
           my $token;  
           while ($token=$parser->get_token) {  
               if ($token->[0] eq 'S') {  
                 my $counter;  
                 my $tag=$token->[1];  
                 my $lctag=lc($tag);  
                 unless ($lctag eq 'allow') {    
                   my %parms=%{$token->[2]};  
                   $counter=$addid{$tag};  
                   if (!$counter) { $counter=$addid{$lctag}; }  
                   if ($counter) {  
       if ($counter eq 'id') {  
   unless (defined($parms{'id'})) {  
                               $maxid++;  
                               $parms{'id'}=$maxid;  
                               print $logfile 'ID: '.$tag.':'.$maxid."\n";  
                           }  
                       } elsif ($counter eq 'index') {  
    unless (defined($parms{'index'})) {  
                               $maxindex++;  
                               $parms{'index'}=$maxindex;  
                               print $logfile 'Index: '.$tag.':'.$maxindex."\n";  
   }  
       }  
   }  
   
                   foreach my $type ('src','href','background','bgimg') {  
       foreach my $key (keys(%parms)) {  
   print $logfile "for $type, and $key\n";  
   if ($key =~ /^$type$/i) {  
       print $logfile "calling set_allow\n";  
       $parms{$key}=&set_allow(\%allow,$logfile,  
       $target,$tag,  
       $parms{$key});  
   }  
       }  
                   }  
   # probably a <randomlabel> image type <label>  
   if ($lctag eq 'label' && defined($parms{'description'})) {  
       my $next_token=$parser->get_token();  
       if ($next_token->[0] eq 'T') {  
   $next_token->[1]=&set_allow(\%allow,$logfile,  
       $target,$tag,  
       $next_token->[1]);  
       }  
       $parser->unget_token($next_token);  
   }  
                   if ($lctag eq 'applet') {  
       my $codebase='';  
                       if (defined($parms{'codebase'})) {  
          my $oldcodebase=$parms{'codebase'};  
                          unless ($oldcodebase=~/\/$/) {  
                             $oldcodebase.='/';  
                          }  
                          $codebase=&urlfixup($oldcodebase,$target);  
                          $codebase=~s/\/$//;      
                          if ($codebase ne $oldcodebase) {  
      $parms{'codebase'}=$codebase;  
                              print $logfile 'URL codebase: '.$tag.':'.  
                                   $oldcodebase.' - '.  
   $codebase."\n";  
  }  
                          $allow{&absoluteurl($codebase,$target).'/*'}=1;  
       } else {  
                         foreach ('archive','code','object') {  
                           if (defined($parms{$_})) {  
       my $oldurl=$parms{$_};  
                               my $newurl=&urlfixup($oldurl,$target);  
       $newurl=~s/\/[^\/]+$/\/\*/;  
                                   print $logfile 'Allow: applet '.$_.':'.  
                                   $oldurl.' allows '.  
   $newurl."\n";  
                               $allow{&absoluteurl($newurl,$target)}=1;  
                           }  
                         }  
                       }  
                   }  
   
                   my $newparmstring='';  
                   my $endtag='';  
                   foreach (keys %parms) {  
                     if ($_ eq '/') {  
                       $endtag=' /';  
                     } else {   
                       my $quote=($parms{$_}=~/\"/?"'":'"');  
                       $newparmstring.=' '.$_.'='.$quote.$parms{$_}.$quote;  
     }  
                   }  
   if (!$endtag) { if ($token->[4]=~m:/>$:) { $endtag=' /'; }; }  
   $outstring.='<'.$tag.$newparmstring.$endtag.'>';  
          } else {  
    $allow{$token->[2]->{'src'}}=1;  
  }  
               } elsif ($token->[0] eq 'E') {  
  if ($token->[2]) {  
                   unless ($token->[1] eq 'allow') {  
                      $outstring.='</'.$token->[1].'>';  
   }  
  }  
               } else {  
                   $outstring.=$token->[1];  
               }  
           }  
 # ------------------------------------------------------------ Construct Allows  # ------------------------------------------------------------ Construct Allows
           
  $scrout.='<h3>Dependencies</h3>';          my $outdep=''; # Collect dependencies output data
         my $allowstr='';          my $allowstr='';
         foreach (sort(keys(%allow))) {          foreach my $thisdep (sort(keys(%allow))) {
    my $thisdep=$_;  
    if ($thisdep !~ /[^\s]/) { next; }     if ($thisdep !~ /[^\s]/) { next; }
              if ($thisdep =~/\$/) {
                 $outdep.='<div class="LC_warning">'
                          .&mt('The resource depends on another resource with variable filename, i.e., [_1].','<tt>'.$thisdep.'</tt>').'<br />'
                          .&mt('You likely need to explicitly allow access to all possible dependencies using the [_1]-tag.','<tt>&lt;allow&gt;</tt>')
                          ."</div>\n";
              }
            unless ($style eq 'rat') {              unless ($style eq 'rat') { 
               $allowstr.="\n".'<allow src="'.$thisdep.'" />';                $allowstr.="\n".'<allow src="'.$thisdep.'" />';
    }     }
            $scrout.='<br>';            $outdep.='<div>';
            unless ($thisdep=~/\*/) {             if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) {
        $scrout.='<a href="'.$thisdep.'">';         $outdep.='<a href="'.$thisdep.'">';
            }             }
            $scrout.='<tt>'.$thisdep.'</tt>';             $outdep.='<tt>'.$thisdep.'</tt>';
            unless ($thisdep=~/\*/) {             if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) {
        $scrout.='</a>';         $outdep.='</a>';
                if (                 if (
        &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'.         &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                                             $thisdep.'.meta') eq '-1') {                                              $thisdep.'.meta') eq '-1') {
    $scrout.=     $outdep.= ' - <span class="LC_error">'.&mt('Currently not available').
                            ' - <font color=red>Currently not available</font>';         '</span>';
                } else {                 } else {
                    my %temphash=(&Apache::lonnet::declutter($target).'___'.                     my %temphash=(&Apache::lonnet::declutter($target).'___'.
                              &Apache::lonnet::declutter($thisdep).'___usage'                               &Apache::lonnet::declutter($thisdep).'___usage'
                                  => time);                                   => time);
                    $thisdep=~/^\/res\/(\w+)\/(\w+)\//;                     $thisdep=~m{^/res/($match_domain)/($match_username)/};
                    if ((defined($1)) && (defined($2))) {                     if ((defined($1)) && (defined($2))) {
                       &Apache::lonnet::put('resevaldata',\%temphash,$1,$2);                        &Apache::lonnet::put('nohist_resevaldata',\%temphash,
      $1,$2);
    }     }
        }         }
            }             }
              $outdep.='</div><br />';
           }
   
           if ($outdep) {
               $scrout.='<h3>'.&mt('Dependencies').'</h3>'
                       .$outdep
         }          }
         $outstring=~s/\n*(\<\/[^\>]+\>)\s*$/$allowstr\n$1\n/s;          $outstring=~s/\n*(\<\/[^\>]+\>[^<]*)$/$allowstr\n$1\n/s;
   
  #Encode any High ASCII characters  # ------------------------------------------------------------- Write modified.
  $outstring=&HTML::Entities::encode($outstring,"\200-\377");  
 # ------------------------------------------------------------- Write modified  
   
         {          {
           my $org;            my $org;
           unless ($org=Apache::File->new('>'.$source)) {            unless ($org=Apache::File->new('>'.$source)) {
              print $logfile "No write permit to $source\n";               print $logfile "No write permit to $source\n";
              return                return ('<span class="LC_error">'.&mt('No write permission to').
               "<font color=red>No write permission to $source, FAIL</font>";       ' '.$source.
        ', '.&mt('FAIL').'</span>',1);
   }    }
           print $org $outstring;            print($org $outstring);
         }          }
   $content=$outstring;    $content=$outstring;
   
       if ($needsfixup) {  
           print $logfile "End of ID and/or index fixup\n".  
         "Max ID   : $maxid (min 10)\n".  
                 "Max Index: $maxindex (min 10)\n";  
       } else {  
   print $logfile "Does not need ID and/or index fixup\n";  
       }  
     }      }
 # --------------------------------------------- Initial step done, now metadata  # -------------------------------------------- Initial step done, now metadata.
   
 # ---------------------------------------- Storage for metadata keys and fields  
   
   # --------------------------------------- Storage for metadata keys and fields.
   # these are globals
   #
      %metadatafields=();       %metadatafields=();
      %metadatakeys=();       %metadatakeys=();
             
      my %oldparmstores=();       my %oldparmstores=();
             
            unless ($batch) {
      $scrout.='<h3>Metadata Information ' .       $scrout.='<h3>'.&mt('Metadata').' ' .
        Apache::loncommon::help_open_topic("Metadata_Description")         &Apache::loncommon::help_open_topic("Metadata_Description")
        . '</h3>';         . '</h3>';
       }
   
 # ------------------------------------------------ First, check out environment  # ------------------------------------------------ First, check out environment
      unless (-e $source.'.meta') {       if ((!(-e $source.'.meta')) || ($env{'form.forceoverride'})) {
         $metadatafields{'author'}=$ENV{'environment.firstname'}.' '.          $metadatafields{'author'}=$env{'environment.firstname'}.' '.
                           $ENV{'environment.middlename'}.' '.                            $env{'environment.middlename'}.' '.
                   $ENV{'environment.lastname'}.' '.                    $env{'environment.lastname'}.' '.
                   $ENV{'environment.generation'};                    $env{'environment.generation'};
         $metadatafields{'author'}=~s/\s+/ /g;          $metadatafields{'author'}=~s/\s+/ /g;
         $metadatafields{'author'}=~s/\s+$//;          $metadatafields{'author'}=~s/\s+$//;
         $metadatafields{'owner'}=$cuname.'@'.$cudom;          $metadatafields{'owner'}=$cuname.':'.$cudom;
   
 # ------------------------------------------------ Check out directory hierachy  # ------------------------------------------------ Check out directory hierachy
   
         my $thisdisfn=$source;          my $thisdisfn=$source;
         $thisdisfn=~s/^\/home\/$cuname\///;          $thisdisfn=~s/^\/home\/\Q$cuname\E\///;
   
         my @urlparts=split(/\//,$thisdisfn);          my @urlparts=split(/\//,$thisdisfn);
         $#urlparts--;          $#urlparts--;
   
         my $currentpath='/home/'.$cuname.'/';          my $currentpath='/home/'.$cuname.'/';
   
    my $prefix='../'x($#urlparts);
         foreach (@urlparts) {          foreach (@urlparts) {
     $currentpath.=$_.'/';      $currentpath.=$_.'/';
             $scrout.=&metaread($logfile,$currentpath.'default.meta');              $scrout.=&metaread($logfile,$currentpath.'default.meta',$prefix);
       $prefix=~s|^\.\./||;
         }          }
   
   # ----------------------------------------------------------- Parse file itself
   # read %metadatafields from file itself
    
    $allmeta=&parseformeta($source,$style);
   
 # ------------------- Clear out parameters and stores (there should not be any)  # ------------------- Clear out parameters and stores (there should not be any)
   
         foreach (keys %metadatafields) {          foreach (keys %metadatafields) {
Line 527  sub publish { Line 1118  sub publish {
  delete $metadatafields{$_};   delete $metadatafields{$_};
             }              }
         }          }
           # ------------------------------------------------------------- Save some stuff
     }          my %savemeta=();
           foreach ('title') {
               $savemeta{$_}=$metadatafields{$_};
    }
   # ------------------------------------------ See if anything new in file itself
    
    $allmeta=&parseformeta($source,$style);
   # ----------------------------------------------------------- Restore the stuff
           foreach (keys %savemeta) {
       $metadatafields{$_}=$savemeta{$_};
    }
      }
   
 # -------------------------------------------------- Parse content for metadata         
     if ($style eq 'ssi') {  # ---------------- Find and document discrepancies in the parameters and stores
         my $oldenv=$ENV{'request.uri'};  
   
         $ENV{'request.uri'}=$target;  
         $allmeta=Apache::lonxml::xmlparse(undef,'meta',$content);  
         $ENV{'request.uri'}=$oldenv;  
   
         &metaeval($allmeta);      my $chparms='';
       foreach (sort keys %metadatafields) {
    if (($_=~/^parameter/) || ($_=~/^stores/)) {
       unless ($_=~/\.\w+$/) { 
    unless ($oldparmstores{$_}) {
       my $disp_key = $_;
       $disp_key =~ tr/\0/_/;
       print $logfile ('New: '.$disp_key."\n");
       $chparms .= $disp_key.' ';
    }
       }
    }
       }
       if ($chparms) {
    $scrout.='<p><b>'.&mt('New parameters or saved values').
       ':</b> '.$chparms.'</p>';
     }      }
 # ---------------- Find and document discrepancies in the parameters and stores  
   
         my $chparms='';      $chparms='';
         foreach (sort keys %metadatafields) {      foreach (sort keys %oldparmstores) {
     if (($_=~/^parameter/) || ($_=~/^stores/)) {   if (($_=~/^parameter/) || ($_=~/^stores/)) {
                 unless ($_=~/\.\w+$/) {       unless (($metadatafields{$_.'.name'}) ||
                    unless ($oldparmstores{$_}) {      ($metadatafields{$_.'.package'}) || ($_=~/\.\w+$/)) {
       print $logfile 'New: '.$_."\n";   my $disp_key = $_;
                       $chparms.=$_.' ';   $disp_key =~ tr/\0/_/;
                    }   print $logfile ('Obsolete: '.$disp_key."\n");
         }   $chparms.=$disp_key.' ';
             }      }
         }   }
         if ($chparms) {      }
     $scrout.='<p><b>New parameters or stored values:</b> '.      if ($chparms) {
                      $chparms;          $scrout.='<p><b>'.&mt('Obsolete parameters or saved values').':</b> '
         }          .$chparms.'</p>'
                   .'<p class="LC_warning"><b>'.&mt('Warning!').'</b><br />'
                   .&mt('If this resource is in active use, student performance data from the previous version may become inaccessible.')
                   .'</p><hr />';
       }
       if ($metadatafields{'copyright'} eq 'priv') {
           $scrout.='<p class="LC_warning"><b>'.&mt('Warning!').'</b><br />'
                   .&mt('Copyright/distribution option "Private" is no longer supported. Select another option from below. Consider "Custom Rights" for maximum control over the usage of your resource.')
                   .'</p><hr />';
       }
   
         $chparms='';  # ------------------------------------------------------- Now have all metadata
         foreach (sort keys %oldparmstores) {  
     if (($_=~/^parameter/) || ($_=~/^stores/)) {      my %keywords=();
                 unless (($metadatafields{$_.'.name'}) ||          
                         ($metadatafields{$_.'.package'}) || ($_=~/\.\w+$/)) {      if (length($content)<500000) {
     print $logfile 'Obsolete: '.$_."\n";   my $textonly=$content;
                     $chparms.=$_.' ';   $textonly=~s/\<script[^\<]+\<\/script\>//g;
                 }   $textonly=~s/\<m\>[^\<]+\<\/m\>//g;
    $textonly=~s/\<[^\>]*\>//g;
   
           #this is a work simplification for german authors for present
           $textonly=HTML::Entities::decode($textonly);           #decode HTML-character
           $textonly=Encode::Encoder::encode('utf8', $textonly);  #encode to perl internal unicode
           $textonly=~tr/A-ZÜÄÖ/a-züäö/;      #add lowercase rule for german "Umlaute"
           $textonly=~s/[\$\&][a-z]\w*//g;
           $textonly=~s/[^a-z^ü^ä^ö^ß\s]//g;  #dont delete german "Umlaute"
   
           foreach ($textonly=~m/[^\s]+/g) {  #match all but whitespaces
               unless ($nokey{$_}) {
                   $keywords{$_}=1;
             }              }
         }          }
         if ($chparms) {  
     $scrout.='<p><b>Obsolete parameters or stored values:</b> '.  
                      $chparms;  
         }  
   
 # ------------------------------------------------------- Now have all metadata  
   
         $scrout.=  
      '<form name="pubform" action="/adm/publish" method="post">'.  
        '<p><input type="submit" value="Finalize Publication" /></p>'.  
           &hiddenfield('phase','two').  
           &hiddenfield('filename',$ENV{'form.filename'}).  
   &hiddenfield('allmeta',&Apache::lonnet::escape($allmeta)).  
           &hiddenfield('dependencies',join(',',keys %allow)).  
           &textfield('Title','title',$metadatafields{'title'}).  
           &textfield('Author(s)','author',$metadatafields{'author'}).  
   &textfield('Subject','subject',$metadatafields{'subject'});  
   
 # --------------------------------------------------- Scan content for keywords      }
               
       foreach my $addkey (split(/[\"\'\,\;]/,$metadatafields{'keywords'})) {
    $addkey=~s/\s+/ /g;
    $addkey=~s/^\s//;
    $addkey=~s/\s$//;
    if ($addkey=~/\w/) {
       $keywords{$addkey}=1;
    }
       }
   # --------------------------------------------------- Now we also have keywords
   # =============================================================================
   # interactive mode html goes into $intr_scrout
   # batch mode throws away this HTML
   # additionally all of the field functions have a by product of setting
   #   $env{'from.'..} so that it can be used by the phase two handler in
   #    batch mode
   
       my $intr_scrout.='<br />'
                       .'<form name="pubform" action="/adm/publish" method="post">';
       unless ($env{'form.makeobsolete'}) {
          $intr_scrout.='<p class="LC_warning">'
                       .&mt('Searching for your resource will be based on the following metadata. Please provide as much data as possible.')
                       .'</p>'
                       .'<p><input type="submit" value="'
                       .&mt('Finalize Publication')
                       .'" /></p>';
       }
       $intr_scrout.=&Apache::lonhtmlcommon::start_pick_box();
       $intr_scrout.=
    &hiddenfield('phase','two').
    &hiddenfield('filename',$env{'form.filename'}).
    &hiddenfield('allmeta',&escape($allmeta)).
    &hiddenfield('dependencies',join(',',keys %allow));
       unless ($env{'form.makeobsolete'}) {
          $intr_scrout.=
    &textfield('Title','title',$metadatafields{'title'}).
    &textfield('Author(s)','author',$metadatafields{'author'}).
    &textfield('Subject','subject',$metadatafields{'subject'});
    # --------------------------------------------------- Scan content for keywords
   
         my $keywords_help = Apache::loncommon::help_open_topic("Publishing_Keywords");      my $keywords_help = &Apache::loncommon::help_open_topic("Publishing_Keywords");
  my $keywordout=<<"END";      my $keywordout=<<"END";
 <script>  <script>
 function checkAll(field)  function checkAll(field) {
 {  
     for (i = 0; i < field.length; i++)      for (i = 0; i < field.length; i++)
         field[i].checked = true ;          field[i].checked = true ;
 }  }
   
 function uncheckAll(field)  function uncheckAll(field) {
 {  
     for (i = 0; i < field.length; i++)      for (i = 0; i < field.length; i++)
         field[i].checked = false ;          field[i].checked = false ;
 }  }
 </script>  </script>
 <p><b>Keywords: $keywords_help</b>   
 <input type="button" value="check all" onclick="javascript:checkAll(document.pubform.keywords)">   
 <input type="button" value="uncheck all" onclick="javascript:uncheckAll(document.pubform.keywords)">   
 <br />  
 END  END
         $keywordout.='<table border=2><tr>';      $keywordout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Keywords'))
         my $colcount=0;                  .$keywords_help
         my %keywords=();                  .'<input type="button" value="'.&mt('check all').'" onclick="javascript:checkAll(document.pubform.keywords)" />'
                           .'<input type="button" value="'.&mt('uncheck all').'" onclick="javascript:uncheckAll(document.pubform.keywords)" />'
  if (length($content)<500000) {                  .'</p><br />'
     my $textonly=$content;                  .&Apache::loncommon::start_data_table();
             $textonly=~s/\<script[^\<]+\<\/script\>//g;      my $cols_per_row = 10;
             $textonly=~s/\<m\>[^\<]+\<\/m\>//g;      my $colcount=0;
             $textonly=~s/\<[^\>]*\>//g;      my $wordcount=0;
             $textonly=~tr/A-Z/a-z/;      my $numkeywords = scalar(keys(%keywords));
             $textonly=~s/[\$\&][a-z]\w*//g;  
             $textonly=~s/[^a-z\s]//g;      foreach my $word (sort(keys(%keywords))) {
           if ($colcount == 0) {
             foreach ($textonly=~m/(\w+)/g) {              $keywordout .= &Apache::loncommon::start_data_table_row();
  unless ($nokey{$_}) {          }
                    $keywords{$_}=1;          $colcount++;
                 }           $wordcount++;
           if (($wordcount == $numkeywords) && ($colcount < $cols_per_row)) {
               my $colspan = 1+$cols_per_row-$colcount;
               $keywordout .= '<td colspan="'.$colspan.'">';
           } else {
               $keywordout .= '<td>';
           }
           $keywordout.='<label><input type="checkbox" name="keywords" value="'.$word.'"';
           if ($metadatafields{'keywords'}) {
               if ($metadatafields{'keywords'}=~/\Q$word\E/) {
                   $keywordout.=' checked="checked"';
                   $env{'form.keywords'}.=$word.',';
             }              }
           } elsif (&Apache::loncommon::keyword($word)) {
               $keywordout.=' checked="checked"';
               $env{'form.keywords'}.=$word.',';
           }
           $keywordout.=' />'.$word.'</label></td>';
           if ($colcount == $cols_per_row) {
               $keywordout.=&Apache::loncommon::end_data_table_row();
               $colcount=0;
         }          }
       }
       if ($colcount > 0) {
           $keywordout .= &Apache::loncommon::end_data_table_row();
       }
   
                   $env{'form.keywords'}=~s/\,$//;
             foreach (split(/\W+/,$metadatafields{'keywords'})) {  
  $keywords{$_}=1;  
             }  
   
             foreach (sort keys %keywords) {      $keywordout.=&Apache::loncommon::end_data_table_row()
                 $keywordout.='<td><input type=checkbox name="keywords" value="'.$_.'"';                   .&Apache::loncommon::end_data_table()
                 if ($metadatafields{'keywords'}) {                   .&Apache::lonhtmlcommon::row_closure();
                    if ($metadatafields{'keywords'}=~/$_/) {   
                       $keywordout.=' checked';   
                    }  
         } elsif (&Apache::loncommon::keyword($_)) {  
             $keywordout.=' checked';  
                 }   
                 $keywordout.='>'.$_.'</td>';  
                 if ($colcount>10) {  
     $keywordout.="</tr><tr>\n";  
                     $colcount=0;  
                 }  
                 $colcount++;  
             }  
           
  $keywordout.='</tr></table>';  
   
         $scrout.=$keywordout;      $intr_scrout.=$keywordout;
   
         $scrout.=&textfield('Additional Keywords','addkey','');      $intr_scrout.=&textfield('Additional Keywords','addkey','');
   
         $scrout.=&textfield('Notes','notes',$metadatafields{'notes'});      $intr_scrout.=&textfield('Notes','notes',$metadatafields{'notes'});
   
         $scrout.=      $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Abstract'))
              '<p><b>Abstract:</b><br><textarea cols=80 rows=5 name=abstract>'.                   .'<textarea cols="80" rows="5" name="abstract">'
               $metadatafields{'abstract'}.'</textarea>';                   .$metadatafields{'abstract'}
                    .'</textarea>'
                    .&Apache::lonhtmlcommon::row_closure();
   
  $source=~/\.(\w+)$/;      $source=~/\.(\w+)$/;
   
  $scrout.=&hiddenfield('mime',$1);      $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Grade Levels'))
                    .&mt('Lowest Grade Level:').'&nbsp;'
                    .&select_level_form($metadatafields{'lowestgradelevel'},'lowestgradelevel')
   #                .&Apache::lonhtmlcommon::row_closure();
   #   $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Highest Grade Level'))
                    .' '.&mt('Highest Grade Level:').'&nbsp;'
                    .&select_level_form($metadatafields{'highestgradelevel'},'highestgradelevel')
                    .&Apache::lonhtmlcommon::row_closure();
   
         $scrout.=&selectbox('Language','language',      $intr_scrout.=&textfield('Standards','standards',$metadatafields{'standards'});
                             $metadatafields{'language'},  
     \&Apache::loncommon::languagedescription,      $intr_scrout.=&hiddenfield('mime',$1);
     (&Apache::loncommon::languageids),  
       my $defaultlanguage=$metadatafields{'language'};
       $defaultlanguage =~ s/\s*notset\s*//g;
       $defaultlanguage =~ s/^,\s*//g;
       $defaultlanguage =~ s/,\s*$//g;
   
       $intr_scrout.=&selectbox('Language','language',
        $defaultlanguage,
        \&Apache::loncommon::languagedescription,
        (&Apache::loncommon::languageids),
      );       );
   
         unless ($metadatafields{'creationdate'}) {      unless ($metadatafields{'creationdate'}) {
     $metadatafields{'creationdate'}=time;   $metadatafields{'creationdate'}=time;
         }      }
         $scrout.=&hiddenfield('creationdate',$metadatafields{'creationdate'});      $intr_scrout.=&hiddenfield('creationdate',
          &Apache::lonmysql::unsqltime($metadatafields{'creationdate'}));
   
         $scrout.=&hiddenfield('lastrevisiondate',time);      $intr_scrout.=&hiddenfield('lastrevisiondate',time);
   
          my $pubowner_last;
  $scrout.=&textfield('Publisher/Owner','owner',      if ($style eq 'prv') {
                             $metadatafields{'owner'});          $pubowner_last = 1;
 # --------------------------------------------------- Correct copyright for rat              }
       $intr_scrout.=&textfield('Publisher/Owner','owner',
        $metadatafields{'owner'},$pubowner_last);
   
     if ($style eq 'rat') {  # ---------------------------------------------- Retrofix for unused copyright
  if ($metadatafields{'copyright'} eq 'public') {       if ($metadatafields{'copyright'} eq 'free') {
     delete $metadatafields{'copyright'};   $metadatafields{'copyright'}='default';
    $metadatafields{'sourceavail'}='open';
       }
       if ($metadatafields{'copyright'} eq 'priv') {
           $metadatafields{'copyright'}='domain';
       }
   # ------------------------------------------------ Dial in reasonable defaults
       my $defaultoption=$metadatafields{'copyright'};
       unless ($defaultoption) { $defaultoption='default'; }
       my $defaultsourceoption=$metadatafields{'sourceavail'};
       unless ($defaultsourceoption) { $defaultsourceoption='closed'; }
       unless ($style eq 'prv') {
   # -------------------------------------------------- Correct copyright for rat.
    if ($style eq 'rat') {
   # -------------------------------------- Retrofix for non-applicable copyright
       if ($metadatafields{'copyright'} eq 'public') { 
    delete $metadatafields{'copyright'};
    $defaultoption='default';
       }
       $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
        $defaultoption,
        \&Apache::loncommon::copyrightdescription,
       (grep !/^(public|priv)$/,(&Apache::loncommon::copyrightids)));
    } else {
       $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
        $defaultoption,
        \&Apache::loncommon::copyrightdescription,
        (grep !/^priv$/,(&Apache::loncommon::copyrightids)));
  }   }
         $scrout.=&selectbox('Copyright/Distribution','copyright',   my $copyright_help =
                             $metadatafields{'copyright'},      &Apache::loncommon::help_open_topic('Publishing_Copyright');
     \&Apache::loncommon::copyrightdescription,          my $replace=&mt('Copyright/Distribution:');
      (grep !/^public$/,(&Apache::loncommon::copyrightids)));   $intr_scrout =~ s/$replace/$replace.' '.$copyright_help/ge;
   
    $intr_scrout.=&text_with_browse_field('Custom Distribution File','customdistributionfile',$metadatafields{'customdistributionfile'},'rights');
    $intr_scrout.=&selectbox('Source Distribution','sourceavail',
    $defaultsourceoption,
    \&Apache::loncommon::source_copyrightdescription,
    (&Apache::loncommon::source_copyrightids));
   # $intr_scrout.=&text_with_browse_field('Source Custom Distribution File','sourcerights',$metadatafields{'sourcerights'},'rights');
    my $uctitle=&mt('Obsolete');
           my $obsolete_checked=($metadatafields{'obsolete'})?' checked="checked"':'';
           $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title($uctitle)
                        .'<input type="checkbox" name="obsolete"'.$obsolete_checked.' />'
                        .&Apache::lonhtmlcommon::row_closure(1);
           $intr_scrout.=&text_with_browse_field('Suggested Replacement for Obsolete File',
       'obsoletereplacement',
       $metadatafields{'obsoletereplacement'},'',1);
       } else {
    $intr_scrout.=&hiddenfield('copyright','private');
     }      }
     else {     } else {
         $scrout.=&selectbox('Copyright/Distribution','copyright',         $intr_scrout.=
                             $metadatafields{'copyright'},   &hiddenfield('title',$metadatafields{'title'}).
     \&Apache::loncommon::copyrightdescription,   &hiddenfield('author',$metadatafields{'author'}).
      (&Apache::loncommon::copyrightids));   &hiddenfield('subject',$metadatafields{'subject'}).
    &hiddenfield('keywords',$metadatafields{'keywords'}).
    &hiddenfield('abstract',$metadatafields{'abstract'}).
    &hiddenfield('notes',$metadatafields{'notes'}).
    &hiddenfield('mime',$metadatafields{'mime'}).
    &hiddenfield('creationdate',$metadatafields{'creationdate'}).
    &hiddenfield('lastrevisiondate',time).
    &hiddenfield('owner',$metadatafields{'owner'}).
    &hiddenfield('lowestgradelevel',$metadatafields{'lowestgradelevel'}).
    &hiddenfield('standards',$metadatafields{'standards'}).
    &hiddenfield('highestgradelevel',$metadatafields{'highestgradelevel'}).
    &hiddenfield('language',$metadatafields{'language'}).
    &hiddenfield('copyright',$metadatafields{'copyright'}).
    &hiddenfield('sourceavail',$metadatafields{'sourceavail'}).
    &hiddenfield('customdistributionfile',$metadatafields{'customdistributionfile'}).
    &hiddenfield('obsolete',1).
    &text_with_browse_field('Suggested Replacement for Obsolete File',
       'obsoletereplacement',
       $metadatafields{'obsoletereplacement'},'',1);
      }
       if (!$batch) {
    $scrout.=$intr_scrout
               .&Apache::lonhtmlcommon::end_pick_box()
               .'<p><input type="submit" value="'
       .&mt($env{'form.makeobsolete'}?'Make Obsolete':'Finalize Publication')
               .'" /></p>'
               .'</form>';
     }      }
       return($scrout,0);
     my $copyright_help = Apache::loncommon::help_open_topic("Publishing_Copyright");  
     $scrout =~ s/DISTRIBUTION:/'DISTRIBUTION: ' . $copyright_help/ge;  
     return $scrout.  
       '<p><input type="submit" value="Finalize Publication" /></p></form>';  
 }  }
   
 # -------------------------------------------------------- Publication Step Two  #########################################
   #########################################
   
 sub phasetwo {  =pod 
   
     my ($source,$target,$style,$distarget)=@_;  =item B<phasetwo>
     my $logfile;  
     my $scrout='';  
     unless ($logfile=Apache::File->new('>>'.$source.'.log')) {  
  return   
          '<font color=red>No write permission to user directory, FAIL</font>';  
     }  
     print $logfile   
 "\n================= Publish ".localtime()." Phase Two  ================\n";  
   
      %metadatafields=();  Render second interface showing status of publication steps.
      %metadatakeys=();  This is publication step two.
   
      &metaeval(&Apache::lonnet::unescape($ENV{'form.allmeta'}));  Parameters:
   
      $metadatafields{'title'}=$ENV{'form.title'};  =over 4
      $metadatafields{'author'}=$ENV{'form.author'};  
      $metadatafields{'subject'}=$ENV{'form.subject'};  
      $metadatafields{'notes'}=$ENV{'form.notes'};  
      $metadatafields{'abstract'}=$ENV{'form.abstract'};  
      $metadatafields{'mime'}=$ENV{'form.mime'};  
      $metadatafields{'language'}=$ENV{'form.language'};  
      $metadatafields{'creationdate'}=$ENV{'form.creationdate'};  
      $metadatafields{'lastrevisiondate'}=$ENV{'form.lastrevisiondate'};  
      $metadatafields{'owner'}=$ENV{'form.owner'};  
      $metadatafields{'copyright'}=$ENV{'form.copyright'};  
      $metadatafields{'dependencies'}=$ENV{'form.dependencies'};  
   
      my $allkeywords=$ENV{'form.addkey'};  
      if (exists($ENV{'form.keywords'}) && (ref($ENV{'form.keywords'}))) {  
          my @Keywords = @{$ENV{'form.keywords'}};  
          foreach (@Keywords) {  
              $allkeywords.=','.$_;  
          }  
      }  
      $allkeywords=~s/\W+/\,/;  
      $allkeywords=~s/^\,//;  
      $metadatafields{'keywords'}=$allkeywords;  
    
      {  
        print $logfile "\nWrite metadata file for ".$source;  
        my $mfh;  
        unless ($mfh=Apache::File->new('>'.$source.'.meta')) {  
  return   
          '<font color=red>Could not write metadata, FAIL</font>';  
        }  
        foreach (sort keys %metadatafields) {  
  unless ($_=~/\./) {  
            my $unikey=$_;  
            $unikey=~/^([A-Za-z]+)/;  
            my $tag=$1;  
            $tag=~tr/A-Z/a-z/;  
            print $mfh "\n\<$tag";  
            foreach (split(/\,/,$metadatakeys{$unikey})) {  
                my $value=$metadatafields{$unikey.'.'.$_};  
                $value=~s/\"/\'\'/g;  
                print $mfh ' '.$_.'="'.$value.'"';  
            }  
    print $mfh '>'.  
      &HTML::Entities::encode($metadatafields{$unikey})  
        .'</'.$tag.'>';  
          }  
        }  
        $scrout.='<p>Wrote Metadata';  
        print $logfile "\nWrote metadata";  
      }  
   
 # -------------------------------- Synchronize entry with SQL metadata database  =item I<$source>
   my $warning;  
   
   unless ($metadatafields{'copyright'} eq 'priv') {  =item I<$target>
   
     my $dbh;  =item I<$style>
     {  
  unless (  =item I<$distarget>
  $dbh = DBI->connect("DBI:mysql:loncapa","www",  
     $Apache::lonnet::perlvar{'lonSqlAccess'},{ RaiseError =>0,PrintError=>0})  =back
  ) {   
     $warning='<font color=red>WARNING: Cannot connect to '.  Returns:
  'database!</font>';  
  }  =over 4
  else {  
     my %sqldatafields;  =item integer
     $sqldatafields{'url'}=$distarget;  
     my $sth=$dbh->prepare(  0: fail
   'delete from metadata where url like binary'.  1: success
   '"'.$sqldatafields{'url'}.'"');  
     $sth->execute();  =cut
     foreach ('title','author','subject','keywords','notes','abstract',  
      'mime','language','creationdate','lastrevisiondate','owner',  #'stupid emacs
      'copyright') {  #########################################
  my $field=$metadatafields{$_}; $field=~s/\"/\'\'/g;   #########################################
  $sqldatafields{$_}=$field;  sub phasetwo {
     }  
           my ($r,$source,$target,$style,$distarget,$batch)=@_;
     $sth=$dbh->prepare('insert into metadata values ('.      $source=~s/\/+/\//g;
        '"'.delete($sqldatafields{'title'}).'"'.','.      $target=~s/\/+/\//g;
        '"'.delete($sqldatafields{'author'}).'"'.','.  #
        '"'.delete($sqldatafields{'subject'}).'"'.','.  # Unless trying to get rid of something, check name validity
        '"'.delete($sqldatafields{'url'}).'"'.','.  #
        '"'.delete($sqldatafields{'keywords'}).'"'.','.      unless ($env{'form.obsolete'}) {
        '"'.'current'.'"'.','.   if ($target=~/(\_\_\_|\&\&\&|\:\:\:)/) {
        '"'.delete($sqldatafields{'notes'}).'"'.','.      $r->print('<span class="LC_error">'.
        '"'.delete($sqldatafields{'abstract'}).'"'.','.        &mt('Unsupported character combination [_1] in filename, FAIL.',"<tt>'.$1.'</tt>").
        '"'.delete($sqldatafields{'mime'}).'"'.','.        '</span>');
        '"'.delete($sqldatafields{'language'}).'"'.','.      return 0;
        '"'.   }
        sqltime(delete($sqldatafields{'creationdate'}))   unless ($target=~/\.(\w+)$/) {
        .'"'.','.      $r->print('<span class="LC_error">'.&mt('No valid extension found in filename, FAIL').'</span>');
        '"'.      return 0;
        sqltime(delete(   }
        $sqldatafields{'lastrevisiondate'})).'"'.','.   if ($target=~/\.(\d+)\.(\w+)$/) {
        '"'.delete($sqldatafields{'owner'}).'"'.','.      $r->print('<span class="LC_error">'.&mt('Filename of resource contains internal version number. Cannot publish such resources, FAIL').'</span>');
        '"'.delete(      return 0;
        $sqldatafields{'copyright'}).'"'.')');  
     $sth->execute();  
     $dbh->disconnect;  
     $scrout.='<p>Synchronized SQL metadata database';  
     print $logfile "\nSynchronized SQL metadata database";  
  }   }
     }      }
   
 } else {  #
     $scrout.='<p>Private Publication - did not synchronize database';  # End name check
     print $logfile "\nPrivate: Did not synchronize data into ".  #
  "SQL metadata database";      $distarget=~s/\/+/\//g;
 }      my $logfile;
 # ----------------------------------------------------------- Copy old versions      unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
       $r->print(
 if (-e $target) {          '<span class="LC_error">'.
     my $filename;   &mt('No write permission to user directory, FAIL').'</span>');
     my $maxversion=0;          return 0;
     $target=~/(.*)\/([^\/]+)\.(\w+)$/;  
     my $srcf=$2;  
     my $srct=$3;  
     my $srcd=$1;  
     unless ($srcd=~/^\/home\/httpd\/html\/res/) {  
  print $logfile "\nPANIC: Target dir is ".$srcd;  
         return "<font color=red>Invalid target directory, FAIL</font>";  
     }      }
     opendir(DIR,$srcd);      
     while ($filename=readdir(DIR)) {      if ($source =~ /\.rights$/) {
        if ($filename=~/$srcf\.(\d+)\.$srct$/) {   $r->print('<p><span class="LC_warning">'.&mt('Warning: It can take up to 1 hour for rights changes to fully propagate.').'</span></p>');
    $maxversion=($1>$maxversion)?$1:$maxversion;  
        }  
     }      }
     closedir(DIR);  
     $maxversion++;  
     $scrout.='<p>Creating old version '.$maxversion;  
     print $logfile "\nCreating old version ".$maxversion;  
   
     my $copyfile=$srcd.'/'.$srcf.'.'.$maxversion.'.'.$srct;      print $logfile 
           "\n================= Publish ".localtime()." Phase Two  ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
       
       %metadatafields=();
       %metadatakeys=();
   
       &metaeval(&unescape($env{'form.allmeta'}));
       
       $metadatafields{'title'}=$env{'form.title'};
       $metadatafields{'author'}=$env{'form.author'};
       $metadatafields{'subject'}=$env{'form.subject'};
       $metadatafields{'notes'}=$env{'form.notes'};
       $metadatafields{'abstract'}=$env{'form.abstract'};
       $metadatafields{'mime'}=$env{'form.mime'};
       $metadatafields{'language'}=$env{'form.language'};
       $metadatafields{'creationdate'}=$env{'form.creationdate'};
       $metadatafields{'lastrevisiondate'}=$env{'form.lastrevisiondate'};
       $metadatafields{'owner'}=$env{'form.owner'};
       $metadatafields{'copyright'}=$env{'form.copyright'};
       $metadatafields{'standards'}=$env{'form.standards'};
       $metadatafields{'lowestgradelevel'}=$env{'form.lowestgradelevel'};
       $metadatafields{'highestgradelevel'}=$env{'form.highestgradelevel'};
       $metadatafields{'customdistributionfile'}=
                                    $env{'form.customdistributionfile'};
       $metadatafields{'sourceavail'}=$env{'form.sourceavail'};
       $metadatafields{'obsolete'}=$env{'form.obsolete'};
       $metadatafields{'obsoletereplacement'}=
                           $env{'form.obsoletereplacement'};
       $metadatafields{'dependencies'}=$env{'form.dependencies'};
       $metadatafields{'modifyinguser'}=$env{'user.name'}.':'.
                                    $env{'user.domain'};
       $metadatafields{'authorspace'}=$cuname.':'.$cudom;
       $metadatafields{'domain'}=$cudom;
       
       my $allkeywords=$env{'form.addkey'};
       if (exists($env{'form.keywords'})) {
           if (ref($env{'form.keywords'})) {
               $allkeywords .= ','.join(',',@{$env{'form.keywords'}});
           } else {
               $allkeywords .= ','.$env{'form.keywords'};
           }
       }
       $allkeywords=~s/[\"\']//g;
       $allkeywords=~s/\s*[\;\,]\s*/\,/g;
       $allkeywords=~s/\s+/ /g;
       $allkeywords=~s/^[ \,]//;
       $allkeywords=~s/[ \,]$//;
       $metadatafields{'keywords'}=$allkeywords;
       
   # check if custom distribution file is specified
       if ($metadatafields{'copyright'} eq 'custom') {
    my $file=$metadatafields{'customdistributionfile'};
    unless ($file=~/\.rights$/) {
               $r->print(
                   '<span class="LC_error">'.&mt('No valid custom distribution rights file specified, FAIL').
    '</span>');
       return 0;
           }
       }
       {
           print $logfile "\nWrite metadata file for ".$source;
           my $mfh;
           unless ($mfh=Apache::File->new('>'.$source.'.meta')) {
               $r->print( 
                   '<span class="LC_error">'.&mt('Could not write metadata, FAIL').
    '</span>');
       return 0;
           }
           foreach (sort keys %metadatafields) {
               unless ($_=~/\./) {
                   my $unikey=$_;
                   $unikey=~/^([A-Za-z]+)/;
                   my $tag=$1;
                   $tag=~tr/A-Z/a-z/;
                   print $mfh "\n\<$tag";
                   foreach (split(/\,/,$metadatakeys{$unikey})) {
                       my $value=$metadatafields{$unikey.'.'.$_};
                       $value=~s/\"/\'\'/g;
                       print $mfh ' '.$_.'="'.$value.'"';
                   }
                   print $mfh '>'.
                       &HTML::Entities::encode($metadatafields{$unikey},'<>&"')
                           .'</'.$tag.'>';
               }
           }
           $r->print('<p>'.&mt('Wrote Metadata').'</p>');
           print $logfile "\nWrote metadata";
       }
       
   # -------------------------------- Synchronize entry with SQL metadata database
   
       $metadatafields{'url'} = $distarget;
       $metadatafields{'version'} = 'current';
   
       my ($error,$success) = &store_metadata(%metadatafields);
       if ($success) {
    $r->print('<p>'.&mt('Synchronized SQL metadata database').'</p>');
    print $logfile "\nSynchronized SQL metadata database";
       } else {
    $r->print($error);
    print $logfile "\n".$error;
       }
   # --------------------------------------------- Delete author resource messages
       my $delresult=&Apache::lonmsg::del_url_author_res_msg($target); 
       $r->print('<p>'.&mt('Removing error messages:').' '.$delresult.'</p>');
       print $logfile "\nRemoving error messages: $delresult";
   # ----------------------------------------------------------- Copy old versions
      
       if (-e $target) {
           my $filename;
           my $maxversion=0;
           $target=~/(.*)\/([^\/]+)\.(\w+)$/;
           my $srcf=$2;
           my $srct=$3;
           my $srcd=$1;
           my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
           unless ($srcd=~/^\Q$docroot\E\/res/) {
               print $logfile "\nPANIC: Target dir is ".$srcd;
               $r->print(
    "<span class=\"LC_error\">".&mt('Invalid target directory, FAIL')."</span>");
       return 0;
           }
           opendir(DIR,$srcd);
           while ($filename=readdir(DIR)) {
               if (-l $srcd.'/'.$filename) {
                   unlink($srcd.'/'.$filename);
                   unlink($srcd.'/'.$filename.'.meta');
               } else {
                   if ($filename=~/^\Q$srcf\E\.(\d+)\.\Q$srct\E$/) {
                       $maxversion=($1>$maxversion)?$1:$maxversion;
                   }
               }
           }
           closedir(DIR);
           $maxversion++;
           $r->print('<p>'.&mt('Creating old version [_1]',$maxversion).'</p>');
           print $logfile "\nCreating old version ".$maxversion."\n";
           
           my $copyfile=$srcd.'/'.$srcf.'.'.$maxversion.'.'.$srct;
           
         if (copy($target,$copyfile)) {          if (copy($target,$copyfile)) {
     print $logfile "Copied old target to ".$copyfile."\n";      print $logfile "Copied old target to ".$copyfile."\n";
             $scrout.='<p>Copied old target file';              $r->print('<p>'.&mt('Copied old target file').'</p>');
         } else {          } else {
     print $logfile "Unable to write ".$copyfile.':'.$!."\n";      print $logfile "Unable to write ".$copyfile.':'.$!."\n";
            return "<font color=red>Failed to copy old target, $!, FAIL</font>";              $r->print("<span class=\"LC_error\">".&mt('Failed to copy old target').
    ", $!, ".&mt('FAIL')."</span>");
       return 0;
         }          }
           
 # --------------------------------------------------------------- Copy Metadata  # --------------------------------------------------------------- Copy Metadata
   
  $copyfile=$copyfile.'.meta';   $copyfile=$copyfile.'.meta';
           
         if (copy($target.'.meta',$copyfile)) {          if (copy($target.'.meta',$copyfile)) {
     print $logfile "Copied old target metadata to ".$copyfile."\n";      print $logfile "Copied old target metadata to ".$copyfile."\n";
             $scrout.='<p>Copied old metadata';              $r->print('<p>'.&mt('Copied old metadata').'</p>')
         } else {          } else {
     print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";      print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
             if (-e $target.'.meta') {              if (-e $target.'.meta') {
                return                   $r->print( 
        "<font color=red>Failed to write old metadata copy, $!, FAIL</font>";                      "<span class=\"LC_error\">".
   &mt('Failed to write old metadata copy').", $!, ".&mt('FAIL')."</span>");
    return 0;
     }      }
         }          }
           
           
 } else {      } else {
     $scrout.='<p>Initial version';          $r->print('<p>'.&mt('Initial version').'</p>');
     print $logfile "\nInitial version";          print $logfile "\nInitial version";
 }      }
   
 # ---------------------------------------------------------------- Write Source  # ---------------------------------------------------------------- Write Source
  my $copyfile=$target;      my $copyfile=$target;
       
            my @parts=split(/\//,$copyfile);      my @parts=split(/\//,$copyfile);
            my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";      my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
       
            my $count;      my $count;
            for ($count=5;$count<$#parts;$count++) {      for ($count=5;$count<$#parts;$count++) {
                $path.="/$parts[$count]";          $path.="/$parts[$count]";
                if ((-e $path)!=1) {          if ((-e $path)!=1) {
                    print $logfile "\nCreating directory ".$path;              print $logfile "\nCreating directory ".$path;
                    $scrout.='<p>Created directory '.$parts[$count];              mkdir($path,0777);
    mkdir($path,0777);              $r->print('<p>'
                }                       .&mt('Created directory [_1]'
            }                           ,'<span class="LC_filename">'.$parts[$count].'</span>')
                        .'</p>'
         if (copy($source,$copyfile)) {              );
     print $logfile "Copied original source to ".$copyfile."\n";  
             $scrout.='<p>Copied source file';  
         } else {  
     print $logfile "Unable to write ".$copyfile.':'.$!."\n";  
             return "<font color=red>Failed to copy source, $!, FAIL</font>";  
         }          }
       }
       
       if (copy($source,$copyfile)) {
           print $logfile "\nCopied original source to ".$copyfile."\n";
           $r->print('<p>'.&mt('Copied source file').'</p>');
       } else {
           print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";
           $r->print("<span class=\"LC_error\">".
       &mt('Failed to copy source').", $!, ".&mt('FAIL')."</span>");
    return 0;
       }
       
 # --------------------------------------------------------------- Copy Metadata  # --------------------------------------------------------------- Copy Metadata
   
         $copyfile=$copyfile.'.meta';      $copyfile=$copyfile.'.meta';
       
         if (copy($source.'.meta',$copyfile)) {      if (copy($source.'.meta',$copyfile)) {
     print $logfile "Copied original metadata to ".$copyfile."\n";          print $logfile "\nCopied original metadata to ".$copyfile."\n";
             $scrout.='<p>Copied metadata';          $r->print('<p>'.&mt('Copied metadata').'</p>');
         } else {      } else {
     print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";          print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n";
             return           $r->print(
           "<font color=red>Failed to write metadata copy, $!, FAIL</font>";              "<span class=\"LC_error\">".&mt('Failed to write metadata copy').", $!, ".&mt('FAIL')."</span>");
         }   return 0;
       }
       $r->rflush;
   
 # --------------------------------------------------- Send update notifications  # ------------------------------------------------------------- Trigger updates
       push(@{$modified_urls},[$target,$source]);
       unless ($registered_cleanup) {
    $r->register_cleanup(\&notify);
    $registered_cleanup=1;
       }
   
 {  # ---------------------------------------------------------- Clear local caches
       my $thisdistarget=$target;
       $thisdistarget=~s/^\Q$docroot\E//;
       &Apache::lonnet::devalidate_cache_new('resversion',$target);
       &Apache::lonnet::devalidate_cache_new('meta',
    &Apache::lonnet::declutter($thisdistarget));
   
   # ------------------------------------------------------------- Everything done
       $logfile->close();
       $r->print('<p class="LC_success">'.&mt('Done').'</p>');
   
     my $filename;  # ------------------------------------------------ Provide link to new resource
        unless ($batch) {
     $target=~/(.*)\/([^\/]+)$/;          my $thissrc=$source;
     my $srcf=$2;          $thissrc=~s{^/home/($match_username)/public_html}{/priv/$1};
     opendir(DIR,$1);          
     while ($filename=readdir(DIR)) {          my $thissrcdir=$thissrc;
        if ($filename=~/$srcf\.(\w+)$/) {          $thissrcdir=~s/\/[^\/]+$/\//;
    my $subhost=$1;          
            if ($subhost ne 'meta') {          
        $scrout.='<p>Notifying host '.$subhost.':';          $r->print(
                print $logfile "\nNotifying host '.$subhost.':'";             '<hr /><a href="'.$thisdistarget.'"><font size="+2">'.
                my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);             &mt('View Published Version').'</font></a>'.
                $scrout.=$reply;             '<p><a href="'.$thissrc.'"><font size="+2">'.
                print $logfile $reply;                  &mt('Back to Source').'</font></a></p>'.
            }             '<p><a href="'.$thissrcdir.
        }                     '"><font size="+2">'.
     &mt('Back to Source Directory').'</font></a></p>');
     }      }
     closedir(DIR);      return 1;
   
 }  }
   
   # =============================================================== Notifications
   sub notify {  
   # --------------------------------------------------- Send update notifications
       foreach my $targetsource (@{$modified_urls}){
    my ($target,$source)=@{$targetsource};
    my $logfile=Apache::File->new('>>'.$source.'.log');
    print $logfile "\nCleanup phase: Notifications\n";
    my @subscribed=&get_subscribed_hosts($target);
    foreach my $subhost (@subscribed) {
       print $logfile "\nNotifying host ".$subhost.':';
       my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
       print $logfile $reply;
    }
 # ---------------------------------------- Send update notifications, meta only  # ---------------------------------------- Send update notifications, meta only
    my @subscribedmeta=&get_subscribed_hosts("$target.meta");
 {   foreach my $subhost (@subscribedmeta) {
       print $logfile "\nNotifying host for metadata only ".$subhost.':';
     my $filename;      my $reply=&Apache::lonnet::critical('update:'.$target.'.meta',
     $subhost);
     $target=~/(.*)\/([^\/]+)$/;      print $logfile $reply;
     my $srcf=$2.'.meta';   } 
     opendir(DIR,$1);  # --------------------------------------------------- Notify subscribed courses
     while ($filename=readdir(DIR)) {   my %courses=&coursedependencies($target);
        if ($filename=~/$srcf\.(\w+)$/) {   my $now=time;
    my $subhost=$1;   foreach (keys %courses) {
            if ($subhost ne 'meta') {      print $logfile "\nNotifying course ".$_.':';
        $scrout.=      my ($cdom,$cname)=split(/\_/,$_);
                 '<p>Notifying host for metadata only '.$subhost.':';      my $reply=&Apache::lonnet::cput
                print $logfile    ('versionupdate',{$target => $now},$cdom,$cname);
                 "\nNotifying host for metadata only '.$subhost.':'";      print $logfile $reply;
                my $reply=&Apache::lonnet::critical(   }
                                 'update:'.$target.'.meta',$subhost);   print $logfile "\n============ Done ============\n";
                $scrout.=$reply;   $logfile->close();
                print $logfile $reply;                
            }  
        }  
     }      }
     closedir(DIR);      if ($lock) { &Apache::lonnet::remove_lock($lock); }
       return OK;
 }  }
   
 # ------------------------------------------------ Provide link to new resource  #########################################
   
     my $thisdistarget=$target;  
     $thisdistarget=~s/^$docroot//;  
   
     my $thissrc=$source;  
     $thissrc=~s/^\/home\/(\w+)\/public_html/\/priv\/$1/;  
   
     my $thissrcdir=$thissrc;  sub batchpublish {
     $thissrcdir=~s/\/[^\/]+$/\//;      my ($r,$srcfile,$targetfile)=@_;
       #publication pollutes %env with form.* values
       my %oldenv=%env;
       $srcfile=~s/\/+/\//g;
       $targetfile=~s/\/+/\//g;
       my $thisdisfn=$srcfile;
       $thisdisfn=~s/\/home\/korte\/public_html\///;
       $srcfile=~s/\/+/\//g;
   
       my $docroot=$r->dir_config('lonDocRoot');
       my $thisdistarget=$targetfile;
       $thisdistarget=~s/^\Q$docroot\E//;
   
   
       %metadatafields=();
       %metadatakeys=();
       $srcfile=~/\.(\w+)$/;
       my $thistype=$1;
   
   
     return $warning.$scrout.      my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
       '<hr><a href="'.$thisdistarget.'"><font size=+2>View Published Version</font></a>'.       
       '<p><a href="'.$thissrc.'"><font size=+2>Back to Source</font></a>'.      $r->print('<h2>'
       '<p><a href="'.$thissrcdir.               .&mt('Publishing [_1]'
       '"><font size=+2>Back to Source Directory</font></a>';                   ,'<span class="LC_filename">'.$thisdisfn.'</span>')
                .'</h2>'
       );
   
   # phase one takes
   #  my ($source,$target,$style,$batch)=@_;
       my ($outstring,$error)=&publish($srcfile,$targetfile,$thisembstyle,1);
       $r->print('<p>'.$outstring.'</p>');
   # phase two takes
   # my ($source,$target,$style,$distarget,batch)=@_;
   # $env{'form.allmeta'},$env{'form.title'},$env{'form.author'},...
       if (!$error) {
    $r->print('<p>');
    &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1);
    $r->print('</p>');
       }
       %env=%oldenv;
       return '';
 }  }
   
 # ================================================================ Main Handler  #########################################
   
 sub handler {  
   my $r=shift;  
   
   if ($r->header_only) {  
      $r->content_type('text/html');  
      $r->send_http_header;  
      return OK;  
   }  
   
 # Get query string for limited number of parameters  
   
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},  
                                             ['filename']);  
   
 # -------------------------------------------------------------- Check filename  
   
   my $fn=$ENV{'form.filename'};  sub publishdirectory {
       my ($r,$fn,$thisdisfn)=@_;
       $fn=~s/\/+/\//g;
       $thisdisfn=~s/\/+/\//g;
       my $resdir=
    $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cudom.'/'.$cuname.'/'.
    $thisdisfn;
       $r->print(&Apache::lonhtmlcommon::start_pick_box()
                .&Apache::lonhtmlcommon::row_title(&mt('Directory'))
               .'<span class="LC_filename">'.$thisdisfn.'</span>'
               .&Apache::lonhtmlcommon::row_closure()
               .&Apache::lonhtmlcommon::row_title(&mt('Target'))
               .'<span class="LC_filename">'.$resdir.'</span>'
       );
   
       my $dirptr=16384; # Mask indicating a directory in stat.cmode.
       unless ($env{'form.phase'} eq 'two') {
   # ask user what they want
           $r->print(&Apache::lonhtmlcommon::row_closure()
                    .&Apache::lonhtmlcommon::row_title(&mt('Options'))
           );
           $r->print('<form name="pubdirpref" method="post">'.
     &hiddenfield('phase','two').
     &hiddenfield('filename',$env{'form.filename'}).
     &checkbox('pubrec','include subdirectories').
     &checkbox('forcerepub','force republication of previously published files').
                     &checkbox('obsolete','make file(s) obsolete').
     &checkbox('forceoverride','force directory level metadata over existing').
     '<br /><input type="submit" value="'.&mt('Publish Directory').'" /></form>');
           $r->print(&Apache::lonhtmlcommon::row_closure(1)
                    .&Apache::lonhtmlcommon::end_pick_box()
           );
           $lock=0;
       } else {
           $r->print(&Apache::lonhtmlcommon::row_closure(1)
                    .&Apache::lonhtmlcommon::end_pick_box()
           );
           unless ($lock) { $lock=&Apache::lonnet::set_lock(&mt('Publishing [_1]',$fn)); }
   # actually publish things
    opendir(DIR,$fn);
    my @files=sort(readdir(DIR));
    foreach my $filename (@files) {
       my ($cdev,$cino,$cmode,$cnlink,
    $cuid,$cgid,$crdev,$csize,
    $catime,$cmtime,$cctime,
    $cblksize,$cblocks)=stat($fn.'/'.$filename);
       
       my $extension='';
       if ($filename=~/\.(\w+)$/) { $extension=$1; }
       if ($cmode&$dirptr) {
    if (($filename!~/^\./) && ($env{'form.pubrec'})) {
       &publishdirectory($r,$fn.'/'.$filename,$thisdisfn.'/'.$filename);
    }
       } elsif ((&Apache::loncommon::fileembstyle($extension) ne 'hdn') &&
        ($filename!~/^[\#\.]/) && ($filename!~/\~$/)) {
   # find out publication status and/or exiting metadata
    my $publishthis=0;
    if (-e $resdir.'/'.$filename) {
       my ($rdev,$rino,$rmode,$rnlink,
    $ruid,$rgid,$rrdev,$rsize,
    $ratime,$rmtime,$rctime,
    $rblksize,$rblocks)=stat($resdir.'/'.$filename);
       if (($rmtime<$cmtime) || ($env{'form.forcerepub'})) {
   # previously published, modified now
    $publishthis=1;
       }
       my $meta_cmtime = (stat($fn.'/'.$filename.'.meta'))[9];
       my $meta_rmtime = (stat($resdir.'/'.$filename.'.meta'))[9];
       if ( $meta_rmtime<$meta_cmtime ) {
    $publishthis=1;
       }
    } else {
   # never published
       $publishthis=1;
    }
   
    if ($publishthis) {
       &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename);
    } else {
       $r->print('<br />'.&mt('Skipping').' '.$filename.'<br />');
    }
    $r->rflush();
       }
    }
    closedir(DIR);
       }
   }
   
     #########################################
   unless ($fn) {   # publish a default.meta file
      $r->log_reason($cuname.' at '.$cudom.  
          ' trying to publish empty filename', $r->filename);   
      return HTTP_NOT_FOUND;  
   }   
   
   ($cuname,$cudom)=  
     &Apache::loncacc::constructaccess($fn,$r->dir_config('lonDefDomain'));  
   unless (($cuname) && ($cudom)) {  
      $r->log_reason($cuname.' at '.$cudom.  
          ' trying to publish file '.$ENV{'form.filename'}.  
          ' ('.$fn.') - not authorized',   
          $r->filename);   
      return HTTP_NOT_ACCEPTABLE;  
   }  
   
   unless (&Apache::lonnet::homeserver($cuname,$cudom)   
           eq $r->dir_config('lonHostID')) {  
      $r->log_reason($cuname.' at '.$cudom.  
          ' trying to publish file '.$ENV{'form.filename'}.  
          ' ('.$fn.') - not homeserver ('.  
          &Apache::lonnet::homeserver($cuname,$cudom).')',   
          $r->filename);   
      return HTTP_NOT_ACCEPTABLE;  
   }  
   
   $fn=~s/^http\:\/\/[^\/]+//;  
   $fn=~s/^\/\~(\w+)/\/home\/$1\/public_html/;  
   
   my $targetdir='';  
   $docroot=$r->dir_config('lonDocRoot');   
   if ($1 ne $cuname) {  
      $r->log_reason($cuname.' at '.$cudom.  
          ' trying to publish unowned file '.$ENV{'form.filename'}.  
          ' ('.$fn.')',   
          $r->filename);   
      return HTTP_NOT_ACCEPTABLE;  
   } else {  
       $targetdir=$docroot.'/res/'.$cudom;  
   }  
                                    
     
   unless (-e $fn) {   
      $r->log_reason($cuname.' at '.$cudom.  
          ' trying to publish non-existing file '.$ENV{'form.filename'}.  
          ' ('.$fn.')',   
          $r->filename);   
      return HTTP_NOT_FOUND;  
   }   
   
 unless ($ENV{'form.phase'} eq 'two') {  sub defaultmetapublish {
       my ($r,$fn,$cuname,$cudom)=@_;
       $fn=~s/^\/\~$cuname\//\/home\/$cuname\/public_html\//;
       unless (-e $fn) {
          return HTTP_NOT_FOUND;
       }
       my $target=$fn;
       $target=~s/^\/home\/$cuname\/public_html\//$Apache::lonnet::perlvar{'lonDocRoot'}\/res\/$cudom\/$cuname\//;
   
 # --------------------------------- File is there and owned, init lookup tables  
   
   %addid=();      &Apache::loncommon::content_type($r,'text/html');
       $r->send_http_header;
   
   {      $r->print(&Apache::loncommon::start_page('Metadata Publication'));
       my $fh=Apache::File->new($r->dir_config('lonTabDir').'/addid.tab');  
       while (<$fh>=~/(\w+)\s+(\w+)/) {  
           $addid{$1}=$2;  
       }  
   }  
   
   %nokey=();  # ---------------------------------------------------------------- Write Source
       my $copyfile=$target;
       
       my @parts=split(/\//,$copyfile);
       my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
       
       my $count;
       for ($count=5;$count<$#parts;$count++) {
           $path.="/$parts[$count]";
           if ((-e $path)!=1) {
               mkdir($path,0777);
               $r->print('<p>'
                        .&mt('Created directory [_1]'
                            ,'<span class="LC_filename">'.$parts[$count].'</span>')
                        .'</p>'
               );
           }
       }
       
       if (copy($fn,$copyfile)) {
           $r->print('<p>'.&mt('Copied source file').'</p>');
       } else {
           return "<span class=\"LC_error\">".
       &mt('Failed to copy source').", $!, ".&mt('FAIL')."</span>";
       }
   
   {  # --------------------------------------------------- Send update notifications
      my $fh=Apache::File->new($r->dir_config('lonIncludes').'/un_keyword.tab');  
       while (<$fh>) {  
           my $word=$_;  
           chomp($word);  
           $nokey{$word}=1;  
       }  
   }  
   
       my @subscribed=&get_subscribed_hosts($target);
       foreach my $subhost (@subscribed) {
    $r->print('<p>'.&mt('Notifying host').' '.$subhost.':');$r->rflush;
    my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
    $r->print($reply.'</p><br />');$r->rflush;
       }
   # ------------------------------------------------------------------- Link back
       my $link=$fn;
       $link=~s/^\/home\/$cuname\/public_html\//\/priv\/$cuname\//;
       $r->print("<a href='$link'>".&mt('Back to Metadata').'</a>');
       $r->print(&Apache::loncommon::end_page());
       return OK;
 }  }
   #########################################
   
 # ----------------------------------------------------------- Start page output  =pod
   
   $r->content_type('text/html');  
   $r->send_http_header;  
   
   $r->print('<html><head><title>LON-CAPA Publishing</title></head>');  
   $r->print(  
    '<body bgcolor="#FFFFFF"><img align=right src=/adm/lonIcons/lonlogos.gif>');  
   my $thisfn=$fn;  
      
 # ------------------------------------------------------------- Individual file  
   {  
       $thisfn=~/\.(\w+)$/;  
       my $thistype=$1;  
       my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);  
   
       my $thistarget=$thisfn;  
         
       $thistarget=~s/^\/home/$targetdir/;  
       $thistarget=~s/\/public\_html//;  
   
       my $thisdistarget=$thistarget;  
       $thisdistarget=~s/^$docroot//;  
   
       my $thisdisfn=$thisfn;  
       $thisdisfn=~s/^\/home\/$cuname\/public_html\///;  
   
       $r->print('<h2>Publishing '.  
         &Apache::loncommon::filedescription($thistype).' <tt>'.  
         $thisdisfn.'</tt></h2><b>Target:</b> <tt>'.$thisdistarget.'</tt><p>');  
      
        if (($cuname ne $ENV{'user.name'}) || ($cudom ne $ENV{'user.domain'})) {  
           $r->print('<h3><font color=red>Co-Author: '.$cuname.' at '.$cudom.  
                '</font></h3>');  
       }  
   
       if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') {  
           $r->print('<br><a href="/adm/diff?filename=/~'.$cuname.'/'.  
                     $thisdisfn.  
    '&versionone=priv" target=cat>Diffs with Current Version</a><p>');  
       }  
     
 # ------------ We are publishing from $thisfn to $thistarget with $thisembstyle  
   
        unless ($ENV{'form.phase'} eq 'two') {  =item B<handler>
          $r->print(  
           '<hr>'.&publish($thisfn,$thistarget,$thisembstyle));  
        } else {  
          $r->print(  
           '<hr>'.&phasetwo($thisfn,$thistarget,$thisembstyle,$thisdistarget));   
        }    
   
   }  A basic outline of the handler subroutine follows.
   $r->print('</body></html>');  
   
   return OK;  =over 4
 }  
   
 1;  =item *
 __END__  
   
 =head1 NAME  Get query string for limited number of parameters.
   
 Apache::lonpublisher - Publication Handler  =item *
   
 =head1 SYNOPSIS  Check filename.
   
 Invoked by /etc/httpd/conf/srm.conf:  =item *
   
  <Location /adm/publish>  File is there and owned, init lookup tables.
  PerlAccessHandler       Apache::lonacc  
  SetHandler perl-script  
  PerlHandler Apache::lonpublisher  
  ErrorDocument     403 /adm/login  
  ErrorDocument     404 /adm/notfound.html  
  ErrorDocument     406 /adm/unauthorized.html  
  ErrorDocument  500 /adm/errorhandler  
  </Location>  
   
 =head1 INTRODUCTION  =item *
   
 This module publishes a file.  This involves gathering metadata,  Start page output.
 versioning the file, copying file from construction space to  
 publication space, and copying metadata from construction space  
 to publication space.  
   
 This is part of the LearningOnline Network with CAPA project  =item *
 described at http://www.lon-capa.org.  
   
 =head1 HANDLER SUBROUTINE  Evaluate individual file, and then output information.
   
 This routine is called by Apache and mod_perl.  =item *
   
 =over 4  Publishing from $thisfn to $thistarget with $thisembstyle.
   
 =item *  =back
   
 Get query string for limited number of parameters  =cut
   
 =item *  #########################################
   #########################################
   sub handler {
       my $r=shift;
   
 Check filename      if ($r->header_only) {
    &Apache::loncommon::content_type($r,'text/html');
    $r->send_http_header;
    return OK;
       }
   
 =item *  # Get query string for limited number of parameters
   
 File is there and owned, init lookup tables      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
                                               ['filename']);
   
 =item *  # -------------------------------------- Flag and buffer for registered cleanup
       $registered_cleanup=0;
       @{$modified_urls}=();
   # -------------------------------------------------------------- Check filename
   
 Start page output      my $fn=&unescape($env{'form.filename'});
   
 =item *      ($cuname,$cudom)=
    &Apache::loncacc::constructaccess($fn,$r->dir_config('lonDefDomain'));
   
 Individual file  # special publication: default.meta file
       if ($fn=~/\/default.meta$/) {
    return &defaultmetapublish($r,$fn,$cuname,$cudom); 
       }
       $fn=~s/\.meta$//;
     
       unless ($fn) { 
    $r->log_reason($cuname.' at '.$cudom.
          ' trying to publish empty filename', $r->filename); 
    return HTTP_NOT_FOUND;
       } 
   
       unless (($cuname) && ($cudom)) {
    $r->log_reason($cuname.' at '.$cudom.
          ' trying to publish file '.$env{'form.filename'}.
          ' ('.$fn.') - not authorized', 
          $r->filename); 
    return HTTP_NOT_ACCEPTABLE;
       }
   
 =item *      my $home=&Apache::lonnet::homeserver($cuname,$cudom);
       my $allowed=0;
       my @ids=&Apache::lonnet::current_machine_ids();
       foreach my $id (@ids) { if ($id eq $home) { $allowed = 1; }  }
       unless ($allowed) {
    $r->log_reason($cuname.' at '.$cudom.
          ' trying to publish file '.$env{'form.filename'}.
          ' ('.$fn.') - not homeserver ('.$home.')', 
          $r->filename); 
    return HTTP_NOT_ACCEPTABLE;
       }
   
 publish from $thisfn to $thistarget with $thisembstyle      $fn=~s{^http://[^/]+}{};
       $fn=~s{^/~($match_username)}{/home/$1/public_html};
   
 =back      my $targetdir='';
       $docroot=$r->dir_config('lonDocRoot'); 
       if ($1 ne $cuname) {
    $r->log_reason($cuname.' at '.$cudom.
          ' trying to publish unowned file '.
          $env{'form.filename'}.' ('.$fn.')', 
          $r->filename); 
    return HTTP_NOT_ACCEPTABLE;
       } else {
    $targetdir=$docroot.'/res/'.$cudom;
       }
                                    
     
       unless (-e $fn) { 
    $r->log_reason($cuname.' at '.$cudom.
          ' trying to publish non-existing file '.
          $env{'form.filename'}.' ('.$fn.')', 
          $r->filename); 
    return HTTP_NOT_FOUND;
       } 
   
 =head1 OTHER SUBROUTINES  # -------------------------------- File is there and owned, init lookup tables.
   
 =over 4      %addid=();
       
       {
    my $fh=Apache::File->new($r->dir_config('lonTabDir').'/addid.tab');
    while (<$fh>=~/(\w+)\s+(\w+)/) {
       $addid{$1}=$2;
    }
       }
   
 =item *      %nokey=();
   
 metaeval() : Evaluate string with metadata      {
    my $fh=Apache::File->new($r->dir_config('lonIncludes').'/un_keyword.tab');
    while (<$fh>) {
       my $word=$_;
       chomp($word);
       $nokey{$word}=1;
    }
       }
   
 =item *  # ---------------------------------------------------------- Start page output.
   
 metaread() : Read a metadata file      &Apache::loncommon::content_type($r,'text/html');
       $r->send_http_header;
       
       # Breadcrumbs
       &Apache::lonhtmlcommon::clear_breadcrumbs();
       &Apache::lonhtmlcommon::add_breadcrumb({
           'text'  => 'Construction Space',
           'href'  => &Apache::loncommon::authorspace(),
       });
       &Apache::lonhtmlcommon::add_breadcrumb({
           'text'  => 'Resource Publication',
           'href'  => '',
       });
   
       my $js='<script type="text/javascript">'.
    &Apache::loncommon::browser_and_searcher_javascript().
    '</script>';
       $r->print(&Apache::loncommon::start_page('Resource Publication',$js)
                .&Apache::lonhtmlcommon::breadcrumbs()
                .&Apache::loncommon::head_subbox(
                     &Apache::loncommon::CSTR_pageheader()) # FIXME crumbs broken?
       );
   
 =item *  
   
 sqltime() : convert 'time' format into a datetime sql format      my $thisfn=$fn;
   
 =item *      my $thistarget=$thisfn;
         
       $thistarget=~s/^\/home/$targetdir/;
       $thistarget=~s/\/public\_html//;
   
 textfield() : form field      my $thisdistarget=$thistarget;
       $thisdistarget=~s/^\Q$docroot\E//;
   
 =item *      my $thisdisfn=$thisfn;
       $thisdisfn=~s/^\/home\/\Q$cuname\E\/public_html\///;
   
 hiddenfield() : form field      if ($fn=~/\/$/) {
   # -------------------------------------------------------- This is a directory
    &publishdirectory($r,$fn,$thisdisfn);
    $r->print('<hr /><a href="/priv/'
     .$cuname.'/'.$thisdisfn
     .'">'.&mt('Return to Directory').'</a>');
   
 =item *  
   
 selectbox() : form field      } else {
   # ---------------------- Evaluate individual file, and then output information.
    $thisfn=~/\.(\w+)$/;
    my $thistype=$1;
    my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
           if ($thistype eq 'page') {  $thisembstyle = 'rat'; }
   
           $r->print('<h2>'
                    .&mt('Publishing [_1]'
                        ,'<span class="LC_filename">'.$thisdisfn.'</span>')
                    .'</h2>'
           );
   
           $r->print('<h3>'.&mt('Resource Details').'</h3>');
   
           $r->print(&Apache::lonhtmlcommon::start_pick_box());
   
           $r->print(&Apache::lonhtmlcommon::row_title(&mt('Type'))
                    .&Apache::loncommon::filedescription($thistype)
                    .&Apache::lonhtmlcommon::row_closure()
                    );
   
           $r->print(&Apache::lonhtmlcommon::row_title(&mt('Link to Resource'))
                    .'<tt>'
                    );
    $r->print(<<ENDCAPTION);
   <a href='javascript:void(window.open("/~$cuname/$thisdisfn","cat","height=300,width=500,scrollbars=1,resizable=1,menubar=0,location=1"))'>
   $thisdisfn</a>
   ENDCAPTION
           $r->print('</tt>'
                    .&Apache::lonhtmlcommon::row_closure()
                    );
   
           $r->print(&Apache::lonhtmlcommon::row_title(&mt('Target'))
                    .'<tt>'.$thisdistarget.'</tt>'
                    );
    if (($cuname ne $env{'user.name'})||($cudom ne $env{'user.domain'})) {
               $r->print(&Apache::lonhtmlcommon::row_closure()
                        .&Apache::lonhtmlcommon::row_title(&mt('Co-Author'))
                        .'<span class="LC_warning">'
        .&Apache::loncommon::plainname($cuname,$cudom) .' ('.$cuname.':'.$cudom.')'
                        .'</span>'
                        );
    }
   
 =item *   if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') {
               $r->print(&Apache::lonhtmlcommon::row_closure()
                        .&Apache::lonhtmlcommon::row_title(&mt('Diffs')));
       $r->print(<<ENDDIFF);
   <a href='javascript:void(window.open("/adm/diff?filename=/~$cuname/$thisdisfn&versiontwo=priv","cat","height=300,width=500,scrollbars=1,resizable=1,menubar=0,location=1"))'>
   ENDDIFF
               $r->print(&mt('Diffs with Current Version').'</a>');
    }
           
           $r->print(&Apache::lonhtmlcommon::row_closure(1)
                    .&Apache::lonhtmlcommon::end_pick_box()
                    );
     
   # ------------------ Publishing from $thisfn to $thistarget with $thisembstyle.
   
 urlfixup() : fixup URL (Publication Step One)   unless ($env{'form.phase'} eq 'two') {
   # ---------------------------------------------------------- Parse for problems
       my ($warningcount,$errorcount);
       if ($thisembstyle eq 'ssi') {
    ($warningcount,$errorcount)=&checkonthis($r,$thisfn);
       }
       unless ($errorcount) {
    my ($outstring,$error)=
       &publish($thisfn,$thistarget,$thisembstyle);
    $r->print($outstring);
       } else {
    $r->print('<h3 class="LC_error">'.
     &mt('The document contains errors and cannot be published.').
     '</h3>');
       }
    } else {
       &phasetwo($r,$thisfn,$thistarget,$thisembstyle,$thisdistarget); 
       $r->print('<hr />');
    }
       }
       $r->print(&Apache::loncommon::end_page());
   
 =item *      return OK;
   }
   
 publish() : publish (Publication Step One)  1;
   __END__
   
 =item *  =pod
   
 phasetwo() : render second interface showing status of publication steps  =back
 (Publication Step Two)  
   
 =back  =back
   
 =cut  =cut
   

Removed from v.1.84  
changed lines
  Added in v.1.261


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