--- loncom/publisher/lonpublisher.pm 2004/10/05 13:41:36 1.179
+++ loncom/publisher/lonpublisher.pm 2007/02/01 02:38:05 1.219
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Publication Handler
#
-# $Id: lonpublisher.pm,v 1.179 2004/10/05 13:41:36 matthew Exp $
+# $Id: lonpublisher.pm,v 1.219 2007/02/01 02:38:05 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -121,7 +121,7 @@ use HTML::LCParser;
use Apache::lonxml;
use Apache::loncacc;
use DBI;
-use Apache::lonnet();
+use Apache::lonnet;
use Apache::loncommon();
use Apache::lonmysql;
use Apache::lonlocal;
@@ -129,6 +129,8 @@ use Apache::loncfile;
use LONCAPA::lonmetadata;
use Apache::lonmsg;
use vars qw(%metadatafields %metadatakeys);
+use LONCAPA qw(:DEFAULT :match);
+
my %addid;
my %nokey;
@@ -138,6 +140,9 @@ my $docroot;
my $cuname;
my $cudom;
+my $registered_cleanup;
+my $modified_urls;
+
=pod
=item B
@@ -178,17 +183,18 @@ sub metaeval {
if ($token->[0] eq 'S') {
my $entry=$token->[1];
my $unikey=$entry;
+ next if ($entry =~ m/^(?:parameter|stores)_/);
if (defined($token->[2]->{'package'})) {
- $unikey.='_package_'.$token->[2]->{'package'};
+ $unikey.="\0package\0".$token->[2]->{'package'};
}
if (defined($token->[2]->{'part'})) {
- $unikey.='_'.$token->[2]->{'part'};
+ $unikey.="\0".$token->[2]->{'part'};
}
if (defined($token->[2]->{'id'})) {
- $unikey.='_'.$token->[2]->{'id'};
+ $unikey.="\0".$token->[2]->{'id'};
}
if (defined($token->[2]->{'name'})) {
- $unikey.='_'.$token->[2]->{'name'};
+ $unikey.="\0".$token->[2]->{'name'};
}
foreach (@{$token->[3]}) {
$metadatafields{$unikey.'.'.$_}=$token->[2]->{$_};
@@ -275,9 +281,8 @@ sub metaread {
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;
+ 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);
@@ -323,26 +328,47 @@ sub textfield {
$value=~s/\s+$//gs;
$value=~s/\s+/ /gs;
$title=&mt($title);
- $ENV{'form.'.$name}=$value;
+ $env{'form.'.$name}=$value;
return "\n$title:".
"
".
'';
}
+sub text_with_browse_field {
+ my ($title,$name,$value,$restriction)=@_;
+ $value=~s/^\s+//gs;
+ $value=~s/\s+$//gs;
+ $value=~s/\s+/ /gs;
+ $title=&mt($title);
+ $env{'form.'.$name}=$value;
+ return "\n$title:".
+ "
".
+ ''.
+ 'Select '.
+ 'Search';
+
+}
+
sub hiddenfield {
my ($name,$value)=@_;
- $ENV{'form.'.$name}=$value;
+ $env{'form.'.$name}=$value;
return "\n".'';
}
+sub checkbox {
+ my ($name,$text)=@_;
+ return "\n
";
+}
+
sub selectbox {
my ($title,$name,$value,$functionref,@idlist)=@_;
$title=&mt($title);
$value=(split(/\s*,\s*/,$value))[-1];
if (defined($value)) {
- $ENV{'form.'.$name}=$value;
+ $env{'form.'.$name}=$value;
} else {
- $ENV{'form.'.$name}=$idlist[0];
+ $env{'form.'.$name}=$idlist[0];
}
my $selout="\n$title:".
'
'.&mt('Warning!').
+ '
'.
+ &mt('If this resource is in active use, student performance data from the previous version may become inaccessible.').'
';
}
# ------------------------------------------------------- Now have all metadata
@@ -1089,21 +1162,22 @@ sub publish {
# 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
+# $env{'from.'..} so that it can be used by the phase two handler in
# batch mode
my $intr_scrout.=
'';
+ &mt($env{'form.makeobsolete'}?'Make Obsolete':'Finalize Publication').'" />';
}
return($scrout,0);
}
@@ -1296,10 +1391,10 @@ Returns:
=over 4
-=item Scalar string
+=item integer
-String contains status (errors and warnings) and information associated with
-the server's attempts at publication.
+0: fail
+1: success
=cut
@@ -1311,13 +1406,29 @@ sub phasetwo {
my ($r,$source,$target,$style,$distarget,$batch)=@_;
$source=~s/\/+/\//g;
$target=~s/\/+/\//g;
-
- if ($target=~/\_\_\_/) {
- $r->print(
- ''.&mt('Unsupported character combination').
- ' "___" '.&mt('in filename, FAIL').'');
- return 0;
+#
+# Unless trying to get rid of something, check name validity
+#
+ unless ($env{'form.obsolete'}) {
+ if ($target=~/(\_\_\_|\&\&\&|\:\:\:)/) {
+ $r->print(
+ ''.&mt('Unsupported character combination').
+ ' "'.$1.'" '.&mt('in filename, FAIL').'');
+ return 0;
+ }
+ unless ($target=~/\.(\w+)$/) {
+ $r->print(''.&mt('No valid extension found in filename, FAIL').'');
+ return 0;
+ }
+ if ($target=~/\.(\d+)\.(\w+)$/) {
+ $r->print(''.&mt('Cannot publish versioned resource, FAIL').'');
+ return 0;
+ }
}
+
+#
+# End name check
+#
$distarget=~s/\/+/\//g;
my $logfile;
unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
@@ -1327,44 +1438,45 @@ sub phasetwo {
return 0;
}
print $logfile
- "\n================= Publish ".localtime()." Phase Two ================\n".$ENV{'user.name'}.'@'.$ENV{'user.domain'}."\n";
+ "\n================= Publish ".localtime()." Phase Two ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
%metadatafields=();
%metadatakeys=();
- &metaeval(&Apache::lonnet::unescape($ENV{'form.allmeta'}));
+ &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{'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'};
+ $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;
+ $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'}});
+ 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 .= ','.$env{'form.keywords'};
}
}
$allkeywords=~s/[\"\']//g;
@@ -1378,18 +1490,20 @@ sub phasetwo {
if ($metadatafields{'copyright'} eq 'custom') {
my $file=$metadatafields{'customdistributionfile'};
unless ($file=~/\.rights$/) {
- return
+ $r->print(
''.&mt('No valid custom distribution rights file specified, FAIL').
- '';
+ '');
+ return 0;
}
}
{
print $logfile "\nWrite metadata file for ".$source;
my $mfh;
unless ($mfh=Apache::File->new('>'.$source.'.meta')) {
- return
+ $r->print(
''.&mt('Could not write metadata, FAIL').
- '';
+ '');
+ return 0;
}
foreach (sort keys %metadatafields) {
unless ($_=~/\./) {
@@ -1440,7 +1554,9 @@ sub phasetwo {
my $srcd=$1;
unless ($srcd=~/^\/home\/httpd\/html\/res/) {
print $logfile "\nPANIC: Target dir is ".$srcd;
- return "Invalid target directory, FAIL";
+ $r->print(
+ "Invalid target directory, FAIL");
+ return 0;
}
opendir(DIR,$srcd);
while ($filename=readdir(DIR)) {
@@ -1465,8 +1581,9 @@ sub phasetwo {
$r->print(''.&mt('Copied old target file').'
');
} else {
print $logfile "Unable to write ".$copyfile.':'.$!."\n";
- return "".&mt('Failed to copy old target').
- ", $!, ".&mt('FAIL')."";
+ $r->print("".&mt('Failed to copy old target').
+ ", $!, ".&mt('FAIL')."");
+ return 0;
}
# --------------------------------------------------------------- Copy Metadata
@@ -1479,9 +1596,10 @@ sub phasetwo {
} else {
print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
if (-e $target.'.meta') {
- return
+ $r->print(
"".
-&mt('Failed to write old metadata copy').", $!, ".&mt('FAIL')."";
+&mt('Failed to write old metadata copy').", $!, ".&mt('FAIL')."");
+ return 0;
}
}
@@ -1512,8 +1630,9 @@ sub phasetwo {
$r->print(''.&mt('Copied source file').'
');
} else {
print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";
- return "".
- &mt('Failed to copy source').", $!, ".&mt('FAIL')."";
+ $r->print("".
+ &mt('Failed to copy source').", $!, ".&mt('FAIL')."");
+ return 0;
}
# --------------------------------------------------------------- Copy Metadata
@@ -1525,53 +1644,31 @@ sub phasetwo {
$r->print(''.&mt('Copied metadata').'
');
} else {
print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n";
- return
- "".&mt('Failed to write metadata copy').", $!, ".&mt('FAIL')."";
+ $r->print(
+ "".&mt('Failed to write metadata copy').", $!, ".&mt('FAIL')."");
+ return 0;
}
$r->rflush;
-# --------------------------------------------------- Send update notifications
- my @subscribed=&get_subscribed_hosts($target);
- foreach my $subhost (@subscribed) {
- $r->print(''.&mt('Notifying host').' '.$subhost.':');$r->rflush;
- print $logfile "\nNotifying host ".$subhost.':';
- my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
- $r->print($reply.'
');$r->rflush;
- print $logfile $reply;
+# ------------------------------------------------------------- Trigger updates
+ push(@{$modified_urls},[$target,$source]);
+ unless ($registered_cleanup) {
+ $r->register_cleanup(\¬ify);
+ $registered_cleanup=1;
}
-
-# ---------------------------------------- Send update notifications, meta only
- my @subscribedmeta=&get_subscribed_hosts("$target.meta");
- foreach my $subhost (@subscribedmeta) {
- $r->print(''.
-&mt('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(''.&mt('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;
- }
+# ---------------------------------------------------------- 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));
+
# ------------------------------------------------ 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/;
+ $thissrc=~s{^/home/($match_username)/public_html}{/priv/$1};
my $thissrcdir=$thissrc;
$thissrcdir=~s/\/[^\/]+$/\//;
@@ -1586,15 +1683,54 @@ sub phasetwo {
'">'.
&mt('Back to Source Directory').'');
}
- return ''.&mt('Done').'
';
+ $logfile->close();
+ $r->print(''.&mt('Done').'
');
+ 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
+ my @subscribedmeta=&get_subscribed_hosts("$target.meta");
+ foreach my $subhost (@subscribedmeta) {
+ print $logfile "\nNotifying host for metadata only ".$subhost.':';
+ my $reply=&Apache::lonnet::critical('update:'.$target.'.meta',
+ $subhost);
+ print $logfile $reply;
+ }
+# --------------------------------------------------- Notify subscribed courses
+ my %courses=&coursedependencies($target);
+ my $now=time;
+ foreach (keys %courses) {
+ print $logfile "\nNotifying course ".$_.':';
+ my ($cdom,$cname)=split(/\_/,$_);
+ my $reply=&Apache::lonnet::cput
+ ('versionupdate',{$target => $now},$cdom,$cname);
+ print $logfile $reply;
+ }
+ print $logfile "\n============ Done ============\n";
+ $logfile->close();
+ }
+ return OK;
}
#########################################
sub batchpublish {
my ($r,$srcfile,$targetfile)=@_;
- #publication pollutes %ENV with form.* values
- my %oldENV=%ENV;
+ #publication pollutes %env with form.* values
+ my %oldenv=%env;
$srcfile=~s/\/+/\//g;
$targetfile=~s/\/+/\//g;
my $thisdisfn=$srcfile;
@@ -1622,13 +1758,13 @@ sub batchpublish {
$r->print(''.$outstring.'
');
# phase two takes
# my ($source,$target,$style,$distarget,batch)=@_;
-# $ENV{'form.allmeta'},$ENV{'form.title'},$ENV{'form.author'},...
+# $env{'form.allmeta'},$env{'form.title'},$env{'form.author'},...
if (!$error) {
$r->print('');
&phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1);
$r->print('
');
}
- %ENV=%oldENV;
+ %env=%oldenv;
return '';
}
@@ -1645,47 +1781,65 @@ sub publishdirectory {
&mt('Target').': '.$resdir.'
');
my $dirptr=16384; # Mask indicating a directory in stat.cmode.
-
- 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!~/\~$/)) {
+ unless ($env{'form.phase'} eq 'two') {
+# ask user what they want
+ $r->print('');
+ } else {
+# 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'})) {
+ 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;
- }
- } else {
+ $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('
'.&mt('Skipping').' '.$filename.'
');
+ $publishthis=1;
+ }
+
+ if ($publishthis) {
+ &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename);
+ } else {
+ $r->print('
'.&mt('Skipping').' '.$filename.'
');
+ }
+ $r->rflush();
}
- $r->rflush();
}
+ closedir(DIR);
}
- closedir(DIR);
}
#########################################
@@ -1704,8 +1858,7 @@ sub defaultmetapublish {
&Apache::loncommon::content_type($r,'text/html');
$r->send_http_header;
- $r->print('LON-CAPA Publishing');
- $r->print(&Apache::loncommon::bodytag('Catalog Information Publication'));
+ $r->print(&Apache::loncommon::start_page('Catalog Information Publication'));
# ---------------------------------------------------------------- Write Source
my $copyfile=$target;
@@ -1741,7 +1894,7 @@ sub defaultmetapublish {
my $link=$fn;
$link=~s/^\/home\/$cuname\/public_html\//\/priv\/$cuname\//;
$r->print("".&mt('Back to Catalog Information').'');
- $r->print('