Annotation of loncom/publisher/lonpublisher.pm, revision 1.297
1.1 www 1: # The LearningOnline Network with CAPA
2: # Publication Handler
1.54 albertel 3: #
1.297 ! raeburn 4: # $Id: lonpublisher.pm,v 1.296 2016/06/19 01:08:01 raeburn Exp $
1.54 albertel 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: #
1.65 harris41 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: ###############################################################################
1.1 www 41:
1.90 matthew 42:
43: ######################################################################
44: ######################################################################
45:
46: =pod
47:
1.94 harris41 48: =head1 NAME
1.90 matthew 49:
50: lonpublisher - LON-CAPA publishing handler
51:
1.94 harris41 52: =head1 SYNOPSIS
1.90 matthew 53:
1.94 harris41 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>
1.127 bowersj2 66:
67: =head1 OVERVIEW
68:
1.274 raeburn 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.
1.127 bowersj2 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.
1.94 harris41 86:
87: =head1 DESCRIPTION
88:
89: B<lonpublisher> takes the proper steps to add resources to the LON-CAPA
1.90 matthew 90: digital library. This includes updating the metadata table in the
91: LON-CAPA database.
92:
1.94 harris41 93: B<lonpublisher> is many things to many people.
1.90 matthew 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:
1.94 harris41 100: =head2 SUBROUTINES
101:
102: Many of the undocumented subroutines implement various magical
103: parsing shortcuts.
1.90 matthew 104:
105: =cut
106:
107: ######################################################################
108: ######################################################################
109:
110:
1.1 www 111: package Apache::lonpublisher;
112:
1.65 harris41 113: # ------------------------------------------------- modules used by this module
1.1 www 114: use strict;
115: use Apache::File;
1.13 www 116: use File::Copy;
1.2 www 117: use Apache::Constants qw(:common :http :methods);
1.76 albertel 118: use HTML::LCParser;
1.245 onken 119: use HTML::Entities;
120: use Encode::Encoder;
1.4 www 121: use Apache::lonxml;
1.24 harris41 122: use DBI;
1.192 albertel 123: use Apache::lonnet;
1.65 harris41 124: use Apache::loncommon();
1.241 raeburn 125: use Apache::lonhtmlcommon;
1.89 matthew 126: use Apache::lonmysql;
1.134 www 127: use Apache::lonlocal;
1.145 albertel 128: use Apache::loncfile;
1.166 matthew 129: use LONCAPA::lonmetadata;
1.159 www 130: use Apache::lonmsg;
1.296 raeburn 131: use vars qw(%metadatafields %metadatakeys %addid $readit);
1.215 albertel 132: use LONCAPA qw(:DEFAULT :match);
1.209 www 133:
1.12 www 134: my $docroot;
135:
1.27 www 136: my $cuname;
137: my $cudom;
138:
1.182 www 139: my $registered_cleanup;
1.183 www 140: my $modified_urls;
1.182 www 141:
1.233 www 142: my $lock;
143:
1.90 matthew 144: =pod
145:
1.287 raeburn 146: =over 4
147:
1.94 harris41 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
1.90 matthew 158:
1.94 harris41 159: =item I<$metastring>
160:
161: A string that contains metadata.
162:
163: =back
164:
165: Returns:
166:
167: nothing
1.90 matthew 168:
169: =cut
170:
171: #########################################
172: #########################################
1.144 www 173: #
174: # Modifies global %metadatafields %metadatakeys
175: #
176:
1.7 www 177: sub metaeval {
1.140 albertel 178: my ($metastring,$prefix)=@_;
1.7 www 179:
1.139 albertel 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;
1.219 albertel 186: next if ($entry =~ m/^(?:parameter|stores)_/);
1.139 albertel 187: if (defined($token->[2]->{'package'})) {
1.219 albertel 188: $unikey.="\0package\0".$token->[2]->{'package'};
1.139 albertel 189: }
190: if (defined($token->[2]->{'part'})) {
1.219 albertel 191: $unikey.="\0".$token->[2]->{'part'};
1.139 albertel 192: }
193: if (defined($token->[2]->{'id'})) {
1.219 albertel 194: $unikey.="\0".$token->[2]->{'id'};
1.139 albertel 195: }
196: if (defined($token->[2]->{'name'})) {
1.219 albertel 197: $unikey.="\0".$token->[2]->{'name'};
1.139 albertel 198: }
1.294 raeburn 199: foreach my $item (@{$token->[3]}) {
200: $metadatafields{$unikey.'.'.$item}=$token->[2]->{$item};
1.139 albertel 201: if ($metadatakeys{$unikey}) {
1.294 raeburn 202: $metadatakeys{$unikey}.=','.$item;
1.139 albertel 203: } else {
1.294 raeburn 204: $metadatakeys{$unikey}=$item;
1.139 albertel 205: }
206: }
1.140 albertel 207: my $newentry=$parser->get_text('/'.$entry);
1.174 www 208: if (($entry eq 'customdistributionfile') ||
209: ($entry eq 'sourcerights')) {
1.140 albertel 210: $newentry=~s/^\s*//;
211: if ($newentry !~m|^/res|) { $newentry=$prefix.$newentry; }
212: }
1.149 www 213: # actually store
1.162 albertel 214: if ( $entry eq 'rule' && exists($metadatafields{$unikey})) {
215: $metadatafields{$unikey}.=','.$newentry;
216: } else {
217: $metadatafields{$unikey}=$newentry;
218: }
1.139 albertel 219: }
220: }
1.7 www 221: }
222:
1.90 matthew 223: #########################################
224: #########################################
225:
226: =pod
227:
1.94 harris41 228: =item B<metaread>
1.90 matthew 229:
230: Read a metadata file
231:
1.94 harris41 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:
1.90 matthew 256: =cut
257:
258: #########################################
259: #########################################
1.7 www 260: sub metaread {
1.140 albertel 261: my ($logfile,$fn,$prefix)=@_;
1.7 www 262: unless (-e $fn) {
1.94 harris41 263: print($logfile 'No file '.$fn."\n");
1.271 www 264: return '<p class="LC_warning">'
265: .&mt('No file: [_1]',&Apache::loncfile::display($fn))
266: .'</p>';
1.7 www 267: }
1.94 harris41 268: print($logfile 'Processing '.$fn."\n");
1.7 www 269: my $metastring;
270: {
1.140 albertel 271: my $metafh=Apache::File->new($fn);
272: $metastring=join('',<$metafh>);
1.7 www 273: }
1.140 albertel 274: &metaeval($metastring,$prefix);
1.271 www 275: return '<p class="LC_info">'
276: .&mt('Processed file: [_1]',&Apache::loncfile::display($fn))
277: .'</p>';
1.7 www 278: }
1.12 www 279:
1.90 matthew 280: #########################################
281: #########################################
282:
1.101 www 283: sub coursedependencies {
284: my $url=&Apache::lonnet::declutter(shift);
285: $url=~s/\.meta$//;
1.215 albertel 286: my ($adomain,$aauthor)=($url=~ m{^($match_domain)/($match_username)/});
287: my $regexp=quotemeta($url);
1.101 www 288: $regexp='___'.$regexp.'___course';
289: my %evaldata=&Apache::lonnet::dump('nohist_resevaldata',$adomain,
290: $aauthor,$regexp);
291: my %courses=();
1.294 raeburn 292: foreach my $item (keys(%evaldata)) {
293: if ($item=~/^([a-zA-Z0-9]+_[a-zA-Z0-9]+)___.+___course$/) {
1.101 www 294: $courses{$1}=1;
295: }
296: }
297: return %courses;
298: }
299: #########################################
300: #########################################
301:
302:
1.90 matthew 303: =pod
304:
1.94 harris41 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).
1.90 matthew 312:
313: =over 4
314:
1.94 harris41 315: =item B<textfield>
1.90 matthew 316:
1.295 raeburn 317: =item B<text_with_browse_field>
318:
1.94 harris41 319: =item B<hiddenfield>
1.90 matthew 320:
1.295 raeburn 321: =item B<checkbox>
322:
1.94 harris41 323: =item B<selectbox>
1.90 matthew 324:
325: =back
326:
327: =cut
328:
329: #########################################
330: #########################################
1.8 www 331: sub textfield {
1.240 raeburn 332: my ($title,$name,$value,$noline)=@_;
1.141 www 333: $value=~s/^\s+//gs;
334: $value=~s/\s+$//gs;
335: $value=~s/\s+/ /gs;
1.134 www 336: $title=&mt($title);
1.192 albertel 337: $env{'form.'.$name}=$value;
1.238 bisitz 338: return "\n".&Apache::lonhtmlcommon::row_title($title)
339: .'<input type="text" name="'.$name.'" size="80" value="'.$value.'" />'
1.240 raeburn 340: .&Apache::lonhtmlcommon::row_closure($noline);
1.11 www 341: }
342:
1.180 albertel 343: sub text_with_browse_field {
1.240 raeburn 344: my ($title,$name,$value,$restriction,$noline)=@_;
1.180 albertel 345: $value=~s/^\s+//gs;
346: $value=~s/\s+$//gs;
347: $value=~s/\s+/ /gs;
348: $title=&mt($title);
1.192 albertel 349: $env{'form.'.$name}=$value;
1.238 bisitz 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>'
1.240 raeburn 359: .&Apache::lonhtmlcommon::row_closure($noline);
1.180 albertel 360: }
361:
1.11 www 362: sub hiddenfield {
363: my ($name,$value)=@_;
1.192 albertel 364: $env{'form.'.$name}=$value;
1.94 harris41 365: return "\n".'<input type="hidden" name="'.$name.'" value="'.$value.'" />';
1.8 www 366: }
367:
1.193 www 368: sub checkbox {
369: my ($name,$text)=@_;
1.201 albertel 370: return "\n<br /><label><input type='checkbox' name='$name' /> ".
371: &mt($text)."</label>";
1.193 www 372: }
373:
1.9 www 374: sub selectbox {
1.65 harris41 375: my ($title,$name,$value,$functionref,@idlist)=@_;
1.134 www 376: $title=&mt($title);
1.123 albertel 377: $value=(split(/\s*,\s*/,$value))[-1];
1.167 albertel 378: if (defined($value)) {
1.192 albertel 379: $env{'form.'.$name}=$value;
1.167 albertel 380: } else {
1.192 albertel 381: $env{'form.'.$name}=$idlist[0];
1.167 albertel 382: }
1.238 bisitz 383: my $selout="\n".&Apache::lonhtmlcommon::row_title($title)
384: .'<select name="'.$name.'">';
1.294 raeburn 385: foreach my $id (@idlist) {
386: $selout.='<option value="'.$id.'"';
387: if ($id eq $value) {
1.257 bisitz 388: $selout.=' selected="selected"';
389: }
1.294 raeburn 390: $selout.='>'.&{$functionref}($id).'</option>';
1.65 harris41 391: }
1.238 bisitz 392: $selout.='</select>'.&Apache::lonhtmlcommon::row_closure();
393: return $selout;
1.9 www 394: }
395:
1.167 albertel 396: sub select_level_form {
397: my ($value,$name)=@_;
1.192 albertel 398: $env{'form.'.$name}=$value;
399: if (!defined($value)) { $env{'form.'.$name}=0; }
1.167 albertel 400: return &Apache::loncommon::select_level_form($value,$name);
401: }
1.295 raeburn 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:
1.90 matthew 455: #########################################
456: #########################################
457:
458: =pod
459:
1.94 harris41 460: =item B<urlfixup>
1.90 matthew 461:
462: Fix up a url? First step of publication
1.12 www 463:
1.90 matthew 464: =cut
465:
466: #########################################
467: #########################################
1.34 www 468: sub urlfixup {
1.35 www 469: my ($url,$target)=@_;
1.39 www 470: unless ($url) { return ''; }
1.68 albertel 471: #javascript code needs no fixing
472: if ($url =~ /^javascript:/i) { return $url; }
1.69 albertel 473: if ($url =~ /^mailto:/i) { return $url; }
1.68 albertel 474: #internal document links need no fixing
475: if ($url =~ /^\#/) { return $url; }
1.223 albertel 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//;
1.65 harris41 481: }
1.223 albertel 482: if ($url=~m{^(?:http|https|ftp)://}) { return $url; }
1.222 albertel 483: $url=~s{\Q~$cuname\E}{res/$cudom/$cuname};
1.71 www 484: return $url;
485: }
486:
1.90 matthew 487: #########################################
488: #########################################
489:
490: =pod
491:
1.94 harris41 492: =item B<absoluteurl>
1.90 matthew 493:
1.94 harris41 494: Currently undocumented.
1.90 matthew 495:
496: =cut
1.71 www 497:
1.90 matthew 498: #########################################
499: #########################################
1.71 www 500: sub absoluteurl {
501: my ($url,$target)=@_;
502: unless ($url) { return ''; }
1.35 www 503: if ($target) {
504: $target=~s/\/[^\/]+$//;
505: $url=&Apache::lonnet::hreflocation($target,$url);
506: }
507: return $url;
1.34 www 508: }
509:
1.90 matthew 510: #########################################
511: #########################################
512:
513: =pod
514:
1.94 harris41 515: =item B<set_allow>
1.90 matthew 516:
517: Currently undocumented
518:
519: =cut
520:
521: #########################################
522: #########################################
1.81 albertel 523: sub set_allow {
1.290 raeburn 524: my ($allow,$logfile,$target,$tag,$oldurl,$type)=@_;
1.81 albertel 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) &&
1.220 albertel 534: ($newurl !~ /^(?:http|https|ftp):/i) &&
1.81 albertel 535: ($newurl !~ /^\#/)) {
1.290 raeburn 536: if (($type eq 'src') || ($type eq 'href')) {
537: if ($newurl =~ /^([^?]+)\?[^?]*$/) {
538: $newurl = $1;
539: }
540: }
1.81 albertel 541: $$allow{&absoluteurl($newurl,$target)}=1;
542: }
1.218 raeburn 543: return $return_url;
1.81 albertel 544: }
545:
1.90 matthew 546: #########################################
547: #########################################
548:
549: =pod
550:
1.94 harris41 551: =item B<get_subscribed_hosts>
1.90 matthew 552:
553: Currently undocumented
554:
555: =cut
556:
557: #########################################
558: #########################################
1.85 albertel 559: sub get_subscribed_hosts {
560: my ($target)=@_;
561: my @subscribed;
562: my $filename;
563: $target=~/(.*)\/([^\/]+)$/;
564: my $srcf=$2;
565: opendir(DIR,$1);
1.225 albertel 566: # cycle through listed files, subscriptions used to exist
567: # as "filename.lonid"
1.85 albertel 568: while ($filename=readdir(DIR)) {
1.216 albertel 569: if ($filename=~/\Q$srcf\E\.($match_lonid)$/) {
1.85 albertel 570: my $subhost=$1;
1.225 albertel 571: if (($subhost ne 'meta'
572: && $subhost ne 'subscription'
573: && $subhost ne 'meta.subscription'
574: && $subhost ne 'tmp') &&
1.98 www 575: ($subhost ne $Apache::lonnet::perlvar{'lonHostID'})) {
1.85 albertel 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>) {
1.216 albertel 584: if ($subline =~ /^($match_lonid):/) {
1.98 www 585: if ($1 ne $Apache::lonnet::perlvar{'lonHostID'}) {
586: push(@subscribed,$1);
587: }
1.85 albertel 588: }
589: }
590: }
591: return @subscribed;
592: }
593:
1.86 albertel 594:
1.90 matthew 595: #########################################
596: #########################################
597:
598: =pod
599:
1.94 harris41 600: =item B<get_max_ids_indices>
1.90 matthew 601:
602: Currently undocumented
603:
604: =cut
605:
606: #########################################
607: #########################################
1.86 albertel 608: sub get_max_ids_indices {
609: my ($content)=@_;
610: my $maxindex=10;
611: my $maxid=10;
612: my $needsfixup=0;
1.106 albertel 613: my $duplicateids=0;
614:
615: my %allids;
616: my %duplicatedids;
1.86 albertel 617:
618: my $parser=HTML::LCParser->new($content);
1.207 albertel 619: $parser->xml_mode(1);
1.86 albertel 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') {
1.186 albertel 626: if (defined($token->[2]->{'id'}) &&
627: $token->[2]->{'id'} !~ /^\s*$/) {
1.86 albertel 628: $maxid=($token->[2]->{'id'}>$maxid)?$token->[2]->{'id'}:$maxid;
1.106 albertel 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: }
1.86 albertel 635: } else {
636: $needsfixup=1;
637: }
638: } else {
1.186 albertel 639: if (defined($token->[2]->{'index'}) &&
640: $token->[2]->{'index'} !~ /^\s*$/) {
1.86 albertel 641: $maxindex=($token->[2]->{'index'}>$maxindex)?$token->[2]->{'index'}:$maxindex;
642: } else {
643: $needsfixup=1;
644: }
645: }
646: }
647: }
648: }
1.106 albertel 649: return ($needsfixup,$maxid,$maxindex,$duplicateids,
650: (keys(%duplicatedids)));
1.86 albertel 651: }
652:
1.90 matthew 653: #########################################
654: #########################################
655:
656: =pod
657:
1.94 harris41 658: =item B<get_all_text_unbalanced>
1.90 matthew 659:
660: Currently undocumented
661:
662: =cut
663:
664: #########################################
665: #########################################
1.87 albertel 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: }
1.177 albertel 682: if ($result =~ /\Q$tag\E/s) {
1.176 albertel 683: ($result,my $redo)=$result =~ /(.*)\Q$tag\E(.*)/is;
1.88 albertel 684: #&Apache::lonnet::logthis('Got a winner with leftovers ::'.$2);
685: #&Apache::lonnet::logthis('Result is :'.$1);
1.176 albertel 686: $redo=$tag.$redo;
1.87 albertel 687: push (@$pars,HTML::LCParser->new(\$redo));
688: $$pars[-1]->xml_mode('1');
689: last;
690: }
691: }
692: return $result
693: }
694:
1.90 matthew 695: #########################################
696: #########################################
697:
698: =pod
699:
1.94 harris41 700: =item B<fix_ids_and_indices>
1.90 matthew 701:
702: Currently undocumented
703:
704: =cut
705:
706: #########################################
707: #########################################
1.87 albertel 708: #Arguably this should all be done as a lonnet::ssi instead
1.86 albertel 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:
1.106 albertel 719: my ($needsfixup,$maxid,$maxindex,$duplicateids,@duplicatedids)=
720: &get_max_ids_indices(\$content);
1.86 albertel 721:
1.106 albertel 722: print $logfile ("Got $needsfixup,$maxid,$maxindex,$duplicateids--".
723: join(', ',@duplicatedids));
724: if ($duplicateids) {
725: print $logfile "Duplicate ID(s) exist, ".join(', ',@duplicatedids)."\n";
1.226 albertel 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>';
1.106 albertel 727: return ($outstring,1);
728: }
1.86 albertel 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='';
1.236 www 735: my $responsecounter=1;
1.86 albertel 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: }
1.202 albertel 750: if ($lctag eq 'base') { next; }
1.236 www 751: if (($lctag eq 'part') || ($lctag eq 'problem')) {
752: $responsecounter=0;
753: }
754: if ($lctag=~/response$/) { $responsecounter++; }
1.249 www 755: if ($lctag eq 'import') { $responsecounter++; }
1.86 albertel 756: my %parms=%{$token->[2]};
757: $counter=$addid{$tag};
758: if (!$counter) { $counter=$addid{$lctag}; }
759: if ($counter) {
760: if ($counter eq 'id') {
1.186 albertel 761: unless (defined($parms{'id'}) &&
762: $parms{'id'}!~/^\s*$/) {
1.86 albertel 763: $maxid++;
764: $parms{'id'}=$maxid;
1.205 albertel 765: print $logfile 'ID(new) : '.$tag.':'.$maxid."\n";
766: } else {
767: print $logfile 'ID(kept): '.$tag.':'.$parms{'id'}."\n";
1.86 albertel 768: }
769: } elsif ($counter eq 'index') {
1.186 albertel 770: unless (defined($parms{'index'}) &&
771: $parms{'index'}!~/^\s*$/) {
1.86 albertel 772: $maxindex++;
773: $parms{'index'}=$maxindex;
774: print $logfile 'Index: '.$tag.':'.$maxindex."\n";
775: }
776: }
777: }
1.203 www 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) {
1.292 raeburn 782: next if (($lctag eq 'img') && ($type eq 'src') &&
783: ($parms{$key} =~ m{^data\:image/gif;base64,}));
1.203 www 784: $parms{$key}=&set_allow(\%allow,$logfile,
785: $target,$tag,
1.290 raeburn 786: $parms{$key},$type);
1.203 www 787: }
1.86 albertel 788: }
789: }
790: }
791: # probably a <randomlabel> image type <label>
1.135 albertel 792: # or a <image> tag inside <imageresponse>
793: if (($lctag eq 'label' && defined($parms{'description'}))
794: ||
795: ($lctag eq 'image')) {
1.86 albertel 796: my $next_token=$parser[-1]->get_token();
797: if ($next_token->[0] eq 'T') {
1.218 raeburn 798: $next_token->[1] =~ s/[\n\r\f]+//g;
1.86 albertel 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='';
1.148 albertel 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;
1.86 albertel 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 {
1.148 albertel 829: foreach my $key (keys(%parms)) {
830: if ($key =~ /(archive|code|object)/i) {
831: my $oldurl=$parms{$key};
1.86 albertel 832: my $newurl=&urlfixup($oldurl,$target);
833: $newurl=~s/\/[^\/]+$/\/\*/;
1.148 albertel 834: print $logfile 'Allow: applet '.lc($key).':'.
835: $oldurl.' allows '.$newurl."\n";
1.86 albertel 836: $allow{&absoluteurl($newurl,$target)}=1;
837: }
838: }
839: }
840: }
841: my $newparmstring='';
842: my $endtag='';
1.294 raeburn 843: foreach my $parkey (keys(%parms)) {
844: if ($parkey eq '/') {
1.86 albertel 845: $endtag=' /';
846: } else {
1.294 raeburn 847: my $quote=($parms{$parkey}=~/\"/?"'":'"');
848: $newparmstring.=' '.$parkey.'='.$quote.$parms{$parkey}.$quote;
1.86 albertel 849: }
850: }
851: if (!$endtag) { if ($token->[4]=~m:/>$:) { $endtag=' /'; }; }
852: $outstring.='<'.$tag.$newparmstring.$endtag.'>';
1.286 raeburn 853: if ($lctag eq 'm' || $lctag eq 'answer' || $lctag eq 'display' ||
854: $lctag eq 'tex') {
1.130 albertel 855: $outstring.=&get_all_text_unbalanced('/'.$lctag,\@parser);
1.286 raeburn 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;
1.293 raeburn 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;
1.286 raeburn 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: }
1.293 raeburn 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: }
1.291 raeburn 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: }
1.293 raeburn 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: }
1.290 raeburn 939: $outstring .= $script;
1.286 raeburn 940: }
941: }
1.86 albertel 942: } elsif ($token->[0] eq 'E') {
943: if ($token->[2]) {
944: unless ($token->[1] eq 'allow') {
945: $outstring.='</'.$token->[1].'>';
946: }
1.236 www 947: }
948: if ((($token->[1] eq 'part') || ($token->[1] eq 'problem'))
949: && (!$responsecounter)) {
1.239 bisitz 950: my $outstring='<span class="LC_error">'.&mt('Found [_1] without responses. This resource cannot be published.',$token->[1]).'</span>';
1.236 www 951: return ($outstring,1);
952: }
1.86 albertel 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:
1.106 albertel 968: return ($outstring,0,%allow);
1.86 albertel 969: }
970:
1.89 matthew 971: #########################################
972: #########################################
973:
974: =pod
975:
1.94 harris41 976: =item B<store_metadata>
1.89 matthew 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 {
1.151 www 990: my %metadata = @_;
1.89 matthew 991: my $error;
992: # Determine if the table exists
993: my $status = &Apache::lonmysql::check_table('metadata');
994: if (! defined($status)) {
1.246 bisitz 995: $error='<span class="LC_error">'
996: .&mt('WARNING: Cannot connect to database!')
997: .'</span>';
1.89 matthew 998: &Apache::lonnet::logthis($error);
999: return ($error,undef);
1000: }
1001: if ($status == 0) {
1002: # It would be nice to actually create the table....
1.246 bisitz 1003: $error ='<span class="LC_error">'
1004: .&mt('WARNING: The metadata table does not exist in the LON-CAPA database!')
1005: .'</span>';
1.89 matthew 1006: &Apache::lonnet::logthis($error);
1007: return ($error,undef);
1008: }
1.172 matthew 1009: my $dbh = &Apache::lonmysql::get_dbh();
1.237 www 1010: if (($metadata{'obsolete'}) || ($metadata{'copyright'} eq 'priv')) {
1.172 matthew 1011: # remove this entry
1.228 albertel 1012: my $delitem = 'url = '.$dbh->quote($metadata{'url'});
1013: $status = &LONCAPA::lonmetadata::delete_metadata($dbh,undef,$delitem);
1014:
1.152 www 1015: } else {
1.213 albertel 1016: $status = &LONCAPA::lonmetadata::update_metadata($dbh,undef,undef,
1.172 matthew 1017: \%metadata);
1.152 www 1018: }
1.172 matthew 1019: if (defined($status) && $status ne '') {
1.246 bisitz 1020: $error='<span class="LC_error">'
1.248 raeburn 1021: .&mt('Error occurred saving new values in metadata table in LON-CAPA database!')
1.246 bisitz 1022: .'</span>';
1.89 matthew 1023: &Apache::lonnet::logthis($error);
1.172 matthew 1024: &Apache::lonnet::logthis($status);
1.89 matthew 1025: return ($error,undef);
1026: }
1.213 albertel 1027: return (undef,'success');
1.89 matthew 1028: }
1029:
1.142 www 1030:
1.185 www 1031: # ========================================== Parse file for errors and warnings
1032:
1033: sub checkonthis {
1034: my ($r,$source)=@_;
1.187 www 1035: my $uri=&Apache::lonnet::hreflocation($source);
1036: $uri=~s/\/$//;
1.190 albertel 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);
1.187 www 1041: if (($errorcount) || ($warningcount)) {
1.242 bisitz 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>');
1.185 www 1057: } else {
1.190 albertel 1058: #$r->print('<font color="green">'.&mt('ok').'</font>');
1.185 www 1059: }
1060: $r->rflush();
1.187 www 1061: return ($warningcount,$errorcount);
1.185 www 1062: }
1063:
1.142 www 1064: # ============================================== Parse file itself for metadata
1.144 www 1065: #
1066: # parses a file with target meta, sets global %metadatafields %metadatakeys
1.142 www 1067:
1068: sub parseformeta {
1069: my ($source,$style)=@_;
1.143 www 1070: my $allmeta='';
1.142 www 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);
1.143 www 1077: $allmeta=&Apache::lonnet::ssi_body($source,('grade_target' => 'meta'));
1.142 www 1078: &metaeval($allmeta);
1079: }
1.143 www 1080: return $allmeta;
1.142 www 1081: }
1082:
1.90 matthew 1083: #########################################
1084: #########################################
1085:
1086: =pod
1087:
1.94 harris41 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),
1.90 matthew 1093:
1.113 albertel 1094: Returns a 2 element array, the first is the string to be shown to the
1.248 raeburn 1095: user, the second is an error code, either 1 (an error occurred) or 0
1.113 albertel 1096: (no error occurred)
1097:
1.94 harris41 1098: I<Additional documentation needed.>
1.90 matthew 1099:
1100: =cut
1101:
1102: #########################################
1103: #########################################
1.2 www 1104: sub publish {
1.50 www 1105:
1.296 raeburn 1106: my ($source,$target,$style,$batch,$nokeyref)=@_;
1.2 www 1107: my $logfile;
1.4 www 1108: my $scrout='';
1.23 www 1109: my $allmeta='';
1110: my $content='';
1.36 www 1111: my %allow=();
1.4 www 1112:
1.2 www 1113: unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
1.226 albertel 1114: return ('<span class="LC_error">'.&mt('No write permission to user directory, FAIL').'</span>',1);
1.2 www 1115: }
1116: print $logfile
1.211 albertel 1117: "\n\n================= Publish ".localtime()." Phase One ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
1.2 www 1118:
1.119 www 1119: if (($style eq 'ssi') || ($style eq 'rat') || ($style eq 'prv')) {
1.3 www 1120: # ------------------------------------------------------- This needs processing
1.4 www 1121:
1122: # ----------------------------------------------------------------- Backup Copy
1.3 www 1123: my $copyfile=$source.'.save';
1.13 www 1124: if (copy($source,$copyfile)) {
1.3 www 1125: print $logfile "Copied original file to ".$copyfile."\n";
1126: } else {
1.13 www 1127: print $logfile "Unable to write backup ".$copyfile.':'.$!."\n";
1.239 bisitz 1128: return ("<span class=\"LC_error\">".&mt("Failed to write backup copy, [_1], FAIL",$1)."</span>",1);
1.3 www 1129: }
1.4 www 1130: # ------------------------------------------------------------- IDs and indices
1.86 albertel 1131:
1.106 albertel 1132: my ($outstring,$error);
1133: ($outstring,$error,%allow)=&fix_ids_and_indices($logfile,$source,
1134: $target);
1.113 albertel 1135: if ($error) { return ($outstring,$error); }
1.36 www 1136: # ------------------------------------------------------------ Construct Allows
1.62 www 1137:
1.246 bisitz 1138: my $outdep=''; # Collect dependencies output data
1.62 www 1139: my $allowstr='';
1.232 raeburn 1140: foreach my $thisdep (sort(keys(%allow))) {
1.73 albertel 1141: if ($thisdep !~ /[^\s]/) { next; }
1.231 www 1142: if ($thisdep =~/\$/) {
1.246 bisitz 1143: $outdep.='<div class="LC_warning">'
1.232 raeburn 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>')
1.246 bisitz 1146: ."</div>\n";
1.231 www 1147: }
1.62 www 1148: unless ($style eq 'rat') {
1149: $allowstr.="\n".'<allow src="'.$thisdep.'" />';
1150: }
1.246 bisitz 1151: $outdep.='<div>';
1.231 www 1152: if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) {
1.246 bisitz 1153: $outdep.='<a href="'.$thisdep.'">';
1.44 www 1154: }
1.246 bisitz 1155: $outdep.='<tt>'.$thisdep.'</tt>';
1.231 www 1156: if ($thisdep!~/[\*\$]/ && $thisdep!~m|^/adm/|) {
1.246 bisitz 1157: $outdep.='</a>';
1.59 www 1158: if (
1159: &Apache::lonnet::getfile($Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
1160: $thisdep.'.meta') eq '-1') {
1.246 bisitz 1161: $outdep.= ' - <span class="LC_error">'.&mt('Currently not available').
1.226 albertel 1162: '</span>';
1.59 www 1163: } else {
1.279 www 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: #
1.59 www 1169: my %temphash=(&Apache::lonnet::declutter($target).'___'.
1170: &Apache::lonnet::declutter($thisdep).'___usage'
1171: => time);
1.215 albertel 1172: $thisdep=~m{^/res/($match_domain)/($match_username)/};
1.59 www 1173: if ((defined($1)) && (defined($2))) {
1.92 albertel 1174: &Apache::lonnet::put('nohist_resevaldata',\%temphash,
1175: $1,$2);
1.59 www 1176: }
1177: }
1.44 www 1178: }
1.246 bisitz 1179: $outdep.='</div><br />';
1180: }
1181:
1182: if ($outdep) {
1183: $scrout.='<h3>'.&mt('Dependencies').'</h3>'
1184: .$outdep
1.65 harris41 1185: }
1.175 albertel 1186: $outstring=~s/\n*(\<\/[^\>]+\>[^<]*)$/$allowstr\n$1\n/s;
1.62 www 1187:
1.94 harris41 1188: # ------------------------------------------------------------- Write modified.
1.37 www 1189:
1.4 www 1190: {
1191: my $org;
1192: unless ($org=Apache::File->new('>'.$source)) {
1193: print $logfile "No write permit to $source\n";
1.226 albertel 1194: return ('<span class="LC_error">'.&mt('No write permission to').
1.136 www 1195: ' '.$source.
1.226 albertel 1196: ', '.&mt('FAIL').'</span>',1);
1.4 www 1197: }
1.94 harris41 1198: print($org $outstring);
1.4 www 1199: }
1200: $content=$outstring;
1.34 www 1201:
1.37 www 1202: }
1.94 harris41 1203: # -------------------------------------------- Initial step done, now metadata.
1.7 www 1204:
1.94 harris41 1205: # --------------------------------------- Storage for metadata keys and fields.
1.144 www 1206: # these are globals
1207: #
1.8 www 1208: %metadatafields=();
1209: %metadatakeys=();
1210:
1211: my %oldparmstores=();
1.44 www 1212:
1.97 www 1213: unless ($batch) {
1.254 bisitz 1214: $scrout.='<h3>'.&mt('Metadata').' ' .
1.239 bisitz 1215: &Apache::loncommon::help_open_topic("Metadata_Description")
1.84 bowersj2 1216: . '</h3>';
1.97 www 1217: }
1.7 www 1218:
1219: # ------------------------------------------------ First, check out environment
1.195 www 1220: if ((!(-e $source.'.meta')) || ($env{'form.forceoverride'})) {
1.192 albertel 1221: $metadatafields{'author'}=$env{'environment.firstname'}.' '.
1222: $env{'environment.middlename'}.' '.
1223: $env{'environment.lastname'}.' '.
1224: $env{'environment.generation'};
1.8 www 1225: $metadatafields{'author'}=~s/\s+/ /g;
1226: $metadatafields{'author'}=~s/\s+$//;
1.211 albertel 1227: $metadatafields{'owner'}=$cuname.':'.$cudom;
1.7 www 1228:
1229: # ------------------------------------------------ Check out directory hierachy
1230:
1231: my $thisdisfn=$source;
1232:
1.269 www 1233: $thisdisfn=~s/^\Q$docroot\E\/priv\/\Q$cudom\E\/\Q$cuname\E\///;
1234: my @urlparts=('.',split(/\//,$thisdisfn));
1.7 www 1235: $#urlparts--;
1236:
1.269 www 1237: my $currentpath=$docroot.'/priv/'.$cudom.'/'.$cuname.'/';
1.7 www 1238:
1.140 albertel 1239: my $prefix='../'x($#urlparts);
1.269 www 1240: foreach my $subdir (@urlparts) {
1241: $currentpath.=$subdir.'/';
1.140 albertel 1242: $scrout.=&metaread($logfile,$currentpath.'default.meta',$prefix);
1243: $prefix=~s|^\.\./||;
1.65 harris41 1244: }
1.185 www 1245:
1.149 www 1246: # ----------------------------------------------------------- Parse file itself
1247: # read %metadatafields from file itself
1248:
1249: $allmeta=&parseformeta($source,$style);
1.7 www 1250:
1251: # ------------------- Clear out parameters and stores (there should not be any)
1252:
1.294 raeburn 1253: foreach my $field (keys(%metadatafields)) {
1254: if (($field=~/^parameter/) || ($field=~/^stores/)) {
1255: delete $metadatafields{$field};
1.7 www 1256: }
1.65 harris41 1257: }
1.7 www 1258:
1.8 www 1259: } else {
1.7 www 1260: # ---------------------- Read previous metafile, remember parameters and stores
1261:
1262: $scrout.=&metaread($logfile,$source.'.meta');
1263:
1.294 raeburn 1264: foreach my $field (keys(%metadatafields)) {
1265: if (($field=~/^parameter/) || ($field=~/^stores/)) {
1266: $oldparmstores{$field}=1;
1267: delete $metadatafields{$field};
1.7 www 1268: }
1.65 harris41 1269: }
1.195 www 1270: # ------------------------------------------------------------- Save some stuff
1271: my %savemeta=();
1.294 raeburn 1272: if ($metadatafields{'title'}) { $savemeta{'title'}=$metadatafields{'title'}; }
1.161 albertel 1273: # ------------------------------------------ See if anything new in file itself
1274:
1275: $allmeta=&parseformeta($source,$style);
1.195 www 1276: # ----------------------------------------------------------- Restore the stuff
1.294 raeburn 1277: foreach my $item (keys(%savemeta)) {
1278: $metadatafields{$item}=$savemeta{$item};
1.195 www 1279: }
1.144 www 1280: }
1.7 www 1281:
1.144 www 1282:
1.7 www 1283: # ---------------- Find and document discrepancies in the parameters and stores
1284:
1.116 albertel 1285: my $chparms='';
1.294 raeburn 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;
1.219 albertel 1291: $disp_key =~ tr/\0/_/;
1292: print $logfile ('New: '.$disp_key."\n");
1293: $chparms .= $disp_key.' ';
1.116 albertel 1294: }
1295: }
1296: }
1297: }
1298: if ($chparms) {
1.224 albertel 1299: $scrout.='<p><b>'.&mt('New parameters or saved values').
1.136 www 1300: ':</b> '.$chparms.'</p>';
1.116 albertel 1301: }
1.7 www 1302:
1.116 albertel 1303: $chparms='';
1.294 raeburn 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;
1.219 albertel 1309: $disp_key =~ tr/\0/_/;
1310: print $logfile ('Obsolete: '.$disp_key."\n");
1311: $chparms.=$disp_key.' ';
1.116 albertel 1312: }
1313: }
1314: }
1315: if ($chparms) {
1.258 bisitz 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 />';
1.116 albertel 1321: }
1.229 www 1322: if ($metadatafields{'copyright'} eq 'priv') {
1.258 bisitz 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 />';
1.229 www 1326: }
1.37 www 1327:
1.8 www 1328: # ------------------------------------------------------- Now have all metadata
1.5 www 1329:
1.116 albertel 1330: my %keywords=();
1.97 www 1331:
1.116 albertel 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;
1.245 onken 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
1.296 raeburn 1346: unless ($nokeyref->{$_}) {
1.245 onken 1347: $keywords{$_}=1;
1348: }
1349: }
1350:
1351:
1.116 albertel 1352: }
1.97 www 1353:
1.168 www 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: }
1.116 albertel 1361: }
1.97 www 1362: # --------------------------------------------------- Now we also have keywords
1363: # =============================================================================
1.167 albertel 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
1.192 albertel 1367: # $env{'from.'..} so that it can be used by the phase two handler in
1.167 albertel 1368: # batch mode
1369:
1.239 bisitz 1370: my $intr_scrout.='<br />'
1.238 bisitz 1371: .'<form name="pubform" action="/adm/publish" method="post">';
1372: unless ($env{'form.makeobsolete'}) {
1.246 bisitz 1373: $intr_scrout.='<p class="LC_warning">'
1.239 bisitz 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="'
1.238 bisitz 1377: .&mt('Finalize Publication')
1.271 www 1378: .'" /> <a href="'.&Apache::loncfile::url($source).'">'.&mt('Cancel').'</a></p>';
1.238 bisitz 1379: }
1.239 bisitz 1380: $intr_scrout.=&Apache::lonhtmlcommon::start_pick_box();
1.238 bisitz 1381: $intr_scrout.=
1.167 albertel 1382: &hiddenfield('phase','two').
1.192 albertel 1383: &hiddenfield('filename',$env{'form.filename'}).
1.209 www 1384: &hiddenfield('allmeta',&escape($allmeta)).
1.294 raeburn 1385: &hiddenfield('dependencies',join(',',keys(%allow)));
1.194 www 1386: unless ($env{'form.makeobsolete'}) {
1387: $intr_scrout.=
1.167 albertel 1388: &textfield('Title','title',$metadatafields{'title'}).
1389: &textfield('Author(s)','author',$metadatafields{'author'}).
1390: &textfield('Subject','subject',$metadatafields{'subject'});
1.194 www 1391: # --------------------------------------------------- Scan content for keywords
1.7 www 1392:
1.238 bisitz 1393: my $keywords_help = &Apache::loncommon::help_open_topic("Publishing_Keywords");
1.167 albertel 1394: my $keywordout=<<"END";
1.77 matthew 1395: <script>
1.116 albertel 1396: function checkAll(field) {
1.77 matthew 1397: for (i = 0; i < field.length; i++)
1398: field[i].checked = true ;
1399: }
1400:
1.116 albertel 1401: function uncheckAll(field) {
1.77 matthew 1402: for (i = 0; i < field.length; i++)
1403: field[i].checked = false ;
1404: }
1405: </script>
1.117 albertel 1406: END
1.238 bisitz 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 />'
1.247 raeburn 1412: .&Apache::loncommon::start_data_table();
1413: my $cols_per_row = 10;
1.167 albertel 1414: my $colcount=0;
1.247 raeburn 1415: my $wordcount=0;
1416: my $numkeywords = scalar(keys(%keywords));
1.116 albertel 1417:
1.247 raeburn 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/) {
1.253 bisitz 1433: $keywordout.=' checked="checked"';
1.247 raeburn 1434: $env{'form.keywords'}.=$word.',';
1435: }
1436: } elsif (&Apache::loncommon::keyword($word)) {
1.253 bisitz 1437: $keywordout.=' checked="checked"';
1.247 raeburn 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;
1.243 bisitz 1444: }
1445: }
1.247 raeburn 1446: if ($colcount > 0) {
1447: $keywordout .= &Apache::loncommon::end_data_table_row();
1448: }
1.243 bisitz 1449:
1.192 albertel 1450: $env{'form.keywords'}=~s/\,$//;
1.116 albertel 1451:
1.241 raeburn 1452: $keywordout.=&Apache::loncommon::end_data_table_row()
1453: .&Apache::loncommon::end_data_table()
1.238 bisitz 1454: .&Apache::lonhtmlcommon::row_closure();
1.51 www 1455:
1.167 albertel 1456: $intr_scrout.=$keywordout;
1.9 www 1457:
1.167 albertel 1458: $intr_scrout.=&textfield('Additional Keywords','addkey','');
1.12 www 1459:
1.167 albertel 1460: $intr_scrout.=&textfield('Notes','notes',$metadatafields{'notes'});
1.9 www 1461:
1.238 bisitz 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();
1.9 www 1467:
1.167 albertel 1468: $source=~/\.(\w+)$/;
1.150 www 1469:
1.238 bisitz 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();
1.150 www 1478:
1.238 bisitz 1479: $intr_scrout.=&textfield('Standards','standards',$metadatafields{'standards'});
1.11 www 1480:
1.167 albertel 1481: $intr_scrout.=&hiddenfield('mime',$1);
1.11 www 1482:
1.167 albertel 1483: my $defaultlanguage=$metadatafields{'language'};
1484: $defaultlanguage =~ s/\s*notset\s*//g;
1485: $defaultlanguage =~ s/^,\s*//g;
1486: $defaultlanguage =~ s/,\s*$//g;
1.123 albertel 1487:
1.167 albertel 1488: $intr_scrout.=&selectbox('Language','language',
1489: $defaultlanguage,
1490: \&Apache::loncommon::languagedescription,
1491: (&Apache::loncommon::languageids),
1492: );
1.11 www 1493:
1.167 albertel 1494: unless ($metadatafields{'creationdate'}) {
1495: $metadatafields{'creationdate'}=time;
1496: }
1497: $intr_scrout.=&hiddenfield('creationdate',
1498: &Apache::lonmysql::unsqltime($metadatafields{'creationdate'}));
1.116 albertel 1499:
1.167 albertel 1500: $intr_scrout.=&hiddenfield('lastrevisiondate',time);
1.11 www 1501:
1.240 raeburn 1502: my $pubowner_last;
1503: if ($style eq 'prv') {
1504: $pubowner_last = 1;
1505: }
1.167 albertel 1506: $intr_scrout.=&textfield('Publisher/Owner','owner',
1.240 raeburn 1507: $metadatafields{'owner'},$pubowner_last);
1.84 bowersj2 1508:
1.173 www 1509: # ---------------------------------------------- Retrofix for unused copyright
1510: if ($metadatafields{'copyright'} eq 'free') {
1511: $metadatafields{'copyright'}='default';
1512: $metadatafields{'sourceavail'}='open';
1513: }
1.229 www 1514: if ($metadatafields{'copyright'} eq 'priv') {
1515: $metadatafields{'copyright'}='domain';
1516: }
1.174 www 1517: # ------------------------------------------------ Dial in reasonable defaults
1.167 albertel 1518: my $defaultoption=$metadatafields{'copyright'};
1519: unless ($defaultoption) { $defaultoption='default'; }
1.174 www 1520: my $defaultsourceoption=$metadatafields{'sourceavail'};
1521: unless ($defaultsourceoption) { $defaultsourceoption='closed'; }
1.167 albertel 1522: unless ($style eq 'prv') {
1.174 www 1523: # -------------------------------------------------- Correct copyright for rat.
1.167 albertel 1524: if ($style eq 'rat') {
1.174 www 1525: # -------------------------------------- Retrofix for non-applicable copyright
1.167 albertel 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,
1.229 www 1533: (grep !/^(public|priv)$/,(&Apache::loncommon::copyrightids)));
1.116 albertel 1534: } else {
1.174 www 1535: $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
1536: $defaultoption,
1537: \&Apache::loncommon::copyrightdescription,
1.229 www 1538: (grep !/^priv$/,(&Apache::loncommon::copyrightids)));
1.65 harris41 1539: }
1.174 www 1540: my $copyright_help =
1.238 bisitz 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');
1.174 www 1546: $intr_scrout.=&selectbox('Source Distribution','sourceavail',
1547: $defaultsourceoption,
1548: \&Apache::loncommon::source_copyrightdescription,
1549: (&Apache::loncommon::source_copyrightids));
1.198 www 1550: # $intr_scrout.=&text_with_browse_field('Source Custom Distribution File','sourcerights',$metadatafields{'sourcerights'},'rights');
1.174 www 1551: my $uctitle=&mt('Obsolete');
1.257 bisitz 1552: my $obsolete_checked=($metadatafields{'obsolete'})?' checked="checked"':'';
1.238 bisitz 1553: $intr_scrout.="\n".&Apache::lonhtmlcommon::row_title($uctitle)
1.256 bisitz 1554: .'<input type="checkbox" name="obsolete"'.$obsolete_checked.' />'
1.238 bisitz 1555: .&Apache::lonhtmlcommon::row_closure(1);
1556: $intr_scrout.=&text_with_browse_field('Suggested Replacement for Obsolete File',
1.180 albertel 1557: 'obsoletereplacement',
1.240 raeburn 1558: $metadatafields{'obsoletereplacement'},'',1);
1.174 www 1559: } else {
1560: $intr_scrout.=&hiddenfield('copyright','private');
1561: }
1.194 www 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'}).
1.195 www 1581: &hiddenfield('obsolete',1).
1.194 www 1582: &text_with_browse_field('Suggested Replacement for Obsolete File',
1583: 'obsoletereplacement',
1.240 raeburn 1584: $metadatafields{'obsoletereplacement'},'',1);
1.194 www 1585: }
1.167 albertel 1586: if (!$batch) {
1.238 bisitz 1587: $scrout.=$intr_scrout
1.239 bisitz 1588: .&Apache::lonhtmlcommon::end_pick_box()
1589: .'<p><input type="submit" value="'
1.238 bisitz 1590: .&mt($env{'form.makeobsolete'}?'Make Obsolete':'Finalize Publication')
1.239 bisitz 1591: .'" /></p>'
1592: .'</form>';
1.97 www 1593: }
1.167 albertel 1594: return($scrout,0);
1.2 www 1595: }
1.1 www 1596:
1.296 raeburn 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:
1.90 matthew 1609: #########################################
1610: #########################################
1611:
1612: =pod
1613:
1.94 harris41 1614: =item B<phasetwo>
1.90 matthew 1615:
1616: Render second interface showing status of publication steps.
1617: This is publication step two.
1618:
1.94 harris41 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:
1.296 raeburn 1631: =item I<$batch>
1632:
1633: =item I<$usebuffer>
1634:
1.94 harris41 1635: =back
1636:
1637: Returns:
1638:
1639: =over 4
1640:
1.296 raeburn 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:
1.94 harris41 1646:
1.197 www 1647: 0: fail
1648: 1: success
1.94 harris41 1649:
1.288 raeburn 1650: =back
1651:
1.90 matthew 1652: =cut
1.12 www 1653:
1.100 matthew 1654: #'stupid emacs
1.90 matthew 1655: #########################################
1656: #########################################
1.11 www 1657: sub phasetwo {
1658:
1.296 raeburn 1659: my ($r,$source,$target,$style,$distarget,$batch,$usebuffer)=@_;
1.102 www 1660: $source=~s/\/+/\//g;
1661: $target=~s/\/+/\//g;
1.196 www 1662: #
1663: # Unless trying to get rid of something, check name validity
1664: #
1.296 raeburn 1665: my $output;
1.196 www 1666: unless ($env{'form.obsolete'}) {
1667: if ($target=~/(\_\_\_|\&\&\&|\:\:\:)/) {
1.296 raeburn 1668: $output = '<span class="LC_error">'.
1.226 albertel 1669: &mt('Unsupported character combination [_1] in filename, FAIL.',"<tt>'.$1.'</tt>").
1.296 raeburn 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: }
1.196 www 1681: }
1682: unless ($target=~/\.(\w+)$/) {
1.296 raeburn 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: }
1.196 www 1694: }
1695: if ($target=~/\.(\d+)\.(\w+)$/) {
1.296 raeburn 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: }
1.196 www 1707: }
1708: }
1.109 www 1709:
1.196 www 1710: #
1711: # End name check
1712: #
1.102 www 1713: $distarget=~s/\/+/\//g;
1.11 www 1714: my $logfile;
1715: unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
1.296 raeburn 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: }
1.11 www 1727: }
1.227 albertel 1728:
1729: if ($source =~ /\.rights$/) {
1.296 raeburn 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: }
1.227 albertel 1735: }
1736:
1.11 www 1737: print $logfile
1.211 albertel 1738: "\n================= Publish ".localtime()." Phase Two ================\n".$env{'user.name'}.':'.$env{'user.domain'}."\n";
1.100 matthew 1739:
1740: %metadatafields=();
1741: %metadatakeys=();
1.167 albertel 1742:
1.209 www 1743: &metaeval(&unescape($env{'form.allmeta'}));
1.295 raeburn 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:
1.192 albertel 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'};
1.115 www 1785: $metadatafields{'customdistributionfile'}=
1.192 albertel 1786: $env{'form.customdistributionfile'};
1787: $metadatafields{'sourceavail'}=$env{'form.sourceavail'};
1788: $metadatafields{'obsolete'}=$env{'form.obsolete'};
1.138 www 1789: $metadatafields{'obsoletereplacement'}=
1.192 albertel 1790: $env{'form.obsoletereplacement'};
1791: $metadatafields{'dependencies'}=$env{'form.dependencies'};
1.211 albertel 1792: $metadatafields{'modifyinguser'}=$env{'user.name'}.':'.
1.192 albertel 1793: $env{'user.domain'};
1.211 albertel 1794: $metadatafields{'authorspace'}=$cuname.':'.$cudom;
1.214 albertel 1795: $metadatafields{'domain'}=$cudom;
1.100 matthew 1796:
1.192 albertel 1797: my $allkeywords=$env{'form.addkey'};
1798: if (exists($env{'form.keywords'})) {
1799: if (ref($env{'form.keywords'})) {
1800: $allkeywords .= ','.join(',',@{$env{'form.keywords'}});
1.100 matthew 1801: } else {
1.192 albertel 1802: $allkeywords .= ','.$env{'form.keywords'};
1.100 matthew 1803: }
1804: }
1.168 www 1805: $allkeywords=~s/[\"\']//g;
1.170 www 1806: $allkeywords=~s/\s*[\;\,]\s*/\,/g;
1.168 www 1807: $allkeywords=~s/\s+/ /g;
1808: $allkeywords=~s/^[ \,]//;
1809: $allkeywords=~s/[ \,]$//;
1.100 matthew 1810: $metadatafields{'keywords'}=$allkeywords;
1811:
1.149 www 1812: # check if custom distribution file is specified
1813: if ($metadatafields{'copyright'} eq 'custom') {
1814: my $file=$metadatafields{'customdistributionfile'};
1815: unless ($file=~/\.rights$/) {
1.296 raeburn 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: }
1.149 www 1828: }
1829: }
1.100 matthew 1830: {
1831: print $logfile "\nWrite metadata file for ".$source;
1832: my $mfh;
1833: unless ($mfh=Apache::File->new('>'.$source.'.meta')) {
1.296 raeburn 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: }
1.100 matthew 1846: }
1.294 raeburn 1847: foreach my $field (sort(keys(%metadatafields))) {
1848: unless ($field=~/\./) {
1849: my $unikey=$field;
1.100 matthew 1850: $unikey=~/^([A-Za-z]+)/;
1851: my $tag=$1;
1852: $tag=~tr/A-Z/a-z/;
1853: print $mfh "\n\<$tag";
1.294 raeburn 1854: foreach my $item (split(/\,/,$metadatakeys{$unikey})) {
1855: my $value=$metadatafields{$unikey.'.'.$item};
1.100 matthew 1856: $value=~s/\"/\'\'/g;
1.294 raeburn 1857: print $mfh ' '.$item.'="'.$value.'"';
1.100 matthew 1858: }
1859: print $mfh '>'.
1.165 albertel 1860: &HTML::Entities::encode($metadatafields{$unikey},'<>&"')
1.100 matthew 1861: .'</'.$tag.'>';
1862: }
1863: }
1.296 raeburn 1864:
1865: $output .= '<p>'.&mt('Wrote Metadata').'</p>';
1866: unless ($usebuffer) {
1867: $r->print($output);
1868: $output = '';
1869: }
1.100 matthew 1870: print $logfile "\nWrote metadata";
1871: }
1872:
1873: # -------------------------------- Synchronize entry with SQL metadata database
1.12 www 1874:
1.89 matthew 1875: $metadatafields{'url'} = $distarget;
1876: $metadatafields{'version'} = 'current';
1.152 www 1877:
1.297 ! raeburn 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: }
1.24 harris41 1885: }
1.297 ! raeburn 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: }
1.296 raeburn 1899: }
1.159 www 1900: # --------------------------------------------- Delete author resource messages
1901: my $delresult=&Apache::lonmsg::del_url_author_res_msg($target);
1.296 raeburn 1902: $output .= '<p>'.&mt('Removing error messages:').' '.$delresult.'</p>';
1903: unless ($usebuffer) {
1904: $r->print($output);
1905: $output = '';
1906: }
1.159 www 1907: print $logfile "\nRemoving error messages: $delresult";
1.12 www 1908: # ----------------------------------------------------------- Copy old versions
1909:
1.100 matthew 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;
1.261 raeburn 1917: my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1918: unless ($srcd=~/^\Q$docroot\E\/res/) {
1.100 matthew 1919: print $logfile "\nPANIC: Target dir is ".$srcd;
1.296 raeburn 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: }
1.100 matthew 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 {
1.252 raeburn 1939: if ($filename=~/^\Q$srcf\E\.(\d+)\.\Q$srct\E$/) {
1.100 matthew 1940: $maxversion=($1>$maxversion)?$1:$maxversion;
1941: }
1942: }
1943: }
1944: closedir(DIR);
1945: $maxversion++;
1.296 raeburn 1946: $output .= '<p>'.&mt('Creating old version [_1]',$maxversion).'</p>';
1947: unless ($usebuffer) {
1948: $r->print($output);
1949: $output = '';
1950: }
1.125 www 1951: print $logfile "\nCreating old version ".$maxversion."\n";
1.100 matthew 1952:
1953: my $copyfile=$srcd.'/'.$srcf.'.'.$maxversion.'.'.$srct;
1954:
1.13 www 1955: if (copy($target,$copyfile)) {
1.12 www 1956: print $logfile "Copied old target to ".$copyfile."\n";
1.296 raeburn 1957: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied old target file'));
1958: unless ($usebuffer) {
1959: $r->print($output);
1960: $output = '';
1961: }
1.12 www 1962: } else {
1.13 www 1963: print $logfile "Unable to write ".$copyfile.':'.$!."\n";
1.296 raeburn 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: }
1.12 www 1975: }
1.100 matthew 1976:
1.12 www 1977: # --------------------------------------------------------------- Copy Metadata
1978:
1979: $copyfile=$copyfile.'.meta';
1.100 matthew 1980:
1.13 www 1981: if (copy($target.'.meta',$copyfile)) {
1.14 www 1982: print $logfile "Copied old target metadata to ".$copyfile."\n";
1.296 raeburn 1983: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied old metadata'));
1984: unless ($usebuffer) {
1985: $r->print($output);
1986: $output = '';
1987: }
1.12 www 1988: } else {
1.13 www 1989: print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
1.14 www 1990: if (-e $target.'.meta') {
1.296 raeburn 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: }
1.14 www 2003: }
1.12 www 2004: }
1.100 matthew 2005: } else {
1.296 raeburn 2006: $output .= '<p>'.&mt('Initial version').'</p>';
2007: unless ($usebuffer) {
2008: $r->print($output);
2009: $output = '';
2010: }
1.100 matthew 2011: print $logfile "\nInitial version";
2012: }
1.12 www 2013:
2014: # ---------------------------------------------------------------- Write Source
1.100 matthew 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);
1.296 raeburn 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: }
1.12 www 2034: }
1.100 matthew 2035: }
2036:
2037: if (copy($source,$copyfile)) {
2038: print $logfile "\nCopied original source to ".$copyfile."\n";
1.296 raeburn 2039: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied source file'));
2040: unless ($usebuffer) {
2041: $r->print($output);
2042: $output = '';
2043: }
1.100 matthew 2044: } else {
2045: print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";
1.296 raeburn 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: }
1.100 matthew 2058: }
2059:
1.265 www 2060: # ---------------------------------------------- Delete local tmp-preview files
2061: unlink($copyfile.'.tmp');
1.12 www 2062: # --------------------------------------------------------------- Copy Metadata
2063:
1.100 matthew 2064: $copyfile=$copyfile.'.meta';
2065:
2066: if (copy($source.'.meta',$copyfile)) {
2067: print $logfile "\nCopied original metadata to ".$copyfile."\n";
1.296 raeburn 2068: $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied metadata'));
2069: unless ($usebuffer) {
2070: $r->print($output);
2071: $output = '';
2072: }
1.100 matthew 2073: } else {
2074: print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n";
1.296 raeburn 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;
1.100 matthew 2090: }
1.12 www 2091:
1.181 www 2092: # ------------------------------------------------------------- Trigger updates
1.183 www 2093: push(@{$modified_urls},[$target,$source]);
1.182 www 2094: unless ($registered_cleanup) {
1.263 raeburn 2095: my $handlers = $r->get_handlers('PerlCleanupHandler');
2096: $r->set_handlers('PerlCleanupHandler' => [\¬ify,@{$handlers}]);
1.182 www 2097: $registered_cleanup=1;
2098: }
1.199 www 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:
1.255 bisitz 2107: # ------------------------------------------------------------- Everything done
2108: $logfile->close();
1.296 raeburn 2109: $output .= '<p class="LC_success">'.&mt('Done').'</p>';
2110: unless ($usebuffer) {
2111: $r->print($output);
2112: $output = '';
2113: }
1.255 bisitz 2114:
1.12 www 2115: # ------------------------------------------------ Provide link to new resource
1.100 matthew 2116: unless ($batch) {
2117:
1.271 www 2118: my $thissrc=&Apache::loncfile::url($source);
1.100 matthew 2119: my $thissrcdir=$thissrc;
2120: $thissrcdir=~s/\/[^\/]+$/\//;
2121:
1.296 raeburn 2122: $output .=
1.284 bisitz 2123: &Apache::lonhtmlcommon::actionbox([
1.264 raeburn 2124: '<a href="'.$thisdistarget.'">'.
2125: &mt('View Published Version').
1.284 bisitz 2126: '</a>',
1.264 raeburn 2127: '<a href="'.$thissrc.'">'.
2128: &mt('Back to Source').
1.284 bisitz 2129: '</a>',
1.264 raeburn 2130: '<a href="'.$thissrcdir.'">'.
2131: &mt('Back to Source Directory').
1.296 raeburn 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: }
1.100 matthew 2151: }
1.11 www 2152: }
2153:
1.181 www 2154: # =============================================================== Notifications
2155: sub notify {
2156: # --------------------------------------------------- Send update notifications
1.183 www 2157: foreach my $targetsource (@{$modified_urls}){
1.182 www 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: }
1.181 www 2167: # ---------------------------------------- Send update notifications, meta only
1.182 www 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: }
1.181 www 2175: # --------------------------------------------------- Notify subscribed courses
1.182 www 2176: my %courses=&coursedependencies($target);
2177: my $now=time;
1.294 raeburn 2178: foreach my $course (keys(%courses)) {
2179: print $logfile "\nNotifying course ".$course.':';
2180: my ($cdom,$cname)=split(/\_/,$course);
1.182 www 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();
1.181 www 2187: }
1.233 www 2188: if ($lock) { &Apache::lonnet::remove_lock($lock); }
1.182 www 2189: return OK;
1.181 www 2190: }
2191:
1.95 www 2192: #########################################
2193:
2194: sub batchpublish {
1.296 raeburn 2195: my ($r,$srcfile,$targetfile,$nokeyref,$usebuffer)=@_;
1.192 albertel 2196: #publication pollutes %env with form.* values
2197: my %oldenv=%env;
1.102 www 2198: $srcfile=~s/\/+/\//g;
2199: $targetfile=~s/\/+/\//g;
1.96 www 2200:
1.97 www 2201: my $docroot=$r->dir_config('lonDocRoot');
2202: my $thisdistarget=$targetfile;
1.122 albertel 2203: $thisdistarget=~s/^\Q$docroot\E//;
1.97 www 2204:
1.96 www 2205:
1.139 albertel 2206: %metadatafields=();
2207: %metadatakeys=();
2208: $srcfile=~/\.(\w+)$/;
2209: my $thistype=$1;
1.97 www 2210:
2211:
1.139 albertel 2212: my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
1.96 www 2213:
1.296 raeburn 2214: my $output = '<h2>'
1.271 www 2215: .&mt('Publishing [_1]',&Apache::loncfile::display($srcfile))
1.296 raeburn 2216: .'</h2>';
2217: unless ($usebuffer) {
2218: $r->print($output);
2219: $output = '';
2220: }
1.97 www 2221:
2222: # phase one takes
2223: # my ($source,$target,$style,$batch)=@_;
1.296 raeburn 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: }
1.96 www 2231: # phase two takes
2232: # my ($source,$target,$style,$distarget,batch)=@_;
1.192 albertel 2233: # $env{'form.allmeta'},$env{'form.title'},$env{'form.author'},...
1.113 albertel 2234: if (!$error) {
1.296 raeburn 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: }
1.113 albertel 2241: }
1.192 albertel 2242: %env=%oldenv;
1.296 raeburn 2243: if ($usebuffer) {
2244: return $output;
2245: } else {
2246: return '';
2247: }
1.95 www 2248: }
1.1 www 2249:
1.90 matthew 2250: #########################################
1.95 www 2251:
2252: sub publishdirectory {
1.296 raeburn 2253: my ($r,$fn,$thisdisfn,$nokeyref)=@_;
1.102 www 2254: $fn=~s/\/+/\//g;
2255: $thisdisfn=~s/\/+/\//g;
1.273 www 2256: my $thisdisresdir=$thisdisfn;
2257: $thisdisresdir=~s/^\/priv\//\/res\//;
1.276 raeburn 2258: my $resdir = $r->dir_config('lonDocRoot').$thisdisresdir;
1.289 bisitz 2259: $r->print('<form name="pubdirpref" method="post" action="">'
2260: .&Apache::lonhtmlcommon::start_pick_box()
1.258 bisitz 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'))
1.273 www 2265: .'<span class="LC_filename">'.$thisdisresdir.'</span>'
1.258 bisitz 2266: );
1.139 albertel 2267:
2268: my $dirptr=16384; # Mask indicating a directory in stat.cmode.
1.193 www 2269: unless ($env{'form.phase'} eq 'two') {
2270: # ask user what they want
1.258 bisitz 2271: $r->print(&Apache::lonhtmlcommon::row_closure()
2272: .&Apache::lonhtmlcommon::row_title(&mt('Options'))
2273: );
1.289 bisitz 2274: $r->print(&hiddenfield('phase','two').
1.193 www 2275: &hiddenfield('filename',$env{'form.filename'}).
2276: &checkbox('pubrec','include subdirectories').
1.194 www 2277: &checkbox('forcerepub','force republication of previously published files').
1.206 albertel 2278: &checkbox('obsolete','make file(s) obsolete').
1.295 raeburn 2279: &checkbox('forceoverride','force directory level metadata over existing').
2280: &common_access('dist',&mt('apply common copyright/distribution'),
2281: ['default','domain','custom']).
2282: &common_access('source',&mt('apply common source availability'),
2283: ['closed','open'])
1.289 bisitz 2284: );
1.258 bisitz 2285: $r->print(&Apache::lonhtmlcommon::row_closure(1)
2286: .&Apache::lonhtmlcommon::end_pick_box()
1.289 bisitz 2287: .'<br /><input type="submit" value="'.&mt('Publish Directory').'" /></form>'
1.258 bisitz 2288: );
1.233 www 2289: $lock=0;
1.193 www 2290: } else {
1.258 bisitz 2291: $r->print(&Apache::lonhtmlcommon::row_closure(1)
2292: .&Apache::lonhtmlcommon::end_pick_box()
2293: );
1.234 www 2294: unless ($lock) { $lock=&Apache::lonnet::set_lock(&mt('Publishing [_1]',$fn)); }
1.193 www 2295: # actually publish things
2296: opendir(DIR,$fn);
2297: my @files=sort(readdir(DIR));
2298: foreach my $filename (@files) {
2299: my ($cdev,$cino,$cmode,$cnlink,
2300: $cuid,$cgid,$crdev,$csize,
2301: $catime,$cmtime,$cctime,
2302: $cblksize,$cblocks)=stat($fn.'/'.$filename);
2303:
2304: my $extension='';
2305: if ($filename=~/\.(\w+)$/) { $extension=$1; }
2306: if ($cmode&$dirptr) {
2307: if (($filename!~/^\./) && ($env{'form.pubrec'})) {
1.296 raeburn 2308: &publishdirectory($r,$fn.'/'.$filename,$thisdisfn.'/'.$filename,$nokeyref);
1.193 www 2309: }
2310: } elsif ((&Apache::loncommon::fileembstyle($extension) ne 'hdn') &&
2311: ($filename!~/^[\#\.]/) && ($filename!~/\~$/)) {
1.96 www 2312: # find out publication status and/or exiting metadata
1.193 www 2313: my $publishthis=0;
2314: if (-e $resdir.'/'.$filename) {
2315: my ($rdev,$rino,$rmode,$rnlink,
2316: $ruid,$rgid,$rrdev,$rsize,
2317: $ratime,$rmtime,$rctime,
2318: $rblksize,$rblocks)=stat($resdir.'/'.$filename);
2319: if (($rmtime<$cmtime) || ($env{'form.forcerepub'})) {
1.96 www 2320: # previously published, modified now
1.193 www 2321: $publishthis=1;
2322: }
1.212 albertel 2323: my $meta_cmtime = (stat($fn.'/'.$filename.'.meta'))[9];
2324: my $meta_rmtime = (stat($resdir.'/'.$filename.'.meta'))[9];
2325: if ( $meta_rmtime<$meta_cmtime ) {
2326: $publishthis=1;
2327: }
1.193 www 2328: } else {
2329: # never published
1.96 www 2330: $publishthis=1;
1.193 www 2331: }
1.212 albertel 2332:
1.193 www 2333: if ($publishthis) {
1.296 raeburn 2334: &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename,$nokeyref);
1.193 www 2335: } else {
2336: $r->print('<br />'.&mt('Skipping').' '.$filename.'<br />');
2337: }
2338: $r->rflush();
1.139 albertel 2339: }
2340: }
1.193 www 2341: closedir(DIR);
1.139 albertel 2342: }
1.95 www 2343: }
1.160 www 2344:
2345: #########################################
2346: # publish a default.meta file
2347:
2348: sub defaultmetapublish {
2349: my ($r,$fn,$cuname,$cudom)=@_;
2350: unless (-e $fn) {
2351: return HTTP_NOT_FOUND;
2352: }
2353: my $target=$fn;
1.270 www 2354: $target=~s/^\Q$Apache::lonnet::perlvar{'lonDocRoot'}\E\/priv\//\Q$Apache::lonnet::perlvar{'lonDocRoot'}\E\/res\//;
1.160 www 2355:
2356:
2357: &Apache::loncommon::content_type($r,'text/html');
2358: $r->send_http_header;
2359:
1.251 schafran 2360: $r->print(&Apache::loncommon::start_page('Metadata Publication'));
1.160 www 2361:
2362: # ---------------------------------------------------------------- Write Source
2363: my $copyfile=$target;
2364:
2365: my @parts=split(/\//,$copyfile);
2366: my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";
2367:
2368: my $count;
2369: for ($count=5;$count<$#parts;$count++) {
2370: $path.="/$parts[$count]";
2371: if ((-e $path)!=1) {
2372: mkdir($path,0777);
1.255 bisitz 2373: $r->print('<p>'
2374: .&mt('Created directory [_1]'
2375: ,'<span class="LC_filename">'.$parts[$count].'</span>')
2376: .'</p>'
2377: );
1.160 www 2378: }
2379: }
2380:
2381: if (copy($fn,$copyfile)) {
2382: $r->print('<p>'.&mt('Copied source file').'</p>');
2383: } else {
1.226 albertel 2384: return "<span class=\"LC_error\">".
2385: &mt('Failed to copy source').", $!, ".&mt('FAIL')."</span>";
1.160 www 2386: }
2387:
2388: # --------------------------------------------------- Send update notifications
2389:
2390: my @subscribed=&get_subscribed_hosts($target);
2391: foreach my $subhost (@subscribed) {
2392: $r->print('<p>'.&mt('Notifying host').' '.$subhost.':');$r->rflush;
2393: my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
2394: $r->print($reply.'</p><br />');$r->rflush;
2395: }
2396: # ------------------------------------------------------------------- Link back
1.281 raeburn 2397: $r->print("<a href='".&Apache::loncfile::display($fn)."'>".&mt('Back to Metadata').'</a>');
1.208 albertel 2398: $r->print(&Apache::loncommon::end_page());
1.160 www 2399: return OK;
2400: }
1.90 matthew 2401: #########################################
2402:
2403: =pod
2404:
1.94 harris41 2405: =item B<handler>
1.90 matthew 2406:
2407: A basic outline of the handler subroutine follows.
2408:
2409: =over 4
2410:
1.94 harris41 2411: =item *
2412:
2413: Get query string for limited number of parameters.
2414:
2415: =item *
2416:
2417: Check filename.
2418:
2419: =item *
2420:
2421: File is there and owned, init lookup tables.
2422:
2423: =item *
1.90 matthew 2424:
1.94 harris41 2425: Start page output.
1.90 matthew 2426:
1.94 harris41 2427: =item *
1.90 matthew 2428:
1.94 harris41 2429: Evaluate individual file, and then output information.
1.90 matthew 2430:
1.94 harris41 2431: =item *
1.90 matthew 2432:
1.94 harris41 2433: Publishing from $thisfn to $thistarget with $thisembstyle.
1.90 matthew 2434:
2435: =back
2436:
2437: =cut
2438:
2439: #########################################
2440: #########################################
1.1 www 2441: sub handler {
1.139 albertel 2442: my $r=shift;
1.2 www 2443:
1.139 albertel 2444: if ($r->header_only) {
2445: &Apache::loncommon::content_type($r,'text/html');
2446: $r->send_http_header;
2447: return OK;
2448: }
1.2 www 2449:
1.43 www 2450: # Get query string for limited number of parameters
2451:
1.80 matthew 2452: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
2453: ['filename']);
1.43 www 2454:
1.183 www 2455: # -------------------------------------- Flag and buffer for registered cleanup
1.182 www 2456: $registered_cleanup=0;
1.183 www 2457: @{$modified_urls}=();
1.2 www 2458: # -------------------------------------------------------------- Check filename
2459:
1.209 www 2460: my $fn=&unescape($env{'form.filename'});
1.280 raeburn 2461: ($cuname,$cudom)=&Apache::lonnet::constructaccess($fn);
1.268 www 2462: # ----------------------------------------------------- Do we have permissions?
2463: unless (($cuname) && ($cudom)) {
2464: $r->log_reason($env{'user.name'}.' at '.$env{'user.domain'}.
2465: ' trying to publish file '.$env{'form.filename'}.
2466: ' - not authorized',
2467: $r->filename);
2468: return HTTP_NOT_ACCEPTABLE;
2469: }
2470: # ----------------------------------------------------------------- Get docroot
2471: $docroot=$r->dir_config('lonDocRoot');
1.160 www 2472:
2473:
2474: # special publication: default.meta file
2475: if ($fn=~/\/default.meta$/) {
2476: return &defaultmetapublish($r,$fn,$cuname,$cudom);
2477: }
1.159 www 2478: $fn=~s/\.meta$//;
1.268 www 2479:
2480: # sanity test on the filename
2481:
1.139 albertel 2482: unless ($fn) {
2483: $r->log_reason($cuname.' at '.$cudom.
2484: ' trying to publish empty filename', $r->filename);
2485: return HTTP_NOT_FOUND;
2486: }
2487:
1.268 www 2488: unless (-e $docroot.$fn) {
1.139 albertel 2489: $r->log_reason($cuname.' at '.$cudom.
2490: ' trying to publish non-existing file '.
1.192 albertel 2491: $env{'form.filename'}.' ('.$fn.')',
1.139 albertel 2492: $r->filename);
2493: return HTTP_NOT_FOUND;
2494: }
1.2 www 2495:
1.296 raeburn 2496: # --------------------------------- File is there and owned, start page output
1.2 www 2497:
1.139 albertel 2498: &Apache::loncommon::content_type($r,'text/html');
2499: $r->send_http_header;
1.180 albertel 2500:
1.259 bisitz 2501: # Breadcrumbs
2502: &Apache::lonhtmlcommon::clear_breadcrumbs();
2503: &Apache::lonhtmlcommon::add_breadcrumb({
1.282 raeburn 2504: 'text' => 'Authoring Space',
1.277 raeburn 2505: 'href' => &Apache::loncommon::authorspace($fn),
1.259 bisitz 2506: });
2507: &Apache::lonhtmlcommon::add_breadcrumb({
2508: 'text' => 'Resource Publication',
2509: 'href' => '',
2510: });
2511:
1.208 albertel 2512: my $js='<script type="text/javascript">'.
2513: &Apache::loncommon::browser_and_searcher_javascript().
2514: '</script>';
1.295 raeburn 2515: my $startargs = {};
2516: if ($fn=~/\/$/) {
2517: unless ($env{'form.phase'} eq 'two') {
2518: $startargs->{'add_entries'} = { onload => 'javascript:setDefaultAccess();' };
2519: $js .= <<"END";
2520: <script type="text/javascript">
2521: // <![CDATA[
2522: function showHideAccess(caller,div) {
2523: if (document.getElementById(div)) {
2524: if (caller.checked) {
2525: document.getElementById(div).style.display='inline-block';
2526: } else {
2527: document.getElementById(div).style.display='none';
2528: }
2529: }
2530: }
2531:
2532: function showHideCustom(caller,divid) {
2533: if (document.getElementById(divid)) {
2534: if (caller.options[caller.selectedIndex].value == 'custom') {
2535: document.getElementById(divid).style.display="inline-block";
2536: } else {
2537: document.getElementById(divid).style.display="none";
2538: }
2539: }
2540: }
2541: function setDefaultAccess() {
2542: var chkids = Array('LC_commondist','LC_commonsource');
2543: for (var i=0; i<chkids.length; i++) {
2544: if (document.getElementById(chkids[i])) {
2545: document.getElementById(chkids[i]).checked = false;
2546: }
2547: if (document.getElementById(chkids[i]+'select')) {
2548: document.getElementById(chkids[i]+'select').selectedIndex = 0;
2549: }
2550: if (document.getElementById(chkids[i]+'div')) {
2551: document.getElementById(chkids[i]+'div').style.display = 'none';
2552: }
2553: }
2554: }
2555: // ]]>
2556: </script>
2557:
2558: END
2559: }
2560: }
2561: $r->print(&Apache::loncommon::start_page('Resource Publication',$js,$startargs)
1.259 bisitz 2562: .&Apache::lonhtmlcommon::breadcrumbs()
2563: .&Apache::loncommon::head_subbox(
1.275 raeburn 2564: &Apache::loncommon::CSTR_pageheader($docroot.$fn))
1.259 bisitz 2565: );
1.101 www 2566:
1.268 www 2567: my $thisdisfn=&HTML::Entities::encode($fn,'<>&"');
2568: my $thistarget=$fn;
2569: $thistarget=~s/^\/priv\//\/res\//;
2570: my $thisdistarget=&HTML::Entities::encode($thistarget,'<>&"');
1.296 raeburn 2571: my $nokeyref = &getnokey($r->dir_config('lonIncludes'));
1.95 www 2572:
1.139 albertel 2573: if ($fn=~/\/$/) {
1.95 www 2574: # -------------------------------------------------------- This is a directory
1.296 raeburn 2575: &publishdirectory($r,$docroot.$fn,$thisdisfn,$nokeyref);
1.289 bisitz 2576: $r->print(
2577: '<br /><br />'.
2578: &Apache::lonhtmlcommon::actionbox([
2579: '<a href="'.$thisdisfn.'">'.&mt('Return to Directory').'</a>']));
1.139 albertel 2580: } else {
1.94 harris41 2581: # ---------------------- Evaluate individual file, and then output information.
1.268 www 2582: $fn=~/\.(\w+)$/;
1.139 albertel 2583: my $thistype=$1;
2584: my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
1.200 raeburn 2585: if ($thistype eq 'page') { $thisembstyle = 'rat'; }
1.2 www 2586:
1.254 bisitz 2587: $r->print('<h2>'
2588: .&mt('Publishing [_1]'
2589: ,'<span class="LC_filename">'.$thisdisfn.'</span>')
2590: .'</h2>'
2591: );
1.239 bisitz 2592:
2593: $r->print('<h3>'.&mt('Resource Details').'</h3>');
2594:
2595: $r->print(&Apache::lonhtmlcommon::start_pick_box());
2596:
2597: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Type'))
2598: .&Apache::loncommon::filedescription($thistype)
2599: .&Apache::lonhtmlcommon::row_closure()
2600: );
2601:
2602: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Link to Resource'))
2603: .'<tt>'
2604: );
1.139 albertel 2605: $r->print(<<ENDCAPTION);
1.268 www 2606: <a href='javascript:void(window.open("$thisdisfn","cat","height=300,width=500,scrollbars=1,resizable=1,menubar=0,location=1"))'>
1.129 www 2607: $thisdisfn</a>
2608: ENDCAPTION
1.239 bisitz 2609: $r->print('</tt>'
2610: .&Apache::lonhtmlcommon::row_closure()
2611: );
2612:
2613: $r->print(&Apache::lonhtmlcommon::row_title(&mt('Target'))
2614: .'<tt>'.$thisdistarget.'</tt>'
2615: );
1.192 albertel 2616: if (($cuname ne $env{'user.name'})||($cudom ne $env{'user.domain'})) {
1.240 raeburn 2617: $r->print(&Apache::lonhtmlcommon::row_closure()
2618: .&Apache::lonhtmlcommon::row_title(&mt('Co-Author'))
1.239 bisitz 2619: .'<span class="LC_warning">'
1.258 bisitz 2620: .&Apache::loncommon::plainname($cuname,$cudom) .' ('.$cuname.':'.$cudom.')'
1.239 bisitz 2621: .'</span>'
2622: );
1.139 albertel 2623: }
1.26 www 2624:
1.139 albertel 2625: if (&Apache::loncommon::fileembstyle($thistype) eq 'ssi') {
1.240 raeburn 2626: $r->print(&Apache::lonhtmlcommon::row_closure()
2627: .&Apache::lonhtmlcommon::row_title(&mt('Diffs')));
1.139 albertel 2628: $r->print(<<ENDDIFF);
1.284 bisitz 2629: <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"))'>
1.129 www 2630: ENDDIFF
1.240 raeburn 2631: $r->print(&mt('Diffs with Current Version').'</a>');
1.139 albertel 2632: }
1.240 raeburn 2633:
2634: $r->print(&Apache::lonhtmlcommon::row_closure(1)
2635: .&Apache::lonhtmlcommon::end_pick_box()
2636: );
1.11 www 2637:
1.268 www 2638: # ---------------------- Publishing from $fn to $thistarget with $thisembstyle.
1.2 www 2639:
1.192 albertel 2640: unless ($env{'form.phase'} eq 'two') {
1.185 www 2641: # ---------------------------------------------------------- Parse for problems
1.189 albertel 2642: my ($warningcount,$errorcount);
2643: if ($thisembstyle eq 'ssi') {
1.268 www 2644: ($warningcount,$errorcount)=&checkonthis($r,$fn);
1.189 albertel 2645: }
2646: unless ($errorcount) {
1.187 www 2647: my ($outstring,$error)=
1.296 raeburn 2648: &publish($docroot.$fn,$docroot.$thistarget,$thisembstyle,undef,$nokeyref);
1.246 bisitz 2649: $r->print($outstring);
1.187 www 2650: } else {
1.239 bisitz 2651: $r->print('<h3 class="LC_error">'.
1.189 albertel 2652: &mt('The document contains errors and cannot be published.').
1.187 www 2653: '</h3>');
2654: }
1.139 albertel 2655: } else {
1.296 raeburn 2656: my ($output,$error) = &phasetwo($r,$docroot.$fn,$docroot.$thistarget,
2657: $thisembstyle,$thisdistarget);
2658: $r->print($output);
1.139 albertel 2659: }
2660: }
1.208 albertel 2661: $r->print(&Apache::loncommon::end_page());
1.15 www 2662:
1.139 albertel 2663: return OK;
1.1 www 2664: }
2665:
1.296 raeburn 2666: BEGIN {
2667:
2668: # ----------------------------------- Read addid.tab
2669: unless ($readit) {
2670: %addid=();
2671:
2672: {
2673: my $tabdir = $Apache::lonnet::perlvar{'lonTabDir'};
2674: my $fh=Apache::File->new($tabdir.'/addid.tab');
2675: while (<$fh>=~/(\w+)\s+(\w+)/) {
2676: $addid{$1}=$2;
2677: }
2678: }
2679: }
2680: $readit=1;
2681: }
2682:
2683:
1.1 www 2684: 1;
2685: __END__
2686:
1.89 matthew 2687: =pod
1.126 bowersj2 2688:
2689: =back
1.66 harris41 2690:
1.89 matthew 2691: =cut
1.66 harris41 2692:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>