1: # The LearningOnline Network with CAPA
2: # Publication Handler
3: #
4: # $Id: lonpublisher.pm,v 1.298 2021/06/03 13:59:44 raeburn Exp $
5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
28: ###
29:
30: ###############################################################################
31: ## ##
32: ## ORGANIZATION OF THIS PERL MODULE ##
33: ## ##
34: ## 1. Modules used by this module ##
35: ## 2. Various subroutines ##
36: ## 3. Publication Step One ##
37: ## 4. Phase Two ##
38: ## 5. Main Handler ##
39: ## ##
40: ###############################################################################
41:
42:
43: ######################################################################
44: ######################################################################
45:
46: =pod
47:
48: =head1 NAME
49:
50: lonpublisher - LON-CAPA publishing handler
51:
52: =head1 SYNOPSIS
53:
54: B<lonpublisher> is used by B<mod_perl> inside B<Apache>. This is the
55: invocation by F<loncapa_apache.conf>:
56:
57: <Location /adm/publish>
58: PerlAccessHandler Apache::lonacc
59: SetHandler perl-script
60: PerlHandler Apache::lonpublisher
61: ErrorDocument 403 /adm/login
62: ErrorDocument 404 /adm/notfound.html
63: ErrorDocument 406 /adm/unauthorized.html
64: ErrorDocument 500 /adm/errorhandler
65: </Location>
66:
67: =head1 OVERVIEW
68:
69: Authors can only write-access the C</priv/domain/authorname/> space.
70: They can copy resources into the resource area through the
71: publication step, and move them back through a recover step.
72: Authors do not have direct write-access to their resource space.
73:
74: During the publication step, several events will be
75: triggered. Metadata is gathered, where a wizard manages default
76: entries on a hierarchical per-directory base: The wizard imports the
77: metadata (including access privileges and royalty information) from
78: the most recent published resource in the current directory, and if
79: that is not available, from the next directory above, etc. The Network
80: keeps all previous versions of a resource and makes them available by
81: an explicit version number, which is inserted between the file name
82: and extension, for example C<foo.2.html>, while the most recent
83: version does not carry a version number (C<foo.html>). Servers
84: subscribing to a changed resource are notified that a new version is
85: available.
86:
87: =head1 DESCRIPTION
88:
89: B<lonpublisher> takes the proper steps to add resources to the LON-CAPA
90: digital library. This includes updating the metadata table in the
91: LON-CAPA database.
92:
93: B<lonpublisher> is many things to many people.
94:
95: This module publishes a file. This involves gathering metadata,
96: versioning the file, copying file from construction space to
97: publication space, and copying metadata from construction space
98: to publication space.
99:
100: =head2 SUBROUTINES
101:
102: Many of the undocumented subroutines implement various magical
103: parsing shortcuts.
104:
105: =cut
106:
107: ######################################################################
108: ######################################################################
109:
110:
111: package Apache::lonpublisher;
112:
113: # ------------------------------------------------- modules used by this module
114: use strict;
115: use Apache::File;
116: use File::Copy;
117: use Apache::Constants qw(:common :http :methods);
118: use HTML::LCParser;
119: use HTML::Entities;
120: use Encode::Encoder;
121: use Apache::lonxml;
122: use DBI;
123: use Apache::lonnet;
124: use Apache::loncommon();
125: use Apache::lonhtmlcommon;
126: use Apache::lonmysql;
127: use Apache::lonlocal;
128: use Apache::loncfile;
129: use LONCAPA::lonmetadata;
130: use Apache::lonmsg;
131: use vars qw(%metadatafields %metadatakeys %addid $readit);
132: use LONCAPA qw(:DEFAULT :match);
133:
134: my $docroot;
135:
136: my $cuname;
137: my $cudom;
138:
139: my $registered_cleanup;
140: my $modified_urls;
141:
142: my $lock;
143:
144: =pod
145:
146: =over 4
147:
148: =item B<metaeval>
149:
150: Evaluates a string that contains metadata. This subroutine
151: stores values inside I<%metadatafields> and I<%metadatakeys>.
152: The hash key is a I<$unikey> corresponding to a unique id
153: that is descriptive of the parser location inside the XML tree.
154:
155: Parameters:
156:
157: =over 4
158:
159: =item I<$metastring>
160:
161: A string that contains metadata.
162:
163: =back
164:
165: Returns:
166:
167: nothing
168:
169: =cut
170:
171: #########################################
172: #########################################
173: #
174: # Modifies global %metadatafields %metadatakeys
175: #
176:
177: sub metaeval {
178: my ($metastring,$prefix)=@_;
179:
180: my $parser=HTML::LCParser->new(\$metastring);
181: my $token;
182: while ($token=$parser->get_token) {
183: if ($token->[0] eq 'S') {
184: my $entry=$token->[1];
185: my $unikey=$entry;
186: next if ($entry =~ m/^(?:parameter|stores)_/);
187: if (defined($token->[2]->{'package'})) {
188: $unikey.="\0package\0".$token->[2]->{'package'};
189: }
190: if (defined($token->[2]->{'part'})) {
191: $unikey.="\0".$token->[2]->{'part'};
192: }
193: if (defined($token->[2]->{'id'})) {
194: $unikey.="\0".$token->[2]->{'id'};
195: }
196: if (defined($token->[2]->{'name'})) {
197: $unikey.="\0".$token->[2]->{'name'};
198: }
199: foreach my $item (@{$token->[3]}) {
200: $metadatafields{$unikey.'.'.$item}=$token->[2]->{$item};
201: if ($metadatakeys{$unikey}) {
202: $metadatakeys{$unikey}.=','.$item;
203: } else {
204: $metadatakeys{$unikey}=$item;
205: }
206: }
207: my $newentry=$parser->get_text('/'.$entry);
208: if (($entry eq 'customdistributionfile') ||
209: ($entry eq 'sourcerights')) {
210: $newentry=~s/^\s*//;
211: if ($newentry !~m|^/res|) { $newentry=$prefix.$newentry; }
212: }
213: # actually store
214: if ( $entry eq 'rule' && exists($metadatafields{$unikey})) {
215: $metadatafields{$unikey}.=','.$newentry;
216: } else {
217: $metadatafields{$unikey}=$newentry;
218: }
219: }
220: }
221: }
222:
223: #########################################
224: #########################################
225:
226: =pod
227:
228: =item B<metaread>
229:
230: Read a metadata file
231:
232: Parameters:
233:
234: =over
235:
236: =item I<$logfile>
237:
238: File output stream to output errors and warnings to.
239:
240: =item I<$fn>
241:
242: File name (including path).
243:
244: =back
245:
246: Returns:
247:
248: =over 4
249:
250: =item Scalar string (if successful)
251:
252: XHTML text that indicates successful reading of the metadata.
253:
254: =back
255:
256: =cut
257:
258: #########################################
259: #########################################
260: sub metaread {
261: my ($logfile,$fn,$prefix)=@_;
262: unless (-e $fn) {
263: print($logfile 'No file '.$fn."\n");
264: return '<p class="LC_warning">'
265: .&mt('No file: [_1]',&Apache::loncfile::display($fn))
266: .'</p>';
267: }
268: print($logfile 'Processing '.$fn."\n");
269: my $metastring;
270: {
271: my $metafh=Apache::File->new($fn);
272: $metastring=join('',<$metafh>);
273: }
274: &metaeval($metastring,$prefix);
275: return '<p class="LC_info">'
276: .&mt('Processed file: [_1]',&Apache::loncfile::display($fn))
277: .'</p>';
278: }
279:
280: #########################################
281: #########################################
282:
283: sub coursedependencies {
284: my $url=&Apache::lonnet::declutter(shift);
285: $url=~s/\.meta$//;
286: my ($adomain,$aauthor)=($url=~ m{^($match_domain)/($match_username)/});
287: my $regexp=quotemeta($url);
288: $regexp='___'.$regexp.'___course';
289: my %evaldata=&Apache::lonnet::dump('nohist_resevaldata',$adomain,
290: $aauthor,$regexp);
291: my %courses=();
292: foreach my $item (keys(%evaldata)) {
293: if ($item=~/^([a-zA-Z0-9]+_[a-zA-Z0-9]+)___.+___course$/) {
294: $courses{$1}=1;
295: }
296: }
297: return %courses;
298: }
299: #########################################
300: #########################################
301:
302:
303: =pod
304:
305: =item Form-field-generating subroutines.
306:
307: For input parameters, these subroutines take in values
308: such as I<$name>, I<$value> and other form field metadata.
309: The output (scalar string that is returned) is an XHTML
310: string which presents the form field (foreseeably inside
311: <form></form> tags).
312:
313: =over 4
314:
315: =item B<textfield>
316:
317: =item B<text_with_browse_field>
318:
319: =item B<hiddenfield>
320:
321: =item B<checkbox>
322:
323: =item B<selectbox>
324:
325: =back
326:
327: =cut
328:
329: #########################################
330: #########################################
331: sub textfield {
332: my ($title,$name,$value,$noline)=@_;
333: $value=~s/^\s+//gs;
334: $value=~s/\s+$//gs;
335: $value=~s/\s+/ /gs;
336: $title=&mt($title);
337: $env{'form.'.$name}=$value;
338: return "\n".&Apache::lonhtmlcommon::row_title($title)
339: .'<input type="text" name="'.$name.'" size="80" value="'.$value.'" />'
340: .&Apache::lonhtmlcommon::row_closure($noline);
341: }
342:
343: sub text_with_browse_field {
344: my ($title,$name,$value,$restriction,$noline)=@_;
345: $value=~s/^\s+//gs;
346: $value=~s/\s+$//gs;
347: $value=~s/\s+/ /gs;
348: $title=&mt($title);
349: $env{'form.'.$name}=$value;
350: return "\n".&Apache::lonhtmlcommon::row_title($title)
351: .'<input type="text" name="'.$name.'" size="80" value="'.$value.'" />'
352: .'<br />'
353: .'<a href="javascript:openbrowser(\'pubform\',\''.$name.'\',\''.$restriction.'\');">'
354: .&mt('Select')
355: .'</a> '
356: .'<a href="javascript:opensearcher(\'pubform\',\''.$name.'\');">'
357: .&mt('Search')
358: .'</a>'
359: .&Apache::lonhtmlcommon::row_closure($noline);
360: }
361:
362: sub hiddenfield {
363: my ($name,$value)=@_;
364: $env{'form.'.$name}=$value;
365: return "\n".'<input type="hidden" name="'.$name.'" value="'.$value.'" />';
366: }
367:
368: sub checkbox {
369: my ($name,$text)=@_;
370: return "\n<br /><label><input type='checkbox' name='$name' /> ".
371: &mt($text)."</label>";
372: }
373:
374: sub selectbox {
375: my ($title,$name,$value,$functionref,@idlist)=@_;
376: $title=&mt($title);
377: $value=(split(/\s*,\s*/,$value))[-1];
378: if (defined($value)) {
379: $env{'form.'.$name}=$value;
380: } else {
381: $env{'form.'.$name}=$idlist[0];
382: }
383: my $selout="\n".&Apache::lonhtmlcommon::row_title($title)
384: .'<select name="'.$name.'">';
385: foreach my $id (@idlist) {
386: $selout.='<option value="'.$id.'"';
387: if ($id eq $value) {
388: $selout.=' selected="selected"';
389: }
390: $selout.='>'.&{$functionref}($id).'</option>';
391: }
392: $selout.='</select>'.&Apache::lonhtmlcommon::row_closure();
393: return $selout;
394: }
395:
396: sub select_level_form {
397: my ($value,$name)=@_;
398: $env{'form.'.$name}=$value;
399: if (!defined($value)) { $env{'form.'.$name}=0; }
400: return &Apache::loncommon::select_level_form($value,$name);
401: }
402:
403: sub common_access {
404: my ($name,$text,$options)=@_;
405: return unless (ref($options) eq 'ARRAY');
406: my $formname = 'pubdirpref';
407: my $chkname = 'common'.$name;
408: my $chkid = 'LC_'.$chkname;
409: my $divid = $chkid.'div';
410: my $customdivid = 'LC_customfile';
411: my $selname = $chkname.'select';
412: my $selid = $chkid.'select';
413: my $selonchange;
414: if ($name eq 'dist') {
415: $selonchange = ' onchange="showHideCustom(this,'."'$customdivid'".');"';
416: }
417: my %lt = &Apache::lonlocal::texthash(
418: 'default' => 'System wide - can be used for any courses system wide',
419: 'domain' => 'Domain only - use limited to courses in the domai',
420: 'custom' => 'Customized right of use ...',
421: 'public' => 'Public - no authentication or authorization required for use',
422: 'closed' => 'Closed - XML source is closed to everyone',
423: 'open' => 'Open - XML source is open to people who want to use it',
424: 'sel' => 'Select',
425: );
426: my $output = <<"END";
427: <br />
428: <span class="LC_nobreak">
429: <label>
430: <input type="checkbox" name="commonaccess" value="$name" id="$chkid"
431: onclick="showHideAccess(this,'$divid');" />
432: $text</label></span>
433: <div id="$divid" style="padding:0;clear:both;margin:0;border:0;display:none">
434: <select name="$selname" id="$selid" $selonchange>
435: <option value="" selected="selected">$lt{'sel'}</option>
436: END
437: foreach my $val (@{$options}) {
438: $output .= '<option value="'.$val.'">'.$lt{$val}.'</option>'."\n";
439: }
440: $output .= '
441: </select>';
442: if ($name eq 'dist') {
443: $output .= <<"END";
444: <div id="$customdivid" style="padding:0;clear:both;margin:0;border:0;display:none">
445: <input type="text" name="commoncustomrights" size="60" value="" />
446: <a href="javascript:openbrowser('$formname','commoncustomrights','rights');">
447: $lt{'sel'}</a></div>
448: END
449: }
450: $output .= '
451: </div>
452: ';
453: }
454:
455: #########################################
456: #########################################
457:
458: =pod
459:
460: =item B<urlfixup>
461:
462: Fix up a url? First step of publication
463:
464: =cut
465:
466: #########################################
467: #########################################
468: sub urlfixup {
469: my ($url,$target)=@_;
470: unless ($url) { return ''; }
471: #javascript code needs no fixing
472: if ($url =~ /^javascript:/i) { return $url; }
473: if ($url =~ /^mailto:/i) { return $url; }
474: #internal document links need no fixing
475: if ($url =~ /^\#/) { return $url; }
476: my ($host)=($url=~m{(?:(?:http|https|ftp)://)*([^/]+)});
477: my @lonids = &Apache::lonnet::machine_ids($host);
478: if (@lonids) {
479: $url=~s{^(?:http|https|ftp)://}{};
480: $url=~s/^\Q$host\E//;
481: }
482: if ($url=~m{^(?:http|https|ftp)://}) { return $url; }
483: $url=~s{\Q~$cuname\E}{res/$cudom/$cuname};
484: return $url;
485: }
486:
487: #########################################
488: #########################################
489:
490: =pod
491:
492: =item B<absoluteurl>
493:
494: Currently undocumented.
495:
496: =cut
497:
498: #########################################
499: #########################################
500: sub absoluteurl {
501: my ($url,$target)=@_;
502: unless ($url) { return ''; }
503: if ($target) {
504: $target=~s/\/[^\/]+$//;
505: $url=&Apache::lonnet::hreflocation($target,$url);
506: }
507: return $url;
508: }
509:
510: #########################################
511: #########################################
512:
513: =pod
514:
515: =item B<set_allow>
516:
517: Currently undocumented
518:
519: =cut
520:
521: #########################################
522: #########################################
523: sub set_allow {
524: my ($allow,$logfile,$target,$tag,$oldurl,$type)=@_;
525: my $newurl=&urlfixup($oldurl,$target);
526: my $return_url=$oldurl;
527: print $logfile 'GUYURL: '.$tag.':'.$oldurl.' - '.$newurl."\n";
528: if ($newurl ne $oldurl) {
529: $return_url=$newurl;
530: print $logfile 'URL: '.$tag.':'.$oldurl.' - '.$newurl."\n";
531: }
532: if (($newurl !~ /^javascript:/i) &&
533: ($newurl !~ /^mailto:/i) &&
534: ($newurl !~ /^(?:http|https|ftp):/i) &&
535: ($newurl !~ /^\#/)) {
536: if (($type eq 'src') || ($type eq 'href')) {
537: if ($newurl =~ /^([^?]+)\?[^?]*$/) {
538: $newurl = $1;
539: }
540: }
541: $$allow{&absoluteurl($newurl,$target)}=1;
542: }
543: return $return_url;
544: }
545:
546: #########################################
547: #########################################
548:
549: =pod
550:
551: =item B<get_subscribed_hosts>
552:
553: Currently undocumented
554:
555: =cut
556:
557: #########################################
558: #########################################
559: sub get_subscribed_hosts {
560: my ($target)=@_;
561: my @subscribed;
562: my $filename;
563: $target=~/(.*)\/([^\/]+)$/;
564: my $srcf=$2;
565: opendir(DIR,$1);
566: # cycle through listed files, subscriptions used to exist
567: # as "filename.lonid"
568: while ($filename=readdir(DIR)) {
569: if ($filename=~/\Q$srcf\E\.($match_lonid)$/) {
570: my $subhost=$1;
571: if (($subhost ne 'meta'
572: && $subhost ne 'subscription'
573: && $subhost ne 'meta.subscription'
574: && $subhost ne 'tmp') &&
575: ($subhost ne $Apache::lonnet::perlvar{'lonHostID'})) {
576: push(@subscribed,$subhost);
577: }
578: }
579: }
580: closedir(DIR);
581: my $sh;
582: if ( $sh=Apache::File->new("$target.subscription") ) {
583: while (my $subline=<$sh>) {
584: if ($subline =~ /^($match_lonid):/) {
585: if ($1 ne $Apache::lonnet::perlvar{'lonHostID'}) {
586: push(@subscribed,$1);
587: }
588: }
589: }
590: }
591: return @subscribed;
592: }
593:
594:
595: #########################################
596: #########################################
597:
598: =pod
599:
600: =item B<get_max_ids_indices>
601:
602: Currently undocumented
603:
604: =cut
605:
606: #########################################
607: #########################################
608: sub get_max_ids_indices {
609: my ($content)=@_;
610: my $maxindex=10;
611: my $maxid=10;
612: my $needsfixup=0;
613: my $duplicateids=0;
614:
615: my %allids;
616: my %duplicatedids;
617:
618: my $parser=HTML::LCParser->new($content);
619: $parser->xml_mode(1);
620: my $token;
621: while ($token=$parser->get_token) {
622: if ($token->[0] eq 'S') {
623: my $counter;
624: if ($counter=$addid{$token->[1]}) {
625: if ($counter eq 'id') {
626: if (defined($token->[2]->{'id'}) &&
627: $token->[2]->{'id'} !~ /^\s*$/) {
628: $maxid=($token->[2]->{'id'}>$maxid)?$token->[2]->{'id'}:$maxid;
629: if (exists($allids{$token->[2]->{'id'}})) {
630: $duplicateids=1;
631: $duplicatedids{$token->[2]->{'id'}}=1;
632: } else {
633: $allids{$token->[2]->{'id'}}=1;
634: }
635: } else {
636: $needsfixup=1;
637: }
638: } else {
639: if (defined($token->[2]->{'index'}) &&
640: $token->[2]->{'index'} !~ /^\s*$/) {
641: $maxindex=($token->[2]->{'index'}>$maxindex)?$token->[2]->{'index'}:$maxindex;
642: } else {
643: $needsfixup=1;
644: }
645: }
646: }
647: }
648: }
649: return ($needsfixup,$maxid,$maxindex,$duplicateids,
650: (keys(%duplicatedids)));
651: }
652:
653: #########################################
654: #########################################
655:
656: =pod
657:
658: =item B<get_all_text_unbalanced>
659:
660: Currently undocumented
661:
662: =cut
663:
664: #########################################
665: #########################################
666: sub get_all_text_unbalanced {
667: #there is a copy of this in lonxml.pm
668: my($tag,$pars)= @_;
669: my $token;
670: my $result='';
671: $tag='<'.$tag.'>';
672: while ($token = $$pars[-1]->get_token) {
673: if (($token->[0] eq 'T')||($token->[0] eq 'C')||($token->[0] eq 'D')) {
674: $result.=$token->[1];
675: } elsif ($token->[0] eq 'PI') {
676: $result.=$token->[2];
677: } elsif ($token->[0] eq 'S') {
678: $result.=$token->[4];
679: } elsif ($token->[0] eq 'E') {
680: $result.=$token->[2];
681: }
682: if ($result =~ /\Q$tag\E/s) {
683: ($result,my $redo)=$result =~ /(.*)\Q$tag\E(.*)/is;
684: #&Apache::lonnet::logthis('Got a winner with leftovers ::'.$2);
685: #&Apache::lonnet::logthis('Result is :'.$1);
686: $redo=$tag.$redo;
687: push (@$pars,HTML::LCParser->new(\$redo));
688: $$pars[-1]->xml_mode('1');
689: last;
690: }
691: }
692: return $result
693: }
694:
695: #########################################
696: #########################################
697:
698: =pod
699:
700: =item B<fix_ids_and_indices>
701:
702: Currently undocumented
703:
704: =cut
705:
706: #########################################
707: #########################################
708: #Arguably this should all be done as a lonnet::ssi instead
709: sub fix_ids_and_indices {
710: my ($logfile,$source,$target)=@_;
711:
712: my %allow;
713: my $content;
714: {
715: my $org=Apache::File->new($source);
716: $content=join('',<$org>);
717: }
718:
719: my ($needsfixup,$maxid,$maxindex,$duplicateids,@duplicatedids)=
720: &get_max_ids_indices(\$content);
721:
722: print $logfile ("Got $needsfixup,$maxid,$maxindex,$duplicateids--".
723: join(', ',@duplicatedids));
724: if ($duplicateids) {
725: print $logfile "Duplicate ID(s) exist, ".join(', ',@duplicatedids)."\n";
726: my $outstring='<span class="LC_error">'.&mt('Unable to publish file, it contains duplicated ID(s), ID(s) need to be unique. The duplicated ID(s) are').': '.join(', ',@duplicatedids).'</span>';
727: return ($outstring,1);
728: }
729: if ($needsfixup) {
730: print $logfile "Needs ID and/or index fixup\n".
731: "Max ID : $maxid (min 10)\n".
732: "Max Index: $maxindex (min 10)\n";
733: }
734: my $outstring='';
735: my $responsecounter=1;
736: my @parser;
737: $parser[0]=HTML::LCParser->new(\$content);
738: $parser[-1]->xml_mode(1);
739: my $token;
740: while (@parser) {
741: while ($token=$parser[-1]->get_token) {
742: if ($token->[0] eq 'S') {
743: my $counter;
744: my $tag=$token->[1];
745: my $lctag=lc($tag);
746: if ($lctag eq 'allow') {
747: $allow{$token->[2]->{'src'}}=1;
748: next;
749: }
750: if ($lctag eq 'base') { next; }
751: if (($lctag eq 'part') || ($lctag eq 'problem')) {
752: $responsecounter=0;
753: }
754: if ($lctag=~/response$/) { $responsecounter++; }
755: if ($lctag eq 'import') { $responsecounter++; }
756: my %parms=%{$token->[2]};
757: $counter=$addid{$tag};
758: if (!$counter) { $counter=$addid{$lctag}; }
759: if ($counter) {
760: if ($counter eq 'id') {
761: unless (defined($parms{'id'}) &&
762: $parms{'id'}!~/^\s*$/) {
763: $maxid++;
764: $parms{'id'}=$maxid;
765: print $logfile 'ID(new) : '.$tag.':'.$maxid."\n";
766: } else {
767: print $logfile 'ID(kept): '.$tag.':'.$parms{'id'}."\n";
768: }
769: } elsif ($counter eq 'index') {
770: unless (defined($parms{'index'}) &&
771: $parms{'index'}!~/^\s*$/) {
772: $maxindex++;
773: $parms{'index'}=$maxindex;
774: print $logfile 'Index: '.$tag.':'.$maxindex."\n";
775: }
776: }
777: }
778: unless ($parms{'type'} eq 'zombie') {
779: foreach my $type ('src','href','background','bgimg') {
780: foreach my $key (keys(%parms)) {
781: if ($key =~ /^$type$/i) {
782: next if (($lctag eq 'img') && ($type eq 'src') &&
783: ($parms{$key} =~ m{^data\:image/gif;base64,}));
784: $parms{$key}=&set_allow(\%allow,$logfile,
785: $target,$tag,
786: $parms{$key},$type);
787: }
788: }
789: }
790: }
791: # probably a <randomlabel> image type <label>
792: # or a <image> tag inside <imageresponse>
793: if (($lctag eq 'label' && defined($parms{'description'}))
794: ||
795: ($lctag eq 'image')) {
796: my $next_token=$parser[-1]->get_token();
797: if ($next_token->[0] eq 'T') {
798: $next_token->[1] =~ s/[\n\r\f]+//g;
799: $next_token->[1]=&set_allow(\%allow,$logfile,
800: $target,$tag,
801: $next_token->[1]);
802: }
803: $parser[-1]->unget_token($next_token);
804: }
805: if ($lctag eq 'applet') {
806: my $codebase='';
807: my $havecodebase=0;
808: foreach my $key (keys(%parms)) {
809: if (lc($key) eq 'codebase') {
810: $codebase=$parms{$key};
811: $havecodebase=1;
812: }
813: }
814: if ($havecodebase) {
815: my $oldcodebase=$codebase;
816: unless ($oldcodebase=~/\/$/) {
817: $oldcodebase.='/';
818: }
819: $codebase=&urlfixup($oldcodebase,$target);
820: $codebase=~s/\/$//;
821: if ($codebase ne $oldcodebase) {
822: $parms{'codebase'}=$codebase;
823: print $logfile 'URL codebase: '.$tag.':'.
824: $oldcodebase.' - '.
825: $codebase."\n";
826: }
827: $allow{&absoluteurl($codebase,$target).'/*'}=1;
828: } else {
829: foreach my $key (keys(%parms)) {
830: if ($key =~ /(archive|code|object)/i) {
831: my $oldurl=$parms{$key};
832: my $newurl=&urlfixup($oldurl,$target);
833: $newurl=~s/\/[^\/]+$/\/\*/;
834: print $logfile 'Allow: applet '.lc($key).':'.
835: $oldurl.' allows '.$newurl."\n";
836: $allow{&absoluteurl($newurl,$target)}=1;
837: }
838: }
839: }
840: }
841: my $newparmstring='';
842: my $endtag='';
843: foreach my $parkey (keys(%parms)) {
844: if ($parkey eq '/') {
845: $endtag=' /';
846: } else {
847: my $quote=($parms{$parkey}=~/\"/?"'":'"');
848: $newparmstring.=' '.$parkey.'='.$quote.$parms{$parkey}.$quote;
849: }
850: }
851: if (!$endtag) { if ($token->[4]=~m:/>$:) { $endtag=' /'; }; }
852: $outstring.='<'.$tag.$newparmstring.$endtag.'>';
853: if ($lctag eq 'm' || $lctag eq 'answer' || $lctag eq 'display' ||
854: $lctag eq 'tex') {
855: $outstring.=&get_all_text_unbalanced('/'.$lctag,\@parser);
856: } elsif ($lctag eq 'script') {
857: if ($parms{'type'} eq 'loncapa/perl') {
858: $outstring.=&get_all_text_unbalanced('/'.$lctag,\@parser);
859: } else {
860: my $script = &get_all_text_unbalanced('/'.$lctag,\@parser);
861: if ($script =~ m{\.set\w+(Src|Swf)\(["']}i) {
862: my @srcs = split(/\.set/i,$script);
863: if (scalar(@srcs) > 1) {
864: foreach my $item (@srcs) {
865: if ($item =~ m{^(FlashPlayerSwf|MediaSrc|XMPSrc|ConfigurationSrc|PosterImageSrc)\((['"])(?:(?!\2).)+\2\)}is) {
866: my $srctype = $1;
867: my $quote = $2;
868: my ($url) = ($item =~ m{^\Q$srctype($quote\E([^$quote]+)\Q$quote)\E});
869: $url = &urlfixup($url);
870: unless ($url=~m{^(?:http|https|ftp)://}) {
871: $allow{&absoluteurl($url,$target)}=1;
872: if ($srctype eq 'ConfigurationSrc') {
873: if ($url =~ m{^(.+/)configuration_express\.xml$}) {
874: #
875: # Camtasia 8.1: express_show/spritesheet.png needed, and included in zip archive.
876: # Not referenced directly in <main>.html or <main>_player.html files,
877: # so add this file to %allow (where <main> is name user gave to file/archive).
878: #
879: my $spritesheet = $1.'express_show/spritesheet.png';
880: $allow{&absoluteurl($spritesheet,$target)}=1;
881:
882: #
883: # Camtasia 8.4: skins/express_show/spritesheet.min.css needed, and included in zip archive.
884: # Not referenced directly in <main>.html or <main>_player.html files,
885: # so add this file to %allow (where <main> is name user gave to file/archive).
886: #
887: my $spritecss = $1.'express_show/spritesheet.min.css';
888: $allow{&absoluteurl($spritecss,$target)}=1;
889: }
890: } elsif ($srctype eq 'PosterImageSrc') {
891: if ($url =~ m{^(.+)_First_Frame\.png$}) {
892: my $prefix = $1;
893: #
894: # Camtasia 8.1: <main>_Thumbnails.png needed, and included in zip archive.
895: # Not referenced directly in <main>.html or <main>_player.html files,
896: # so add this file to %allow (where <main> is name user gave to file/archive).
897: #
898: my $thumbnail = $prefix.'_Thumbnails.png';
899: $allow{&absoluteurl($thumbnail,$target)}=1;
900: }
901: }
902: }
903: }
904: }
905: }
906: }
907: if ($script =~ m{\.addMediaSrc\((["'])((?!\1).+)\1\);}) {
908: my $src = $2;
909: if ($src) {
910: my $url = &urlfixup($src);
911: unless ($url=~m{^(?:http|https|ftp)://}) {
912: $allow{&absoluteurl($url,$target)}=1;
913: }
914: }
915: }
916: if ($script =~ /\(document,\s*(['"])script\1,\s*\[([^\]]+)\]\);/s) {
917: my $scriptslist = $2;
918: my @srcs = split(/\s*,\s*/,$scriptslist);
919: foreach my $src (@srcs) {
920: if ($src =~ /(["'])(?:(?!\1).)+\.js\1/) {
921: my $quote = $1;
922: my ($url) = ($src =~ m/\Q$quote\E([^$quote]+)\Q$quote\E/);
923: $url = &urlfixup($url);
924: unless ($url=~m{^(?:http|https|ftp)://}) {
925: $allow{&absoluteurl($url,$target)}=1;
926: }
927: }
928: }
929: }
930: if ($script =~ m{loadScript\(\s*(['"])((?:(?!\1).)+\.js)\1,\s*function}is) {
931: my $src = $2;
932: if ($src) {
933: my $url = &urlfixup($src);
934: unless ($url=~m{^(?:http|https|ftp)://}) {
935: $allow{&absoluteurl($url,$target)}=1;
936: }
937: }
938: }
939: $outstring .= $script;
940: }
941: }
942: } elsif ($token->[0] eq 'E') {
943: if ($token->[2]) {
944: unless ($token->[1] eq 'allow') {
945: $outstring.='</'.$token->[1].'>';
946: }
947: }
948: if ((($token->[1] eq 'part') || ($token->[1] eq 'problem'))
949: && (!$responsecounter)) {
950: my $outstring='<span class="LC_error">'.&mt('Found [_1] without responses. This resource cannot be published.',$token->[1]).'</span>';
951: return ($outstring,1);
952: }
953: } else {
954: $outstring.=$token->[1];
955: }
956: }
957: pop(@parser);
958: }
959:
960: if ($needsfixup) {
961: print $logfile "End of ID and/or index fixup\n".
962: "Max ID : $maxid (min 10)\n".
963: "Max Index: $maxindex (min 10)\n";
964: } else {
965: print $logfile "Does not need ID and/or index fixup\n";
966: }
967:
968: return ($outstring,0,%allow);
969: }
970:
971: #########################################
972: #########################################
973:
974: =pod
975:
976: =item B<store_metadata>
977:
978: Store the metadata in the metadata table in the loncapa database.
979: Uses lonmysql to access the database.
980:
981: Inputs: \%metadata
982:
983: Returns: (error,status). error is undef on success, status is undef on error.
984:
985: =cut
986:
987: #########################################
988: #########################################
989: sub store_metadata {
990: my %metadata = @_;
991: my $error;
992: # Determine if the table exists
993: my $status = &Apache::lonmysql::check_table('metadata');
994: if (! defined($status)) {
995: $error='<span class="LC_error">'
996: .&mt('WARNING: Cannot connect to database!')
997: .'</span>';
998: &Apache::lonnet::logthis($error);
999: return ($error,undef);
1000: }
1001: if ($status == 0) {
1002: # It would be nice to actually create the table....
1003: $error ='<span class="LC_error">'
1004: .&mt('WARNING: The metadata table does not exist in the LON-CAPA database!')
1005: .'</span>';
1006: &Apache::lonnet::logthis($error);
1007: return ($error,undef);
1008: }
1009: my $dbh = &Apache::lonmysql::get_dbh();
1010: if (($metadata{'obsolete'}) || ($metadata{'copyright'} eq 'priv')) {
1011: # remove this entry
1012: my $delitem = 'url = '.$dbh->quote($metadata{'url'});
1013: $status = &LONCAPA::lonmetadata::delete_metadata($dbh,undef,$delitem);
1014:
1015: } else {
1016: $status = &LONCAPA::lonmetadata::update_metadata($dbh,undef,undef,
1017: \%metadata);
1018: }
1019: if (defined($status) && $status ne '') {
1020: $error='<span class="LC_error">'
1021: .&mt('Error occurred saving new values in metadata table in LON-CAPA database!')
1022: .'</span>';
1023: &Apache::lonnet::logthis($error);
1024: &Apache::lonnet::logthis($status);
1025: return ($error,undef);
1026: }
1027: return (undef,'success');
1028: }
1029:
1030:
1031: # ========================================== Parse file for errors and warnings
1032:
1033: sub checkonthis {
1034: my ($r,$source)=@_;
1035: my $uri=&Apache::lonnet::hreflocation($source);
1036: $uri=~s/\/$//;
1037: my $result=&Apache::lonnet::ssi_body($uri,
1038: ('grade_target'=>'web',
1039: 'return_only_error_and_warning_counts' => 1));
1040: my ($errorcount,$warningcount)=split(':',$result);
1041: if (($errorcount) || ($warningcount)) {
1042: $r->print('<h3>'.&mt('Warnings and Errors').'</h3>');
1043: $r->print('<tt>'.$uri.'</tt>:');
1044: $r->print('<ul>');
1045: if ($warningcount) {
1046: $r->print('<li><div class="LC_warning">'
1047: .&mt('[quant,_1,warning]',$warningcount)
1048: .'</div></li>');
1049: }
1050: if ($errorcount) {
1051: $r->print('<li><div class="LC_error">'
1052: .&mt('[quant,_1,error]',$errorcount)
1053: .' <img src="/adm/lonMisc/bomb.gif" />'
1054: .'</div></li>');
1055: }
1056: $r->print('</ul>');
1057: } else {
1058: #$r->print('<font color="green">'.&mt('ok').'</font>');
1059: }
1060: $r->rflush();
1061: return ($warningcount,$errorcount);
1062: }
1063:
1064: # ============================================== Parse file itself for metadata
1065: #
1066: # parses a file with target meta, sets global %metadatafields %metadatakeys
1067:
1068: sub parseformeta {
1069: my ($source,$style)=@_;
1070: my $allmeta='';
1071: if (($style eq 'ssi') || ($style eq 'prv')) {
1072: my $dir=$source;
1073: $dir=~s-/[^/]*$--;
1074: my $file=$source;
1075: $file=(split('/',$file))[-1];
1076: $source=&Apache::lonnet::hreflocation($dir,$file);
1077: $allmeta=&Apache::lonnet::ssi_body($source,('grade_target' => 'meta'));
1078: &metaeval($allmeta);
1079: }
1080: return $allmeta;
1081: }
1082:
1083: #########################################
1084: #########################################
1085:
1086: =pod
1087:
1088: =item B<publish>
1089:
1090: This is the workhorse function of this module. This subroutine generates
1091: backup copies, performs any automatic processing (prior to publication,
1092: especially for rat and ssi files),
1093:
1094: Returns a 2 element array, the first is the string to be shown to the
1095: user, the second is an error code, either 1 (an error occurred) or 0
1096: (no error occurred)
1097:
1098: I<Additional documentation needed.>
1099:
1100: =cut
1101:
1102: #########################################
1103: #########################################
1104: sub publish {
1105:
1106: my ($source,$target,$style,$batch,$nokeyref)=@_;
1107: my $logfile;
1108: my $scrout='';
1109: my $allmeta='';
1110: my $content='';
1111: my %allow=();
1112:
1113: unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
1114: return ('<span class="LC_error">'.&mt('No write permission to user directory, FAIL').'</span>',1);
1115: }
1116: print $logfile
1117: "\n\n================= Publish ".localtime()." Phase One ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
1118:
1119: if (($style eq 'ssi') || ($style eq 'rat') || ($style eq 'prv')) {
1120: # ------------------------------------------------------- This needs processing
1121:
1122: # ----------------------------------------------------------------- Backup Copy
1123: my $copyfile=$source.'.save';
1124: if (copy($source,$copyfile)) {
1125: print $logfile "Copied original file to ".$copyfile."\n";
1126: } else {
1127: print $logfile "Unable to write backup ".$copyfile.':'.$!."\n";
1128: return ("<span class=\"LC_error\">".&mt("Failed to write backup copy, [_1], FAIL",$1)."</span>",1);
1129: }
1130: # ------------------------------------------------------------- IDs and indices
1131:
1132: my ($outstring,$error);
1133: ($outstring,$error,%allow)=&fix_ids_and_indices($logfile,$source,
1134: $target);
1135: if ($error) { return ($outstring,$error); }
1136: # ------------------------------------------------------------ Construct Allows
1137:
1138: my $outdep=''; # Collect dependencies output data
1139: my $allowstr='';
1140: foreach my $thisdep (sort(keys(%allow))) {
1141: if ($thisdep !~ /[^\s]/) { next; }
1142: if ($thisdep =~/\$/) {
1143: $outdep.='<div class="LC_warning">'
1144: .&mt('The resource depends on another resource with variable filename, i.e., [_1].','<tt>'.$thisdep.'</tt>').'<br />'
1145: .&mt('You likely need to explicitly allow access to all possible dependencies using the [_1]-tag.','<tt><allow></tt>')
1146: ."</div>\n";
1147: }
1148: unless ($style eq 'rat') {
1149: $allowstr.="\n".'<allow src="'.$thisdep.'" />';
1150: }
1151: $outdep.='<div>';
1152: if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) {
1153: $outdep.='<a href="'.$thisdep.'">';
1154: }
1155: $outdep.='<tt>'.$thisdep.'</tt>';
1156: if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) {
1157: $outdep.='</a>';
1158: if (
1159: &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
1160: $thisdep.'.meta') eq '-1') {
1161: $outdep.= ' - <span class="LC_error">'.&mt('Currently not available').
1162: '</span>';
1163: } else {
1164: #
1165: # Store the fact that the dependency has been used by the target file
1166: # Unfortunately, usage is erroneously named sequsage in lonmeta.pm
1167: # The translation happens in lonmetadata.pm
1168: #
1169: my %temphash=(&Apache::lonnet::declutter($target).'___'.
1170: &Apache::lonnet::declutter($thisdep).'___usage'
1171: => time);
1172: $thisdep=~m{^/res/($match_domain)/($match_username)/};
1173: if ((defined($1)) && (defined($2))) {
1174: &Apache::lonnet::put('nohist_resevaldata',\%temphash,
1175: $1,$2);
1176: }
1177: }
1178: }
1179: $outdep.='</div><br />';
1180: }
1181:
1182: if ($outdep) {
1183: $scrout.='<h3>'.&mt('Dependencies').'</h3>'
1184: .$outdep
1185: }
1186: $outstring=~s/\n*(\<\/[^\>]+\>[^<]*)$/$allowstr\n$1\n/s;
1187:
1188: # ------------------------------------------------------------- Write modified.
1189:
1190: {
1191: my $org;
1192: unless ($org=Apache::File->new('>'.$source)) {
1193: print $logfile "No write permit to $source\n";
1194: return ('<span class="LC_error">'.&mt('No write permission to').
1195: ' '.$source.
1196: ', '.&mt('FAIL').'</span>',1);
1197: }
1198: print($org $outstring);
1199: }
1200: $content=$outstring;
1201:
1202: }
1203: # -------------------------------------------- Initial step done, now metadata.
1204:
1205: # --------------------------------------- Storage for metadata keys and fields.
1206: # these are globals
1207: #
1208: %metadatafields=();
1209: %metadatakeys=();
1210:
1211: my %oldparmstores=();
1212:
1213: unless ($batch) {
1214: $scrout.='<h3>'.&mt('Metadata').' ' .
1215: &Apache::loncommon::help_open_topic("Metadata_Description")
1216: . '</h3>';
1217: }
1218:
1219: # ------------------------------------------------ First, check out environment
1220: if ((!(-e $source.'.meta')) || ($env{'form.forceoverride'})) {
1221: $metadatafields{'author'}=$env{'environment.firstname'}.' '.
1222: $env{'environment.middlename'}.' '.
1223: $env{'environment.lastname'}.' '.
1224: $env{'environment.generation'};
1225: $metadatafields{'author'}=~s/\s+/ /g;
1226: $metadatafields{'author'}=~s/\s+$//;
1227: $metadatafields{'owner'}=$cuname.':'.$cudom;
1228:
1229: # ------------------------------------------------ Check out directory hierachy
1230:
1231: my $thisdisfn=$source;
1232:
1233: $thisdisfn=~s/^\Q$docroot\E\/priv\/\Q$cudom\E\/\Q$cuname\E\///;
1234: my @urlparts=('.',split(/\//,$thisdisfn));
1235: $#urlparts--;
1236:
1237: my $currentpath=$docroot.'/priv/'.$cudom.'/'.$cuname.'/';
1238:
1239: my $prefix='../'x($#urlparts);
1240: foreach my $subdir (@urlparts) {
1241: $currentpath.=$subdir.'/';
1242: $scrout.=&metaread($logfile,$currentpath.'default.meta',$prefix);
1243: $prefix=~s|^\.\./||;
1244: }
1245:
1246: # ----------------------------------------------------------- Parse file itself
1247: # read %metadatafields from file itself
1248:
1249: $allmeta=&parseformeta($source,$style);
1250:
1251: # ------------------- Clear out parameters and stores (there should not be any)
1252:
1253: foreach my $field (keys(%metadatafields)) {
1254: if (($field=~/^parameter/) || ($field=~/^stores/)) {
1255: delete $metadatafields{$field};
1256: }
1257: }
1258:
1259: } else {
1260: # ---------------------- Read previous metafile, remember parameters and stores
1261:
1262: $scrout.=&metaread($logfile,$source.'.meta');
1263:
1264: foreach my $field (keys(%metadatafields)) {
1265: if (($field=~/^parameter/) || ($field=~/^stores/)) {
1266: $oldparmstores{$field}=1;
1267: delete $metadatafields{$field};
1268: }
1269: }
1270: # ------------------------------------------------------------- Save some stuff
1271: my %savemeta=();
1272: if ($metadatafields{'title'}) { $savemeta{'title'}=$metadatafields{'title'}; }
1273: # ------------------------------------------ See if anything new in file itself
1274:
1275: $allmeta=&parseformeta($source,$style);
1276: # ----------------------------------------------------------- Restore the stuff
1277: foreach my $item (keys(%savemeta)) {
1278: $metadatafields{$item}=$savemeta{$item};
1279: }
1280: }
1281:
1282:
1283: # ---------------- Find and document discrepancies in the parameters and stores
1284:
1285: my $chparms='';
1286: foreach my $field (sort(keys(%metadatafields))) {
1287: if (($field=~/^parameter/) || ($field=~/^stores/)) {
1288: unless ($field=~/\.\w+$/) {
1289: unless ($oldparmstores{$field}) {
1290: my $disp_key = $field;
1291: $disp_key =~ tr/\0/_/;
1292: print $logfile ('New: '.$disp_key."\n");
1293: $chparms .= $disp_key.' ';
1294: }
1295: }
1296: }
1297: }
1298: if ($chparms) {
1299: $scrout.='<p><b>'.&mt('New parameters or saved values').
1300: ':</b> '.$chparms.'</p>';
1301: }
1302:
1303: $chparms='';
1304: foreach my $olditem (sort(keys(%oldparmstores))) {
1305: if (($olditem=~/^parameter/) || ($olditem=~/^stores/)) {
1306: unless (($metadatafields{$olditem.'.name'}) ||
1307: ($metadatafields{$olditem.'.package'}) || ($olditem=~/\.\w+$/)) {
1308: my $disp_key = $olditem;
1309: $disp_key =~ tr/\0/_/;
1310: print $logfile ('Obsolete: '.$disp_key."\n");
1311: $chparms.=$disp_key.' ';
1312: }
1313: }
1314: }
1315: if ($chparms) {
1316: $scrout.='<p><b>'.&mt('Obsolete parameters or saved values').':</b> '
1317: .$chparms.'</p>'
1318: .'<p class="LC_warning"><b>'.&mt('Warning!').'</b><br />'
1319: .&mt('If this resource is in active use, student performance data from the previous version may become inaccessible.')
1320: .'</p><hr />';
1321: }
1322: if ($metadatafields{'copyright'} eq 'priv') {
1323: $scrout.='<p class="LC_warning"><b>'.&mt('Warning!').'</b><br />'
1324: .&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.')
1325: .'</p><hr />';
1326: }
1327:
1328: # ------------------------------------------------------- Now have all metadata
1329:
1330: my %keywords=();
1331:
1332: if (length($content)<500000) {
1333: my $textonly=$content;
1334: $textonly=~s/\<script[^\<]+\<\/script\>//g;
1335: $textonly=~s/\<m\>[^\<]+\<\/m\>//g;
1336: $textonly=~s/\<[^\>]*\>//g;
1337:
1338: #this is a work simplification for german authors for present
1339: $textonly=HTML::Entities::decode($textonly); #decode HTML-character
1340: $textonly=Encode::Encoder::encode('utf8', $textonly); #encode to perl internal unicode
1341: $textonly=~tr/A-ZÜÄÖ/a-züäö/; #add lowercase rule for german "Umlaute"
1342: $textonly=~s/[\$\&][a-z]\w*//g;
1343: $textonly=~s/[^a-z^ü^ä^ö^ß\s]//g; #dont delete german "Umlaute"
1344:
1345: foreach ($textonly=~m/[^\s]+/g) { #match all but whitespaces
1346: unless ($nokeyref->{$_}) {
1347: $keywords{$_}=1;
1348: }
1349: }
1350:
1351:
1352: }
1353:
1354: foreach my $addkey (split(/[\"\'\,\;]/,$metadatafields{'keywords'})) {
1355: $addkey=~s/\s+/ /g;
1356: $addkey=~s/^\s//;
1357: $addkey=~s/\s$//;
1358: if ($addkey=~/\w/) {
1359: $keywords{$addkey}=1;
1360: }
1361: }
1362: # --------------------------------------------------- Now we also have keywords
1363: # =============================================================================
1364: # interactive mode html goes into $intr_scrout
1365: # batch mode throws away this HTML
1366: # additionally all of the field functions have a by product of setting
1367: # $env{'from.'..} so that it can be used by the phase two handler in
1368: # batch mode
1369:
1370: my $intr_scrout.='<br />'
1371: .'<form name="pubform" action="/adm/publish" method="post">';
1372: unless ($env{'form.makeobsolete'}) {
1373: $intr_scrout.='<p class="LC_warning">'
1374: .&mt('Searching for your resource will be based on the following metadata. Please provide as much data as possible.')
1375: .'</p>'
1376: .'<p><input type="submit" value="'
1377: .&mt('Finalize Publication')
1378: .'" /> <a href="'.&Apache::loncfile::url($source).'">'.&mt('Cancel').'</a></p>';
1379: }
1380: $intr_scrout.=&Apache::lonhtmlcommon::start_pick_box();
1381: $intr_scrout.=
1382: &hiddenfield('phase','two').
1383: &hiddenfield('filename',$env{'form.filename'}).
1384: &hiddenfield('allmeta',&escape($allmeta)).
1385: &hiddenfield('dependencies',join(',',keys(%allow)));
1386: unless ($env{'form.makeobsolete'}) {
1387: $intr_scrout.=
1388: &textfield('Title','title',$metadatafields{'title'}).
1389: &textfield('Author(s)','author',$metadatafields{'author'}).
1390: &textfield('Subject','subject',$metadatafields{'subject'});
1391: # --------------------------------------------------- Scan content for keywords
1392:
1393: my $keywords_help = &Apache::loncommon::help_open_topic("Publishing_Keywords");
1394: my $keywordout=<<"END";
1395: <script>
1396: function checkAll(field) {
1397: for (i = 0; i < field.length; i++)
1398: field[i].checked = true ;
1399: }
1400:
1401: function uncheckAll(field) {
1402: for (i = 0; i < field.length; i++)
1403: field[i].checked = false ;
1404: }
1405: </script>
1406: END
1407: $keywordout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Keywords'))
1408: .$keywords_help
1409: .'<input type="button" value="'.&mt('check all').'" onclick="javascript:checkAll(document.pubform.keywords)" />'
1410: .'<input type="button" value="'.&mt('uncheck all').'" onclick="javascript:uncheckAll(document.pubform.keywords)" />'
1411: .'</p><br />'
1412: .&Apache::loncommon::start_data_table();
1413: my $cols_per_row = 10;
1414: my $colcount=0;
1415: my $wordcount=0;
1416: my $numkeywords = scalar(keys(%keywords));
1417:
1418: foreach my $word (sort(keys(%keywords))) {
1419: if ($colcount == 0) {
1420: $keywordout .= &Apache::loncommon::start_data_table_row();
1421: }
1422: $colcount++;
1423: $wordcount++;
1424: if (($wordcount == $numkeywords) && ($colcount < $cols_per_row)) {
1425: my $colspan = 1+$cols_per_row-$colcount;
1426: $keywordout .= '<td colspan="'.$colspan.'">';
1427: } else {
1428: $keywordout .= '<td>';
1429: }
1430: $keywordout.='<label><input type="checkbox" name="keywords" value="'.$word.'"';
1431: if ($metadatafields{'keywords'}) {
1432: if ($metadatafields{'keywords'}=~/\Q$word\E/) {
1433: $keywordout.=' checked="checked"';
1434: $env{'form.keywords'}.=$word.',';
1435: }
1436: } elsif (&Apache::loncommon::keyword($word)) {
1437: $keywordout.=' checked="checked"';
1438: $env{'form.keywords'}.=$word.',';
1439: }
1440: $keywordout.=' />'.$word.'</label></td>';
1441: if ($colcount == $cols_per_row) {
1442: $keywordout.=&Apache::loncommon::end_data_table_row();
1443: $colcount=0;
1444: }
1445: }
1446: if ($colcount > 0) {
1447: $keywordout .= &Apache::loncommon::end_data_table_row();
1448: }
1449:
1450: $env{'form.keywords'}=~s/\,$//;
1451:
1452: $keywordout.=&Apache::loncommon::end_data_table_row()
1453: .&Apache::loncommon::end_data_table()
1454: .&Apache::lonhtmlcommon::row_closure();
1455:
1456: $intr_scrout.=$keywordout;
1457:
1458: $intr_scrout.=&textfield('Additional Keywords','addkey','');
1459:
1460: $intr_scrout.=&textfield('Notes','notes',$metadatafields{'notes'});
1461:
1462: $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Abstract'))
1463: .'<textarea cols="80" rows="5" name="abstract">'
1464: .$metadatafields{'abstract'}
1465: .'</textarea>'
1466: .&Apache::lonhtmlcommon::row_closure();
1467:
1468: $source=~/\.(\w+)$/;
1469:
1470: $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Grade Levels'))
1471: .&mt('Lowest Grade Level:').' '
1472: .&select_level_form($metadatafields{'lowestgradelevel'},'lowestgradelevel')
1473: # .&Apache::lonhtmlcommon::row_closure();
1474: # $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title(&mt('Highest Grade Level'))
1475: .' '.&mt('Highest Grade Level:').' '
1476: .&select_level_form($metadatafields{'highestgradelevel'},'highestgradelevel')
1477: .&Apache::lonhtmlcommon::row_closure();
1478:
1479: $intr_scrout.=&textfield('Standards','standards',$metadatafields{'standards'});
1480:
1481: $intr_scrout.=&hiddenfield('mime',$1);
1482:
1483: my $defaultlanguage=$metadatafields{'language'};
1484: $defaultlanguage =~ s/\s*notset\s*//g;
1485: $defaultlanguage =~ s/^,\s*//g;
1486: $defaultlanguage =~ s/,\s*$//g;
1487:
1488: $intr_scrout.=&selectbox('Language','language',
1489: $defaultlanguage,
1490: \&Apache::loncommon::languagedescription,
1491: (&Apache::loncommon::languageids),
1492: );
1493:
1494: unless ($metadatafields{'creationdate'}) {
1495: $metadatafields{'creationdate'}=time;
1496: }
1497: $intr_scrout.=&hiddenfield('creationdate',
1498: &Apache::lonmysql::unsqltime($metadatafields{'creationdate'}));
1499:
1500: $intr_scrout.=&hiddenfield('lastrevisiondate',time);
1501:
1502: my $pubowner_last;
1503: if ($style eq 'prv') {
1504: $pubowner_last = 1;
1505: }
1506: $intr_scrout.=&textfield('Publisher/Owner','owner',
1507: $metadatafields{'owner'},$pubowner_last);
1508:
1509: # ---------------------------------------------- Retrofix for unused copyright
1510: if ($metadatafields{'copyright'} eq 'free') {
1511: $metadatafields{'copyright'}='default';
1512: $metadatafields{'sourceavail'}='open';
1513: }
1514: if ($metadatafields{'copyright'} eq 'priv') {
1515: $metadatafields{'copyright'}='domain';
1516: }
1517: # ------------------------------------------------ Dial in reasonable defaults
1518: my $defaultoption=$metadatafields{'copyright'};
1519: unless ($defaultoption) { $defaultoption='default'; }
1520: my $defaultsourceoption=$metadatafields{'sourceavail'};
1521: unless ($defaultsourceoption) { $defaultsourceoption='closed'; }
1522: unless ($style eq 'prv') {
1523: # -------------------------------------------------- Correct copyright for rat.
1524: if ($style eq 'rat') {
1525: # -------------------------------------- Retrofix for non-applicable copyright
1526: if ($metadatafields{'copyright'} eq 'public') {
1527: delete $metadatafields{'copyright'};
1528: $defaultoption='default';
1529: }
1530: $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
1531: $defaultoption,
1532: \&Apache::loncommon::copyrightdescription,
1533: (grep !/^(public|priv)$/,(&Apache::loncommon::copyrightids)));
1534: } else {
1535: $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
1536: $defaultoption,
1537: \&Apache::loncommon::copyrightdescription,
1538: (grep !/^priv$/,(&Apache::loncommon::copyrightids)));
1539: }
1540: my $copyright_help =
1541: &Apache::loncommon::help_open_topic('Publishing_Copyright');
1542: my $replace=&mt('Copyright/Distribution:');
1543: $intr_scrout =~ s/$replace/$replace.' '.$copyright_help/ge;
1544:
1545: $intr_scrout.=&text_with_browse_field('Custom Distribution File','customdistributionfile',$metadatafields{'customdistributionfile'},'rights');
1546: $intr_scrout.=&selectbox('Source Distribution','sourceavail',
1547: $defaultsourceoption,
1548: \&Apache::loncommon::source_copyrightdescription,
1549: (&Apache::loncommon::source_copyrightids));
1550: # $intr_scrout.=&text_with_browse_field('Source Custom Distribution File','sourcerights',$metadatafields{'sourcerights'},'rights');
1551: my $uctitle=&mt('Obsolete');
1552: my $obsolete_checked=($metadatafields{'obsolete'})?' checked="checked"':'';
1553: $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title($uctitle)
1554: .'<input type="checkbox" name="obsolete"'.$obsolete_checked.' />'
1555: .&Apache::lonhtmlcommon::row_closure(1);
1556: $intr_scrout.=&text_with_browse_field('Suggested Replacement for Obsolete File',
1557: 'obsoletereplacement',
1558: $metadatafields{'obsoletereplacement'},'',1);
1559: } else {
1560: $intr_scrout.=&hiddenfield('copyright','private');
1561: }
1562: } else {
1563: $intr_scrout.=
1564: &hiddenfield('title',$metadatafields{'title'}).
1565: &hiddenfield('author',$metadatafields{'author'}).
1566: &hiddenfield('subject',$metadatafields{'subject'}).
1567: &hiddenfield('keywords',$metadatafields{'keywords'}).
1568: &hiddenfield('abstract',$metadatafields{'abstract'}).
1569: &hiddenfield('notes',$metadatafields{'notes'}).
1570: &hiddenfield('mime',$metadatafields{'mime'}).
1571: &hiddenfield('creationdate',$metadatafields{'creationdate'}).
1572: &hiddenfield('lastrevisiondate',time).
1573: &hiddenfield('owner',$metadatafields{'owner'}).
1574: &hiddenfield('lowestgradelevel',$metadatafields{'lowestgradelevel'}).
1575: &hiddenfield('standards',$metadatafields{'standards'}).
1576: &hiddenfield('highestgradelevel',$metadatafields{'highestgradelevel'}).
1577: &hiddenfield('language',$metadatafields{'language'}).
1578: &hiddenfield('copyright',$metadatafields{'copyright'}).
1579: &hiddenfield('sourceavail',$metadatafields{'sourceavail'}).
1580: &hiddenfield('customdistributionfile',$metadatafields{'customdistributionfile'}).
1581: &hiddenfield('obsolete',1).
1582: &text_with_browse_field('Suggested Replacement for Obsolete File',
1583: 'obsoletereplacement',
1584: $metadatafields{'obsoletereplacement'},'',1);
1585: }
1586: if (!$batch) {
1587: $scrout.=$intr_scrout
1588: .&Apache::lonhtmlcommon::end_pick_box()
1589: .'<p><input type="submit" value="'
1590: .&mt($env{'form.makeobsolete'}?'Make Obsolete':'Finalize Publication')
1591: .'" /></p>'
1592: .'</form>';
1593: }
1594: return($scrout,0);
1595: }
1596:
1597: sub getnokey {
1598: my ($includedir) = @_;
1599: my $nokey={};
1600: my $fh=Apache::File->new($includedir.'/un_keyword.tab');
1601: while (<$fh>) {
1602: my $word=$_;
1603: chomp($word);
1604: $nokey->{$word}=1;
1605: }
1606: return $nokey;
1607: }
1608:
1609: #########################################
1610: #########################################
1611:
1612: =pod
1613:
1614: =item B<phasetwo>
1615:
1616: Render second interface showing status of publication steps.
1617: This is publication step two.
1618:
1619: Parameters:
1620:
1621: =over 4
1622:
1623: =item I<$source>
1624:
1625: =item I<$target>
1626:
1627: =item I<$style>
1628:
1629: =item I<$distarget>
1630:
1631: =item I<$batch>
1632:
1633: =item I<$usebuffer>
1634:
1635: =back
1636:
1637: Returns:
1638:
1639: =over 4
1640:
1641: =item integer or array
1642:
1643: if $userbuffer arg is true, and if caller wants an array
1644: then the array ($output,$rtncode) will be returned, otherwise
1645: just the $rtncode will be returned. $rtncode is an integer:
1646:
1647: 0: fail
1648: 1: success
1649:
1650: =back
1651:
1652: =cut
1653:
1654: #'stupid emacs
1655: #########################################
1656: #########################################
1657: sub phasetwo {
1658:
1659: my ($r,$source,$target,$style,$distarget,$batch,$usebuffer)=@_;
1660: $source=~s/\/+/\//g;
1661: $target=~s/\/+/\//g;
1662: #
1663: # Unless trying to get rid of something, check name validity
1664: #
1665: my $output;
1666: unless ($env{'form.obsolete'}) {
1667: if ($target=~/(\_\_\_|\&\&\&|\:\:\:)/) {
1668: $output = '<span class="LC_error">'.
1669: &mt('Unsupported character combination [_1] in filename, FAIL.',"<tt>'.$1.'</tt>").
1670: '</span>';
1671: if ($usebuffer) {
1672: if (wantarray) {
1673: return ($output,0);
1674: } else {
1675: return 0;
1676: }
1677: } else {
1678: $r->print($output);
1679: return 0;
1680: }
1681: }
1682: unless ($target=~/\.(\w+)$/) {
1683: $output = '<span class="LC_error">'.&mt('No valid extension found in filename, FAIL').'</span>';
1684: if ($usebuffer) {
1685: if (wantarray) {
1686: return ($output,0);
1687: } else {
1688: return 0;
1689: }
1690: } else {
1691: $r->print($output);
1692: return 0;
1693: }
1694: }
1695: if ($target=~/\.(\d+)\.(\w+)$/) {
1696: $output = '<span class="LC_error">'.&mt('Filename of resource contains internal version number. Cannot publish such resources, FAIL').'</span>';
1697: if ($usebuffer) {
1698: if (wantarray) {
1699: return ($output,0);
1700: } else {
1701: return 0;
1702: }
1703: } else {
1704: $r->print($output);
1705: return 0;
1706: }
1707: }
1708: }
1709:
1710: #
1711: # End name check
1712: #
1713: $distarget=~s/\/+/\//g;
1714: my $logfile;
1715: unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
1716: $output = '<span class="LC_error">'.
1717: &mt('No write permission to user directory, FAIL').'</span>';
1718: if ($usebuffer) {
1719: if (wantarray) {
1720: return ($output,0);
1721: } else {
1722: return 0;
1723: }
1724: } else {
1725: return 0;
1726: }
1727: }
1728:
1729: if ($source =~ /\.rights$/) {
1730: $output = '<p><span class="LC_warning">'.&mt('Warning: It can take up to 1 hour for rights changes to fully propagate.').'</span></p>';
1731: unless ($usebuffer) {
1732: $r->print($output);
1733: $output = '';
1734: }
1735: }
1736:
1737: print $logfile
1738: "\n================= Publish ".localtime()." Phase Two ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
1739:
1740: %metadatafields=();
1741: %metadatakeys=();
1742:
1743: &metaeval(&unescape($env{'form.allmeta'}));
1744:
1745: if ($batch) {
1746: my %commonaccess;
1747: map { $commonaccess{$_} = 1; } &Apache::loncommon::get_env_multiple('form.commonaccess');
1748: if ($commonaccess{'dist'}) {
1749: unless ($style eq 'prv') {
1750: if ($env{'form.commondistselect'} eq 'custom') {
1751: unless ($source =~ /\.rights$/) {
1752: if ($env{'form.commoncustomrights'} =~ m{^/res/.+\.rights$}) {
1753: $env{'form.customdistributionfile'} = $env{'form.commoncustomrights'};
1754: $env{'form.copyright'} = $env{'form.commondistselect'};
1755: }
1756: }
1757: } elsif ($env{'form.commondistselect'} =~ /^default|domain|public$/) {
1758: $env{'form.copyright'} = $env{'form.commondistselect'};
1759: }
1760: }
1761: }
1762: unless ($style eq 'prv') {
1763: if ($commonaccess{'source'}) {
1764: if (($env{'form.commonsourceselect'} eq 'open') || ($env{'form.commonsourceselect'} eq 'closed')) {
1765: $env{'form.sourceavail'} = $env{'form.commonsourceselect'};
1766: }
1767: }
1768: }
1769: }
1770:
1771: $metadatafields{'title'}=$env{'form.title'};
1772: $metadatafields{'author'}=$env{'form.author'};
1773: $metadatafields{'subject'}=$env{'form.subject'};
1774: $metadatafields{'notes'}=$env{'form.notes'};
1775: $metadatafields{'abstract'}=$env{'form.abstract'};
1776: $metadatafields{'mime'}=$env{'form.mime'};
1777: $metadatafields{'language'}=$env{'form.language'};
1778: $metadatafields{'creationdate'}=$env{'form.creationdate'};
1779: $metadatafields{'lastrevisiondate'}=$env{'form.lastrevisiondate'};
1780: $metadatafields{'owner'}=$env{'form.owner'};
1781: $metadatafields{'copyright'}=$env{'form.copyright'};
1782: $metadatafields{'standards'}=$env{'form.standards'};
1783: $metadatafields{'lowestgradelevel'}=$env{'form.lowestgradelevel'};
1784: $metadatafields{'highestgradelevel'}=$env{'form.highestgradelevel'};
1785: $metadatafields{'customdistributionfile'}=
1786: $env{'form.customdistributionfile'};
1787: $metadatafields{'sourceavail'}=$env{'form.sourceavail'};
1788: $metadatafields{'obsolete'}=$env{'form.obsolete'};
1789: $metadatafields{'obsoletereplacement'}=
1790: $env{'form.obsoletereplacement'};
1791: $metadatafields{'dependencies'}=$env{'form.dependencies'};
1792: $metadatafields{'modifyinguser'}=$env{'user.name'}.':'.
1793: $env{'user.domain'};
1794: $metadatafields{'authorspace'}=$cuname.':'.$cudom;
1795: $metadatafields{'domain'}=$cudom;
1796:
1797: my $allkeywords=$env{'form.addkey'};
1798: if (exists($env{'form.keywords'})) {
1799: if (ref($env{'form.keywords'})) {
1800: $allkeywords .= ','.join(',',@{$env{'form.keywords'}});
1801: } else {
1802: $allkeywords .= ','.$env{'form.keywords'};
1803: }
1804: }
1805: $allkeywords=~s/[\"\']//g;
1806: $allkeywords=~s/\s*[\;\,]\s*/\,/g;
1807: $allkeywords=~s/\s+/ /g;
1808: $allkeywords=~s/^[ \,]//;
1809: $allkeywords=~s/[ \,]$//;
1810: $metadatafields{'keywords'}=$allkeywords;
1811:
1812: # check if custom distribution file is specified
1813: if ($metadatafields{'copyright'} eq 'custom') {
1814: my $file=$metadatafields{'customdistributionfile'};
1815: unless ($file=~/\.rights$/) {
1816: $output .= '<span class="LC_error">'.&mt('No valid custom distribution rights file specified, FAIL').
1817: '</span>';
1818: if ($usebuffer) {
1819: if (wantarray) {
1820: return ($output,0);
1821: } else {
1822: return 0;
1823: }
1824: } else {
1825: $r->print($output);
1826: return 0;
1827: }
1828: }
1829: }
1830: {
1831: print $logfile "\nWrite metadata file for ".$source;
1832: my $mfh;
1833: unless ($mfh=Apache::File->new('>'.$source.'.meta')) {
1834: $output .= '<span class="LC_error">'.&mt('Could not write metadata, FAIL').
1835: '</span>';
1836: if ($usebuffer) {
1837: if (wantarray) {
1838: return ($output,0);
1839: } else {
1840: return 0;
1841: }
1842: } else {
1843: $r->print($output);
1844: return 0;
1845: }
1846: }
1847: foreach my $field (sort(keys(%metadatafields))) {
1848: unless ($field=~/\./) {
1849: my $unikey=$field;
1850: $unikey=~/^([A-Za-z]+)/;
1851: my $tag=$1;
1852: $tag=~tr/A-Z/a-z/;
1853: print $mfh "\n\<$tag";
1854: foreach my $item (split(/\,/,$metadatakeys{$unikey})) {
1855: my $value=$metadatafields{$unikey.'.'.$item};
1856: $value=~s/\"/\'\'/g;
1857: print $mfh ' '.$item.'="'.$value.'"';
1858: }
1859: print $mfh '>'.
1860: &HTML::Entities::encode($metadatafields{$unikey},'<>&"')
1861: .'</'.$tag.'>';
1862: }
1863: }
1864:
1865: $output .= '<p>'.&mt('Wrote Metadata').'</p>';
1866: unless ($usebuffer) {
1867: $r->print($output);
1868: $output = '';
1869: }
1870: print $logfile "\nWrote metadata";
1871: }
1872:
1873: # -------------------------------- Synchronize entry with SQL metadata database
1874:
1875: $metadatafields{'url'} = $distarget;
1876: $metadatafields{'version'} = 'current';
1877:
1878: my $crsauthor;
1879: if ($env{'request.course.id'}) {
1880: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
1881: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1882: if ($distarget =~ m{^/res/$cdom/$cnum}) {
1883: $crsauthor = 1;
1884: }
1885: }
1886: unless ($crsauthor) {
1887: my ($error,$success) = &store_metadata(%metadatafields);
1888: if ($success) {
1889: $output .= '<p>'.&mt('Synchronized SQL metadata database').'</p>';
1890: print $logfile "\nSynchronized SQL metadata database";
1891: } else {
1892: $output .= $error;
1893: print $logfile "\n".$error;
1894: }
1895: unless ($usebuffer) {
1896: $r->print($output);
1897: $output = '';
1898: }
1899: }
1900: # --------------------------------------------- Delete author resource messages
1901: my $delresult=&Apache::lonmsg::del_url_author_res_msg($target);
1902: $output .= '<p>'.&mt('Removing error messages:').' '.$delresult.'</p>';
1903: unless ($usebuffer) {
1904: $r->print($output);
1905: $output = '';
1906: }
1907: print $logfile "\nRemoving error messages: $delresult";
1908: # ----------------------------------------------------------- Copy old versions
1909:
1910: if (-e $target) {
1911: my $filename;
1912: my $maxversion=0;
1913: $target=~/(.*)\/([^\/]+)\.(\w+)$/;
1914: my $srcf=$2;
1915: my $srct=$3;
1916: my $srcd=$1;
1917: my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1918: unless ($srcd=~/^\Q$docroot\E\/res/) {
1919: print $logfile "\nPANIC: Target dir is ".$srcd;
1920: $output .=
1921: "<span class=\"LC_error\">".&mt('Invalid target directory, FAIL')."</span>";
1922: if ($usebuffer) {
1923: if (wantarray) {
1924: return ($output,0);
1925: } else {
1926: return 0;
1927: }
1928: } else {
1929: $r->print($output);
1930: return 0;
1931: }
1932: }
1933: opendir(DIR,$srcd);
1934: while ($filename=readdir(DIR)) {
1935: if (-l $srcd.'/'.$filename) {
1936: unlink($srcd.'/'.$filename);
1937: unlink($srcd.'/'.$filename.'.meta');
1938: } else {
1939: if ($filename=~/^\Q$srcf\E\.(\d+)\.\Q$srct\E$/) {
1940: $maxversion=($1>$maxversion)?$1:$maxversion;
1941: }
1942: }
1943: }
1944: closedir(DIR);
1945: $maxversion++;
1946: $output .= '<p>'.&mt('Creating old version [_1]',$maxversion).'</p>';
1947: unless ($usebuffer) {
1948: $r->print($output);
1949: $output = '';
1950: }
1951: print $logfile "\nCreating old version ".$maxversion."\n";
1952:
1953: my $copyfile=$srcd.'/'.$srcf.'.'.$maxversion.'.'.$srct;
1954:
1955: if (copy($target,$copyfile)) {
1956: print $logfile "Copied old target to ".$copyfile."\n";
1957: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied old target file'));
1958: unless ($usebuffer) {
1959: $r->print($output);
1960: $output = '';
1961: }
1962: } else {
1963: print $logfile "Unable to write ".$copyfile.':'.$!."\n";
1964: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Failed to copy old target').", $!",1);
1965: if ($usebuffer) {
1966: if (wantarray) {
1967: return ($output,0);
1968: } else {
1969: return 0;
1970: }
1971: } else {
1972: $r->print($output);
1973: return 0;
1974: }
1975: }
1976:
1977: # --------------------------------------------------------------- Copy Metadata
1978:
1979: $copyfile=$copyfile.'.meta';
1980:
1981: if (copy($target.'.meta',$copyfile)) {
1982: print $logfile "Copied old target metadata to ".$copyfile."\n";
1983: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied old metadata'));
1984: unless ($usebuffer) {
1985: $r->print($output);
1986: $output = '';
1987: }
1988: } else {
1989: print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
1990: if (-e $target.'.meta') {
1991: $output .= &Apache::lonhtmlcommon::confirm_success(
1992: &mt('Failed to write old metadata copy').", $!",1);
1993: if ($usebuffer) {
1994: if (wantarray) {
1995: return ($output,0);
1996: } else {
1997: return 0;
1998: }
1999: } else {
2000: $r->print($output);
2001: return 0;
2002: }
2003: }
2004: }
2005: } else {
2006: $output .= '<p>'.&mt('Initial version').'</p>';
2007: unless ($usebuffer) {
2008: $r->print($output);
2009: $output = '';
2010: }
2011: print $logfile "\nInitial version";
2012: }
2013:
2014: # ---------------------------------------------------------------- Write Source
2015: my $copyfile=$target;
2016:
2017: my @parts=split(/\//,$copyfile);
2018: my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
2019:
2020: my $count;
2021: for ($count=5;$count<$#parts;$count++) {
2022: $path.="/$parts[$count]";
2023: if ((-e $path)!=1) {
2024: print $logfile "\nCreating directory ".$path;
2025: mkdir($path,0777);
2026: $output .= '<p>'
2027: .&mt('Created directory [_1]'
2028: ,'<span class="LC_filename">'.$parts[$count].'</span>')
2029: .'</p>';
2030: unless ($usebuffer) {
2031: $r->print($output);
2032: $output = '';
2033: }
2034: }
2035: }
2036:
2037: if (copy($source,$copyfile)) {
2038: print $logfile "\nCopied original source to ".$copyfile."\n";
2039: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied source file'));
2040: unless ($usebuffer) {
2041: $r->print($output);
2042: $output = '';
2043: }
2044: } else {
2045: print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";
2046: $output .= &Apache::lonhtmlcommon::confirm_success(
2047: &mt('Failed to copy source').", $!",1);
2048: if ($usebuffer) {
2049: if (wantarray) {
2050: return ($output,0);
2051: } else {
2052: return 0;
2053: }
2054: } else {
2055: $r->print($output);
2056: return 0;
2057: }
2058: }
2059:
2060: # ---------------------------------------------- Delete local tmp-preview files
2061: unlink($copyfile.'.tmp');
2062: # --------------------------------------------------------------- Copy Metadata
2063:
2064: $copyfile=$copyfile.'.meta';
2065:
2066: if (copy($source.'.meta',$copyfile)) {
2067: print $logfile "\nCopied original metadata to ".$copyfile."\n";
2068: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied metadata'));
2069: unless ($usebuffer) {
2070: $r->print($output);
2071: $output = '';
2072: }
2073: } else {
2074: print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n";
2075: $output .= &Apache::lonhtmlcommon::confirm_success(
2076: &mt('Failed to write metadata copy').", $!",1);
2077: if ($usebuffer) {
2078: if (wantarray) {
2079: return ($output,0);
2080: } else {
2081: return 0;
2082: }
2083: } else {
2084: $r->print($output);
2085: return 0;
2086: }
2087: }
2088: unless ($usebuffer) {
2089: $r->rflush;
2090: }
2091:
2092: # ------------------------------------------------------------- Trigger updates
2093: push(@{$modified_urls},[$target,$source]);
2094: unless ($registered_cleanup) {
2095: my $handlers = $r->get_handlers('PerlCleanupHandler');
2096: $r->set_handlers('PerlCleanupHandler' => [\¬ify,@{$handlers}]);
2097: $registered_cleanup=1;
2098: }
2099:
2100: # ---------------------------------------------------------- Clear local caches
2101: my $thisdistarget=$target;
2102: $thisdistarget=~s/^\Q$docroot\E//;
2103: &Apache::lonnet::devalidate_cache_new('resversion',$target);
2104: &Apache::lonnet::devalidate_cache_new('meta',
2105: &Apache::lonnet::declutter($thisdistarget));
2106:
2107: # ------------------------------------------------------------- Everything done
2108: $logfile->close();
2109: $output .= '<p class="LC_success">'.&mt('Done').'</p>';
2110: unless ($usebuffer) {
2111: $r->print($output);
2112: $output = '';
2113: }
2114:
2115: # ------------------------------------------------ Provide link to new resource
2116: unless ($batch) {
2117:
2118: my $thissrc=&Apache::loncfile::url($source);
2119: my $thissrcdir=$thissrc;
2120: $thissrcdir=~s/\/[^\/]+$/\//;
2121:
2122: $output .=
2123: &Apache::lonhtmlcommon::actionbox([
2124: '<a href="'.$thisdistarget.'">'.
2125: &mt('View Published Version').
2126: '</a>',
2127: '<a href="'.$thissrc.'">'.
2128: &mt('Back to Source').
2129: '</a>',
2130: '<a href="'.$thissrcdir.'">'.
2131: &mt('Back to Source Directory').
2132: '</a>']);
2133: unless ($usebuffer) {
2134: $r->print($output);
2135: $output = '';
2136: }
2137: }
2138:
2139: if ($usebuffer) {
2140: if (wantarray) {
2141: return ($output,1);
2142: } else {
2143: return 1;
2144: }
2145: } else {
2146: if (wantarray) {
2147: return ('',1);
2148: } else {
2149: return 1;
2150: }
2151: }
2152: }
2153:
2154: # =============================================================== Notifications
2155: sub notify {
2156: # --------------------------------------------------- Send update notifications
2157: foreach my $targetsource (@{$modified_urls}){
2158: my ($target,$source)=@{$targetsource};
2159: my $logfile=Apache::File->new('>>'.$source.'.log');
2160: print $logfile "\nCleanup phase: Notifications\n";
2161: my @subscribed=&get_subscribed_hosts($target);
2162: foreach my $subhost (@subscribed) {
2163: print $logfile "\nNotifying host ".$subhost.':';
2164: my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
2165: print $logfile $reply;
2166: }
2167: # ---------------------------------------- Send update notifications, meta only
2168: my @subscribedmeta=&get_subscribed_hosts("$target.meta");
2169: foreach my $subhost (@subscribedmeta) {
2170: print $logfile "\nNotifying host for metadata only ".$subhost.':';
2171: my $reply=&Apache::lonnet::critical('update:'.$target.'.meta',
2172: $subhost);
2173: print $logfile $reply;
2174: }
2175: # --------------------------------------------------- Notify subscribed courses
2176: my %courses=&coursedependencies($target);
2177: my $now=time;
2178: foreach my $course (keys(%courses)) {
2179: print $logfile "\nNotifying course ".$course.':';
2180: my ($cdom,$cname)=split(/\_/,$course);
2181: my $reply=&Apache::lonnet::cput
2182: ('versionupdate',{$target => $now},$cdom,$cname);
2183: print $logfile $reply;
2184: }
2185: print $logfile "\n============ Done ============\n";
2186: $logfile->close();
2187: }
2188: if ($lock) { &Apache::lonnet::remove_lock($lock); }
2189: return OK;
2190: }
2191:
2192: #########################################
2193:
2194: sub batchpublish {
2195: my ($r,$srcfile,$targetfile,$nokeyref,$usebuffer)=@_;
2196: #publication pollutes %env with form.* values
2197: my %oldenv=%env;
2198: $srcfile=~s/\/+/\//g;
2199: $targetfile=~s/\/+/\//g;
2200:
2201: my $docroot=$r->dir_config('lonDocRoot');
2202: my $thisdistarget=$targetfile;
2203: $thisdistarget=~s/^\Q$docroot\E//;
2204:
2205:
2206: %metadatafields=();
2207: %metadatakeys=();
2208: $srcfile=~/\.(\w+)$/;
2209: my $thistype=$1;
2210:
2211:
2212: my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
2213:
2214: my $output = '<h2>'
2215: .&mt('Publishing [_1]',&Apache::loncfile::display($srcfile))
2216: .'</h2>';
2217: unless ($usebuffer) {
2218: $r->print($output);
2219: $output = '';
2220: }
2221:
2222: # phase one takes
2223: # my ($source,$target,$style,$batch)=@_;
2224: my ($outstring,$error)=&publish($srcfile,$targetfile,$thisembstyle,1,$nokeyref);
2225:
2226: if ($usebuffer) {
2227: $output .= '<p>'.$outstring.'</p>';
2228: } else {
2229: $r->print('<p>'.$outstring.'</p>');
2230: }
2231: # phase two takes
2232: # my ($source,$target,$style,$distarget,batch)=@_;
2233: # $env{'form.allmeta'},$env{'form.title'},$env{'form.author'},...
2234: if (!$error) {
2235: if ($usebuffer) {
2236: my ($result,$error) = &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1,$usebuffer);
2237: $output .= '<p>'.$result.'</p>';
2238: } else {
2239: &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1);
2240: }
2241: }
2242: %env=%oldenv;
2243: if ($usebuffer) {
2244: return $output;
2245: } else {
2246: return '';
2247: }
2248: }
2249:
2250: #########################################
2251:
2252: sub publishdirectory {
2253: my ($r,$fn,$thisdisfn,$nokeyref)=@_;
2254: $fn=~s/\/+/\//g;
2255: $thisdisfn=~s/\/+/\//g;
2256: my $thisdisresdir=$thisdisfn;
2257: $thisdisresdir=~s/^\/priv\//\/res\//;
2258: my $resdir = $r->dir_config('lonDocRoot').$thisdisresdir;
2259: $r->print('<form name="pubdirpref" method="post" action="">'
2260: .&Apache::lonhtmlcommon::start_pick_box()
2261: .&Apache::lonhtmlcommon::row_title(&mt('Directory'))
2262: .'<span class="LC_filename">'.$thisdisfn.'</span>'
2263: .&Apache::lonhtmlcommon::row_closure()
2264: .&Apache::lonhtmlcommon::row_title(&mt('Target'))
2265: .'<span class="LC_filename">'.$thisdisresdir.'</span>'
2266: );
2267:
2268: my $dirptr=16384; # Mask indicating a directory in stat.cmode.
2269: unless ($env{'form.phase'} eq 'two') {
2270: # ask user what they want
2271: $r->print(&Apache::lonhtmlcommon::row_closure()
2272: .&Apache::lonhtmlcommon::row_title(&mt('Options'))
2273: );
2274: $r->print(&hiddenfield('phase','two').
2275: &hiddenfield('filename',$env{'form.filename'}).
2276: &checkbox('pubrec','include subdirectories').
2277: &checkbox('forcerepub','force republication of previously published files').
2278: &checkbox('obsolete','make file(s) obsolete').
2279: &checkbox('forceoverride','force directory level metadata over existing').
2280: &checkbox('excludeunpub','exclude currently unpublished files').
2281: &common_access('dist',&mt('apply common copyright/distribution'),
2282: ['default','domain','custom']).
2283: &common_access('source',&mt('apply common source availability'),
2284: ['closed','open'])
2285: );
2286: $r->print(&Apache::lonhtmlcommon::row_closure(1)
2287: .&Apache::lonhtmlcommon::end_pick_box()
2288: .'<br /><input type="submit" value="'.&mt('Publish Directory').'" /></form>'
2289: );
2290: $lock=0;
2291: } else {
2292: $r->print(&Apache::lonhtmlcommon::row_closure(1)
2293: .&Apache::lonhtmlcommon::end_pick_box()
2294: );
2295: unless ($lock) { $lock=&Apache::lonnet::set_lock(&mt('Publishing [_1]',$fn)); }
2296: # actually publish things
2297: opendir(DIR,$fn);
2298: my @files=sort(readdir(DIR));
2299: foreach my $filename (@files) {
2300: my ($cdev,$cino,$cmode,$cnlink,
2301: $cuid,$cgid,$crdev,$csize,
2302: $catime,$cmtime,$cctime,
2303: $cblksize,$cblocks)=stat($fn.'/'.$filename);
2304:
2305: my $extension='';
2306: if ($filename=~/\.(\w+)$/) { $extension=$1; }
2307: if ($cmode&$dirptr) {
2308: if (($filename!~/^\./) && ($env{'form.pubrec'})) {
2309: &publishdirectory($r,$fn.'/'.$filename,$thisdisfn.'/'.$filename,$nokeyref);
2310: }
2311: } elsif ((&Apache::loncommon::fileembstyle($extension) ne 'hdn') &&
2312: ($filename!~/^[\#\.]/) && ($filename!~/\~$/)) {
2313: # find out publication status and/or existing metadata
2314: my $publishthis=0;
2315: if (-e $resdir.'/'.$filename) {
2316: my ($rdev,$rino,$rmode,$rnlink,
2317: $ruid,$rgid,$rrdev,$rsize,
2318: $ratime,$rmtime,$rctime,
2319: $rblksize,$rblocks)=stat($resdir.'/'.$filename);
2320: if (($rmtime<$cmtime) || ($env{'form.forcerepub'})) {
2321: # previously published, modified now
2322: $publishthis=1;
2323: }
2324: my $meta_cmtime = (stat($fn.'/'.$filename.'.meta'))[9];
2325: my $meta_rmtime = (stat($resdir.'/'.$filename.'.meta'))[9];
2326: if ( $meta_rmtime<$meta_cmtime ) {
2327: $publishthis=1;
2328: }
2329: } else {
2330: # never published
2331: unless ($env{'form.excludeunpub'}) {
2332: $publishthis=1;
2333: }
2334: }
2335:
2336: if ($publishthis) {
2337: &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename,$nokeyref);
2338: } else {
2339: $r->print('<br />'.&mt('Skipping').' '.$filename.'<br />');
2340: }
2341: $r->rflush();
2342: }
2343: }
2344: closedir(DIR);
2345: }
2346: }
2347:
2348: #########################################
2349: # publish a default.meta file
2350:
2351: sub defaultmetapublish {
2352: my ($r,$fn,$cuname,$cudom)=@_;
2353: unless (-e $fn) {
2354: return HTTP_NOT_FOUND;
2355: }
2356: my $target=$fn;
2357: $target=~s/^\Q$Apache::lonnet::perlvar{'lonDocRoot'}\E\/priv\//\Q$Apache::lonnet::perlvar{'lonDocRoot'}\E\/res\//;
2358:
2359:
2360: &Apache::loncommon::content_type($r,'text/html');
2361: $r->send_http_header;
2362:
2363: $r->print(&Apache::loncommon::start_page('Metadata Publication'));
2364:
2365: # ---------------------------------------------------------------- Write Source
2366: my $copyfile=$target;
2367:
2368: my @parts=split(/\//,$copyfile);
2369: my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
2370:
2371: my $count;
2372: for ($count=5;$count<$#parts;$count++) {
2373: $path.="/$parts[$count]";
2374: if ((-e $path)!=1) {
2375: mkdir($path,0777);
2376: $r->print('<p>'
2377: .&mt('Created directory [_1]'
2378: ,'<span class="LC_filename">'.$parts[$count].'</span>')
2379: .'</p>'
2380: );
2381: }
2382: }
2383:
2384: if (copy($fn,$copyfile)) {
2385: $r->print('<p>'.&mt('Copied source file').'</p>');
2386: } else {
2387: return "<span class=\"LC_error\">".
2388: &mt('Failed to copy source').", $!, ".&mt('FAIL')."</span>";
2389: }
2390:
2391: # --------------------------------------------------- Send update notifications
2392:
2393: my @subscribed=&get_subscribed_hosts($target);
2394: foreach my $subhost (@subscribed) {
2395: $r->print('<p>'.&mt('Notifying host').' '.$subhost.':');$r->rflush;
2396: my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
2397: $r->print($reply.'</p><br />');$r->rflush;
2398: }
2399: # ------------------------------------------------------------------- Link back
2400: $r->print("<a href='".&Apache::loncfile::display($fn)."'>".&mt('Back to Metadata').'</a>');
2401: $r->print(&Apache::loncommon::end_page());
2402: return OK;
2403: }
2404: #########################################
2405:
2406: =pod
2407:
2408: =item B<handler>
2409:
2410: A basic outline of the handler subroutine follows.
2411:
2412: =over 4
2413:
2414: =item *
2415:
2416: Get query string for limited number of parameters.
2417:
2418: =item *
2419:
2420: Check filename.
2421:
2422: =item *
2423:
2424: File is there and owned, init lookup tables.
2425:
2426: =item *
2427:
2428: Start page output.
2429:
2430: =item *
2431:
2432: Evaluate individual file, and then output information.
2433:
2434: =item *
2435:
2436: Publishing from $thisfn to $thistarget with $thisembstyle.
2437:
2438: =back
2439:
2440: =cut
2441:
2442: #########################################
2443: #########################################
2444: sub handler {
2445: my $r=shift;
2446:
2447: if ($r->header_only) {
2448: &Apache::loncommon::content_type($r,'text/html');
2449: $r->send_http_header;
2450: return OK;
2451: }
2452:
2453: # Get query string for limited number of parameters
2454:
2455: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
2456: ['filename']);
2457:
2458: # -------------------------------------- Flag and buffer for registered cleanup
2459: $registered_cleanup=0;
2460: @{$modified_urls}=();
2461: # -------------------------------------------------------------- Check filename
2462:
2463: my $fn=&unescape($env{'form.filename'});
2464: ($cuname,$cudom)=&Apache::lonnet::constructaccess($fn);
2465: # ----------------------------------------------------- Do we have permissions?
2466: unless (($cuname) && ($cudom)) {
2467: $r->log_reason($env{'user.name'}.' at '.$env{'user.domain'}.
2468: ' trying to publish file '.$env{'form.filename'}.
2469: ' - not authorized',
2470: $r->filename);
2471: return HTTP_NOT_ACCEPTABLE;
2472: }
2473: # ----------------------------------------------------------------- Get docroot
2474: $docroot=$r->dir_config('lonDocRoot');
2475:
2476:
2477: # special publication: default.meta file
2478: if ($fn=~/\/default.meta$/) {
2479: return &defaultmetapublish($r,$fn,$cuname,$cudom);
2480: }
2481: $fn=~s/\.meta$//;
2482:
2483: # sanity test on the filename
2484:
2485: unless ($fn) {
2486: $r->log_reason($cuname.' at '.$cudom.
2487: ' trying to publish empty filename', $r->filename);
2488: return HTTP_NOT_FOUND;
2489: }
2490:
2491: unless (-e $docroot.$fn) {
2492: $r->log_reason($cuname.' at '.$cudom.
2493: ' trying to publish non-existing file '.
2494: $env{'form.filename'}.' ('.$fn.')',
2495: $r->filename);
2496: return HTTP_NOT_FOUND;
2497: }
2498:
2499: # --------------------------------- File is there and owned, start page output
2500:
2501: &Apache::loncommon::content_type($r,'text/html');
2502: $r->send_http_header;
2503:
2504: # Breadcrumbs
2505: &Apache::lonhtmlcommon::clear_breadcrumbs();
2506: &Apache::lonhtmlcommon::add_breadcrumb({
2507: 'text' => 'Authoring Space',
2508: 'href' => &Apache::loncommon::authorspace($fn),
2509: });
2510: &Apache::lonhtmlcommon::add_breadcrumb({
2511: 'text' => 'Resource Publication',
2512: 'href' => '',
2513: });
2514:
2515: my $js='<script type="text/javascript">'.
2516: &Apache::loncommon::browser_and_searcher_javascript().
2517: '</script>';
2518: my $startargs = {};
2519: if ($fn=~/\/$/) {
2520: unless ($env{'form.phase'} eq 'two') {
2521: $startargs->{'add_entries'} = { onload => 'javascript:setDefaultAccess();' };
2522: $js .= <<"END";
2523: <script type="text/javascript">
2524: // <![CDATA[
2525: function showHideAccess(caller,div) {
2526: if (document.getElementById(div)) {
2527: if (caller.checked) {
2528: document.getElementById(div).style.display='inline-block';
2529: } else {
2530: document.getElementById(div).style.display='none';
2531: }
2532: }
2533: }
2534:
2535: function showHideCustom(caller,divid) {
2536: if (document.getElementById(divid)) {
2537: if (caller.options[caller.selectedIndex].value == 'custom') {
2538: document.getElementById(divid).style.display="inline-block";
2539: } else {
2540: document.getElementById(divid).style.display="none";
2541: }
2542: }
2543: }
2544: function setDefaultAccess() {
2545: var chkids = Array('LC_commondist','LC_commonsource');
2546: for (var i=0; i<chkids.length; i++) {
2547: if (document.getElementById(chkids[i])) {
2548: document.getElementById(chkids[i]).checked = false;
2549: }
2550: if (document.getElementById(chkids[i]+'select')) {
2551: document.getElementById(chkids[i]+'select').selectedIndex = 0;
2552: }
2553: if (document.getElementById(chkids[i]+'div')) {
2554: document.getElementById(chkids[i]+'div').style.display = 'none';
2555: }
2556: }
2557: }
2558: // ]]>
2559: </script>
2560:
2561: END
2562: }
2563: }
2564: $r->print(&Apache::loncommon::start_page('Resource Publication',$js,$startargs)
2565: .&Apache::lonhtmlcommon::breadcrumbs()
2566: .&Apache::loncommon::head_subbox(
2567: &Apache::loncommon::CSTR_pageheader($docroot.$fn))
2568: );
2569:
2570: my $thisdisfn=&HTML::Entities::encode($fn,'<>&"');
2571: my $thistarget=$fn;
2572: $thistarget=~s/^\/priv\//\/res\//;
2573: my $thisdistarget=&HTML::Entities::encode($thistarget,'<>&"');
2574: my $nokeyref = &getnokey($r->dir_config('lonIncludes'));
2575:
2576: if ($fn=~/\/$/) {
2577: # -------------------------------------------------------- This is a directory
2578: &publishdirectory($r,$docroot.$fn,$thisdisfn,$nokeyref);
2579: $r->print(
2580: '<br /><br />'.
2581: &Apache::lonhtmlcommon::actionbox([
2582: '<a href="'.$thisdisfn.'">'.&mt('Return to Directory').'</a>']));
2583: } else {
2584: # ---------------------- Evaluate individual file, and then output information.
2585: $fn=~/\.(\w+)$/;
2586: my $thistype=$1;
2587: my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
2588: if ($thistype eq 'page') { $thisembstyle = 'rat'; }
2589:
2590: $r->print('<h2>'
2591: .&mt('Publishing [_1]'
2592: ,'<span class="LC_filename">'.$thisdisfn.'</span>')
2593: .'</h2>'
2594: );
2595:
2596: $r->print('<h3>'.&mt('Resource Details').'</h3>');
2597:
2598: $r->print(&Apache::lonhtmlcommon::start_pick_box());
2599:
2600: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Type'))
2601: .&Apache::loncommon::filedescription($thistype)
2602: .&Apache::lonhtmlcommon::row_closure()
2603: );
2604:
2605: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Link to Resource'))
2606: .'<tt>'
2607: );
2608: $r->print(<<ENDCAPTION);
2609: <a href='javascript:void(window.open("$thisdisfn","cat","height=300,width=500,scrollbars=1,resizable=1,menubar=0,location=1"))'>
2610: $thisdisfn</a>
2611: ENDCAPTION
2612: $r->print('</tt>'
2613: .&Apache::lonhtmlcommon::row_closure()
2614: );
2615:
2616: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Target'))
2617: .'<tt>'.$thisdistarget.'</tt>'
2618: );
2619: if (($cuname ne $env{'user.name'})||($cudom ne $env{'user.domain'})) {
2620: $r->print(&Apache::lonhtmlcommon::row_closure()
2621: .&Apache::lonhtmlcommon::row_title(&mt('Co-Author'))
2622: .'<span class="LC_warning">'
2623: .&Apache::loncommon::plainname($cuname,$cudom) .' ('.$cuname.':'.$cudom.')'
2624: .'</span>'
2625: );
2626: }
2627:
2628: if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') {
2629: $r->print(&Apache::lonhtmlcommon::row_closure()
2630: .&Apache::lonhtmlcommon::row_title(&mt('Diffs')));
2631: $r->print(<<ENDDIFF);
2632: <a href='javascript:void(window.open("/adm/diff?filename=$thisdisfn&versiontwo=priv","cat","height=300,width=500,scrollbars=1,resizable=1,menubar=0,location=1"))'>
2633: ENDDIFF
2634: $r->print(&mt('Diffs with Current Version').'</a>');
2635: }
2636:
2637: $r->print(&Apache::lonhtmlcommon::row_closure(1)
2638: .&Apache::lonhtmlcommon::end_pick_box()
2639: );
2640:
2641: # ---------------------- Publishing from $fn to $thistarget with $thisembstyle.
2642:
2643: unless ($env{'form.phase'} eq 'two') {
2644: # ---------------------------------------------------------- Parse for problems
2645: my ($warningcount,$errorcount);
2646: if ($thisembstyle eq 'ssi') {
2647: ($warningcount,$errorcount)=&checkonthis($r,$fn);
2648: }
2649: unless ($errorcount) {
2650: my ($outstring,$error)=
2651: &publish($docroot.$fn,$docroot.$thistarget,$thisembstyle,undef,$nokeyref);
2652: $r->print($outstring);
2653: } else {
2654: $r->print('<h3 class="LC_error">'.
2655: &mt('The document contains errors and cannot be published.').
2656: '</h3>');
2657: }
2658: } else {
2659: my ($output,$error) = &phasetwo($r,$docroot.$fn,$docroot.$thistarget,
2660: $thisembstyle,$thisdistarget);
2661: $r->print($output);
2662: }
2663: }
2664: $r->print(&Apache::loncommon::end_page());
2665:
2666: return OK;
2667: }
2668:
2669: BEGIN {
2670:
2671: # ----------------------------------- Read addid.tab
2672: unless ($readit) {
2673: %addid=();
2674:
2675: {
2676: my $tabdir = $Apache::lonnet::perlvar{'lonTabDir'};
2677: my $fh=Apache::File->new($tabdir.'/addid.tab');
2678: while (<$fh>=~/(\w+)\s+(\w+)/) {
2679: $addid{$1}=$2;
2680: }
2681: }
2682: }
2683: $readit=1;
2684: }
2685:
2686:
2687: 1;
2688: __END__
2689:
2690: =pod
2691:
2692: =back
2693:
2694: =cut
2695:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>