--- loncom/publisher/lonpublisher.pm 2003/03/14 16:02:49 1.117 +++ loncom/publisher/lonpublisher.pm 2009/11/24 00:57:11 1.249.2.1 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Publication Handler # -# $Id: lonpublisher.pm,v 1.117 2003/03/14 16:02:49 albertel Exp $ +# $Id: lonpublisher.pm,v 1.249.2.1 2009/11/24 00:57:11 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,24 +25,6 @@ # # 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 -# ### ############################################################################### @@ -82,6 +64,26 @@ invocation by F: ErrorDocument 500 /adm/errorhandler +=head1 OVERVIEW + +Authors can only write-access the C 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, while the most recent +version does not carry a version number (C). Servers +subscribing to a changed resource are notified that a new version is +available. + =head1 DESCRIPTION B takes the proper steps to add resources to the LON-CAPA @@ -116,13 +118,22 @@ use Apache::File; use File::Copy; use Apache::Constants qw(:common :http :methods); use HTML::LCParser; +use HTML::Entities; +use Encode::Encoder; use Apache::lonxml; use Apache::loncacc; use DBI; -use Apache::lonnet(); +use Apache::lonnet; 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 %nokey; @@ -132,6 +143,11 @@ my $docroot; my $cuname; my $cudom; +my $registered_cleanup; +my $modified_urls; + +my $lock; + =pod =item B @@ -159,46 +175,54 @@ nothing ######################################### ######################################### +# +# Modifies global %metadatafields %metadatakeys +# + sub metaeval { - my $metastring=shift; + my ($metastring,$prefix)=@_; - 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}=~/$newentry/) || - ($newentry eq '')) { - $metadatafields{$unikey}.=', '.$newentry; - } - } else { - $metadatafields{$unikey}=$parser->get_text('/'.$entry); - } - } - } + 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; + next if ($entry =~ m/^(?:parameter|stores)_/); + if (defined($token->[2]->{'package'})) { + $unikey.="\0package\0".$token->[2]->{'package'}; + } + if (defined($token->[2]->{'part'})) { + $unikey.="\0".$token->[2]->{'part'}; + } + if (defined($token->[2]->{'id'})) { + $unikey.="\0".$token->[2]->{'id'}; + } + if (defined($token->[2]->{'name'})) { + $unikey.="\0".$token->[2]->{'name'}; + } + foreach (@{$token->[3]}) { + $metadatafields{$unikey.'.'.$_}=$token->[2]->{$_}; + if ($metadatakeys{$unikey}) { + $metadatakeys{$unikey}.=','.$_; + } else { + $metadatakeys{$unikey}=$_; + } + } + my $newentry=$parser->get_text('/'.$entry); + if (($entry eq 'customdistributionfile') || + ($entry eq 'sourcerights')) { + $newentry=~s/^\s*//; + if ($newentry !~m|^/res|) { $newentry=$prefix.$newentry; } + } +# actually store + if ( $entry eq 'rule' && exists($metadatafields{$unikey})) { + $metadatafields{$unikey}.=','.$newentry; + } else { + $metadatafields{$unikey}=$newentry; + } + } + } } ######################################### @@ -239,19 +263,23 @@ XHTML text that indicates successful rea ######################################### ######################################### sub metaread { - my ($logfile,$fn)=@_; + my ($logfile,$fn,$prefix)=@_; unless (-e $fn) { print($logfile 'No file '.$fn."\n"); - return '
No file: '.$fn.''; + return '
' + .&mt('No file: [_1]' + ,' '.&Apache::loncfile::display($fn).'
'); } print($logfile 'Processing '.$fn."\n"); my $metastring; { - my $metafh=Apache::File->new($fn); - $metastring=join('',<$metafh>); + my $metafh=Apache::File->new($fn); + $metastring=join('',<$metafh>); } - &metaeval($metastring); - return '
Processed file: '.$fn.''; + &metaeval($metastring,$prefix); + return '
' + .&mt('Processed file: [_1]' + ,' '.&Apache::loncfile::display($fn).'
'); } ######################################### @@ -260,9 +288,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); @@ -303,21 +330,59 @@ string which presents the form field (fo ######################################### ######################################### sub textfield { - my ($title,$name,$value)=@_; - return "\n

$title:
". - ''; + my ($title,$name,$value,$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) + .'' + .&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) + .'' + .'
' + .'' + .&mt('Select') + .' ' + .'' + .&mt('Search') + .'' + .&Apache::lonhtmlcommon::row_closure($noline); } sub hiddenfield { my ($name,$value)=@_; + $env{'form.'.$name}=$value; return "\n".''; } +sub checkbox { + my ($name,$text)=@_; + return "\n
"; +} + sub selectbox { my ($title,$name,$value,$functionref,@idlist)=@_; - my $uctitle=uc($title); - my $selout="\n

$uctitle:". - "
".''; foreach (@idlist) { $selout.='

'.&mt('Warnings and Errors').'

'); + $r->print(''.$uri.':'); + $r->print('
    '); + if ($warningcount) { + $r->print('
  • ' + .&mt('[quant,_1,warning]',$warningcount) + .'
  • '); + } + if ($errorcount) { + $r->print('
  • ' + .&mt('[quant,_1,error]',$errorcount) + .' ' + .'
  • '); + } + $r->print('
'); + } else { + #$r->print(''.&mt('ok').''); + } + $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; } ######################################### @@ -790,7 +948,7 @@ backup copies, performs any automatic pr 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 occured) or 0 +user, the second is an error code, either 1 (an error occurred) or 0 (no error occurred) I @@ -809,12 +967,12 @@ sub publish { my %allow=(); unless ($logfile=Apache::File->new('>>'.$source.'.log')) { - return ('No write permission to user directory, FAIL',1); + return (''.&mt('No write permission to user directory, FAIL').'',1); } 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 # ----------------------------------------------------------------- Backup Copy @@ -823,7 +981,7 @@ sub publish { print $logfile "Copied original file to ".$copyfile."\n"; } else { print $logfile "Unable to write backup ".$copyfile.':'.$!."\n"; - return ("Failed to write backup copy, $!,FAIL",1); + return ("".&mt("Failed to write backup copy, [_1], FAIL",$1)."",1); } # ------------------------------------------------------------- IDs and indices @@ -833,50 +991,60 @@ sub publish { if ($error) { return ($outstring,$error); } # ------------------------------------------------------------ Construct Allows - $scrout.='

Dependencies

'; + my $outdep=''; # Collect dependencies output data my $allowstr=''; - foreach (sort(keys(%allow))) { - my $thisdep=$_; + foreach my $thisdep (sort(keys(%allow))) { if ($thisdep !~ /[^\s]/) { next; } + if ($thisdep =~/\$/) { + $outdep.='
' + .&mt('The resource depends on another resource with variable filename, i.e., [_1].',''.$thisdep.'').'
' + .&mt('You likely need to explicitly allow access to all possible dependencies using the [_1]-tag.','<allow>') + ."
\n"; + } unless ($style eq 'rat') { $allowstr.="\n".''; } - $scrout.='
'; - unless ($thisdep=~/\*/) { - $scrout.=''; + $outdep.='
'; + if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) { + $outdep.=''; } - $scrout.=''.$thisdep.''; - unless ($thisdep=~/\*/) { - $scrout.=''; + $outdep.=''.$thisdep.''; + if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) { + $outdep.=''; if ( &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'. $thisdep.'.meta') eq '-1') { - $scrout.= ' - Currently not available'. - ''; + $outdep.= ' - '.&mt('Currently not available'). + ''; } else { my %temphash=(&Apache::lonnet::declutter($target).'___'. &Apache::lonnet::declutter($thisdep).'___usage' => time); - $thisdep=~/^\/res\/(\w+)\/(\w+)\//; + $thisdep=~m{^/res/($match_domain)/($match_username)/}; if ((defined($1)) && (defined($2))) { &Apache::lonnet::put('nohist_resevaldata',\%temphash, $1,$2); } } } + $outdep.='

'; } - $outstring=~s/\n*(\<\/[^\>]+\>)\s*$/$allowstr\n$1\n/s; - #Encode any High ASCII characters - $outstring=&HTML::Entities::encode($outstring,"\200-\377"); + if ($outdep) { + $scrout.='

'.&mt('Dependencies').'

' + .$outdep + } + $outstring=~s/\n*(\<\/[^\>]+\>[^<]*)$/$allowstr\n$1\n/s; + # ------------------------------------------------------------- Write modified. { my $org; unless ($org=Apache::File->new('>'.$source)) { print $logfile "No write permit to $source\n"; - return ('No write permission to '.$source. - ', FAIL',1); + return (''.&mt('No write permission to'). + ' '.$source. + ', '.&mt('FAIL').'',1); } print($org $outstring); } @@ -886,43 +1054,51 @@ sub publish { # -------------------------------------------- Initial step done, now metadata. # --------------------------------------- Storage for metadata keys and fields. - +# these are globals +# %metadatafields=(); %metadatakeys=(); my %oldparmstores=(); unless ($batch) { - $scrout.='

Metadata Information ' . - Apache::loncommon::help_open_topic("Metadata_Description") + $scrout.='

'.&mt('Metadata Information').' ' . + &Apache::loncommon::help_open_topic("Metadata_Description") . '

'; } # ------------------------------------------------ First, check out environment - unless (-e $source.'.meta') { - $metadatafields{'author'}=$ENV{'environment.firstname'}.' '. - $ENV{'environment.middlename'}.' '. - $ENV{'environment.lastname'}.' '. - $ENV{'environment.generation'}; + if ((!(-e $source.'.meta')) || ($env{'form.forceoverride'})) { + $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; + $metadatafields{'owner'}=$cuname.':'.$cudom; # ------------------------------------------------ Check out directory hierachy my $thisdisfn=$source; - $thisdisfn=~s/^\/home\/$cuname\///; + $thisdisfn=~s/^\/home\/\Q$cuname\E\///; my @urlparts=split(/\//,$thisdisfn); $#urlparts--; my $currentpath='/home/'.$cuname.'/'; + my $prefix='../'x($#urlparts); foreach (@urlparts) { $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) foreach (keys %metadatafields) { @@ -942,19 +1118,21 @@ sub publish { delete $metadatafields{$_}; } } - - } - -# -------------------------------------------------- Parse content for metadata - if ($style eq 'ssi') { - my $oldenv=$ENV{'request.uri'}; - - $ENV{'request.uri'}=$target; - $allmeta=Apache::lonxml::xmlparse(undef,'meta',$content); - $ENV{'request.uri'}=$oldenv; +# ------------------------------------------------------------- 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{$_}; + } + } - &metaeval($allmeta); - } + # ---------------- Find and document discrepancies in the parameters and stores my $chparms=''; @@ -962,14 +1140,17 @@ sub publish { if (($_=~/^parameter/) || ($_=~/^stores/)) { unless ($_=~/\.\w+$/) { unless ($oldparmstores{$_}) { - print $logfile 'New: '.$_."\n"; - $chparms.=$_.' '; + my $disp_key = $_; + $disp_key =~ tr/\0/_/; + print $logfile ('New: '.$disp_key."\n"); + $chparms .= $disp_key.' '; } } } } if ($chparms) { - $scrout.='

New parameters or stored values: '.$chparms; + $scrout.='

'.&mt('New parameters or saved values'). + ': '.$chparms.'

'; } $chparms=''; @@ -977,14 +1158,23 @@ sub publish { if (($_=~/^parameter/) || ($_=~/^stores/)) { unless (($metadatafields{$_.'.name'}) || ($metadatafields{$_.'.package'}) || ($_=~/\.\w+$/)) { - print $logfile 'Obsolete: '.$_."\n"; - $chparms.=$_.' '; + my $disp_key = $_; + $disp_key =~ tr/\0/_/; + print $logfile ('Obsolete: '.$disp_key."\n"); + $chparms.=$disp_key.' '; } } } if ($chparms) { - $scrout.='

Obsolete parameters or stored values: '. - $chparms; + $scrout.='

'.&mt('Obsolete parameters or saved values').': '. + $chparms.'

'.&mt('Warning!'). + '

'. + &mt('If this resource is in active use, student performance data from the previous version may become inaccessible.').'


'; + } + if ($metadatafields{'copyright'} eq 'priv') { + $scrout.='

'.&mt('Warning!'). + '

'. + &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.').'


'; } # ------------------------------------------------------- Now have all metadata @@ -996,41 +1186,64 @@ sub publish { $textonly=~s/\//g; $textonly=~s/\[^\<]+\<\/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; - } - } - } + #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; + } + } + + + } - foreach (split(/\W+/,$metadatafields{'keywords'})) { - $keywords{$_}=1; + 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 -# - unless ($batch) { - $scrout.= - '
'. - '

'. - &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'}); +# 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.='
' + .''; + unless ($env{'form.makeobsolete'}) { + $intr_scrout.='

' + .&mt('Searching for your resource will be based on the following metadata. Please provide as much data as possible.') + .'

' + .'

'; + } + $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 -# --------------------------------------------------- Scan content for keywords - - my $keywords_help = Apache::loncommon::help_open_topic("Publishing_Keywords"); - my $keywordout=<<"END"; + my $keywords_help = &Apache::loncommon::help_open_topic("Publishing_Keywords"); + my $keywordout=<<"END"; -

Keywords: $keywords_help - - -
END - $keywordout.=''; - my $colcount=0; + $keywordout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Keywords')) + .$keywords_help + .'' + .'' + .'


' + .&Apache::loncommon::start_data_table(); + my $cols_per_row = 10; + my $colcount=0; + my $wordcount=0; + my $numkeywords = scalar(keys(%keywords)); + + foreach my $word (sort(keys(%keywords))) { + if ($colcount == 0) { + $keywordout .= &Apache::loncommon::start_data_table_row(); + } + $colcount++; + $wordcount++; + if (($wordcount == $numkeywords) && ($colcount < $cols_per_row)) { + my $colspan = 1+$cols_per_row-$colcount; + $keywordout .= ''; + if ($colcount == $cols_per_row) { + $keywordout.=&Apache::loncommon::end_data_table_row(); + $colcount=0; + } + } + if ($colcount > 0) { + $keywordout .= &Apache::loncommon::end_data_table_row(); + } - foreach (sort keys %keywords) { - $keywordout.='\n"; - $colcount=0; - } - $colcount++; - } + $env{'form.keywords'}=~s/\,$//; - $keywordout.='
'; + } else { + $keywordout .= ''; + } + $keywordout.=''; - if ($colcount>10) { - $keywordout.="
'; + $keywordout.=&Apache::loncommon::end_data_table_row() + .&Apache::loncommon::end_data_table() + .&Apache::lonhtmlcommon::row_closure(); - $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.= - '

Abstract:
'; + $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Abstract')) + .'' + .&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:').' ' + .&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:').' ' + .&select_level_form($metadatafields{'highestgradelevel'},'highestgradelevel') + .&Apache::lonhtmlcommon::row_closure(); - $scrout.=&selectbox('Language','language', - $metadatafields{'language'}, - \&Apache::loncommon::languagedescription, - (&Apache::loncommon::languageids), - ); + $intr_scrout.=&textfield('Standards','standards',$metadatafields{'standards'}); - unless ($metadatafields{'creationdate'}) { - $metadatafields{'creationdate'}=time; - } - $scrout.=&hiddenfield('creationdate', - &Apache::loncommon::unsqltime($metadatafields{'creationdate'})); + $intr_scrout.=&hiddenfield('mime',$1); - $scrout.=&hiddenfield('lastrevisiondate',time); + 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), + ); - $scrout.=&textfield('Publisher/Owner','owner', - $metadatafields{'owner'}); + unless ($metadatafields{'creationdate'}) { + $metadatafields{'creationdate'}=time; + } + $intr_scrout.=&hiddenfield('creationdate', + &Apache::lonmysql::unsqltime($metadatafields{'creationdate'})); + + $intr_scrout.=&hiddenfield('lastrevisiondate',time); + + my $pubowner_last; + if ($style eq 'prv') { + $pubowner_last = 1; + } + $intr_scrout.=&textfield('Publisher/Owner','owner', + $metadatafields{'owner'},$pubowner_last); +# ---------------------------------------------- Retrofix for unused copyright + if ($metadatafields{'copyright'} eq 'free') { + $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. - unless ($style eq 'prv') { - 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)); + if ($style eq 'rat') { +# -------------------------------------- Retrofix for non-applicable copyright + if ($metadatafields{'copyright'} eq 'public') { + delete $metadatafields{'copyright'}; + $defaultoption='default'; } - - my $copyright_help = - Apache::loncommon::help_open_topic('Publishing_Copyright'); - $scrout =~ s/DISTRIBUTION:/'DISTRIBUTION: ' . $copyright_help/ge; - $scrout.=&textfield('Custom Distribution File','customdistributionfile', - $metadatafields{'customdistributionfile'}). - $copyright_help; + $intr_scrout.=&selectbox('Copyright/Distribution','copyright', + $defaultoption, + \&Apache::loncommon::copyrightdescription, + (grep !/^(public|priv)$/,(&Apache::loncommon::copyrightids))); } else { - $scrout.=&hiddenfield('copyright','private'); + $intr_scrout.=&selectbox('Copyright/Distribution','copyright', + $defaultoption, + \&Apache::loncommon::copyrightdescription, + (grep !/^priv$/,(&Apache::loncommon::copyrightids))); } - return ($scrout.'

',0); -# ============================================================================= -# BATCH MODE -# + my $copyright_help = + &Apache::loncommon::help_open_topic('Publishing_Copyright'); + my $replace=&mt('Copyright/Distribution:'); + $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="1" ':''; + $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title($uctitle) + .'' + .&Apache::lonhtmlcommon::row_closure(1); + $intr_scrout.=&text_with_browse_field('Suggested Replacement for Obsolete File', + 'obsoletereplacement', + $metadatafields{'obsoletereplacement'},'',1); } else { -# Transfer metadata directly to environment for stage 2 - foreach (keys %metadatafields) { - $ENV{'form.'.$_}=$metadatafields{$_}; - } - $ENV{'form.addkey'}=''; - $ENV{'form.keywords'}=''; - foreach (keys %keywords) { - if ($metadatafields{'keywords'}) { - if ($metadatafields{'keywords'}=~/$_/) { - $ENV{'form.keywords'}.=$_.','; - } - } elsif (&Apache::loncommon::keyword($_)) { - $ENV{'form.keywords'}.=$_.','; - } - } - $ENV{'form.keywords'}=~s/\,$//; - unless ($ENV{'form.creationdate'}) { $ENV{'form.creationdate'}=time; } - $ENV{'form.lastrevisiondate'}=time; - if ((($style eq 'rat') && ($ENV{'form.copyright'} eq 'public')) || - (!$ENV{'form.copyright'})) { - $ENV{'form.copyright'}='default'; - } - $ENV{'form.allmeta'}=&Apache::lonnet::escape($allmeta); - return ($scrout,0); + $intr_scrout.=&hiddenfield('copyright','private'); + } + } else { + $intr_scrout.= + &hiddenfield('title',$metadatafields{'title'}). + &hiddenfield('author',$metadatafields{'author'}). + &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() + .'

' + .''; } + return($scrout,0); } ######################################### @@ -1187,10 +1474,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 @@ -1202,60 +1489,109 @@ sub phasetwo { my ($r,$source,$target,$style,$distarget,$batch)=@_; $source=~s/\/+/\//g; $target=~s/\/+/\//g; - - if ($target=~/\_\_\_/) { - $r->print( - 'Unsupported character combination "___" 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] in filename, FAIL.',"'.$1.'"). + ''); + 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')) { $r->print( - 'No write permission to user directory, FAIL'); + ''. + &mt('No write permission to user directory, FAIL').''); return 0; } + + if ($source =~ /\.rights$/) { + $r->print('

'.&mt('Warning: It can take up to 1 hour for rights changes to fully propagate.').'

'); + } + print $logfile - "\n================= Publish ".localtime()." Phase Two ================\n"; + "\n================= Publish ".localtime()." Phase Two ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n"; %metadatafields=(); %metadatakeys=(); + + &metaeval(&unescape($env{'form.allmeta'})); - &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{'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{'dependencies'}=$ENV{'form.dependencies'}; + $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'}}); + 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/\W+/\,/; - $allkeywords=~s/^\,//; + $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( + ''.&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 - 'Could not write metadata, FAIL'; + $r->print( + ''.&mt('Could not write metadata, FAIL'). + ''); + return 0; } foreach (sort keys %metadatafields) { unless ($_=~/\./) { @@ -1270,11 +1606,11 @@ sub phasetwo { print $mfh ' '.$_.'="'.$value.'"'; } print $mfh '>'. - &HTML::Entities::encode($metadatafields{$unikey}) + &HTML::Entities::encode($metadatafields{$unikey},'<>&"') .''; } } - $r->print('

Wrote Metadata'); + $r->print('

'.&mt('Wrote Metadata').'

'); print $logfile "\nWrote metadata"; } @@ -1282,20 +1618,19 @@ sub phasetwo { $metadatafields{'url'} = $distarget; $metadatafields{'version'} = 'current'; - unless ($metadatafields{'copyright'} eq 'priv') { - my ($error,$success) = &store_metadata(\%metadatafields); - if ($success) { - $r->print('

Synchronized SQL metadata database'); - print $logfile "\nSynchronized SQL metadata database"; - } else { - $r->print($error); - print $logfile "\n".$error; - } + + my ($error,$success) = &store_metadata(%metadatafields); + if ($success) { + $r->print('

'.&mt('Synchronized SQL metadata database').'

'); + print $logfile "\nSynchronized SQL metadata database"; } else { - $r->print('

Private Publication - did not synchronize database'); - print $logfile "\nPrivate: Did not synchronize data into ". - "SQL metadata database"; + $r->print($error); + print $logfile "\n".$error; } +# --------------------------------------------- Delete author resource messages + my $delresult=&Apache::lonmsg::del_url_author_res_msg($target); + $r->print('

'.&mt('Removing error messages:').' '.$delresult.'

'); + print $logfile "\nRemoving error messages: $delresult"; # ----------------------------------------------------------- Copy old versions if (-e $target) { @@ -1307,7 +1642,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( + "".&mt('Invalid target directory, FAIL').""); + return 0; } opendir(DIR,$srcd); while ($filename=readdir(DIR)) { @@ -1315,24 +1652,26 @@ sub phasetwo { unlink($srcd.'/'.$filename); unlink($srcd.'/'.$filename.'.meta'); } else { - if ($filename=~/$srcf\.(\d+)\.$srct$/) { + 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; + $r->print('

Creating old version '.$maxversion.'

'); + print $logfile "\nCreating old version ".$maxversion."\n"; my $copyfile=$srcd.'/'.$srcf.'.'.$maxversion.'.'.$srct; if (copy($target,$copyfile)) { print $logfile "Copied old target to ".$copyfile."\n"; - $r->print('

Copied old target file'); + $r->print('

'.&mt('Copied old target file').'

'); } else { print $logfile "Unable to write ".$copyfile.':'.$!."\n"; - return "Failed to copy old target, $!, FAIL"; + $r->print("".&mt('Failed to copy old target'). + ", $!, ".&mt('FAIL').""); + return 0; } # --------------------------------------------------------------- Copy Metadata @@ -1341,18 +1680,20 @@ sub phasetwo { if (copy($target.'.meta',$copyfile)) { print $logfile "Copied old target metadata to ".$copyfile."\n"; - $r->print('

Copied old metadata') + $r->print('

'.&mt('Copied old metadata').'

') } else { print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n"; if (-e $target.'.meta') { - return - "Failed to write old metadata copy, $!, FAIL"; + $r->print( + "". +&mt('Failed to write old metadata copy').", $!, ".&mt('FAIL').""); + return 0; } } } else { - $r->print('

Initial version'); + $r->print('

'.&mt('Initial version').'

'); print $logfile "\nInitial version"; } @@ -1367,17 +1708,19 @@ sub phasetwo { $path.="/$parts[$count]"; if ((-e $path)!=1) { print $logfile "\nCreating directory ".$path; - $r->print('

Created directory '.$parts[$count]); + $r->print('

'.&mt('Created directory').' '.$parts[$count].'

'); mkdir($path,0777); } } if (copy($source,$copyfile)) { print $logfile "\nCopied original source to ".$copyfile."\n"; - $r->print('

Copied source file'); + $r->print('

'.&mt('Copied source file').'

'); } else { print $logfile "\nUnable to write ".$copyfile.':'.$!."\n"; - return "Failed to copy source, $!, FAIL"; + $r->print("". + &mt('Failed to copy source').", $!, ".&mt('FAIL').""); + return 0; } # --------------------------------------------------------------- Copy Metadata @@ -1386,73 +1729,97 @@ sub phasetwo { if (copy($source.'.meta',$copyfile)) { print $logfile "\nCopied original metadata to ".$copyfile."\n"; - $r->print('

Copied metadata'); + $r->print('

'.&mt('Copied metadata').'

'); } else { print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n"; - return - "Failed to write metadata copy, $!, 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('

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('

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; - } +# ---------------------------------------------------------- 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/^$docroot//; 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/\/[^\/]+$/\//; $r->print( - '


'. - 'View Published Version'. - '

Back to Source'. + '


'. + &mt('View Published Version').''. + '

'. + &mt('Back to Source').'

'. '

Back to Source Directory'); + '">'. + &mt('Back to Source Directory').'

'); } + $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(); + } + if ($lock) { &Apache::lonnet::remove_lock($lock); } + return OK; } ######################################### sub batchpublish { my ($r,$srcfile,$targetfile)=@_; + #publication pollutes %env with form.* values + my %oldenv=%env; $srcfile=~s/\/+/\//g; $targetfile=~s/\/+/\//g; my $thisdisfn=$srcfile; @@ -1461,20 +1828,18 @@ sub batchpublish { my $docroot=$r->dir_config('lonDocRoot'); my $thisdistarget=$targetfile; - $thisdistarget=~s/^$docroot//; + $thisdistarget=~s/^\Q$docroot\E//; - undef %metadatafields; - undef %metadatakeys; - %metadatafields=(); - %metadatakeys=(); - $srcfile=~/\.(\w+)$/; - my $thistype=$1; + %metadatafields=(); + %metadatakeys=(); + $srcfile=~/\.(\w+)$/; + my $thistype=$1; - my $thisembstyle=&Apache::loncommon::fileembstyle($thistype); + my $thisembstyle=&Apache::loncommon::fileembstyle($thistype); - $r->print('

Publishing '.$thisdisfn.'

'); + $r->print('

'.&mt('Publishing').' '.$thisdisfn.'

'); # phase one takes # my ($source,$target,$style,$batch)=@_; @@ -1482,12 +1847,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; return ''; } @@ -1498,53 +1864,129 @@ sub publishdirectory { $fn=~s/\/+/\//g; $thisdisfn=~s/\/+/\//g; my $resdir= - $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cudom.'/'.$cuname.'/'. - $thisdisfn; - $r->print('

Directory '.$thisdisfn.'

'. - '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!~/\~$/)) { + $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cudom.'/'.$cuname.'/'. + $thisdisfn; + $r->print('

'.&mt('Directory').' '.$thisdisfn.'

'. + &mt('Target').': '.$resdir.'
'); + + my $dirptr=16384; # Mask indicating a directory in stat.cmode. + unless ($env{'form.phase'} eq 'two') { +# ask user what they want + $r->print('
'. + &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 catalog information over existing'). + '
'); + $lock=0; + } else { + 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) { + 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('
Skipping '.$filename.'
'); - } - $r->rflush(); - } - } - closedir(DIR); + $publishthis=1; + } + + if ($publishthis) { + &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename); + } else { + $r->print('
'.&mt('Skipping').' '.$filename.'
'); + } + $r->rflush(); + } + } + closedir(DIR); + } +} + +######################################### +# publish a default.meta file + +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\//; + + + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; + + $r->print(&Apache::loncommon::start_page('Catalog Information Publication')); + +# ---------------------------------------------------------------- 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) { + $r->print('

'.&mt('Created directory').' '.$parts[$count].'

'); + mkdir($path,0777); + } + } + + if (copy($fn,$copyfile)) { + $r->print('

'.&mt('Copied source file').'

'); + } else { + return "". + &mt('Failed to copy source').", $!, ".&mt('FAIL').""; + } + +# --------------------------------------------------- Send update notifications + + my @subscribed=&get_subscribed_hosts($target); + foreach my $subhost (@subscribed) { + $r->print('

'.&mt('Notifying host').' '.$subhost.':');$r->rflush; + my $reply=&Apache::lonnet::critical('update:'.$target,$subhost); + $r->print($reply.'


');$r->rflush; + } +# ------------------------------------------------------------------- Link back + my $link=$fn; + $link=~s/^\/home\/$cuname\/public_html\//\/priv\/$cuname\//; + $r->print("".&mt('Back to Catalog Information').''); + $r->print(&Apache::loncommon::end_page()); + return OK; } ######################################### @@ -1587,161 +2029,218 @@ Publishing from $thisfn to $thistarget w ######################################### ######################################### sub handler { - my $r=shift; + my $r=shift; - if ($r->header_only) { - $r->content_type('text/html'); - $r->send_http_header; - return OK; - } + if ($r->header_only) { + &Apache::loncommon::content_type($r,'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']); +# -------------------------------------- Flag and buffer for registered cleanup + $registered_cleanup=0; + @{$modified_urls}=(); # -------------------------------------------------------------- Check filename - my $fn=&Apache::lonnet::unescape($ENV{'form.filename'}); + my $fn=&unescape($env{'form.filename'}); + ($cuname,$cudom)= + &Apache::loncacc::constructaccess($fn,$r->dir_config('lonDefDomain')); + +# 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; - } - - ($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 ($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; + } + + 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; + } + + $fn=~s{^http://[^/]+}{}; + $fn=~s{^/~($match_username)}{/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') { + 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; + } # -------------------------------- File is there and owned, init lookup tables. - %addid=(); + %addid=(); + + { + my $fh=Apache::File->new($r->dir_config('lonTabDir').'/addid.tab'); + while (<$fh>=~/(\w+)\s+(\w+)/) { + $addid{$1}=$2; + } + } - { - 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; - } - } + %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('LON-CAPA Publishing'); - $r->print(&Apache::loncommon::bodytag('Resource Publication')); + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; + + my $js=''; + $r->print(&Apache::loncommon::start_page('Resource Publication',$js)); - my $thisfn=$fn; + my $thisfn=$fn; - my $thistarget=$thisfn; + my $thistarget=$thisfn; - $thistarget=~s/^\/home/$targetdir/; - $thistarget=~s/\/public\_html//; + $thistarget=~s/^\/home/$targetdir/; + $thistarget=~s/\/public\_html//; - my $thisdistarget=$thistarget; - $thisdistarget=~s/^$docroot//; + my $thisdistarget=$thistarget; + $thisdistarget=~s/^\Q$docroot\E//; - my $thisdisfn=$thisfn; - $thisdisfn=~s/^\/home\/$cuname\/public_html\///; + my $thisdisfn=$thisfn; + $thisdisfn=~s/^\/home\/\Q$cuname\E\/public_html\///; - if ($fn=~/\/$/) { + if ($fn=~/\/$/) { # -------------------------------------------------------- This is a directory - &publishdirectory($r,$fn,$thisdisfn); + &publishdirectory($r,$fn,$thisdisfn); + $r->print('
'.&mt('Return to Directory').''); - } else { + + } else { # ---------------------- Evaluate individual file, and then output information. - $thisfn=~/\.(\w+)$/; - my $thistype=$1; - my $thisembstyle=&Apache::loncommon::fileembstyle($thistype); - - $r->print('

Publishing '. - &Apache::loncommon::filedescription($thistype).' '. - ''.$thisdisfn. - '

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

'); - } + $thisfn=~/\.(\w+)$/; + my $thistype=$1; + my $thisembstyle=&Apache::loncommon::fileembstyle($thistype); + if ($thistype eq 'page') { $thisembstyle = 'rat'; } + + $r->print('

'.&mt('Publishing [_1]',''.$thisdisfn.'').'

'); + + $r->print('

'.&mt('Resource Details').'

'); + + $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')) + .'' + ); + $r->print(< +$thisdisfn +ENDCAPTION + $r->print('' + .&Apache::lonhtmlcommon::row_closure() + ); + + $r->print(&Apache::lonhtmlcommon::row_title(&mt('Target')) + .''.$thisdistarget.'' + ); + if (($cuname ne $env{'user.name'})||($cudom ne $env{'user.domain'})) { + $r->print(&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title(&mt('Co-Author')) + .'' + .&mt('[_1] at [_2]',$cuname,$cudom) + .'' + ); + } + + if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') { + $r->print(&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title(&mt('Diffs'))); + $r->print(< +ENDDIFF + $r->print(&mt('Diffs with Current Version').''); + } + + $r->print(&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box() + ); # ------------------ 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(''); + 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('

'. + &mt('The document contains errors and cannot be published.'). + '

'); + } + } else { + &phasetwo($r,$thisfn,$thistarget,$thisembstyle,$thisdistarget); + $r->print('
'); + } + } + $r->print(&Apache::loncommon::end_page()); - return OK; + return OK; } 1; @@ -1751,5 +2250,7 @@ __END__ =back +=back + =cut 500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.