--- loncom/publisher/lonpublisher.pm 2002/11/06 22:36:08 1.107 +++ loncom/publisher/lonpublisher.pm 2004/07/03 03:32:30 1.175 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Publication Handler # -# $Id: lonpublisher.pm,v 1.107 2002/11/06 22:36:08 albertel Exp $ +# $Id: lonpublisher.pm,v 1.175 2004/07/03 03:32:30 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -25,28 +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 -# 04/16/2001 Scott Harrison -# 05/03,05/05,05/07 Gerd Kortemeyer -# 05/28/2001 Scott Harrison -# 06/23,08/07,08/11,8/13,8/17,8/18,8/24,9/26,10/16 Gerd Kortemeyer -# 12/04,12/05 Guy Albertelli -# 12/05 Gerd Kortemeyer -# 12/05 Guy Albertelli -# 12/06,12/07 Gerd Kortemeyer -# 12/15,12/16 Scott Harrison -# 12/25 Gerd Kortemeyer -# YEAR=2002 -# 1/16,1/17 Scott Harrison -# 1/17 Gerd Kortemeyer -# ### ############################################################################### @@ -86,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 @@ -121,12 +119,15 @@ use File::Copy; use Apache::Constants qw(:common :http :methods); use HTML::LCParser; use Apache::lonxml; -use Apache::lonhomework; use Apache::loncacc; use DBI; use Apache::lonnet(); use Apache::loncommon(); use Apache::lonmysql; +use Apache::lonlocal; +use Apache::loncfile; +use LONCAPA::lonmetadata; +use Apache::lonmsg; use vars qw(%metadatafields %metadatakeys); my %addid; @@ -164,46 +165,53 @@ 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; + 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}=$_; + } + } + 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; + } + } + } } ######################################### @@ -244,19 +252,21 @@ 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').': '. + &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').': '. + &Apache::loncfile::display($fn).''; } ######################################### @@ -309,20 +319,33 @@ string which presents the form field (fo ######################################### sub textfield { my ($title,$name,$value)=@_; - return "\n

$title:
". + $value=~s/^\s+//gs; + $value=~s/\s+$//gs; + $value=~s/\s+/ /gs; + $title=&mt($title); + $ENV{'form.'.$name}=$value; + return "\n

$title:". + "


". ''; } sub hiddenfield { my ($name,$value)=@_; + $ENV{'form.'.$name}=$value; return "\n".''; } sub selectbox { my ($title,$name,$value,$functionref,@idlist)=@_; - my $uctitle=uc($title); - my $selout="\n

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

Dependencies

'; + $scrout.='

'.&mt('Dependencies').'

'; my $allowstr=''; foreach (sort(keys(%allow))) { my $thisdep=$_; @@ -843,17 +905,17 @@ sub publish { unless ($style eq 'rat') { $allowstr.="\n".''; } - $scrout.='
'; - unless ($thisdep=~/\*/) { + $scrout.='
'; + if ($thisdep!~/\*/ && $thisdep!~m|^/adm/|) { $scrout.=''; } $scrout.=''.$thisdep.''; - unless ($thisdep=~/\*/) { + if ($thisdep!~/\*/ && $thisdep!~m|^/adm/|) { $scrout.=''; if ( &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'. $thisdep.'.meta') eq '-1') { - $scrout.= ' - Currently not available'. + $scrout.= ' - '.&mt('Currently not available'). ''; } else { my %temphash=(&Apache::lonnet::declutter($target).'___'. @@ -867,19 +929,17 @@ sub publish { } } } - $outstring=~s/\n*(\<\/[^\>]+\>)\s*$/$allowstr\n$1\n/s; + $outstring=~s/\n*(\<\/[^\>]+\>[^<]*)$/$allowstr\n$1\n/s; - #Encode any High ASCII characters - $outstring=&HTML::Entities::encode($outstring,"\200-\377"); # ------------------------------------------------------------- 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'; + return (''.&mt('No write permission to'). + ' '.$source. + ', '.&mt('FAIL').'',1); } print($org $outstring); } @@ -889,14 +949,15 @@ 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 ' . + $scrout.='

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

'; } @@ -914,17 +975,23 @@ sub publish { # ------------------------------------------------ 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) @@ -945,219 +1012,259 @@ sub publish { delete $metadatafields{$_}; } } - - } +# ------------------------------------------ See if anything new in file itself + + $allmeta=&parseformeta($source,$style); -# -------------------------------------------------- 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; + } - &metaeval($allmeta); - } + # ---------------- Find and document discrepancies in the parameters and stores - my $chparms=''; - foreach (sort keys %metadatafields) { - if (($_=~/^parameter/) || ($_=~/^stores/)) { - unless ($_=~/\.\w+$/) { - unless ($oldparmstores{$_}) { - print $logfile 'New: '.$_."\n"; - $chparms.=$_.' '; - } - } - } - } - if ($chparms) { - $scrout.='

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

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

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

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


'; + } # ------------------------------------------------------- Now have all metadata - my %keywords=(); + my %keywords=(); - if (length($content)<500000) { - my $textonly=$content; - $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; - } - } - } + if (length($content)<500000) { + my $textonly=$content; + $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; + } + } + } - 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.= + ''. + '

'. + &hiddenfield('phase','two'). + &hiddenfield('filename',$ENV{'form.filename'}). + &hiddenfield('allmeta',&Apache::lonnet::escape($allmeta)). + &hiddenfield('dependencies',join(',',keys %allow)). + &textfield('Title','title',$metadatafields{'title'}). + &textfield('Author(s)','author',$metadatafields{'author'}). + &textfield('Subject','subject',$metadatafields{'subject'}); # --------------------------------------------------- Scan content for keywords - my $keywords_help = Apache::loncommon::help_open_topic("Publishing_Keywords"); - my $keywordout=<<"END"; + 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 $keywordout=<<"END"; -

Keywords: $keywords_help - - +

$KEYWORDS: + $keywords_help + + +


END - $keywordout.=''; - my $colcount=0; + $keywordout.='
'; + my $colcount=0; - foreach (sort keys %keywords) { - $keywordout.='\n"; - $colcount=0; - } - $colcount++; - } - - $keywordout.='
'; - if ($colcount>10) { - $keywordout.="
'; + foreach (sort keys %keywords) { + $keywordout.=''; + if ($colcount>10) { + $keywordout.="\n"; + $colcount=0; + } + $colcount++; + } + $ENV{'form.keywords'}=~s/\,$//; + + $keywordout.=''; + + $intr_scrout.=$keywordout; + + $intr_scrout.=&textfield('Additional Keywords','addkey',''); + + $intr_scrout.=&textfield('Notes','notes',$metadatafields{'notes'}); - $scrout.=$keywordout; + $intr_scrout.= + "\n

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


". + '

'; - $scrout.=&textfield('Additional Keywords','addkey',''); + $source=~/\.(\w+)$/; - $scrout.=&textfield('Notes','notes',$metadatafields{'notes'}); - $scrout.= - '

Abstract:
'; + $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'}); - $source=~/\.(\w+)$/; - $scrout.=&hiddenfield('mime',$1); - $scrout.=&selectbox('Language','language', - $metadatafields{'language'}, - \&Apache::loncommon::languagedescription, - (&Apache::loncommon::languageids), + + $intr_scrout.=&hiddenfield('mime',$1); + + 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), ); - unless ($metadatafields{'creationdate'}) { - $metadatafields{'creationdate'}=time; - } - $scrout.=&hiddenfield('creationdate', - &Apache::loncommon::unsqltime($metadatafields{'creationdate'})); + unless ($metadatafields{'creationdate'}) { + $metadatafields{'creationdate'}=time; + } + $intr_scrout.=&hiddenfield('creationdate', + &Apache::lonmysql::unsqltime($metadatafields{'creationdate'})); + + $intr_scrout.=&hiddenfield('lastrevisiondate',time); - $scrout.=&hiddenfield('lastrevisiondate',time); - - $scrout.=&textfield('Publisher/Owner','owner', - $metadatafields{'owner'}); + $intr_scrout.=&textfield('Publisher/Owner','owner', + $metadatafields{'owner'}); +# ---------------------------------------------- Retrofix for unused copyright + if ($metadatafields{'copyright'} eq 'free') { + $metadatafields{'copyright'}='default'; + $metadatafields{'sourceavail'}='open'; + } +# ------------------------------------------------ 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. - 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)); - } - - my $copyright_help = - Apache::loncommon::help_open_topic('Publishing_Copyright'); - $scrout =~ s/DISTRIBUTION:/'DISTRIBUTION: ' . $copyright_help/ge; - return $scrout. - '

'; -# ============================================================================= -# BATCH MODE -# - } 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'}.=$_.','; - } + if ($style eq 'rat') { +# -------------------------------------- Retrofix for non-applicable copyright + if ($metadatafields{'copyright'} eq 'public') { + delete $metadatafields{'copyright'}; + $defaultoption='default'; + } + $intr_scrout.=&selectbox('Copyright/Distribution','copyright', + $defaultoption, + \&Apache::loncommon::copyrightdescription, + (grep !/^public$/,(&Apache::loncommon::copyrightids))); + } else { + $intr_scrout.=&selectbox('Copyright/Distribution','copyright', + $defaultoption, + \&Apache::loncommon::copyrightdescription, + (&Apache::loncommon::copyrightids)); + } + my $copyright_help = + Apache::loncommon::help_open_topic('Publishing_Copyright'); + $intr_scrout =~ s/DISTRIBUTION:/'DISTRIBUTION: ' . $copyright_help/ge; + $intr_scrout.=&textfield('Custom Distribution File','customdistributionfile', + $metadatafields{'customdistributionfile'}). + $copyright_help; + $intr_scrout.=&selectbox('Source Distribution','sourceavail', + $defaultsourceoption, + \&Apache::loncommon::source_copyrightdescription, + (&Apache::loncommon::source_copyrightids)); + $intr_scrout.=&textfield('Source Custom Distribution File','sourcerights', + $metadatafields{'sourcerights'}); + my $uctitle=&mt('Obsolete'); + $intr_scrout.= + "\n

$uctitle:". + '

'; + } + return($scrout,0); } ######################################### @@ -1203,18 +1310,27 @@ sub phasetwo { my ($r,$source,$target,$style,$distarget,$batch)=@_; $source=~s/\/+/\//g; $target=~s/\/+/\//g; + + if ($target=~/\_\_\_/) { + $r->print( + ''.&mt('Unsupported character combination'). + ' "___" '.&mt('in filename, FAIL').''); + return 0; + } $distarget=~s/\/+/\//g; my $logfile; unless ($logfile=Apache::File->new('>>'.$source.'.log')) { - return - 'No write permission to user directory, FAIL'; + $r->print( + ''. + &mt('No write permission to user directory, FAIL').''); + return 0; } print $logfile - "\n================= Publish ".localtime()." Phase Two ================\n"; + "\n================= Publish ".localtime()." Phase Two ================\n".$ENV{'user.name'}.'@'.$ENV{'user.domain'}."\n"; %metadatafields=(); %metadatakeys=(); - + &metaeval(&Apache::lonnet::unescape($ENV{'form.allmeta'})); $metadatafields{'title'}=$ENV{'form.title'}; @@ -1228,7 +1344,19 @@ sub phasetwo { $metadatafields{'lastrevisiondate'}=$ENV{'form.lastrevisiondate'}; $metadatafields{'owner'}=$ENV{'form.owner'}; $metadatafields{'copyright'}=$ENV{'form.copyright'}; + $metadatafields{'standards'}=$ENV{'form.standards'}; + $metadatafields{'lowestgradelevel'}=$ENV{'form.lowestgradelevel'}; + $metadatafields{'highestgradelevel'}=$ENV{'form.highestgradelevel'}; + $metadatafields{'customdistributionfile'}= + $ENV{'form.customdistributionfile'}; + $metadatafields{'sourceavail'}=$ENV{'form.sourceavail'}; + $metadatafields{'obsolete'}=$ENV{'form.obsolete'}; + $metadatafields{'obsoletereplacement'}= + $ENV{'form.obsoletereplacement'}; $metadatafields{'dependencies'}=$ENV{'form.dependencies'}; + $metadatafields{'modifyinguser'}=$ENV{'user.name'}.'@'. + $ENV{'user.domain'}; + $metadatafields{'authorspace'}=$cuname.'@'.$cudom; my $allkeywords=$ENV{'form.addkey'}; if (exists($ENV{'form.keywords'})) { @@ -1238,16 +1366,29 @@ sub phasetwo { $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$/) { + return + ''.&mt('No valid custom distribution rights file specified, FAIL'). + ''; + } + } { print $logfile "\nWrite metadata file for ".$source; my $mfh; unless ($mfh=Apache::File->new('>'.$source.'.meta')) { return - 'Could not write metadata, FAIL'; + ''.&mt('Could not write metadata, FAIL'). + ''; } foreach (sort keys %metadatafields) { unless ($_=~/\./) { @@ -1262,11 +1403,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"; } @@ -1274,20 +1415,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) { @@ -1299,7 +1439,7 @@ sub phasetwo { my $srcd=$1; unless ($srcd=~/^\/home\/httpd\/html\/res/) { print $logfile "\nPANIC: Target dir is ".$srcd; - return "Invalid target directory, FAIL"; + return "Invalid target directory, FAIL"; } opendir(DIR,$srcd); while ($filename=readdir(DIR)) { @@ -1307,24 +1447,25 @@ 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"; + return "".&mt('Failed to copy old target'). + ", $!, ".&mt('FAIL').""; } # --------------------------------------------------------------- Copy Metadata @@ -1333,18 +1474,19 @@ 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"; + "". +&mt('Failed to write old metadata copy').", $!, ".&mt('FAIL').""; } } } else { - $r->print('

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

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

'); print $logfile "\nInitial version"; } @@ -1359,17 +1501,18 @@ 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"; + return "". + &mt('Failed to copy source').", $!, ".&mt('FAIL').""; } # --------------------------------------------------------------- Copy Metadata @@ -1378,21 +1521,21 @@ 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"; + "".&mt('Failed to write metadata copy').", $!, ".&mt('FAIL').""; } $r->rflush; # --------------------------------------------------- Send update notifications my @subscribed=&get_subscribed_hosts($target); foreach my $subhost (@subscribed) { - $r->print('

Notifying host '.$subhost.':');$r->rflush; + $r->print('

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


');$r->rflush; print $logfile $reply; } @@ -1400,11 +1543,12 @@ sub phasetwo { my @subscribedmeta=&get_subscribed_hosts("$target.meta"); foreach my $subhost (@subscribedmeta) { - $r->print('

Notifying host for metadata only '.$subhost.':');$r->rflush; + $r->print('

'. +&mt('Notifying host for metadata only').' '.$subhost.':');$r->rflush; print $logfile "\nNotifying host for metadata only ".$subhost.':'; my $reply=&Apache::lonnet::critical('update:'.$target.'.meta', $subhost); - $r->print($reply.'
');$r->rflush; + $r->print($reply.'


');$r->rflush; print $logfile $reply; } @@ -1412,18 +1556,18 @@ sub phasetwo { my %courses=&coursedependencies($target); my $now=time; foreach (keys %courses) { - $r->print('

Notifying course '.$_.':');$r->rflush; + $r->print('

'.&mt('Notifying course').' '.$_.':');$r->rflush; print $logfile "\nNotifying host ".$_.':'; my ($cdom,$cname)=split(/\_/,$_); my $reply=&Apache::lonnet::cput ('versionupdate',{$target => $now},$cdom,$cname); - $r->print($reply.'
');$r->rflush; + $r->print($reply.'


');$r->rflush; print $logfile $reply; } # ------------------------------------------------ Provide link to new resource unless ($batch) { my $thisdistarget=$target; - $thisdistarget=~s/^$docroot//; + $thisdistarget=~s/^\Q$docroot\E//; my $thissrc=$source; $thissrc=~s/^\/home\/(\w+)\/public_html/\/priv\/$1/; @@ -1433,18 +1577,23 @@ sub phasetwo { $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').'

'); } + return '

'.&mt('Done').'

'; } ######################################### sub batchpublish { my ($r,$srcfile,$targetfile)=@_; + #publication pollutes %ENV with form.* values + my %oldENV=%ENV; $srcfile=~s/\/+/\//g; $targetfile=~s/\/+/\//g; my $thisdisfn=$srcfile; @@ -1453,30 +1602,32 @@ 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)=@_; - $r->print('

'.&publish($srcfile,$targetfile,$thisembstyle,1).'

'); + my ($outstring,$error)=&publish($srcfile,$targetfile,$thisembstyle,1); + $r->print('

'.$outstring.'

'); # phase two takes # my ($source,$target,$style,$distarget,batch)=@_; # $ENV{'form.allmeta'},$ENV{'form.title'},$ENV{'form.author'},... - $r->print('

'); - &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1); - $r->print('

'); + if (!$error) { + $r->print('

'); + &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1); + $r->print('

'); + } + %ENV=%oldENV; return ''; } @@ -1487,53 +1638,110 @@ 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, + $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. + + 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!~/\~$/)) { + 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 $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) { + $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 { + } else { # never published - $publishthis=1; - } - if ($publishthis) { + $publishthis=1; + } + if ($publishthis) { &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename); - } else { - $r->print('
Skipping '.$filename.'
'); - } - $r->rflush(); - } - } - closedir(DIR); + } 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('LON-CAPA Publishing'); + $r->print(&Apache::loncommon::bodytag('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(''); + return OK; } ######################################### @@ -1576,13 +1784,13 @@ 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 @@ -1591,147 +1799,164 @@ sub handler { # -------------------------------------------------------------- Check filename - my $fn=$ENV{'form.filename'}; + my $fn=&Apache::lonnet::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/^\/\~(\w+)/\/home\/$1\/public_html/; + + my $targetdir=''; + $docroot=$r->dir_config('lonDocRoot'); + if ($1 ne $cuname) { + $r->log_reason($cuname.' at '.$cudom. + ' trying to publish unowned file '. + $ENV{'form.filename'}.' ('.$fn.')', + $r->filename); + return HTTP_NOT_ACCEPTABLE; + } else { + $targetdir=$docroot.'/res/'.$cudom; + } - unless (-e $fn) { - $r->log_reason($cuname.' at '.$cudom. - ' trying to publish non-existing file '.$ENV{'form.filename'}. - ' ('.$fn.')', - $r->filename); - return HTTP_NOT_FOUND; - } + unless (-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 ($ENV{'form.phase'} eq 'two') { # -------------------------------- 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; + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; - $r->print('LON-CAPA Publishing'); - $r->print(&Apache::loncommon::bodytag('Resource Publication')); + $r->print('LON-CAPA Publishing'); + $r->print(&Apache::loncommon::bodytag('Resource Publication')); - 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('Done').'
'.&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.'

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

'.&mt('Publishing').' '. + &Apache::loncommon::filedescription($thistype).' '); + + $r->print(< +$thisdisfn +ENDCAPTION + $r->print('

'.&mt('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

'); - } + if (($cuname ne $ENV{'user.name'})||($cudom ne $ENV{'user.domain'})) { + $r->print('

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

'); + } + + if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') { + $r->print(< + +ENDDIFF + $r->print(&mt('Diffs with Current Version').'
'); + } # ------------------ Publishing from $thisfn to $thistarget with $thisembstyle. - unless ($ENV{'form.phase'} eq 'two') { - $r->print( - '
'.&publish($thisfn,$thistarget,$thisembstyle)); - } else { - $r->print('
'); - &phasetwo($r,$thisfn,$thistarget,$thisembstyle,$thisdistarget); - } - - } - $r->print(''); + 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(''); - return OK; + return OK; } 1; @@ -1741,5 +1966,7 @@ __END__ =back +=back + =cut