--- loncom/publisher/lonpublisher.pm	2014/12/12 18:27:34	1.294
+++ loncom/publisher/lonpublisher.pm	2024/12/30 17:13:26	1.295.2.4
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Publication Handler
 #
-# $Id: lonpublisher.pm,v 1.294 2014/12/12 18:27:34 raeburn Exp $
+# $Id: lonpublisher.pm,v 1.295.2.4 2024/12/30 17:13:26 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -318,8 +318,12 @@ string which presents the form field (fo
 
 =item B<textfield>
 
+=item B<text_with_browse_field>
+
 =item B<hiddenfield>
 
+=item B<checkbox>
+
 =item B<selectbox>
 
 =back
@@ -367,7 +371,7 @@ sub hiddenfield {
 
 sub checkbox {
     my ($name,$text)=@_;
-    return "\n<br /><label><input type='checkbox' name='$name' /> ".
+    return "\n<label><input type='checkbox' name='$name' /> ".
 	&mt($text)."</label>";
 }
 
@@ -399,6 +403,58 @@ sub select_level_form {
     if (!defined($value)) { $env{'form.'.$name}=0; }
     return  &Apache::loncommon::select_level_form($value,$name);
 }
+
+sub common_access {
+    my ($name,$text,$options)=@_;
+    return unless (ref($options) eq 'ARRAY');
+    my $formname = 'pubdirpref';
+    my $chkname = 'common'.$name;
+    my $chkid = 'LC_'.$chkname;
+    my $divid = $chkid.'div';
+    my $customdivid = 'LC_customfile'; 
+    my $selname = $chkname.'select';
+    my $selid = $chkid.'select';
+    my $selonchange;
+    if ($name eq 'dist') {
+        $selonchange = ' onchange="showHideCustom(this,'."'$customdivid'".');"';
+    }
+    my %lt = &Apache::lonlocal::texthash(
+                                            'default' => 'System wide - can be used for any courses system wide',
+                                            'domain'  => 'Domain only - use limited to courses in the domain',
+                                            'custom'  => 'Customized right of use ...',
+                                            'public'  => 'Public - no authentication or authorization required for use',
+                                            'closed'  => 'Closed - XML source is closed to everyone',
+                                            'open'    => 'Open - XML source is open to people who want to use it',
+                                            'sel'     => 'Select',
+                                        );
+    my $output = <<"END";
+<span class="LC_nobreak">
+<label>
+<input type="checkbox" name="commonaccess" value="$name" id="$chkid"  
+onclick="showHideAccess(this,'$divid');" />
+$text</label></span>
+<div id="$divid" style="padding:0;clear:both;margin:0;border:0;display:none">
+<select name="$selname" id="$selid" $selonchange>
+<option value="" selected="selected">$lt{'sel'}</option>
+END
+    foreach my $val (@{$options}) {
+        $output .= '<option value="'.$val.'">'.$lt{$val}.'</option>'."\n";
+    }
+    $output .= '
+</select>';
+    if ($name eq 'dist') {
+        $output .= <<"END";
+<div id="$customdivid" style="padding:0;clear:both;margin:0;border:0;display:none">
+<input type="text" name="commoncustomrights" size="60" value="" />
+<a href="javascript:openbrowser('$formname','commoncustomrights','rights');">
+$lt{'sel'}</a></div>
+END
+    }
+    $output .= '
+</div>
+';
+}
+
 #########################################
 #########################################
 
@@ -1629,7 +1685,33 @@ sub phasetwo {
     %metadatakeys=();
 
     &metaeval(&unescape($env{'form.allmeta'}));
-    
+
+    if ($batch) {
+        my %commonaccess;
+        map { $commonaccess{$_} = 1; } &Apache::loncommon::get_env_multiple('form.commonaccess');
+        if ($commonaccess{'dist'}) {
+            unless ($style eq 'prv') { 
+                if ($env{'form.commondistselect'} eq 'custom') {
+                    unless ($source =~ /\.rights$/) {
+                        if ($env{'form.commoncustomrights'} =~ m{^/res/.+\.rights$}) { 
+                            $env{'form.customdistributionfile'} = $env{'form.commoncustomrights'}; 
+                            $env{'form.copyright'} = $env{'form.commondistselect'};
+                        }
+                    }
+                } elsif ($env{'form.commondistselect'} =~ /^default|domain|public$/) {
+                    $env{'form.copyright'} = $env{'form.commondistselect'};
+                }
+            }
+        }
+        unless ($style eq 'prv') {
+            if ($commonaccess{'source'}) {
+                if (($env{'form.commonsourceselect'} eq 'open') || ($env{'form.commonsourceselect'} eq 'closed')) {
+                    $env{'form.sourceavail'} = $env{'form.commonsourceselect'};
+                }
+            }
+        }
+    }
+
     $metadatafields{'title'}=$env{'form.title'};
     $metadatafields{'author'}=$env{'form.author'};
     $metadatafields{'subject'}=$env{'form.subject'};
@@ -1842,11 +1924,7 @@ sub phasetwo {
 
 # ------------------------------------------------------------- Trigger updates
     push(@{$modified_urls},[$target,$source]);
-    unless ($registered_cleanup) {
-        my $handlers = $r->get_handlers('PerlCleanupHandler');
-        $r->set_handlers('PerlCleanupHandler' => [\&notify,@{$handlers}]);
-	$registered_cleanup=1;
-    }
+    &notify_in_cleanup($r);
 
 # ---------------------------------------------------------- Clear local caches
     my $thisdistarget=$target;
@@ -1882,39 +1960,51 @@ sub phasetwo {
     return 1;
 }
 
+sub notify_in_cleanup {
+    my ($r) = @_;
+    unless ($registered_cleanup) {
+        my $handlers = $r->get_handlers('PerlCleanupHandler');
+        $r->set_handlers('PerlCleanupHandler' => [\&notify,@{$handlers}]);
+        $registered_cleanup=1;
+    }
+}
+
 # =============================================================== Notifications
 sub notify {  
 # --------------------------------------------------- Send update notifications
-    foreach my $targetsource (@{$modified_urls}){
-	my ($target,$source)=@{$targetsource};
-	my $logfile=Apache::File->new('>>'.$source.'.log');
-	print $logfile "\nCleanup phase: Notifications\n";
-	my @subscribed=&get_subscribed_hosts($target);
-	foreach my $subhost (@subscribed) {
-	    print $logfile "\nNotifying host ".$subhost.':';
-	    my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
-	    print $logfile $reply;
-	}
+    if (ref($modified_urls) eq 'ARRAY') {
+        foreach my $targetsource (@{$modified_urls}){
+	    my ($target,$source)=@{$targetsource};
+	    my $logfile=Apache::File->new('>>'.$source.'.log');
+	    print $logfile "\nCleanup phase: Notifications\n";
+	    my @subscribed=&get_subscribed_hosts($target);
+	    foreach my $subhost (@subscribed) {
+	        print $logfile "\nNotifying host ".$subhost.':';
+	        my $reply=&Apache::lonnet::critical('update:'.$target,$subhost);
+	        print $logfile $reply;
+	    }
 # ---------------------------------------- Send update notifications, meta only
-	my @subscribedmeta=&get_subscribed_hosts("$target.meta");
-	foreach my $subhost (@subscribedmeta) {
-	    print $logfile "\nNotifying host for metadata only ".$subhost.':';
-	    my $reply=&Apache::lonnet::critical('update:'.$target.'.meta',
-						$subhost);
-	    print $logfile $reply;
-	} 
+	    my @subscribedmeta=&get_subscribed_hosts("$target.meta");
+	    foreach my $subhost (@subscribedmeta) {
+	        print $logfile "\nNotifying host for metadata only ".$subhost.':';
+	        my $reply=&Apache::lonnet::critical('update:'.$target.'.meta',
+						    $subhost);
+	        print $logfile $reply;
+	    }
 # --------------------------------------------------- Notify subscribed courses
-	my %courses=&coursedependencies($target);
-	my $now=time;
-	foreach my $course (keys(%courses)) {
-	    print $logfile "\nNotifying course ".$course.':';
-	    my ($cdom,$cname)=split(/\_/,$course);
-	    my $reply=&Apache::lonnet::cput
-		('versionupdate',{$target => $now},$cdom,$cname);
-	    print $logfile $reply;
-	}
-	print $logfile "\n============ Done ============\n";
-	$logfile->close();
+	    my %courses=&coursedependencies($target);
+	    my $now=time;
+	    foreach my $course (keys(%courses)) {
+	        print $logfile "\nNotifying course ".$course.':';
+	        my ($cdom,$cname)=split(/\_/,$course);
+	        my $reply=&Apache::lonnet::cput
+		    ('versionupdate',{$target => $now},$cdom,$cname);
+	        print $logfile $reply;
+	    }
+	    print $logfile "\n============ Done ============\n";
+	    $logfile->close();
+        }
+        $modified_urls = [];
     }
     if ($lock) { &Apache::lonnet::remove_lock($lock); }
     return OK;
@@ -1981,19 +2071,39 @@ sub publishdirectory {
             .&Apache::lonhtmlcommon::row_title(&mt('Target'))
             .'<span class="LC_filename">'.$thisdisresdir.'</span>'
     );
+    my %reasons = &Apache::lonlocal::texthash(
+                      mod => 'Authoring Space file postdates published file',
+                      modmeta => 'Authoring Space metadata file postdates published file',
+                      unpub => 'Resource is unpublished',
+    );
 
     my $dirptr=16384;		# Mask indicating a directory in stat.cmode.
     unless ($env{'form.phase'} eq 'two') {
 # ask user what they want
         $r->print(&Apache::lonhtmlcommon::row_closure()
-                 .&Apache::lonhtmlcommon::row_title(&mt('Options'))
-        );
+                 .&Apache::lonhtmlcommon::row_title(&mt('Options')
+                 .&Apache::loncommon::help_open_topic('Publishing_Directory_Options')));
         $r->print(&hiddenfield('phase','two').
 		  &hiddenfield('filename',$env{'form.filename'}).
-		  &checkbox('pubrec','include subdirectories').
-		  &checkbox('forcerepub','force republication of previously published files').
-                  &checkbox('obsolete','make file(s) obsolete').
-		  &checkbox('forceoverride','force directory level metadata over existing')
+                  '<fieldset><legend>'.&mt('Recurse').'</legend>'.
+                  &checkbox('pubrec','include subdirectories').
+                  '</fieldset>'.
+                  '<fieldset><legend>'.&mt('Force').'</legend>'.
+                  &checkbox('forcerepub','force republication of previously published files').'<br />'.
+                  &checkbox('forceoverride','force directory level metadata over existing').
+                  '</fieldset>'.
+                  '<fieldset><legend>'.&mt('Exclude').'</legend>'.
+                  &checkbox('excludeunpub','exclude currently unpublished files').'<br />'.
+                  &checkbox('excludemod','exclude modified files').'<br />'.
+                  &checkbox('excludemodmeta','exclude files with modified metadata').
+                  '</fieldset>'.
+                  '<fieldset><legend>'.&mt('Actions').'</legend>'.
+                  &checkbox('obsolete','make file(s) obsolete').'<br />'.
+                  &common_access('dist',&mt('apply common copyright/distribution'),
+                                 ['default','domain','public','custom']).'<br />'.
+                  &common_access('source',&mt('apply common source availability'),
+                                 ['closed','open']).
+                  '</fieldset>'
         );
         $r->print(&Apache::lonhtmlcommon::row_closure(1)
                  .&Apache::lonhtmlcommon::end_pick_box()
@@ -2004,7 +2114,12 @@ sub publishdirectory {
         $r->print(&Apache::lonhtmlcommon::row_closure(1)
                  .&Apache::lonhtmlcommon::end_pick_box()
         );
+        my %commonaccess;
+        map { $commonaccess{$_} = 1; } &Apache::loncommon::get_env_multiple('form.commonaccess');
         unless ($lock) { $lock=&Apache::lonnet::set_lock(&mt('Publishing [_1]',$fn)); }
+        if ($lock) {
+            &notify_in_cleanup($r);
+        }
 # actually publish things
 	opendir(DIR,$fn);
 	my @files=sort(readdir(DIR));
@@ -2013,7 +2128,7 @@ sub publishdirectory {
 		$cuid,$cgid,$crdev,$csize,
 		$catime,$cmtime,$cctime,
 		$cblksize,$cblocks)=stat($fn.'/'.$filename);
-	    
+
 	    my $extension='';
 	    if ($filename=~/\.(\w+)$/) { $extension=$1; }
 	    if ($cmode&$dirptr) {
@@ -2022,8 +2137,9 @@ sub publishdirectory {
 		}
 	    } elsif ((&Apache::loncommon::fileembstyle($extension) ne 'hdn') &&
 		     ($filename!~/^[\#\.]/) && ($filename!~/\~$/)) {
-# find out publication status and/or exiting metadata
+# find out publication status and/or existing metadata
 		my $publishthis=0;
+                my $skipthis;
 		if (-e $resdir.'/'.$filename) {
 		    my ($rdev,$rino,$rmode,$rnlink,
 			$ruid,$rgid,$rrdev,$rsize,
@@ -2031,22 +2147,72 @@ sub publishdirectory {
 			$rblksize,$rblocks)=stat($resdir.'/'.$filename);
 		    if (($rmtime<$cmtime) || ($env{'form.forcerepub'})) {
 # previously published, modified now
-			$publishthis=1;
-		    }
-		    my $meta_cmtime = (stat($fn.'/'.$filename.'.meta'))[9];
-		    my $meta_rmtime = (stat($resdir.'/'.$filename.'.meta'))[9];
-		    if ( $meta_rmtime<$meta_cmtime ) {
-			$publishthis=1;
+                        if ($env{'form.excludemod'}) {
+                            $skipthis='mod';
+                        } else {
+                            $publishthis=1;
+                        }
 		    }
+                    unless ($skipthis) {
+                        my $meta_cmtime = (stat($fn.'/'.$filename.'.meta'))[9];
+                        my $meta_rmtime = (stat($resdir.'/'.$filename.'.meta'))[9];
+                        if ( $meta_rmtime<$meta_cmtime ) {
+                            if ($env{'form.excludemodmeta'}) {
+                                $skipthis='modmeta';
+                                $publishthis=0;
+                            } else {
+                                $publishthis=1;
+                            }
+                        } else {
+                            unless (&Apache::loncommon::fileembstyle($extension) eq 'prv') {
+                                if ($commonaccess{'dist'}) {
+                                    my ($currdist,$currdistfile,$currsourceavail);
+                                    my $currdist =  &Apache::lonnet::metadata($thisdisresdir.'/'.$filename,'copyright');
+                                    if ($currdist eq 'custom') {
+                                        $currdistfile =  &Apache::lonnet::metadata($thisdisresdir.'/'.$filename,'customdistributionfile');
+                                    }
+                                    if ($env{'form.commondistselect'} eq 'custom') {
+                                        if ($env{'form.commoncustomrights'} =~ m{^/res/.+\.rights$}) {
+                                            if ($currdist eq 'custom') {
+                                                unless ($env{'form.commoncustomrights'} eq $currdistfile) {
+                                                    $publishthis=1;
+                                                }
+                                            } else {
+                                                $publishthis=1;
+                                            }
+                                        }
+                                    } elsif ($env{'form.commondistselect'} =~ /^default|domain|public$/) {
+                                        unless ($currdist eq $env{'form.commondistselect'}) {
+                                            $publishthis=1;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
 		} else {
 # never published
-		    $publishthis=1;
+                    if ($env{'form.excludeunpub'}) {
+                        $skipthis='unpub';
+                    } else {
+                        $publishthis=1;
+                    }
 		}
 		
 		if ($publishthis) {
 		    &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename);
 		} else {
-		    $r->print('<br />'.&mt('Skipping').' '.$filename.'<br />');
+                    my $reason;
+                    if ($skipthis) {
+                        $reason = $reasons{$skipthis};
+                    } else {
+                        $reason = &mt('No changes needed to published resource or metadata');
+                    }
+                    $r->print('<br />'.&mt('Skipping').' '.$filename);
+                    if ($reason) {
+                        $r->print(' ('.$reason.')');
+                    }
+                    $r->print('<br />');
 		}
 		$r->rflush();
 	    }
@@ -2247,7 +2413,53 @@ sub handler {
     my $js='<script type="text/javascript">'.
 	&Apache::loncommon::browser_and_searcher_javascript().
 	'</script>';
-    $r->print(&Apache::loncommon::start_page('Resource Publication',$js)
+    my $startargs = {};
+    if ($fn=~/\/$/) {
+        unless ($env{'form.phase'} eq 'two') {
+            $startargs->{'add_entries'} = { onload => 'javascript:setDefaultAccess();' };
+            $js .= <<"END";
+<script type="text/javascript">
+// <![CDATA[
+function showHideAccess(caller,div) {
+    if (document.getElementById(div)) {
+        if (caller.checked) {
+            document.getElementById(div).style.display='inline-block';
+        } else {
+            document.getElementById(div).style.display='none';
+        }
+    }
+}
+
+function showHideCustom(caller,divid) {
+    if (document.getElementById(divid)) {
+        if (caller.options[caller.selectedIndex].value == 'custom') {
+            document.getElementById(divid).style.display="inline-block";
+        } else {
+            document.getElementById(divid).style.display="none";
+        }
+    }
+}
+function setDefaultAccess() {
+    var chkids = Array('LC_commondist','LC_commonsource');
+    for (var i=0; i<chkids.length; i++) {
+        if (document.getElementById(chkids[i])) {
+            document.getElementById(chkids[i]).checked = false;
+        }
+        if (document.getElementById(chkids[i]+'select')) {
+           document.getElementById(chkids[i]+'select').selectedIndex = 0; 
+        }
+        if (document.getElementById(chkids[i]+'div')) {
+            document.getElementById(chkids[i]+'div').style.display = 'none';
+        }
+    }
+}
+// ]]>
+</script>
+
+END
+        }
+    }
+    $r->print(&Apache::loncommon::start_page('Resource Publication',$js,$startargs)
              .&Apache::lonhtmlcommon::breadcrumbs()
              .&Apache::loncommon::head_subbox(
                   &Apache::loncommon::CSTR_pageheader($docroot.$fn))