Diff for /loncom/interface/loncommon.pm between versions 1.1075.2.141.2.12 and 1.1430

version 1.1075.2.141.2.12, 2021/01/04 17:29:30 version 1.1430, 2024/04/14 18:45:57
Line 61  use POSIX qw(strftime mktime); Line 61  use POSIX qw(strftime mktime);
 use Apache::lonmenu();  use Apache::lonmenu();
 use Apache::lonenc();  use Apache::lonenc();
 use Apache::lonlocal;  use Apache::lonlocal;
 use Apache::lonnet();  use Apache::lonnavmaps();
 use HTML::Entities;  use HTML::Entities;
 use Apache::lonhtmlcommon();  use Apache::lonhtmlcommon();
 use Apache::loncoursedata();  use Apache::loncoursedata();
Line 71  use Apache::lonuserutils(); Line 71  use Apache::lonuserutils();
 use Apache::lonuserstate();  use Apache::lonuserstate();
 use Apache::courseclassifier();  use Apache::courseclassifier();
 use LONCAPA qw(:DEFAULT :match);  use LONCAPA qw(:DEFAULT :match);
   use LONCAPA::ltiutils;
   use LONCAPA::LWPReq;
   use LONCAPA::map();
 use HTTP::Request;  use HTTP::Request;
 use DateTime::TimeZone;  use DateTime::TimeZone;
 use DateTime::Locale;  use DateTime::Locale;
 use Encode();  use Encode();
   use Text::Aspell;
 use Authen::Captcha;  use Authen::Captcha;
 use Captcha::reCAPTCHA;  use Captcha::reCAPTCHA;
 use JSON::DWIW;  use JSON::DWIW;
 use LWP::UserAgent;  
 use Crypt::DES;  use Crypt::DES;
 use DynaLoader; # for Crypt::DES version  use DynaLoader; # for Crypt::DES version
   use MIME::Lite;
   use MIME::Types;
 use File::Copy();  use File::Copy();
 use File::Path();  use File::Path();
 use String::CRC32();  use String::CRC32();
Line 169  sub ssi_with_retries { Line 174  sub ssi_with_retries {
 # ----------------------------------------------- Filetypes/Languages/Copyright  # ----------------------------------------------- Filetypes/Languages/Copyright
 my %language;  my %language;
 my %supported_language;  my %supported_language;
   my %supported_codes;
 my %latex_language; # For choosing hyphenation in <transl..>  my %latex_language; # For choosing hyphenation in <transl..>
 my %latex_language_bykey; # for choosing hyphenation from metadata  my %latex_language_bykey; # for choosing hyphenation from metadata
 my %cprtag;  my %cprtag;
Line 203  BEGIN { Line 209  BEGIN {
             while (my $line = <$fh>) {              while (my $line = <$fh>) {
                 next if ($line=~/^\#/);                  next if ($line=~/^\#/);
                 chomp($line);                  chomp($line);
                 my ($key,$two,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));                  my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
                 $language{$key}=$val.' - '.$enc;                  $language{$key}=$val.' - '.$enc;
                 if ($sup) {                  if ($sup) {
                     $supported_language{$key}=$sup;                      $supported_language{$key}=$sup;
       $supported_codes{$key}   = $code;
                 }                  }
  if ($latex) {   if ($latex) {
     $latex_language_bykey{$key} = $latex;      $latex_language_bykey{$key} = $latex;
     $latex_language{$two} = $latex;      $latex_language{$code} = $latex;
  }   }
             }              }
             close($fh);              close($fh);
Line 430  sub studentbrowser_javascript { Line 437  sub studentbrowser_javascript {
 <script type="text/javascript" language="Javascript">  <script type="text/javascript" language="Javascript">
 // <![CDATA[  // <![CDATA[
     var stdeditbrowser;      var stdeditbrowser;
     function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadv) {      function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadv,uident) {
         var url = '/adm/pickstudent?';          var url = '/adm/pickstudent?';
         var filter;          var filter;
  if (!ignorefilter) {   if (!ignorefilter) {
Line 451  sub studentbrowser_javascript { Line 458  sub studentbrowser_javascript {
             }              }
         }          }
         if ((courseadv == 'only') || (courseadv == 'none')) { url+="&courseadv="+courseadv; }          if ((courseadv == 'only') || (courseadv == 'none')) { url+="&courseadv="+courseadv; }
           if (uident !== '') { url+="&identelement="+uident; } 
         var title = 'Student_Browser';          var title = 'Student_Browser';
         var options = 'scrollbars=1,resizable=1,menubar=0';          var options = 'scrollbars=1,resizable=1,menubar=0';
         options += ',width=700,height=600';          options += ',width=700,height=600';
Line 482  ENDRESBRW Line 490  ENDRESBRW
 }  }
   
 sub selectstudent_link {  sub selectstudent_link {
    my ($form,$unameele,$udomele,$courseadv,$clickerid)=@_;     my ($form,$unameele,$udomele,$courseadv,$clickerid,$identelem)=@_;
    my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".     my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
                       &Apache::lonhtmlcommon::entity_encode($unameele)."','".                        &Apache::lonhtmlcommon::entity_encode($unameele)."','".
                       &Apache::lonhtmlcommon::entity_encode($udomele)."'";                        &Apache::lonhtmlcommon::entity_encode($udomele)."'";
Line 499  sub selectstudent_link { Line 507  sub selectstudent_link {
            $callargs .= ",'','','$courseadv'";             $callargs .= ",'','','$courseadv'";
        } elsif ($courseadv eq 'condition') {         } elsif ($courseadv eq 'condition') {
            $callargs .= ",'','','$courseadv'";             $callargs .= ",'','','$courseadv'";
          } elsif ($identelem ne '') {
              $callargs .= ",'','',''";
          }
          if ($identelem ne '') {
              $callargs .= ",'".&Apache::lonhtmlcommon::entity_encode($identelem)."'";
        }         }
        return '<span class="LC_nobreak">'.         return '<span class="LC_nobreak">'.
               '<a href="javascript:openstdbrowser('.$callargs.');">'.                '<a href="javascript:openstdbrowser('.$callargs.');">'.
Line 689  if (!Array.prototype.indexOf) { Line 702  if (!Array.prototype.indexOf) {
         var n = 0;          var n = 0;
         if (arguments.length > 0) {          if (arguments.length > 0) {
             n = Number(arguments[1]);              n = Number(arguments[1]);
             if (n !== n) { // shortcut for verifying if it's NaN              if (n !== n) { // shortcut for verifying if it is NaN
                 n = 0;                  n = 0;
             } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {              } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
                 n = (n > 0 || -1) * Math.floor(Math.abs(n));                  n = (n > 0 || -1) * Math.floor(Math.abs(n));
Line 890  sub selectcourse_link { Line 903  sub selectcourse_link {
    my $linktext = &mt('Select Course');     my $linktext = &mt('Select Course');
    if ($selecttype eq 'Community') {     if ($selecttype eq 'Community') {
        $linktext = &mt('Select Community');         $linktext = &mt('Select Community');
      } elsif ($selecttype eq 'Placement') {
          $linktext = &mt('Select Placement Test'); 
    } elsif ($selecttype eq 'Course/Community') {     } elsif ($selecttype eq 'Course/Community') {
        $linktext = &mt('Select Course/Community');         $linktext = &mt('Select Course/Community');
        $type = '';         $type = '';
Line 925  sub check_uncheck_jscript { Line 940  sub check_uncheck_jscript {
 function checkAll(field) {  function checkAll(field) {
     if (field.length > 0) {      if (field.length > 0) {
         for (i = 0; i < field.length; i++) {          for (i = 0; i < field.length; i++) {
             if (!field[i].disabled) {              if (!field[i].disabled) { 
                 field[i].checked = true;                  field[i].checked = true;
             }              }
         }          }
     } else {      } else {
         if (!field.disabled) {          if (!field.disabled) { 
             field.checked = true;              field.checked = true;
         }          }
     }      }
Line 950  ENDSCRT Line 965  ENDSCRT
 }  }
   
 sub select_timezone {  sub select_timezone {
    my ($name,$selected,$onchange,$includeempty,$disabled)=@_;     my ($name,$selected,$onchange,$includeempty,$id,$disabled)=@_;
    my $output='<select name="'.$name.'" '.$onchange.$disabled.'>'."\n";     my $output='<select name="'.$name.'" '.$id.$onchange.$disabled.'>'."\n";
    if ($includeempty) {     if ($includeempty) {
        $output .= '<option value=""';         $output .= '<option value=""';
        if (($selected eq '') || ($selected eq 'local')) {         if (($selected eq '') || ($selected eq 'local')) {
Line 1006  sub select_datelocale { Line 1021  sub select_datelocale {
                 }                  }
                 $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});                  $locale_names{$id} = Encode::encode('UTF-8',$locale_names{$id});
                 push(@possibles,$id);                  push(@possibles,$id);
             }              } 
         }          }
     }      }
     foreach my $item (sort(@possibles)) {      foreach my $item (sort(@possibles)) {
Line 1042  sub select_language { Line 1057  sub select_language {
   
 =pod  =pod
   
   
   =item * &list_languages()
   
   Returns an array reference that is suitable for use in language prompters.
   Each array element is itself a two element array.  The first element
   is the language code.  The second element a descsriptiuon of the 
   language itself.  This is suitable for use in e.g.
   &Apache::edit::select_arg (once dereferenced that is).
   
   =cut 
   
   sub list_languages {
       my @lang_choices;
   
       foreach my $id (&languageids()) {
    my $code = &supportedlanguagecode($id);
    if ($code) {
       my $selector    = $supported_codes{$id};
       my $description = &plainlanguagedescription($id);
       push(@lang_choices, [$selector, $description]);
    }
       }
       return \@lang_choices;
   }
   
   =pod
   
 =item * &linked_select_forms(...)  =item * &linked_select_forms(...)
   
 linked_select_forms returns a string containing a <script></script> block  linked_select_forms returns a string containing a <script></script> block
Line 1074  linked_select_forms takes the following Line 1116  linked_select_forms takes the following
 =item * $onchangesecond, additional javascript call to execute for an onchange  =item * $onchangesecond, additional javascript call to execute for an onchange
         event for the second <select> tag          event for the second <select> tag
   
   =item * $suffix, to differentiate separate uses of select2data javascript
           objects in a page.
   
 =back   =back 
   
 Below is an example of such a hash.  Only the 'text', 'default', and   Below is an example of such a hash.  Only the 'text', 'default', and 
Line 1128  sub linked_select_forms { Line 1173  sub linked_select_forms {
         $hashref,          $hashref,
         $menuorder,          $menuorder,
         $onchangefirst,          $onchangefirst,
         $onchangesecond          $onchangesecond,
           $suffix
         ) = @_;          ) = @_;
     my $second = "document.$formname.$secondselectname";      my $second = "document.$formname.$secondselectname";
     my $first = "document.$formname.$firstselectname";      my $first = "document.$formname.$firstselectname";
Line 1136  sub linked_select_forms { Line 1182  sub linked_select_forms {
     my $result = '';      my $result = '';
     $result.='<script type="text/javascript" language="JavaScript">'."\n";      $result.='<script type="text/javascript" language="JavaScript">'."\n";
     $result.="// <![CDATA[\n";      $result.="// <![CDATA[\n";
     $result.="var select2data = new Object();\n";      $result.="var select2data${suffix} = new Object();\n";
     $" = '","';      $" = '","';
     my $debug = '';      my $debug = '';
     foreach my $s1 (sort(keys(%$hashref))) {      foreach my $s1 (sort(keys(%$hashref))) {
         $result.="select2data.d_$s1 = new Object();\n";                  $result.="select2data${suffix}['d_$s1'] = new Object();\n";        
         $result.="select2data.d_$s1.def = new String('".          $result.="select2data${suffix}['d_$s1'].def = new String('".
             $hashref->{$s1}->{'default'}."');\n";              $hashref->{$s1}->{'default'}."');\n";
         $result.="select2data.d_$s1.values = new Array(";          $result.="select2data${suffix}['d_$s1'].values = new Array(";
         my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));          my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
         if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {          if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
             @s2values = @{$hashref->{$s1}->{'order'}};              @s2values = @{$hashref->{$s1}->{'order'}};
         }          }
         $result.="\"@s2values\");\n";          $result.="\"@s2values\");\n";
         $result.="select2data.d_$s1.texts = new Array(";                  $result.="select2data${suffix}['d_$s1'].texts = new Array(";        
         my @s2texts;          my @s2texts;
         foreach my $value (@s2values) {          foreach my $value (@s2values) {
             push(@s2texts, $hashref->{$s1}->{'select2'}->{$value});              push(@s2texts, $hashref->{$s1}->{'select2'}->{$value});
Line 1159  sub linked_select_forms { Line 1205  sub linked_select_forms {
     $"=' ';      $"=' ';
     $result.= <<"END";      $result.= <<"END";
   
 function select1_changed() {  function select1${suffix}_changed() {
     // Determine new choice      // Determine new choice
     var newvalue = "d_" + $first.value;      var newvalue = "d_" + $first.options[$first.selectedIndex].value;
     // update select2      // update select2
     var values     = select2data[newvalue].values;      var values     = select2data${suffix}[newvalue].values;
     var texts      = select2data[newvalue].texts;      var texts      = select2data${suffix}[newvalue].texts;
     var select2def = select2data[newvalue].def;      var select2def = select2data${suffix}[newvalue].def;
     var i;      var i;
     // out with the old      // out with the old
     for (i = 0; i < $second.options.length; i++) {      $second.options.length = 0;
         $second.options[i] = null;      // in with the new
     }  
     // in with the nuclear  
     for (i=0;i<values.length; i++) {      for (i=0;i<values.length; i++) {
         $second.options[i] = new Option(values[i]);          $second.options[i] = new Option(values[i]);
         $second.options[i].value = values[i];          $second.options[i].value = values[i];
Line 1185  function select1_changed() { Line 1229  function select1_changed() {
 </script>  </script>
 END  END
     # output the initial values for the selection lists      # output the initial values for the selection lists
     $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed();$onchangefirst\">\n";      $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1${suffix}_changed();$onchangefirst\">\n";
     my @order = sort(keys(%{$hashref}));      my @order = sort(keys(%{$hashref}));
     if (ref($menuorder) eq 'ARRAY') {      if (ref($menuorder) eq 'ARRAY') {
         @order = @{$menuorder};          @order = @{$menuorder};
Line 1196  END Line 1240  END
         $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";          $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
     }      }
     $result .= "</select>\n";      $result .= "</select>\n";
     my %select2 = %{$hashref->{$firstdefault}->{'select2'}};      my %select2;
       if (ref($hashref->{$firstdefault}) eq 'HASH') {
           if (ref($hashref->{$firstdefault}->{'select2'}) eq 'HASH') {
               %select2 = %{$hashref->{$firstdefault}->{'select2'}};
           }
       }
     $result .= $middletext;      $result .= $middletext;
     $result .= "<select size=\"1\" name=\"$secondselectname\"";      $result .= "<select size=\"1\" name=\"$secondselectname\"";
     if ($onchangesecond) {      if ($onchangesecond) {
Line 1221  END Line 1270  END
   
 =pod  =pod
   
 =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)  =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid,$links_target)
   
 Returns a string corresponding to an HTML link to the given help  Returns a string corresponding to an HTML link to the given help
 $topic, where $topic corresponds to the name of a .tex file in  $topic, where $topic corresponds to the name of a .tex file in
Line 1245  $imgid is the id of the img tag used for Line 1294  $imgid is the id of the img tag used for
 used in a javascript call to switch the image src.  See   used in a javascript call to switch the image src.  See 
 lonhtmlcommon::htmlareaselectactive() for an example.  lonhtmlcommon::htmlareaselectactive() for an example.
   
   $links_target will optionally be set to a target (_top, _parent or _self).
   
 =cut  =cut
   
 sub help_open_topic {  sub help_open_topic {
     my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;      my ($topic, $text, $stayOnPage, $width, $height, $imgid, $links_target) = @_;
     $text = "" if (not defined $text);      $text = "" if (not defined $text);
     $stayOnPage = 0 if (not defined $stayOnPage);      $stayOnPage = 0 if (not defined $stayOnPage);
     $width = 500 if (not defined $width);      $width = 500 if (not defined $width);
Line 1262  sub help_open_topic { Line 1313  sub help_open_topic {
     $topic=~s/\W/\_/g;      $topic=~s/\W/\_/g;
   
     if (!$stayOnPage) {      if (!$stayOnPage) {
         if ($env{'browser.mobile'}) {   $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
     $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";  
         } else {  
             $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";  
         }  
     } elsif ($stayOnPage eq 'popup') {      } elsif ($stayOnPage eq 'popup') {
         $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";          $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
     } else {      } else {
Line 1274  sub help_open_topic { Line 1321  sub help_open_topic {
     }      }
   
     # Add the text      # Add the text
     if ($text ne "") {      my $target = ' target="_top"';
       if ($links_target) {
           $target = ' target="'.$links_target.'"';
       } elsif ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
                (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
           $target = '';
       }
       if ($text ne "") {
  $template.='<span class="LC_help_open_topic">'   $template.='<span class="LC_help_open_topic">'
                   .'<a target="_top" href="'.$link.'">'                    .'<a'.$target.' href="'.$link.'">'
                   .$text.'</a>';                    .$text.'</a>';
     }      }
   
Line 1286  sub help_open_topic { Line 1340  sub help_open_topic {
     if ($imgid ne '') {      if ($imgid ne '') {
         $imgid = ' id="'.$imgid.'"';          $imgid = ' id="'.$imgid.'"';
     }      }
     $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'      $template.=' <a'.$target.' href="'.$link.'" title="'.$title.'">'
               .'<img src="'.$helpicon.'" border="0"'                .'<img src="'.$helpicon.'" border="0"'
               .' alt="'.&mt('Help: [_1]',$topic).'"'                .' alt="'.&mt('Help: [_1]',$topic).'"'
               .' title="'.$title.'" style="vertical-align:middle;"'.$imgid                 .' title="'.$title.'" style="vertical-align:middle;"'.$imgid 
Line 1315  sub helpLatexCheatsheet { Line 1369  sub helpLatexCheatsheet {
   .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)    .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
   .'</span>';    .'</span>';
     unless ($not_author) {      unless ($not_author) {
         $out .= ' <span>'          $out .= '<span>'
        .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)                 .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
        .'</span> <span>'                 .'</span> <span>'
                .&help_open_topic('Authoring_Multilingual_Problems',&mt('Languages'),$stayOnPage,undef,600)                 .&help_open_topic('Authoring_Multilingual_Problems',&mt('Languages'),$stayOnPage,undef,600)
                .'</span>';         .'</span>';
     }      }
     $out .= '</span>'; # End cheatsheet      $out .= '</span>'; # End cheatsheet
     return $out;      return $out;
Line 1359  ENDOUTPUT Line 1413  ENDOUTPUT
   
 # now just updates the help link and generates a blue icon  # now just updates the help link and generates a blue icon
 sub help_open_menu {  sub help_open_menu {
     my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text)       my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text,$links_target) 
  = @_;       = @_;    
     $stayOnPage = 1;      $stayOnPage = 1;
     my $output;      my $output;
     if ($component_help) {      if ($component_help) {
  if (!$text) {   if (!$text) {
     $output=&help_open_topic($component_help,undef,$stayOnPage,      $output=&help_open_topic($component_help,undef,$stayOnPage,
        $width,$height);         $width,$height,'',$links_target);
  } else {   } else {
     my $help_text;      my $help_text;
     $help_text=&unescape($topic);      $help_text=&unescape($topic);
     $output='<table><tr><td>'.      $output='<table><tr><td>'.
  &help_open_topic($component_help,$help_text,$stayOnPage,   &help_open_topic($component_help,$help_text,$stayOnPage,
  $width,$height).'</td></tr></table>';   $width,$height,'',$links_target).'</td></tr></table>';
  }   }
     }      }
     my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);      my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
Line 1380  sub help_open_menu { Line 1434  sub help_open_menu {
 }  }
   
 sub top_nav_help {  sub top_nav_help {
     my ($text) = @_;      my ($text,$linkattr) = @_;
     $text = &mt($text);      $text = &mt($text);
     my $stay_on_page;      my $stay_on_page = 1;
     unless ($env{'environment.remote'} eq 'on') {  
         $stay_on_page = 1;  
     }  
     my ($link,$banner_link);      my ($link,$banner_link);
     unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {      unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
         $link = ($stay_on_page) ? "javascript:helpMenu('display')"          $link = ($stay_on_page) ? "javascript:helpMenu('display')"
Line 1396  sub top_nav_help { Line 1448  sub top_nav_help {
     if ($link) {      if ($link) {
         return <<"END";          return <<"END";
 $banner_link  $banner_link
 <a href="$link" title="$title">$text</a>  <a href="$link" title="$title" $linkattr>$text</a>
 END  END
     } else {      } else {
         return '&nbsp;'.$text.'&nbsp;';          return '&nbsp;'.$text.'&nbsp;';
Line 1417  sub help_menu_js { Line 1469  sub help_menu_js {
  'js_ready'    => 1,   'js_ready'    => 1,
                                         'use_absolute' => $httphost,                                          'use_absolute' => $httphost,
  'add_entries' => {   'add_entries' => {
     'border' => '0',      'border' => '0', 
     'rows'   => "110,*",},});      'rows'   => "110,*",},});
     my $end_page =      my $end_page =
         &Apache::loncommon::end_page({'frameset' => 1,          &Apache::loncommon::end_page({'frameset' => 1,
Line 1481  sub help_open_bug { Line 1533  sub help_open_bug {
     {      {
  $link = $url;   $link = $url;
     }      }
   
       my $target = '_top';
       if ((($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) ||
           (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'} eq '_self'))) {
           $target = '_blank';
       }
   
     # Add the text      # Add the text
     if ($text ne "")      if ($text ne "")
     {      {
  $template .=    $template .= 
   "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".    "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
   "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";    "<td bgcolor='#FF5555'><a target=\"$target\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
     }      }
   
     # Add the graphic      # Add the graphic
     my $title = &mt('Report a Bug');      my $title = &mt('Report a Bug');
     my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");      my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
     $template .= <<"ENDTEMPLATE";      $template .= <<"ENDTEMPLATE";
  <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>   <a target="$target" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
 ENDTEMPLATE  ENDTEMPLATE
     if ($text ne '') { $template.='</td></tr></table>' };      if ($text ne '') { $template.='</td></tr></table>' };
     return $template;      return $template;
Line 1705  the id of the element to resize, second Line 1764  the id of the element to resize, second
 surrounds everything that comes after the textarea, this routine needs  surrounds everything that comes after the textarea, this routine needs
 to be attached to the <body> for the onload and onresize events.  to be attached to the <body> for the onload and onresize events.
   
 =back  
   
 =cut  =cut
   
 sub resize_textarea_js {  sub resize_textarea_js {
Line 1759  RESIZE Line 1816  RESIZE
 }  }
   
 sub colorfuleditor_js {  sub colorfuleditor_js {
       my $browse_or_search;
       my $respath;
       my ($cnum,$cdom) = &crsauthor_url();
       if ($cnum) {
           $respath = "/res/$cdom/$cnum/";
           my %js_lt = &Apache::lonlocal::texthash(
               sunm => 'Sub-directory name',
               save => 'Save page to make this permanent',
           );
           &js_escape(\%js_lt);
           my $showfile_js = &show_crsfiles_js();
           $browse_or_search = <<"END";
   
       $showfile_js
   
       function toggleChooser(form,element,titleid,only,search) {
           var disp = 'none';
           if (document.getElementById('chooser_'+element)) {
               var curr = document.getElementById('chooser_'+element).style.display;
               if (curr == 'none') {
                   disp='inline';
                   if (form.elements['chooser_'+element].length) {
                       for (var i=0; i<form.elements['chooser_'+element].length; i++) {
                           form.elements['chooser_'+element][i].checked = false;
                       }
                   }
                   toggleResImport(form,element);
               }
               document.getElementById('chooser_'+element).style.display = disp;
               var dirsel = '';
               var filesel = '';
               if (document.getElementById('chooser_'+element+'_crsres')) {
                   var currcrsres = document.getElementById('chooser_'+element+'_crsres').style.display;
                   if (currcrsres == 'none') {
                       dirsel = 'coursepath_'+element;
                       var filesel = 'coursefile_'+element;
                       var include;
                       if (document.getElementById('crsres_include_'+element)) {
                           include = document.getElementById('crsres_include_'+element).value;
                       }
                       populateCrsSelects(form,dirsel,filesel,1,include,1,0,1,1,0);
                   }
               }
               if (document.getElementById('chooser_'+element+'_upload')) {
                   var currcrsupload = document.getElementById('chooser_'+element+'_upload').style.display;
                   if (currcrsupload == 'none') {
                       dirsel = 'crsauthorpath_'+element;
                       filesel = '';
                       populateCrsSelects(form,dirsel,filesel,0,'',1,0,1,0,1);
                   }
               }
           }
       }
   
       function toggleCrsFile(form,element) {
           if (document.getElementById('chooser_'+element+'_crsres')) {
               var curr = document.getElementById('chooser_'+element+'_crsres').style.display;
               if (curr == 'none') {
                   if (document.getElementById('coursepath_'+element)) {
                       var numdirs;
                       if (document.getElementById('coursepath_'+element).length) {
                           numdirs = document.getElementById('coursepath_'+element).length;
                       }
                       if ((document.getElementById('hascrsres_'+element)) &&
                           (document.getElementById('nocrsres_'+element))) {
                           if (numdirs) {
                               document.getElementById('hascrsres_'+element).style.display='inline-block';
                               document.getElementById('nocrsres_'+element).style.display='none';
                           } else {
                               document.getElementById('hascrsres_'+element).style.display='none';
                               document.getElementById('nocrsres_'+element).style.display='inline-block';
                           }
                       }
                       form.elements['coursepath_'+element].selectedIndex = 0;
                       if (numdirs > 1) {
                           var selelem = form.elements['coursefile_'+element];
                           var i, len = selelem.options.length -1;
                           if (len >=0) {
                               for (i = len; i >= 0; i--) {
                                   selelem.remove(i);
                               }
                               selelem.options[0] = new Option('','');
                           }
                       }
                   }
               }
               document.getElementById('chooser_'+element+'_crsres').style.display = 'block';
           }
           if (document.getElementById('chooser_'+element+'_upload')) {
               document.getElementById('chooser_'+element+'_upload').style.display = 'none';
               if (document.getElementById('uploadcrsres_'+element)) {
                   document.getElementById('uploadcrsres_'+element).value = '';
               }
           }
           return;
       }
   
       function toggleCrsUpload(form,element) {
           if (document.getElementById('chooser_'+element+'_crsres')) {
               document.getElementById('chooser_'+element+'_crsres').style.display = 'none';
           }
           if (document.getElementById('chooser_'+element+'_upload')) {
               var curr = document.getElementById('chooser_'+element+'_upload').style.display;
               if (curr == 'none') {
                   form.elements['newsubdir_'+element][0].checked = true;
                   toggleNewsubdir(form,element);
                   document.getElementById('chooser_'+element+'_upload').style.display = 'block';
                   if (document.getElementById('uploadcrsres_'+element)) {
                       document.getElementById('uploadcrsres_'+element).value = '';
                   }
               }
           }
           return;
       }
   
       function toggleResImport(form,element) {
           var choices = new Array('crsres','upload');
           for (var i=0; i<choices.length; i++) {
               if (document.getElementById('chooser_'+element+'_'+choices[i])) {
                   document.getElementById('chooser_'+element+'_'+choices[i]).style.display = 'none';
               }
           }
       }
   
       function toggleNewsubdir(form,element) {
           var newsub = form.elements['newsubdir_'+element];
           if (newsub) {
               if (newsub.length) {
                   for (var j=0; j<newsub.length; j++) {
                       if (newsub[j].checked) {
                           if (document.getElementById('newsubdirname_'+element)) {
                               if (newsub[j].value == '1') {
                                   document.getElementById('newsubdirname_'+element).type = "text";
                                   if (document.getElementById('newsubdir_'+element)) {
                                       document.getElementById('newsubdir_'+element).innerHTML = '<br />$js_lt{sunm}';
                                   }
                               } else {
                                   document.getElementById('newsubdirname_'+element).type = "hidden";
                                   document.getElementById('newsubdirname_'+element).value = "";
                                   document.getElementById('newsubdir_'+element).innerHTML = "";
                               }
                           }
                           break; 
                       }
                   }
               }
           }
       }
   
       function updateCrsFile(form,element) {
           var directory = form.elements['coursepath_'+element];
           var filename = form.elements['coursefile_'+element];
           var path = directory.options[directory.selectedIndex].value;
           var file = filename.options[filename.selectedIndex].value;
           if (file != '') {
               form.elements[element].value = '$respath';
               if (path == '/') {
                   form.elements[element].value += file;
               } else {
                   form.elements[element].value += path+'/'+file;
               }
               unClean();
               if (document.getElementById('previewimg_'+element)) {
                   document.getElementById('previewimg_'+element).src = form.elements[element].value;
                   var newsrc = document.getElementById('previewimg_'+element).src; 
               }
               if (document.getElementById('showimg_'+element)) {
                   document.getElementById('showimg_'+element).innerHTML = '($js_lt{save})';
               }
           }
           toggleChooser(form,element);
           return;
       }
   
       function uploadDone(suffix,name) {
           if (name) {
       document.forms["lonhomework"].elements[suffix].value = name;
               unClean();
               toggleChooser(document.forms["lonhomework"],suffix);
           }
       }
   
   \$(document).ready(function(){
   
       \$(document).delegate('form :submit', 'click', function( event ) {
           if ( \$( this ).hasClass( "LC_uploadcrsres" ) ) {
               var buttonId = this.id;
               var suffix = buttonId.toString();
               suffix = suffix.replace(/^crsupload_/,'');
               event.preventDefault();
               document.lonhomework.target = 'crsupload_target_'+suffix;
               document.lonhomework.action = '/adm/coursepub?LC_uploadcrsres='+suffix;
               \$(this.form).submit();
               document.lonhomework.target = '';
               if (document.getElementById('crsuploadto_'+suffix)) {
                   document.lonhomework.action = document.getElementById('crsuploadto_'+suffix).value;
               }
               return false;
           }
       });
   });
   END
       }
     return <<"COLORFULEDIT"      return <<"COLORFULEDIT"
 <script type="text/javascript">  <script type="text/javascript">
 // <![CDATA[>  // <![CDATA[>
Line 1804  sub colorfuleditor_js { Line 2064  sub colorfuleditor_js {
             }              }
   
             // only iterate whole storage if nothing to override              // only iterate whole storage if nothing to override
             if(localStorage.getItem(key) == null){              if(localStorage.getItem(key) == null){        
   
                 // prevent storage from growing large                  // prevent storage from growing large
                 if(localStorage.length > 50){                  if(localStorage.length > 50){
                     var regex_getTimestamp = /^(?:\d)+;/;                      var regex_getTimestamp = /^(?:\d)+;/;
                     var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));                      var oldest_timestamp = regex_getTimestamp.exec(localStorage.key(0));
                     var oldest_key;                      var oldest_key;
                       
                     for(var i = 1; i < localStorage.length; i++){                      for(var i = 1; i < localStorage.length; i++){
                         if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {                          if (regex_getTimestamp.exec(localStorage.key(i)) < oldest_timestamp) {
                             oldest_key = localStorage.key(i);                              oldest_key = localStorage.key(i);
Line 1841  sub colorfuleditor_js { Line 2101  sub colorfuleditor_js {
                 pairs = valueArr[i].split(',');                  pairs = valueArr[i].split(',');
                 elements = document.getElementsByName(pairs[0]);                  elements = document.getElementsByName(pairs[0]);
   
                 for (var j = 0; j < elements.length; j++){                  for (var j = 0; j < elements.length; j++){  
                     elements[j].style.display = pairs[1];                      elements[j].style.display = pairs[1];
                     if (pairs[1] == "none"){                      if (pairs[1] == "none"){
                         var regex_id = /([_\\d]+)\$/;                          var regex_id = /([_\\d]+)\$/;
Line 1854  sub colorfuleditor_js { Line 2114  sub colorfuleditor_js {
     }      }
   
     function getTagList () {      function getTagList () {
           
         var stringToSearch = document.lonhomework.innerHTML;          var stringToSearch = document.lonhomework.innerHTML;
   
         var ret = new Array();          var ret = new Array();
Line 1862  sub colorfuleditor_js { Line 2122  sub colorfuleditor_js {
         var tag_list = stringToSearch.match(regex_findBlock);          var tag_list = stringToSearch.match(regex_findBlock);
   
         if(tag_list != null){          if(tag_list != null){
             for(var i = 0; i < tag_list.length; i++){              for(var i = 0; i < tag_list.length; i++){            
                 ret.push(tag_list[i].replace(/"/, ''));                  ret.push(tag_list[i].replace(/"/, ''));
             }              }
         }          }
Line 1899  sub colorfuleditor_js { Line 2159  sub colorfuleditor_js {
   
             for(var i = 0; i < tag_list.length; i++){              for(var i = 0; i < tag_list.length; i++){
                 elem_list = document.getElementsByName(tag_list[i]);                  elem_list = document.getElementsByName(tag_list[i]);
                   
                 if(elem_list.length > 0){                  if(elem_list.length > 0){
                     elem = elem_list[0];                      elem = elem_list[0];
                     break;                      break;
Line 1922  sub colorfuleditor_js { Line 2182  sub colorfuleditor_js {
             rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */              rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
         );          );
     }      }
       
     function autosize(depth){      function autosize(depth){
         var cmInst = window['cm'+depth];          var cmInst = window['cm'+depth];
         var fitsizeButton = document.getElementById('fitsize'+depth);          var fitsizeButton = document.getElementById('fitsize'+depth);
Line 1941  sub colorfuleditor_js { Line 2201  sub colorfuleditor_js {
         }          }
     }      }
   
   $browse_or_search
   
 // ]]>  // ]]>
 </script>  </script>
Line 1989  sub insert_folding_button { Line 2249  sub insert_folding_button {
     my $curDepth = $Apache::lonxml::curdepth;      my $curDepth = $Apache::lonxml::curdepth;
     my $lastresource = $env{'request.ambiguous'};      my $lastresource = $env{'request.ambiguous'};
   
     return "<input type=\"button\" id=\"folding_btn_$curDepth\"      return "<input type=\"button\" id=\"folding_btn_$curDepth\" 
             value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";              value=\"".&mt('Hide')."\" onclick=\"fold_box('$curDepth','$lastresource')\">";
 }  }
   
   sub crsauthor_url {
       my ($url) = @_;
       if ($url eq '') {
           $url = $ENV{'REQUEST_URI'};
       }
       my ($cnum,$cdom);
       if ($env{'request.course.id'}) {
           my ($audom,$auname) = ($url =~ m{^/priv/($match_domain)/($match_name)/});
           if ($audom ne '' && $auname ne '') {
               if (($env{'course.'.$env{'request.course.id'}.'.num'} eq $auname) &&
                   ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $audom)) {
                   $cnum = $auname;
                   $cdom = $audom;
               }
           }
       }
       return ($cnum,$cdom);
   }
   
   sub import_crsauthor_form {
       my ($firstselectname,$secondselectname,$onchangefirst,$only,$suffix,$disabled) = @_;
       return (0) unless ($env{'request.course.id'});
       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
       my $crshome = $env{'course.'.$env{'request.course.id'}.'.home'};
       return (0) unless (($cnum ne '') && ($cdom ne ''));
       my @ids=&Apache::lonnet::current_machine_ids();
       my ($output,$is_home,$toppath,%subdirs,%files,%selimport_menus,$include,$exclude);
   
       if (grep(/^\Q$crshome\E$/,@ids)) {
           $is_home = 1;
       }
       $toppath = "/priv/$cdom/$cnum";
       my $nonemptydir = 1;
       my $js_only;
       if ($only) {
           map { $include->{$_} = 1; } split(/\s*,\s*/,$only);
           $js_only = join(',',map { &js_escape($_); } sort(keys(%{$include})));
       }
       $exclude = &Apache::lonnet::priv_exclude();
       &Apache::lonnet::recursedirs($is_home,1,$include,$exclude,1,0,$toppath,'',\%subdirs,\%files);
       my $numdirs = scalar(keys(%files));
       my %lt = &Apache::lonlocal::texthash (
           fnam => 'Filename',
           dire => 'Directory',
           se   => 'Select',
       );
       $output = $lt{'dire'}.':&nbsp;'.
                 '<select id="'.$firstselectname.'" name="'.$firstselectname.'" '.
                 'onchange="populateCrsSelects(this.form,'."'$firstselectname','$secondselectname',1,'$js_only',0,1,0,0,0".');">'.
                 '<option value="" selected="selected">'.$lt{'se'}.'</option>';
       if ($files{'/'}) {
           $output .= '<option value="/">/</option>'."\n";
       }
       foreach my $key (sort { lc($a) cmp lc($b) } (keys(%files))) {
           next if ($key eq '/');
           $output .= '<option value="'.$key.'">'.$key.'</option>'."\n";
       }
       $output .= '</select><br />'."\n".
                  $lt{'fnam'}.':&nbsp;<select id="'.$secondselectname.'" name="'.$secondselectname.'">'."\n".
                  '<option value="" selected="selected"></option>'."\n".
                  '</select>'."\n".
                  '<input type="hidden" id="crsres_include_'.$suffix.'" value="'.$only.'" />';
       return ($numdirs,$output);
   }
   
   sub show_crsfiles_js {
       my $excluderef = &Apache::lonnet::priv_exclude();
       my $se = &js_escape(&mt('Select'));
       my $exclude;
       if (ref($excluderef) eq 'HASH') {
           $exclude = join(',', map { &js_escape($_); } sort(keys(%{$excluderef})));
       }
       my $js = <<"END";
   
   
       function populateCrsSelects (form,dirsel,filesel,exc,include,setdir,setfile,recurse,nonemptydir,addtopdir) {
           var relpath = '';
           if ((setfile) && (dirsel != null) && (dirsel != 'undefined') && (dirsel != '')) {
               var currdir = form.elements[dirsel].options[form.elements[dirsel].selectedIndex].value;
               if (currdir == '') {
                   if ((filesel != null) && (filesel != 'undefined') && (filesel != '')) {
                       selelem = form.elements[filesel];
                       var j, numfiles = selelem.options.length -1;
                       if (numfiles >=0) {
                           for (j = numfiles; j >= 0; j--) {
                               selelem.remove(j);
                           }
                       }
                       if (selelem.options.length == 0) {
                           selelem.options[selelem.options.length] = new Option('','');
                           selelem.selectedIndex = 0;
                       }
                   }
                   return;
               } else {
                   relpath = encodeURIComponent(form.elements[dirsel].options[form.elements[dirsel].selectedIndex].value);
               }
           }
           var http = new XMLHttpRequest();
           var url = "/adm/courseauthor";
           var crsrole = "$env{'request.role'}";
           var exclude = '';
           if (exc) {
               exclude = '$exclude';
           }
           var params = "role=course&files=1&rec="+recurse+"&nonempty="+nonemptydir+"&exc="+exclude+"&inc="+include+"&addtop="+addtopdir+"&path="+relpath;
           http.open("POST", url, true);
           http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
           http.onreadystatechange = function() {
               if (http.readyState == 4 && http.status == 200) {
                   var data = JSON.parse(http.responseText);
                   var selelem;
                   if ((setdir) && (dirsel != null) && (dirsel != 'undefined') && (dirsel != '')) {
                       if (Array.isArray(data.dirs)) {
                           selelem = form.elements[dirsel];
                           var i, numdirs = selelem.options.length -1;
                           if (numdirs >=0) {
                               for (i = numdirs; i >= 0; i--) {
                                   selelem.remove(i);
                               }
                           }
                           var len = data.dirs.length;
                           if (len) {
                               selelem.options[selelem.options.length] = new Option('$se','');
                               var j;
                               for (j = 0; j < len; j++) {
                                   selelem.options[selelem.options.length] = new Option(data.dirs[j],data.dirs[j]);
                               }
                               selelem.selectedIndex = 0;
                           }
                           if (!setfile) {
                               if ((filesel != null) && (filesel != 'undefined') && (filesel != '')) {
                                   selelem = form.elements[filesel];
                                   var j, numfiles = selelem.options.length -1;
                                   if (numfiles >=0) {
                                       for (j = numfiles; j >= 0; j--) {
                                           selelem.remove(j);
                                       }
                                   }
                                   if (selelem.options.length == 0) {
                                       selelem.options[selelem.options.length] = new Option('','');
                                       selelem.selectedIndex = 0;
                                   }
                               }
                           }
                       }
                   }
                   if ((setfile) && (filesel != null) && (filesel != 'undefined') && (filesel != '')) {
                       selelem = form.elements[filesel];
                       var i, numfiles = selelem.options.length -1;
                       if (numfiles >=0) {
                           for (i = numfiles; i >= 0; i--) {
                               selelem.remove(i);
                           }
                       }
                       var x;
                       for (x in data.files) {
                           if (Array.isArray(data.files[x])) {
                               if (data.files[x].length > 1) {
                                   selelem.options[selelem.options.length] = new Option('$se','');
                               }
                               var len = data.files[x].length;
                               if (len) {
                                   var k;
                                   for (k = 0; k < len; k++) {
                                       selelem.options[selelem.options.length] = new Option(data.files[x][k],data.files[x][k]);
                                   }
                                   selelem.selectedIndex = 0;
                               }
                           }
                       }
                       if (selelem.options.length == 0) {
                           selelem.options[selelem.options.length] = new Option('','');
                           selelem.selectedIndex = 0;
                       }
                   }
               }
           }
           http.send(params);
       }
   END
   }
   
   sub crsauthor_rights {
       my ($rightsfile,$path,$docroot,$cnum,$cdom) = @_;
       my $sourcerights = "$path/$rightsfile";
       my $now = time;
       if (!-e $sourcerights) {
           my $cid = $cdom.'_'.$cnum;
           if (!-e "$docroot/priv/$cdom") {
               mkdir("$docroot/priv/$cdom",0755);
           }
           if (!-e "$docroot/priv/$cdom/$cnum") {
               mkdir("$docroot/priv/$cdom/$cnum",0755);
           }
           if (open(my $fh,">$sourcerights")) {
               print $fh <<END;
   <accessrule effect="deny" realm="" type="course" role="" />
   <accessrule effect="allow" realm="$cid" type="course" role="" />
   END
               close($fh);
           }
       }
       if (!-e "$sourcerights.meta") {
           if (open(my $fh,">$sourcerights.meta")) {
               my $author=$env{'environment.firstname'}.' '.
                          $env{'environment.middlename'}.' '.
                          $env{'environment.lastname'}.' '.
                          $env{'environment.generation'};
               $author =~ s/\s+$//;
               print $fh <<"END";
   
   <abstract></abstract>
   <author>$author</author>
   <authorspace>$cnum:$cdom</authorspace>
   <copyright>private</copyright>
   <creationdate>$now</creationdate>
   <customdistributionfile></customdistributionfile>
   <dependencies></dependencies>
   <domain>$cdom</domain>
   <highestgradelevel>0</highestgradelevel>
   <keywords></keywords>
   <language>notset </language>
   <lastrevisiondate>$now</lastrevisiondate>
   <lowestgradelevel>0</lowestgradelevel>
   <mime>rights</mime>
   <modifyinguser>$env{'user.name'}:$env{'user.domain'}</modifyinguser>
   <notes></notes>
   <obsolete></obsolete>
   <obsoletereplacement></obsoletereplacement>
   <owner>$cnum:$cdom</owner>
   <rule>deny:::course,allow:$cid::course</rule>
   <sourceavail></sourceavail>
   <standards></standards>
   <subject></subject>
   <title>Course Authoring Rights</title>
   END
               close($fh);
           }
       }
       return;
   }
   
   =pod
   
   =item * &iframe_wrapper_headjs()
   
   emits javascript containing two global vars to facilitate handling of resizing
   by code in iframe_wrapper_resizejs() used when an iframe is present in a page
   with standard LON-CAPA menus.
   
   =cut
   
   #
   # Where iframe is in use, if window.onload() executes before the custom resize function
   # has been defined (jQuery), two global javascript vars (LCnotready and LCresizedef)
   # are used to ensure document.ready() triggers a call to resize, so the iframe contents
   # do not obscure the Functions menu.
   #
   
   sub iframe_wrapper_headjs {
       return <<"ENDJS";
   <script type="text/javascript">
   // <![CDATA[
   var LCnotready = 0;
   var LCresizedef = 0;
   // ]]>
   </script>
   
   ENDJS
   
   }
   
   =pod
   
   =item * &iframe_wrapper_resizejs()
   
   emits javascript used to handle resizing for a page containing
   an iframe, to ensure that the iframe does not obscure any
   standard LON-CAPA menu items.
   
   =back
   
   =cut
   
   #
   # jQuery to use when iframe is in use and a page resize occurs.
   # This script will ensure that the iframe does not obscure any
   # standard LON-CAPA inline menus (primary, secondary, and/or
   # breadcrumbs and Functions menus. Expects javascript from
   # &iframe_wrapper_headjs() to be in head portion of the web page,
   # e.g., by inclusion in second arg passed to &start_page().
   #
   
   sub iframe_wrapper_resizejs {
       my $offset = 5;
       &get_unprocessed_cgi($ENV{'QUERY_STRING'},['inhibitmenu']);
       if (($env{'form.inhibitmenu'} eq 'yes') || ($env{'form.only_body'})) {
           $offset = 0;
       }
       return &Apache::lonhtmlcommon::scripttag(<<SCRIPT);
       \$(document).ready( function() {
           \$(window).unbind('resize').resize(function(){
               var header = null;
               var offset = $offset;
               var height = 0;
               var hdrtop = 0;
               if (\$('div.LC_menus_content:first').length) {
                   if (\$('div.LC_menus_content:first').hasClass ("shown")) {
                       header = \$('div.LC_menus_content:first');
                       offset = 12;
                   }
               } else if (\$('div.LC_head_subbox:first').length) {
                   header = \$('div.LC_head_subbox:first');
                   offset = 9;
               } else {
                   if (\$('#LC_breadcrumbs').length) {
                       header = \$('#LC_breadcrumbs');
                   }
               }
               if (header != null && header.length) {
                   height = header.height();
                   hdrtop = header.position().top;
               }
               var pos = height + hdrtop + offset;
               \$('.LC_iframecontainer').css('top', pos);
           });
           LCresizedef = 1;
           if (LCnotready == 1) {
               LCnotready = 0;
               \$(window).trigger('resize');
           }
       });
       window.onload = function(){
            if (LCresizedef) {
                LCnotready = 0;
                \$(window).trigger('resize');
            } else {
                LCnotready = 1;
            }
       };
   SCRIPT
   
   }
   
 =pod  =pod
   
Line 2181  sub create_text_file { Line 2786  sub create_text_file {
 # ------------------------------------------  # ------------------------------------------
   
 sub domain_select {  sub domain_select {
     my ($name,$value,$multiple)=@_;      my ($name,$value,$multiple,$incdoms,$excdoms)=@_;
       my @possdoms;
       if (ref($incdoms) eq 'ARRAY') {
           @possdoms = @{$incdoms};
       } else {
           @possdoms = &Apache::lonnet::all_domains();
       }
   
     my %domains=map {       my %domains=map { 
  $_ => $_.' '. &Apache::lonnet::domain($_,'description')    $_ => $_.' '. &Apache::lonnet::domain($_,'description') 
     } &Apache::lonnet::all_domains();      } @possdoms;
   
       if ((ref($excdoms) eq 'ARRAY') && (@{$excdoms} > 0)) {
           foreach my $dom (@{$excdoms}) {
               delete($domains{$dom});
           }
       }
   
     if ($multiple) {      if ($multiple) {
  $domains{''}=&mt('Any domain');   $domains{''}=&mt('Any domain');
  $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];   $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
Line 2261  option_name => displayed text. An option Line 2880  option_name => displayed text. An option
 a javascript onchange item, e.g., onchange="this.form.submit();".  a javascript onchange item, e.g., onchange="this.form.submit();".
 An optional arg -- $readonly -- if true will cause the select form  An optional arg -- $readonly -- if true will cause the select form
 to be disabled, e.g., for the case where an instructor has a section-  to be disabled, e.g., for the case where an instructor has a section-
 specific role, and is viewing/modifying parameters.    specific role, and is viewing/modifying parameters. 
   
 See lonrights.pm for an example invocation and use.  See lonrights.pm for an example invocation and use.
   
Line 2329  sub display_filter { Line 2948  sub display_filter {
     my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',      my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
                                                     '$secondid','$thirdid')";                                                      '$secondid','$thirdid')";
     return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',      return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
        &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,         &Apache::lonmeta::selectbox('show',$env{'form.show'},'',undef,
    (&mt('all'),10,20,50,100,1000,10000))).     (&mt('all'),10,20,50,100,1000,10000))).
    '</label></span> <span class="LC_nobreak">'.     '</label></span> <span class="LC_nobreak">'.
            &mt('Filter: [_1]',             &mt('Filter: [_1]',
Line 2464  The optional $incdoms is a reference to Line 3083  The optional $incdoms is a reference to
   
 The optional $excdoms is a reference to an array of domains which will be excluded from the available options.  The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
   
 The optional $disabled argument, if true, adds the disabled attribute to the select tag.   The optional $disabled argument, if true, adds the disabled attribute to the select tag.
   
 =cut  =cut
   
Line 2485  sub select_dom_form { Line 3104  sub select_dom_form {
     }      }
     if ($includeempty) { @domains=('',@domains); }      if ($includeempty) { @domains=('',@domains); }
     if (ref($excdoms) eq 'ARRAY') {      if (ref($excdoms) eq 'ARRAY') {
         map { $exclude{$_} = 1; } @{$excdoms};          map { $exclude{$_} = 1; } @{$excdoms}; 
     }      }
     my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";      my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange$disabled>\n";
     foreach my $dom (@domains) {      foreach my $dom (@domains) {
Line 2713  This is not an optimal method, but it wo Line 3332  This is not an optimal method, but it wo
   
 =item * authform_filesystem  =item * authform_filesystem
   
   =item * authform_lti
   
 =back  =back
   
 See loncreateuser.pm for invocation and use examples.  See loncreateuser.pm for invocation and use examples.
Line 2841  sub authform_nochange { Line 3462  sub authform_nochange {
               kerb_def_dom => 'MSU.EDU',                kerb_def_dom => 'MSU.EDU',
               @_,                @_,
           );            );
     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});       my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
     my $result;      my $result;
     if (!$authnum) {      if (!$authnum) {
         $result = &mt('Under your current role you are not permitted to change login settings for this user');          $result = &mt('Under your current role you are not permitted to change login settings for this user');
Line 3035  sub authform_local { Line 3656  sub authform_local {
     my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});      my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
     if ($in{'readonly'}) {      if ($in{'readonly'}) {
         $disabled = ' disabled="disabled"';          $disabled = ' disabled="disabled"';
     }      } 
     if (defined($in{'curr_authtype'})) {      if (defined($in{'curr_authtype'})) {
         if ($in{'curr_authtype'} eq 'loc') {          if ($in{'curr_authtype'} eq 'loc') {
             if ($can_assign{'loc'}) {              if ($can_assign{'loc'}) {
Line 3105  sub authform_filesystem { Line 3726  sub authform_filesystem {
             } else {              } else {
                 $result = &mt('Currently Filesystem Authenticated.');                  $result = &mt('Currently Filesystem Authenticated.');
                 return $result;                  return $result;
             }                         }
         }          }
     } else {      } else {
         if ($authnum == 1) {          if ($authnum == 1) {
Line 3129  sub authform_filesystem { Line 3750  sub authform_filesystem {
                     $fsyscheck.' onchange="'.$jscall.'" onclick="'.                      $fsyscheck.' onchange="'.$jscall.'" onclick="'.
                     $jscall.'"'.$disabled.' />';                      $jscall.'"'.$disabled.' />';
     }      }
     $autharg = '<input type="text" size="10" name="fsysarg" value=""'.      $autharg = '<input type="password" size="10" name="fsysarg" value=""'.
                ' onchange="'.$jscall.'"'.$disabled.' />';                 ' onchange="'.$jscall.'"'.$disabled.' />';
     $result = &mt      $result = &mt
         ('[_1] Filesystem Authenticated (with initial password [_2])',          ('[_1] Filesystem Authenticated (with initial password [_2])',
          '<label><input type="radio" name="login" value="fsys" '.           '<label>'.$authtype,'</label>'.$autharg);
          $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'"'.$disabled.' />',      return $result;
          '</label><input type="password" size="10" name="fsysarg" value="" '.  }
                   'onchange="'.$jscall.'"'.$disabled.' />');  
   sub authform_lti {
       my %in = (
                 formname => 'document.cu',
                 kerb_def_dom => 'MSU.EDU',
                 @_,
                 );
       my ($lticheck,$result,$authtype,$autharg,$jscall,$disabled);
       my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
       if ($in{'readonly'}) {
           $disabled = ' disabled="disabled"';
       }
       if (defined($in{'curr_authtype'})) {
           if ($in{'curr_authtype'} eq 'lti') {
               if ($can_assign{'lti'}) {
                   $lticheck = 'checked="checked" ';
                   if (defined($in{'mode'})) {
                       if ($in{'mode'} eq 'modifyuser') {
                           $lticheck = '';
                       }
                   }
               } else {
                   $result = &mt('Currently LTI Authenticated.');
                   return $result;
               }
           }
       } else {
           if ($authnum == 1) {
               $authtype = '<input type="hidden" name="login" value="lti" />';
           }
       }
       if (!$can_assign{'lti'}) {
           return;
       } elsif ($authtype eq '') {
           if (defined($in{'mode'})) {
               if ($in{'mode'} eq 'modifycourse') {
                   if ($authnum == 1) {
                       $authtype = '<input type="radio" name="login" value="lti"'.$disabled.' />';
                   }
               }
           }
       }
       $jscall = "javascript:changed_radio('lti',$in{'formname'});";
       if (($authtype eq '') && (($in{'mode'} eq 'modifycourse') || ($in{'curr_authtype'} ne 'lti'))) {
           $authtype = '<input type="radio" name="login" value="lti" '.
                       $lticheck.' onchange="'.$jscall.'" onclick="'.
                       $jscall.'"'.$disabled.' />';
       }
       $autharg = '<input type="hidden" name="ltiarg" value="" />';
       if ($authtype) {
           $result = &mt('[_1] LTI Authenticated',
                         '<label>'.$authtype.'</label>'.$autharg);
       } else {
           $result = '<b>'.&mt('LTI Authenticated').'</b>'.
                     $autharg;
       }
     return $result;      return $result;
 }  }
   
Line 3150  sub get_assignable_auth { Line 3826  sub get_assignable_auth {
                           krb5 => 1,                            krb5 => 1,
                           int  => 1,                            int  => 1,
                           loc  => 1,                            loc  => 1,
                             lti  => 1,
                      );                       );
     my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);      my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
     if (ref($domconfig{'usercreation'}) eq 'HASH') {      if (ref($domconfig{'usercreation'}) eq 'HASH') {
Line 3255  sub check_passwd_rules { Line 3932  sub check_passwd_rules {
     return $warning;      return $warning;
 }  }
   
   sub passwd_validation_js {
       my ($currpasswdval,$domain,$context,$id) = @_;
       my (%passwdconf,$alertmsg);
       if ($context eq 'linkprot') {
           my %domconfig = &Apache::lonnet::get_dom('configuration',['ltisec'],$domain);
           if (ref($domconfig{'ltisec'}) eq 'HASH') {
               if (ref($domconfig{'ltisec'}{'rules'}) eq 'HASH') {
                   %passwdconf = %{$domconfig{'ltisec'}{'rules'}};
               }
           }
           if ($id eq 'add') {
               $alertmsg = &mt('Secret for added launcher did not satisfy requirement(s):').'\n\n';
           } elsif ($id =~ /^\d+$/) {
               my $pos = $id+1;
               $alertmsg = &mt('Secret for launcher [_1] did not satisfy requirement(s):','#'.$pos).'\n\n';
           } else {
               $alertmsg = &mt('A secret did not satisfy requirement(s):').'\n\n';
           }
       } else {
           %passwdconf = &Apache::lonnet::get_passwdconf($domain);
           $alertmsg = &mt('Initial password did not satisfy requirement(s):').'\n\n';
       }
       my ($min,$max,@chars,$numrules,$intargjs,%alert);
       $numrules = 0;
       $min = $Apache::lonnet::passwdmin;
       if (ref($passwdconf{'chars'}) eq 'ARRAY') {
           if ($passwdconf{'min'} =~ /^\d+$/) {
               if ($passwdconf{'min'} > $min) {
                   $min = $passwdconf{'min'};
               }
           }
           if ($passwdconf{'max'} =~ /^\d+$/) {
               $max = $passwdconf{'max'};
               $numrules ++;
           }
           @chars = @{$passwdconf{'chars'}};
           if (@chars) {
               $numrules ++;
           }
       }
       if ($min > 0) {
           $numrules ++;
       }
       if (($min > 0) || ($max ne '') || (@chars > 0)) {
           if ($min) {
               $alert{'min'} = &mt('minimum [quant,_1,character]',$min).'\n';
           }
           if ($max) {
               $alert{'max'} = &mt('maximum [quant,_1,character]',$max).'\n';
           }
           my (@charalerts,@charrules);
           if (@chars) {
               if (grep(/^uc$/,@chars)) {
                   push(@charalerts,&mt('contain at least one upper case letter'));
                   push(@charrules,'uc');
               }
               if (grep(/^lc$/,@chars)) {
                   push(@charalerts,&mt('contain at least one lower case letter'));
                   push(@charrules,'lc');
               }
               if (grep(/^num$/,@chars)) {
                   push(@charalerts,&mt('contain at least one number'));
                   push(@charrules,'num');
               }
               if (grep(/^spec$/,@chars)) {
                   push(@charalerts,&mt('contain at least one non-alphanumeric'));
                   push(@charrules,'spec');
               }
           }
           $intargjs = qq|            var rulesmsg = '';\n|.
                       qq|            var currpwval = $currpasswdval;\n|;
               if ($min) {
                   $intargjs .= qq|
               if (currpwval.length < $min) {
                   rulesmsg += ' - $alert{min}';
               }
   |;
               }
               if ($max) {
                   $intargjs .= qq|
               if (currpwval.length > $max) {
                   rulesmsg += ' - $alert{max}';
               }
   |;
               }
               if (@chars > 0) {
                   my $charrulestr = '"'.join('","',@charrules).'"';
                   my $charalertstr = '"'.join('","',@charalerts).'"';
                   $intargjs .= qq|            var brokerules = new Array();\n|.
                                qq|            var charrules = new Array($charrulestr);\n|.
                                qq|            var charalerts = new Array($charalertstr);\n|;
                   my %rules;
                   map { $rules{$_} = 1; } @chars;
                   if ($rules{'uc'}) {
                       $intargjs .= qq|
               var ucRegExp = /[A-Z]/;
               if (!ucRegExp.test(currpwval)) {
                   brokerules.push('uc');
               }
   |;
                   }
                   if ($rules{'lc'}) {
                       $intargjs .= qq|
               var lcRegExp = /[a-z]/;
               if (!lcRegExp.test(currpwval)) {
                   brokerules.push('lc');
               }
   |;
                   }
                   if ($rules{'num'}) {
                        $intargjs .= qq|
               var numRegExp = /[0-9]/;
               if (!numRegExp.test(currpwval)) {
                   brokerules.push('num');
               }
   |;
                   }
                   if ($rules{'spec'}) {
                        $intargjs .= q|
               var specRegExp = /[!"#$%&'()*+,\-.\/:;<=>?@[\\^\]_`{\|}~]/;
               if (!specRegExp.test(currpwval)) {
                   brokerules.push('spec');
               }
   |;
                   }
                   $intargjs .= qq|
               if (brokerules.length > 0) {
                   for (var i=0; i<brokerules.length; i++) {
                       for (var j=0; j<charrules.length; j++) {
                           if (brokerules[i] == charrules[j]) {
                               rulesmsg += ' - '+charalerts[j]+'\\n';
                               break;
                           }
                       }
                   }
               }
   |;
               }
               $intargjs .= qq|
               if (rulesmsg != '') {
                   rulesmsg = '$alertmsg'+rulesmsg;
                   alert(rulesmsg);
                   return false;
               }
   |;
       }
       return ($numrules,$intargjs);
   }
   
 ###############################################################  ###############################################################
 ##    Get Kerberos Defaults for Domain                 ##  ##    Get Kerberos Defaults for Domain                 ##
 ###############################################################  ###############################################################
Line 3422  sub get_related_words { Line 4248  sub get_related_words {
     untie %thesaurus_db;      untie %thesaurus_db;
     return @Words;      return @Words;
 }  }
   ###############################################################
   #
   #  Spell checking
   #
   
 =pod  =pod
   
 =back  =back
   
   =head1 Spell checking
   
   =over 4
   
   =item * &check_spelling($wordlist $language)
   
   Takes a string containing words and feeds it to an external
   spellcheck program via a pipeline. Returns a string containing
   them mis-spelled words.
   
   Parameters:
   
   =over 4
   
   =item - $wordlist
   
   String that will be fed into the spellcheck program.
   
   =item - $language
   
   Language string that specifies the language for which the spell
   check will be performed.
   
   =back
   
   =back
   
   Note: This sub assumes that aspell is installed.
   
   
 =cut  =cut
   
   
   sub check_spelling {
       my ($wordlist, $language) = @_;
       my @misspellings;
       
       # Generate the speller and set the langauge.
       # if explicitly selected:
   
       my $speller = Text::Aspell->new;
       if ($language) {
    $speller->set_option('lang', $language);
       }
   
       # Turn the word list into an array of words by splittingon whitespace
   
       my @words = split(/\s+/, $wordlist);
   
       foreach my $word (@words) {
    if(! $speller->check($word)) {
       push(@misspellings, $word);
    }
       }
       return join(' ', @misspellings);
       
   }
   
 # -------------------------------------------------------------- Plaintext name  # -------------------------------------------------------------- Plaintext name
 =pod  =pod
   
Line 3675  sub syllabuswrapper { Line 4561  sub syllabuswrapper {
   
 # -----------------------------------------------------------------------------  # -----------------------------------------------------------------------------
   
   sub aboutme_on {
       my ($uname,$udom)=@_;
       unless ($uname) { $uname=$env{'user.name'}; }
       unless ($udom)  { $udom=$env{'user.domain'}; }
       return if ($udom eq 'public' && $uname eq 'public');
       my $hashkey=$uname.':'.$udom;
       my ($aboutme,$cached)=&Apache::lonnet::is_cached_new('aboutme',$hashkey);
       if ($cached) {
           return $aboutme;
       }
       $aboutme = &Apache::lonnet::usertools_access($uname,$udom,'aboutme');
       &Apache::lonnet::do_cache_new('aboutme',$hashkey,$aboutme,3600);
       return $aboutme;
   }
   
   sub devalidate_aboutme_cache {
       my ($uname,$udom)=@_;
       if (!$udom)  { $udom =$env{'user.domain'}; }
       if (!$uname) { $uname=$env{'user.name'};   }
       return if ($udom eq 'public' && $uname eq 'public');
       my $id=$uname.':'.$udom;
       &Apache::lonnet::devalidate_cache_new('aboutme',$id);
   }
   
 sub track_student_link {  sub track_student_link {
     my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;      my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
     my $link ="/adm/trackstudent?";      my $link ="/adm/trackstudent?";
Line 3892  category Line 4802  category
   
 sub filecategorytypes {  sub filecategorytypes {
     my ($cat) = @_;      my ($cat) = @_;
     return @{$category_extensions{lc($cat)}};      if (ref($category_extensions{lc($cat)}) eq 'ARRAY') { 
           return @{$category_extensions{lc($cat)}};
       } else {
           return ();
       }
 }  }
   
 =pod  =pod
Line 4059  Return string with previous attempt on p Line 4973  Return string with previous attempt on p
   
 =item * $usec: section of the desired student  =item * $usec: section of the desired student
   
 =item * $identifier: counter for student (multiple students one problem) or  =item * $identifier: counter for student (multiple students one problem) or 
     problem (one student; whole sequence).      problem (one student; whole sequence).
   
 =back  =back
Line 4146  sub get_previous_attempt { Line 5060  sub get_previous_attempt {
             my (@hidden,@unsolved);              my (@hidden,@unsolved);
             if (%typeparts) {              if (%typeparts) {
                 foreach my $id (keys(%typeparts)) {                  foreach my $id (keys(%typeparts)) {
                     if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') ||                      if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || 
                         ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {                          ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
                         push(@hidden,$id);                          push(@hidden,$id);
                     } elsif ($identifier ne '') {                      } elsif ($identifier ne '') {
Line 4207  sub get_previous_attempt { Line 5121  sub get_previous_attempt {
                         if ($key =~ /\./) {                          if ($key =~ /\./) {
                             my $value = $returnhash{$version.':'.$key};                              my $value = $returnhash{$version.':'.$key};
                             if ($key =~ /\.rndseed$/) {                              if ($key =~ /\.rndseed$/) {
                                 my ($id) = ($key =~ /^(.+)\.rndseed$/);                                  my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                                 if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {                                  if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                                     $value = $returnhash{$version.':'.$id.'.rawrndseed'};                                      $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                                 }                                  }
Line 4224  sub get_previous_attempt { Line 5138  sub get_previous_attempt {
                     next if ($key =~ /\.foilorder$/);                      next if ($key =~ /\.foilorder$/);
                     my $value = $returnhash{$version.':'.$key};                      my $value = $returnhash{$version.':'.$key};
                     if ($key =~ /\.rndseed$/) {                      if ($key =~ /\.rndseed$/) {
                         my ($id) = ($key =~ /^(.+)\.rndseed$/);                          my ($id) = ($key =~ /^(.+)\.[^.]+$/);
                         if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {                          if (exists($returnhash{$version.':'.$id.'.rawrndseed'})) {
                             $value = $returnhash{$version.':'.$id.'.rawrndseed'};                              $value = $returnhash{$version.':'.$id.'.rawrndseed'};
                         }                          }
Line 4255  sub get_previous_attempt { Line 5169  sub get_previous_attempt {
                       if ($key =~/$regexp$/ && (defined &$gradesub)) {                        if ($key =~/$regexp$/ && (defined &$gradesub)) {
                           $value = &$gradesub($value);                            $value = &$gradesub($value);
                       }                        }
                       $prevattempts.='<td>'.$value.'&nbsp;</td>';                        $prevattempts.='<td>'. $value.'&nbsp;</td>';
                   } else {                    } else {
                       $prevattempts.='<td>&nbsp;</td>';                        $prevattempts.='<td>&nbsp;</td>';
                   }                    }
Line 4271  sub get_previous_attempt { Line 5185  sub get_previous_attempt {
       if ($key =~/$regexp$/ && (defined &$gradesub)) {        if ($key =~/$regexp$/ && (defined &$gradesub)) {
                   $value = &$gradesub($value);                    $value = &$gradesub($value);
               }                }
       $prevattempts.='<td>'.$value.'&nbsp;</td>';       $prevattempts.='<td>'.$value.'&nbsp;</td>';
           }            }
       }        }
       $prevattempts.= &end_data_table_row().&end_data_table();        $prevattempts.= &end_data_table_row().&end_data_table();
     } else {      } else {
         my $msg;
         if ($symb =~ /ext\.tool$/) {
             $msg = &mt('No grade passed back.');
         } else {
             $msg = &mt('Nothing submitted - no attempts.');
         }
       $prevattempts=        $prevattempts=
   &start_data_table().&start_data_table_row().    &start_data_table().&start_data_table_row().
   '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.    '<td>'.$msg.'</td>'.
   &end_data_table_row().&end_data_table();    &end_data_table_row().&end_data_table();
     }      }
   } else {    } else {
Line 4292  sub get_previous_attempt { Line 5212  sub get_previous_attempt {
 sub format_previous_attempt_value {  sub format_previous_attempt_value {
     my ($key,$value) = @_;      my ($key,$value) = @_;
     if (($key =~ /timestamp/) || ($key=~/duedate/)) {      if (($key =~ /timestamp/) || ($key=~/duedate/)) {
  $value = &Apache::lonlocal::locallocaltime($value);          $value = &Apache::lonlocal::locallocaltime($value);
     } elsif (ref($value) eq 'ARRAY') {      } elsif (ref($value) eq 'ARRAY') {
  $value = '('.join(', ', @{ $value }).')';          $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
     } elsif ($key =~ /answerstring$/) {      } elsif ($key =~ /answerstring$/) {
         my %answers = &Apache::lonnet::str2hash($value);          my %answers = &Apache::lonnet::str2hash($value);
           my @answer = %answers;
           %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
         my @anskeys = sort(keys(%answers));          my @anskeys = sort(keys(%answers));
         if (@anskeys == 1) {          if (@anskeys == 1) {
             my $answer = $answers{$anskeys[0]};              my $answer = $answers{$anskeys[0]};
Line 4319  sub format_previous_attempt_value { Line 5241  sub format_previous_attempt_value {
             }               } 
         }          }
     } else {      } else {
  $value = &unescape($value);          $value = &HTML::Entities::encode(&unescape($value), '"<>&');
     }      }
     return $value;      return $value;
 }  }
Line 4381  sub get_student_view { Line 5303  sub get_student_view {
   }    }
   if (defined($target)) { $form{'grade_target'} = $target; }    if (defined($target)) { $form{'grade_target'} = $target; }
   $feedurl=&Apache::lonnet::clutter($feedurl);    $feedurl=&Apache::lonnet::clutter($feedurl);
     if (($feedurl =~ /ext\.tool$/) && ($target eq 'tex')) {
         $feedurl =~ s{^/adm/wrapper}{};
     }
   my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);    my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
   $userview=~s/\<body[^\>]*\>//gi;    $userview=~s/\<body[^\>]*\>//gi;
   $userview=~s/\<\/body\>//gi;    $userview=~s/\<\/body\>//gi;
Line 4733  sub findallcourses { Line 5658  sub findallcourses {
 ###############################################  ###############################################
   
 sub blockcheck {  sub blockcheck {
     my ($setters,$activity,$uname,$udom,$url,$is_course,$symb,$caller) = @_;      my ($setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
       unless (($activity eq 'docs') || ($activity eq 'reinit') || ($activity eq 'alert')) {
           my ($has_evb,$check_ipaccess);
           my $dom = $env{'user.domain'};
           if ($env{'request.course.id'}) {
               my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
               my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
               my $checkrole = "cm./$cdom/$cnum";
               my $sec = $env{'request.course.sec'};
               if ($sec ne '') {
                   $checkrole .= "/$sec";
               }
               if ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
                   ($env{'request.role'} !~ /^st/)) {
                   $has_evb = 1;
               }
               unless ($has_evb) {
                   if (($activity eq 'printout') || ($activity eq 'grades') || ($activity eq 'search') ||
                       ($activity eq 'boards') || ($activity eq 'groups') || ($activity eq 'chat')) {
                       if ($udom eq $cdom) {
                           $check_ipaccess = 1;
                       }
                   }
               }
           } elsif (($activity eq 'com') || ($activity eq 'port') || ($activity eq 'blogs') ||
                   ($activity eq 'about') || ($activity eq 'wishlist') || ($activity eq 'passwd')) {
               my $checkrole;
               if ($env{'request.role.domain'} eq '') {
                   $checkrole = "cm./$env{'user.domain'}/";
               } else {
                   $checkrole = "cm./$env{'request.role.domain'}/";
               }
               if (($checkrole) && (&Apache::lonnet::allowed('evb',undef,undef,$checkrole))) {
                   $has_evb = 1;
               }
           }
           unless ($has_evb || $check_ipaccess) {
               my @machinedoms = &Apache::lonnet::current_machine_domains();
               if (($dom eq 'public') && ($activity eq 'port')) {
                   $dom = $udom;
               }
               if (($dom ne '') && (grep(/^\Q$dom\E$/,@machinedoms))) {
                   $check_ipaccess = 1;
               } else {
                   my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                   my $internet_names = &Apache::lonnet::get_internet_names($lonhost);
                   my $prim = &Apache::lonnet::domain($dom,'primary');
                   my $intdom = &Apache::lonnet::internet_dom($prim);
                   if (($intdom ne '') && (ref($internet_names) eq 'ARRAY')) {
                       if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
                           $check_ipaccess = 1;
                       }
                   }
               }
           }
           if ($check_ipaccess) {
               my ($ipaccessref,$cached)=&Apache::lonnet::is_cached_new('ipaccess',$dom);
               unless (defined($cached)) {
                   my %domconfig =
                       &Apache::lonnet::get_dom('configuration',['ipaccess'],$dom);
                   $ipaccessref = &Apache::lonnet::do_cache_new('ipaccess',$dom,$domconfig{'ipaccess'},1800);
               }
               if ((ref($ipaccessref) eq 'HASH') && ($clientip)) {
                   foreach my $id (keys(%{$ipaccessref})) {
                       if (ref($ipaccessref->{$id}) eq 'HASH') {
                           my $range = $ipaccessref->{$id}->{'ip'};
                           if ($range) {
                               if (&Apache::lonnet::ip_match($clientip,$range)) {
                                   if (ref($ipaccessref->{$id}->{'commblocks'}) eq 'HASH') {
                                       if ($ipaccessref->{$id}->{'commblocks'}->{$activity} eq 'on') {
                                           return ('','','',$id,$dom);
                                           last;
                                       }
                                   }
                               }
                           }
                       }
                   }
               }
           }
           if (($activity eq 'wishlist') || ($activity eq 'annotate')) {
               return ();
           }
       }
     if (defined($udom) && defined($uname)) {      if (defined($udom) && defined($uname)) {
         # If uname and udom are for a course, check for blocks in the course.          # If uname and udom are for a course, check for blocks in the course.
         if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {          if (($is_course) || (&Apache::lonnet::is_course($udom,$uname))) {
Line 4750  sub blockcheck { Line 5757  sub blockcheck {
     my $startblock = 0;      my $startblock = 0;
     my $endblock = 0;      my $endblock = 0;
     my $triggerblock = '';      my $triggerblock = '';
     my %live_courses = &findallcourses(undef,$uname,$udom);      my %live_courses;
       unless (($activity eq 'wishlist') || ($activity eq 'annotate')) {
           %live_courses = &findallcourses(undef,$uname,$udom);
       }
   
     # If uname is for a user, and activity is course-specific, i.e.,      # If uname is for a user, and activity is course-specific, i.e.,
     # boards, chat or groups, check for blocking in current course only.      # boards, chat or groups, check for blocking in current course only.
Line 4866  sub blockcheck { Line 5876  sub blockcheck {
   
         # Retrieve blocking times and identity of blocker for course          # Retrieve blocking times and identity of blocker for course
         # of specified user, unless user has 'evb' privilege.          # of specified user, unless user has 'evb' privilege.
           
         my ($start,$end,$trigger) =           my ($start,$end,$trigger) = 
             &get_blocks($setters,$activity,$cdom,$cnum,$url,$symb,$caller);              &get_blocks($setters,$activity,$cdom,$cnum,$url,$symb,$caller);
         if (($start != 0) &&           if (($start != 0) && 
Line 4959  sub get_blocks { Line 5969  sub get_blocks {
                 my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};                   my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb}; 
                 if ($start && $end) {                  if ($start && $end) {
                     if (($start <= time) && ($end >= time)) {                      if (($start <= time) && ($end >= time)) {
                         unless (grep(/^\Q$block\E$/,@blockers)) {                          if (ref($commblocks{$block}) eq 'HASH') {
                             push(@blockers,$block);                              if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                             $triggered{$block} = {                                  if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
                                                    start => $start,                                      unless(grep(/^\Q$block\E$/,@blockers)) {
                                                    end   => $end,                                          push(@blockers,$block);
                                                    type  => $type,                                          $triggered{$block} = {
                                                  };                                                                 start => $start,
                                                                  end   => $end,
                                                                  type  => $type,
                                                                };
                                       }
                                   }
                               }
                         }                          }
                     }                      }
                 }                  }
Line 5029  sub parse_block_record { Line 6045  sub parse_block_record {
 }  }
   
 sub blocking_status {  sub blocking_status {
     my ($activity,$uname,$udom,$url,$is_course,$symb,$caller) = @_;      my ($activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller) = @_;
     my %setters;      my %setters;
   
 # check for active blocking  # check for active blocking
     my ($startblock,$endblock,$triggerblock) =       if ($clientip eq '') {
         &blockcheck(\%setters,$activity,$uname,$udom,$url,$is_course,$symb,$caller);          $clientip = &Apache::lonnet::get_requestor_ip();
       }
       my ($startblock,$endblock,$triggerblock,$by_ip,$blockdom) = 
           &blockcheck(\%setters,$activity,$clientip,$uname,$udom,$url,$is_course,$symb,$caller);
     my $blocked = 0;      my $blocked = 0;
     if ($startblock && $endblock) {      if (($startblock && $endblock) || ($by_ip)) {
         $blocked = 1;          $blocked = 1;
     }      }
   
Line 5046  sub blocking_status { Line 6065  sub blocking_status {
 # build a link to a popup window containing the details  # build a link to a popup window containing the details
     my $querystring  = "?activity=$activity";      my $querystring  = "?activity=$activity";
 # $uname and $udom decide whose portfolio (or information page) the user is trying to look at  # $uname and $udom decide whose portfolio (or information page) the user is trying to look at
     if (($activity eq 'port') ||  ($activity eq 'about') || ($activity eq 'passwd')) {      if (($activity eq 'port') || ($activity eq 'about') || ($activity eq 'passwd')) {
         $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/);          $querystring .= "&amp;udom=$udom"      if ($udom =~ /^$match_domain$/); 
         $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);          $querystring .= "&amp;uname=$uname"    if ($uname =~ /^$match_username$/);
     } elsif ($activity eq 'docs') {      } elsif ($activity eq 'docs') {
         my $showurl = &Apache::lonenc::check_encrypt($url);          my $showurl = &Apache::lonenc::check_encrypt($url);
Line 5090  END_MYBLOCK Line 6109  END_MYBLOCK
         $text = &mt('Checking Course Update Blocked');          $text = &mt('Checking Course Update Blocked');
     } elsif ($activity eq 'about') {      } elsif ($activity eq 'about') {
         $text = &mt('Access to User Information Pages Blocked');          $text = &mt('Access to User Information Pages Blocked');
       } elsif ($activity eq 'wishlist') {
           $text = &mt('Access to Stored Links Blocked');
       } elsif ($activity eq 'annotate') {
           $text = &mt('Access to Annotations Blocked');
     }      }
     $output .= <<"END_BLOCK";      $output .= <<"END_BLOCK";
 <div class='$class'>  <div class='$class'>
Line 5118  sub check_ip_acc { Line 6141  sub check_ip_acc {
         ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {          ($ENV{'REMOTE_ADDR'} eq &Apache::lonnet::get_host_ip($Apache::lonnet::perlvar{'lonHostID'}))) {
         $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;          $ip = $env{'request.host'} || $ENV{'REMOTE_ADDR'} || $clientip;
     } else {      } else {
         my $remote_ip = &Apache::lonnet::get_requestor_ip();           my $remote_ip = &Apache::lonnet::get_requestor_ip();
         $ip = $remote_ip || $env{'request.host'} || $clientip;          $ip = $remote_ip || $env{'request.host'} || $clientip;
     }      }
   
Line 5132  sub check_ip_acc { Line 6155  sub check_ip_acc {
     foreach my $item (split(',',$acc)) {      foreach my $item (split(',',$acc)) {
         $item =~ s/^\s*//;          $item =~ s/^\s*//;
         $item =~ s/\s*$//;          $item =~ s/\s*$//;
           my $pattern;
         if ($item =~ /^\!(.+)$/) {          if ($item =~ /^\!(.+)$/) {
             push(@denies,$1);              push(@denies,$1);
         } else {          } else {
             push(@allows,$item);              push(@allows,$item);
         }          }
     }     }
     my $numdenies = scalar(@denies);     my $numdenies = scalar(@denies);
     my $numallows = scalar(@allows);     my $numallows = scalar(@allows);
     my $count = 0;     my $count = 0;
     foreach my $pattern (@denies,@allows) {     foreach my $pattern (@denies,@allows) {
         $count ++;          $count ++; 
         my $acctype = 'allowfrom';          my $acctype = 'allowfrom';
         if ($count <= $numdenies) {          if ($count <= $numdenies) {
             $acctype = 'denyfrom';              $acctype = 'denyfrom';
Line 5253  sub get_domainconf { Line 6277  sub get_domainconf {
                                                 my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};                                                  my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
                                                 $designhash{$udom.'.login.loginvia'} = $server;                                                  $designhash{$udom.'.login.loginvia'} = $server;
                                                 if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {                                                  if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
   
                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};                                                      $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
                                                 } else {                                                  } else {
                                                     $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};                                                      $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
Line 5269  sub get_domainconf { Line 6294  sub get_domainconf {
                                     }                                      }
                                 }                                  }
                             }                              }
                           } elsif ($key eq 'saml') {
                               if (ref($domconfig{'login'}{$key}) eq 'HASH') {
                                   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','window','notsso') {
                                               $designhash{$udom.'.login.'.$key.'_'.$item.'_'.$host} = $domconfig{'login'}{$key}{$host}{$item};
                                           }
                                       }
                                   }
                               }
                         } else {                          } else {
                             foreach my $img (keys(%{$domconfig{'login'}{$key}})) {                              foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
                                 $designhash{$udom.'.login.'.$key.'_'.$img} =                                   $designhash{$udom.'.login.'.$key.'_'.$img} = 
Line 5373  sub domainlogo { Line 6409  sub domainlogo {
  &Apache::lonnet::repcopy($local_name);   &Apache::lonnet::repcopy($local_name);
     }      }
    $imgsrc = &lonhttpdurl($imgsrc);     $imgsrc = &lonhttpdurl($imgsrc);
         }           }
         return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';          my $alttext = $domain;
           if ($designhash{$domain.'.login.alttext_domlogo'} ne '') {
               $alttext = $designhash{$domain.'.login.alttext_domlogo'};
           }
           return '<img src="'.$imgsrc.'" alt="'.$alttext.'" id="lclogindomlogo" />';
     } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {      } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
         return &Apache::lonnet::domain($domain,'description');          return &Apache::lonnet::domain($domain,'description');
     } else {      } else {
Line 5492  sub head_subbox { Line 6532  sub head_subbox {
 Input: (optional) filename from which breadcrumb trail is built.  Input: (optional) filename from which breadcrumb trail is built.
        In most cases no input as needed, as $env{'request.filename'}         In most cases no input as needed, as $env{'request.filename'}
        is appropriate for use in building the breadcrumb trail.         is appropriate for use in building the breadcrumb trail.
          frameset flag
          If page header is being requested for use in a frameset, then
          the second (option) argument -- frameset will be true, and
          the target attribute set for links should be target="_parent".
          If $title is supplied as the thitd arg, that will be used to 
          the left of the breadcrumbs tail for the current path.
   
 Returns: HTML div with CSTR path and recent box  Returns: HTML div with CSTR path and recent box
          To be included on Authoring Space pages           To be included on Authoring Space pages
Line 5499  Returns: HTML div with CSTR path and rec Line 6545  Returns: HTML div with CSTR path and rec
 =cut  =cut
   
 sub CSTR_pageheader {  sub CSTR_pageheader {
     my ($trailfile) = @_;      my ($trailfile,$frameset,$title) = @_;
     if ($trailfile eq '') {      if ($trailfile eq '') {
         $trailfile = $env{'request.filename'};          $trailfile = $env{'request.filename'};
     }      }
Line 5522  sub CSTR_pageheader { Line 6568  sub CSTR_pageheader {
         $lastitem = $thisdisfn;          $lastitem = $thisdisfn;
     }      }
   
       my $crsauthor;
       if (($env{'request.course.id'}) &&
           ($env{'course.'.$env{'request.course.id'}.'.num'} eq $uname) &&
           ($env{'course.'.$env{'request.course.id'}.'.domain'} eq $udom)) {
           $crsauthor = 1;
           if ($title eq '') {
               $title = &mt('Course Authoring Space');
           }
       } elsif ($title eq '') {
           $title = &mt('Authoring Space');
       }
   
       my ($target,$crumbtarget) = (' target="_top"','_top');
       if ($frameset) {
           $target = ' target="_parent"';
           $crumbtarget = '_parent';
       } elsif (($env{'request.lti.login'}) && ($env{'request.lti.target'} eq 'iframe')) {
           $target = '';
           $crumbtarget = '';
       } elsif (($env{'request.deeplink.login'}) && ($env{'request.deeplink.target'})) {
           $target = ' target="'.$env{'request.deeplink.target'}.'"';
           $crumbtarget = $env{'request.deeplink.target'};
       }
   
     my $output =      my $output =
          '<div>'           '<div>'
         .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?          .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
         .'<b>'.&mt('Authoring Space:').'</b> '          .'<b>'.$title.'</b> '
         .'<form name="dirs" method="post" action="'.$formaction          .'<form name="dirs" method="post" action="'.$formaction.'"'.$target.'>'
         .'" target="_top">' #FIXME lonpubdir: target="_parent"          .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,$crumbtarget,'/priv/'.$udom,undef,undef);
         .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);  
   
     if ($lastitem) {      if ($lastitem) {
         $output .=          $output .=
Line 5536  sub CSTR_pageheader { Line 6605  sub CSTR_pageheader {
             .$lastitem              .$lastitem
             .'</span>';              .'</span>';
     }      }
     $output .=  
          '<br />'      if ($crsauthor) {
         #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"          $output .= '</form>'.&Apache::lonmenu::constspaceform($frameset);
         .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')      } else {
         .'</form>'          $output .=
         .&Apache::lonmenu::constspaceform()               '<br />'
         .'</div>';              #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/',$crumbtarget,'/priv','','+1',1)."</b></tt><br />"
               .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
               .'</form>'
               .&Apache::lonmenu::constspaceform($frameset);
       }
       $output .= '</div>';
   
     return $output;      return $output;
 }  }
   
   ##############################################
   =pod
   
   =item * &nocodemirror()
   
   Input: None
   
   Returns: 1 if CodeMirror is deactivated based on
            user's preference, or domain default,
            if user indicated use of default.
   
   =cut
   
   sub nocodemirror {
       my $nocodem = $env{'environment.nocodemirror'};
       unless ($nocodem) {
           my %domdefs = &Apache::lonnet::get_domain_defaults($env{'user.domain'});
           if ($domdefs{'nocodemirror'}) {
               $nocodem = 'yes';
           }
       }
       if ($nocodem eq 'yes') {
           return 1;
       }
       return;
   }
   
   ##############################################
   =pod
   
   =item * &permitted_editors()
   
   Input: $uri (optional)
   
   Returns: %editors hash in which keys are editors
            permitted in current Authoring Space,
            or in current course for web pages
            created in a course.
   
            Value for each key is 1. Possible keys
            are: edit, xml, and daxe.
   
            For a regular Authoring Space, if no specific
            set of editors has been set for the Author
            who owns the Authoring Space, then the
            domain default will be used.  If no domain
            default has been set, then the keys will be
            edit and xml.
   
            For a course author, or for web pages created
            in a course, if no specific set of editors has
            been set for the course, then the domain
            course default will be used. If no domain
            course default has been set, then the keys
            will be edit and xml.
   
   =cut
   
   sub permitted_editors {
       my ($uri) = @_;
       my ($is_author,$is_coauthor,$is_course,$auname,$audom,%editors);
       if ($env{'request.role'} =~ m{^au\./}) {
           $is_author = 1;
       } elsif ($env{'request.role'} =~ m{^(?:ca|aa)\./($match_domain)/($match_username)}) {
           ($audom,$auname) = ($1,$2);
           if (($audom ne '') && ($auname ne '')) {
               if (($env{'user.domain'} eq $audom) &&
                   ($env{'user.name'} eq $auname)) {
                   $is_author = 1;
               } else {
                   $is_coauthor = 1;
               }
           }
       } elsif ($env{'request.course.id'}) {
           my ($cdom,$cnum);
           $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
           $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
           if (($env{'request.editurl'} =~ m{^/priv/\Q$cdom/$cnum\E/}) ||
               ($env{'request.editurl'} =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}) ||
               ($uri =~ m{^/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/})) {
               $is_course = 1;
           } elsif ($env{'request.editurl'} =~ m{^/priv/($match_domain)/($match_username)/}) {
               ($audom,$auname) = ($1,$2);
           } elsif ($env{'request.uri'} =~ m{^/priv/($match_domain)/($match_username)/}) {
               ($audom,$auname) = ($1,$2);
           } elsif (($uri eq '/daxesave') &&
                    (($env{'form.path'} =~ m{^/daxeopen/priv/\Q$cdom/$cnum\E/}) ||
                     ($env{'form.path'} =~ m{^/daxeopen/uploaded/\Q$cdom/$cnum\E/(docs|supplemental)/}))) {
               $is_course = 1;
           } elsif (($uri eq '/daxesave') &&
                    ($env{'form.path'} =~ m{^/daxeopen/priv/($match_domain)/($match_username)/})) {
               ($audom,$auname) = ($1,$2);
           }
           unless ($is_course) {
               if (($audom ne '') && ($auname ne '')) {
                   if (($env{'user.domain'} eq $audom) &&
                       ($env{'user.name'} eq $auname)) {
                       $is_author = 1;
                   } else {
                       $is_coauthor = 1;
                   }
               }
           }
       }
       if ($is_author) {
           if (exists($env{'environment.editors'})) {
               map { $editors{$_} = 1; } split(/,/,$env{'environment.editors'});
           } else {
               %editors = ( edit => 1,
                            xml => 1,
                          );
           }
       } elsif ($is_coauthor) {
           if (exists($env{"environment.internal.editors./$audom/$auname"})) {
               map { $editors{$_} = 1; } split(/,/,$env{"environment.internal.editors./$audom/$auname"});
           } else {
               %editors = ( edit => 1,
                            xml => 1,
                          );
           }
       } elsif ($is_course) {
           if (exists($env{'course.'.$env{'request.course.id'}.'.internal.crseditors'})) {
               map { $editors{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.internal.crseditors'});
           } else {
               my %domdefaults = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'});
               if (exists($domdefaults{'crseditors'})) {
                   map { $editors{$_} = 1; } split(/,/,$domdefaults{'crseditors'});
               } else {
                   %editors = ( edit => 1,
                                xml => 1,
                              );
               }
           }
       } else {
           %editors = ( edit => 1,
                        xml => 1,
                      );
       }
       return %editors;
   }
   
 ###############################################  ###############################################
 ###############################################  ###############################################
   
Line 5584  Inputs: Line 6799  Inputs:
   
 =item * $bgcolor, used to override the bgcolor on a webpage to a specific value  =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
   
 =item * $no_inline_link, if true and in remote mode, don't show the  
          'Switch To Inline Menu' link  
   
 =item * $args, optional argument valid values are  =item * $args, optional argument valid values are
             no_auto_mt_title -> prevents &mt()ing the title arg              no_auto_mt_title -> prevents &mt()ing the title arg
             use_absolute     -> for external resource or syllabus, this will              use_absolute     -> for external resource or syllabus, this will
Line 5598  Inputs: Line 6810  Inputs:
             inlineremote items to be added in "Functions" menu below              inlineremote items to be added in "Functions" menu below
             breadcrumbs.              breadcrumbs.
   
   =item * $ltiscope, optional argument, will be one of: resource, map or
               course, if LON-CAPA is in LTI Provider context. Value is
               the scope of use, i.e., launch was for access to a single, a map
               or the entire course.
   
   =item * $ltiuri, optional argument, if LON-CAPA is in LTI Provider
               context, this will contain the URL for the landing item in
               the course, after launch from an LTI Consumer
   
   =item * $ltimenu, optional argument, if LON-CAPA is in LTI Provider
               context, this will contain a reference to hash of items
               to be included in the page header and/or inline menu.
   
   =item * $menucoll, optional argument, if specific menu collection is in
               effect, either set as the default for the course, or set for
               the deeplink paramater for $env{'request.deeplink.login'}
               then $menucoll will be the number of that collection. 
   
   =item * $menuref, optional argument, reference to a hash, containing the
               menu options included for the menu in effect, based on the
               configuration for the numbered menu collection in use.  
   
   =item * $showncrumbsref, reference to a scalar. Calls to lonmenu::innerregister
               within &bodytag() can result in calls to lonhtmlcommon::breadcrumbs(),
               if so, $showncrumbsref is set there to 1, and will propagate back
               via &bodytag() to &start_page(), to prevent lonhtmlcommon::breadcrumbs()
               being called a second time.
   
 =back  =back
   
 Returns: A uniform header for LON-CAPA web pages.    Returns: A uniform header for LON-CAPA web pages.  
Line 5609  other decorations will be returned. Line 6849  other decorations will be returned.
   
 sub bodytag {  sub bodytag {
     my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,      my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
         $no_nav_bar,$bgcolor,$no_inline_link,$args,$advtoolsref)=@_;          $no_nav_bar,$bgcolor,$args,$advtoolsref,$ltiscope,$ltiuri,
           $ltimenu,$menucoll,$menuref,$showncrumbsref)=@_;
   
     my $public;      my $public;
     if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))      if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
Line 5638  sub bodytag { Line 6879  sub bodytag {
     if ($realm) {      if ($realm) {
         $realm = '/'.$realm;          $realm = '/'.$realm;
     }      }
     if ($role  eq 'ca') {      if ($role eq 'ca') {
         my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});          my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
         $realm = &plainname($rname,$rdom);          $realm = &plainname($rname,$rdom);
     }       } 
 # realm  # realm
       my ($cid,$sec);
     if ($env{'request.course.id'}) {      if ($env{'request.course.id'}) {
           $cid = $env{'request.course.id'};
           if ($env{'request.course.sec'}) {
               $sec = $env{'request.course.sec'};
           }
       } elsif ($realm =~ m{^/($match_domain)/($match_courseid)(?:|/(\w+))$}) {
           if (&Apache::lonnet::is_course($1,$2)) {
               $cid = $1.'_'.$2;
               $sec = $3;
           }
       }
       if ($cid) {
         if ($env{'request.role'} !~ /^cr/) {          if ($env{'request.role'} !~ /^cr/) {
             $role = &Apache::lonnet::plaintext($role,&course_type());              $role = &Apache::lonnet::plaintext($role,&course_type());
         } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {          } elsif ($role =~ m{^cr/($match_domain)/\1-domainconfig/(\w+)$}) {
Line 5653  sub bodytag { Line 6906  sub bodytag {
                 $role = &mt('Helpdesk[_1]','&nbsp;'.$2);                  $role = &mt('Helpdesk[_1]','&nbsp;'.$2);
             }              }
         } else {          } else {
             $role = (split(/\//,$role,4))[-1];              $role = (split(/\//,$role,4))[-1]; 
         }          }
         if ($env{'request.course.sec'}) {          if ($sec) {
             $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$env{'request.course.sec'};              $role .= ('&nbsp;'x2).'-&nbsp;'.&mt('section:').'&nbsp;'.$sec;
         }             }   
  $realm = $env{'course.'.$env{'request.course.id'}.'.description'};   $realm = $env{'course.'.$cid.'.description'};
     } else {      } else {
         $role = &Apache::lonnet::plaintext($role);          $role = &Apache::lonnet::plaintext($role);
     }      }
Line 5680  sub bodytag { Line 6933  sub bodytag {
     if ($public) {      if ($public) {
  undef($role);   undef($role);
     }      }
       
       my $showcrstitle = 1;
       if (($cid) && ($env{'request.lti.login'})) {
           if (ref($ltimenu) eq 'HASH') {
               unless ($ltimenu->{'role'}) {
                   undef($role);
               }
               unless ($ltimenu->{'coursetitle'}) {
                   $realm='&nbsp;';
                   $showcrstitle = 0;
               }
           }
       } elsif (($cid) && ($menucoll)) {
           if (ref($menuref) eq 'HASH') {
               unless ($menuref->{'role'}) {
                   undef($role);
               }
               unless ($menuref->{'crs'}) {
                   $realm='&nbsp;';
                   $showcrstitle = 0;
               }
           }
       }
   
     my $titleinfo = '<h1>'.$title.'</h1>';      my $titleinfo = '<h1>'.$title.'</h1>';
     #      #
     # Extra info if you are the DC      # Extra info if you are the DC
     my $dc_info = '';      my $dc_info = '';
     if ($env{'user.adv'} && exists($env{'user.role.dc./'.      if (($env{'user.adv'}) && ($env{'request.course.id'}) && $showcrstitle &&
                         $env{'course.'.$env{'request.course.id'}.          (exists($env{'user.role.dc./'.$env{'course.'.$cid.'.domain'}.'/'}))) {
                                  '.domain'}.'/'})) {  
         my $cid = $env{'request.course.id'};  
         $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};          $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
         $dc_info =~ s/\s+$//;          $dc_info =~ s/\s+$//;
     }      }
   
     $role = '<span class="LC_nobreak">('.$role.')</span>' if ($role && !$env{'browser.mobile'});      my $crstype;
       if ($cid) {
     if ($env{'request.state'} eq 'construct') { $forcereg=1; }          $crstype = $env{'course.'.$cid.'.type'};
       } elsif ($args->{'crstype'}) {
           $crstype = $args->{'crstype'};
       }
     my $funclist;      if (($crstype eq 'Placement') && (!$env{'request.role.adv'})) {
     if (($env{'environment.remote'} eq 'on') && ($env{'request.state'} ne 'construct')) {          undef($role);
         $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions($httphost), 'start')."\n".  
                     Apache::lonmenu::serverform();  
         my $forbodytag;  
         &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},  
                                             $forcereg,$args->{'group'},  
                                             $args->{'bread_crumbs'},  
                                             $advtoolsref,'','',\$forbodytag);  
         unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') {  
             $funclist = $forbodytag;  
         }  
     } else {      } else {
           $role = '<span class="LC_nobreak">('.$role.')</span>' if ($role && !$env{'browser.mobile'});
       }
   
           if ($env{'request.state'} eq 'construct') { $forcereg=1; }
   
         #    if ($env{'request.state'} eq 'construct') {          #    if ($env{'request.state'} eq 'construct') {
         #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls          #        $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
Line 5720  sub bodytag { Line 6988  sub bodytag {
         $bodytag .= Apache::lonhtmlcommon::scripttag(          $bodytag .= Apache::lonhtmlcommon::scripttag(
             Apache::lonmenu::utilityfunctions($httphost), 'start');              Apache::lonmenu::utilityfunctions($httphost), 'start');
   
         my ($left,$right) = Apache::lonmenu::primary_menu();          my $collapsible;
           if ($args->{'collapsible_header'} ne '') {
               $collapsible = 1;
               my ($menustate,$tiptext,$divclass);
               if ($args->{'start_collapsed'}) {
                   $menustate = 'collapsed';
                   $tiptext = 'display';
                   $divclass = 'hidden';
               } else {
                   $menustate = 'expanded';
                   $tiptext = 'hide';
                   $divclass = 'shown';
               }
               my $alttext = &mt('menu state: '.$menustate);
               my $tooltip = &mt($tiptext.' standard menus');
               $bodytag .= <<"END";
   <div id="LC_expandingContainer" style="display:inline;">
   <div id="LC_collapsible" class="LC_collapse_trigger" style="position: absolute;top: -5px;left: 0px; z-index:101; display:inline;">
   <a href="#" style="text-decoration:none;"><img class="LC_collapsible_indicator" alt="$alttext" title="$tooltip" src="/res/adm/pages/$menustate.png" style="border:0;margin:0;padding:0;max-width:100%;height:auto" /></a></div>
   <div class="LC_menus_content $divclass">
   END
           }
           unless ($args->{'no_primary_menu'}) {
               my ($left,$right) = Apache::lonmenu::primary_menu($crstype,$ltimenu,$menucoll,$menuref,
                                                                 $args->{'links_disabled'},
                                                                 $args->{'links_target'},
                                                                 $collapsible);
   
         if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {              if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
             if ($dc_info) {                  if ($dc_info) {
                  $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;                      $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
                   }
                   $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
                                  <em>$realm</em> $dc_info</div>|;
                   return $bodytag;
             }              }
             $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />  
                            <em>$realm</em> $dc_info</div>|;  
             return $bodytag;  
         }  
   
         unless ($env{'request.symb'} =~ m/\.page___\d+___/) {              unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
             $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;                  $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
         }              }
   
         $bodytag .= $right;              $bodytag .= $right;
   
         if ($dc_info) {              if ($dc_info) {
             $dc_info = &dc_courseid_toggle($dc_info);                  $dc_info = &dc_courseid_toggle($dc_info);
               }
               $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
         }          }
         $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;  
   
         #if directed to not display the secondary menu, don't.          #if directed to not display the secondary menu, don't.  
         if ($args->{'no_secondary_menu'}) {          if ($args->{'no_secondary_menu'}) {
             return $bodytag;              return $bodytag;
         }          }
         #don't show menus for public users          #don't show menus for public users
         if (!$public){          if (!$public){
             $bodytag .= Apache::lonmenu::secondary_menu($httphost);              unless ($args->{'no_inline_menu'}) {
                   $bodytag .= Apache::lonmenu::secondary_menu($httphost,$ltiscope,$ltimenu,
                                                               $args->{'no_primary_menu'},
                                                               $menucoll,$menuref,
                                                               $args->{'links_disabled'},
                                                               $args->{'links_target'});
               }
             $bodytag .= Apache::lonmenu::serverform();              $bodytag .= Apache::lonmenu::serverform();
             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');              $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
             if ($env{'request.state'} eq 'construct') {              if ($env{'request.state'} eq 'construct') {
                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,                  $bodytag .= &Apache::lonmenu::innerregister($forcereg,
                                 $args->{'bread_crumbs'},'','',$hostname);                                  $args->{'bread_crumbs'},'','',$hostname,
                                   $ltiscope,$ltiuri,$showncrumbsref);
             } elsif ($forcereg) {              } elsif ($forcereg) {
                 $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,                  $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
                                                             $args->{'group'},                                  $args->{'group'},$args->{'hide_buttons'},
                                                             $args->{'hide_buttons',                                  $hostname,$ltiscope,$ltiuri,$showncrumbsref);
                                                             $hostname});  
             } else {              } else {
                 my $forbodytag;                  $bodytag .= 
                 &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},                      &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
                                                     $forcereg,$args->{'group'},                                                          $forcereg,$args->{'group'},
                                                     $args->{'bread_crumbs'},                                                          $args->{'bread_crumbs'},
                                                     $advtoolsref,'',$hostname,                                                          $advtoolsref,'',$hostname);
                                                     \$forbodytag);  
                 unless (ref($args->{'bread_crumbs'}) eq 'ARRAY') {  
                     $bodytag .= $forbodytag;  
                 }  
             }              }
         }else{          }else{
             # this is to seperate menu from content when there's no secondary              # this is to seperate menu from content when there's no secondary
Line 5776  sub bodytag { Line 7073  sub bodytag {
             $bodytag .= '<hr style="clear:both" />';              $bodytag .= '<hr style="clear:both" />';
             $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');               $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); 
         }          }
           if ($args->{'collapsible_header'} ne '') {
         return $bodytag;              $bodytag .= $args->{'collapsible_header'}.
     }                          '<div id="LC_collapsible_separator"></div>'.
                           '</div></div>';
 #  
 # Top frame rendering, Remote is up  
 #  
   
     my $imgsrc = $img;  
     if ($img =~ /^\/adm/) {  
         $imgsrc = &lonhttpdurl($img);  
     }  
     my $upperleft='<img src="'.$imgsrc.'" alt="'.$function.'" />';  
   
     my $help=($no_inline_link?''  
               :&Apache::loncommon::top_nav_help('Help'));  
   
     # Explicit link to get inline menu  
     my $menu= ($no_inline_link?''  
                :'<a href="/adm/remote?action=collapse" target="_top">'.&mt('Switch to Inline Menu Mode').'</a>');  
   
     if ($dc_info) {  
         $dc_info = qq|<span class="LC_cusr_subheading">($dc_info)</span>|;  
     }  
   
     my $name = &plainname($env{'user.name'},$env{'user.domain'});  
     unless ($public) {  
         $name = &aboutmewrapper($name,$env{'user.name'},$env{'user.domain'},  
                                 undef,'LC_menubuttons_link');  
     }  
   
     unless ($env{'form.inhibitmenu'}) {  
         $bodytag .= qq|<div id="LC_nav_bar">$name $role</div>  
                        <ol class="LC_primary_menu LC_floatright LC_right">  
                        <li>$help</li>  
                        <li>$menu</li>  
                        </ol><div id="LC_realm"> $realm $dc_info</div>|;  
     }  
     if ($env{'request.state'} eq 'construct') {  
         if (!$public){  
             if ($env{'request.state'} eq 'construct') {  
                 $funclist = &Apache::lonhtmlcommon::scripttag(  
                                 &Apache::lonmenu::utilityfunctions($httphost), 'start').  
                             &Apache::lonhtmlcommon::scripttag('','end').  
                             &Apache::lonmenu::innerregister($forcereg,  
                                                             $args->{'bread_crumbs'});  
             }  
         }          }
     }          return $bodytag;
     return $bodytag."\n".$funclist;  
 }  }
   
 sub dc_courseid_toggle {  sub dc_courseid_toggle {
Line 5857  sub make_attr_string { Line 7110  sub make_attr_string {
  delete($attr_ref->{$key});   delete($attr_ref->{$key});
     }      }
  }   }
         if ($env{'environment.remote'} eq 'on') {   $attr_ref->{'onload'}  = $on_load;
             $attr_ref->{'onload'}  =   $attr_ref->{'onunload'}= $on_unload;
                 &Apache::lonmenu::loadevents().  $on_load;  
             $attr_ref->{'onunload'}=  
                 &Apache::lonmenu::unloadevents().$on_unload;  
         } else {    
     $attr_ref->{'onload'}  = $on_load;  
     $attr_ref->{'onunload'}= $on_unload;  
         }  
     }      }
   
     my $attr_string;      my $attr_string;
Line 5901  sub endbodytag { Line 7147  sub endbodytag {
     }      }
     if ( exists( $env{'internal.head.redirect'} ) ) {      if ( exists( $env{'internal.head.redirect'} ) ) {
         if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {          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=      $endbodytag=
         "<br /><a href=\"$env{'internal.head.redirect'}\">".          "$endbodyjs<br /><a href=\"$env{'internal.head.redirect'}\"$idattr>".
         &mt('Continue').'</a>'.          &mt('Continue').'</a>'.
         $endbodytag;          $endbodytag;
         }          }
     }      }
       if ((ref($args) eq 'HASH') && ($args->{'dashjs'})) {
           $endbodytag = &Apache::lonhtmlcommon::dash_to_minus_js().$endbodytag;
       }
     return $endbodytag;      return $endbodytag;
 }  }
   
Line 5991  form, .inline { Line 7270  form, .inline {
   display: inline;    display: inline;
 }  }
   
   .LC_menus_content.shown{
     display: block;
   }
   
   .LC_menus_content.hidden {
     display: none;
   }
   
 .LC_right {  .LC_right {
   text-align:right;    text-align:right;
 }  }
Line 6011  form, .inline { Line 7298  form, .inline {
   width:400px;    width:400px;
 }  }
   
   #LC_collapsible_separator {
       border: 1px solid black;
       width: 99.9%;
       height: 0px;
   }
   
 .LC_iframecontainer {  .LC_iframecontainer {
     width: 98%;      width: 98%;
     margin: 0;      margin: 0;
Line 6189  ul.LC_breadcrumb_tools_outerlist li { Line 7482  ul.LC_breadcrumb_tools_outerlist li {
     float: right;      float: right;
 }  }
   
   .LC_placement_prog {
       padding-right: 20px;
       font-weight: bold;
       font-size: 90%;
   }
   
 table#LC_title_bar td {  table#LC_title_bar td {
   background: $tabbg;    background: $tabbg;
 }  }
Line 6280  td.LC_menubuttons_text { Line 7579  td.LC_menubuttons_text {
 }  }
   
 td.LC_zero_height {  td.LC_zero_height {
   line-height: 0;    line-height: 0; 
   cellpadding: 0;    cellpadding: 0;
 }  }
   
Line 6605  td.LC_parm_overview_restrictions  { Line 7904  td.LC_parm_overview_restrictions  {
   border-collapse: collapse;    border-collapse: collapse;
 }  }
   
   span.LC_parm_recursive,
   td.LC_parm_recursive {
     font-weight: bold;
     font-size: smaller;
   }
   
 table.LC_parm_overview_restrictions td {  table.LC_parm_overview_restrictions td {
   border-width: 1px 4px 1px 4px;    border-width: 1px 4px 1px 4px;
   border-style: solid;    border-style: solid;
Line 6957  table.LC_data_table tr > td.LC_docs_entr Line 8262  table.LC_data_table tr > td.LC_docs_entr
   color: #990000;    color: #990000;
 }  }
   
   .LC_docs_alias {
     color: #440055;  
   }
   
 .LC_domprefs_email,  .LC_domprefs_email,
   .LC_docs_alias_name,
 .LC_docs_reinit_warn,  .LC_docs_reinit_warn,
 .LC_docs_ext_edit {  .LC_docs_ext_edit {
   font-size: x-small;    font-size: x-small;
Line 7206  fieldset { Line 8516  fieldset {
   /* overflow: hidden; */    /* overflow: hidden; */
 }  }
   
   fieldset#LC_selectuser {
       margin: 0;
       padding: 0;
   }
   
 article.geogebraweb div {  article.geogebraweb div {
     margin: 0;      margin: 0;
 }  }
Line 7254  ol.LC_primary_menu li { Line 8569  ol.LC_primary_menu li {
   line-height: 1.5em;    line-height: 1.5em;
 }  }
   
 ol.LC_primary_menu li a,   ol.LC_primary_menu li a,
 ol.LC_primary_menu li p {  ol.LC_primary_menu li p {
   display: block;    display: block;
   margin: 0;    margin: 0;
Line 7269  ol.LC_primary_menu li p span.LC_primary_ Line 8584  ol.LC_primary_menu li p span.LC_primary_
 }  }
   
 ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {  ol.LC_primary_menu li p span.LC_primary_menu_innerarrow {
   display: inline-block;    display: inline-block;
   width: 5%;    width: 5%;
   float: right;    float: right;
   text-align: right;    text-align: right;
Line 7304  ol.LC_primary_menu li:hover li, ol.LC_pr Line 8619  ol.LC_primary_menu li:hover li, ol.LC_pr
   float: none;    float: none;
   border-left: 1px solid black;    border-left: 1px solid black;
   border-right: 1px solid black;    border-right: 1px solid black;
 /* A dark bottom border to visualize different menu options;  /* A dark bottom border to visualize different menu options; 
 overwritten in the create_submenu routine for the last border-bottom of the menu */  overwritten in the create_submenu routine for the last border-bottom of the menu */
   border-bottom: 1px solid $data_table_dark;    border-bottom: 1px solid $data_table_dark; 
 }  }
   
 ol.LC_primary_menu li li p:hover {  ol.LC_primary_menu li li p:hover {
Line 7749  a#LC_content_toolbar_edittoplevel { Line 9064  a#LC_content_toolbar_edittoplevel {
   background-image:url(/res/adm/pages/edittoplevel.gif);    background-image:url(/res/adm/pages/edittoplevel.gif);
 }  }
   
   a#LC_content_toolbar_printout {
     background-image:url(/res/adm/pages/printout.gif);
   }
   
 ul#LC_toolbar li a:hover {  ul#LC_toolbar li a:hover {
   background-position: bottom center;    background-position: bottom center;
 }  }
Line 7866  ul.LC_funclist li { Line 9185  ul.LC_funclist li {
  cursor:pointer;   cursor:pointer;
 }  }
   
   .LCisDisabled {
     cursor: not-allowed;
     opacity: 0.5;
   }
   
   a[aria-disabled="true"] {
     color: currentColor;
     display: inline-block;  /* For IE11/ MS Edge bug */
     pointer-events: none;
     text-decoration: none;
   }
   
 pre.LC_wordwrap {  pre.LC_wordwrap {
   white-space: pre-wrap;    white-space: pre-wrap;
   white-space: -moz-pre-wrap;    white-space: -moz-pre-wrap;
Line 7875  pre.LC_wordwrap { Line 9206  pre.LC_wordwrap {
 }  }
   
 /*  /*
     styles used for response display
   */
   div.LC_radiofoil, div.LC_rankfoil {
     margin: .5em 0em .5em 0em;
   }
   table.LC_itemgroup {
     margin-top: 1em;
   }
   
   /*
   styles used by TTH when "Default set of options to pass to tth/m    styles used by TTH when "Default set of options to pass to tth/m
   when converting TeX" in course settings has been set    when converting TeX" in course settings has been set
   
Line 7895  span.roman {font-family: serif; font-sty Line 9236  span.roman {font-family: serif; font-sty
 span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}  span.overacc2 {position: relative;  left: .8em; top: -1.2ex;}
 span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}  span.overacc1 {position: relative;  left: .6em; top: -1.2ex;}
   
   /*
     sections with roles, for content only
   */
   section[class^="role-"] {
     padding-left: 10px;
     padding-right: 5px;
     margin-top: 8px;
     margin-bottom: 8px;
     border: 1px solid #2A4;
     border-radius: 5px;
     box-shadow: 0px 1px 1px #BBB;
   }
   section[class^="role-"]>h1 {
     position: relative;
     margin: 0px;
     padding-top: 10px;
     padding-left: 40px;
   }
   section[class^="role-"]>h1:before {
     position: absolute;
     left: -5px;
     top: 5px;
   }
   section.role-activity>h1:before {
     content:url('/adm/daxe/images/section_icons/activity.png');
   }
   section.role-advice>h1:before {
     content:url('/adm/daxe/images/section_icons/advice.png');
   }
   section.role-bibliography>h1:before {
     content:url('/adm/daxe/images/section_icons/bibliography.png');
   }
   section.role-citation>h1:before {
     content:url('/adm/daxe/images/section_icons/citation.png');
   }
   section.role-conclusion>h1:before {
     content:url('/adm/daxe/images/section_icons/conclusion.png');
   }
   section.role-definition>h1:before {
     content:url('/adm/daxe/images/section_icons/definition.png');
   }
   section.role-demonstration>h1:before {
     content:url('/adm/daxe/images/section_icons/demonstration.png');
   }
   section.role-example>h1:before {
     content:url('/adm/daxe/images/section_icons/example.png');
   }
   section.role-explanation>h1:before {
     content:url('/adm/daxe/images/section_icons/explanation.png');
   }
   section.role-introduction>h1:before {
     content:url('/adm/daxe/images/section_icons/introduction.png');
   }
   section.role-method>h1:before {
     content:url('/adm/daxe/images/section_icons/method.png');
   }
   section.role-more_information>h1:before {
     content:url('/adm/daxe/images/section_icons/more_information.png');
   }
   section.role-objectives>h1:before {
     content:url('/adm/daxe/images/section_icons/objectives.png');
   }
   section.role-prerequisites>h1:before {
     content:url('/adm/daxe/images/section_icons/prerequisites.png');
   }
   section.role-remark>h1:before {
     content:url('/adm/daxe/images/section_icons/remark.png');
   }
   section.role-reminder>h1:before {
     content:url('/adm/daxe/images/section_icons/reminder.png');
   }
   section.role-summary>h1:before {
     content:url('/adm/daxe/images/section_icons/summary.png');
   }
   section.role-syntax>h1:before {
     content:url('/adm/daxe/images/section_icons/syntax.png');
   }
   section.role-warning>h1:before {
     content:url('/adm/daxe/images/section_icons/warning.png');
   }
   
 #LC_minitab_header {  #LC_minitab_header {
   float:left;    float:left;
   width:100%;    width:100%;
Line 7948  Inputs: $title - optional title for the Line 9370  Inputs: $title - optional title for the
                                    3- whether the side effect should occur                                     3- whether the side effect should occur
                            (side effect of setting                              (side effect of setting 
                                $env{'internal.head.redirect'} to the url                                  $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         -> force to color decorate a page for a specific
                                domain                                 domain
             function       -> force usage of a specific rolish color scheme              function       -> force usage of a specific rolish color scheme
Line 7985  sub headtag { Line 9413  sub headtag {
     if (!$args->{'frameset'}) {      if (!$args->{'frameset'}) {
  $result .= &Apache::lonhtmlcommon::htmlareaheaders();   $result .= &Apache::lonhtmlcommon::htmlareaheaders();
     }      }
     if ($args->{'force_register'}) {      if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
         $result .= &Apache::lonmenu::registerurl(1);          $result .= Apache::lonxml::display_title();
     }      }
     if (!$args->{'no_nav_bar'}       if (!$args->{'no_nav_bar'} 
  && !$args->{'only_body'}   && !$args->{'only_body'}
Line 8011  sub headtag { Line 9439  sub headtag {
         }          }
     }      }
     if (ref($args->{'redirect'})) {      if (ref($args->{'redirect'})) {
  my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};   my ($time,$url,$inhibit_continue,$to_opener,$skip_enc_check) = @{$args->{'redirect'}};
  $url = &Apache::lonenc::check_encrypt($url);          if (!$skip_enc_check) {
               $url = &Apache::lonenc::check_encrypt($url);
           }
  if (!$inhibit_continue) {   if (!$inhibit_continue) {
     $env{'internal.head.redirect'} = $url;      $env{'internal.head.redirect'} = $url;
  }   }
  $result.=<<ADDMETA   $result.=<<"ADDMETA";
 <meta http-equiv="pragma" content="no-cache" />  <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" />  <meta http-equiv="Refresh" content="$time; url=$url" />
 ADDMETA  ADDMETA
           }
     } else {      } else {
         unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {          unless (($args->{'frameset'}) || ($args->{'js_ready'}) || ($args->{'only_body'}) || ($args->{'no_nav_bar'})) {
             my $requrl = $env{'request.uri'};              my $requrl = $env{'request.uri'};
Line 8034  ADDMETA Line 9492  ADDMETA
                 unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {                  unless (&Apache::lonnet::allowed('mau',$dom_in_use)) {
                     my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);                      my %domdefs = &Apache::lonnet::get_domain_defaults($dom_in_use);
                     my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};                      my $lonhost = $Apache::lonnet::perlvar{'lonHostID'};
                     my $offload;                      my ($offload,$offloadoth);
                     if (ref($domdefs{'offloadnow'}) eq 'HASH') {                      if (ref($domdefs{'offloadnow'}) eq 'HASH') {
                         if ($domdefs{'offloadnow'}{$lonhost}) {                          if ($domdefs{'offloadnow'}{$lonhost}) {
                             $offload = 1;                              $offload = 1;
                               if (($env{'user.domain'} ne '') && ($env{'user.domain'} ne $dom_in_use) &&
                                   (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                                   unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                                       $offloadoth = 1;
                                       $dom_in_use = $env{'user.domain'};
                                   }
                               }
                         }                          }
                     }                      }
                     unless ($offload) {                      unless ($offload) {
Line 8047  ADDMETA Line 9512  ADDMETA
                                     (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {                                      (!(($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public')))) {
                                     unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {                                      unless (&Apache::lonnet::shared_institution($env{'user.domain'})) {
                                         $offload = 1;                                          $offload = 1;
                                           $offloadoth = 1;
                                         $dom_in_use = $env{'user.domain'};                                          $dom_in_use = $env{'user.domain'};
                                     }                                      }
                                 }                                  }
Line 8054  ADDMETA Line 9520  ADDMETA
                         }                          }
                     }                      }
                     if ($offload) {                      if ($offload) {
                         my $newserver = &Apache::lonnet::spareserver(30000,undef,1,$dom_in_use);                          my $newserver = &Apache::lonnet::spareserver(undef,30000,undef,1,$dom_in_use);
                           if (($newserver eq '') && ($offloadoth)) {
                               my @domains = &Apache::lonnet::current_machine_domains();
                               if (($dom_in_use ne '') && (!grep(/^\Q$dom_in_use\E$/,@domains))) { 
                                   ($newserver) = &Apache::lonnet::choose_server($dom_in_use);
                               }
                           }
                         if (($newserver) && ($newserver ne $lonhost)) {                          if (($newserver) && ($newserver ne $lonhost)) {
                             my $numsec = 5;                              my $numsec = 5;
                             my $timeout = $numsec * 1000;                              my $timeout = $numsec * 1000;
Line 8068  ADDMETA Line 9540  ADDMETA
                             }                              }
                             if ($locknum) {                              if ($locknum) {
                                 my @lockinfo = sort(values(%locks));                                  my @lockinfo = sort(values(%locks));
                                 $msg = &mt('Once the following tasks are complete: ')."\n".                                  $msg = &mt('Once the following tasks are complete:')." \n".
                                        join(", ",sort(values(%locks)))."\n";                                         join(", ",sort(values(%locks)))."\n";
                                 if (&show_course()) {                                  if (&show_course()) {
                                     $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');                                      $msg .= &mt('your session will be transferred to a different server, after you click "Courses".');
Line 8147  OFFLOAD Line 9619  OFFLOAD
     if (!$args->{'frameset'}) {      if (!$args->{'frameset'}) {
         $result .= ' /';          $result .= ' /';
     }      }
     $result .= '>'      $result .= '>' 
         .$inhibitprint          .$inhibitprint
  .$head_extra;   .$head_extra;
     my $clientmobile;      my $clientmobile;
Line 8180  sub font_settings { Line 9652  sub font_settings {
     my $headerstring='';      my $headerstring='';
     if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||      if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
         ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {          ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
  $headerstring.=          $headerstring.=
     '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';              '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
         if (!$args->{'frameset'}) {          if (!$args->{'frameset'}) {
             $headerstring.= ' /';      $headerstring.= ' /';
         }          }
         $headerstring .= '>'."\n";   $headerstring .= '>'."\n";
     }      }
     return $headerstring;      return $headerstring;
 }  }
Line 8230  sub print_suppression { Line 9702  sub print_suppression {
         }          }
         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};          my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};          my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
         my $blocked = &blocking_status('printout',$cnum,$cdom,undef,1);          my $clientip = &Apache::lonnet::get_requestor_ip();
           my $blocked = &blocking_status('printout',$clientip,$cnum,$cdom,undef,1);
         if ($blocked) {          if ($blocked) {
             my $checkrole = "cm./$cdom/$cnum";              my $checkrole = "cm./$cdom/$cnum";
             if ($env{'request.course.sec'} ne '') {              if ($env{'request.course.sec'} ne '') {
Line 8338  $args - additional optional args support Line 9811  $args - additional optional args support
              skip_phases    -> hash ref of                skip_phases    -> hash ref of 
                                     head -> skip the <html><head> generation                                      head -> skip the <html><head> generation
                                     body -> skip all <body> generation                                      body -> skip all <body> generation
              no_inline_link -> if true and in remote mode, don't show the  
                                     'Switch To Inline Menu' link  
              no_auto_mt_title -> prevent &mt()ing the title arg               no_auto_mt_title -> prevent &mt()ing the title arg
              bread_crumbs ->             Array containing breadcrumbs               bread_crumbs ->             Array containing breadcrumbs
              bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs               bread_crumbs_component ->  if exists show it as headline else show only the breadcrumbs
              bread_crumbs_nomenu -> if true will pass false as the value of $menulink               bread_crumbs_nomenu -> if true will pass false as the value of $menulink
                                     to lonhtmlcommon::breadcrumbs                                      to lonhtmlcommon::breadcrumbs
              group          -> includes the current group, if page is for a               group          -> includes the current group, if page is for a 
                                specific group                                 specific group
              use_absolute   -> for request for external resource or syllabus, this               use_absolute   -> for request for external resource or syllabus, this
                                will contain https://<hostname> if server uses                                 will contain https://<hostname> if server uses
                                https (as per hosts.tab), but request is for http                                 https (as per hosts.tab), but request is for http
              hostname       -> hostname, originally from $r->hostname(), (optional).               hostname       -> hostname, originally from $r->hostname(), (optional).
                links_disabled -> Links in primary and secondary menus are disabled
                                  (Can enable them once page has loaded - see lonroles.pm
                                  for an example).
                links_target   -> Target for links, e.g., _parent (optional).
   
 =back  =back
   
Line 8363  sub start_page { Line 9838  sub start_page {
     #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));      #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
   
     $env{'internal.start_page'}++;      $env{'internal.start_page'}++;
     my ($result,@advtools);      my ($result,@advtools,$ltiscope,$ltiuri,%ltimenu,$menucoll,%menu);
   
     if (! exists($args->{'skip_phases'}{'head'}) ) {      if (! exists($args->{'skip_phases'}{'head'}) ) {
         $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);          $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
     }      }
       
       if (($env{'request.course.id'}) && ($env{'request.lti.login'})) {
           if ($env{'course.'.$env{'request.course.id'}.'.lti.override'}) {
               unless ($env{'course.'.$env{'request.course.id'}.'.lti.topmenu'}) {
                   $args->{'no_primary_menu'} = 1;
               }
               unless ($env{'course.'.$env{'request.course.id'}.'.lti.inlinemenu'}) {
                   $args->{'no_inline_menu'} = 1;
               }
               if ($env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'}) {
                   map { $ltimenu{$_} = 1; } split(/,/,$env{'course.'.$env{'request.course.id'}.'.lti.lcmenu'});
               }
           } else {
               my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
               my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider');
               if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') {
                   unless ($lti{$env{'request.lti.login'}}{'topmenu'}) {
                       $args->{'no_primary_menu'} = 1;
                   }
                   unless ($lti{$env{'request.lti.login'}}{'inlinemenu'}) {
                       $args->{'no_inline_menu'} = 1;
                   }
                   if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') {
                       map { $ltimenu{$_} = 1; } @{$lti{$env{'request.lti.login'}}{'lcmenu'}};
                   }
               }
           }
           ($ltiscope,$ltiuri) = &LONCAPA::ltiutils::lti_provider_scope($env{'request.lti.uri'},
                                     $env{'course.'.$env{'request.course.id'}.'.domain'},
                                     $env{'course.'.$env{'request.course.id'}.'.num'});
       } elsif ($env{'request.course.id'}) {
           my $expiretime=600;
           if ((time-$env{'course.'.$env{'request.course.id'}.'.last_cache'}) > $expiretime) {
               &Apache::lonnet::coursedescription($env{'request.course.id'},{'freshen_cache' => 1});
           }
           my ($deeplinkmenu,$menuref);
           ($menucoll,$deeplinkmenu,$menuref) = &menucoll_in_effect();
           if ($menucoll) {
               if (ref($menuref) eq 'HASH') {
                   %menu = %{$menuref};
               }
               if ($menu{'top'} eq 'n') {
                   $args->{'no_primary_menu'} = 1;
               }
               if ($menu{'inline'} eq 'n') {
                   unless (&Apache::lonnet::allowed('opa')) {
                       my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
                       my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
                       my $crstype = &course_type();
                       my $now = time;
                       my $ccrole;
                       if ($crstype eq 'Community') {
                           $ccrole = 'co';
                       } else {
                           $ccrole = 'cc';
                       }
                       if ($env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum}) {
                           my ($start,$end) = split(/\./,$env{'user.role.'.$ccrole.'./'.$cdom.'/'.$cnum});
                           if ((($start) && ($start<0)) ||
                               (($end) && ($end<$now))  ||
                               (($start) && ($now<$start))) {
                               $args->{'no_inline_menu'} = 1;
                           }
                       } else {
                           $args->{'no_inline_menu'} = 1;
                       }
                   }
               }
           }
       }
   
       my $showncrumbs;
     if (! exists($args->{'skip_phases'}{'body'}) ) {      if (! exists($args->{'skip_phases'}{'body'}) ) {
  if ($args->{'frameset'}) {   if ($args->{'frameset'}) {
     my $attr_string = &make_attr_string($args->{'force_register'},      my $attr_string = &make_attr_string($args->{'force_register'},
Line 8380  sub start_page { Line 9926  sub start_page {
                          $args->{'function'},       $args->{'add_entries'},                           $args->{'function'},       $args->{'add_entries'},
                          $args->{'only_body'},      $args->{'domain'},                           $args->{'only_body'},      $args->{'domain'},
                          $args->{'force_register'}, $args->{'no_nav_bar'},                           $args->{'force_register'}, $args->{'no_nav_bar'},
                          $args->{'bgcolor'},        $args->{'no_inline_link'},                           $args->{'bgcolor'},        $args,
                          $args,                     \@advtools);                           \@advtools,$ltiscope,$ltiuri,\%ltimenu,$menucoll,
                            \%menu,\$showncrumbs);
         }          }
     }      }
   
Line 8403  sub start_page { Line 9950  sub start_page {
   
     #Breadcrumbs      #Breadcrumbs
     if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {      if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
           unless ($showncrumbs) {
  &Apache::lonhtmlcommon::clear_breadcrumbs();   &Apache::lonhtmlcommon::clear_breadcrumbs();
  #if any br links exists, add them to the breadcrumbs   #if any br links exists, add them to the breadcrumbs
  if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {            if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {         
Line 8416  sub start_page { Line 9964  sub start_page {
                 }                  }
                 my $menulink;                  my $menulink;
                 # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.                  # if arg: bread_crumbs_nomenu is true pass 0 as $menulink item.
                 if (exists($args->{'bread_crumbs_nomenu'})) {                  if ((exists($args->{'bread_crumbs_nomenu'})) ||
                        ($ltiscope eq 'map') || ($ltiscope eq 'resource') ||
                        ((($args->{'crstype'} eq 'Placement') || (($env{'request.course.id'}) &&
                        ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement'))) &&
                        (!$env{'request.role.adv'}))) {
                     $menulink = 0;                      $menulink = 0;
                 } else {                  } else {
                     undef($menulink);                      undef($menulink);
                 }                  }
                   my $linkprotout;
                   if ($env{'request.deeplink.login'}) {
                       my $linkprotout = &Apache::lonmenu::linkprot_exit();
                       if ($linkprotout) {
                           &Apache::lonhtmlcommon::add_breadcrumb_tool('tools',$linkprotout);
                       }
                   }
  #if bread_crumbs_component exists show it as headline else show only the breadcrumbs   #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
  if(exists($args->{'bread_crumbs_component'})){   if(exists($args->{'bread_crumbs_component'})){
  $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink);   $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'},'',$menulink);
  }else{                  } else {
  $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);   $result .= &Apache::lonhtmlcommon::breadcrumbs('','',$menulink);
  }   }
     } elsif (($env{'environment.remote'} eq 'on') &&          }
              ($env{'form.inhibitmenu'} ne 'yes') &&  
              ($env{'request.noversionuri'} =~ m{^/res/}) &&  
              ($env{'request.noversionuri'} !~ m{^/res/adm/pages/})) {  
         $result .= '<div style="padding:0;margin:0;clear:both"><hr /></div>';  
     }      }
     return $result;      return $result;
 }  }
Line 8468  sub end_page { Line 10023  sub end_page {
     return $result;      return $result;
 }  }
   
   sub menucoll_in_effect {
       my ($menucoll,$deeplinkmenu,%menu);
       if ($env{'request.course.id'}) {
           $menucoll = $env{'course.'.$env{'request.course.id'}.'.menudefault'};
           if ($env{'request.deeplink.login'}) {
               my ($deeplink_symb,$deeplink,$check_login_symb);
               my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
               my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
               if ($env{'request.noversionuri'} =~ m{^/(res|uploaded)/}) {
                   if ($env{'request.noversionuri'} =~ /\.(page|sequence)$/) {
                       my $navmap = Apache::lonnavmaps::navmap->new();
                       if (ref($navmap)) {
                           $deeplink = $navmap->get_mapparam(undef,
                                                             &Apache::lonnet::declutter($env{'request.noversionuri'}),
                                                             '0.deeplink');
                       } else {
                           $check_login_symb = 1;
                       }
                   } else {
                       my $symb = &Apache::lonnet::symbread();
                       if ($symb) {
                           $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$symb);
                       } else {
                           $check_login_symb = 1;
                       }
                   }
               } else {
                   $check_login_symb = 1;
               }
               if ($check_login_symb) {
                   $deeplink_symb = &deeplink_login_symb($cnum,$cdom);
                   if ($deeplink_symb =~ /\.(page|sequence)$/) {
                       my $mapname = &Apache::lonnet::deversion((&Apache::lonnet::decode_symb($deeplink_symb))[2]);
                       my $navmap = Apache::lonnavmaps::navmap->new();
                       if (ref($navmap)) {
                           $deeplink = $navmap->get_mapparam(undef,$mapname,'0.deeplink');
                       }
                   } else {
                       $deeplink = &Apache::lonnet::EXT('resource.0.deeplink',$deeplink_symb);
                   }
               }
               if ($deeplink ne '') {
                   my ($state,$others,$listed,$scope,$protect,$display,$target) = split(/,/,$deeplink);
                   if ($display =~ /^\d+$/) {
                       $deeplinkmenu = 1;
                       $menucoll = $display;
                   }
               }
           }
           if ($menucoll) {
               %menu = &page_menu($env{'course.'.$env{'request.course.id'}.'.menucollections'},$menucoll);
           }
       }
       return ($menucoll,$deeplinkmenu,\%menu);
   }
   
   sub deeplink_login_symb {
       my ($cnum,$cdom) = @_;
       my $login_symb;
       if ($env{'request.deeplink.login'}) {
           $login_symb = &symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);
       }
       return $login_symb;
   }
   
   sub symb_from_tinyurl {
       my ($url,$cnum,$cdom) = @_;
       if ($url =~ m{^\Q/tiny/$cdom/\E(\w+)$}) {
           my $key = $1;
           my ($tinyurl,$login);
           my ($result,$cached)=&Apache::lonnet::is_cached_new('tiny',$cdom."\0".$key);
           if (defined($cached)) {
               $tinyurl = $result;
           } else {
               my $configuname = &Apache::lonnet::get_domainconfiguser($cdom);
               my %currtiny = &Apache::lonnet::get('tiny',[$key],$cdom,$configuname);
               if ($currtiny{$key} ne '') {
                   $tinyurl = $currtiny{$key};
                   &Apache::lonnet::do_cache_new('tiny',$cdom."\0".$key,$currtiny{$key},600);
               }
           }
           if ($tinyurl ne '') {
               my ($cnumreq,$symb) = split(/\&/,$tinyurl);
               if (wantarray) {
                   return ($cnumreq,$symb);
               } elsif ($cnumreq eq $cnum) {
                   return $symb;
               }
           }
       }
       if (wantarray) {
           return ();
       } else {
           return;
       }
   }
   
   sub usable_exttools {
       my %tooltypes;
       if ($env{'request.course.id'}) {
           if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'}) {
              if ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'both') {
                  %tooltypes = (
                                crs => 1,
                                dom => 1,
                               );
              } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'crs') {
                  $tooltypes{'crs'} = 1;
              } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.exttool'} eq 'dom') {
                  $tooltypes{'dom'} = 1;
              }
           } else {
               my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
               my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
               my $crstype = lc($env{'course.'.$env{'request.course.id'}.'.type'});
               if ($crstype eq '') {
                   $crstype = 'course';
               }
               if ($crstype eq 'course') {
                   if ($env{'course.'.$env{'request.course.id'}.'internal.coursecode'}) {
                       $crstype = 'official';
                   } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.textbook'}) {
                       $crstype = 'textbook';
                   } elsif ($env{'course.'.$env{'request.course.id'}.'.internal.lti'}) {
                       $crstype = 'lti';
                   } else {
                       $crstype = 'unofficial';
                   }
               }
               my %domdefaults = &Apache::lonnet::get_domain_defaults($cdom);
               if ($domdefaults{$crstype.'domexttool'}) {
                   $tooltypes{'dom'} = 1;
               }
               if ($domdefaults{$crstype.'exttool'}) {
                   $tooltypes{'crs'} = 1;
               }
           }
       }
       return %tooltypes;
   }
   
 sub wishlist_window {  sub wishlist_window {
     return(<<'ENDWISHLIST');      return(<<'ENDWISHLIST');
 <script type="text/javascript">  <script type="text/javascript">
Line 8557  ENDLINK Line 10253  ENDLINK
 }  }
   
 sub modal_adhoc_script {  sub modal_adhoc_script {
     my ($funcname,$width,$height,$content)=@_;      my ($funcname,$width,$height,$content,$possmathjax)=@_;
       my $mathjax;
       if ($possmathjax) {
           $mathjax = <<'ENDJAX';
                  if (typeof MathJax == 'object') {
                      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
                  }
   ENDJAX
       }
     return (<<ENDADHOC);      return (<<ENDADHOC);
 <script type="text/javascript">  <script type="text/javascript">
 // <![CDATA[  // <![CDATA[
Line 8568  sub modal_adhoc_script { Line 10272  sub modal_adhoc_script {
                 modalWindow.height = $height;                  modalWindow.height = $height;
                 modalWindow.content = '$content';                  modalWindow.content = '$content';
                 modalWindow.open();                  modalWindow.open();
                   $mathjax
         };            };  
 // ]]>  // ]]>
 </script>  </script>
Line 8575  ENDADHOC Line 10280  ENDADHOC
 }  }
   
 sub modal_adhoc_inner {  sub modal_adhoc_inner {
     my ($funcname,$width,$height,$content)=@_;      my ($funcname,$width,$height,$content,$possmathjax)=@_;
     my $innerwidth=$width-20;      my $innerwidth=$width-20;
     $content=&js_ready(      $content=&js_ready(
                &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).                   &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
                  &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).                   &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
                  $content.                   $content.
                  &end_scrollbox().                   &end_scrollbox().
                  &end_page()                   &end_page()
              );               );
     return &modal_adhoc_script($funcname,$width,$height,$content);      return &modal_adhoc_script($funcname,$width,$height,$content,$possmathjax);
 }  }
   
 sub modal_adhoc_window {  sub modal_adhoc_window {
     my ($funcname,$width,$height,$content,$linktext)=@_;      my ($funcname,$width,$height,$content,$linktext,$possmathjax)=@_;
     return &modal_adhoc_inner($funcname,$width,$height,$content).      return &modal_adhoc_inner($funcname,$width,$height,$content,$possmathjax).
            "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";             "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
 }  }
   
Line 8950  function expand_div(caller) { Line 10655  function expand_div(caller) {
   
 sub simple_error_page {  sub simple_error_page {
     my ($r,$title,$msg,$args) = @_;      my ($r,$title,$msg,$args) = @_;
       my %displayargs;
     if (ref($args) eq 'HASH') {      if (ref($args) eq 'HASH') {
         if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }          if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
           if ($args->{'only_body'}) {
               $displayargs{'only_body'} = 1;
           }
           if ($args->{'no_nav_bar'}) {
               $displayargs{'no_nav_bar'} = 1;
           }
     } else {      } else {
         $msg = &mt($msg);          $msg = &mt($msg);
     }      }
   
     my $page =      my $page =
  &Apache::loncommon::start_page($title).   &Apache::loncommon::start_page($title,'',\%displayargs).
  '<p class="LC_error">'.$msg.'</p>'.   '<p class="LC_error">'.$msg.'</p>'.
  &Apache::loncommon::end_page();   &Apache::loncommon::end_page();
     if (ref($r)) {      if (ref($r)) {
Line 9140  Scalar: 1 if 'Course' to be used, 0 othe Line 10852  Scalar: 1 if 'Course' to be used, 0 othe
   
 ###############################################  ###############################################
 sub show_course {  sub show_course {
       my ($udom,$uname) = @_;
       if (($udom ne '') && ($uname ne '')) {
           if (($udom ne $env{'user.domain'}) || ($uname ne $env{'user.name'})) {
               if (&Apache::lonnet::is_advanced_user($udom,$uname)) {
                   return 0;
               } else {
                   return 1;
               }
           }
       }
     my $course = !$env{'user.adv'};      my $course = !$env{'user.adv'};
     if (!$env{'user.adv'}) {      if (!$env{'user.adv'}) {
         foreach my $env (keys(%env)) {          foreach my $env (keys(%env)) {
Line 9263  sub get_sections { Line 10985  sub get_sections {
         }          }
     }      }
   
     if ($check_students) {      if ($check_students) { 
  my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);   my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
  my $sec_index = &Apache::loncoursedata::CL_SECTION();   my $sec_index = &Apache::loncoursedata::CL_SECTION();
  my $status_index = &Apache::loncoursedata::CL_STATUS();   my $status_index = &Apache::loncoursedata::CL_STATUS();
Line 9585  Incoming parameters: Line 11307  Incoming parameters:
 2. user's domain  2. user's domain
 3. quota name - portfolio, author, or course  3. quota name - portfolio, author, or course
    (if no quota name provided, defaults to portfolio).     (if no quota name provided, defaults to portfolio).
 4. crstype - official, unofficial, textbook or community, if quota name is  4. crstype - official, unofficial, textbook, placement or community, 
    course     if quota name is course
   
 Returns:  Returns:
 1. Disk quota (in MB) assigned to student.  1. Disk quota (in MB) assigned to student.
Line 9659  sub get_user_quota { Line 11381  sub get_user_quota {
         if ($quota eq '' || wantarray) {          if ($quota eq '' || wantarray) {
             if ($quotaname eq 'course') {              if ($quotaname eq 'course') {
                 my %domdefs = &Apache::lonnet::get_domain_defaults($udom);                  my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
                 if (($crstype eq 'official') || ($crstype eq 'unofficial') ||                  if (($crstype eq 'official') || ($crstype eq 'unofficial') || 
                     ($crstype eq 'community') || ($crstype eq 'textbook')) {                      ($crstype eq 'community') || ($crstype eq 'textbook') ||
                       ($crstype eq 'placement')) { 
                     $defquota = $domdefs{$crstype.'quota'};                      $defquota = $domdefs{$crstype.'quota'};
                 }                  }
                 if ($defquota eq '') {                  if ($defquota eq '') {
Line 9808  Inputs: 7 Line 11531  Inputs: 7
 4. filename of file for which action is being requested  4. filename of file for which action is being requested
 5. filesize (kB) of file  5. filesize (kB) of file
 6. action being taken: copy or upload.  6. action being taken: copy or upload.
 7. quotatype (in course context -- official, unofficial, community or textbook).  7. quotatype (in course context -- official, unofficial, textbook, placement or community).
   
 Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,  Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
          otherwise return null.           otherwise return null.
Line 9844  sub excess_filesize_warning { Line 11567  sub excess_filesize_warning {
 ###############################################  ###############################################
   
   
   
   
 sub get_secgrprole_info {  sub get_secgrprole_info {
     my ($cdom,$cnum,$needroles,$type)  = @_;      my ($cdom,$cnum,$needroles,$type)  = @_;
     my %sections_count = &get_sections($cdom,$cnum);      my %sections_count = &get_sections($cdom,$cnum);
Line 9952  sub user_picker { Line 11677  sub user_picker {
         $allow_blank = 0;          $allow_blank = 0;
         $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,[$currdom]);          $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,[$currdom]);
     } else {      } else {
         $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1);          my $defdom = $env{'request.role.domain'};
           my ($trusted,$untrusted);
           if (($context eq 'requestcrs') || ($context eq 'course')) {
               ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('enroll',$defdom);
           } elsif ($context eq 'author') {
               ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('othcoau',$defdom);
           } elsif ($context eq 'domain') {
               ($trusted,$untrusted) = &Apache::lonnet::trusted_domains('domroles',$defdom);
           }
           $domform = &select_dom_form($currdom,'srchdomain',$allow_blank,1,undef,$trusted,$untrusted);
     }      }
     my $srchinsel = ' <select name="srchin">';      my $srchinsel = ' <select name="srchin">';
   
Line 10157  sub user_rule_check { Line 11891  sub user_rule_check {
     if (ref($usershash) eq 'HASH') {      if (ref($usershash) eq 'HASH') {
         if (keys(%{$usershash}) > 1) {          if (keys(%{$usershash}) > 1) {
             my (%by_username,%by_id,%userdoms);              my (%by_username,%by_id,%userdoms);
             my $checkid;              my $checkid; 
             if (ref($checks) eq 'HASH') {              if (ref($checks) eq 'HASH') {
                 if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {                  if ((!defined($checks->{'username'})) && (defined($checks->{'id'}))) {
                     $checkid = 1;                      $checkid = 1;
Line 10168  sub user_rule_check { Line 11902  sub user_rule_check {
                 if ($checkid) {                  if ($checkid) {
                     if (ref($usershash->{$user}) eq 'HASH') {                      if (ref($usershash->{$user}) eq 'HASH') {
                         if ($usershash->{$user}->{'id'} ne '') {                          if ($usershash->{$user}->{'id'} ne '') {
                             $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname;                              $by_id{$udom}{$usershash->{$user}->{'id'}} = $uname; 
                             $userdoms{$udom} = 1;                              $userdoms{$udom} = 1;
                             if (ref($inst_results) eq 'HASH') {                              if (ref($inst_results) eq 'HASH') {
                                 $inst_results->{$uname.':'.$udom} = {};                                  $inst_results->{$uname.':'.$udom} = {};
Line 10238  sub user_rule_check { Line 11972  sub user_rule_check {
                 if (ref($usershash->{$user}) eq 'HASH') {                  if (ref($usershash->{$user}) eq 'HASH') {
                     if (ref($checks) eq 'HASH') {                      if (ref($checks) eq 'HASH') {
                         if (defined($checks->{'username'})) {                          if (defined($checks->{'username'})) {
                             ($inst_response{$user},%{$inst_results->{$user}}) =                              ($inst_response{$user},%{$inst_results->{$user}}) = 
                                 &Apache::lonnet::get_instuser($udom,$uname);                                  &Apache::lonnet::get_instuser($udom,$uname);
                         } elsif (defined($checks->{'id'})) {                          } elsif (defined($checks->{'id'})) {
                             if ($usershash->{$user}->{'id'} ne '') {                              if ($usershash->{$user}->{'id'} ne '') {
Line 10261  sub user_rule_check { Line 11995  sub user_rule_check {
                         if (ref($domconfig{'usercreation'}) eq 'HASH') {                          if (ref($domconfig{'usercreation'}) eq 'HASH') {
                             foreach my $item ('username','id') {                              foreach my $item ('username','id') {
                                 if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {                                  if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
                                    $$curr_rules{$udom}{$item} =                                     $$curr_rules{$udom}{$item} = 
                                        $domconfig{'usercreation'}{$item.'_rule'};                                         $domconfig{'usercreation'}{$item.'_rule'};
                                 }                                  }
                             }                              }
Line 10284  sub user_rule_check { Line 12018  sub user_rule_check {
                     $id = $inst_results->{$user}->{'id'};                      $id = $inst_results->{$user}->{'id'};
                 }                  }
             }              }
             if ($id eq '') {              if ($id eq '') { 
                 if (ref($usershash->{$user})) {                  if (ref($usershash->{$user})) {
                     $id = $usershash->{$user}->{'id'};                      $id = $usershash->{$user}->{'id'};
                 }                  }
Line 10445  sub sorted_inst_types { Line 12179  sub sorted_inst_types {
 }  }
   
 sub get_institutional_codes {  sub get_institutional_codes {
     my ($settings,$allcourses,$LC_code) = @_;      my ($cdom,$crs,$settings,$allcourses,$LC_code) = @_;
 # Get complete list of course sections to update  # Get complete list of course sections to update
     my @currsections = ();      my @currsections = ();
     my @currxlists = ();      my @currxlists = ();
       my (%unclutteredsec,%unclutteredlcsec);
     my $coursecode = $$settings{'internal.coursecode'};      my $coursecode = $$settings{'internal.coursecode'};
       my $crskey = $crs.':'.$coursecode;
       @{$unclutteredsec{$crskey}} = ();
       @{$unclutteredlcsec{$crskey}} = ();
   
     if ($$settings{'internal.sectionnums'} ne '') {      if ($$settings{'internal.sectionnums'} ne '') {
         @currsections = split(/,/,$$settings{'internal.sectionnums'});          @currsections = split(/,/,$$settings{'internal.sectionnums'});
Line 10460  sub get_institutional_codes { Line 12198  sub get_institutional_codes {
     }      }
   
     if (@currxlists > 0) {      if (@currxlists > 0) {
         foreach (@currxlists) {          foreach my $xl (@currxlists) {
             if (m/^([^:]+):(\w*)$/) {              if ($xl =~ /^([^:]+):(\w*)$/) {
                 unless (grep/^$1$/,@{$allcourses}) {                  unless (grep/^$1$/,@{$allcourses}) {
                     push(@{$allcourses},$1);                      push(@{$allcourses},$1);
                     $$LC_code{$1} = $2;                      $$LC_code{$1} = $2;
Line 10469  sub get_institutional_codes { Line 12207  sub get_institutional_codes {
             }              }
         }          }
     }      }
    
     if (@currsections > 0) {      if (@currsections > 0) {
         foreach (@currsections) {          foreach my $sec (@currsections) {
             if (m/^(\w+):(\w*)$/) {              if ($sec =~ m/^(\w+):(\w*)$/ ) {
                 my $sec = $coursecode.$1;                  my $instsec = $1;
                 my $lc_sec = $2;                  my $lc_sec = $2;
                 unless (grep/^$sec$/,@{$allcourses}) {                  unless (grep/^\Q$instsec\E$/,@{$unclutteredsec{$crskey}}) {
                       push(@{$unclutteredsec{$crskey}},$instsec);
                       push(@{$unclutteredlcsec{$crskey}},$lc_sec);
                   }
               }
           }
       }
   
       if (@{$unclutteredsec{$crskey}} > 0) {
           my %formattedsec = &Apache::lonnet::auto_instsec_reformat($cdom,'clutter',\%unclutteredsec);
           if ((ref($formattedsec{$crskey}) eq 'ARRAY') && (ref($unclutteredlcsec{$crskey}) eq 'ARRAY')) {
               for (my $i=0; $i<@{$formattedsec{$crskey}}; $i++) {
                   my $sec = $coursecode.$formattedsec{$crskey}[$i];
                   unless (grep/^\Q$sec\E$/,@{$allcourses}) {
                     push(@{$allcourses},$sec);                      push(@{$allcourses},$sec);
                     $$LC_code{$sec} = $lc_sec;                      $$LC_code{$sec} = $unclutteredlcsec{$crskey}[$i];
                 }                  }
             }              }
         }          }
Line 10586  future_reservable - ref to hash of stude Line 12337  future_reservable - ref to hash of stude
   
     Keys in inner hash are:      Keys in inner hash are:
     (a) symb: either blank or symb to which slot use is restricted.      (a) symb: either blank or symb to which slot use is restricted.
     (b) startreserve:  start date of reservation period.      (b) startreserve: start date of reservation period.
     (c) uniqueperiod: start,end dates when slot is to be uniquely      (c) uniqueperiod: start,end dates when slot is to be uniquely
         selected.          selected.
   
Line 10596  future_reservable - ref to hash of stude Line 12347  future_reservable - ref to hash of stude
   
 sub get_future_slots {  sub get_future_slots {
     my ($cnum,$cdom,$now,$symb) = @_;      my ($cnum,$cdom,$now,$symb) = @_;
       my $map;
       if ($symb) {
           ($map) = &Apache::lonnet::decode_symb($symb);
       }
     my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);      my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
     my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);      my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
     foreach my $slot (keys(%slots)) {      foreach my $slot (keys(%slots)) {
         next unless($slots{$slot}->{'type'} eq 'schedulable_student');          next unless($slots{$slot}->{'type'} eq 'schedulable_student');
         if ($symb) {          if ($symb) {
             next if (($slots{$slot}->{'symb'} ne '') &&               if ($slots{$slot}->{'symb'} ne '') {
                      ($slots{$slot}->{'symb'} ne $symb));                  my $canuse;
                   my %oksymbs;
                   my @slotsymbs = split(/\s*,\s*/,$slots{$slot}->{'symb'});
                   map { $oksymbs{$_} = 1; } @slotsymbs;
                   if ($oksymbs{$symb}) {
                       $canuse = 1;
                   } else {
                       foreach my $item (@slotsymbs) {
                           if ($item =~ /\.(page|sequence)$/) {
                               (undef,undef,my $sloturl) = &Apache::lonnet::decode_symb($item);
                               if (($map ne '') && ($map eq $sloturl)) {
                                   $canuse = 1;
                                   last;
                               }
                           }
                       }
                   }
                   next unless ($canuse);
               }
         }          }
         if (($slots{$slot}->{'starttime'} > $now) &&          if (($slots{$slot}->{'starttime'} > $now) &&
             ($slots{$slot}->{'endtime'} > $now)) {              ($slots{$slot}->{'endtime'} > $now)) {
Line 10655  sub get_future_slots { Line 12428  sub get_future_slots {
                 $reservable_now{$slot} = {                  $reservable_now{$slot} = {
                                            symb       => $symb,                                             symb       => $symb,
                                            endreserve => $lastres,                                             endreserve => $lastres,
                                            uniqueperiod => $uniqueperiod,                                                uniqueperiod => $uniqueperiod,
                                          };                                           };
             } elsif (($startreserve > $now) &&              } elsif (($startreserve > $now) &&
                      (!$endreserve || $endreserve > $startreserve)) {                       (!$endreserve || $endreserve > $startreserve)) {
Line 10820  sub get_env_multiple { Line 12593  sub get_env_multiple {
     return(@values);      return(@values);
 }  }
   
   # Looks at given dependencies, and returns something depending on the context.
   # For coursedocs paste, returns (undef, $counter, $numpathchg, \%existing).
   # For syllabus rewrites, returns (undef, $counter, $numpathchg, \%existing, \%mapping).
   # For all other contexts, returns ($output, $counter, $numpathchg).
   # $output: string with the HTML output. Can contain missing dependencies with an upload form, existing dependencies, and dependencies no longer in use.
   # $counter: integer with the number of existing dependencies when no HTML output is returned, and the number of missing dependencies when an HTML output is returned.
   # $numpathchg: integer with the number of cleaned up dependency paths.
   # \%existing: hash reference clean path -> 1 only for existing dependencies.
   # \%mapping: hash reference clean path -> original path for all dependencies.
   # @param {string} actionurl - The path to the handler, indicative of the context.
   # @param {string} state - Can contain HTML with hidden inputs that will be added to the output form.
   # @param {hash reference} allfiles - List of file info from lonnet::extract_embedded_items
   # @param {hash reference} codebase - undef, not modified by lonnet::extract_embedded_items ?
   # @param {hash reference} args - More parameters ! Possible keys: error_on_invalid_names (boolean), ignore_remote_references (boolean), current_path (string), docs_url (string), docs_title (string), context (string)
   # @return {Array} - array depending on the context (not a reference)
 sub ask_for_embedded_content {  sub ask_for_embedded_content {
       # NOTE: documentation was added afterwards, it could be wrong
     my ($actionurl,$state,$allfiles,$codebase,$args)=@_;      my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
     my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,      my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
         %currsubfile,%unused,$rem);          %currsubfile,%unused,$rem);
Line 10836  sub ask_for_embedded_content { Line 12625  sub ask_for_embedded_content {
     my $heading = &mt('Upload embedded files');      my $heading = &mt('Upload embedded files');
     my $buttontext = &mt('Upload');      my $buttontext = &mt('Upload');
   
       # fills these variables based on the context:
       # $navmap, $cdom, $cnum, $udom, $uname, $url, $toplevel, $getpropath,
       # $path, $fileloc, $title, $rem, $filename
     if ($env{'request.course.id'}) {      if ($env{'request.course.id'}) {
         if ($actionurl eq '/adm/dependencies') {          if ($actionurl eq '/adm/dependencies') {
             $navmap = Apache::lonnavmaps::navmap->new();              $navmap = Apache::lonnavmaps::navmap->new();
Line 10843  sub ask_for_embedded_content { Line 12635  sub ask_for_embedded_content {
         $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};          $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};          $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
     }      }
     if (($actionurl eq '/adm/portfolio') ||      if (($actionurl eq '/adm/portfolio') || 
         ($actionurl eq '/adm/coursegrp_portfolio')) {          ($actionurl eq '/adm/coursegrp_portfolio')) {
         my $current_path='/';          my $current_path='/';
         if ($env{'form.currentpath'}) {          if ($env{'form.currentpath'}) {
Line 10875  sub ask_for_embedded_content { Line 12667  sub ask_for_embedded_content {
             $toplevel = $url;              $toplevel = $url;
             if ($args->{'context'} eq 'paste') {              if ($args->{'context'} eq 'paste') {
                 ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});                  ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
                 ($path) =                  ($path) = 
                     ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});                      ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
                 $fileloc = &Apache::lonnet::filelocation('',$toplevel);                  $fileloc = &Apache::lonnet::filelocation('',$toplevel);
                 $fileloc =~ s{^/}{};                  $fileloc =~ s{^/}{};
             }              }
         }          }
     } elsif ($actionurl eq '/adm/dependencies') {      } elsif ($actionurl eq '/adm/dependencies')  {
         if ($env{'request.course.id'} ne '') {          if ($env{'request.course.id'} ne '') {
             if (ref($args) eq 'HASH') {              if (ref($args) eq 'HASH') {
                 $url = $args->{'docs_url'};                  $url = $args->{'docs_url'};
                 $title = $args->{'docs_title'};                  $title = $args->{'docs_title'};
                 $toplevel = $url;                  $toplevel = $url; 
                 unless ($toplevel =~ m{^/}) {                  unless ($toplevel =~ m{^/}) {
                     $toplevel = "/$url";                      $toplevel = "/$url";
                 }                  }
Line 10920  sub ask_for_embedded_content { Line 12712  sub ask_for_embedded_content {
         $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';          $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
         $fileloc =~ s{^/}{};          $fileloc =~ s{^/}{};
     }      }
       
       # parses the dependency paths to get some info
       # fills $newfiles, $mapping, $subdependencies, $dependencies
       # $newfiles: hash URL -> 1 for new files or external URLs
       # (will be completed later)
       # $mapping:
       #   for external URLs: external URL -> external URL
       #   for relative paths: clean path -> original path
       # $subdependencies: hash clean path -> clean file name -> 1 for relative paths in subdirectories
       # $dependencies: hash clean or not file name -> 1 for relative paths not in subdirectories
     foreach my $file (keys(%{$allfiles})) {      foreach my $file (keys(%{$allfiles})) {
         my $embed_file;          my $embed_file;
         if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {          if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
Line 10962  sub ask_for_embedded_content { Line 12764  sub ask_for_embedded_content {
             }              }
         }          }
     }      }
       
       # looks for all existing files in dependency subdirectories (from $subdependencies filled above)
       # and lists
       # fills $currsubfile, $pathchanges, $existing, $numexisting, $newfiles, $unused
       # $currsubfile: hash clean path -> file name -> 1 for all existing files in the path
       # $pathchanges: hash clean path -> 1 if the file in subdirectory exists and
       #                                    the path had to be cleaned up
       # $existing: hash clean path -> 1 if the file exists
       # $numexisting: number of keys in $existing
       # $newfiles: updated with clean path -> 1 for files in subdirectories that do not exist
       # $unused: only for /adm/dependencies, hash clean path -> 1 for existing files in
       #                                      dependency subdirectories that are
       #                                      not listed as dependencies, with some exceptions using $rem
     my $dirptr = 16384;      my $dirptr = 16384;
     foreach my $path (keys(%subdependencies)) {      foreach my $path (keys(%subdependencies)) {
         $currsubfile{$path} = {};          $currsubfile{$path} = {};
         if (($actionurl eq '/adm/portfolio') ||          if (($actionurl eq '/adm/portfolio') || 
             ($actionurl eq '/adm/coursegrp_portfolio')) {               ($actionurl eq '/adm/coursegrp_portfolio')) {
             my ($sublistref,$listerror) =              my ($sublistref,$listerror) =
                 &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);                  &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
             if (ref($sublistref) eq 'ARRAY') {              if (ref($sublistref) eq 'ARRAY') {
Line 11037  sub ask_for_embedded_content { Line 12852  sub ask_for_embedded_content {
             }              }
         }          }
     }      }
       
       # fills $currfile, hash file name -> 1 or [$size,$mtime]
       # for files in $url or $fileloc (target directory) in some contexts
     my %currfile;      my %currfile;
     if (($actionurl eq '/adm/portfolio') ||      if (($actionurl eq '/adm/portfolio') ||
         ($actionurl eq '/adm/coursegrp_portfolio')) {          ($actionurl eq '/adm/coursegrp_portfolio')) {
Line 11075  sub ask_for_embedded_content { Line 12893  sub ask_for_embedded_content {
             }              }
         }          }
     }      }
       # updates $pathchanges, $existing, $numexisting, $newfiles and $unused for files that
       # are not in subdirectories, using $currfile
     foreach my $file (keys(%dependencies)) {      foreach my $file (keys(%dependencies)) {
         if (exists($currfile{$file})) {          if (exists($currfile{$file})) {
             unless ($mapping{$file} eq $file) {              unless ($mapping{$file} eq $file) {
Line 11103  sub ask_for_embedded_content { Line 12923  sub ask_for_embedded_content {
             $unused{$file} = 1;              $unused{$file} = 1;
         }          }
     }      }
       
       # returns some results for coursedocs paste and syllabus rewrites ($output is undef)
     if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&      if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
         ($args->{'context'} eq 'paste')) {          ($args->{'context'} eq 'paste')) {
         $counter = scalar(keys(%existing));          $counter = scalar(keys(%existing));
         $numpathchg = scalar(keys(%pathchanges));          $numpathchg = scalar(keys(%pathchanges));
         return ($output,$counter,$numpathchg,\%existing);          return ($output,$counter,$numpathchg,\%existing);
     } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") &&      } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") && 
              (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {               (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
         $counter = scalar(keys(%existing));          $counter = scalar(keys(%existing));
         $numpathchg = scalar(keys(%pathchanges));          $numpathchg = scalar(keys(%pathchanges));
         return ($output,$counter,$numpathchg,\%existing,\%mapping);          return ($output,$counter,$numpathchg,\%existing,\%mapping);
     }      }
       
       # returns HTML otherwise, with dependency results and to ask for more uploads
       
       # $upload_output: missing dependencies (with upload form)
       # $modify_output: uploaded dependencies (in use)
       # $delete_output: files no longer in use (unused files are not listed for londocs, bug?)
     foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {      foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
         if ($actionurl eq '/adm/dependencies') {          if ($actionurl eq '/adm/dependencies') {
             next if ($embed_file =~ m{^\w+://});              next if ($embed_file =~ m{^\w+://});
Line 11337  sub ask_for_embedded_content { Line 13165  sub ask_for_embedded_content {
   
 Performs clean-up of directories, subdirectories and filename in an  Performs clean-up of directories, subdirectories and filename in an
 embedded object, referenced in an HTML file which is being uploaded  embedded object, referenced in an HTML file which is being uploaded
 to a course or portfolio, where  to a course or portfolio, where 
 "Upload embedded images/multimedia files if HTML file" checkbox was  "Upload embedded images/multimedia files if HTML file" checkbox was
 checked.  checked.
   
Line 11356  sub clean_path { Line 13184  sub clean_path {
         @contents = ($embed_file);          @contents = ($embed_file);
     }      }
     my $lastidx = scalar(@contents)-1;      my $lastidx = scalar(@contents)-1;
     for (my $i=0; $i<=$lastidx; $i++) {      for (my $i=0; $i<=$lastidx; $i++) { 
         $contents[$i]=~s{\\}{/}g;          $contents[$i]=~s{\\}{/}g;
         $contents[$i]=~s/\s+/\_/g;          $contents[$i]=~s/\s+/\_/g;
         $contents[$i]=~s{[^/\w\.\-]}{}g;          $contents[$i]=~s{[^/\w\.\-]}{}g;
Line 11695  sub modify_html_refs { Line 13523  sub modify_html_refs {
     }      }
     my (%allfiles,%codebase,$output,$content);      my (%allfiles,%codebase,$output,$content);
     my @changes = &get_env_multiple('form.namechange');      my @changes = &get_env_multiple('form.namechange');
     unless ((@changes > 0)  || ($context eq 'syllabus')) {      unless ((@changes > 0) || ($context eq 'syllabus')) {
         if (wantarray) {          if (wantarray) {
             return ('',0,0);               return ('',0,0); 
         } else {          } else {
Line 11830  sub modify_html_refs { Line 13658  sub modify_html_refs {
                         }                          }
                     }                      }
                     if ($rewrites) {                      if ($rewrites) {
                         my $saveresult;                          my $saveresult; 
                         my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);                          my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
                         if ($url eq $container) {                          if ($url eq $container) {
                             my ($fname) = ($container =~ m{/([^/]+)$});                              my ($fname) = ($container =~ m{/([^/]+)$});
Line 12356  sub process_decompression { Line 14184  sub process_decompression {
             }              }
             my $numskip = scalar(@to_skip);              my $numskip = scalar(@to_skip);
             my $numoverwrite = scalar(@to_overwrite);              my $numoverwrite = scalar(@to_overwrite);
             if (($numskip) && (!$numoverwrite)) {              if (($numskip) && (!$numoverwrite)) { 
                 $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');                           $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');         
             } elsif ($dir eq '') {              } elsif ($dir eq '') {
                 $error = &mt('Directory containing archive file unavailable.');                  $error = &mt('Directory containing archive file unavailable.');
Line 12366  sub process_decompression { Line 14194  sub process_decompression {
                     my $tempdir = time.'_'.$$.int(rand(10000));                      my $tempdir = time.'_'.$$.int(rand(10000));
                     mkdir("$dir/$tempdir",0755);                      mkdir("$dir/$tempdir",0755);
                     if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) {                      if (&File::Copy::move("$dir/$file","$dir/$tempdir/$file")) {
                         ($decompressed,$display) =                          ($decompressed,$display) = 
                             &decompress_uploaded_file($file,"$dir/$tempdir");                              &decompress_uploaded_file($file,"$dir/$tempdir");
                         foreach my $item (@to_skip) {                          foreach my $item (@to_skip) {
                             if (($item ne '') && ($item !~ /\.\./)) {                              if (($item ne '') && ($item !~ /\.\./)) {
                                 if (-f "$dir/$tempdir/$item") {                                  if (-f "$dir/$tempdir/$item") { 
                                     unlink("$dir/$tempdir/$item");                                      unlink("$dir/$tempdir/$item");
                                 } elsif (-d "$dir/$tempdir/$item") {                                  } elsif (-d "$dir/$tempdir/$item") {
                                     &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 });                                      &File::Path::remove_tree("$dir/$tempdir/$item",{ safe => 1 });
Line 12410  sub process_decompression { Line 14238  sub process_decompression {
                     if (ref($newdirlistref) eq 'ARRAY') {                      if (ref($newdirlistref) eq 'ARRAY') {
                         foreach my $dir_line (@{$newdirlistref}) {                          foreach my $dir_line (@{$newdirlistref}) {
                             my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);                              my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
                             unless (($item =~ /^\.+$/) || ($item eq $file)) {                               unless (($item =~ /^\.+$/) || ($item eq $file)) {
                                 push(@newitems,$item);                                  push(@newitems,$item);
                                 if ($dirptr&$testdir) {                                  if ($dirptr&$testdir) {
                                     $is_dir{$item} = 1;                                      $is_dir{$item} = 1;
Line 12465  sub process_decompression { Line 14293  sub process_decompression {
                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};                                      $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
                                     $displayed{'folder'} = $i;                                      $displayed{'folder'} = $i;
                                 } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||                                  } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
                                          (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) {                                           (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { 
                                     $env{'form.archive_'.$i} = 'display';                                      $env{'form.archive_'.$i} = 'display';
                                     $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};                                      $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
                                     $displayed{'web'} = $i;                                      $displayed{'web'} = $i;
Line 12917  sub process_extracted_files { Line 14745  sub process_extracted_files {
         $folders{'0'} = $items[-2];          $folders{'0'} = $items[-2];
         if ($env{'form.folderpath'} =~ /\:1$/) {          if ($env{'form.folderpath'} =~ /\:1$/) {
             $containers{'0'}='page';              $containers{'0'}='page';
         } else {          } else {  
             $containers{'0'}='sequence';              $containers{'0'}='sequence';
         }          }
     }      }
Line 12998  sub process_extracted_files { Line 14826  sub process_extracted_files {
                             $newseqid{$i} = $newidx;                              $newseqid{$i} = $newidx;
                             unless ($errtext) {                              unless ($errtext) {
                                 $result .=  '<li>'.&mt('Folder: [_1] added to course',                                  $result .=  '<li>'.&mt('Folder: [_1] added to course',
                                                        &HTML::Entities::encode($docstitle,'<>&"'))..                                                         &HTML::Entities::encode($docstitle,'<>&"')).
                                             '</li>'."\n";                                              '</li>'."\n";
                             }                              }
                         }                          }
Line 13008  sub process_extracted_files { Line 14836  sub process_extracted_files {
                             my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.                              my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
                                       $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.                                        $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
                                       $title;                                        $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}") {                                  if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
                                     mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);                                      mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
                                 }                                  }
Line 13023  sub process_extracted_files { Line 14853  sub process_extracted_files {
                                             $fetch =~ s/^\Q$prefix$dir\E//;                                              $fetch =~ s/^\Q$prefix$dir\E//;
                                             $prompttofetch{$fetch} = 1;                                              $prompttofetch{$fetch} = 1;
                                         }                                          }
                                    }                                      }
                                 }                                  }
                                 $LONCAPA::map::resources[$newidx]=                                  $LONCAPA::map::resources[$newidx]=
                                     $docstitle.':'.$url.':false:normal:res';                                      $docstitle.':'.$url.':false:normal:res';
Line 13048  sub process_extracted_files { Line 14878  sub process_extracted_files {
                 }                  }
             } else {              } else {
                 $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',                  $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',
                                 &HTML::Entities::encode($path,'<>&"')).'<br />';                                  &HTML::Entities::encode($path,'<>&"')).'<br />'; 
             }              }
         }          }
         for (my $i=1; $i<=$numitems; $i++) {          for (my $i=1; $i<=$numitems; $i++) {
Line 13070  sub process_extracted_files { Line 14900  sub process_extracted_files {
                         }                          }
                         if ($itemidx eq '') {                          if ($itemidx eq '') {
                             $itemidx =  0;                              $itemidx =  0;
                         }                          } 
                         if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {                          if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
                             if ($mapinner{$referrer{$i}}) {                              if ($mapinner{$referrer{$i}}) {
                                 $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";                                  $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
Line 13119  sub process_extracted_files { Line 14949  sub process_extracted_files {
                                     $showpath = "$relpath/$title";                                      $showpath = "$relpath/$title";
                                 } else {                                  } else {
                                     $showpath = "/$title";                                      $showpath = "/$title";
                                 }                                  } 
                                 $result .= '<li>'.&mt('[_1] included as a dependency',                                  $result .= '<li>'.&mt('[_1] included as a dependency',
                                                       &HTML::Entities::encode($showpath,'<>&"')).                                                        &HTML::Entities::encode($showpath,'<>&"')).
                                            '</li>'."\n";                                             '</li>'."\n";
                                 unless ($ishome) {                                  unless ($ishome) {
                                     my $fetch = "$fullpath/$title";                                      my $fetch = "$fullpath/$title";
                                     $fetch =~ s/^\Q$prefix$dir\E//;                                      $fetch =~ s/^\Q$prefix$dir\E//; 
                                     $prompttofetch{$fetch} = 1;                                      $prompttofetch{$fetch} = 1;
                                 }                                  }
                             }                              }
Line 14341  generated by lonerrorhandler.pm, CHECKRP Line 16171  generated by lonerrorhandler.pm, CHECKRP
 lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.  lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
   
 Inputs:  Inputs:
 defmail (scalar - email address of default recipient),  defmail (scalar - email address of default recipient), 
 mailing type (scalar: errormail, packagesmail, helpdeskmail,  mailing type (scalar: errormail, packagesmail, helpdeskmail,
 requestsmail, updatesmail, or idconflictsmail).  requestsmail, updatesmail, or idconflictsmail).
   
 defdom (domain for which to retrieve configuration settings),  defdom (domain for which to retrieve configuration settings),
   
 origmail (scalar - email address of recipient from loncapa.conf,  origmail (scalar - email address of recipient from loncapa.conf, 
 i.e., predates configuration by DC via domainprefs.pm  i.e., predates configuration by DC via domainprefs.pm
   
 $requname username of requester (if mailing type is helpdeskmail)  $requname username of requester (if mailing type is helpdeskmail)
Line 14356  $requdom domain of requester (if mailing Line 16186  $requdom domain of requester (if mailing
   
 $reqemail e-mail address of requester (if mailing type is helpdeskmail)  $reqemail e-mail address of requester (if mailing type is helpdeskmail)
   
   
 Returns: comma separated list of addresses to which to send e-mail.  Returns: comma separated list of addresses to which to send e-mail.
   
 =back  =back
Line 14599  sub build_recipient_list { Line 16430  sub build_recipient_list {
   
 =pod  =pod
   
   =over 4
   
   =item * &mime_email()
   
   Sends an email with a possible attachment
   
   Inputs:
   
   =over 4
   
   from -              Sender's email address
   
   replyto -           Reply-To email address
   
   to -                Email address of recipient
   
   subject -           Subject of email
   
   body -              Body of email
   
   cc_string -         Carbon copy email address
   
   bcc -               Blind carbon copy email address
   
   attachment_path -   Path of file to be attached
   
   file_name -         Name of file to be attached
   
   attachment_text -   The body of an attachment of type "TEXT"
   
   =back
   
   =back
   
   =cut
   
   ############################################################
   ############################################################
   
   sub mime_email {
       my ($from,$replyto,$to,$subject,$body,$cc_string,$bcc,$attachment_path, 
           $file_name,$attachment_text) = @_;
    
       my $msg = MIME::Lite->new(
                From    => $from,
                To      => $to,
                Subject => $subject,
                Type    =>'TEXT',
                Data    => $body,
                );
       if ($replyto ne '') {
           $msg->add("Reply-To" => $replyto);
       }
       if ($cc_string ne '') {
           $msg->add("Cc" => $cc_string);
       }
       if ($bcc ne '') {
           $msg->add("Bcc" => $bcc);
       }
       $msg->attr("content-type"         => "text/plain");
       $msg->attr("content-type.charset" => "UTF-8");
       # Attach file if given
       if ($attachment_path) {
           unless ($file_name) {
               if ($attachment_path =~ m-/([^/]+)$-) { $file_name = $1; }
           }
           my ($type, $encoding) = MIME::Types::by_suffix($attachment_path);
           $msg->attach(Type     => $type,
                        Path     => $attachment_path,
                        Filename => $file_name
                        );
       # Otherwise attach text if given
       } elsif ($attachment_text) {
           $msg->attach(Type => 'TEXT',
                        Data => $attachment_text);
       }
       # Send it
       $msg->send('sendmail');
   }
   
   ############################################################
   ############################################################
   
   =pod
   
 =head1 Course Catalog Routines  =head1 Course Catalog Routines
   
 =over 4  =over 4
Line 14703  sub extract_categories { Line 16619  sub extract_categories {
                     $trailstr = &mt('Official courses (with institutional codes)');                      $trailstr = &mt('Official courses (with institutional codes)');
                 } elsif ($name eq 'communities') {                  } elsif ($name eq 'communities') {
                     $trailstr = &mt('Communities');                      $trailstr = &mt('Communities');
                   } elsif ($name eq 'placement') {
                       $trailstr = &mt('Placement Tests');
                 } else {                  } else {
                     $trailstr = $name;                      $trailstr = $name;
                 }                  }
Line 14769  sub recurse_categories { Line 16687  sub recurse_categories {
         for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {          for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
             my $name = $cats->[$depth]{$category}[$k];              my $name = $cats->[$depth]{$category}[$k];
             my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;              my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
             my $trailstr = join(' -&gt; ',(@{$parents},$category));              my $trailstr = join(' &raquo; ',(@{$parents},$category));
             if ($allitems->{$item} eq '') {              if ($allitems->{$item} eq '') {
                 push(@{$trails},$trailstr);                  push(@{$trails},$trailstr);
                 $allitems->{$item} = scalar(@{$trails})-1;                  $allitems->{$item} = scalar(@{$trails})-1;
Line 14852  sub assign_categories_table { Line 16770  sub assign_categories_table {
                     next if ($parent eq 'instcode');                      next if ($parent eq 'instcode');
                     if ($type eq 'Community') {                      if ($type eq 'Community') {
                         next unless ($parent eq 'communities');                          next unless ($parent eq 'communities');
                       } elsif ($type eq 'Placement') {
                           next unless ($parent eq 'placement');
                     } else {                      } else {
                         next if ($parent eq 'communities');                          next if (($parent eq 'communities') || ($parent eq 'placement'));
                     }                      }
                     my $css_class = $itemcount%2?' class="LC_odd_row"':'';                      my $css_class = $itemcount%2?' class="LC_odd_row"':'';
                     my $item = &escape($parent).'::0';                      my $item = &escape($parent).'::0';
Line 14866  sub assign_categories_table { Line 16786  sub assign_categories_table {
                     my $parent_title = $parent;                      my $parent_title = $parent;
                     if ($parent eq 'communities') {                      if ($parent eq 'communities') {
                         $parent_title = &mt('Communities');                          $parent_title = &mt('Communities');
                       } elsif ($parent eq 'placement') {
                           $parent_title = &mt('Placement Tests');
                     }                      }
                     $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.                      $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
                               '<input type="checkbox" name="usecategory" value="'.                                '<input type="checkbox" name="usecategory" value="'.
Line 14971  sub assign_category_rows { Line 16893  sub assign_category_rows {
   
   
 sub commit_customrole {  sub commit_customrole {
     my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;      my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context,$othdomby,$requester) = @_;
       my $result = &Apache::lonnet::assigncustomrole(
                        $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,
                        $context,$othdomby,$requester);
     my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.      my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
                          ($start?', '.&mt('starting').' '.localtime($start):'').                           ($start?', '.&mt('starting').' '.localtime($start):'').
                          ($end?', ending '.localtime($end):'').': <b>'.                           ($end?', ending '.localtime($end):'').': <b>'.$result.'</b><br />';
               &Apache::lonnet::assigncustomrole(      if (wantarray) {
                  $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).          return ($output,$result);
                  '</b><br />';      } else {
     return $output;          return $output;
       }
 }  }
   
 sub commit_standardrole {  sub commit_standardrole {
     my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;      my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits,
     my ($output,$logmsg,$linefeed);          $othdomby,$requester) = @_;
       my ($output,$logmsg,$linefeed,$result);
     if ($context eq 'auto') {      if ($context eq 'auto') {
         $linefeed = "\n";          $linefeed = "\n";
     } else {      } else {
         $linefeed = "<br />\n";          $linefeed = "<br />\n";
     }        }  
     if ($three eq 'st') {      if ($three eq 'st') {
         my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,          $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
                                          $one,$two,$sec,$context,$credits);                                        $one,$two,$sec,$context,$credits,$othdomby,
                                         $requester);
         if (($result =~ /^error/) || ($result eq 'not_in_class') ||           if (($result =~ /^error/) || ($result eq 'not_in_class') || 
             ($result eq 'unknown_course') || ($result eq 'refused')) {              ($result eq 'unknown_course') || ($result eq 'refused')) {
             $output = $logmsg.' '.&mt('Error: ').$result."\n";               $output = $logmsg.' '.&mt('Error: ').$result."\n"; 
Line 15011  sub commit_standardrole { Line 16939  sub commit_standardrole {
         $output = &mt('Assigning').' '.$three.' in '.$url.          $output = &mt('Assigning').' '.$three.' in '.$url.
                ($start?', '.&mt('starting').' '.localtime($start):'').                 ($start?', '.&mt('starting').' '.localtime($start):'').
                ($end?', '.&mt('ending').' '.localtime($end):'').': ';                 ($end?', '.&mt('ending').' '.localtime($end):'').': ';
         my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);          $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,
                                                 '','',$context,$othdomby,$requester);
         if ($context eq 'auto') {          if ($context eq 'auto') {
             $output .= $result.$linefeed;              $output .= $result.$linefeed;
         } else {          } else {
             $output .= '<b>'.$result.'</b>'.$linefeed;              $output .= '<b>'.$result.'</b>'.$linefeed;
         }          }
     }      }
     return $output;      if (wantarray) {
           return ($output,$result);
       } else {
           return $output;
       }
 }  }
   
 sub commit_studentrole {  sub commit_studentrole {
     my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,      my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
         $credits) = @_;          $credits,$othdomby,$requester) = @_;
     my ($result,$linefeed,$oldsecurl,$newsecurl);      my ($result,$linefeed,$oldsecurl,$newsecurl);
     if ($context eq 'auto') {      if ($context eq 'auto') {
         $linefeed = "\n";          $linefeed = "\n";
Line 15047  sub commit_studentrole { Line 16980  sub commit_studentrole {
                 }                  }
                 $oldsecurl = $uurl;                  $oldsecurl = $uurl;
                 $expire_role_result =                   $expire_role_result = 
                     &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);                      &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,
                 if ($env{'request.course.sec'} ne '') {                                                   '','','',$context,$othdomby,$requester);
                   if ($env{'request.course.sec'} ne '') {
                     if ($expire_role_result eq 'refused') {                      if ($expire_role_result eq 'refused') {
                         my @roles = ('st');                          my @roles = ('st');
                         my @statuses = ('previous');                          my @statuses = ('previous');
Line 15074  sub commit_studentrole { Line 17008  sub commit_studentrole {
                 &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,                  &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
                                                            undef,undef,undef,$sec,                                                             undef,undef,undef,$sec,
                                                            $end,$start,'','',$cid,                                                             $end,$start,'','',$cid,
                                                            '',$context,$credits);                                                             '',$context,$credits,'',
                                                              $othdomby,$requester);
             if ($modify_section_result =~ /^ok/) {              if ($modify_section_result =~ /^ok/) {
                 if ($secchange == 1) {                  if ($secchange == 1) {
                     if ($sec eq '') {                      if ($sec eq '') {
Line 15096  sub commit_studentrole { Line 17031  sub commit_studentrole {
                     }                      }
                 }                  }
             } else {              } else {
                 if ($secchange) {                         if ($secchange) { 
                     $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;                      $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;
                 } else {                  } else {
                     $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;                      $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
Line 15216  sub check_clone { Line 17151  sub check_clone {
                             if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {                              if ($args->{'ccdomain'} eq $args->{'clonedomain'}) {
                                 $can_clone = 1;                                  $can_clone = 1;
                             }                              }
                         } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&                          } elsif (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                                  ($args->{'clonedomain'} eq  $args->{'course_domain'})) {                                   ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
                             if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},                              if (&Apache::lonnet::default_instcode_cloning($args->{'clonedomain'},$domdefs{'canclone'},
                                                                           $clonehash{'internal.coursecode'},$args->{'crscode'})) {                                                                            $clonehash{'internal.coursecode'},$args->{'crscode'})) {
Line 15235  sub check_clone { Line 17170  sub check_clone {
                     $can_clone = 1;                      $can_clone = 1;
                 }                  }
                 unless ($can_clone) {                  unless ($can_clone) {
                     if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) &&                      if (($clonehash{'internal.coursecode'}) && ($args->{'crscode'}) && 
                         ($args->{'clonedomain'} eq  $args->{'course_domain'})) {                          ($args->{'clonedomain'} eq  $args->{'course_domain'})) {
                         my (%gotdomdefaults,%gotcodedefaults);                          my (%gotdomdefaults,%gotcodedefaults);
                         foreach my $cloner (@cloners) {                          foreach my $cloner (@cloners) {
Line 15274  sub check_clone { Line 17209  sub check_clone {
                 if ($args->{'crstype'} eq 'Community') {                  if ($args->{'crstype'} eq 'Community') {
                     $ccrole = 'co';                      $ccrole = 'co';
                 }                  }
                 my %roleshash =          my %roleshash =
                     &Apache::lonnet::get_my_roles($args->{'ccuname'},      &Apache::lonnet::get_my_roles($args->{'ccuname'},
                                                   $args->{'ccdomain'},            $args->{'ccdomain'},
                                                   'userroles',['active'],[$ccrole],                                                    'userroles',['active'],[$ccrole],
                                                   [$args->{'clonedomain'}]);            [$args->{'clonedomain'}]);
                 if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {          if ($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) {
                     $can_clone = 1;                      $can_clone = 1;
                 } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},                  } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},
                                                           $args->{'ccuname'},$args->{'ccdomain'})) {                                                            $args->{'ccuname'},$args->{'ccdomain'})) {
Line 15335  sub construct_course { Line 17270  sub construct_course {
 #  #
 # Open course  # Open course
 #  #
     my $crstype = lc($args->{'crstype'});      my $showncrstype;
       if ($args->{'crstype'} eq 'Placement') {
           $showncrstype = 'placement test'; 
       } else {  
           $showncrstype = lc($args->{'crstype'});
       }
     my %cenv=();      my %cenv=();
     $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},      $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
                                              $args->{'cdescr'},                                               $args->{'cdescr'},
Line 15354  sub construct_course { Line 17294  sub construct_course {
     # if anyone ever decides to not show this, and Utils::Course::new      # if anyone ever decides to not show this, and Utils::Course::new
     # will need to be suitably modified.      # will need to be suitably modified.
     if (($callercontext eq 'auto') && ($user_lh ne '')) {      if (($callercontext eq 'auto') && ($user_lh ne '')) {
         $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;          $outcome .= &mt_user($user_lh,'New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
     } else {      } else {
         $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;          $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$showncrstype,$$courseid).$linefeed;
     }      }
     if ($$courseid =~ /^error:/) {      if ($$courseid =~ /^error:/) {
         return (0,$outcome,$clonemsgref);          return (0,$outcome,$clonemsgref);
Line 15387  sub construct_course { Line 17327  sub construct_course {
         push(@clonemsg,          push(@clonemsg,
                       {                        {
                           mt => 'Created [_1] by cloning from [_2]',                            mt => 'Created [_1] by cloning from [_2]',
                           args => [$crstype,$clonetitle],                            args => [$showncrstype,$clonetitle],
                       });                        });
  my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);   my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
 # Copy all files  # Copy all files
         my @info =          my @info =
     &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},      &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},
                                                      $args->{'dateshift'},$args->{'crscode'},                                               $args->{'dateshift'},$args->{'crscode'},
                                                      $args->{'ccuname'}.':'.$args->{'ccdomain'},                                                       $args->{'ccuname'}.':'.$args->{'ccdomain'},
                                                      $args->{'tinyurls'});                                                       $args->{'tinyurls'});
         if (@info) {          if (@info) {
Line 15423  sub construct_course { Line 17363  sub construct_course {
                    'plc.users.denied',                     'plc.users.denied',
                    'hidefromcat',                     'hidefromcat',
                    'checkforpriv',                     'checkforpriv',
                    'categories',                     'categories'],
                    'internal.uniquecode'],  
                    $$crsudom,$$crsunum);                     $$crsudom,$$crsunum);
         if ($args->{'textbook'}) {          if ($args->{'textbook'}) {
             $cenv{'internal.textbook'} = $args->{'textbook'};              $cenv{'internal.textbook'} = $args->{'textbook'};
Line 15439  sub construct_course { Line 17378  sub construct_course {
     if ($args->{'crstype'}) {      if ($args->{'crstype'}) {
         $cenv{'type'}=$args->{'crstype'};          $cenv{'type'}=$args->{'crstype'};
     }      }
       if ($args->{'lti'}) {
           $cenv{'internal.lti'}=$args->{'lti'};
       }
     if ($args->{'crsid'}) {      if ($args->{'crsid'}) {
         $cenv{'courseid'}=$args->{'crsid'};          $cenv{'courseid'}=$args->{'crsid'};
     }      }
Line 15460  sub construct_course { Line 17402  sub construct_course {
         $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};          $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
     }      }
     my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.      my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
       my @oklcsecs = (); # Used to accumulate LON-CAPA sections for validated institutional sections.
     if ($args->{'crssections'}) {      if ($args->{'crssections'}) {
         $cenv{'internal.sectionnums'} = '';          $cenv{'internal.sectionnums'} = '';
         if ($args->{'crssections'} =~ m/,/) {          if ($args->{'crssections'} =~ m/,/) {
Line 15473  sub construct_course { Line 17416  sub construct_course {
                 my $class = $args->{'crscode'}.$sec;                  my $class = $args->{'crscode'}.$sec;
                 my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});                  my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
                 $cenv{'internal.sectionnums'} .= $item.',';                  $cenv{'internal.sectionnums'} .= $item.',';
                 unless ($addcheck eq 'ok') {                  if ($addcheck eq 'ok') {
                       unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
                           push(@oklcsecs,$gp);
                       }
                   } else {
                     push(@badclasses,$class);                      push(@badclasses,$class);
                 }                  }
             }              }
Line 15501  sub construct_course { Line 17448  sub construct_course {
                 my ($xl,$gp) = split/:/,$item;                  my ($xl,$gp) = split/:/,$item;
                 my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});                  my $addcheck =  &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
                 $cenv{'internal.crosslistings'} .= $item.',';                  $cenv{'internal.crosslistings'} .= $item.',';
                 unless ($addcheck eq 'ok') {                  if ($addcheck eq 'ok') {
                       unless (grep(/^\Q$gp\E$/,@oklcsecs)) {
                           push(@oklcsecs,$gp);
                       }
                   } else {
                     push(@badclasses,$xl);                      push(@badclasses,$xl);
                 }                  }
             }              }
Line 15559  sub construct_course { Line 17510  sub construct_course {
             $outcome .= $linefeed;              $outcome .= $linefeed;
         } else {          } else {
             $outcome .= "</ul><br /><br /></div>\n";              $outcome .= "</ul><br /><br /></div>\n";
         }          } 
     }      }
     if ($args->{'no_end_date'}) {      if ($args->{'no_end_date'}) {
         $args->{'endaccess'} = 0;          $args->{'endaccess'} = 0;
     }      }
   #  If an official course with institutional sections is created by cloning 
   #  an existing course, section-specific hiding of course totals in student's
   #  view of grades as copied from cloned course, will be checked for valid 
   #  sections.
       if (($can_clone && $cloneid) &&
           ($cenv{'internal.coursecode'} ne '') &&
           ($cenv{'grading'} eq 'standard') &&
           ($cenv{'hidetotals'} ne '') &&
           ($cenv{'hidetotals'} ne 'all')) {
           my @hidesecs;
           my $deletehidetotals;
           if (@oklcsecs) {
               foreach my $sec (split(/,/,$cenv{'hidetotals'})) {
                   if (grep(/^\Q$sec$/,@oklcsecs)) {
                       push(@hidesecs,$sec);
                   }
               }
               if (@hidesecs) {
                   $cenv{'hidetotals'} = join(',',@hidesecs);
               } else {
                   $deletehidetotals = 1;
               }
           } else {
               $deletehidetotals = 1;
           }
           if ($deletehidetotals) {
               delete($cenv{'hidetotals'});
               &Apache::lonnet::del('environment',['hidetotals'],$$crsudom,$$crsunum);
           }
       }
     $cenv{'internal.autostart'}=$args->{'enrollstart'};      $cenv{'internal.autostart'}=$args->{'enrollstart'};
     $cenv{'internal.autoend'}=$args->{'enrollend'};      $cenv{'internal.autoend'}=$args->{'enrollend'};
     $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};      $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
Line 15628  sub construct_course { Line 17609  sub construct_course {
             if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {              if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
                 $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;                  $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
                 my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');                  my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
             }              } 
             if (ref($coderef)) {              if (ref($coderef)) {
                 $$coderef = $code;                  $$coderef = $code;
             }              }
Line 15682  sub construct_course { Line 17663  sub construct_course {
 #  #
     unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')      unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
     || ($cloneid)) {      || ($cloneid)) {
  use LONCAPA::map;  
  $outcome .= &mt('Setting first resource').': ';   $outcome .= &mt('Setting first resource').': ';
   
  my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';   my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
Line 15705  sub construct_course { Line 17685  sub construct_course {
         $outcome .= ($fatal?$errtext:'write ok').$linefeed;          $outcome .= ($fatal?$errtext:'write ok').$linefeed;
     }      }
   
   # 
   # Set params for Placement Tests
   #
       if ($args->{'crstype'} eq 'Placement') {
          my %storecontent; 
          my $prefix=$$crsudom.'_'.$$crsunum.'.0.';
          my %defaults = (
                           buttonshide   => { value => 'yes',
                                              type => 'string_yesno',},
                           type          => { value => 'randomizetry',
                                              type  => 'string_questiontype',},
                           maxtries      => { value => 1,
                                              type => 'int_pos',},
                           problemstatus => { value => 'no',
                                              type  => 'string_problemstatus',},
                         );
          foreach my $key (keys(%defaults)) {
              $storecontent{$prefix.$key} = $defaults{$key}{'value'};
              $storecontent{$prefix.$key.'.type'} = $defaults{$key}{'type'};
          }
          &Apache::lonnet::cput
                    ('resourcedata',\%storecontent,$$crsudom,$$crsunum); 
       }
   
     return (1,$outcome,\@clonemsg);      return (1,$outcome,\@clonemsg);
 }  }
   
Line 15718  sub make_unique_code { Line 17722  sub make_unique_code {
     my $tries = 0;      my $tries = 0;
     my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);      my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
     my ($code,$error);      my ($code,$error);
     
     while (($gotlock ne 'ok') && ($tries<3)) {      while (($gotlock ne 'ok') && ($tries<3)) {
         $tries ++;          $tries ++;
         sleep 1;          sleep 1;
Line 15765  sub generate_code { Line 17769  sub generate_code {
 ############################################################  ############################################################
 ############################################################  ############################################################
   
 #SD  # Community, Course and Placement Test
 # only Community and Course, or anything else?  
 sub course_type {  sub course_type {
     my ($cid) = @_;      my ($cid) = @_;
     if (!defined($cid)) {      if (!defined($cid)) {
Line 15784  sub group_term { Line 17787  sub group_term {
     my %names = (      my %names = (
                   'Course' => 'group',                    'Course' => 'group',
                   'Community' => 'group',                    'Community' => 'group',
                     'Placement' => 'group',
                 );                  );
     return $names{$crstype};      return $names{$crstype};
 }  }
   
 sub course_types {  sub course_types {
     my @types = ('official','unofficial','community','textbook');      my @types = ('official','unofficial','community','textbook','placement','lti');
     my %typename = (      my %typename = (
                          official   => 'Official course',                           official   => 'Official course',
                          unofficial => 'Unofficial course',                           unofficial => 'Unofficial course',
                          community  => 'Community',                           community  => 'Community',
                          textbook   => 'Textbook course',                           textbook   => 'Textbook course',
                            placement  => 'Placement test',
                            lti        => 'LTI provider',
                    );                     );
     return (\@types,\%typename);      return (\@types,\%typename);
 }  }
Line 15874  sub compare_arrays { Line 17880  sub compare_arrays {
     return @difference;      return @difference;
 }  }
   
   sub lon_status_items {
       my %defaults = (
                        E         => 100,
                        W         => 4,
                        N         => 1,
                        U         => 5,
                        threshold => 200,
                        sysmail   => 2500,
                      );
       my %names = (
                      E => 'Errors',
                      W => 'Warnings',
                      N => 'Notices',
                      U => 'Unsent',
                   );
       return (\%defaults,\%names);
   }
   
 # -------------------------------------------------------- Initialize user login  # -------------------------------------------------------- Initialize user login
 sub init_user_environment {  sub init_user_environment {
     my ($r, $username, $domain, $authhost, $form, $args) = @_;      my ($r, $username, $domain, $authhost, $form, $args) = @_;
Line 15881  sub init_user_environment { Line 17905  sub init_user_environment {
   
     my $public=($username eq 'public' && $domain eq 'public');      my $public=($username eq 'public' && $domain eq 'public');
   
 # See if old ID present, if so, remove      my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv,
           $coauthorenv);
     my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);  
     my $now=time;      my $now=time;
   
     if ($public) {      if ($public) {
Line 15904  sub init_user_environment { Line 17927  sub init_user_environment {
  }   }
  if (!$cookie) { $cookie="publicuser_$oldest"; }   if (!$cookie) { $cookie="publicuser_$oldest"; }
     } else {      } else {
  # if this isn't a robot, kill any existing non-robot sessions   # See if old ID present, if so, remove if this isn't a robot,
    # killing any existing non-robot sessions
  if (!$args->{'robot'}) {   if (!$args->{'robot'}) {
     opendir(DIR,$lonids);      opendir(DIR,$lonids);
     while ($filename=readdir(DIR)) {      while ($filename=readdir(DIR)) {
Line 15948  sub init_user_environment { Line 17972  sub init_user_environment {
           
 # Initialize roles  # Initialize roles
   
  ($userroles,$firstaccenv,$timerintenv) =    ($userroles,$firstaccenv,$timerintenv,$coauthorenv) = 
             &Apache::lonnet::rolesinit($domain,$username,$authhost);              &Apache::lonnet::rolesinit($domain,$username,$authhost);
     }      }
 # ------------------------------------ Check browser type and MathML capability  # ------------------------------------ Check browser type and MathML capability
Line 15960  sub init_user_environment { Line 17984  sub init_user_environment {
   
     my %userenv = &Apache::lonnet::dump('environment',$domain,$username);      my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
     my ($tmp) = keys(%userenv);      my ($tmp) = keys(%userenv);
     if ($tmp !~ /^(con_lost|error|no_such_host)/i) {      if ($tmp =~ /^(con_lost|error|no_such_host)/i) {
     } else {  
  undef(%userenv);   undef(%userenv);
     }      }
     if (($userenv{'interface'}) && (!$form->{'interface'})) {      if (($userenv{'interface'}) && (!$form->{'interface'})) {
Line 15976  sub init_user_environment { Line 17999  sub init_user_environment {
 # --------------------------------------------------------- Write first profile  # --------------------------------------------------------- Write first profile
   
     {      {
         my $ip = &Apache::lonnet::get_requestor_ip();          my $ip = &Apache::lonnet::get_requestor_ip($r);
  my %initial_env =    my %initial_env = 
     ("user.name"          => $username,      ("user.name"          => $username,
      "user.domain"        => $domain,       "user.domain"        => $domain,
Line 16027  sub init_user_environment { Line 18050  sub init_user_environment {
             my %is_adv = ( is_adv => $env{'user.adv'} );              my %is_adv = ( is_adv => $env{'user.adv'} );
             my %domdef = &Apache::lonnet::get_domain_defaults($domain);              my %domdef = &Apache::lonnet::get_domain_defaults($domain);
   
             foreach my $tool ('aboutme','blog','webdav','portfolio') {              foreach my $tool ('aboutme','blog','webdav','portfolio','portaccess','timezone') {
                 $userenv{'availabletools.'.$tool} =                   $userenv{'availabletools.'.$tool} =
                     &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',                      &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
                                                       undef,\%userenv,\%domdef,\%is_adv);                                                        undef,\%userenv,\%domdef,\%is_adv);
             }              }
   
             foreach my $crstype ('official','unofficial','community','textbook') {              foreach my $crstype ('official','unofficial','community','textbook','placement','lti') {
                 $userenv{'canrequest.'.$crstype} =                  $userenv{'canrequest.'.$crstype} =
                     &Apache::lonnet::usertools_access($username,$domain,$crstype,                      &Apache::lonnet::usertools_access($username,$domain,$crstype,
                                                       'reload','requestcourses',                                                        'reload','requestcourses',
                                                       \%userenv,\%domdef,\%is_adv);                                                        \%userenv,\%domdef,\%is_adv);
             }              }
   
               if ((ref($userroles) eq 'HASH') && ($userroles->{'user.author'}) &&
                   (exists($userroles->{"user.role.au./$domain/"}))) {
                   if ($userenv{'authoreditors'}) {
                       $userenv{'editors'} = $userenv{'authoreditors'};
                   } elsif ($domdef{'editors'} ne '') {
                       $userenv{'editors'} = $domdef{'editors'};
                   } else {
                       $userenv{'editors'} = 'edit,xml';
                   }
               }
   
             $userenv{'canrequest.author'} =              $userenv{'canrequest.author'} =
                 &Apache::lonnet::usertools_access($username,$domain,'requestauthor',                  &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
                                                   'reload','requestauthor',                                                    'reload','requestauthor',
Line 16047  sub init_user_environment { Line 18081  sub init_user_environment {
             my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],              my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
                                                  $domain,$username);                                                   $domain,$username);
             my $reqstatus = $reqauthor{'author_status'};              my $reqstatus = $reqauthor{'author_status'};
             if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {              if ($reqstatus eq 'approval' || $reqstatus eq 'approved') { 
                 if (ref($reqauthor{'author'}) eq 'HASH') {                  if (ref($reqauthor{'author'}) eq 'HASH') {
                     $userenv{'requestauthorqueued'} = $reqstatus.':'.                      $userenv{'requestauthorqueued'} = $reqstatus.':'.
                                                       $reqauthor{'author'}{'timestamp'};                                                        $reqauthor{'author'}{'timestamp'};
                 }                  }
             }              }
               my ($types,$typename) = &course_types();
               if (ref($types) eq 'ARRAY') {
                   my @options = ('approval','validate','autolimit');
                   my $optregex = join('|',@options);
                   my (%willtrust,%trustchecked);
                   foreach my $type (@{$types}) {
                       my $dom_str = $env{'environment.reqcrsotherdom.'.$type};
                       if ($dom_str ne '') {
                           my $updatedstr = '';
                           my @possdomains = split(',',$dom_str);
                           foreach my $entry (@possdomains) {
                               my ($extdom,$extopt) = split(':',$entry);
                               unless ($trustchecked{$extdom}) {
                                   $willtrust{$extdom} = &Apache::lonnet::will_trust('reqcrs',$domain,$extdom);
                                   $trustchecked{$extdom} = 1;
                               }
                               if ($willtrust{$extdom}) {
                                   $updatedstr .= $entry.',';
                               }
                           }
                           $updatedstr =~ s/,$//;
                           if ($updatedstr) {
                               $userenv{'reqcrsotherdom.'.$type} = $updatedstr;
                           } else {
                               delete($userenv{'reqcrsotherdom.'.$type});
                           }
                       }
                   }
               }
         }          }
   
  $env{'user.environment'} = "$lonids/$cookie.id";   $env{'user.environment'} = "$lonids/$cookie.id";
   
  if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",   if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
Line 16068  sub init_user_environment { Line 18130  sub init_user_environment {
             if (ref($timerintenv) eq 'HASH') {              if (ref($timerintenv) eq 'HASH') {
                 &_add_to_env(\%disk_env,$timerintenv);                  &_add_to_env(\%disk_env,$timerintenv);
             }              }
               if (ref($coauthorenv) eq 'HASH') {
                   if (keys(%{$coauthorenv})) {
                       &_add_to_env(\%disk_env,$coauthorenv);
                   }
               }
     if (ref($args->{'extra_env'})) {      if (ref($args->{'extra_env'})) {
  &_add_to_env(\%disk_env,$args->{'extra_env'});   &_add_to_env(\%disk_env,$args->{'extra_env'});
     }      }
Line 16158  and quotacheck.pl Line 18225  and quotacheck.pl
   
 Inputs:  Inputs:
   
 filterlist - anonymous array of fields to include as potential filters  filterlist - anonymous array of fields to include as potential filters 
   
 crstype - course type  crstype - course type
   
 roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used  roleelement - fifth arg in selectcourse_link() populates fifth arg in javascript: opencrsbrowser() function, used
               to pop-open a course selector (will contain "extra element").                to pop-open a course selector (will contain "extra element"). 
   
 multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1  multelement - if multiple course selections will be allowed, this will be a hidden form element: name: multiple; value: 1
   
Line 16179  cloneruname - username of owner of new c Line 18246  cloneruname - username of owner of new c
   
 clonerudom - domain of owner of new course who wants to clone  clonerudom - domain of owner of new course who wants to clone
   
 typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community)  typeelem - text to use for left column in row containing course type (i.e., Course, Community or Course/Community) 
   
 codetitlesref - reference to array of titles of components in institutional codes (official courses)  codetitlesref - reference to array of titles of components in institutional codes (official courses)
   
 codedom - domain  codedom - domain
   
 formname - value of form element named "form".  formname - value of form element named "form". 
   
 fixeddom - domain, if fixed.  fixeddom - domain, if fixed.
   
 prevphase - value to assign to form element named "phase" when going back to the previous screen  prevphase - value to assign to form element named "phase" when going back to the previous screen  
   
 cnameelement - name of form element in form on opener page which will receive title of selected course  cnameelement - name of form element in form on opener page which will receive title of selected course 
   
 cnumelement - name of form element in form on opener page which will receive courseID  of selected course  cnumelement - name of form element in form on opener page which will receive courseID  of selected course
   
Line 16282  sub build_filters { Line 18349  sub build_filters {
         $createdfilterform = &timebased_select_form('createdfilter',$filter);          $createdfilterform = &timebased_select_form('createdfilter',$filter);
     }      }
   
       my $prefix = $crstype;
       if ($crstype eq 'Placement') {
           $prefix = 'Placement Test'
       }
     my %lt = &Apache::lonlocal::texthash(      my %lt = &Apache::lonlocal::texthash(
                 'cac' => "$crstype Activity",                  'cac' => "$prefix Activity",
                 'ccr' => "$crstype Created",                  'ccr' => "$prefix Created",
                 'cde' => "$crstype Title",                  'cde' => "$prefix Title",
                 'cdo' => "$crstype Domain",                  'cdo' => "$prefix Domain",
                 'ins' => 'Institutional Code',                  'ins' => 'Institutional Code',
                 'inc' => 'Institutional Categorization',                  'inc' => 'Institutional Categorization',
                 'cow' => "$crstype Owner/Co-owner",                  'cow' => "$prefix Owner/Co-owner",
                 'cop' => "$crstype Personnel Includes",                  'cop' => "$prefix Personnel Includes",
                 'cog' => 'Type',                  'cog' => 'Type',
              );               );
   
Line 16298  sub build_filters { Line 18369  sub build_filters {
         my $typeval = 'Course';          my $typeval = 'Course';
         if ($crstype eq 'Community') {          if ($crstype eq 'Community') {
             $typeval = 'Community';              $typeval = 'Community';
           } elsif ($crstype eq 'Placement') {
               $typeval = 'Placement';
         }          }
         $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';          $typeselectform = '<input type="hidden" name="type" value="'.$typeval.'" />';
     } else {      } else {
Line 16306  sub build_filters { Line 18379  sub build_filters {
             $typeselectform .= ' onchange="'.$onchange.'"';              $typeselectform .= ' onchange="'.$onchange.'"';
         }          }
         $typeselectform .= '>'."\n";          $typeselectform .= '>'."\n";
         foreach my $posstype ('Course','Community') {          foreach my $posstype ('Course','Community','Placement') {
               my $shown;
               if ($posstype eq 'Placement') {
                   $shown = &mt('Placement Test');
               } else {
                   $shown = &mt($posstype);
               }
             $typeselectform.='<option value="'.$posstype.'"'.              $typeselectform.='<option value="'.$posstype.'"'.
                 ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".&mt($posstype)."</option>\n";                  ($posstype eq $crstype ? ' selected="selected" ' : ''). ">".$shown."</option>\n";
         }          }
         $typeselectform.="</select>";          $typeselectform.="</select>";
     }      }
Line 16333  sub build_filters { Line 18412  sub build_filters {
         if (exists($filter->{'instcodefilter'})) {          if (exists($filter->{'instcodefilter'})) {
 #            if (($fixeddom) || ($formname eq 'requestcrs') ||  #            if (($fixeddom) || ($formname eq 'requestcrs') ||
 #                ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {  #                ($formname eq 'modifycourse') || ($formname eq 'filterpicker')) {
             if ($codedom) {              if ($codedom) { 
                 $officialjs = 1;                  $officialjs = 1;
                 ($instcodeform,$jscript,$$numtitlesref) =                  ($instcodeform,$jscript,$$numtitlesref) =
                     &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',                      &Apache::courseclassifier::instcode_selectors($codedom,'filterpicker',
Line 16462  $typeelement Line 18541  $typeelement
     return $jscript.$clonewarning.$output;      return $jscript.$clonewarning.$output;
 }  }
   
 =pod  =pod 
   
 =item * &timebased_select_form()  =item * &timebased_select_form()
   
Line 16477  item - name of form element (sincefilter Line 18556  item - name of form element (sincefilter
 filter - anonymous hash of criteria and their values  filter - anonymous hash of criteria and their values
   
 Returns: HTML for a select box contained a blank, then six time selections,  Returns: HTML for a select box contained a blank, then six time selections,
          with value set in incoming form variables currently selected.           with value set in incoming form variables currently selected. 
   
 Side Effects: None  Side Effects: None
   
Line 16514  page load completion for page showing se Line 18593  page load completion for page showing se
   
 Inputs: None  Inputs: None
   
 Returns: markup containing updateFilters() and hideSearching() javascript functions.  Returns: markup containing updateFilters() and hideSearching() javascript functions. 
   
 Side Effects: None  Side Effects: None
   
Line 16553  to retrieve a hash for which keys are co Line 18632  to retrieve a hash for which keys are co
   
 Inputs:  Inputs:
   
 dom - domain being searched  dom - domain being searched 
   
 type - course type ('Course' or 'Community' or '.' if any).  type - course type ('Course' or 'Community' or '.' if any).
   
Line 16565  cloneruname - optional username of new c Line 18644  cloneruname - optional username of new c
   
 clonerudom - optional domain of new course owner  clonerudom - optional domain of new course owner
   
 domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by,  domcloner - optional "domcloner" flag; has value=1 if user has ccc priv in domain being filtered by, 
             (used when DC is using course creation form)              (used when DC is using course creation form)
   
 codetitles - reference to array of titles of components in institutional codes (official courses).  codetitles - reference to array of titles of components in institutional codes (official courses).
Line 16575  cc_clone - escaped comma separated list Line 18654  cc_clone - escaped comma separated list
   
 reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone  reqcrsdom - domain of new course, where search_courses is used to identify potential courses to clone
   
 reqinstcode - institutional code of new course, where search_courses is used to identify potential  reqinstcode - institutional code of new course, where search_courses is used to identify potential 
               courses to clone                courses to clone 
   
 Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.  Returns: %courses - hash of courses satisfying search criteria, keys = course IDs, values are corresponding colon-separated escaped description, institutional code, owner and type.
   
Line 16702  $required - LON-CAPA version needed by c Line 18781  $required - LON-CAPA version needed by c
   
 Returns:  Returns:
   
 $switchserver - query string tp append to /adm/switchserver call (if  $switchserver - query string tp append to /adm/switchserver call (if 
                 current server's LON-CAPA version is too old.                  current server's LON-CAPA version is too old. 
   
 $warning - Message is displayed if no suitable server could be found.  $warning - Message is displayed if no suitable server could be found.
   
Line 16816  Inputs: Line 18895  Inputs:
 $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)  $loncaparev - Version on current server (format: Major.Minor.Subrelease-datestamp)
   
 $interval (optional) - Time which may elapse (in s) between last check for content  $interval (optional) - Time which may elapse (in s) between last check for content
                        change in current course. (default: 600 s).                         change in current course. (default: 600 s).  
   
 Returns: an array; first element is:  Returns: an array; first element is:
   
Line 16824  Returns: an array; first element is: Line 18903  Returns: an array; first element is:
   
 'switch' - if content updates mean user's session  'switch' - if content updates mean user's session
            needs to be switched to a server running a newer LON-CAPA version             needs to be switched to a server running a newer LON-CAPA version
    
 'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)  'update' - if course session needs to be refreshed (i.e., Big Hash needs to be reloaded)
            on current server hosting user's session             on current server hosting user's session                
   
 ''       - if no action required.  ''       - if no action required.
   
Line 16834  Returns: an array; first element is: Line 18913  Returns: an array; first element is:
   
 If first item element is 'switch':  If first item element is 'switch':
   
 second item is $switchwarning - Warning message if no suitable server found to host session.  second item is $switchwarning - Warning message if no suitable server found to host session. 
   
 third item is $switchserver - query string to append to /adm/switchserver containing lonHostID  third item is $switchserver - query string to append to /adm/switchserver containing lonHostID
                               and current role.                                and current role. 
   
 otherwise: no other elements returned.  otherwise: no other elements returned.
   
Line 16856  sub needs_coursereinit { Line 18935  sub needs_coursereinit {
     }      }
     if (($now-$env{'request.course.timechecked'})>$interval) {      if (($now-$env{'request.course.timechecked'})>$interval) {
         &Apache::lonnet::appenv({'request.course.timechecked'=>$now});          &Apache::lonnet::appenv({'request.course.timechecked'=>$now});
         my $blocked = &blocking_status('reinit',$cnum,$cdom,undef,1);          my $blocked = &blocking_status('reinit',undef,$cnum,$cdom,undef,1);
         if ($blocked) {          if ($blocked) {
             return ();              return ();
         }          }
         my $lastchange = &Apache::lonnet::get_coursechange($cdom,$cnum);          my $update;
         if ($lastchange > $env{'request.course.tied'}) {          my $lastmainchange = &Apache::lonnet::get_coursechange($cdom,$cnum);
             my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');          my $lastsuppchange = &Apache::lonnet::get_suppchange($cdom,$cnum);
             if ($curr_reqd_hash{'internal.releaserequired'} ne '') {          if ($lastmainchange > $env{'request.course.tied'}) {
                 my $required = $env{'course.'.$cdom.'_'.$cnum.'.internal.releaserequired'};              my ($needswitch,$switchwarning,$switchserver) = &switch_for_update($loncaparev,$cdom,$cnum);
                 if ($curr_reqd_hash{'internal.releaserequired'} ne $required) {              if ($needswitch) {
                     &Apache::lonnet::appenv({'course.'.$cdom.'_'.$cnum.'.internal.releaserequired' =>                  return ('switch',$switchwarning,$switchserver);
                                              $curr_reqd_hash{'internal.releaserequired'}});              }
                     my ($switchserver,$switchwarning) =              $update = 'main';
                         &check_release_required($loncaparev,$cdom.'_'.$cnum,$env{'request.role'},          }
                                                 $curr_reqd_hash{'internal.releaserequired'});          if ($lastsuppchange > $env{'request.course.suppupdated'}) {
                     if ($switchwarning ne '' || $switchserver ne '') {              if ($update) {
                         return ('switch',$switchwarning,$switchserver);                  $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 ();      return ();
Line 16886  sub update_content_constraints { Line 18989  sub update_content_constraints {
     my ($cdom,$cnum,$chome,$cid) = @_;      my ($cdom,$cnum,$chome,$cid) = @_;
     my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');      my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
     my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});      my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
     my %checkresponsetypes;      my (%checkresponsetypes,%checkcrsrestypes);
     foreach my $key (keys(%Apache::lonnet::needsrelease)) {      foreach my $key (keys(%Apache::lonnet::needsrelease)) {
         my ($item,$name,$value) = split(/:/,$key);          my ($item,$name,$value) = split(/:/,$key);
         if ($item eq 'resourcetag') {          if ($item eq 'resourcetag') {
             if ($name eq 'responsetype') {              if ($name eq 'responsetype') {
                 $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}                  $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
             }              }
           } elsif ($item eq 'course') {
               if ($name eq 'courserestype') {
                   $checkcrsrestypes{$value} = $Apache::lonnet::needsrelease{$key};
               }
         }          }
     }      }
     my $navmap = Apache::lonnavmaps::navmap->new();      my $navmap = Apache::lonnavmaps::navmap->new();
     if (defined($navmap)) {      if (defined($navmap)) {
         my %allresponses;          my (%allresponses,%allcrsrestypes);
         foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {          foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0)) {
               if ($res->is_tool()) {
                   if ($allcrsrestypes{'exttool'}) {
                       $allcrsrestypes{'exttool'} ++;
                   } else {
                       $allcrsrestypes{'exttool'} = 1;
                   }
                   next;
               }
             my %responses = $res->responseTypes();              my %responses = $res->responseTypes();
             foreach my $key (keys(%responses)) {              foreach my $key (keys(%responses)) {
                 next unless(exists($checkresponsetypes{$key}));                  next unless(exists($checkresponsetypes{$key}));
Line 16911  sub update_content_constraints { Line 19026  sub update_content_constraints {
                 ($reqdmajor,$reqdminor) = ($major,$minor);                  ($reqdmajor,$reqdminor) = ($major,$minor);
             }              }
         }          }
           foreach my $key (keys(%allcrsrestypes)) {
               my ($major,$minor) = split(/\./,$checkcrsrestypes{$key});
               if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
                   ($reqdmajor,$reqdminor) = ($major,$minor);
               }
           }
         undef($navmap);          undef($navmap);
     }      }
       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);
           }
       }
     unless (($reqdmajor eq '') && ($reqdminor eq '')) {      unless (($reqdmajor eq '') && ($reqdminor eq '')) {
         &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);          &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
     }      }
Line 16959  sub parse_supplemental_title { Line 19086  sub parse_supplemental_title {
         my $name =  &plainname($uname,$udom);          my $name =  &plainname($uname,$udom);
         $name = &HTML::Entities::encode($name,'"<>&\'');          $name = &HTML::Entities::encode($name,'"<>&\'');
         $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');          $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
         $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.          $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.$name;
             $name.': <br />'.$foldertitle;          if ($foldertitle ne '') {
               $title .= ': <br />'.$foldertitle;
           }
     }      }
     if (wantarray) {      if (wantarray) {
         return ($title,$foldertitle,$renametitle);          return ($title,$foldertitle,$renametitle);
Line 16968  sub parse_supplemental_title { Line 19097  sub parse_supplemental_title {
     return $title;      return $title;
 }  }
   
   sub get_supplemental {
       my ($cnum,$cdom,$ignorecache,$possdel)=@_;
       my $hashid=$cnum.':'.$cdom;
       my ($supplemental,$cached,$set_httprefs);
       unless ($ignorecache) {
           ($supplemental,$cached) = &Apache::lonnet::is_cached_new('supplemental',$hashid);
       }
       unless (defined($cached)) {
           my $chome=&Apache::lonnet::homeserver($cnum,$cdom);
           unless ($chome eq 'no_host') {
               my @order = @LONCAPA::map::order;
               my @resources = @LONCAPA::map::resources;
               my @resparms = @LONCAPA::map::resparms;
               my @zombies = @LONCAPA::map::zombies;
               my ($errors,%ids,%hidden);
               $errors =
                   &recurse_supplemental($cnum,$cdom,'supplemental.sequence',
                                         $errors,$possdel,\%ids,\%hidden);
               @LONCAPA::map::order = @order;
               @LONCAPA::map::resources = @resources;
               @LONCAPA::map::resparms = @resparms;
               @LONCAPA::map::zombies = @zombies;
               $set_httprefs = 1;
               if ($env{'request.course.id'} eq $cdom.'_'.$cnum) {
                   &Apache::lonnet::appenv({'request.course.suppupdated' => time});
               }
               $supplemental = {
                                  ids => \%ids,
                                  hidden => \%hidden,
                               };
               &Apache::lonnet::do_cache_new('supplemental',$hashid,$supplemental,600);
           }
       }
       return ($supplemental,$set_httprefs);
   }
   
 sub recurse_supplemental {  sub recurse_supplemental {
     my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;      my ($cnum,$cdom,$suppmap,$errors,$possdel,$suppids,$hiddensupp,$hidden) = @_;
     if ($suppmap) {      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);          my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
         if ($fatal) {          if ($fatal) {
             $errors ++;              $errors ++;
         } else {          } else {
             if ($#LONCAPA::map::resources > 0) {              my @order = @LONCAPA::map::order;
                 foreach my $res (@LONCAPA::map::resources) {              if (@order > 0) {
                     my ($title,$src,$ext,$type,$status)=split(/\:/,$res);                  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')) {                      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)$}) {                          if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
                             ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);                              $errors = &recurse_supplemental($cnum,$cdom,$1,$errors,$possdel,$suppids,
                                                               $hiddensupp,$hiddensupp->{$id});
                         } else {                          } else {
                             $numfiles ++;                              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);
                               }
                           }
                       }
                   }
               }
           }
       }
       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});
               }
         }          }
     }      }
     return ($numfiles,$errors);  }
   
   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 {  sub symb_to_docspath {
Line 17062  sub symb_to_docspath { Line 19310  sub symb_to_docspath {
     return $path;      return $path;
 }  }
   
   sub validate_folderpath {
       my ($supplementalflag,$allowed,$coursenum,$coursedom) = @_;
       if ($env{'form.folderpath'} ne '') {
           my @items = split(/\&/,$env{'form.folderpath'});
           my ($badpath,$changed,$got_supp,$supppath,%supphidden,%suppids);
           for (my $i=0; $i<@items; $i++) {
               my $odd = $i%2;
               if (($odd) && (!$supplementalflag) && ($items[$i] !~ /^[^:]*:(|\d+):(|1):(|1):(|1):(|1)$/)) {
                   $badpath = 1;
               } elsif ($odd && $supplementalflag) {
                   my $idx = $i-1;
                   if ($items[$i] =~ /^([^:]*)::(|1):::$/) {
                       my $esc_name = $1;
                       if ((!$allowed) || ($items[$idx] eq 'supplemental')) {
                           $supppath .= '&'.$esc_name;
                           $changed = 1;
                       } else {
                           $supppath .= '&'.$items[$i];
                       }
                   } elsif (($allowed) && ($items[$idx] ne 'supplemental')) {
                       $changed = 1;
                       my $is_hidden;
                       unless ($got_supp) {
                           my ($supplemental) = &get_supplemental($coursenum,$coursedom);
                           if (ref($supplemental) eq 'HASH') {
                               if (ref($supplemental->{'hidden'}) eq 'HASH') {
                                   %supphidden = %{$supplemental->{'hidden'}};
                               }
                               if (ref($supplemental->{'ids'}) eq 'HASH') {
                                   %suppids = %{$supplemental->{'ids'}};
                               }
                           }
                           $got_supp = 1;
                       }
                       if (ref($suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}) eq 'ARRAY') {
                           my $mapid = $suppids{"/uploaded/$coursedom/$coursenum/$items[$idx].sequence"}->[0];
                           if ($supphidden{$mapid}) {
                               $is_hidden = 1;
                           }
                       }
                       $supppath .= '&'.$items[$i].'::'.$is_hidden.':::';
                   } else {
                       $supppath .= '&'.$items[$i];
                   }
               } elsif ((!$odd) && ($items[$i] !~ /^(default|supplemental)(|_\d+)$/)) {
                   $badpath = 1;
               } elsif ($supplementalflag) {
                   $supppath .= '&'.$items[$i];
               }
               last if ($badpath);
           }
           if ($badpath) {
               delete($env{'form.folderpath'});
           } elsif ($changed && $supplementalflag) {
               $supppath =~ s/^\&//;
               $env{'form.folderpath'} = $supppath;
           }
       }
       return;
   }
   
 sub captcha_display {  sub captcha_display {
     my ($context,$lonhost,$defdom) = @_;      my ($context,$lonhost,$defdom) = @_;
     my ($output,$error);      my ($output,$error);
     my ($captcha,$pubkey,$privkey,$version) =      my ($captcha,$pubkey,$privkey,$version) = 
         &get_captcha_config($context,$lonhost,$defdom);          &get_captcha_config($context,$lonhost,$defdom);
     if ($captcha eq 'original') {      if ($captcha eq 'original') {
         $output = &create_captcha();          $output = &create_captcha();
Line 17136  sub get_captcha_config { Line 19445  sub get_captcha_config {
                 $captcha = 'recaptcha';                  $captcha = 'recaptcha';
                 $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};                  $version = $domconfhash{$serverhomedom.'.login.recaptchaversion'};
                 if ($version ne '2') {                  if ($version ne '2') {
                     $version = 1;                      $version = 1; 
                 }                  }
             } else {              } else {
                 $captcha = 'original';                  $captcha = 'original';
Line 17165  sub get_captcha_config { Line 19474  sub get_captcha_config {
                 $captcha = 'original';                  $captcha = 'original';
             }              }
         }          }
     }      } 
     return ($captcha,$pubkey,$privkey,$version);      return ($captcha,$pubkey,$privkey,$version);
 }  }
   
Line 17182  sub create_captcha { Line 19491  sub create_captcha {
   
         if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {          if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
             $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".              $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
                         '<span class="LC_nobreak">'.
                       &mt('Type in the letters/numbers shown below').'&nbsp;'.                        &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" />'.
                       '<br />'.                        '</span><br />'.
                       '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';                        '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
             last;              last;
         }          }
     }      }
       if ($output eq '') {
           &Apache::lonnet::logthis("Failed to create Captcha code after $tries attempts.");
       }
     return $output;      return $output;
 }  }
   
Line 17227  sub check_captcha { Line 19540  sub check_captcha {
 sub create_recaptcha {  sub create_recaptcha {
     my ($pubkey,$version) = @_;      my ($pubkey,$version) = @_;
     if ($version >= 2) {      if ($version >= 2) {
         return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>';          return '<div class="g-recaptcha" data-sitekey="'.$pubkey.'"></div>'.
                  '<div style="padding:0;clear:both;margin:0;border:0"></div>';
     } else {      } else {
         my $use_ssl;          my $use_ssl;
         if ($ENV{'SERVER_PORT'} == 443) {          if ($ENV{'SERVER_PORT'} == 443) {
Line 17239  sub create_recaptcha { Line 19553  sub create_recaptcha {
                &mt('If the text is hard to read, [_1] will replace them.',                 &mt('If the text is hard to read, [_1] will replace them.',
                    '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').                     '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
                '<br /><br />';                 '<br /><br />';
      }      }
 }  }
   
 sub check_recaptcha {  sub check_recaptcha {
Line 17247  sub check_recaptcha { Line 19561  sub check_recaptcha {
     my $captcha_chk;      my $captcha_chk;
     my $ip = &Apache::lonnet::get_requestor_ip();      my $ip = &Apache::lonnet::get_requestor_ip();
     if ($version >= 2) {      if ($version >= 2) {
         my $ua = LWP::UserAgent->new;  
         $ua->timeout(10);  
         my %info = (          my %info = (
                      secret   => $privkey,                       secret   => $privkey, 
                      response => $env{'form.g-recaptcha-response'},                       response => $env{'form.g-recaptcha-response'},
                      remoteip => $ip,                       remoteip => $ip,
                    );                     );
         my $response = $ua->post('https://www.google.com/recaptcha/api/siteverify',\%info);          my $request=new HTTP::Request('POST','https://www.google.com/recaptcha/api/siteverify');
           $request->content(join('&',map {
                            my $name = escape($_);
                            "$name=" . ( ref($info{$_}) eq 'ARRAY'
                            ? join("&$name=", map {escape($_) } @{$info{$_}})
                            : &escape($info{$_}) );
           } keys(%info)));
           my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',10,1);
         if ($response->is_success)  {          if ($response->is_success)  {
             my $data = JSON::DWIW->from_json($response->decoded_content);              my $data = JSON::DWIW->from_json($response->decoded_content);
             if (ref($data) eq 'HASH') {              if (ref($data) eq 'HASH') {
Line 17317  sub cleanup_html { Line 19636  sub cleanup_html {
   
 # Checks for critical messages and returns a redirect url if one exists.  # Checks for critical messages and returns a redirect url if one exists.
 # $interval indicates how often to check for messages.  # $interval indicates how often to check for messages.
 # $context is the calling context -- roles, grades, contents, menu or flip.  # $context is the calling context -- roles, grades, contents, menu or flip. 
 sub critical_redirect {  sub critical_redirect {
     my ($interval,$context) = @_;      my ($interval,$context) = @_;
       unless (($env{'user.domain'} ne '') && ($env{'user.name'} ne '')) {
           return ();
       }
     if ((time-$env{'user.criticalcheck.time'})>$interval) {      if ((time-$env{'user.criticalcheck.time'})>$interval) {
         if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {          if (($env{'request.course.id'}) && (($context eq 'flip') || ($context eq 'contents'))) {
             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};              my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};              my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
             my $blocked = &blocking_status('alert',$cnum,$cdom,undef,1);              my $blocked = &blocking_status('alert',undef,$cnum,$cdom,undef,1);
             if ($blocked) {              if ($blocked) {
                 my $checkrole = "cm./$cdom/$cnum";                  my $checkrole = "cm./$cdom/$cnum";
                 if ($env{'request.course.sec'} ne '') {                  if ($env{'request.course.sec'} ne '') {
Line 17336  sub critical_redirect { Line 19658  sub critical_redirect {
                 }                  }
             }              }
         }          }
         my @what=&Apache::lonnet::dump('critical', $env{'user.domain'},          my @what=&Apache::lonnet::dump('critical', $env{'user.domain'}, 
                                         $env{'user.name'});                                          $env{'user.name'});
         &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});          &Apache::lonnet::appenv({'user.criticalcheck.time'=>time});
         my $redirecturl;          my $redirecturl;
         if ($what[0]) {          if ($what[0]) {
             if (($what[0] ne 'con_lost') && ($what[0]!~/^error\:/)) {      if (($what[0] ne 'con_lost') && ($what[0] ne 'no_such_host') && ($what[0]!~/^error\:/)) {
                 $redirecturl='/adm/email?critical=display';          $redirecturl='/adm/email?critical=display';
                 my $url=&Apache::lonnet::absolute_url().$redirecturl;          my $url=&Apache::lonnet::absolute_url().$redirecturl;
                 return (1, $url);                  return (1, $url);
             }              }
         }          }
     }      } 
     return ();      return ();
 }  }
   
Line 17472  sub make_short_symbs { Line 19794  sub make_short_symbs {
                         push(@errors,&mt('error: could not store unique six character URLs'));                          push(@errors,&mt('error: could not store unique six character URLs'));
                     }                      }
                 }                  }
                   my $dellockres = &Apache::lonnet::del_dom('tiny',["lock\0$now"],$cdom);
                   unless ($dellockres eq 'ok') {
                       push(@errors,&mt('error: could not release lockfile'));
                   }
               } else {
                   push(@errors,&mt('error: could not obtain lockfile'));
               }
               if (keys(%courseonly)) {
                   my $result = &Apache::lonnet::newput('tiny',\%courseonly,$cdom,$cnum);
                   if ($result ne 'ok') {
                       push(@errors,&mt('error: could not update course look-up of short URLs'));
                   }
             }              }
         }          }
     }      }
Line 17534  sub shorten_symbs { Line 19868  sub shorten_symbs {
         } else {          } else {
             foreach my $key (keys(%collisions)) {              foreach my $key (keys(%collisions)) {
                 $failed->{$key} = 1;                  $failed->{$key} = 1;
                 $failed->{$key} = 1;  
             }              }
         }          }
     }      }
Line 17564  sub is_nonframeable { Line 19897  sub is_nonframeable {
     }      }
     my $uselink;      my $uselink;
     my $request = new HTTP::Request('HEAD',$url);      my $request = new HTTP::Request('HEAD',$url);
     my $ua = LWP::UserAgent->new;      my $response = &LONCAPA::LWPReq::makerequest('',$request,'','',5);
     $ua->timeout(5);  
     my $response=$ua->request($request);  
     if ($response->is_success()) {      if ($response->is_success()) {
         my $secpolicy = lc($response->header('content-security-policy'));          my $secpolicy = lc($response->header('content-security-policy'));
         my $xframeop = lc($response->header('x-frame-options'));          my $xframeop = lc($response->header('x-frame-options'));
Line 17694  sub is_nonframeable { Line 20025  sub is_nonframeable {
     return $uselink;      return $uselink;
 }  }
   
   sub page_menu {
       my ($menucolls,$menunum) = @_;
       my %menu;
       foreach my $item (split(/;/,$menucolls)) {
           my ($num,$value) = split(/\%/,$item);
           if ($num eq $menunum) {
               my @entries = split(/\&/,$value);
               foreach my $entry (@entries) {
                   my ($name,$fields) = split(/=/,$entry);
                   if (($name eq 'top') || ($name eq 'inline') || ($name eq 'foot') || ($name eq 'main')) {
                       $menu{$name} = $fields;
                   } else {
                       my @shown;
                       if ($fields =~ /,/) {
                           @shown = split(/,/,$fields);
                       } else {
                           @shown = ($fields);
                       }
                       if (@shown) {
                           foreach my $field (@shown) {
                               next if ($field eq '');
                               $menu{$field} = 1;
                           }
                       }
                   }
               }
           }
       }
       return %menu;
   }
   
 1;  1;
 __END__;  __END__;
   

Removed from v.1.1075.2.141.2.12  
changed lines
  Added in v.1.1430


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>