# The LearningOnline Network with CAPA
# Publication Handler
#
# $Id: lonpublisher.pm,v 1.124 2003/07/05 10:07:12 www 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
# 05/03,05/05,05/07 Gerd Kortemeyer
# 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/25 Gerd Kortemeyer
# YEAR=2002
# 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 ##
## ##
###############################################################################
######################################################################
######################################################################
=pod
=head1 NAME
lonpublisher - LON-CAPA publishing handler
=head1 SYNOPSIS
B is used by B inside B. This is the
invocation by F:
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
=head1 DESCRIPTION
B 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 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;
# ------------------------------------------------- modules used by this module
use strict;
use Apache::File;
use File::Copy;
use Apache::Constants qw(:common :http :methods);
use HTML::LCParser;
use Apache::lonxml;
use Apache::loncacc;
use DBI;
use Apache::lonnet();
use Apache::loncommon();
use Apache::lonmysql;
use vars qw(%metadatafields %metadatakeys);
my %addid;
my %nokey;
my $docroot;
my $cuname;
my $cudom;
=pod
=item B
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
#########################################
#########################################
sub metaeval {
my $metastring=shift;
my $parser=HTML::LCParser->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}=~/\Q$newentry\E/) ||
($newentry eq '')) {
$metadatafields{$unikey}.=', '.$newentry;
}
} else {
$metadatafields{$unikey}=$parser->get_text('/'.$entry);
}
}
}
}
#########################################
#########################################
=pod
=item B
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 {
my ($logfile,$fn)=@_;
unless (-e $fn) {
print($logfile 'No file '.$fn."\n");
return ' No file:'.$fn.'';
}
print($logfile 'Processing '.$fn."\n");
my $metastring;
{
my $metafh=Apache::File->new($fn);
$metastring=join('',<$metafh>);
}
&metaeval($metastring);
return ' Processed file:'.$fn.'';
}
#########################################
#########################################
sub coursedependencies {
my $url=&Apache::lonnet::declutter(shift);
$url=~s/\.meta$//;
my ($adomain,$aauthor)=($url=~/^(\w+)\/(\w+)\//);
my $regexp=$url;
$regexp=~s/(\W)/\\$1/g;
$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
=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
tags).
=over 4
=item B
=item B
=item B
=back
=cut
#########################################
#########################################
sub textfield {
my ($title,$name,$value)=@_;
my $uctitle=uc($title);
return "\n
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 "Invalid target directory, FAIL";
}
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('
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";
$r->print('
Copied old target file
');
} else {
print $logfile "Unable to write ".$copyfile.':'.$!."\n";
return "Failed to copy old target, $!, FAIL";
}
# --------------------------------------------------------------- Copy Metadata
$copyfile=$copyfile.'.meta';
if (copy($target.'.meta',$copyfile)) {
print $logfile "Copied old target metadata to ".$copyfile."\n";
$r->print('
Copied old metadata
')
} else {
print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
if (-e $target.'.meta') {
return
"Failed to write old metadata copy, $!, FAIL";
}
}
} else {
$r->print('
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;
$r->print('
Created directory '.$parts[$count].'
');
mkdir($path,0777);
}
}
if (copy($source,$copyfile)) {
print $logfile "\nCopied original source to ".$copyfile."\n";
$r->print('
Copied source file
');
} else {
print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";
return "Failed to copy source, $!, FAIL";
}
# --------------------------------------------------------------- Copy Metadata
$copyfile=$copyfile.'.meta';
if (copy($source.'.meta',$copyfile)) {
print $logfile "\nCopied original metadata to ".$copyfile."\n";
$r->print('
Copied metadata
');
} else {
print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n";
return
"Failed to write metadata copy, $!, FAIL";
}
$r->rflush;
# --------------------------------------------------- Send update notifications
my @subscribed=&get_subscribed_hosts($target);
foreach my $subhost (@subscribed) {
$r->print('
');$r->rflush;
print $logfile $reply;
}
# ---------------------------------------- Send update notifications, meta only
my @subscribedmeta=&get_subscribed_hosts("$target.meta");
foreach my $subhost (@subscribedmeta) {
$r->print('
Notifying host for metadata only '.$subhost.':');$r->rflush;
print $logfile "\nNotifying host for metadata only ".$subhost.':';
my $reply=&Apache::lonnet::critical('update:'.$target.'.meta',
$subhost);
$r->print($reply.'
');$r->rflush;
print $logfile $reply;
}
# --------------------------------------------------- Notify subscribed courses
my %courses=&coursedependencies($target);
my $now=time;
foreach (keys %courses) {
$r->print('
Notifying course '.$_.':');$r->rflush;
print $logfile "\nNotifying host ".$_.':';
my ($cdom,$cname)=split(/\_/,$_);
my $reply=&Apache::lonnet::cput
('versionupdate',{$target => $now},$cdom,$cname);
$r->print($reply.'
');$r->rflush;
print $logfile $reply;
}
# ------------------------------------------------ Provide link to new resource
unless ($batch) {
my $thisdistarget=$target;
$thisdistarget=~s/^\Q$docroot\E//;
my $thissrc=$source;
$thissrc=~s/^\/home\/(\w+)\/public_html/\/priv\/$1/;
my $thissrcdir=$thissrc;
$thissrcdir=~s/\/[^\/]+$/\//;
$r->print(
''.
'View Published Version'.
'
');
}
}
#########################################
sub batchpublish {
my ($r,$srcfile,$targetfile)=@_;
$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//;
undef %metadatafields;
undef %metadatakeys;
%metadatafields=();
%metadatakeys=();
$srcfile=~/\.(\w+)$/;
my $thistype=$1;
my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
$r->print('
Publishing '.$thisdisfn.'
');
# phase one takes
# my ($source,$target,$style,$batch)=@_;
my ($outstring,$error)=&publish($srcfile,$targetfile,$thisembstyle,1);
$r->print('
'.$outstring.'
');
# phase two takes
# my ($source,$target,$style,$distarget,batch)=@_;
# $ENV{'form.allmeta'},$ENV{'form.title'},$ENV{'form.author'},...
if (!$error) {
$r->print('
Target:'.$thisdistarget.' ');
if (($cuname ne $ENV{'user.name'}) || ($cudom ne $ENV{'user.domain'})) {
$r->print('
Co-Author: '.$cuname.' at '.$cudom.
'
');
}
if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') {
$r->print(' Diffs with Current Version ');
}
# ------------------ Publishing from $thisfn to $thistarget with $thisembstyle.
unless ($ENV{'form.phase'} eq 'two') {
my ($outstring,$error)=&publish($thisfn,$thistarget,$thisembstyle);
$r->print(''.$outstring);
} else {
$r->print('');
&phasetwo($r,$thisfn,$thistarget,$thisembstyle,$thisdistarget);
}
}
$r->print('