--- loncom/publisher/lonpublisher.pm	2014/07/27 11:14:36	1.290
+++ loncom/publisher/lonpublisher.pm	2024/12/27 04:01:41	1.306
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Publication Handler
 #
-# $Id: lonpublisher.pm,v 1.290 2014/07/27 11:14:36 raeburn Exp $
+# $Id: lonpublisher.pm,v 1.306 2024/12/27 04:01:41 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -128,13 +128,9 @@ use Apache::lonlocal;
 use Apache::loncfile;
 use LONCAPA::lonmetadata;
 use Apache::lonmsg;
-use vars qw(%metadatafields %metadatakeys);
+use vars qw(%metadatafields %metadatakeys %addid $readit);
 use LONCAPA qw(:DEFAULT :match);
  
-
-my %addid;
-my %nokey;
-
 my $docroot;
 
 my $cuname;
@@ -200,12 +196,12 @@ sub metaeval {
 	    if (defined($token->[2]->{'name'})) { 
 		$unikey.="\0".$token->[2]->{'name'}; 
 	    }
-	    foreach (@{$token->[3]}) {
-		$metadatafields{$unikey.'.'.$_}=$token->[2]->{$_};
+	    foreach my $item (@{$token->[3]}) {
+		$metadatafields{$unikey.'.'.$item}=$token->[2]->{$item};
 		if ($metadatakeys{$unikey}) {
-		    $metadatakeys{$unikey}.=','.$_;
+		    $metadatakeys{$unikey}.=','.$item;
 		} else {
-		    $metadatakeys{$unikey}=$_;
+		    $metadatakeys{$unikey}=$item;
 		}
 	    }
 	    my $newentry=$parser->get_text('/'.$entry);
@@ -293,8 +289,8 @@ sub coursedependencies {
     my %evaldata=&Apache::lonnet::dump('nohist_resevaldata',$adomain,
 				       $aauthor,$regexp);
     my %courses=();
-    foreach (keys %evaldata) {
-	if ($_=~/^([a-zA-Z0-9]+_[a-zA-Z0-9]+)___.+___course$/) {
+    foreach my $item (keys(%evaldata)) {
+	if ($item=~/^([a-zA-Z0-9]+_[a-zA-Z0-9]+)___.+___course$/) {
 	    $courses{$1}=1;
         }
     }
@@ -318,8 +314,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
@@ -329,7 +329,7 @@ string which presents the form field (fo
 #########################################
 #########################################
 sub textfield {
-    my ($title,$name,$value,$noline)=@_;
+    my ($title,$name,$value,$noline,$readonly)=@_;
     $value=~s/^\s+//gs;
     $value=~s/\s+$//gs;
     $value=~s/\s+/ /gs;
@@ -341,22 +341,31 @@ sub textfield {
 }
 
 sub text_with_browse_field {
-    my ($title,$name,$value,$restriction,$noline)=@_;
+    my ($title,$name,$value,$restriction,$noline,$readonly)=@_;
     $value=~s/^\s+//gs;
     $value=~s/\s+$//gs;
     $value=~s/\s+/ /gs;
     $title=&mt($title);
     $env{'form.'.$name}=$value;
-    return "\n".&Apache::lonhtmlcommon::row_title($title)
-          .'<input type="text" name="'.$name.'" size="80" value="'.$value.'" />'
-          .'<br />'
+    my $disabled;
+    if ($readonly) {
+        $disabled = ' disabled="disabled"';
+    }
+    my $output =
+          "\n".&Apache::lonhtmlcommon::row_title($title)
+          .'<input type="text" name="'.$name.'" size="80" value="'.$value.'"'.$disabled.' />';
+    unless ($readonly) {
+        $output .=
+          '<br />'
 	  .'<a href="javascript:openbrowser(\'pubform\',\''.$name.'\',\''.$restriction.'\');">'
           .&mt('Select')
           .'</a>&nbsp;'
 	  .'<a href="javascript:opensearcher(\'pubform\',\''.$name.'\');">'
           .&mt('Search')
-          .'</a>'
-          .&Apache::lonhtmlcommon::row_closure($noline);
+          .'</a>';
+    }
+    $output .= &Apache::lonhtmlcommon::row_closure($noline);
+    return $output;
 }
 
 sub hiddenfield {
@@ -367,12 +376,12 @@ 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>";
 }
 
 sub selectbox {
-    my ($title,$name,$value,$functionref,@idlist)=@_;
+    my ($title,$name,$value,$readonly,$functionref,@idlist)=@_;
     $title=&mt($title);
     $value=(split(/\s*,\s*/,$value))[-1];
     if (defined($value)) {
@@ -382,12 +391,15 @@ sub selectbox {
     }
     my $selout="\n".&Apache::lonhtmlcommon::row_title($title)
               .'<select name="'.$name.'">';
-    foreach (@idlist) {
-        $selout.='<option value="'.$_.'"';
-        if ($_ eq $value) {
+    foreach my $id (@idlist) {
+        $selout.='<option value="'.$id.'"';
+        if ($id eq $value) {
 	    $selout.=' selected="selected"';
         }
-        $selout.='>'.&{$functionref}($_).'</option>';
+        if ($readonly) {
+            $selout .= ' disabled="disabled"';
+        }
+        $selout.='>'.&{$functionref}($id).'</option>';
     }
     $selout.='</select>'.&Apache::lonhtmlcommon::row_closure();
     return $selout;
@@ -399,6 +411,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>
+';
+}
+
 #########################################
 #########################################
 
@@ -726,6 +790,8 @@ sub fix_ids_and_indices {
 		    foreach my $type ('src','href','background','bgimg') {
 			foreach my $key (keys(%parms)) {
 			    if ($key =~ /^$type$/i) {
+                                next if (($lctag eq 'img') && ($type eq 'src') && 
+                                         ($parms{$key} =~ m{^data\:image/gif;base64,}));
 				$parms{$key}=&set_allow(\%allow,$logfile,
 							$target,$tag,
 							$parms{$key},$type);
@@ -785,12 +851,12 @@ sub fix_ids_and_indices {
 		}
 		my $newparmstring='';
 		my $endtag='';
-		foreach (keys %parms) {
-		    if ($_ eq '/') {
+		foreach my $parkey (keys(%parms)) {
+		    if ($parkey eq '/') {
 			$endtag=' /';
 		    } else { 
-			my $quote=($parms{$_}=~/\"/?"'":'"');
-			$newparmstring.=' '.$_.'='.$quote.$parms{$_}.$quote;
+			my $quote=($parms{$parkey}=~/\"/?"'":'"');
+			$newparmstring.=' '.$parkey.'='.$quote.$parms{$parkey}.$quote;
 		    }
 		}
 		if (!$endtag) { if ($token->[4]=~m:/>$:) { $endtag=' /'; }; }
@@ -823,6 +889,14 @@ sub fix_ids_and_indices {
 #
                                                     my $spritesheet = $1.'express_show/spritesheet.png';
                                                     $allow{&absoluteurl($spritesheet,$target)}=1;
+
+#
+# Camtasia 8.4: skins/express_show/spritesheet.min.css needed, and included in zip archive.
+# Not referenced directly in <main>.html or <main>_player.html files,
+# so add this file to %allow (where <main> is name user gave to file/archive).
+#
+                                                    my $spritecss = $1.'express_show/spritesheet.min.css';
+                                                    $allow{&absoluteurl($spritecss,$target)}=1;
                                                 }
                                             } elsif ($srctype eq 'PosterImageSrc') {
                                                 if ($url =~ m{^(.+)_First_Frame\.png$}) {
@@ -841,6 +915,38 @@ sub fix_ids_and_indices {
                                 }
                             }
                         }
+                        if ($script =~ m{\.addMediaSrc\((["'])((?!\1).+)\1\);}) {
+                            my $src = $2;
+                            if ($src) {
+                                my $url = &urlfixup($src);
+                                unless ($url=~m{^(?:http|https|ftp)://}) {
+                                    $allow{&absoluteurl($url,$target)}=1;
+                                }
+                            }
+                        }
+                        if ($script =~ /\(document,\s*(['"])script\1,\s*\[([^\]]+)\]\);/s) {
+                            my $scriptslist = $2;
+                            my @srcs = split(/\s*,\s*/,$scriptslist);
+                            foreach my $src (@srcs) {
+                                if ($src =~ /(["'])(?:(?!\1).)+\.js\1/) {
+                                    my $quote = $1;
+                                    my ($url) = ($src =~ m/\Q$quote\E([^$quote]+)\Q$quote\E/);
+                                    $url = &urlfixup($url);
+                                    unless ($url=~m{^(?:http|https|ftp)://}) {
+                                        $allow{&absoluteurl($url,$target)}=1;
+                                    }
+                                }
+                            }
+                        }
+                        if ($script =~ m{loadScript\(\s*(['"])((?:(?!\1).)+\.js)\1,\s*function}is) {
+                            my $src = $2;
+                            if ($src) {
+                                my $url = &urlfixup($src);
+                                unless ($url=~m{^(?:http|https|ftp)://}) {
+                                    $allow{&absoluteurl($url,$target)}=1;
+                                }
+                            }
+                        }
                         $outstring .= $script;
                     }
                 }
@@ -1008,7 +1114,7 @@ I<Additional documentation needed.>
 #########################################
 sub publish {
 
-    my ($source,$target,$style,$batch)=@_;
+    my ($source,$target,$style,$batch,$nokeyref)=@_;
     my $logfile;
     my $scrout='';
     my $allmeta='';
@@ -1105,6 +1211,20 @@ sub publish {
 	  $content=$outstring;
 
     }
+
+# ----------------------------------------------------- Course Authoring Space.
+    my ($courseauthor,$crsaurights,$readonly);
+    if ($env{'request.course.id'}) {
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
+        if ($source =~ m{^\Q$docroot/priv/$cdom/$cnum/\E}) {
+            $courseauthor = $cnum.':'.$cdom;
+            $crsaurights = "/res/$cdom/$cnum/default.rights";
+            $readonly = 1;
+        }
+    }
+
 # -------------------------------------------- Initial step done, now metadata.
 
 # --------------------------------------- Storage for metadata keys and fields.
@@ -1155,9 +1275,9 @@ sub publish {
 
 # ------------------- Clear out parameters and stores (there should not be any)
 
-        foreach (keys %metadatafields) {
-	    if (($_=~/^parameter/) || ($_=~/^stores/)) {
-		delete $metadatafields{$_};
+        foreach my $field (keys(%metadatafields)) {
+	    if (($field=~/^parameter/) || ($field=~/^stores/)) {
+		delete $metadatafields{$field};
             }
         }
 
@@ -1166,23 +1286,21 @@ sub publish {
 
         $scrout.=&metaread($logfile,$source.'.meta');
 
-        foreach (keys %metadatafields) {
-	    if (($_=~/^parameter/) || ($_=~/^stores/)) {
-                $oldparmstores{$_}=1;
-		delete $metadatafields{$_};
+        foreach my $field (keys(%metadatafields)) {
+	    if (($field=~/^parameter/) || ($field=~/^stores/)) {
+                $oldparmstores{$field}=1;
+		delete $metadatafields{$field};
             }
         }
 # ------------------------------------------------------------- Save some stuff
         my %savemeta=();
-        foreach ('title') {
-            if ($metadatafields{$_}) { $savemeta{$_}=$metadatafields{$_}; }
-	}
+        if ($metadatafields{'title'}) { $savemeta{'title'}=$metadatafields{'title'}; }
 # ------------------------------------------ See if anything new in file itself
  
 	$allmeta=&parseformeta($source,$style);
 # ----------------------------------------------------------- Restore the stuff
-        foreach (keys %savemeta) {
-	    $metadatafields{$_}=$savemeta{$_};
+        foreach my $item (keys(%savemeta)) {
+	    $metadatafields{$item}=$savemeta{$item};
 	}
    }
 
@@ -1190,11 +1308,11 @@ sub publish {
 # ---------------- Find and document discrepancies in the parameters and stores
 
     my $chparms='';
-    foreach (sort keys %metadatafields) {
-	if (($_=~/^parameter/) || ($_=~/^stores/)) {
-	    unless ($_=~/\.\w+$/) { 
-		unless ($oldparmstores{$_}) {
-		    my $disp_key = $_;
+    foreach my $field (sort(keys(%metadatafields))) {
+	if (($field=~/^parameter/) || ($field=~/^stores/)) {
+	    unless ($field=~/\.\w+$/) {
+		unless ($oldparmstores{$field}) {
+		    my $disp_key = $field;
 		    $disp_key =~ tr/\0/_/;
 		    print $logfile ('New: '.$disp_key."\n");
 		    $chparms .= $disp_key.' ';
@@ -1208,11 +1326,11 @@ sub publish {
     }
 
     $chparms='';
-    foreach (sort keys %oldparmstores) {
-	if (($_=~/^parameter/) || ($_=~/^stores/)) {
-	    unless (($metadatafields{$_.'.name'}) ||
-		    ($metadatafields{$_.'.package'}) || ($_=~/\.\w+$/)) {
-		my $disp_key = $_;
+    foreach my $olditem (sort(keys(%oldparmstores))) {
+	if (($olditem=~/^parameter/) || ($olditem=~/^stores/)) {
+	    unless (($metadatafields{$olditem.'.name'}) ||
+		    ($metadatafields{$olditem.'.package'}) || ($olditem=~/\.\w+$/)) {
+		my $disp_key = $olditem;
 		$disp_key =~ tr/\0/_/;
 		print $logfile ('Obsolete: '.$disp_key."\n");
 		$chparms.=$disp_key.' ';
@@ -1250,7 +1368,7 @@ sub publish {
         $textonly=~s/[^a-z^ü^ä^ö^ß\s]//g;  #dont delete german "Umlaute"
 
         foreach ($textonly=~m/[^\s]+/g) {  #match all but whitespaces
-            unless ($nokey{$_}) {
+            unless ($nokeyref->{$_}) {
                 $keywords{$_}=1;
             }
         }
@@ -1289,7 +1407,7 @@ sub publish {
 	&hiddenfield('phase','two').
 	&hiddenfield('filename',$env{'form.filename'}).
 	&hiddenfield('allmeta',&escape($allmeta)).
-	&hiddenfield('dependencies',join(',',keys %allow));
+	&hiddenfield('dependencies',join(',',keys(%allow)));
     unless ($env{'form.makeobsolete'}) {
        $intr_scrout.=
 	&textfield('Title','title',$metadatafields{'title'}).
@@ -1393,7 +1511,7 @@ END
     $defaultlanguage =~ s/,\s*$//g;
 
     $intr_scrout.=&selectbox('Language','language',
-			     $defaultlanguage,
+			     $defaultlanguage,'',
 			     \&Apache::loncommon::languagedescription,
 			     (&Apache::loncommon::languageids),
 			     );
@@ -1410,8 +1528,11 @@ END
     if ($style eq 'prv') {
         $pubowner_last = 1;
     }
+    if ($courseauthor) {
+        $metadatafields{'owner'} = $courseauthor;
+    }
     $intr_scrout.=&textfield('Publisher/Owner','owner',
-			     $metadatafields{'owner'},$pubowner_last);
+			     $metadatafields{'owner'},$pubowner_last,$readonly);
 
 # ---------------------------------------------- Retrofix for unused copyright
     if ($metadatafields{'copyright'} eq 'free') {
@@ -1424,6 +1545,10 @@ END
 # ------------------------------------------------ Dial in reasonable defaults
     my $defaultoption=$metadatafields{'copyright'};
     unless ($defaultoption) { $defaultoption='default'; }
+    if ($courseauthor) {
+        $defaultoption='custom';
+        $metadatafields{'customdistributionfile'}=$crsaurights;
+    }
     my $defaultsourceoption=$metadatafields{'sourceavail'};
     unless ($defaultsourceoption) { $defaultsourceoption='closed'; }
     unless ($style eq 'prv') {
@@ -1435,12 +1560,12 @@ END
 		$defaultoption='default';
 	    }
 	    $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
-				     $defaultoption,
+				     $defaultoption,$readonly,
 				     \&Apache::loncommon::copyrightdescription,
 				    (grep !/^(public|priv)$/,(&Apache::loncommon::copyrightids)));
 	} else {
 	    $intr_scrout.=&selectbox('Copyright/Distribution','copyright',
-				     $defaultoption,
+				     $defaultoption,$readonly,
 				     \&Apache::loncommon::copyrightdescription,
 				     (grep !/^priv$/,(&Apache::loncommon::copyrightids)));
 	}
@@ -1449,9 +1574,9 @@ END
         my $replace=&mt('Copyright/Distribution:');
 	$intr_scrout =~ s/$replace/$replace.' '.$copyright_help/ge;
 
-	$intr_scrout.=&text_with_browse_field('Custom Distribution File','customdistributionfile',$metadatafields{'customdistributionfile'},'rights');
+	$intr_scrout.=&text_with_browse_field('Custom Distribution File','customdistributionfile',$metadatafields{'customdistributionfile'},'rights','',$readonly);
 	$intr_scrout.=&selectbox('Source Distribution','sourceavail',
-				 $defaultsourceoption,
+				 $defaultsourceoption,'',
 				 \&Apache::loncommon::source_copyrightdescription,
 				 (&Apache::loncommon::source_copyrightids));
 #	$intr_scrout.=&text_with_browse_field('Source Custom Distribution File','sourcerights',$metadatafields{'sourcerights'},'rights');
@@ -1501,6 +1626,18 @@ END
     return($scrout,0);
 }
 
+sub getnokey {
+    my ($includedir) = @_;
+    my $nokey={};
+    my $fh=Apache::File->new($includedir.'/un_keyword.tab');
+    while (<$fh>) {
+        my $word=$_;
+        chomp($word);
+        $nokey->{$word}=1;
+    }
+    return $nokey;
+}
+
 #########################################
 #########################################
 
@@ -1523,13 +1660,21 @@ Parameters:
 
 =item I<$distarget>
 
+=item I<$batch>
+
+=item I<$usebuffer>
+
 =back
 
 Returns:
 
 =over 4
 
-=item integer
+=item integer or array
+
+if $userbuffer arg is true, and if caller wants an array
+then the array ($output,$rtncode) will be returned, otherwise
+just the $rtncode will be returned.  $rtncode is an integer:
 
 0: fail
 1: success
@@ -1543,26 +1688,54 @@ Returns:
 #########################################
 sub phasetwo {
 
-    my ($r,$source,$target,$style,$distarget,$batch)=@_;
+    my ($r,$source,$target,$style,$distarget,$batch,$usebuffer)=@_;
     $source=~s/\/+/\//g;
     $target=~s/\/+/\//g;
 #
 # Unless trying to get rid of something, check name validity
 #
+    my $output;
     unless ($env{'form.obsolete'}) {
 	if ($target=~/(\_\_\_|\&\&\&|\:\:\:)/) {
-	    $r->print('<span class="LC_error">'.
+	    $output = '<span class="LC_error">'.
 		      &mt('Unsupported character combination [_1] in filename, FAIL.',"<tt>'.$1.'</tt>").
-		      '</span>');
-	    return 0;
+		      '</span>';
+            if ($usebuffer) {
+                if (wantarray) { 
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else {
+                $r->print($output);
+	        return 0;
+            }
 	}
 	unless ($target=~/\.(\w+)$/) {
-	    $r->print('<span class="LC_error">'.&mt('No valid extension found in filename, FAIL').'</span>');
-	    return 0;
+            $output = '<span class="LC_error">'.&mt('No valid extension found in filename, FAIL').'</span>'; 
+            if ($usebuffer) {
+                if (wantarray) {
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else {
+	        $r->print($output);
+	        return 0;
+            }
 	}
 	if ($target=~/\.(\d+)\.(\w+)$/) {
-	    $r->print('<span class="LC_error">'.&mt('Filename of resource contains internal version number. Cannot publish such resources, FAIL').'</span>');
-	    return 0;
+	    $output = '<span class="LC_error">'.&mt('Filename of resource contains internal version number. Cannot publish such resources, FAIL').'</span>';
+            if ($usebuffer) {
+                if (wantarray) {
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else { 
+                $r->print($output);
+	        return 0;
+            }
 	}
     }
 
@@ -1572,14 +1745,25 @@ sub phasetwo {
     $distarget=~s/\/+/\//g;
     my $logfile;
     unless ($logfile=Apache::File->new('>>'.$source.'.log')) {
-	$r->print(
-        '<span class="LC_error">'.
-		&mt('No write permission to user directory, FAIL').'</span>');
-        return 0;
+        $output = '<span class="LC_error">'.
+		  &mt('No write permission to user directory, FAIL').'</span>';
+        if ($usebuffer) {
+            if (wantarray) {
+                return ($output,0);
+            } else {
+                return 0;
+            }
+        } else {
+            return 0;
+        }
     }
     
     if ($source =~ /\.rights$/) {
-	$r->print('<p><span class="LC_warning">'.&mt('Warning: It can take up to 1 hour for rights changes to fully propagate.').'</span></p>');
+	$output = '<p><span class="LC_warning">'.&mt('Warning: It can take up to 1 hour for rights changes to fully propagate.').'</span></p>';
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = ''; 
+        }
     }
 
     print $logfile 
@@ -1589,7 +1773,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'};
@@ -1615,7 +1825,21 @@ sub phasetwo {
 	                                 $env{'user.domain'};
     $metadatafields{'authorspace'}=$cuname.':'.$cudom;
     $metadatafields{'domain'}=$cudom;
-    
+
+    my $crsauthor;
+    if ($env{'request.course.id'}) {
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        if ($distarget =~ m{^/res/$cdom/$cnum}) {
+            $crsauthor = 1;
+            my $default_rights = "/res/$cdom/$cnum/default.rights";
+            unless ($distarget eq $default_rights) {
+                $metadatafields{'copyright'} = 'custom';
+                $metadatafields{'customdistributionfile'} = $default_rights;
+            }
+        }
+    }
+
     my $allkeywords=$env{'form.addkey'};
     if (exists($env{'form.keywords'})) {
         if (ref($env{'form.keywords'})) {
@@ -1635,39 +1859,60 @@ sub phasetwo {
     if ($metadatafields{'copyright'} eq 'custom') {
 	my $file=$metadatafields{'customdistributionfile'};
 	unless ($file=~/\.rights$/) {
-            $r->print(
-                '<span class="LC_error">'.&mt('No valid custom distribution rights file specified, FAIL').
-		'</span>');
-	    return 0;
+            $output .= '<span class="LC_error">'.&mt('No valid custom distribution rights file specified, FAIL').
+		       '</span>';
+            if ($usebuffer) {
+                if (wantarray) {
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else {
+                $r->print($output);
+	        return 0;
+            }
         }
     }
     {
         print $logfile "\nWrite metadata file for ".$source;
         my $mfh;
         unless ($mfh=Apache::File->new('>'.$source.'.meta')) {
-            $r->print( 
-                '<span class="LC_error">'.&mt('Could not write metadata, FAIL').
-		'</span>');
-	    return 0;
-        }
-        foreach (sort keys %metadatafields) {
-            unless ($_=~/\./) {
-                my $unikey=$_;
+            $output .= '<span class="LC_error">'.&mt('Could not write metadata, FAIL').
+		       '</span>';
+            if ($usebuffer) {
+                if (wantarray) {
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else {
+                $r->print($output);
+	        return 0;
+            }
+        }
+        foreach my $field (sort(keys(%metadatafields))) {
+            unless ($field=~/\./) {
+                my $unikey=$field;
                 $unikey=~/^([A-Za-z]+)/;
                 my $tag=$1;
                 $tag=~tr/A-Z/a-z/;
                 print $mfh "\n\<$tag";
-                foreach (split(/\,/,$metadatakeys{$unikey})) {
-                    my $value=$metadatafields{$unikey.'.'.$_};
+                foreach my $item (split(/\,/,$metadatakeys{$unikey})) {
+                    my $value=$metadatafields{$unikey.'.'.$item};
                     $value=~s/\"/\'\'/g;
-                    print $mfh ' '.$_.'="'.$value.'"';
+                    print $mfh ' '.$item.'="'.$value.'"';
                 }
                 print $mfh '>'.
                     &HTML::Entities::encode($metadatafields{$unikey},'<>&"')
                         .'</'.$tag.'>';
             }
         }
-        $r->print('<p>'.&mt('Wrote Metadata').'</p>');
+
+        $output  .= '<p>'.&mt('Wrote Metadata').'</p>';
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
         print $logfile "\nWrote metadata";
     }
     
@@ -1676,17 +1921,27 @@ sub phasetwo {
     $metadatafields{'url'} = $distarget;
     $metadatafields{'version'} = 'current';
 
-    my ($error,$success) = &store_metadata(%metadatafields);
-    if ($success) {
-	$r->print('<p>'.&mt('Synchronized SQL metadata database').'</p>');
-	print $logfile "\nSynchronized SQL metadata database";
-    } else {
-	$r->print($error);
-	print $logfile "\n".$error;
+    unless ($crsauthor) {
+        my ($error,$success) = &store_metadata(%metadatafields);
+        if ($success) {
+	    $output .= '<p>'.&mt('Synchronized SQL metadata database').'</p>';
+	    print $logfile "\nSynchronized SQL metadata database";
+        } else {
+	    $output .= $error;
+	    print $logfile "\n".$error;
+        }
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
     }
 # --------------------------------------------- Delete author resource messages
     my $delresult=&Apache::lonmsg::del_url_author_res_msg($target); 
-    $r->print('<p>'.&mt('Removing error messages:').' '.$delresult.'</p>');
+    $output .= '<p>'.&mt('Removing error messages:').' '.$delresult.'</p>';
+    unless ($usebuffer) {
+        $r->print($output);
+        $output = '';
+    }
     print $logfile "\nRemoving error messages: $delresult";
 # ----------------------------------------------------------- Copy old versions
    
@@ -1700,9 +1955,18 @@ sub phasetwo {
         my $docroot = $Apache::lonnet::perlvar{'lonDocRoot'};
         unless ($srcd=~/^\Q$docroot\E\/res/) {
             print $logfile "\nPANIC: Target dir is ".$srcd;
-            $r->print(
-	 "<span class=\"LC_error\">".&mt('Invalid target directory, FAIL')."</span>");
-	    return 0;
+            $output .= 
+	 "<span class=\"LC_error\">".&mt('Invalid target directory, FAIL')."</span>";
+            if ($usebuffer) {
+                if (wantarray) {
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else {
+                $r->print($output);
+	        return 0;
+            }
         }
         opendir(DIR,$srcd);
         while ($filename=readdir(DIR)) {
@@ -1717,18 +1981,35 @@ sub phasetwo {
         }
         closedir(DIR);
         $maxversion++;
-        $r->print('<p>'.&mt('Creating old version [_1]',$maxversion).'</p>');
+        $output .= '<p>'.&mt('Creating old version [_1]',$maxversion).'</p>';
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
         print $logfile "\nCreating old version ".$maxversion."\n";
         
         my $copyfile=$srcd.'/'.$srcf.'.'.$maxversion.'.'.$srct;
         
         if (copy($target,$copyfile)) {
 	    print $logfile "Copied old target to ".$copyfile."\n";
-            $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied old target file')));
+            $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied old target file'));
+            unless ($usebuffer) {
+                $r->print($output);
+                $output = '';
+            }
         } else {
 	    print $logfile "Unable to write ".$copyfile.':'.$!."\n";
-            $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Failed to copy old target').", $!",1));
-	    return 0;
+            $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Failed to copy old target').", $!",1);
+            if ($usebuffer) {
+                if (wantarray) {
+                    return ($output,0);
+                } else {
+                    return 0;
+                }
+            } else {
+                $r->print($output); 
+	        return 0;
+            }
         }
         
 # --------------------------------------------------------------- Copy Metadata
@@ -1737,19 +2018,34 @@ sub phasetwo {
         
         if (copy($target.'.meta',$copyfile)) {
 	    print $logfile "Copied old target metadata to ".$copyfile."\n";
-            $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied old metadata')));
+            $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied old metadata'));
+            unless ($usebuffer) {
+                $r->print($output);
+                $output = '';
+            }
         } else {
 	    print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";
             if (-e $target.'.meta') {
-                $r->print(&Apache::lonhtmlcommon::confirm_success(
-                           &mt('Failed to write old metadata copy').", $!",1));
-		return 0;
+                $output .= &Apache::lonhtmlcommon::confirm_success(
+                               &mt('Failed to write old metadata copy').", $!",1);
+                if ($usebuffer) {
+                    if (wantarray) {
+                        return ($output,0);
+                    } else {
+                        return 0;
+                    }
+                } else {
+                    $r->print($output);
+                    return 0;
+                }
 	    }
         }
-        
-        
     } else {
-        $r->print('<p>'.&mt('Initial version').'</p>');
+        $output .= '<p>'.&mt('Initial version').'</p>';
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
         print $logfile "\nInitial version";
     }
 
@@ -1765,22 +2061,38 @@ sub phasetwo {
         if ((-e $path)!=1) {
             print $logfile "\nCreating directory ".$path;
             mkdir($path,0777);
-            $r->print('<p>'
-                     .&mt('Created directory [_1]'
-                         ,'<span class="LC_filename">'.$parts[$count].'</span>')
-                     .'</p>'
-            );
+            $output .= '<p>'
+                      .&mt('Created directory [_1]'
+                           ,'<span class="LC_filename">'.$parts[$count].'</span>')
+                      .'</p>';
+            unless ($usebuffer) {
+                $r->print($output);
+                $output = '';
+            }
         }
     }
     
     if (copy($source,$copyfile)) {
         print $logfile "\nCopied original source to ".$copyfile."\n";
-        $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied source file')));
+        $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied source file'));
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
     } else {
         print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";
-        $r->print(&Apache::lonhtmlcommon::confirm_success(
-	    &mt('Failed to copy source').", $!",1));
-	return 0;
+        $output .= &Apache::lonhtmlcommon::confirm_success(
+	    &mt('Failed to copy source').", $!",1);
+        if ($usebuffer) {
+            if (wantarray) {
+                return ($output,0);
+            } else {
+                return 0;
+            }
+        } else {
+            $r->print($output);
+            return 0;
+        }
     }
     
 # ---------------------------------------------- Delete local tmp-preview files
@@ -1791,22 +2103,33 @@ sub phasetwo {
     
     if (copy($source.'.meta',$copyfile)) {
         print $logfile "\nCopied original metadata to ".$copyfile."\n";
-        $r->print(&Apache::lonhtmlcommon::confirm_success(&mt('Copied metadata')));
+        $output .= &Apache::lonhtmlcommon::confirm_success(&mt('Copied metadata'));
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
     } else {
         print $logfile "\nUnable to write metadata ".$copyfile.':'.$!."\n";
-        $r->print(&Apache::lonhtmlcommon::confirm_success(
-                  &mt('Failed to write metadata copy').", $!",1));
-	return 0;
+        $output .= &Apache::lonhtmlcommon::confirm_success(
+                     &mt('Failed to write metadata copy').", $!",1);
+        if ($usebuffer) {
+            if (wantarray) {
+                return ($output,0);
+            } else {
+                return 0;
+            }
+        } else {
+            $r->print($output);
+            return 0;
+        }
+    }
+    unless ($usebuffer) {
+        $r->rflush;
     }
-    $r->rflush;
 
 # ------------------------------------------------------------- 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;
@@ -1817,7 +2140,11 @@ sub phasetwo {
 
 # ------------------------------------------------------------- Everything done
     $logfile->close();
-    $r->print('<p class="LC_success">'.&mt('Done').'</p>');
+    $output .= '<p class="LC_success">'.&mt('Done').'</p>';
+    unless ($usebuffer) {
+        $r->print($output);
+        $output = '';
+    }
 
 # ------------------------------------------------ Provide link to new resource
     unless ($batch) {
@@ -1826,7 +2153,7 @@ sub phasetwo {
         my $thissrcdir=$thissrc;
         $thissrcdir=~s/\/[^\/]+$/\//;
         
-        $r->print(
+        $output .= 
             &Apache::lonhtmlcommon::actionbox([
                 '<a href="'.$thisdistarget.'">'.
                 &mt('View Published Version').
@@ -1836,45 +2163,73 @@ sub phasetwo {
                 '</a>',
                 '<a href="'.$thissrcdir.'">'.
                 &mt('Back to Source Directory').
-                '</a>'])
-        );
+                '</a>']);
+        unless ($usebuffer) {
+            $r->print($output);
+            $output = '';
+        }
+    }
+
+    if ($usebuffer) {
+        if (wantarray) {
+            return ($output,1);
+        } else {
+            return 1;
+        }
+    } else {
+        if (wantarray) {
+            return ('',1);
+        } else {
+            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;
     }
-    return 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 (keys %courses) {
-	    print $logfile "\nNotifying course ".$_.':';
-	    my ($cdom,$cname)=split(/\_/,$_);
-	    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;
@@ -1883,12 +2238,11 @@ sub notify {
 #########################################
 
 sub batchpublish {
-    my ($r,$srcfile,$targetfile)=@_;
+    my ($r,$srcfile,$targetfile,$nokeyref,$usebuffer)=@_;
     #publication pollutes %env with form.* values
     my %oldenv=%env;
     $srcfile=~s/\/+/\//g;
     $targetfile=~s/\/+/\//g;
-    $srcfile=~s/\/+/\//g;
 
     my $docroot=$r->dir_config('lonDocRoot');
     my $thisdistarget=$targetfile;
@@ -1903,31 +2257,46 @@ sub batchpublish {
 
     my $thisembstyle=&Apache::loncommon::fileembstyle($thistype);
      
-    $r->print('<h2>'
+    my $output = '<h2>'
              .&mt('Publishing [_1]',&Apache::loncfile::display($srcfile))
-             .'</h2>'
-    );
+             .'</h2>';
+    unless ($usebuffer) {
+        $r->print($output);
+        $output = '';
+    }
 
 # phase one takes
 #  my ($source,$target,$style,$batch)=@_;
-    my ($outstring,$error)=&publish($srcfile,$targetfile,$thisembstyle,1);
-    $r->print('<p>'.$outstring.'</p>');
+    my ($outstring,$error)=&publish($srcfile,$targetfile,$thisembstyle,1,$nokeyref);
+    
+    if ($usebuffer) {
+        $output .= '<p>'.$outstring.'</p>';
+    } else {
+        $r->print('<p>'.$outstring.'</p>');
+    }
 # phase two takes
 # my ($source,$target,$style,$distarget,batch)=@_;
 # $env{'form.allmeta'},$env{'form.title'},$env{'form.author'},...
     if (!$error) {
-	$r->print('<p>');
-	&phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1);
-	$r->print('</p>');
+        if ($usebuffer) {
+	    my ($result,$error) = &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1,$usebuffer);
+	    $output .= '<p>'.$result.'</p>';
+        } else {
+            &phasetwo($r,$srcfile,$targetfile,$thisembstyle,$thisdistarget,1);
+        }
     }
     %env=%oldenv;
-    return '';
+    if ($usebuffer) {
+        return $output;
+    } else {
+        return '';
+    } 
 }
 
 #########################################
 
 sub publishdirectory {
-    my ($r,$fn,$thisdisfn)=@_;
+    my ($r,$fn,$thisdisfn,$nokeyref,$crsauthor)=@_;
     $fn=~s/\/+/\//g;
     $thisdisfn=~s/\/+/\//g;
     my $thisdisresdir=$thisdisfn;
@@ -1941,19 +2310,41 @@ 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 />');
+        unless ($crsauthor) {
+            $r->print(&common_access('dist',&mt('apply common copyright/distribution'),
+                                     ['default','domain','public','custom']).'<br />');
+        }
+        $r->print(&common_access('source',&mt('apply common source availability'),
+                                 ['closed','open']).
+                  '</fieldset>'
         );
         $r->print(&Apache::lonhtmlcommon::row_closure(1)
                  .&Apache::lonhtmlcommon::end_pick_box()
@@ -1964,7 +2355,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));
@@ -1973,17 +2369,18 @@ sub publishdirectory {
 		$cuid,$cgid,$crdev,$csize,
 		$catime,$cmtime,$cctime,
 		$cblksize,$cblocks)=stat($fn.'/'.$filename);
-	    
+
 	    my $extension='';
 	    if ($filename=~/\.(\w+)$/) { $extension=$1; }
 	    if ($cmode&$dirptr) {
 		if (($filename!~/^\./) && ($env{'form.pubrec'})) {
-		    &publishdirectory($r,$fn.'/'.$filename,$thisdisfn.'/'.$filename);
+		    &publishdirectory($r,$fn.'/'.$filename,$thisdisfn.'/'.$filename,$nokeyref,$crsauthor);
 		}
 	    } 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,
@@ -1991,22 +2388,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);
+		    &batchpublish($r,$fn.'/'.$filename,$resdir.'/'.$filename,$nokeyref);
 		} 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();
 	    }
@@ -2166,38 +2613,27 @@ sub handler {
 	return HTTP_NOT_FOUND;
     } 
 
-# -------------------------------- File is there and owned, init lookup tables.
-
-    %addid=();
-    
-    {
-	my $fh=Apache::File->new($r->dir_config('lonTabDir').'/addid.tab');
-	while (<$fh>=~/(\w+)\s+(\w+)/) {
-	    $addid{$1}=$2;
-	}
-    }
-
-    %nokey=();
-
-    {
-	my $fh=Apache::File->new($r->dir_config('lonIncludes').'/un_keyword.tab');
-	while (<$fh>) {
-	    my $word=$_;
-	    chomp($word);
-	    $nokey{$word}=1;
-	}
-    }
-
-# ---------------------------------------------------------- Start page output.
+# --------------------------------- File is there and owned, start page output
 
     &Apache::loncommon::content_type($r,'text/html');
     $r->send_http_header;
-    
+
     # Breadcrumbs
     &Apache::lonhtmlcommon::clear_breadcrumbs();
+    my $crumbtext = 'Authoring Space';
+    my $crumbhref = &Apache::loncommon::authorspace($fn);
+    my $crsauthor;
+    if ($env{'request.course.id'}) {
+        my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+        my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+        if ($crumbhref eq "/priv/$cdom/$cnum/") {
+            $crumbtext = 'Course Authoring Space';
+            $crsauthor = 1;
+        }
+    }
     &Apache::lonhtmlcommon::add_breadcrumb({
-        'text'  => 'Authoring Space',
-        'href'  => &Apache::loncommon::authorspace($fn),
+        'text'  => $crumbtext,
+        'href'  => $crumbhref,
     });
     &Apache::lonhtmlcommon::add_breadcrumb({
         'text'  => 'Resource Publication',
@@ -2207,7 +2643,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))
@@ -2217,10 +2699,11 @@ sub handler {
     my $thistarget=$fn;
     $thistarget=~s/^\/priv\//\/res\//;
     my $thisdistarget=&HTML::Entities::encode($thistarget,'<>&"');
+    my $nokeyref = &getnokey($r->dir_config('lonIncludes'));
 
     if ($fn=~/\/$/) {
 # -------------------------------------------------------- This is a directory
-	&publishdirectory($r,$docroot.$fn,$thisdisfn);
+	&publishdirectory($r,$docroot.$fn,$thisdisfn,$nokeyref,$crsauthor);
         $r->print(
             '<br /><br />'.
             &Apache::lonhtmlcommon::actionbox([
@@ -2293,7 +2776,7 @@ ENDDIFF
 	    }
 	    unless ($errorcount) {
 		my ($outstring,$error)=
-		    &publish($docroot.$fn,$docroot.$thistarget,$thisembstyle);
+		    &publish($docroot.$fn,$docroot.$thistarget,$thisembstyle,undef,$nokeyref);
 		$r->print($outstring);
 	    } else {
 		$r->print('<h3 class="LC_error">'.
@@ -2301,7 +2784,9 @@ ENDDIFF
 			  '</h3>');
 	    }
 	} else {
-	    &phasetwo($r,$docroot.$fn,$docroot.$thistarget,$thisembstyle,$thisdistarget); 
+	    my ($output,$error) = &phasetwo($r,$docroot.$fn,$docroot.$thistarget,
+                                            $thisembstyle,$thisdistarget);
+            $r->print($output);
 	}
     }
     $r->print(&Apache::loncommon::end_page());
@@ -2309,6 +2794,24 @@ ENDDIFF
     return OK;
 }
 
+BEGIN {
+
+# ----------------------------------- Read addid.tab
+    unless ($readit) {
+        %addid=();
+
+        {
+            my $tabdir = $Apache::lonnet::perlvar{'lonTabDir'};
+            my $fh=Apache::File->new($tabdir.'/addid.tab');
+            while (<$fh>=~/(\w+)\s+(\w+)/) {
+                $addid{$1}=$2;
+            }
+        }
+    }
+    $readit=1;
+}
+
+
 1;
 __END__