File:  [LON-CAPA] / loncom / publisher / lonpublisher.pm
Revision 1.73: download - view: text, annotated - select for diffs
Thu Feb 14 22:01:39 2002 UTC (22 years, 2 months ago) by albertel
Branches: MAIN
CVS tags: HEAD
- stops saying "Currently Not Available" on blank allows (maps often have blanks resources.)
- now sorts depdencies, looks prettier

# The LearningOnline Network with CAPA
# Publication Handler
#
# $Id: lonpublisher.pm,v 1.73 2002/02/14 22:01:39 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
# This file is part of the LearningOnline Network with CAPA (LON-CAPA).
#
# LON-CAPA is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# LON-CAPA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LON-CAPA; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# /home/httpd/html/adm/gpl.txt
#
# 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
#
###

###############################################################################
##                                                                           ##
## ORGANIZATION OF THIS PERL MODULE                                          ##
##                                                                           ##
## 1. Modules used by this module                                            ##
## 2. Various subroutines                                                    ##
## 3. Publication Step One                                                   ##
## 4. Phase Two                                                              ##
## 5. Main Handler                                                           ##
##                                                                           ##
###############################################################################

package Apache::lonpublisher;

# ------------------------------------------------- modules used by this module
use strict;
use Apache::File;
use File::Copy;
use Apache::Constants qw(:common :http :methods);
use HTML::TokeParser;
use Apache::lonxml;
use Apache::lonhomework;
use Apache::loncacc;
use DBI;
use Apache::lonnet();
use Apache::loncommon();

my %addid;
my %nokey;

my %metadatafields;
my %metadatakeys;

my $docroot;

my $cuname;
my $cudom;

# ----------------------------------------------- Evaluate string with metadata
sub metaeval {
    my $metastring=shift;
   
        my $parser=HTML::TokeParser->new(\$metastring);
        my $token;
        while ($token=$parser->get_token) {
           if ($token->[0] eq 'S') {
	      my $entry=$token->[1];
              my $unikey=$entry;
              if (defined($token->[2]->{'package'})) { 
                  $unikey.='_package_'.$token->[2]->{'package'};
              } 
              if (defined($token->[2]->{'part'})) { 
                 $unikey.='_'.$token->[2]->{'part'}; 
	      }
              if (defined($token->[2]->{'id'})) { 
                  $unikey.='_'.$token->[2]->{'id'};
              } 
              if (defined($token->[2]->{'name'})) { 
                 $unikey.='_'.$token->[2]->{'name'}; 
	      }
              foreach (@{$token->[3]}) {
		  $metadatafields{$unikey.'.'.$_}=$token->[2]->{$_};
                  if ($metadatakeys{$unikey}) {
		      $metadatakeys{$unikey}.=','.$_;
                  } else {
                      $metadatakeys{$unikey}=$_;
                  }
              }
              if ($metadatafields{$unikey}) {
		  my $newentry=$parser->get_text('/'.$entry);
                  unless (($metadatafields{$unikey}=~/$newentry/) ||
                          ($newentry eq '')) {
                     $metadatafields{$unikey}.=', '.$newentry;
		  }
	      } else {
                 $metadatafields{$unikey}=$parser->get_text('/'.$entry);
              }
          }
       }
}

# -------------------------------------------------------- Read a metadata file
sub metaread {
    my ($logfile,$fn)=@_;
    unless (-e $fn) {
	print $logfile 'No file '.$fn."\n";
        return '<br><b>No file:</b> <tt>'.$fn.'</tt>';
    }
    print $logfile 'Processing '.$fn."\n";
    my $metastring;
    {
     my $metafh=Apache::File->new($fn);
     $metastring=join('',<$metafh>);
    }
    &metaeval($metastring);
    return '<br><b>Processed file:</b> <tt>'.$fn.'</tt>';
}

# ---------------------------- convert 'time' format into a datetime sql format
sub sqltime {
    my $timef=shift @_;
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
	localtime($timef);
    $mon++; $year+=1900;
    return "$year-$mon-$mday $hour:$min:$sec";
}

# --------------------------------------------------------- Various form fields

sub textfield {
    my ($title,$name,$value)=@_;
    return "\n<p><b>$title:</b><br>".
           '<input type=text name="'.$name.'" size=80 value="'.$value.'">';
}

sub hiddenfield {
    my ($name,$value)=@_;
    return "\n".'<input type=hidden name="'.$name.'" value="'.$value.'">';
}

sub selectbox {
    my ($title,$name,$value,$functionref,@idlist)=@_;
    my $uctitle=uc($title);
    my $selout="\n<p><font color=\"#800000\" face=\"helvetica\"><b>$uctitle:".
	"</b></font><br />".'<select name="'.$name.'">';
    foreach (@idlist) {
        $selout.='<option value=\''.$_.'\'';
        if ($_ eq $value) {
	    $selout.=' selected>'.&{$functionref}($_).'</option>';
	}
        else {$selout.='>'.&{$functionref}($_).'</option>';}
    }
    return $selout.'</select>';
}

# -------------------------------------------------------- Publication Step One

sub urlfixup {
    my ($url,$target)=@_;
    unless ($url) { return ''; }
    #javascript code needs no fixing
    if ($url =~ /^javascript:/i) { return $url; }
    if ($url =~ /^mailto:/i) { return $url; }
    #internal document links need no fixing
    if ($url =~ /^\#/) { return $url; } 
    my ($host)=($url=~/(?:http\:\/\/)*([^\/]+)/);
    foreach (values %Apache::lonnet::hostname) {
	if ($_ eq $host) {
	    $url=~s/^http\:\/\///;
            $url=~s/^$host//;
        }
    }
    if ($url=~/^http\:\/\//) { return $url; }
    $url=~s/\~$cuname/res\/$cudom\/$cuname/;
    return $url;
}


sub absoluteurl {
    my ($url,$target)=@_;
    unless ($url) { return ''; }
    if ($target) {
	$target=~s/\/[^\/]+$//;
       $url=&Apache::lonnet::hreflocation($target,$url);
    }
    return $url;
}

sub publish {

    my ($source,$target,$style)=@_;
    my $logfile;
    my $scrout='';
    my $allmeta='';
    my $content='';
    my %allow=();
    undef %allow;

    unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
	return 
         '<font color=red>No write permission to user directory, FAIL</font>';
    }
    print $logfile 
"\n\n================= Publish ".localtime()." Phase One  ================\n";

    if (($style eq 'ssi') || ($style eq 'rat')) {
# ------------------------------------------------------- This needs processing

# ----------------------------------------------------------------- Backup Copy
	my $copyfile=$source.'.save';
        if (copy($source,$copyfile)) {
	    print $logfile "Copied original file to ".$copyfile."\n";
        } else {
	    print $logfile "Unable to write backup ".$copyfile.':'.$!."\n";
          return "<font color=red>Failed to write backup copy, $!,FAIL</font>";
        }
# ------------------------------------------------------------- IDs and indices

        my $maxindex=10;
        my $maxid=10;

        my $needsfixup=0;

        {
          my $org=Apache::File->new($source);
          $content=join('',<$org>);
        }
        {
          my $parser=HTML::TokeParser->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::TokeParser->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)) {
			  if ($key =~ /^$type$/i) {
			      my $oldurl=$parms{$key};
			      my $newurl=&urlfixup($oldurl,$target);
			      if ($newurl ne $oldurl) {
				  $parms{$key}=$newurl;
				  print $logfile 'URL: '.$tag.':'.$oldurl.' - '.
				      $newurl."\n";
			      }
			      $allow{&absoluteurl($newurl,$target)}=1;
			  }
			  last;
		      }
                  }

                  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
    
	$scrout.='<h3>Dependencies</h3>';
        my $allowstr='';
        foreach (sort(keys(%allow))) {
	   my $thisdep=$_;
	   if ($thisdep !~ /[^\s]/) { next; }
           unless ($style eq 'rat') { 
              $allowstr.="\n".'<allow src="'.$thisdep.'" />';
	   }
           $scrout.='<br>';
           unless ($thisdep=~/\*/) {
	       $scrout.='<a href="'.$thisdep.'">';
           }
           $scrout.='<tt>'.$thisdep.'</tt>';
           unless ($thisdep=~/\*/) {
	       $scrout.='</a>';
               if (
       &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
                                            $thisdep.'.meta') eq '-1') {
		   $scrout.=
                           ' - <font color=red>Currently not available</font>';
               } else {
                   my %temphash=(&Apache::lonnet::declutter($target).'___'.
                             &Apache::lonnet::declutter($thisdep).'___usage'
                                 => time);
                   $thisdep=~/^\/res\/(\w+)\/(\w+)\//;
                   if ((defined($1)) && (defined($2))) {
                      &Apache::lonnet::put('resevaldata',\%temphash,$1,$2);
		   }
	       }
           }
        }
        $allowstr=~s/\n+/\n/g;
        $outstring=~s/(\<\/[^\>]+\>\s*)$/$allowstr$1/s;

# ------------------------------------------------------------- Write modified

        {
          my $org;
          unless ($org=Apache::File->new('>'.$source)) {
             print $logfile "No write permit to $source\n";
             return 
              "<font color=red>No write permission to $source, FAIL</font>";
	  }
          print $org $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

# ---------------------------------------- Storage for metadata keys and fields

     %metadatafields=();
     %metadatakeys=();
     
     my %oldparmstores=();
     
     $scrout.='<h3>Metadata Information</h3>';

# ------------------------------------------------ First, check out environment
     unless (-e $source.'.meta') {
        $metadatafields{'author'}=$ENV{'environment.firstname'}.' '.
	                          $ENV{'environment.middlename'}.' '.
		                  $ENV{'environment.lastname'}.' '.
		                  $ENV{'environment.generation'};
        $metadatafields{'author'}=~s/\s+/ /g;
        $metadatafields{'author'}=~s/\s+$//;
        $metadatafields{'owner'}=$cuname.'@'.$cudom;

# ------------------------------------------------ Check out directory hierachy

        my $thisdisfn=$source;
        $thisdisfn=~s/^\/home\/$cuname\///;

        my @urlparts=split(/\//,$thisdisfn);
        $#urlparts--;

        my $currentpath='/home/'.$cuname.'/';

        foreach (@urlparts) {
	    $currentpath.=$_.'/';
            $scrout.=&metaread($logfile,$currentpath.'default.meta');
        }

# ------------------- Clear out parameters and stores (there should not be any)

        foreach (keys %metadatafields) {
	    if (($_=~/^parameter/) || ($_=~/^stores/)) {
		delete $metadatafields{$_};
            }
        }

    } else {
# ---------------------- Read previous metafile, remember parameters and stores

        $scrout.=&metaread($logfile,$source.'.meta');

        foreach (keys %metadatafields) {
	    if (($_=~/^parameter/) || ($_=~/^stores/)) {
                $oldparmstores{$_}=1;
		delete $metadatafields{$_};
            }
        }
        
    }

# -------------------------------------------------- Parse content for metadata
    if ($style eq 'ssi') {
        my $oldenv=$ENV{'request.uri'};

        $ENV{'request.uri'}=$target;
        $allmeta=Apache::lonxml::xmlparse('meta',$content);
        $ENV{'request.uri'}=$oldenv;

        &metaeval($allmeta);
    }
# ---------------- Find and document discrepancies in the parameters and stores

        my $chparms='';
        foreach (sort keys %metadatafields) {
	    if (($_=~/^parameter/) || ($_=~/^stores/)) {
                unless ($_=~/\.\w+$/) { 
                   unless ($oldparmstores{$_}) {
		      print $logfile 'New: '.$_."\n";
                      $chparms.=$_.' ';
                   }
	        }
            }
        }
        if ($chparms) {
	    $scrout.='<p><b>New parameters or stored values:</b> '.
                     $chparms;
        }

        $chparms='';
        foreach (sort keys %oldparmstores) {
	    if (($_=~/^parameter/) || ($_=~/^stores/)) {
                unless (($metadatafields{$_.'.name'}) ||
                        ($metadatafields{$_.'.package'}) || ($_=~/\.\w+$/)) {
		    print $logfile 'Obsolete: '.$_."\n";
                    $chparms.=$_.' ';
                }
            }
        }
        if ($chparms) {
	    $scrout.='<p><b>Obsolete parameters or stored values:</b> '.
                     $chparms;
        }

# ------------------------------------------------------- Now have all metadata

        $scrout.=
     '<form 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

	my $keywordout='<p><b>Keywords:</b><br><table border=2><tr>';
        my $colcount=0;
        my %keywords=();
        
	if (length($content)<500000) {
	    my $textonly=$content;
            $textonly=~s/\<script[^\<]+\<\/script\>//g;
            $textonly=~s/\<m\>[^\<]+\<\/m\>//g;
            $textonly=~s/\<[^\>]*\>//g;
            $textonly=~tr/A-Z/a-z/;
            $textonly=~s/[\$\&][a-z]\w*//g;
            $textonly=~s/[^a-z\s]//g;

            foreach ($textonly=~m/(\w+)/g) {
		unless ($nokey{$_}) {
                   $keywords{$_}=1;
                } 
            }
        }

            
            foreach (split(/\W+/,$metadatafields{'keywords'})) {
		$keywords{$_}=1;
            }

            foreach (sort keys %keywords) {
                $keywordout.='<td><input type=checkbox name="key.'.$_.'"';
                if ($metadatafields{'keywords'}) {
                   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;

        $scrout.=&textfield('Additional Keywords','addkey','');

        $scrout.=&textfield('Notes','notes',$metadatafields{'notes'});

        $scrout.=
             '<p><b>Abstract:</b><br><textarea cols=80 rows=5 name=abstract>'.
              $metadatafields{'abstract'}.'</textarea>';

	$source=~/\.(\w+)$/;

	$scrout.=&hiddenfield('mime',$1);

        $scrout.=&selectbox('Language','language',
                            $metadatafields{'language'},
			    \&Apache::loncommon::languagedescription,
			    (&Apache::loncommon::languageids),
			     );

        unless ($metadatafields{'creationdate'}) {
	    $metadatafields{'creationdate'}=time;
        }
        $scrout.=&hiddenfield('creationdate',$metadatafields{'creationdate'});

        $scrout.=&hiddenfield('lastrevisiondate',time);

			   
	$scrout.=&textfield('Publisher/Owner','owner',
                            $metadatafields{'owner'});
# --------------------------------------------------- Correct copyright for rat        
    if ($style eq 'rat') {
	if ($metadatafields{'copyright'} eq 'public') { 
	    delete $metadatafields{'copyright'};
	}
        $scrout.=&selectbox('Copyright/Distribution','copyright',
                            $metadatafields{'copyright'},
			    \&Apache::loncommon::copyrightdescription,
		     (grep !/^public$/,(&Apache::loncommon::copyrightids)));
    }
    else {
        $scrout.=&selectbox('Copyright/Distribution','copyright',
                            $metadatafields{'copyright'},
			    \&Apache::loncommon::copyrightdescription,
			     (&Apache::loncommon::copyrightids));
    }
    return $scrout.
      '<p><input type="submit" value="Finalize Publication" /></p></form>';
}

# -------------------------------------------------------- Publication Step Two

sub phasetwo {

    my ($source,$target,$style,$distarget)=@_;
    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=();
     %metadatakeys=();

     &metaeval(&Apache::lonnet::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{'dependencies'}=$ENV{'form.dependencies'};

     my $allkeywords=$ENV{'form.addkey'};
     foreach (keys %ENV) {
         if ($_=~/^form\.key\.(\w+)/) {
	     $allkeywords.=','.$1;
         }
     }
     $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 '>'.$metadatafields{$unikey}.'</'.$tag.'>';
         }
       }
       $scrout.='<p>Wrote Metadata';
       print $logfile "\nWrote metadata";
     }

# -------------------------------- Synchronize entry with SQL metadata database
  my $warning;

  unless ($metadatafields{'copyright'} eq 'priv') {

    my $dbh;
    {
	unless (
		$dbh = DBI->connect("DBI:mysql:loncapa","www",
    $Apache::lonnet::perlvar{'lonSqlAccess'},{ RaiseError =>0,PrintError=>0})
		) { 
	    $warning='<font color=red>WARNING: Cannot connect to '.
		'database!</font>';
	}
	else {
	    my %sqldatafields;
	    $sqldatafields{'url'}=$distarget;
	    my $sth=$dbh->prepare(
				  'delete from metadata where url like binary'.
				  '"'.$sqldatafields{'url'}.'"');
	    $sth->execute();
	    foreach ('title','author','subject','keywords','notes','abstract',
	     'mime','language','creationdate','lastrevisiondate','owner',
	     'copyright') {
		my $field=$metadatafields{$_}; $field=~s/\"/\'\'/g; 
		$sqldatafields{$_}=$field;
	    }
	    
	    $sth=$dbh->prepare('insert into metadata values ('.
			       '"'.delete($sqldatafields{'title'}).'"'.','.
			       '"'.delete($sqldatafields{'author'}).'"'.','.
			       '"'.delete($sqldatafields{'subject'}).'"'.','.
			       '"'.delete($sqldatafields{'url'}).'"'.','.
			       '"'.delete($sqldatafields{'keywords'}).'"'.','.
			       '"'.'current'.'"'.','.
			       '"'.delete($sqldatafields{'notes'}).'"'.','.
			       '"'.delete($sqldatafields{'abstract'}).'"'.','.
			       '"'.delete($sqldatafields{'mime'}).'"'.','.
			       '"'.delete($sqldatafields{'language'}).'"'.','.
			       '"'.
			       sqltime(delete($sqldatafields{'creationdate'}))
			       .'"'.','.
			       '"'.
			       sqltime(delete(
			       $sqldatafields{'lastrevisiondate'})).'"'.','.
			       '"'.delete($sqldatafields{'owner'}).'"'.','.
			       '"'.delete(
			       $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';
    print $logfile "\nPrivate: Did not synchronize data into ".
	"SQL metadata database";
}
# ----------------------------------------------------------- Copy old versions
   
if (-e $target) {
    my $filename;
    my $maxversion=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 ($filename=~/$srcf\.(\d+)\.$srct$/) {
	   $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;

        if (copy($target,$copyfile)) {
	    print $logfile "Copied old target to ".$copyfile."\n";
            $scrout.='<p>Copied old target file';
        } else {
	    print $logfile "Unable to write ".$copyfile.':'.$!."\n";
           return "<font color=red>Failed to copy old target, $!, FAIL</font>";
        }

# --------------------------------------------------------------- Copy Metadata

	$copyfile=$copyfile.'.meta';

        if (copy($target.'.meta',$copyfile)) {
	    print $logfile "Copied old target metadata to ".$copyfile."\n";
            $scrout.='<p>Copied old metadata';
        } else {
	    print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
            if (-e $target.'.meta') {
               return 
       "<font color=red>Failed to write old metadata copy, $!, FAIL</font>";
	    }
        }


} else {
    $scrout.='<p>Initial version';
    print $logfile "\nInitial version";
}

# ---------------------------------------------------------------- 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) {
                   print $logfile "\nCreating directory ".$path;
                   $scrout.='<p>Created directory '.$parts[$count];
		   mkdir($path,0777);
               }
           }

        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>";
        }

# --------------------------------------------------------------- Copy Metadata

        $copyfile=$copyfile.'.meta';

        if (copy($source.'.meta',$copyfile)) {
	    print $logfile "Copied original metadata to ".$copyfile."\n";
            $scrout.='<p>Copied metadata';
        } else {
	    print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
            return 
          "<font color=red>Failed to write metadata copy, $!, FAIL</font>";
        }

# --------------------------------------------------- Send update notifications

{

    my $filename;
 
    $target=~/(.*)\/([^\/]+)$/;
    my $srcf=$2;
    opendir(DIR,$1);
    while ($filename=readdir(DIR)) {
       if ($filename=~/$srcf\.(\w+)$/) {
	   my $subhost=$1;
           if ($subhost ne 'meta') {
	       $scrout.='<p>Notifying host '.$subhost.':';
               print $logfile "\nNotifying host '.$subhost.':'";
               my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
               $scrout.=$reply;
               print $logfile $reply;              
           }
       }
    }
    closedir(DIR);

}

# ---------------------------------------- Send update notifications, meta only

{

    my $filename;
 
    $target=~/(.*)\/([^\/]+)$/;
    my $srcf=$2.'.meta';
    opendir(DIR,$1);
    while ($filename=readdir(DIR)) {
       if ($filename=~/$srcf\.(\w+)$/) {
	   my $subhost=$1;
           if ($subhost ne 'meta') {
	       $scrout.=
                '<p>Notifying host for metadata only '.$subhost.':';
               print $logfile 
                "\nNotifying host for metadata only '.$subhost.':'";
               my $reply=&Apache::lonnet::critical(
                                'update:'.$target.'.meta',$subhost);
               $scrout.=$reply;
               print $logfile $reply;              
           }
       }
    }
    closedir(DIR);

}

# ------------------------------------------------ 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;
    $thissrcdir=~s/\/[^\/]+$/\//;


    return $warning.$scrout.
      '<hr><a href="'.$thisdistarget.'"><font size=+2>View Target</font></a>'.
      '<p><a href="'.$thissrc.'"><font size=+2>Back to Source</font></a>'.
      '<p><a href="'.$thissrcdir.
      '"><font size=+2>Back to Source Directory</font></a>';

}

# ================================================================ 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

    foreach (split(/&/,$ENV{'QUERY_STRING'})) {
       my ($name, $value) = split(/=/,$_);
       $value =~ tr/+/ /;
       $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
       if ($name eq 'filename') {
           unless ($ENV{'form.'.$name}) {
              $ENV{'form.'.$name}=$value;
	   }
       }
    }


# -------------------------------------------------------------- Check filename

  my $fn=$ENV{'form.filename'};

  
  unless ($fn) { 
     $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') {

# --------------------------------- File is there and owned, init lookup tables

  %addid=();

  {
      my $fh=Apache::File->new($r->dir_config('lonTabDir').'/addid.tab');
      while (<$fh>=~/(\w+)\s+(\w+)/) {
          $addid{$1}=$2;
      }
  }

  %nokey=();

  {
     my $fh=Apache::File->new($r->dir_config('lonIncludes').'/un_keyword.tab');
      while (<$fh>) {
          my $word=$_;
          chomp($word);
          $nokey{$word}=1;
      }
  }

}

# ----------------------------------------------------------- Start page output

  $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') {
         $r->print(
          '<hr>'.&publish($thisfn,$thistarget,$thisembstyle));
       } else {
         $r->print(
          '<hr>'.&phasetwo($thisfn,$thistarget,$thisembstyle,$thisdistarget)); 
       }  

  }
  $r->print('</body></html>');

  return OK;
}

1;
__END__

=head1 NAME

Apache::lonpublisher - Publication Handler

=head1 SYNOPSIS

Invoked by /etc/httpd/conf/srm.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 INTRODUCTION

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.

This is part of the LearningOnline Network with CAPA project
described at http://www.lon-capa.org.

=head1 HANDLER SUBROUTINE

This routine is called by Apache and mod_perl.

=over 4

=item *

Get query string for limited number of parameters

=item *

Check filename

=item *

File is there and owned, init lookup tables

=item *

Start page output

=item *

Individual file

=item *

publish from $thisfn to $thistarget with $thisembstyle

=back

=head1 OTHER SUBROUTINES

=over 4

=item *

metaeval() : Evaluate string with metadata

=item *

metaread() : Read a metadata file

=item *

sqltime() : convert 'time' format into a datetime sql format

=item *

textfield() : form field

=item *

hiddenfield() : form field

=item *

selectbox() : form field

=item *

urlfixup() : fixup URL (Publication Step One)

=item *

publish() : publish (Publication Step One)

=item *

phasetwo() : render second interface showing status of publication steps
(Publication Step Two)

=back

=cut

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