--- loncom/interface/loncommon.pm	2022/06/30 21:04:14	1.1385
+++ loncom/interface/loncommon.pm	2022/10/21 21:18:56	1.1392
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.1385 2022/06/30 21:04:14 raeburn Exp $
+# $Id: loncommon.pm,v 1.1392 2022/10/21 21:18:56 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -957,8 +957,8 @@ ENDSCRT
 }
 
 sub select_timezone {
-   my ($name,$selected,$onchange,$includeempty,$disabled)=@_;
-   my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";
+   my ($name,$selected,$onchange,$includeempty,$id,$disabled)=@_;
+   my $output='<select name="'.$name.'" '.$id.$onchange.$disabled.'>'."\n";
    if ($includeempty) {
        $output .= '<option value=""';
        if (($selected eq '') || ($selected eq 'local')) {
@@ -5998,7 +5998,7 @@ sub get_domainconf {
                                 foreach my $host (keys(%{$domconfig{'login'}{$key}})) {
                                     if (ref($domconfig{'login'}{$key}{$host}) eq 'HASH') {
                                         $designhash{$udom.'.login.'.$key.'_'.$host} = 1;
-                                        foreach my $item ('text','img','alt','url','title','notsso') {
+                                        foreach my $item ('text','img','alt','url','title','window','notsso') {
                                             $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item};
                                         }
                                     }
@@ -6674,8 +6674,38 @@ sub endbodytag {
     }
     if ( exists( $env{'internal.head.redirect'} ) ) {
         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
+            my ($endbodyjs,$idattr);
+            if ($env{'internal.head.to_opener'}) {
+                my $linkid = 'LC_continue_link';
+                $idattr = ' id="'.$linkid.'"';
+                my $redirect_for_js = &js_escape($env{'internal.head.redirect'});
+                $endbodyjs=<<ENDJS;
+<script type="text/javascript">
+// <![CDATA[
+function ebFunction(evt) {
+    evt.preventDefault();
+    var dest = '$redirect_for_js';
+    if (window.opener != null && !window.opener.closed) {
+        window.opener.location.href=dest;
+        window.close();
+    } else {
+        window.location.href=dest;
+    }
+    return false;
+}
+
+\$(document).ready(function () {
+  if (document.getElementById('$linkid')) {
+    var clickelem = document.getElementById('$linkid');
+    clickelem.addEventListener('click',ebFunction,false);
+  }
+});
+// ]]>
+</script>
+ENDJS
+            }
 	    $endbodytag=
-	        "<br /><a href=\"$env{'internal.head.redirect'}\">".
+	        "$endbodyjs<br /><a href=\"$env{'internal.head.redirect'}\"$idattr>".
 	        &mt('Continue').'</a>'.
 	        $endbodytag;
         }
@@ -8845,7 +8875,13 @@ Inputs: $title - optional title for the
                                    3- whether the side effect should occur
                            (side effect of setting 
                                $env{'internal.head.redirect'} to the url 
-                               redirected too)
+                               redirected to)
+                                   4- whether the redirect target should be
+                                      the opener of the current (pop-up)
+                                      window (side effect of setting
+                                      $env{'internal.head.to_opener'} to
+                                      1, if true.
+                                   5- whether encrypt check should be skipped
             domain         -> force to color decorate a page for a specific
                                domain
             function       -> force usage of a specific rolish color scheme
@@ -8908,15 +8944,45 @@ sub headtag {
         }
     }
     if (ref($args->{'redirect'})) {
-	my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
-	$url = &Apache::lonenc::check_encrypt($url);
+	my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}};
+        if (!$skip_enc_check) {
+            $url = &Apache::lonenc::check_encrypt($url);
+        }
 	if (!$inhibit_continue) {
 	    $env{'internal.head.redirect'} = $url;
 	}
-	$result.=<<ADDMETA
+	$result.=<<"ADDMETA";
 <meta http-equiv="pragma" content="no-cache" />
+ADDMETA
+        if ($to_opener) {
+            $env{'internal.head.to_opener'} = 1;
+            my $dest = &js_escape($url);
+            my $timeout = int($time * 1000);
+            $result .=<<"ENDJS";
+<script type="text/javascript">
+// <![CDATA[
+function LC_To_Opener() {
+    var dest = '$dest';
+    if (dest != '') {
+        if (window.opener != null && !window.opener.closed) {
+            window.opener.location.href=dest;
+            window.close();
+        } else {
+            window.location.href=dest;
+        }
+    }
+}
+\$(document).ready(function () {
+    setTimeout('LC_To_Opener()',$timeout);
+});
+// ]]>
+</script>
+ENDJS
+        } else {
+            $result.=<<"ADDMETA";
 <meta http-equiv="Refresh" content="$time; url=$url" />
 ADDMETA
+        }
     } else {
         unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
             my $requrl = $env{'request.uri'};
@@ -14221,7 +14287,9 @@ sub process_extracted_files {
                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                                       $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
                                       $title;
-                            if (($outer !~ /\D/) && ($mapinner{$outer} !~ /\D/) && ($newidx !~ /\D/)) {
+                            if (($outer !~ /\D/) &&
+                                (($mapinner{$outer} eq 'default') || ($mapinner{$outer} !~ /\D/)) &&
+                                ($newidx !~ /\D/)) {
                                 if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
                                     mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
                                 }
@@ -17381,7 +17449,7 @@ sub init_user_environment {
             my %is_adv = ( is_adv => $env{'user.adv'} );
             my %domdef = &Apache::lonnet::get_domain_defaults($domain);
 
-            foreach my $tool ('aboutme','blog','webdav','portfolio') {
+            foreach my $tool ('aboutme','blog','webdav','portfolio','timezone') {
                 $userenv{'availabletools.'.$tool} = 
                     &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
                                                       undef,\%userenv,\%domdef,\%is_adv);
@@ -18254,23 +18322,47 @@ sub needs_coursereinit {
         if ($blocked) {
             return ();
         }
-        my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
-        if ($lastchange > $env{'request.course.tied'}) {
-            my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
-            if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
-                my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
-                if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
-                    &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
-                                             $curr_reqd_hash{'internal.releaserequired'}});
-                    my ($switchserver,$switchwarning) =
-                        &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
-                                                $curr_reqd_hash{'internal.releaserequired'});
-                    if ($switchwarning ne '' || $switchserver ne '') {
-                        return ('switch',$switchwarning,$switchserver);
-                    }
+        my $update;
+        my $lastmainchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
+        my $lastsuppchange = &Apache::lonnet::get_suppchange($cdom,$cnum);
+        if ($lastmainchange > $env{'request.course.tied'}) {
+            my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
+            if ($needswitch) {
+                return ('switch',$switchwarning,$switchserver);
+            }
+            $update = 'main';
+        }
+        if ($lastsuppchange > $env{'request.course.suppupdated'}) {
+            if ($update) {
+                $update = 'both';
+            } else {
+                my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
+                if ($needswitch) {
+                    return ('switch',$switchwarning,$switchserver);
+                } else {
+                    $update = 'supp';
                 }
             }
-            return ('update');
+            return ($update);
+        }
+    }
+    return ();
+}
+
+sub switch_for_update {
+    my ($loncaparev,$cdom,$cnum) = @_;
+    my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
+    if ($curr_reqd_hash{'internal.releaserequired'} ne '') {
+        my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};
+        if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {
+            &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>
+                                    $curr_reqd_hash{'internal.releaserequired'}});
+            my ($switchserver,$switchwarning) =
+                &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},
+                                        $curr_reqd_hash{'internal.releaserequired'});
+            if ($switchwarning ne '' || $switchserver ne '') {
+                return ('switch',$switchwarning,$switchserver);
+            }
         }
     }
     return ();
@@ -18333,17 +18425,13 @@ sub update_content_constraints {
         @resparms = @LONCAPA::map::resparms;
         @zombies = @LONCAPA::map::zombies;
     }
-    my $suppmap = 'supplemental.sequence';
-    my ($suppcount,$supptools,$errors) = (0,0,0);
-    ($suppcount,$supptools,$errors) = &recurse_supplemental($cnum,$cdom,$suppmap,
-                                                            $suppcount,$supptools,$errors);
     if ($keeporder) {
         @LONCAPA::map::resources = @resources;
         @LONCAPA::map::order = @order;
         @LONCAPA::map::resparms = @resparms;
         @LONCAPA::map::zombies = @zombies;
     }
-    if ($supptools) {
+    if (&Apache::lonnet::count_supptools($cnum,$cdom,1)) {
         my ($major,$minor) = split(/\./,$checkcrsrestypes{'exttool'});
         if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
             ($reqdmajor,$reqdminor) = ($major,$minor);
@@ -18405,31 +18493,110 @@ sub parse_supplemental_title {
 }
 
 sub recurse_supplemental {
-    my ($cnum,$cdom,$suppmap,$numfiles,$numexttools,$errors) = @_;
-    if ($suppmap) {
+    my ($cnum,$cdom,$suppmap,$errors,$possdel,$suppids,$hiddensupp,$hidden) = @_;
+    if (($suppmap) && (ref($suppids) eq 'HASH') && (ref($hiddensupp) eq 'HASH')) {
+        my $mapnum;
+        if ($suppmap eq 'supplemental.sequence') {
+            $mapnum = 0;
+        } else {
+            ($mapnum) = ($suppmap =~ /^supplemental_(\d+)\.sequence$/);
+        }
         my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
         if ($fatal) {
             $errors ++;
         } else {
-            if ($#LONCAPA::map::resources > 0) {
-                foreach my $res (@LONCAPA::map::resources) {
-                    my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
+            my @order = @LONCAPA::map::order;
+            if (@order > 0) {
+                my @resources = @LONCAPA::map::resources;
+                my @resparms = @LONCAPA::map::resparms;
+                foreach my $idx (@order) {
+                    my ($title,$src,$ext,$type,$status)=split(/\:/,$resources[$idx]);
                     if (($src ne '') && ($status eq 'res')) {
+                        my $id = $mapnum.':'.$idx;
+                        push(@{$suppids->{$src}},$id);
+                        if (($hidden) || (&get_supp_parameter($resparms[$idx],'parameter_hiddenresource') =~ /^yes/i)) {
+                            $hiddensupp->{$id} = 1;
+                        }
                         if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
-                            ($numfiles,$numexttools,$errors) = &recurse_supplemental($cnum,$cdom,$1,
-                                                                   $numfiles,$numexttools,$errors);
+                            $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids,
+                                                            $hiddensupp,$hiddensupp->{$id});
                         } else {
-                            if ($src =~ m{^/adm/$cdom/$cnum/\d+/ext\.tool$}) {
-                                $numexttools ++;
+                            my $allowed;
+                            if (($env{'request.role.adv'}) || (!$hiddensupp->{$id})) {
+                                $allowed = 1;
+                            } elsif ($possdel) {
+                                foreach my $item (@{$suppids->{$src}}) {
+                                    next if ($item eq $id);
+                                    unless ($hiddensupp->{$item}) {
+                                       $allowed = 1;
+                                       last;
+                                    }
+                                }
+                                if ((!$allowed) && (exists($env{'httpref.'.$src}))) {
+                                    &Apache::lonnet::delenv('httpref.'.$src);
+                                }
+                            }
+                            if ($allowed && (!exists($env{'httpref.'.$src}))) {
+                                &Apache::lonnet::allowuploaded('/adm/coursedoc',$src);
                             }
-                            $numfiles ++;
                         }
                     }
                 }
             }
         }
     }
-    return ($numfiles,$numexttools,$errors);
+    return $errors;
+}
+
+sub set_supp_httprefs {
+    my ($cnum,$cdom,$supplemental,$possdel) = @_;
+    if (ref($supplemental) eq 'HASH') {
+        if ((ref($supplemental->{'ids'}) eq 'HASH') && (ref($supplemental->{'hidden'}) eq 'HASH')) {
+            foreach my $src (keys(%{$supplemental->{'ids'}})) {
+                next if ($src =~ /\.sequence$/);
+                if (ref($supplemental->{'ids'}->{$src}) eq 'ARRAY') {
+                    my $allowed;
+                    if ($env{'request.role.adv'}) {
+                        $allowed = 1;
+                    } else {
+                        foreach my $id (@{$supplemental->{'ids'}->{$src}}) {
+                            unless ($supplemental->{'hidden'}->{$id}) {
+                                $allowed = 1;
+                                last;
+                            }
+                        }
+                    }
+                    if (exists($env{'httpref.'.$src})) {
+                        if ($possdel) {
+                            unless ($allowed) {
+                                &Apache::lonnet::delenv('httpref.'.$src);
+                            }
+                        }
+                    } elsif ($allowed) {
+                        &Apache::lonnet::allowuploaded('/adm/coursedoc',$src);
+                    }
+                }
+            }
+            if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
+                &Apache::lonnet::appenv({'request.course.suppupdated' => time});
+            }
+        }
+    }
+}
+
+sub get_supp_parameter {
+    my ($resparm,$name)=@_;
+    return if ($resparm eq '');
+    my $value=undef;
+    my $ptype=undef;
+    foreach (split('&&&',$resparm)) {
+        my ($thistype,$thisname,$thisvalue)=split('___',$_);
+        if ($thisname eq $name) {
+            $value=$thisvalue;
+            $ptype=$thistype;
+        }
+    }
+    return $value;
 }
 
 sub symb_to_docspath {
@@ -18624,7 +18791,7 @@ sub create_captcha {
             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
                       '<span class="LC_nobreak">'.
                       &mt('Type in the letters/numbers shown below').'&nbsp;'.
-                      '<input type="text" size="5" name="code" value="" autocomplete="off" />'.
+                      '<input type="text" size="5" name="code" value="" autocomplete="new-password" />'.
                       '</span><br />'.
                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
             last;