--- loncom/publisher/lonpublisher.pm 2007/03/02 23:18:19 1.221 +++ loncom/publisher/lonpublisher.pm 2013/10/16 18:28:18 1.286 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Publication Handler # -# $Id: lonpublisher.pm,v 1.221 2007/03/02 23:18:19 albertel Exp $ +# $Id: lonpublisher.pm,v 1.286 2013/10/16 18:28:18 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -66,10 +66,10 @@ invocation by F: =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. +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 @@ -118,11 +118,13 @@ 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::loncommon(); +use Apache::lonhtmlcommon; use Apache::lonmysql; use Apache::lonlocal; use Apache::loncfile; @@ -143,6 +145,8 @@ my $cudom; my $registered_cleanup; my $modified_urls; +my $lock; + =pod =item B @@ -261,8 +265,9 @@ sub metaread { my ($logfile,$fn,$prefix)=@_; unless (-e $fn) { print($logfile 'No file '.$fn."\n"); - return '
'.&mt('No file').': '. - &Apache::loncfile::display($fn).''; + return '

' + .&mt('No file: [_1]',&Apache::loncfile::display($fn)) + .'

'; } print($logfile 'Processing '.$fn."\n"); my $metastring; @@ -271,8 +276,9 @@ sub metaread { $metastring=join('',<$metafh>); } &metaeval($metastring,$prefix); - return '
'.&mt('Processed file').': '. - &Apache::loncfile::display($fn).''; + return '

' + .&mt('Processed file: [_1]',&Apache::loncfile::display($fn)) + .'

'; } ######################################### @@ -323,30 +329,34 @@ string which presents the form field (fo ######################################### ######################################### sub textfield { - my ($title,$name,$value)=@_; + 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

$title:". - "


". - ''; + return "\n".&Apache::lonhtmlcommon::row_title($title) + .'' + .&Apache::lonhtmlcommon::row_closure($noline); } sub text_with_browse_field { - my ($title,$name,$value,$restriction)=@_; + 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

$title:". - "


". - ''. - 'Select '. - 'Search'; - + return "\n".&Apache::lonhtmlcommon::row_title($title) + .'' + .'
' + .'' + .&mt('Select') + .' ' + .'' + .&mt('Search') + .'' + .&Apache::lonhtmlcommon::row_closure($noline); } sub hiddenfield { @@ -370,16 +380,17 @@ sub selectbox { } else { $env{'form.'.$name}=$idlist[0]; } - my $selout="\n

$title:". - '


'; foreach (@idlist) { - $selout.='';} + $selout.=' selected="selected"'; + } + $selout.='>'.&{$functionref}($_).''; } - return $selout.''; + $selout.=''.&Apache::lonhtmlcommon::row_closure(); + return $selout; } sub select_level_form { @@ -409,16 +420,14 @@ sub urlfixup { if ($url =~ /^mailto:/i) { return $url; } #internal document links need no fixing if ($url =~ /^\#/) { return $url; } - my ($host)=($url=~/(?:(?:http|https|ftp)\:\/\/)*([^\/]+)/); - my %all_hostnames = &Apache::lonnet::all_hostnames(); - foreach my $hostname (values(%all_hostnames)) { - if ($hostname eq $host) { - $url=~s/^(?:http|https|ftp)\:\/\///; - $url=~s/^\Q$host\E//; - } + my ($host)=($url=~m{(?:(?:http|https|ftp)://)*([^/]+)}); + my @lonids = &Apache::lonnet::machine_ids($host); + if (@lonids) { + $url=~s{^(?:http|https|ftp)://}{}; + $url=~s/^\Q$host\E//; } - if ($url=~/^(?:http|https|ftp)\:\/\//) { return $url; } - $url=~s/\~$cuname/res\/$cudom\/$cuname/; + if ($url=~m{^(?:http|https|ftp)://}) { return $url; } + $url=~s{\Q~$cuname\E}{res/$cudom/$cuname}; return $url; } @@ -496,11 +505,15 @@ sub get_subscribed_hosts { $target=~/(.*)\/([^\/]+)$/; my $srcf=$2; opendir(DIR,$1); + # cycle through listed files, subscriptions used to exist + # as "filename.lonid" while ($filename=readdir(DIR)) { if ($filename=~/\Q$srcf\E\.($match_lonid)$/) { my $subhost=$1; - if (($subhost ne 'meta' && $subhost ne 'subscription' && - $subhost ne 'tmp') && + if (($subhost ne 'meta' + && $subhost ne 'subscription' + && $subhost ne 'meta.subscription' + && $subhost ne 'tmp') && ($subhost ne $Apache::lonnet::perlvar{'lonHostID'})) { push(@subscribed,$subhost); } @@ -652,7 +665,7 @@ sub fix_ids_and_indices { join(', ',@duplicatedids)); if ($duplicateids) { print $logfile "Duplicate ID(s) exist, ".join(', ',@duplicatedids)."\n"; - my $outstring=''.&mt('Unable to publish file, it contains duplicated ID(s), ID(s) need to be unique. The duplicated ID(s) are').': '.join(', ',@duplicatedids).''; + my $outstring=''.&mt('Unable to publish file, it contains duplicated ID(s), ID(s) need to be unique. The duplicated ID(s) are').': '.join(', ',@duplicatedids).''; return ($outstring,1); } if ($needsfixup) { @@ -661,6 +674,7 @@ sub fix_ids_and_indices { "Max Index: $maxindex (min 10)\n"; } my $outstring=''; + my $responsecounter=1; my @parser; $parser[0]=HTML::LCParser->new(\$content); $parser[-1]->xml_mode(1); @@ -676,6 +690,11 @@ sub fix_ids_and_indices { next; } if ($lctag eq 'base') { next; } + if (($lctag eq 'part') || ($lctag eq 'problem')) { + $responsecounter=0; + } + if ($lctag=~/response$/) { $responsecounter++; } + if ($lctag eq 'import') { $responsecounter++; } my %parms=%{$token->[2]}; $counter=$addid{$tag}; if (!$counter) { $counter=$addid{$lctag}; } @@ -771,16 +790,66 @@ sub fix_ids_and_indices { } if (!$endtag) { if ($token->[4]=~m:/>$:) { $endtag=' /'; }; } $outstring.='<'.$tag.$newparmstring.$endtag.'>'; - if ($lctag eq 'm' || $lctag eq 'script' - || $lctag eq 'display' || $lctag eq 'tex') { + if ($lctag eq 'm' || $lctag eq 'answer' || $lctag eq 'display' || + $lctag eq 'tex') { $outstring.=&get_all_text_unbalanced('/'.$lctag,\@parser); - } + } elsif ($lctag eq 'script') { + if ($parms{'type'} eq 'loncapa/perl') { + $outstring.=&get_all_text_unbalanced('/'.$lctag,\@parser); + } else { + my $script = &get_all_text_unbalanced('/'.$lctag,\@parser); + if ($script =~ m{\.set\w+(Src|Swf)\(["']}i) { + my @srcs = split(/\.set/i,$script); + if (scalar(@srcs) > 1) { + foreach my $item (@srcs) { + if ($item =~ m{^(FlashPlayerSwf|MediaSrc|XMPSrc|ConfigurationSrc|PosterImageSrc)\((['"])(?:(?!\2).)+\2\)}is) { + my $srctype = $1; + my $quote = $2; + my ($url) = ($item =~ m{^\Q$srctype($quote\E([^$quote]+)\Q$quote)\E}); + $url = &urlfixup($url); + unless ($url=~m{^(?:http|https|ftp)://}) { + $allow{&absoluteurl($url,$target)}=1; + if ($srctype eq 'ConfigurationSrc') { + if ($url =~ m{^(.+/)configuration_express\.xml$}) { +# +# Camtasia 8.1: express_show/spritesheet.png needed, and included in zip archive. +# Not referenced directly in
.html or
_player.html files, +# so add this file to %allow (where
is name user gave to file/archive). +# + my $spritesheet = $1.'express_show/spritesheet.png'; + $allow{&absoluteurl($spritesheet,$target)}=1; + } + } elsif ($srctype eq 'PosterImageSrc') { + if ($url =~ m{^(.+)_First_Frame\.png$}) { + my $prefix = $1; +# +# Camtasia 8.1:
_Thumbnails.png needed, and included in zip archive. +# Not referenced directly in
.html or
_player.html files, +# so add this file to %allow (where
is name user gave to file/archive). +# + my $thumbnail = $prefix.'_Thumbnails.png'; + $allow{&absoluteurl($thumbnail,$target)}=1; + } + } + } + } + } + } + } + $outstring .= $script + } + } } elsif ($token->[0] eq 'E') { if ($token->[2]) { unless ($token->[1] eq 'allow') { $outstring.='[1].'>'; } - } + } + if ((($token->[1] eq 'part') || ($token->[1] eq 'problem')) + && (!$responsecounter)) { + my $outstring=''.&mt('Found [_1] without responses. This resource cannot be published.',$token->[1]).''; + return ($outstring,1); + } } else { $outstring.=$token->[1]; } @@ -823,31 +892,34 @@ sub store_metadata { # Determine if the table exists my $status = &Apache::lonmysql::check_table('metadata'); if (! defined($status)) { - $error='WARNING: Cannot connect to '. - 'database!'; + $error='' + .&mt('WARNING: Cannot connect to database!') + .''; &Apache::lonnet::logthis($error); return ($error,undef); } if ($status == 0) { # It would be nice to actually create the table.... - $error ='WARNING: The metadata table does not '. - 'exist in the LON-CAPA database.'; + $error ='' + .&mt('WARNING: The metadata table does not exist in the LON-CAPA database!') + .''; &Apache::lonnet::logthis($error); return ($error,undef); } my $dbh = &Apache::lonmysql::get_dbh(); - if (($metadata{'obsolete'}) || ($metadata{'copyright'} eq 'priv') || - ($metadata{'copyright'} eq 'custom')) { + if (($metadata{'obsolete'}) || ($metadata{'copyright'} eq 'priv')) { # remove this entry - $status=&LONCAPA::lonmetadata::delete_metadata($dbh,undef, - $metadata{'url'}); + my $delitem = 'url = '.$dbh->quote($metadata{'url'}); + $status = &LONCAPA::lonmetadata::delete_metadata($dbh,undef,$delitem); + } else { $status = &LONCAPA::lonmetadata::update_metadata($dbh,undef,undef, \%metadata); } if (defined($status) && $status ne '') { - $error='Error occured storing new values in '. - 'metadata table in LON-CAPA database'; + $error='' + .&mt('Error occurred saving new values in metadata table in LON-CAPA database!') + .''; &Apache::lonnet::logthis($error); &Apache::lonnet::logthis($status); return ($error,undef); @@ -867,17 +939,21 @@ sub checkonthis { 'return_only_error_and_warning_counts' => 1)); my ($errorcount,$warningcount)=split(':',$result); if (($errorcount) || ($warningcount)) { - $r->print('
'.$uri.': '); - if ($errorcount) { - $r->print(''. - $errorcount.' '. - &mt('error(s)').' '); - } - if ($warningcount) { - $r->print(''. - $warningcount.' '. - &mt('warning(s)').''); - } + $r->print('

'.&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').''); } @@ -916,7 +992,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 @@ -935,7 +1011,7 @@ sub publish { my %allow=(); unless ($logfile=Apache::File->new('>>'.$source.'.log')) { - return (''.&mt('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".$env{'user.name'}.':'.$env{'user.domain'}."\n"; @@ -949,7 +1025,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 @@ -959,27 +1035,37 @@ sub publish { if ($error) { return ($outstring,$error); } # ------------------------------------------------------------ Construct Allows - $scrout.='

'.&mt('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.='
'; - if ($thisdep!~/\*/ && $thisdep!~m|^/adm/|) { - $scrout.=''; + $outdep.='
'; + if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) { + $outdep.=''; } - $scrout.=''.$thisdep.''; - if ($thisdep!~/\*/ && $thisdep!~m|^/adm/|) { - $scrout.=''; + $outdep.=''.$thisdep.''; + if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) { + $outdep.=''; if ( &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'. $thisdep.'.meta') eq '-1') { - $scrout.= ' - '.&mt('Currently not available'). - ''; + $outdep.= ' - '.&mt('Currently not available'). + ''; } else { +# +# Store the fact that the dependency has been used by the target file +# Unfortunately, usage is erroneously named sequsage in lonmeta.pm +# The translation happens in lonmetadata.pm +# my %temphash=(&Apache::lonnet::declutter($target).'___'. &Apache::lonnet::declutter($thisdep).'___usage' => time); @@ -990,6 +1076,12 @@ sub publish { } } } + $outdep.='

'; + } + + if ($outdep) { + $scrout.='

'.&mt('Dependencies').'

' + .$outdep } $outstring=~s/\n*(\<\/[^\>]+\>[^<]*)$/$allowstr\n$1\n/s; @@ -999,9 +1091,9 @@ sub publish { my $org; unless ($org=Apache::File->new('>'.$source)) { print $logfile "No write permit to $source\n"; - return (''.&mt('No write permission to'). + return (''.&mt('No write permission to'). ' '.$source. - ', '.&mt('FAIL').'',1); + ', '.&mt('FAIL').'',1); } print($org $outstring); } @@ -1019,8 +1111,8 @@ sub publish { my %oldparmstores=(); unless ($batch) { - $scrout.='

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

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

'; } @@ -1037,16 +1129,16 @@ sub publish { # ------------------------------------------------ Check out directory hierachy my $thisdisfn=$source; - $thisdisfn=~s/^\/home\/\Q$cuname\E\///; - my @urlparts=split(/\//,$thisdisfn); + $thisdisfn=~s/^\Q$docroot\E\/priv\/\Q$cudom\E\/\Q$cuname\E\///; + my @urlparts=('.',split(/\//,$thisdisfn)); $#urlparts--; - my $currentpath='/home/'.$cuname.'/'; + my $currentpath=$docroot.'/priv/'.$cudom.'/'.$cuname.'/'; my $prefix='../'x($#urlparts); - foreach (@urlparts) { - $currentpath.=$_.'/'; + foreach my $subdir (@urlparts) { + $currentpath.=$subdir.'/'; $scrout.=&metaread($logfile,$currentpath.'default.meta',$prefix); $prefix=~s|^\.\./||; } @@ -1078,7 +1170,7 @@ sub publish { # ------------------------------------------------------------- Save some stuff my %savemeta=(); foreach ('title') { - $savemeta{$_}=$metadatafields{$_}; + if ($metadatafields{$_}) { $savemeta{$_}=$metadatafields{$_}; } } # ------------------------------------------ See if anything new in file itself @@ -1106,7 +1198,7 @@ sub publish { } } if ($chparms) { - $scrout.='

'.&mt('New parameters or stored values'). + $scrout.='

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

'; } @@ -1123,10 +1215,16 @@ sub publish { } } if ($chparms) { - $scrout.='

'.&mt('Obsolete parameters or stored values').': '. - $chparms.'

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

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


'; + $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 @@ -1138,17 +1236,22 @@ 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 my $addkey (split(/[\"\'\,\;]/,$metadatafields{'keywords'})) { $addkey=~s/\s+/ /g; @@ -1166,9 +1269,18 @@ sub publish { # $env{'from.'..} so that it can be used by the phase two handler in # batch mode - my $intr_scrout.= - '
'. - '

'.($env{'form.makeobsolete'}?'':'').'

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

' + .'

'.&mt('Cancel').'

'; + } + $intr_scrout.=&Apache::lonhtmlcommon::start_pick_box(); + $intr_scrout.= &hiddenfield('phase','two'). &hiddenfield('filename',$env{'form.filename'}). &hiddenfield('allmeta',&escape($allmeta)). @@ -1180,10 +1292,7 @@ sub publish { &textfield('Subject','subject',$metadatafields{'subject'}); # --------------------------------------------------- Scan content for keywords - my $keywords_help = Apache::loncommon::help_open_topic("Publishing_Keywords"); - my $KEYWORDS=&mt('Keywords'); - my $CheckAll=&mt('check all'); - my $UncheckAll=&mt('uncheck all'); + my $keywords_help = &Apache::loncommon::help_open_topic("Publishing_Keywords"); my $keywordout=<<"END"; -

$KEYWORDS: - $keywords_help - - -

-
END - $keywordout.=''; + $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 (sort keys %keywords) { - $keywordout.=''; - if ($colcount>10) { - $keywordout.="\n"; - $colcount=0; - } - $colcount++; + 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(); + } + $env{'form.keywords'}=~s/\,$//; - $keywordout.='
'; + } else { + $keywordout .= ''; + } + $keywordout.='
'; + $keywordout.=&Apache::loncommon::end_data_table_row() + .&Apache::loncommon::end_data_table() + .&Apache::lonhtmlcommon::row_closure(); $intr_scrout.=$keywordout; @@ -1234,28 +1361,24 @@ END $intr_scrout.=&textfield('Notes','notes',$metadatafields{'notes'}); - $intr_scrout.= - "\n

".&mt('Abstract').":". - "


". - '

'; + $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Abstract')) + .'' + .&Apache::lonhtmlcommon::row_closure(); $source=~/\.(\w+)$/; + $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(); - $intr_scrout.= - "\n

". - &mt('Lowest Grade Level').':'. - "


". - &select_level_form($metadatafields{'lowestgradelevel'},'lowestgradelevel'). - "\n

". - &mt('Highest Grade Level').':'. - "


". - &select_level_form($metadatafields{'highestgradelevel'},'highestgradelevel'). - &textfield('Standards','standards',$metadatafields{'standards'}); - - - + $intr_scrout.=&textfield('Standards','standards',$metadatafields{'standards'}); $intr_scrout.=&hiddenfield('mime',$1); @@ -1278,15 +1401,21 @@ END $intr_scrout.=&hiddenfield('lastrevisiondate',time); - + my $pubowner_last; + if ($style eq 'prv') { + $pubowner_last = 1; + } $intr_scrout.=&textfield('Publisher/Owner','owner', - $metadatafields{'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'; } @@ -1303,33 +1432,32 @@ END $intr_scrout.=&selectbox('Copyright/Distribution','copyright', $defaultoption, \&Apache::loncommon::copyrightdescription, - (grep !/^public$/,(&Apache::loncommon::copyrightids))); + (grep !/^(public|priv)$/,(&Apache::loncommon::copyrightids))); } else { $intr_scrout.=&selectbox('Copyright/Distribution','copyright', $defaultoption, \&Apache::loncommon::copyrightdescription, - (&Apache::loncommon::copyrightids)); + (grep !/^priv$/,(&Apache::loncommon::copyrightids))); } my $copyright_help = - Apache::loncommon::help_open_topic('Publishing_Copyright'); - $intr_scrout =~ s/Distribution:/'Distribution: ' . $copyright_help/ge; - $intr_scrout.=&text_with_browse_field('Custom Distribution File','customdistributionfile',$metadatafields{'customdistributionfile'},'rights').$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'); - $intr_scrout.= - "\n

'; + $scrout.=$intr_scrout + .&Apache::lonhtmlcommon::end_pick_box() + .'

' + .''; } return($scrout,0); } @@ -1412,17 +1544,17 @@ sub phasetwo { # unless ($env{'form.obsolete'}) { if ($target=~/(\_\_\_|\&\&\&|\:\:\:)/) { - $r->print( - ''.&mt('Unsupported character combination'). - ' "'.$1.'" '.&mt('in filename, FAIL').''); + $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').''); + $r->print(''.&mt('No valid extension found in filename, FAIL').''); return 0; } if ($target=~/\.(\d+)\.(\w+)$/) { - $r->print(''.&mt('Cannot publish versioned resource, FAIL').''); + $r->print(''.&mt('Filename of resource contains internal version number. Cannot publish such resources, FAIL').''); return 0; } } @@ -1434,10 +1566,15 @@ sub phasetwo { my $logfile; unless ($logfile=Apache::File->new('>>'.$source.'.log')) { $r->print( - ''. - &mt('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".$env{'user.name'}.':'.$env{'user.domain'}."\n"; @@ -1492,8 +1629,8 @@ sub phasetwo { my $file=$metadatafields{'customdistributionfile'}; unless ($file=~/\.rights$/) { $r->print( - ''.&mt('No valid custom distribution rights file specified, FAIL'). - ''); + ''.&mt('No valid custom distribution rights file specified, FAIL'). + ''); return 0; } } @@ -1502,8 +1639,8 @@ sub phasetwo { my $mfh; unless ($mfh=Apache::File->new('>'.$source.'.meta')) { $r->print( - ''.&mt('Could not write metadata, FAIL'). - ''); + ''.&mt('Could not write metadata, FAIL'). + ''); return 0; } foreach (sort keys %metadatafields) { @@ -1553,10 +1690,11 @@ sub phasetwo { my $srcf=$2; my $srct=$3; my $srcd=$1; - unless ($srcd=~/^\/home\/httpd\/html\/res/) { + my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'}; + unless ($srcd=~/^\Q$docroot\E\/res/) { print $logfile "\nPANIC: Target dir is ".$srcd; $r->print( - "Invalid target directory, FAIL"); + "".&mt('Invalid target directory, FAIL').""); return 0; } opendir(DIR,$srcd); @@ -1565,25 +1703,24 @@ sub phasetwo { unlink($srcd.'/'.$filename); unlink($srcd.'/'.$filename.'.meta'); } else { - if ($filename=~/\Q$srcf\E\.(\d+)\.\Q$srct\E$/) { + if ($filename=~/^\Q$srcf\E\.(\d+)\.\Q$srct\E$/) { $maxversion=($1>$maxversion)?$1:$maxversion; } } } closedir(DIR); $maxversion++; - $r->print('

Creating old version '.$maxversion.'

'); + $r->print('

'.&mt('Creating old version [_1]',$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('

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

'); + $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied old target file'))); } else { print $logfile "Unable to write ".$copyfile.':'.$!."\n"; - $r->print("".&mt('Failed to copy old target'). - ", $!, ".&mt('FAIL').""); + $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Failed to copy old target').", $!",1)); return 0; } @@ -1593,13 +1730,12 @@ sub phasetwo { if (copy($target.'.meta',$copyfile)) { print $logfile "Copied old target metadata to ".$copyfile."\n"; - $r->print('

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

') + $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied old metadata'))); } else { print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n"; if (-e $target.'.meta') { - $r->print( - "". -&mt('Failed to write old metadata copy').", $!, ".&mt('FAIL').""); + $r->print(&Apache::lonhtmlcommon::confirm_success( + &mt('Failed to write old metadata copy').", $!",1)); return 0; } } @@ -1621,32 +1757,38 @@ sub phasetwo { $path.="/$parts[$count]"; if ((-e $path)!=1) { print $logfile "\nCreating directory ".$path; - $r->print('

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

'); mkdir($path,0777); + $r->print('

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

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

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

'); + $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied source file'))); } else { print $logfile "\nUnable to write ".$copyfile.':'.$!."\n"; - $r->print("". - &mt('Failed to copy source').", $!, ".&mt('FAIL').""); + $r->print(&Apache::lonhtmlcommon::confirm_success( + &mt('Failed to copy source').", $!",1)); return 0; } +# ---------------------------------------------- Delete local tmp-preview files + unlink($copyfile.'.tmp'); # --------------------------------------------------------------- Copy Metadata $copyfile=$copyfile.'.meta'; if (copy($source.'.meta',$copyfile)) { print $logfile "\nCopied original metadata to ".$copyfile."\n"; - $r->print('

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

'); + $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied metadata'))); } else { print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n"; - $r->print( - "".&mt('Failed to write metadata copy').", $!, ".&mt('FAIL').""); + $r->print(&Apache::lonhtmlcommon::confirm_success( + &mt('Failed to write metadata copy').", $!",1)); return 0; } $r->rflush; @@ -1654,7 +1796,8 @@ sub phasetwo { # ------------------------------------------------------------- Trigger updates push(@{$modified_urls},[$target,$source]); unless ($registered_cleanup) { - $r->register_cleanup(\¬ify); + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\¬ify,@{$handlers}]); $registered_cleanup=1; } @@ -1665,27 +1808,30 @@ sub phasetwo { &Apache::lonnet::devalidate_cache_new('meta', &Apache::lonnet::declutter($thisdistarget)); +# ------------------------------------------------------------- Everything done + $logfile->close(); + $r->print('

'.&mt('Done').'

'); + # ------------------------------------------------ Provide link to new resource unless ($batch) { - my $thissrc=$source; - $thissrc=~s{^/home/($match_username)/public_html}{/priv/$1}; - + my $thissrc=&Apache::loncfile::url($source); my $thissrcdir=$thissrc; $thissrcdir=~s/\/[^\/]+$/\//; - $r->print( - '
'. - &mt('View Published Version').''. - '

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

'. - '

'. - &mt('Back to Source Directory').'

'); + &Apache::lonhtmlcommon::actionbox([ + ''. + &mt('View Published Version'). + '', + ''. + &mt('Back to Source'). + '', + ''. + &mt('Back to Source Directory'). + '']) + ); } - $logfile->close(); - $r->print('

'.&mt('Done').'

'); return 1; } @@ -1723,6 +1869,7 @@ sub notify { print $logfile "\n============ Done ============\n"; $logfile->close(); } + if ($lock) { &Apache::lonnet::remove_lock($lock); } return OK; } @@ -1734,8 +1881,6 @@ sub batchpublish { my %oldenv=%env; $srcfile=~s/\/+/\//g; $targetfile=~s/\/+/\//g; - my $thisdisfn=$srcfile; - $thisdisfn=~s/\/home\/korte\/public_html\///; $srcfile=~s/\/+/\//g; my $docroot=$r->dir_config('lonDocRoot'); @@ -1751,7 +1896,10 @@ sub batchpublish { my $thisembstyle=&Apache::loncommon::fileembstyle($thistype); - $r->print('

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

'); + $r->print('

' + .&mt('Publishing [_1]',&Apache::loncfile::display($srcfile)) + .'

' + ); # phase one takes # my ($source,$target,$style,$batch)=@_; @@ -1775,24 +1923,40 @@ sub publishdirectory { my ($r,$fn,$thisdisfn)=@_; $fn=~s/\/+/\//g; $thisdisfn=~s/\/+/\//g; - my $resdir= - $Apache::lonnet::perlvar{'lonDocRoot'}.'/res/'.$cudom.'/'.$cuname.'/'. - $thisdisfn; - $r->print('

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

'. - &mt('Target').': '.$resdir.'
'); + my $thisdisresdir=$thisdisfn; + $thisdisresdir=~s/^\/priv\//\/res\//; + my $resdir = $r->dir_config('lonDocRoot').$thisdisresdir; + $r->print(&Apache::lonhtmlcommon::start_pick_box() + .&Apache::lonhtmlcommon::row_title(&mt('Directory')) + .''.$thisdisfn.'' + .&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title(&mt('Target')) + .''.$thisdisresdir.'' + ); my $dirptr=16384; # Mask indicating a directory in stat.cmode. unless ($env{'form.phase'} eq 'two') { # ask user what they want - $r->print('
'. + $r->print(&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title(&mt('Options')) + ); + $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'). + &checkbox('forceoverride','force directory level metadata over existing'). '
'); + $r->print(&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box() + ); + $lock=0; } else { + $r->print(&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box() + ); + unless ($lock) { $lock=&Apache::lonnet::set_lock(&mt('Publishing [_1]',$fn)); } # actually publish things opendir(DIR,$fn); my @files=sort(readdir(DIR)); @@ -1848,18 +2012,17 @@ sub publishdirectory { 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\//; + $target=~s/^\Q$Apache::lonnet::perlvar{'lonDocRoot'}\E\/priv\//\Q$Apache::lonnet::perlvar{'lonDocRoot'}\E\/res\//; &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; - $r->print(&Apache::loncommon::start_page('Catalog Information Publication')); + $r->print(&Apache::loncommon::start_page('Metadata Publication')); # ---------------------------------------------------------------- Write Source my $copyfile=$target; @@ -1871,16 +2034,20 @@ sub defaultmetapublish { for ($count=5;$count<$#parts;$count++) { $path.="/$parts[$count]"; if ((-e $path)!=1) { - $r->print('

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

'); mkdir($path,0777); + $r->print('

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

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

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

'); } else { - return "". - &mt('Failed to copy source').", $!, ".&mt('FAIL').""; + return "". + &mt('Failed to copy source').", $!, ".&mt('FAIL').""; } # --------------------------------------------------- Send update notifications @@ -1892,9 +2059,7 @@ sub defaultmetapublish { $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("".&mt('Back to Metadata').''); $r->print(&Apache::loncommon::end_page()); return OK; } @@ -1958,59 +2123,34 @@ sub handler { # -------------------------------------------------------------- Check filename my $fn=&unescape($env{'form.filename'}); + ($cuname,$cudom)=&Apache::lonnet::constructaccess($fn); +# ----------------------------------------------------- Do we have permissions? + unless (($cuname) && ($cudom)) { + $r->log_reason($env{'user.name'}.' at '.$env{'user.domain'}. + ' trying to publish file '.$env{'form.filename'}. + ' - not authorized', + $r->filename); + return HTTP_NOT_ACCEPTABLE; + } +# ----------------------------------------------------------------- Get docroot + $docroot=$r->dir_config('lonDocRoot'); - ($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$//; - + +# sanity test on the filename + 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) { + unless (-e $docroot.$fn) { $r->log_reason($cuname.' at '.$cudom. ' trying to publish non-existing file '. $env{'form.filename'}.' ('.$fn.')', @@ -2045,82 +2185,112 @@ sub handler { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; + # Breadcrumbs + &Apache::lonhtmlcommon::clear_breadcrumbs(); + &Apache::lonhtmlcommon::add_breadcrumb({ + 'text' => 'Authoring Space', + 'href' => &Apache::loncommon::authorspace($fn), + }); + &Apache::lonhtmlcommon::add_breadcrumb({ + 'text' => 'Resource Publication', + 'href' => '', + }); + my $js=''; - $r->print(&Apache::loncommon::start_page('Resource Publication',$js)); - - - my $thisfn=$fn; - - my $thistarget=$thisfn; - - $thistarget=~s/^\/home/$targetdir/; - $thistarget=~s/\/public\_html//; - - my $thisdistarget=$thistarget; - $thisdistarget=~s/^\Q$docroot\E//; - - my $thisdisfn=$thisfn; - $thisdisfn=~s/^\/home\/\Q$cuname\E\/public_html\///; + $r->print(&Apache::loncommon::start_page('Resource Publication',$js) + .&Apache::lonhtmlcommon::breadcrumbs() + .&Apache::loncommon::head_subbox( + &Apache::loncommon::CSTR_pageheader($docroot.$fn)) + ); + + my $thisdisfn=&HTML::Entities::encode($fn,'<>&"'); + my $thistarget=$fn; + $thistarget=~s/^\/priv\//\/res\//; + my $thisdistarget=&HTML::Entities::encode($thistarget,'<>&"'); if ($fn=~/\/$/) { # -------------------------------------------------------- This is a directory - &publishdirectory($r,$fn,$thisdisfn); - $r->print('
'.&mt('Return to Directory').''); - - + &publishdirectory($r,$docroot.$fn,$thisdisfn); + $r->print('
'.&mt('Return to Directory').''); } else { # ---------------------- Evaluate individual file, and then output information. - $thisfn=~/\.(\w+)$/; + $fn=~/\.(\w+)$/; my $thistype=$1; my $thisembstyle=&Apache::loncommon::fileembstyle($thistype); if ($thistype eq 'page') { $thisembstyle = 'rat'; } - $r->print('

'.&mt('Publishing').' '. - &Apache::loncommon::filedescription($thistype).' '); + $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('

'.&mt('Target').': '. - $thisdistarget.'
'); - + $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('

'.&mt('Co-Author').': '. - $cuname.&mt(' at ').$cudom.'

'); + $r->print(&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title(&mt('Co-Author')) + .'' + .&Apache::loncommon::plainname($cuname,$cudom) .' ('.$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(&mt('Diffs with Current Version').''); } + + $r->print(&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box() + ); -# ------------------ Publishing from $thisfn to $thistarget with $thisembstyle. +# ---------------------- Publishing from $fn to $thistarget with $thisembstyle. unless ($env{'form.phase'} eq 'two') { # ---------------------------------------------------------- Parse for problems my ($warningcount,$errorcount); if ($thisembstyle eq 'ssi') { - ($warningcount,$errorcount)=&checkonthis($r,$thisfn); + ($warningcount,$errorcount)=&checkonthis($r,$fn); } unless ($errorcount) { my ($outstring,$error)= - &publish($thisfn,$thistarget,$thisembstyle); - $r->print('
'.$outstring); + &publish($docroot.$fn,$docroot.$thistarget,$thisembstyle); + $r->print($outstring); } else { - $r->print('

'. + $r->print('

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

'); } } else { - &phasetwo($r,$thisfn,$thistarget,$thisembstyle,$thisdistarget); - $r->print('
'); + &phasetwo($r,$docroot.$fn,$docroot.$thistarget,$thisembstyle,$thisdistarget); } } $r->print(&Apache::loncommon::end_page());