Diff for /loncom/interface/domainprefs.pm between versions 1.160.6.102.2.1 and 1.160.6.118.2.19

version 1.160.6.102.2.1, 2020/01/15 16:55:18 version 1.160.6.118.2.19, 2024/02/25 06:15:54
Line 27 Line 27
 #  #
 #  #
 ###############################################################  ###############################################################
 ##############################################################  ###############################################################
   
 =pod  =pod
   
Line 104  $datatable  - HTML containing form eleme Line 104  $datatable  - HTML containing form eleme
   
 In the case of course requests, radio buttons are displayed for each institutional  In the case of course requests, radio buttons are displayed for each institutional
 affiliate type (and also default, and _LC_adv) for each of the course types   affiliate type (and also default, and _LC_adv) for each of the course types 
 (official, unofficial, community, and textbook).  In each case the radio buttons   (official, unofficial, community, textbook, and lti).
 allow the selection of one of four values:  In each case the radio buttons allow the selection of one of four values:
   
 0, approval, validate, autolimit=N (where N is blank, or a positive integer).  0, approval, validate, autolimit=N (where N is blank, or a positive integer).
 which have the following effects:  which have the following effects:
Line 167  use Apache::lonmsg(); Line 167  use Apache::lonmsg();
 use Apache::lonconfigsettings;  use Apache::lonconfigsettings;
 use Apache::lonuserutils();  use Apache::lonuserutils();
 use Apache::loncoursequeueadmin();  use Apache::loncoursequeueadmin();
   use Apache::courseprefs();
 use LONCAPA qw(:DEFAULT :match);  use LONCAPA qw(:DEFAULT :match);
 use LONCAPA::Enrollment;  use LONCAPA::Enrollment;
 use LONCAPA::lonauthcgi();  use LONCAPA::lonauthcgi();
Line 174  use File::Copy; Line 175  use File::Copy;
 use Locale::Language;  use Locale::Language;
 use DateTime::TimeZone;  use DateTime::TimeZone;
 use DateTime::Locale;  use DateTime::Locale;
   use Time::HiRes qw( sleep );
   use Net::CIDR;
   use Crypt::CBC;
   
 my $registered_cleanup;  my $registered_cleanup;
 my $modified_urls;  my $modified_urls;
Line 217  sub handler { Line 221  sub handler {
                 'serverstatuses','requestcourses','helpsettings',                  'serverstatuses','requestcourses','helpsettings',
                 'coursedefaults','usersessions','loadbalancing',                  'coursedefaults','usersessions','loadbalancing',
                 'requestauthor','selfenrollment','inststatus',                  'requestauthor','selfenrollment','inststatus',
                 'passwords','ltitools'],$dom);                  'passwords','ltitools','toolsec','lti','ltisec',
                   'wafproxy','ipaccess'],$dom);
       my %encconfig =
           &Apache::lonnet::get_dom('encconfig',['ltitools','lti','linkprot'],$dom,undef,1);
       my ($checked_is_home,$is_home);
     if (ref($domconfig{'ltitools'}) eq 'HASH') {      if (ref($domconfig{'ltitools'}) eq 'HASH') {
         my %encconfig =  
             &Apache::lonnet::get_dom('encconfig',['ltitools'],$dom);  
         if (ref($encconfig{'ltitools'}) eq 'HASH') {          if (ref($encconfig{'ltitools'}) eq 'HASH') {
               my $home = &Apache::lonnet::domain($dom,'primary');
               unless (($home eq 'no_host') || ($home eq '')) {
                   my @ids=&Apache::lonnet::current_machine_ids();
                   if (grep(/^\Q$home\E$/,@ids)) {
                       $is_home = 1;
                   }
               }
               $checked_is_home = 1;
             foreach my $id (keys(%{$domconfig{'ltitools'}})) {              foreach my $id (keys(%{$domconfig{'ltitools'}})) {
                 if (ref($domconfig{'ltitools'}{$id}) eq 'HASH') {                  if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') &&
                     foreach my $item ('key','secret') {                      (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) {
                         $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item};                      $domconfig{'ltitools'}{$id}{'key'} = $encconfig{'ltitools'}{$id}{'key'};
                       if (($is_home) && ($phase eq 'process')) {
                           $domconfig{'ltitools'}{$id}{'secret'} = $encconfig{'ltitools'}{$id}{'secret'};
                       }
                   }
               }
           }
       }
       if (ref($domconfig{'lti'}) eq 'HASH') {
           if (ref($encconfig{'lti'}) eq 'HASH') {
               unless ($checked_is_home) {
                   my $home = &Apache::lonnet::domain($dom,'primary');
                   unless (($home eq 'no_host') || ($home eq '')) {
                       my @ids=&Apache::lonnet::current_machine_ids();
                       if (grep(/^\Q$home\E$/,@ids)) {
                           $is_home = 1;
                       }
                   }
                   $checked_is_home = 1;
               }
               foreach my $id (keys(%{$domconfig{'lti'}})) {
                   if ((ref($domconfig{'lti'}{$id}) eq 'HASH') &&
                       (ref($encconfig{'lti'}{$id}) eq 'HASH')) {
                       $domconfig{'lti'}{$id}{'key'} = $encconfig{'lti'}{$id}{'key'};
                       if (($is_home) && ($phase eq 'process')) {
                           $domconfig{'lti'}{$id}{'secret'} = $encconfig{'lti'}{$id}{'secret'};
                       }
                   }
               }
           }
       }
       if (ref($domconfig{'ltisec'}) eq 'HASH') {
           if (ref($domconfig{'ltisec'}{'linkprot'}) eq 'HASH') {
               if (ref($encconfig{'linkprot'}) eq 'HASH') {
                   foreach my $id (keys(%{$domconfig{'ltisec'}{'linkprot'}})) {
                       unless ($id =~ /^\d+$/) {
                           delete($domconfig{'ltisec'}{'linkprot'}{$id});
                       }
                       if ((ref($domconfig{'ltisec'}{'linkprot'}{$id}) eq 'HASH') &&
                           (ref($encconfig{'linkprot'}{$id}) eq 'HASH')) {
                           foreach my $item ('key','secret') {
                               $domconfig{'ltisec'}{'linkprot'}{$id}{$item} = $encconfig{'linkprot'}{$id}{$item};
                           }
                     }                      }
                 }                  }
             }              }
         }          }
     }      }
     my @prefs_order = ('rolecolors','login','defaults','passwords','quotas','autoenroll',      my @prefs_order = ('rolecolors','login','ipaccess','defaults','wafproxy','passwords',
                        'autoupdate','autocreate','directorysrch','contacts',                         'quotas','autoenroll','autoupdate','autocreate','directorysrch',
                        'usercreation','selfcreation','usermodification','scantron',                         'contacts','usercreation','selfcreation','usermodification',
                        'requestcourses','requestauthor','coursecategories',                         'scantron','requestcourses','requestauthor','coursecategories',
                        'serverstatuses','helpsettings','coursedefaults',                         'serverstatuses','helpsettings','coursedefaults',
                        'ltitools','selfenrollment','usersessions');                         'ltitools','selfenrollment','usersessions','lti');
     my %existing;      my %existing;
     if (ref($domconfig{'loadbalancing'}) eq 'HASH') {      if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
         %existing = %{$domconfig{'loadbalancing'}};          %existing = %{$domconfig{'loadbalancing'}};
Line 267  sub handler { Line 323  sub handler {
                                  {col1 => 'Log-in Help',                                   {col1 => 'Log-in Help',
                                   col2 => 'Value'},                                    col2 => 'Value'},
                                  {col1 => 'Custom HTML in document head',                                   {col1 => 'Custom HTML in document head',
                                   col2 => 'Value'}],                                    col2 => 'Value'},
                                    {col1 => 'SSO',
                                     col2 => 'Dual login: SSO and non-SSO options'},
                                   ],
                       print => \&print_login,                        print => \&print_login,
                       modify => \&modify_login,                        modify => \&modify_login,
                     },                      },
Line 277  sub handler { Line 336  sub handler {
                       header => [{col1 => 'Setting',                        header => [{col1 => 'Setting',
                                   col2 => 'Value'},                                    col2 => 'Value'},
                                  {col1 => 'Institutional user types',                                   {col1 => 'Institutional user types',
                                   col2 => 'Name displayed'}],                                    col2 => 'Name displayed'},
                                    {col1 => 'Mapping for missing usernames via standard log-in',
                                     col2 => 'Rules in use'}],
                       print => \&print_defaults,                        print => \&print_defaults,
                       modify => \&modify_defaults,                        modify => \&modify_defaults,
                     },                      },
           'wafproxy' =>
                       { text => 'Web Application Firewall/Reverse Proxy',
                         help => 'Domain_Configuration_WAF_Proxy',
                         header => [{col1 => 'Domain(s)',
                                     col2 => 'Servers and WAF/Reverse Proxy alias(es)',
                                    },
                                    {col1 => 'Domain(s)',
                                     col2 => 'WAF Configuration',}],
                         print => \&print_wafproxy,
                         modify => \&modify_wafproxy,
                       },
         'passwords' =>          'passwords' =>
                     { text => 'Passwords (Internal authentication)',                      { text => 'Passwords (Internal authentication)',
                       help => 'Domain_Configuration_Passwords',                        help => 'Domain_Configuration_Passwords',
Line 296  sub handler { Line 368  sub handler {
                       modify => \&modify_passwords,                        modify => \&modify_passwords,
                     },                      },
         'quotas' =>           'quotas' => 
                     { text => 'Blogs, personal web pages, webDAV/quotas, portfolios',                      { text => 'Blogs, personal pages/timezones, webDAV/quotas, portfolio',
                       help => 'Domain_Configuration_Quotas',                        help => 'Domain_Configuration_Quotas',
                       header => [{col1 => 'User affiliation',                        header => [{col1 => 'User affiliation',
                                   col2 => 'Available tools',                                    col2 => 'Available tools',
Line 349  sub handler { Line 421  sub handler {
                                 col2 => 'Value',},                                  col2 => 'Value',},
                                {col1 => 'Recipient(s) for notifications',                                 {col1 => 'Recipient(s) for notifications',
                                 col2 => 'Value',},                                  col2 => 'Value',},
                                  {col1 => 'Nightly status check e-mail',
                                   col2 => 'Settings',},
                                {col1 => 'Ask helpdesk form settings',                                 {col1 => 'Ask helpdesk form settings',
                                 col2 => 'Value',},],                                  col2 => 'Value',},],
                     print => \&print_contacts,                      print => \&print_contacts,
Line 505  sub handler { Line 579  sub handler {
         'ltitools' =>          'ltitools' =>
                  {text => 'External Tools (LTI)',                   {text => 'External Tools (LTI)',
                   help => 'Domain_Configuration_LTI_Tools',                    help => 'Domain_Configuration_LTI_Tools',
                   header => [{col1 => 'Setting',                    header => [{col1 => 'Encryption of shared secrets',
                               col2 => 'Value',}],                                col2 => 'Settings'},
                                {col1 => 'Rules for shared secrets',
                                 col2 => 'Settings'},
                                {col1 => 'Providers',
                                 col2 => 'Settings',}],
                   print => \&print_ltitools,                    print => \&print_ltitools,
                   modify => \&modify_ltitools,                    modify => \&modify_ltitools,
                  },                   },
           'lti' =>
                    {text => 'LTI Link Protection and LTI Consumers',
                     help => 'Domain_Configuration_LTI_Provider',
                     header => [{col1 => 'Encryption of shared secrets',
                                 col2 => 'Settings'},
                                {col1 => 'Rules for shared secrets',
                                 col2 => 'Settings'},
                                {col1 => 'Link Protectors in Courses',
                                 col2 => 'Values'},
                                {col1 => 'Link Protectors',
                                 col2 => 'Settings'},
                                {col1 => 'Consumers',
                                 col2 => 'Settings'},],
                     print => \&print_lti,
                     modify => \&modify_lti,
                    },
           'ipaccess' =>
                          {text => 'IP-based access control',
                           help => 'Domain_Configuration_IP_Access',
                           header => [{col1 => 'Setting',
                                       col2 => 'Value'},],
                           print  => \&print_ipaccess,
                           modify => \&modify_ipaccess,
                          },
     );      );
     if (keys(%servers) > 1) {      if (keys(%servers) > 1) {
         $prefs{'login'}  = { text   => 'Log-in page options',          $prefs{'login'}  = { text   => 'Log-in page options',
Line 517  sub handler { Line 619  sub handler {
                             header => [{col1 => 'Log-in Service',                              header => [{col1 => 'Log-in Service',
                                         col2 => 'Server Setting',},                                          col2 => 'Server Setting',},
                                        {col1 => 'Log-in Page Items',                                         {col1 => 'Log-in Page Items',
                                         col2 => ''},                                          col2 => 'Settings'},
                                        {col1 => 'Log-in Help',                                         {col1 => 'Log-in Help',
                                         col2 => 'Value'},                                          col2 => 'Value'},
                                        {col1 => 'Custom HTML in document head',                                         {col1 => 'Custom HTML in document head',
                                         col2 => 'Value'}],                                          col2 => 'Value'},
                                          {col1 => 'SSO',
                                           col2 => 'Dual login: SSO and non-SSO options'},
                                         ],
                             print => \&print_login,                              print => \&print_login,
                             modify => \&modify_login,                              modify => \&modify_login,
                            };                             };
Line 562  $javascript_validations Line 667  $javascript_validations
 </script>  </script>
 $coursebrowserjs  $coursebrowserjs
 END  END
           } elsif (grep(/^ipaccess$/,@actions)) {
               $js .= &Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'});
         }          }
         if (grep(/^selfcreation$/,@actions)) {          if (grep(/^selfcreation$/,@actions)) {
             $js .= &selfcreate_javascript();              $js .= &selfcreate_javascript();
Line 692  sub process_changes { Line 799  sub process_changes {
         $output = &modify_usersessions($dom,$lastactref,%domconfig);          $output = &modify_usersessions($dom,$lastactref,%domconfig);
     } elsif ($action eq 'loadbalancing') {      } elsif ($action eq 'loadbalancing') {
         $output = &modify_loadbalancing($dom,%domconfig);          $output = &modify_loadbalancing($dom,%domconfig);
     } elsif ($action eq 'passwords') {  
         $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);  
     } elsif ($action eq 'ltitools') {      } elsif ($action eq 'ltitools') {
         $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);          $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'lti') {
           $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'passwords') {
           $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);
       } elsif ($action eq 'wafproxy') {
           $output = &modify_wafproxy($dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'ipaccess') {
           $output = &modify_ipaccess($dom,$lastactref,%domconfig);
     }      }
     return $output;      return $output;
 }  }
Line 709  sub print_config_box { Line 822  sub print_config_box {
     } elsif ($action eq 'defaults') {      } elsif ($action eq 'defaults') {
         $output = &defaults_javascript($settings);           $output = &defaults_javascript($settings); 
     } elsif ($action eq 'passwords') {      } elsif ($action eq 'passwords') {
         $output = &passwords_javascript();          $output = &passwords_javascript($action);
     } elsif ($action eq 'helpsettings') {      } elsif ($action eq 'helpsettings') {
         my (%privs,%levelscurrent);          my (%privs,%levelscurrent);
         my %full=();          my %full=();
Line 726  sub print_config_box { Line 839  sub print_config_box {
         $output =          $output =
             &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full,              &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full,
                                                       \@templateroles);                                                        \@templateroles);
       } elsif ($action eq 'ltitools') {
           $output .= &Apache::lonconfigsettings::ltitools_javascript($settings);
       } elsif ($action eq 'lti') {
           $output .= &passwords_javascript('ltisecrets')."\n".
                      &lti_javascript($dom,$settings);
       } elsif ($action eq 'wafproxy') {
           $output .= &wafproxy_javascript($dom);
       } elsif ($action eq 'autoupdate') {
           $output .= &autoupdate_javascript();
       } elsif ($action eq 'autoenroll') {
           $output .= &autoenroll_javascript();
       } elsif ($action eq 'login') {
           $output .= &saml_javascript();
       } elsif ($action eq 'ipaccess') {
           $output .= &ipaccess_javascript($settings);
     }      }
     $output .=      $output .=
          '<table class="LC_nested_outer">           '<table class="LC_nested_outer">
           <tr>            <tr>
            <th align="left" valign="middle"><span class="LC_nobreak">'.             <th class="LC_left_item LC_middle"><span class="LC_nobreak">'.
            &mt($item->{text}).'&nbsp;'.             &mt($item->{text}).'&nbsp;'.
            &Apache::loncommon::help_open_topic($item->{'help'}).'</span></th>'."\n".             &Apache::loncommon::help_open_topic($item->{'help'}).'</span></th>'."\n".
           '</tr>';            '</tr>';
Line 742  sub print_config_box { Line 870  sub print_config_box {
     if ($numheaders > 1) {      if ($numheaders > 1) {
         my $colspan = '';          my $colspan = '';
         my $rightcolspan = '';          my $rightcolspan = '';
           my $leftnobr = '';
         if (($action eq 'rolecolors') || ($action eq 'defaults') ||          if (($action eq 'rolecolors') || ($action eq 'defaults') ||
             ($action eq 'directorysrch') ||              ($action eq 'directorysrch') ||
             (($action eq 'login') && ($numheaders < 4))) {              (($action eq 'login') && ($numheaders < 5))) {
             $colspan = ' colspan="2"';              $colspan = ' colspan="2"';
         }          }
         if ($action eq 'usersessions') {          if ($action eq 'usersessions') {
             $rightcolspan = ' colspan="3"';               $rightcolspan = ' colspan="3"'; 
         }          }
           if ($action eq 'passwords') {
               $leftnobr = ' LC_nobreak';
           }
         $output .= '          $output .= '
           <tr>            <tr>
            <td>             <td>
             <table class="LC_nested">              <table class="LC_nested">
              <tr class="LC_info_row">               <tr class="LC_info_row">
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[0]->{'col1'}).'</td>                <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[0]->{'col1'}).'</td>
               <td class="LC_right_item"'.$rightcolspan.'>'.&mt($item->{'header'}->[0]->{'col2'}).'</td>                <td class="LC_right_item"'.$rightcolspan.'>'.&mt($item->{'header'}->[0]->{'col2'}).'</td>
              </tr>';               </tr>';
         $rowtotal ++;          $rowtotal ++;
         if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||          if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||
             ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||              ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||
             ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'directorysrch') ||              ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'directorysrch') ||
             ($action eq 'helpsettings') || ($action eq 'contacts')) {              ($action eq 'helpsettings') || ($action eq 'contacts') || ($action eq 'wafproxy') ||
               ($action eq 'lti') || ($action eq 'ltitools')) {
             $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);              $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'passwords') {          } elsif ($action eq 'passwords') {
             $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal);              $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal);
Line 771  sub print_config_box { Line 904  sub print_config_box {
         } elsif ($action eq 'scantron') {          } elsif ($action eq 'scantron') {
             $output .= $item->{'print'}->($r,'top',$dom,$confname,$settings,\$rowtotal);              $output .= $item->{'print'}->($r,'top',$dom,$confname,$settings,\$rowtotal);
         } elsif ($action eq 'login') {          } elsif ($action eq 'login') {
             if ($numheaders == 4) {              if ($numheaders == 5) {
                 $colspan = ' colspan="2"';                  $colspan = ' colspan="2"';
                 $output .= &print_login('service',$dom,$confname,$phase,$settings,\$rowtotal);                  $output .= &print_login('service',$dom,$confname,$phase,$settings,\$rowtotal);
             } else {              } else {
Line 797  sub print_config_box { Line 930  sub print_config_box {
         if (($action eq 'autoupdate') || ($action eq 'usercreation') ||          if (($action eq 'autoupdate') || ($action eq 'usercreation') ||
             ($action eq 'selfcreation') || ($action eq 'selfenrollment') ||              ($action eq 'selfcreation') || ($action eq 'selfenrollment') ||
             ($action eq 'usersessions') || ($action eq 'coursecategories') ||              ($action eq 'usersessions') || ($action eq 'coursecategories') ||
             ($action eq 'contacts') || ($action eq 'passwords')) {              ($action eq 'contacts') || ($action eq 'passwords') || 
               ($action eq 'defaults') || ($action eq 'lti') ||
               ($action eq 'ltitools')) {
             if ($action eq 'coursecategories') {              if ($action eq 'coursecategories') {
                 $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal);                  $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal);
                 $colspan = ' colspan="2"';                  $colspan = ' colspan="2"';
             } elsif ($action eq 'passwords') {              } elsif ($action eq 'passwords') {
                 $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal);                  $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal);
               } elsif ($action eq 'lti') {
                   $output .= $item->{'print'}->('upper',$dom,$settings,\$rowtotal).'
                              </table>
                             </td>
                            </tr>
                            <tr>
                             <td>
                             <table class="LC_nested">
                              <tr class="LC_info_row">
                               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td>
                               <td class="LC_right_item">'.&mt($item->{'header'}->[2]->{'col2'}).'</td>
                              </tr>'."\n".
                              $item->{'print'}->('middle',$dom,$settings,\$rowtotal);
             } else {              } else {
                 $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal);                  $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal);
             }              }
               my $hdridx = 2;
               if ($action eq 'lti') {
                   $hdridx = 3;
               }
             $output .= '              $output .= '
            </table>             </table>
           </td>            </td>
Line 814  sub print_config_box { Line 966  sub print_config_box {
            <td>             <td>
             <table class="LC_nested">              <table class="LC_nested">
              <tr class="LC_info_row">               <tr class="LC_info_row">
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td>                <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td>
               <td class="LC_right_item">'.&mt($item->{'header'}->[2]->{'col2'}).'</td>                <td class="LC_right_item">'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td>
              </tr>'."\n";               </tr>'."\n";
             if ($action eq 'coursecategories') {              if ($action eq 'coursecategories') {
                 $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal);                  $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal);
             } elsif ($action eq 'passwords') {              } elsif (($action eq 'contacts') || ($action eq 'passwords')) {
                 $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal).'                  if ($action eq 'passwords') {
                       $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal);
                   } else {
                       $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal);
                   }
                   $hdridx ++;
                   $output .= '
              </tr>               </tr>
             </table>              </table>
            </td>             </td>
Line 829  sub print_config_box { Line 987  sub print_config_box {
            <td>             <td>
             <table class="LC_nested">              <table class="LC_nested">
              <tr class="LC_info_row">               <tr class="LC_info_row">
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>                <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td>
               <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td></tr>'."\n".                <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td></tr>'."\n";
               $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal).'                  if ($action eq 'passwords') {
                       $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal);
                   } else {
                       $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
                   }
                   $output .= '
             </table>              </table>
           </td>            </td>
          </tr>           </tr>
Line 841  sub print_config_box { Line 1004  sub print_config_box {
             }              }
             $rowtotal ++;              $rowtotal ++;
         } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') ||          } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') ||
                  ($action eq 'defaults') || ($action eq 'directorysrch') ||                   ($action eq 'directorysrch') || ($action eq 'helpsettings') ||
                  ($action eq 'helpsettings')) {                   ($action eq 'wafproxy')) {
             $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);              $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'scantron') {          } elsif ($action eq 'scantron') {
             $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal);              $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal);
         } elsif ($action eq 'login') {          } elsif ($action eq 'login') {
             if ($numheaders == 4) {              if ($numheaders == 5) {
                 $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'                  $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'
            </table>             </table>
           </td>            </td>
Line 871  sub print_config_box { Line 1034  sub print_config_box {
            <td>             <td>
             <table class="LC_nested">              <table class="LC_nested">
              <tr class="LC_info_row">';               <tr class="LC_info_row">';
             if ($numheaders == 4) {              if ($numheaders == 5) {
                 $output .= '                  $output .= '
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>                <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>
               <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td>                <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td>
Line 883  sub print_config_box { Line 1046  sub print_config_box {
              </tr>';               </tr>';
             }              }
             $rowtotal ++;              $rowtotal ++;
             $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal);              $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal).'
              </table>
             </td>
            </tr>
            <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">';
               if ($numheaders == 5) {
                   $output .= '
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[4]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[4]->{'col2'}).'</td>
                </tr>';
               } else {
                   $output .= '
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td>
                </tr>';
               }
               $rowtotal ++;
               $output .= &print_login('saml',$dom,$confname,$phase,$settings,\$rowtotal);
         } elsif ($action eq 'requestcourses') {          } elsif ($action eq 'requestcourses') {
             $output .= &print_requestmail($dom,$action,$settings,\$rowtotal);              $output .= &print_requestmail($dom,$action,$settings,\$rowtotal);
             $rowtotal ++;              $rowtotal ++;
Line 1003  sub print_config_box { Line 1186  sub print_config_box {
             $output .= &print_quotas($dom,$settings,\$rowtotal,$action);              $output .= &print_quotas($dom,$settings,\$rowtotal,$action);
         } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') ||           } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || 
                  ($action eq 'serverstatuses') || ($action eq 'loadbalancing') ||                   ($action eq 'serverstatuses') || ($action eq 'loadbalancing') ||
                  ($action eq 'ltitools')) {                   ($action eq 'ipaccess')) {
             $output .= $item->{'print'}->($dom,$settings,\$rowtotal);              $output .= $item->{'print'}->($dom,$settings,\$rowtotal);
         }          }
     }      }
Line 1017  sub print_config_box { Line 1200  sub print_config_box {
   
 sub print_login {  sub print_login {
     my ($caller,$dom,$confname,$phase,$settings,$rowtotal) = @_;      my ($caller,$dom,$confname,$phase,$settings,$rowtotal) = @_;
     my ($css_class,$datatable);      my ($css_class,$datatable,$switchserver,%lt);
     my %choices = &login_choices();      my %choices = &login_choices();
       if (($caller eq 'help') || ($caller eq 'headtag') || ($caller eq 'saml')) {
           %lt = &login_file_options();
           $switchserver = &check_switchserver($dom,$confname);
       }
     if ($caller eq 'service') {      if ($caller eq 'service') {
         my %servers = &Apache::lonnet::internet_dom_servers($dom);          my %servers = &Apache::lonnet::internet_dom_servers($dom);
         my $choice = $choices{'disallowlogin'};          my $choice = $choices{'disallowlogin'};
Line 1106  sub print_login { Line 1292  sub print_login {
             }              }
         }          }
         my @images = ('img','logo','domlogo','login');          my @images = ('img','logo','domlogo','login');
           my @alttext = ('img','logo','domlogo');
         my @logintext = ('textcol','bgcol');          my @logintext = ('textcol','bgcol');
         my @bgs = ('pgbg','mainbg','sidebg');          my @bgs = ('pgbg','mainbg','sidebg');
         my @links = ('link','alink','vlink');          my @links = ('link','alink','vlink');
Line 1147  sub print_login { Line 1334  sub print_login {
                     $designs{'showlogo'}{$item} = $settings->{'showlogo'}{$item};                      $designs{'showlogo'}{$item} = $settings->{'showlogo'}{$item};
                 }                  }
             }              }
               foreach my $item (@alttext) {
                   if (ref($settings->{'alttext'}) eq 'HASH') {
                       if ($settings->{'alttext'}->{$item} ne '') {
                           $designs{'alttext'}{$item} = $settings->{'alttext'}{$item};
                       }
                   }
               }
             foreach my $item (@logintext) {              foreach my $item (@logintext) {
                 if ($settings->{$item} ne '') {                  if ($settings->{$item} ne '') {
                     $designs{'logintext'}{$item} = $settings->{$item};                      $designs{'logintext'}{$item} = $settings->{$item};
Line 1213  sub print_login { Line 1407  sub print_login {
         $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext);          $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext);
         $datatable .= '</tr></table></td></tr>';          $datatable .= '</tr></table></td></tr>';
     } elsif ($caller eq 'help') {      } elsif ($caller eq 'help') {
         my ($defaulturl,$defaulttype,%url,%type,%lt,%langchoices);          my ($defaulturl,$defaulttype,%url,%type,%langchoices);
         my $switchserver = &check_switchserver($dom,$confname);  
         my $itemcount = 1;          my $itemcount = 1;
         $defaulturl = '/adm/loginproblems.html';          $defaulturl = '/adm/loginproblems.html';
         $defaulttype = 'default';          $defaulttype = 'default';
         %lt = &Apache::lonlocal::texthash (  
                      del     => 'Delete?',  
                      rep     => 'Replace:',  
                      upl     => 'Upload:',  
                      default => 'Default',  
                      custom  => 'Custom',  
                                              );  
         %langchoices = &Apache::lonlocal::texthash(&get_languages_hash());          %langchoices = &Apache::lonlocal::texthash(&get_languages_hash());
         my @currlangs;          my @currlangs;
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
Line 1321  sub print_login { Line 1507  sub print_login {
                 }                  }
             }              }
         }          }
         my %lt = &Apache::lonlocal::texthash(  
                                                del  => 'Delete?',  
                                                rep  => 'Replace:',  
                                                upl  => 'Upload:',  
                                                curr => 'View contents',  
                                                none => 'None',  
         );  
         my $switchserver = &check_switchserver($dom,$confname);  
         foreach my $lonhost (sort(keys(%domservers))) {          foreach my $lonhost (sort(keys(%domservers))) {
             my $exempt = &check_exempt_addresses($currexempt{$lonhost});              my $exempt = &check_exempt_addresses($currexempt{$lonhost});
             $datatable .= '<tr><td>'.$domservers{$lonhost}.'</td>';              $datatable .= '<tr><td>'.$domservers{$lonhost}.'</td>';
Line 1352  sub print_login { Line 1530  sub print_login {
             $datatable .= '</td><td><input type="text" name="loginheadtagexempt_'.$lonhost.'" value="'.$exempt.'" /></td></tr>';              $datatable .= '</td><td><input type="text" name="loginheadtagexempt_'.$lonhost.'" value="'.$exempt.'" /></td></tr>';
         }          }
         $datatable .= '</table></td></tr>';          $datatable .= '</table></td></tr>';
       } elsif ($caller eq 'saml') {
           my %domservers = &Apache::lonnet::get_servers($dom);
           $datatable .= '<tr><td colspan="3" style="text-align: left">'.
                         '<table><tr><th>'.$choices{'hostid'}.'</th>'.
                         '<th>'.$choices{'samllanding'}.'</th>'.
                         '<th>'.$choices{'samloptions'}.'</th></tr>'."\n";
           my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso,%styleon,%styleoff);
           foreach my $lonhost (keys(%domservers)) {
               $samlurl{$lonhost} = '/adm/sso';
               $styleon{$lonhost} = 'display:none';
               $styleoff{$lonhost} = '';
           }
           if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) {
               foreach my $lonhost (keys(%{$settings->{'saml'}})) {
                   if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') {
                       $saml{$lonhost} = 1;
                       $samltext{$lonhost} = $settings->{'saml'}{$lonhost}{'text'};
                       $samlimg{$lonhost} = $settings->{'saml'}{$lonhost}{'img'};
                       $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'};
                       $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'};
                       $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'};
                       $samlwindow{$lonhost} = $settings->{'saml'}{$lonhost}{'window'};
                       $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'};
                       $styleon{$lonhost} = '';
                       $styleoff{$lonhost} = 'display:none';
                   } else {
                       $styleon{$lonhost} = 'display:none';
                       $styleoff{$lonhost} = '';
                   }
               }
           }
           my $itemcount = 1;
           foreach my $lonhost (sort(keys(%domservers))) {
               my $samlon = ' ';
               my $samloff = ' checked="checked" ';
               if ($saml{$lonhost}) {
                   $samlon = $samloff;
                   $samloff = ' ';
               }
               my $samlwinon = '';
               my $samlwinoff = ' checked="checked"';
               if ($samlwindow{$lonhost}) {
                   $samlwinon = $samlwinoff;
                   $samlwinoff = '';
               }
               my $css_class = $itemcount%2?' class="LC_odd_row"':'';
               $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.$domservers{$lonhost}.'</span></td>'.
                             '<td><span class="LC_nobreak"><label><input type="radio" name="saml_'.$lonhost.'"'.$samloff.
                             'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="0" />'.
                             &mt('No').'</label>'.('&nbsp;'x2).
                             '<label><input type="radio" name="saml_'.$lonhost.'"'.$samlon.
                             'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="1" />'.
                             &mt('Yes').'</label></span></td>'.
                             '<td id="samloptionson_'.$lonhost.'" style="'.$styleon{$lonhost}.'" width="100%">'.
                             '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th></tr>'.
                             '<tr><th>'.&mt('Text').'</th><th>'.&mt('Image').'</th>'.
                             '<th>'.&mt('Alt Text').'</th></tr>'.
                             '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="20" value="'.
                             $samltext{$lonhost}.'" /></td><td>';
               if ($samlimg{$lonhost}) {
                   $datatable .= '<img src="'.$samlimg{$lonhost}.'" /><br />'.
                                 '<span class="LC_nobreak"><label>'.
                                 '<input type="checkbox" name="saml_img_del" value="'.$lonhost.'" />'.
                                 $lt{'del'}.'</label>&nbsp;'.$lt{'rep'}.'</span>';
               } else {
                   $datatable .= $lt{'upl'};
               }
               $datatable .='<br />';
               if ($switchserver) {
                   $datatable .= &mt('Upload to library server: [_1]',$switchserver);
               } else {
                   $datatable .= '<input type="file" name="saml_img_'.$lonhost.'" />';
               }
               $datatable .= '</td>'.
                             '<td><input type="text" name="saml_alt_'.$lonhost.'" size="25" '.
                             'value="'.$samlalt{$lonhost}.'" /></td></tr></table><br />'.
                             '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th><th align="center">'.
                             '<span class="LC_nobreak">'.&mt('Non-SSO').'</span></th></tr>'.
                             '<tr><th>'.&mt('URL').'</th><th>'.&mt('Tool Tip').'</th>'.
                             '<th>'.&mt('Pop-up if iframe').'</th><th>'.&mt('Text').'</th></tr>'.
                             '<tr'.$css_class.'>'.
                             '<td><input type="text" name="saml_url_'.$lonhost.'" size="30" '.
                             'value="'.$samlurl{$lonhost}.'" /></td>'.
                             '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="20">'.
                             $samltitle{$lonhost}.'</textarea></td>'.
                             '<td><label><input type="radio" name="saml_window_'.$lonhost.'" value=""'.$samlwinoff.'>'.
                             &mt('No').'</label>'.('&nbsp;'x2).'<label><input type="radio" '.
                             'name="saml_window_'.$lonhost.'" value="1"'.$samlwinon.'>'.&mt('Yes').'</label></td>'.
                             '<td><input type="text" name="saml_notsso_'.$lonhost.'" size="12" '.
                             'value="'.$samlnotsso{$lonhost}.'" /></td></tr>'.
                             '</table></td>'.
                             '<td id="samloptionsoff_'.$lonhost.'" style="'.$styleoff{$lonhost}.'" width="100%">&nbsp;</td></tr>';
              $itemcount ++;
           }
           $datatable .= '</table></td></tr>';
     }      }
     return $datatable;      return $datatable;
 }  }
Line 1388  sub login_choices { Line 1661  sub login_choices {
             headtag       => "Custom markup",              headtag       => "Custom markup",
             action        => "Action",              action        => "Action",
             current       => "Current",              current       => "Current",
               samllanding   => "Dual login?",
               samloptions   => "Options",
               alttext       => "Alt text",
         );          );
     return %choices;      return %choices;
 }  }
   
   sub login_file_options {
         return &Apache::lonlocal::texthash(
                                              del     => 'Delete?',
                                              rep     => 'Replace:',
                                              upl     => 'Upload:',
                                              curr    => 'View contents',
                                              default => 'Default',
                                              custom  => 'Custom',
                                              none    => 'None',
         );
   }
   
   sub print_ipaccess {
       my ($dom,$settings,$rowtotal) = @_;
       my $css_class;
       my $itemcount = 0;
       my $datatable;
       my %ordered;
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   if ($num eq '') {
                       $num = scalar(keys(%{$settings}));
                   }
                   $ordered{$num} = $item;
               }
           }
       }
       my $maxnum = scalar(keys(%ordered));
       if (keys(%ordered)) {
           my @items = sort { $a <=> $b } keys(%ordered);
           for (my $i=0; $i<@items; $i++) {
               $css_class = $itemcount%2?' class="LC_odd_row"':'';
               my $item = $ordered{$items[$i]};
               my ($name,$ipranges,%commblocks,%courses);
               if (ref($settings->{$item}) eq 'HASH') {
                   $name = $settings->{$item}->{'name'};
                   $ipranges = $settings->{$item}->{'ip'};
                   if (ref($settings->{$item}->{'commblocks'}) eq 'HASH') {
                       %commblocks = %{$settings->{$item}->{'commblocks'}};
                   }
                   if (ref($settings->{$item}->{'courses'}) eq 'HASH') {
                       %courses = %{$settings->{$item}->{'courses'}};
                   }
               }
               my $chgstr = ' onchange="javascript:reorderIPaccess(this.form,'."'ipaccess_pos_".$item."'".');"';
               $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                            .'<select name="ipaccess_pos_'.$item.'"'.$chgstr.'>';
               for (my $k=0; $k<=$maxnum; $k++) {
                   my $vpos = $k+1;
                   my $selstr;
                   if ($k == $i) {
                       $selstr = ' selected="selected" ';
                   }
                   $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
               }
               $datatable .= '</select>'.('&nbsp;'x2).
                   '<label><input type="checkbox" name="ipaccess_del" value="'.$item.'" />'.
                   &mt('Delete?').'</label></span></td>'.
                   '<td colspan="2"><input type="hidden" name="ipaccess_id_'.$i.'" value="'.$item.'" />'.
                   &ipaccess_options($i,$itemcount,$dom,$name,$ipranges,\%commblocks,\%courses).
                   '</td></tr>';
               $itemcount ++;
           }
       }
       $css_class = $itemcount%2?' class="LC_odd_row"':'';
       my $chgstr = ' onchange="javascript:reorderIPaccess(this.form,'."'ipaccess_pos_add'".');"';
       $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
                     '<input type="hidden" name="ipaccess_maxnum" value="'.$maxnum.'" />'."\n".
                     '<select name="ipaccess_pos_add"'.$chgstr.'>';
       for (my $k=0; $k<$maxnum+1; $k++) {
           my $vpos = $k+1;
           my $selstr;
           if ($k == $maxnum) {
               $selstr = ' selected="selected" ';
           }
           $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
       }
       $datatable .= '</select>&nbsp;'."\n".
                     '<input type="checkbox" name="ipaccess_add" value="1" />'.&mt('Add').'</span></td>'."\n".
                     '<td colspan="2">'.
                     &ipaccess_options('add',$itemcount,$dom).
                     '</td>'."\n".
                     '</tr>'."\n";
       $$rowtotal ++;
       return $datatable;
   }
   
   sub ipaccess_options {
       my ($num,$itemcount,$dom,$name,$ipranges,$blocksref,$coursesref) = @_;
       my (%currblocks,%currcourses,$output);
       if (ref($blocksref) eq 'HASH') {
           %currblocks = %{$blocksref};
       }
       if (ref($coursesref) eq 'HASH') {
           %currcourses = %{$coursesref};
       }
       $output = '<fieldset><legend>'.&mt('Location(s)').'</legend>'.
                 '<span class="LC_nobreak">'.&mt('Name').':&nbsp;'.
                 '<input type="text" name="ipaccess_name_'.$num.'" value="'.$name.'" />'.
                 '</span></fieldset>'.
                 '<fieldset><legend>'.&mt('IP Range(s)').'</legend>'.
                 &mt('Format for each IP range').': '.&mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'<br />'.
                 &mt('Range(s) will be stored as IP netblock(s) in CIDR notation (comma separated)').'<br />'.
                 '<textarea name="ipaccess_range_'.$num.'" rows="3" cols="80">'.
                 $ipranges.'</textarea></fieldset>'.
                 '<fieldset><legend>'.&mt('Functionality Blocked?').'</legend>'.
                 &blocker_checkboxes($num,$blocksref).'</fieldset>'.
                 '<fieldset><legend>'.&mt('Courses/Communities allowed').'</legend>'.
                 '<table>';
       foreach my $cid (sort(keys(%currcourses))) {
           my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1});
           $output .= '<tr><td><span class="LC_nobreak">'.
                      '<label><input type="checkbox" name="ipaccess_course_delete_'.$num.'" value="'.$cid.'" />'.
                      &mt('Delete?').'&nbsp;<span class="LC_cusr_emph">'.$courseinfo{'description'}.'</span></label></span>'.
                      ' <span class="LC_fontsize_medium">('.$cid.')</span></td></tr>';
       }
       $output .= '<tr><td><span class="LC_nobreak">'.&mt('Add').':&nbsp;'.
                  '<input type="text" name="ipaccess_cdesc_'.$num.'" value="" onfocus="this.blur();opencrsbrowser('."'display','ipaccess_cnum_$num','ipaccess_cdom_$num','ipaccess_cdesc_$num'".');" />'.
                   &Apache::loncommon::selectcourse_link('display','ipaccess_cnum_'.$num,'ipaccess_cdom_'.$num,'ipaccess_cdesc_'.$num,$dom,undef,'Course/Community').
                  '<input type="hidden" name="ipaccess_cnum_'.$num.'" value="" />'.
                  '<input type="hidden" name="ipaccess_cdom_'.$num.'" value="" />'.
                  '</span></td></tr></table>'."\n".
                  '</fieldset>';
       return $output;
   }
   
   sub blocker_checkboxes {
       my ($num,$blocks) = @_;
       my ($typeorder,$types) = &commblocktype_text();
       my $numinrow = 6;
       my $output = '<table>';
       for (my $i=0; $i<@{$typeorder}; $i++) {
           my $block = $typeorder->[$i];
           my $blockstatus;
           if (ref($blocks) eq 'HASH') {
               if ($blocks->{$block} eq 'on') {
                   $blockstatus = 'checked="checked"';
               }
           }
           my $rem = $i%($numinrow);
           if ($rem == 0) {
               if ($i > 0) {
                   $output .= '</tr>';
               }
               $output .= '<tr>';
           }
           if ($i == scalar(@{$typeorder})-1) {
               my $colsleft = $numinrow-$rem;
               if ($colsleft > 1) {
                   $output .= '<td colspan="'.$colsleft.'">';
               } else {
                   $output .= '<td>';
               }
           } else {
               $output .= '<td>';
           }
           my $item = 'ipaccess_block_'.$num;
           if ($blockstatus) {
               $blockstatus = ' '.$blockstatus;
           }
           $output .= '<span class="LC_nobreak"><label>'."\n".
                      '<input type="checkbox" name="'.$item.'"'.
                      $blockstatus.' value="'.$block.'"'.' />'.
                      $types->{$block}.'</label></span>'."\n".
                      '<br /></td>';
       }
       $output .= '</tr></table>';
       return $output;
   }
   
   sub commblocktype_text {
       my %types = &Apache::lonlocal::texthash(
           'com' => 'Messaging',
           'chat' => 'Chat Room',
           'boards' => 'Discussion',
           'port' => 'Portfolio',
           'groups' => 'Groups',
           'blogs' => 'Blogs',
           'about' => 'User Information',
           'printout' => 'Printouts',
           'passwd' => 'Change Password',
           'grades' => 'Gradebook',
           'search' => 'Course search',
           'wishlist' => 'Stored links',
           'annotate' => 'Annotations',
       );
       my $typeorder = ['com','chat','boards','port','groups','blogs','about','wishlist','printout','grades','search','annotate','passwd'];
       return ($typeorder,\%types);
   }
   
 sub print_rolecolors {  sub print_rolecolors {
     my ($phase,$role,$dom,$confname,$settings,$rowtotal) = @_;      my ($phase,$role,$dom,$confname,$settings,$rowtotal) = @_;
     my %choices = &color_font_choices();      my %choices = &color_font_choices();
Line 1541  sub display_color_options { Line 2009  sub display_color_options {
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $css_class = $itemcount%2?' class="LC_odd_row"':'';
         $datatable .= '<tr'.$css_class.'>'.          $datatable .= '<tr'.$css_class.'>'.
                       '<td>'.$choices->{$img};                        '<td>'.$choices->{$img};
         my ($imgfile,$img_import,$login_hdr_pick,$logincolors);          my ($imgfile,$img_import,$login_hdr_pick,$logincolors,$alttext);
         if ($role eq 'login') {          if ($role eq 'login') {
             if ($img eq 'login') {              if ($img eq 'login') {
                 $login_hdr_pick =                  $login_hdr_pick =
Line 1549  sub display_color_options { Line 2017  sub display_color_options {
                 $logincolors =                  $logincolors =
                     &login_text_colors($img,$role,$logintext,$phase,$choices,                      &login_text_colors($img,$role,$logintext,$phase,$choices,
                                        $designs,$defaults);                                         $designs,$defaults);
             } elsif ($img ne 'domlogo') {              } else {
                 $datatable.= &logo_display_options($img,$defaults,$designs);                  if ($img ne 'domlogo') {
                       $datatable.= &logo_display_options($img,$defaults,$designs);
                   }
                   if (ref($designs->{'alttext'}) eq 'HASH') {
                       $alttext = $designs->{'alttext'}{$img};
                   }
             }              }
         }          }
         $datatable .= '</td>';          $datatable .= '</td>';
Line 1642  sub display_color_options { Line 2115  sub display_color_options {
                 $datatable .='&nbsp;<input type="file" name="'.$role.'_'.$img.'" />';                  $datatable .='&nbsp;<input type="file" name="'.$role.'_'.$img.'" />';
             }              }
         }          }
           if (($role eq 'login') && ($img ne 'login')) {
               $datatable .= ('&nbsp;' x2).' <span class="LC_nobreak"><label>'.$choices->{'alttext'}.':'.
                             '<input type="text" name="'.$role.'_alt_'.$img.'" size="10" value="'.$alttext.'" />'.
                             '</label></span>';
           }
         $datatable .= '</td></tr>';          $datatable .= '</td></tr>';
     }      }
     $itemcount ++;      $itemcount ++;
Line 1804  sub print_quotas { Line 2282  sub print_quotas {
     my $typecount = 0;      my $typecount = 0;
     my ($css_class,%titles);      my ($css_class,%titles);
     if ($context eq 'requestcourses') {      if ($context eq 'requestcourses') {
         @usertools = ('official','unofficial','community','textbook');          @usertools = ('official','unofficial','community','textbook','lti');
         @options =('norequest','approval','validate','autolimit');          @options =('norequest','approval','validate','autolimit');
         %validations = &Apache::lonnet::auto_courserequest_checks($dom);          %validations = &Apache::lonnet::auto_courserequest_checks($dom);
         %titles = &courserequest_titles();          %titles = &courserequest_titles();
Line 1813  sub print_quotas { Line 2291  sub print_quotas {
         @options = ('norequest','approval','automatic');          @options = ('norequest','approval','automatic');
         %titles = &authorrequest_titles();          %titles = &authorrequest_titles();
     } else {      } else {
         @usertools = ('aboutme','blog','webdav','portfolio');          @usertools = ('aboutme','blog','webdav','portfolio','timezone');
         %titles = &tool_titles();          %titles = &tool_titles();
     }      }
     if (ref($types) eq 'ARRAY') {      if (ref($types) eq 'ARRAY') {
Line 1917  sub print_quotas { Line 2395  sub print_quotas {
                         }                          }
                     } else {                      } else {
                         my $checked = 'checked="checked" ';                          my $checked = 'checked="checked" ';
                           if ($item eq 'timezone') {
                               $checked = '';
                           }
                         if (ref($settings) eq 'HASH') {                          if (ref($settings) eq 'HASH') {
                             if (ref($settings->{$item}) eq 'HASH') {                              if (ref($settings->{$item}) eq 'HASH') {
                                 if ($settings->{$item}->{$type} == 0) {                                  if (!$settings->{$item}->{$type}) {
                                     $checked = '';                                      $checked = '';
                                 } elsif ($settings->{$item}->{$type} == 1) {                                  } elsif ($settings->{$item}->{$type} == 1) {
                                     $checked =  'checked="checked" ';                                      $checked =  'checked="checked" ';
Line 2268  sub print_studentcode { Line 2749  sub print_studentcode {
     my ($settings,$rowtotal) = @_;      my ($settings,$rowtotal) = @_;
     my $rownum = 0;       my $rownum = 0; 
     my ($output,%current);      my ($output,%current);
     my @crstypes = ('official','unofficial','community','textbook');      my @crstypes = ('official','unofficial','community','textbook','lti');
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if (ref($settings->{'uniquecode'}) eq 'HASH') {          if (ref($settings->{'uniquecode'}) eq 'HASH') {
             foreach my $type (@crstypes) {              foreach my $type (@crstypes) {
Line 2364  sub print_textbookcourses { Line 2845  sub print_textbookcourses {
                               ('&nbsp;'x2).                                ('&nbsp;'x2).
                               '<span class="LC_nobreak">'.&mt('Thumbnail:');                                '<span class="LC_nobreak">'.&mt('Thumbnail:');
                 if ($image) {                  if ($image) {
                     $datatable .= '<span class="LC_nobreak">'.                      $datatable .= $imgsrc.
                                   $imgsrc.  
                                   '<label><input type="checkbox" name="'.$type.'_image_del"'.                                    '<label><input type="checkbox" name="'.$type.'_image_del"'.
                                   ' value="'.$key.'" />'.&mt('Delete?').'</label></span> '.                                    ' value="'.$key.'" />'.&mt('Delete?').'</label></span> '.
                                   '<span class="LC_nobreak">&nbsp;'.&mt('Replace:').'&nbsp;';                                    '<span class="LC_nobreak">&nbsp;'.&mt('Replace:').'&nbsp;';
Line 2521  ENDSCRIPT Line 3001  ENDSCRIPT
   
 sub ltitools_javascript {  sub ltitools_javascript {
     my ($settings) = @_;      my ($settings) = @_;
     return unless(ref($settings) eq 'HASH');      my $togglejs = &ltitools_toggle_js();
       unless (ref($settings) eq 'HASH') {
           return $togglejs;
       }
     my (%ordered,$total,%jstext);      my (%ordered,$total,%jstext);
     $total = 0;      $total = 0;
     foreach my $item (keys(%{$settings})) {      foreach my $item (keys(%{$settings})) {
Line 2539  sub ltitools_javascript { Line 3022  sub ltitools_javascript {
     return <<"ENDSCRIPT";      return <<"ENDSCRIPT";
 <script type="text/javascript">  <script type="text/javascript">
 // <![CDATA[  // <![CDATA[
 function reorderLTI(form,item) {  function reorderLTITools(form,item) {
     var changedVal;      var changedVal;
 $jstext  $jstext
     var newpos = 'ltitools_add_pos';      var newpos = 'ltitools_add_pos';
Line 2584  $jstext Line 3067  $jstext
 // ]]>  // ]]>
 </script>  </script>
   
   $togglejs
   
   ENDSCRIPT
   }
   
   sub ltitools_toggle_js {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   
   function toggleLTITools(form,setting,item) {
       var radioname = '';
       var divid = '';
       if ((setting == 'passback') || (setting == 'roster')) {
           radioname = 'ltitools_'+setting+'_'+item;
           divid = 'ltitools_'+setting+'time_'+item;
           var num = form.elements[radioname].length;
           if (num) {
               var setvis = '';
               for (var i=0; i<num; i++) {
                   if (form.elements[radioname][i].checked) {
                       if (form.elements[radioname][i].value == '1') {
                           if (document.getElementById(divid)) {
                               document.getElementById(divid).style.display = 'inline-block';
                           }
                           setvis = 1;
                       }
                       break;
                   }
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       if (setting == 'user') {
           divid = 'ltitools_'+setting+'_div_'+item;
           var checkid = 'ltitools_'+setting+'_field_'+item;
           if (document.getElementById(divid)) {
               if (document.getElementById(checkid)) {
                   if (document.getElementById(checkid).checked) {
                       document.getElementById(divid).style.display = 'inline-block';
                   } else {
                       document.getElementById(divid).style.display = 'none';
                   }
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub wafproxy_javascript {
       my ($dom) = @_;
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function updateWAF() {
       if (document.getElementById('wafproxy_remoteip')) {
           var wafremote = 0;
           if (document.display.wafproxy_remoteip.options[document.display.wafproxy_remoteip.selectedIndex].value == 'h') {
               wafremote = 1;
           }
           var fields = new Array('header','trust');
           for (var i=0; i<fields.length; i++) {
               if (document.getElementById('wafproxy_'+fields[i])) {
                   if (wafremote == 1) {
                       document.getElementById('wafproxy_'+fields[i]).style.display = 'table-row';
                   }
                   else {
                       document.getElementById('wafproxy_'+fields[i]).style.display = 'none';
                   }
               }
           }
           if (document.getElementById('wafproxyranges_$dom')) {
               if (wafremote == 1) {
                   document.getElementById('wafproxyranges_$dom').style.display = 'inline-block';
               } else {
                   for (var i=0; i<document.display.wafproxy_vpnaccess.length; i++) {
                       if (document.display.wafproxy_vpnaccess[i].checked) {
                           if (document.display.wafproxy_vpnaccess[i].value == 0) {
                               document.getElementById('wafproxyranges_$dom').style.display = 'none';
                           }
                       }
                   }
               }
           }
       }
       return;
   }
   
   function checkWAF() {
       if (document.getElementById('wafproxy_remoteip')) {
           var wafvpn = 0;
           for (var i=0; i<document.display.wafproxy_vpnaccess.length; i++) {
               if (document.display.wafproxy_vpnaccess[i].checked) {
                   if (document.display.wafproxy_vpnaccess[i].value == 1) {
                       wafvpn = 1;
                   }
                   break;
               }
           }
           var vpn = new Array('vpnint','vpnext');
           for (var i=0; i<vpn.length; i++) {
               if (document.getElementById('wafproxy_show_'+vpn[i])) {
                   if (wafvpn == 1) {
                       document.getElementById('wafproxy_show_'+vpn[i]).style.display = 'table-row';
                   }
                   else {
                       document.getElementById('wafproxy_show_'+vpn[i]).style.display = 'none';
                   }
               }
           }
           if (document.getElementById('wafproxyranges_$dom')) {
               if (wafvpn == 1) {
                   document.getElementById('wafproxyranges_$dom').style.display = 'inline-block';
               }
               else if (document.display.wafproxy_remoteip.options[document.display.wafproxy_remoteip.selectedIndex].value != 'h') {
                   document.getElementById('wafproxyranges_$dom').style.display = 'none';
               }
           }
       }
       return;
   }
   
   function toggleWAF() {
       if (document.getElementById('wafproxy_table')) {
           var wafproxy = 0;
           for (var i=0; i<document.display.wafproxy_${dom}.length; i++) {
                if (document.display.wafproxy_${dom}[i].checked) {
                    if (document.display.wafproxy_${dom}[i].value == 1) {
                        wafproxy = 1;
                        break;
                   }
               }
           }
           if (wafproxy == 1) {
               document.getElementById('wafproxy_table').style.display='inline';
           }
           else {
              document.getElementById('wafproxy_table').style.display='none';
           }
           if (document.getElementById('wafproxyrow_${dom}')) {
               if (wafproxy == 1) {
                   document.getElementById('wafproxyrow_${dom}').style.display = 'table-row';
               }
               else {
                   document.getElementById('wafproxyrow_${dom}').style.display = 'none';
               }
           }
           if (document.getElementById('nowafproxyrow_$dom')) {
               if (wafproxy == 1) {
                   document.getElementById('nowafproxyrow_${dom}').style.display = 'none';
               }
               else {
                   document.getElementById('nowafproxyrow_${dom}').style.display = 'table-row';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub lti_javascript {
       my ($dom,$settings) = @_;
       my $togglejs = &lti_toggle_js($dom);
       my $linkprot_js = &Apache::courseprefs::linkprot_javascript();
       unless (ref($settings) eq 'HASH') {
           return $togglejs.'
   <script type="text/javascript">
   // <![CDATA[
   
   '.$linkprot_js.'
   
   // ]]>
   </script>
   ';
       }
       my (%ordered,$total,%jstext);
       $total = scalar(keys(%{$settings}));
       foreach my $item (keys(%{$settings})) {
           if (ref($settings->{$item}) eq 'HASH') {
               my $num = $settings->{$item}{'order'};
               if ($num eq '') {
                   $num = $total - 1;
               }
               $ordered{$num} = $item;
           }
       }
       my @jsarray = ();
       foreach my $item (sort {$a <=> $b } (keys(%ordered))) {
           push(@jsarray,$ordered{$item});
       }
       my $jstext = '    var lti = Array('."'".join("','",@jsarray)."'".');'."\n";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function reorderLTI(form,item) {
       var changedVal;
   $jstext
       var newpos = 'lti_pos_add';
       var maxh = 1 + $total;
       var current = new Array;
       var newitemVal = form.elements[newpos].options[form.elements[newpos].selectedIndex].value;
       if (item == newpos) {
           changedVal = newitemVal;
       } else {
           changedVal = form.elements[item].options[form.elements[item].selectedIndex].value;
           current[newitemVal] = newpos;
       }
       for (var i=0; i<lti.length; i++) {
           var elementName = 'lti_pos_'+lti[i];
           if (elementName != item) {
               if (form.elements[elementName]) {
                   var currVal = form.elements[elementName].options[form.elements[elementName].selectedIndex].value;
                   current[currVal] = elementName;
               }
           }
       }
       var oldVal;
       for (var j=0; j<maxh; j++) {
           if (current[j] == undefined) {
               oldVal = j;
           }
       }
       if (oldVal < changedVal) {
           for (var k=oldVal+1; k<=changedVal ; k++) {
              var elementName = current[k];
              form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex - 1;
           }
       } else {
           for (var k=changedVal; k<oldVal; k++) {
               var elementName = current[k];
               form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex + 1;
           }
       }
       return;
   }
   
   $linkprot_js
   
   // ]]>
   </script>
   
   $togglejs
   
   ENDSCRIPT
   }
   
   sub lti_toggle_js {
       my ($dom) = @_;
       my %lcauthparmtext = &Apache::lonlocal::texthash (
                               localauth => 'Local auth argument',
                               krb       => 'Kerberos domain',
                            );
       my $crsincalert = &mt('"User\'s identity sent" needs to be set to "Yes" first,[_1] before setting "Course\'s identity sent" to "Yes"',"\n");
       &js_escape(\$crsincalert);
       my %servers = &Apache::lonnet::get_servers($dom,'library');
       my $primary = &Apache::lonnet::domain($dom,'primary');
       my $course_servers = "'".join("','",keys(%servers))."'";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   
   function toggleLTI(form,setting,item) {
       if ((setting == 'requser') || (setting == 'crsinc')) {
           var usrfieldsets = document.getElementsByClassName('ltioption_usr_'+item);
           var setvis = '';
           var radioname = 'lti_requser_'+item;
           var num = form.elements[radioname].length;
           if (num) {
               for (var i=0; i<num; i++) {
                   if (form.elements[radioname][i].checked) {
                       if (form.elements[radioname][i].value == '1') {
                           setvis = 1;
                           break;
                       }
                   }
               }
           }
           if (usrfieldsets.length) {
               for (var j=0; j<usrfieldsets.length; j++) {
                   if (setvis) {
                       usrfieldsets[j].style.display = 'block';
                   } else {
                       usrfieldsets[j].style.display = 'none';
                   }
               }
           }
           var crsfieldsets = document.getElementsByClassName('ltioption_crs_'+item);
           if (crsfieldsets.length) {
               radioname = 'lti_crsinc_'+item;
               var num = form.elements[radioname].length;
               if (num) {
                   var crsvis = '';
                   for (var i=0; i<num; i++) {
                       if (form.elements[radioname][i].checked) {
                           if (form.elements[radioname][i].value == '1') {
                               if (setvis == '') {
                                   if (setting == 'crsinc'){
                                       alert("$crsincalert");
                                       form.elements[radioname][0].checked = true;
                                   }
                               } else {
                                   crsvis = 1;
                               }
                               break;
                           }
                       }
                   }
                   setvis = crsvis;
               }
               for (var j=0; j<crsfieldsets.length; j++) {
                   if (setvis) {
                       crsfieldsets[j].style.display = 'block';
                   } else {
                       crsfieldsets[j].style.display = 'none';
                   }
               }
           }
       } else if ((setting == 'user') || (setting == 'crs') || (setting == 'passback') || (setting == 'callback')) {
           var radioname = '';
           var divid = '';
           if (setting == 'user') {
               radioname = 'lti_mapuser_'+item;
               divid = 'lti_userfield_'+item;
           } else if (setting == 'crs') {
               radioname = 'lti_mapcrs_'+item;
               divid = 'lti_crsfield_'+item;
           } else if (setting == 'callback') {
               radioname = 'lti_callback_'+item;
               divid = 'lti_callbackfield_'+item;
           } else {
               radioname = 'lti_passback_'+item;
               divid =  'lti_passback_'+item;
           }
           var num = form.elements[radioname].length;
           if (num) {
               var setvis = '';
               for (var i=0; i<num; i++) {
                  if (form.elements[radioname][i].checked) {
                      if ((setting == 'passback') || (setting == 'callback')) {
                          if (form.elements[radioname][i].value == '1') {
                              if (document.getElementById(divid)) {
                                  document.getElementById(divid).style.display = 'inline-block';
                              }
                              setvis = 1;
                              break;
                          }
                      } else {
                          if (form.elements[radioname][i].value == 'other') {
                              if (document.getElementById(divid)) {
                                  document.getElementById(divid).style.display = 'inline-block';
                              }
                              setvis = 1;
                              break;
                          }
                      }
                  }
               }
               if (!setvis) {
                   if (document.getElementById(divid)) {
                       document.getElementById(divid).style.display = 'none';
                   }
               }
           }
       } else if ((setting == 'sec') || (setting == 'secsrc')) {
           var numsec = form.elements['lti_crssec_'+item].length;
           if (numsec) {
               var setvis = '';
               for (var i=0; i<numsec; i++) {
                   if (form.elements['lti_crssec_'+item][i].checked) {
                       if (form.elements['lti_crssec_'+item][i].value == '1') {
                           if (document.getElementById('lti_crssecfield_'+item)) {
                               document.getElementById('lti_crssecfield_'+item).style.display = 'inline-block';
                               setvis = 1;
                               var numsrcsec = form.elements['lti_crssecsrc_'+item].length;
                               if (numsrcsec) {
                                   var setsrcvis = '';
                                   for (var j=0; j<numsrcsec; j++) {
                                       if (form.elements['lti_crssecsrc_'+item][j].checked) {
                                           if (form.elements['lti_crssecsrc_'+item][j].value == 'other') {
                                               if (document.getElementById('lti_secsrcfield_'+item)) {
                                                   document.getElementById('lti_secsrcfield_'+item).style.display = 'inline-block';
                                                   setsrcvis = 1;
                                               }
                                           }
                                       }
                                   }
                                   if (!setsrcvis) {
                                       if (document.getElementById('lti_secsrcfield_'+item)) {
                                           document.getElementById('lti_secsrcfield_'+item).style.display = 'none';
                                       }
                                   }
                               }
                           }
                       }
                   }
               }
               if (!setvis) {
                   if (document.getElementById('lti_crssecfield_'+item)) {
                       document.getElementById('lti_crssecfield_'+item).style.display = 'none';
                   }
                   if (document.getElementById('lti_secsrcfield_'+item)) {
                       document.getElementById('lti_secsrcfield_'+item).style.display = 'none';
                   }
               }
           }
       } else if (setting == 'lcauth') {
           var numauth = form.elements['lti_lcauth_'+item].length;
           if (numauth) {
               for (var i=0; i<numauth; i++) {
                   if (form.elements['lti_lcauth_'+item][i].checked) {
                       if (document.getElementById('lti_'+setting+'_parmrow_'+item)) {
                           if ((form.elements['lti_'+setting+'_'+item][i].value == 'internal') || (form.elements['lti_'+setting+'_'+item][i].value == 'lti')) {
                               document.getElementById('lti_'+setting+'_parmrow_'+item).style.display = 'none';
                           } else {
                               document.getElementById('lti_'+setting+'_parmrow_'+item).style.display = 'table-row';
                               if (document.getElementById('lti_'+setting+'_parmtext_'+item)) {
                                   if (form.elements['lti_'+setting+'_'+item][i].value == 'localauth') {
                                       document.getElementById('lti_'+setting+'_parmtext_'+item).innerHTML = "$lcauthparmtext{'localauth'}";
                                   } else {
                                       document.getElementById('lti_'+setting+'_parmtext_'+item).innerHTML = "$lcauthparmtext{'krb'}";
                                   }
                               }
                           }
                       }
                   }
               }
           }
       } else if (setting == 'lcmenu') {
           var menus = new Array('lti_topmenu_'+item,'lti_inlinemenu_'+item);
           var divid = 'lti_menufield_'+item;
           var setvis = '';
           for (var i=0; i<menus.length; i++) {
               var radioname = menus[i];
               var num = form.elements[radioname].length;
               if (num) {
                   for (var j=0; j<num; j++) {
                       if (form.elements[radioname][j].checked) {
                           if (form.elements[radioname][j].value == '1') {
                               if (document.getElementById(divid)) {
                                   document.getElementById(divid).style.display = 'inline-block';
                               }
                               setvis = 1;
                               break;
                           }
                       }
                   }
               }
               if (setvis == 1) {
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       return;
   }
   
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub autoupdate_javascript {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function toggleLastActiveDays(form) {
       var radioname = 'lastactive';
       var divid = 'lastactive_div';
       var num = form.elements[radioname].length;
       if (num) {
           var setvis = '';
           for (var i=0; i<num; i++) {
               if (form.elements[radioname][i].checked) {
                   if (form.elements[radioname][i].value == '1') {
                       if (document.getElementById(divid)) {
                           document.getElementById(divid).style.display = 'inline-block';
                       }
                       setvis = 1;
                   }
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub autoenroll_javascript {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function toggleFailsafe(form) {
       var radioname = 'autoenroll_failsafe';
       var divid = 'autoenroll_failsafe_div';
       var num = form.elements[radioname].length;
       if (num) {
           var setvis = '';
           for (var i=0; i<num; i++) {
               if (form.elements[radioname][i].checked) {
                   if ((form.elements[radioname][i].value == 'zero') || (form.elements[radioname][i].value == 'any')) {
                       if (document.getElementById(divid)) {
                           document.getElementById(divid).style.display = 'inline-block';
                       }
                       setvis = 1;
                   }
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub saml_javascript {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function toggleSamlOptions(form,hostid) {
       var radioname = 'saml_'+hostid;
       var tablecellon = 'samloptionson_'+hostid;
       var tablecelloff = 'samloptionsoff_'+hostid;
       var num = form.elements[radioname].length;
       if (num) {
           var setvis = '';
           for (var i=0; i<num; i++) {
               if (form.elements[radioname][i].checked) {
                   if (form.elements[radioname][i].value == '1') {
                       if (document.getElementById(tablecellon)) {
                           document.getElementById(tablecellon).style.display='';
                       }
                       if (document.getElementById(tablecelloff)) {
                           document.getElementById(tablecelloff).style.display='none';
                       }
                       setvis = 1;
                   }
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(tablecellon)) {
                   document.getElementById(tablecellon).style.display='none';
               }
               if (document.getElementById(tablecelloff)) {
                   document.getElementById(tablecelloff).style.display='';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub ipaccess_javascript {
       my ($settings) = @_;
       my (%ordered,$total,%jstext);
       $total = 0;
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   $ordered{$num} = $item;
               }
           }
           $total = scalar(keys(%{$settings}));
       }
       my @jsarray = ();
       foreach my $item (sort {$a <=> $b } (keys(%ordered))) {
           push(@jsarray,$ordered{$item});
       }
       my $jstext = '    var ipaccess = Array('."'".join("','",@jsarray)."'".');'."\n";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function reorderIPaccess(form,item) {
       var changedVal;
   $jstext
       var newpos = 'ipaccess_pos_add';
       var maxh = 1 + $total;
       var current = new Array;
       var newitemVal = form.elements[newpos].options[form.elements[newpos].selectedIndex].value;
       if (item == newpos) {
           changedVal = newitemVal;
       } else {
           changedVal = form.elements[item].options[form.elements[item].selectedIndex].value;
           current[newitemVal] = newpos;
       }
       for (var i=0; i<ipaccess.length; i++) {
           var elementName = 'ipaccess_pos_'+ipaccess[i];
           if (elementName != item) {
               if (form.elements[elementName]) {
                   var currVal = form.elements[elementName].options[form.elements[elementName].selectedIndex].value;
                   current[currVal] = elementName;
               }
           }
       }
       var oldVal;
       for (var j=0; j<maxh; j++) {
           if (current[j] == undefined) {
               oldVal = j;
           }
       }
       if (oldVal < changedVal) {
           for (var k=oldVal+1; k<=changedVal ; k++) {
              var elementName = current[k];
              form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex - 1;
           }
       } else {
           for (var k=changedVal; k<oldVal; k++) {
               var elementName = current[k];
               form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex + 1;
           }
       }
       return;
   }
   // ]]>
   </script>
   
 ENDSCRIPT  ENDSCRIPT
 }  }
   
 sub print_autoenroll {  sub print_autoenroll {
     my ($dom,$settings,$rowtotal) = @_;      my ($dom,$settings,$rowtotal) = @_;
     my $autorun = &Apache::lonnet::auto_run(undef,$dom),      my $autorun = &Apache::lonnet::auto_run(undef,$dom),
     my ($defdom,$runon,$runoff,$coownerson,$coownersoff,$failsafe);      my ($defdom,$runon,$runoff,$coownerson,$coownersoff,
           $failsafe,$autofailsafe,$failsafesty,%failsafechecked);
       $failsafesty = 'none';
       %failsafechecked = (
           off => ' checked="checked"',
       );
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if (exists($settings->{'run'})) {          if (exists($settings->{'run'})) {
             if ($settings->{'run'} eq '0') {              if ($settings->{'run'} eq '0') {
Line 2624  sub print_autoenroll { Line 3768  sub print_autoenroll {
         if (exists($settings->{'sender_domain'})) {          if (exists($settings->{'sender_domain'})) {
             $defdom = $settings->{'sender_domain'};              $defdom = $settings->{'sender_domain'};
         }          }
         if (exists($settings->{'autofailsafe'})) {          if (exists($settings->{'failsafe'})) {
             $failsafe = $settings->{'autofailsafe'};              $failsafe = $settings->{'failsafe'};
               if ($failsafe eq 'zero') {
                   $failsafechecked{'zero'} = ' checked="checked"';
                   $failsafechecked{'off'} = '';
                   $failsafesty = 'inline-block';
               } elsif ($failsafe eq 'any') {
                   $failsafechecked{'any'} = ' checked="checked"';
                   $failsafechecked{'off'} = '';
               }
               $autofailsafe = $settings->{'autofailsafe'};
           } elsif (exists($settings->{'autofailsafe'})) {
               $autofailsafe = $settings->{'autofailsafe'};
               if ($autofailsafe ne '') {
                   $failsafechecked{'zero'} = ' checked="checked"';
                   $failsafe = 'zero';
                   $failsafechecked{'off'} = '';
               }
         }          }
     } else {      } else {
         if ($autorun) {          if ($autorun) {
Line 2664  sub print_autoenroll { Line 3824  sub print_autoenroll {
                   $coownersoff.' value="0" />'.&mt('No').'</label></span></td>'.                    $coownersoff.' value="0" />'.&mt('No').'</label></span></td>'.
                   '</tr><tr>'.                    '</tr><tr>'.
                   '<td>'.&mt('Failsafe for no drops when institutional data missing').'</td>'.                    '<td>'.&mt('Failsafe for no drops when institutional data missing').'</td>'.
                   '<td class="LC_right_item"><span class="LC_nobreak">'.                    '<td class="LC_left_item"><span class="LC_nobreak">'.
                   '<input type="text" name="autoenroll_failsafe"'.                    '<span class="LC_nobreak"><label><input type="radio" name="autoenroll_failsafe" value="off" onclick="toggleFailsafe(this.form)"'.$failsafechecked{'off'}.' />'.&mt('Not in use').'</label></span>&nbsp;&nbsp;&nbsp; '.
                   ' value="'.$failsafe.'" size="4" /></span></td></tr>';                    '<span class="LC_nobreak"><label><input type="radio" name="autoenroll_failsafe" value="zero" onclick="toggleFailsafe(this.form)"'.$failsafechecked{'zero'}.' />'.&mt('Retrieved section enrollment is zero').'</label></span><br />'.
                     '<span class="LC_nobreak"><label><input type="radio" name="autoenroll_failsafe" value="any" onclick="toggleFailsafe(this.form)"'.$failsafechecked{'any'}.' />'.&mt('Retrieved section enrollment is zero or greater').'</label></span>'.
                     '<div class="LC_floatleft" style="display:'.$failsafesty.';" id="autoenroll_failsafe_div">'.
                     '<span class="LC_nobreak">'.
                     &mt('Threshold for number of students in section to drop: [_1]',
                         '<input type="text" name="autoenroll_autofailsafe" value="'.$autofailsafe.'" size="4" />').
                     '</span></div></td></tr>';
     $$rowtotal += 4;      $$rowtotal += 4;
     return $datatable;      return $datatable;
 }  }
   
 sub print_autoupdate {  sub print_autoupdate {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my $datatable;      my ($enable,$datatable);
     if ($position eq 'top') {      if ($position eq 'top') {
           my %choices = &Apache::lonlocal::texthash (
                             run        => 'Auto-update active?',
                             classlists => 'Update information in classlists?',
                             unexpired  => 'Skip updates for users without active or future roles?',
                             lastactive => 'Skip updates for inactive users?',
           );
           my $itemcount = 0;
         my $updateon = ' ';          my $updateon = ' ';
         my $updateoff = ' checked="checked" ';          my $updateoff = ' checked="checked" ';
         my $classlistson = ' ';  
         my $classlistsoff = ' checked="checked" ';  
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ($settings->{'run'} eq '1') {              if ($settings->{'run'} eq '1') {
                 $updateon = $updateoff;                  $updateon = $updateoff;
                 $updateoff = ' ';                  $updateoff = ' ';
             }              }
             if ($settings->{'classlists'} eq '1') {  
                 $classlistson = $classlistsoff;  
                 $classlistsoff = ' ';  
             }  
         }          }
         my %title = (          $enable = '<tr class="LC_odd_row">'.
                    run => 'Auto-update active?',                    '<td>'.$choices{'run'}.'</td>'.
                    classlists => 'Update information in classlists?',                    '<td class="LC_left_item"><span class="LC_nobreak"><label>'.
                     );  
         $datatable = '<tr class="LC_odd_row">'.   
                   '<td>'.&mt($title{'run'}).'</td>'.  
                   '<td class="LC_right_item"><span class="LC_nobreak"><label>'.  
                   '<input type="radio" name="autoupdate_run"'.                    '<input type="radio" name="autoupdate_run"'.
                   $updateon.' value="1" />'.&mt('Yes').'</label>&nbsp;'.                    $updateoff.'value="0" />'.&mt('No').'</label>&nbsp;'.
                   '<label><input type="radio" name="autoupdate_run"'.                    '<label><input type="radio" name="autoupdate_run"'.
                   $updateoff.'value="0" />'.&mt('No').'</label></span></td>'.                    $updateon.'value="1" />'.&mt('Yes').'</label></span></td>'.
                   '</tr><tr>'.  
                   '<td>'.&mt($title{'classlists'}).'</td>'.  
                   '<td class="LC_right_item"><span class="LC_nobreak">'.  
                   '<label><input type="radio" name="classlists"'.  
                   $classlistson.' value="1" />'.&mt('Yes').'</label>&nbsp;'.  
                   '<label><input type="radio" name="classlists"'.  
                   $classlistsoff.'value="0" />'.&mt('No').'</label></span></td>'.  
                   '</tr>';                    '</tr>';
         $$rowtotal += 2;          my @toggles = ('classlists','unexpired');
           my %defaultchecked = ('classlists' => 'off',
                                 'unexpired'  => 'off'
                                 );
           $$rowtotal ++;
           ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,
                                                        \%choices,$itemcount,'','','left','no');
           $datatable = $enable.$datatable;
           $$rowtotal += $itemcount;
           my $lastactiveon = ' ';
           my $lastactiveoff = ' checked="checked" ';
           my $lastactivestyle = 'none';
           my $lastactivedays;
           my $onclick = ' onclick="javascript:toggleLastActiveDays(this.form);"';
           if (ref($settings) eq 'HASH') {
               if ($settings->{'lastactive'} =~ /^\d+$/) {
                   $lastactiveon = $lastactiveoff;
                   $lastactiveoff = ' ';
                   $lastactivestyle = 'inline-block';
                   $lastactivedays = $settings->{'lastactive'};
               }
           }
           my $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'>'.
                         '<td>'.$choices{'lastactive'}.'</td>'.
                         '<td class="LC_left_item"><span class="LC_nobreak"><label>'.
                         '<input type="radio" name="lastactive"'.
                         $lastactiveoff.'value="0"'.$onclick.' />'.&mt('No').'</label>'.
                         '&nbsp;<label>'.
                         '<input type="radio" name="lastactive"'.
                         $lastactiveon.' value="1"'.$onclick.' />'.&mt('Yes').'</label>'.
                         '<div id="lastactive_div" style="display:'.$lastactivestyle.';">'.
                         ':&nbsp;'.&mt('inactive = no activity in last [_1] days',
                             '<input type="text" size="5" name="lastactivedays" value="'.
                             $lastactivedays.'" />').
                         '</span></td>'.
                         '</tr>';
           $$rowtotal ++;
     } elsif ($position eq 'middle') {      } elsif ($position eq 'middle') {
         my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);          my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
         my $numinrow = 3;          my $numinrow = 3;
Line 2948  sub print_contacts { Line 4141  sub print_contacts {
     my $datatable;      my $datatable;
     my @contacts = ('adminemail','supportemail');      my @contacts = ('adminemail','supportemail');
     my (%checked,%to,%otheremails,%bccemails,%includestr,%includeloc,%currfield,      my (%checked,%to,%otheremails,%bccemails,%includestr,%includeloc,%currfield,
         $maxsize,$fields,$fieldtitles,$fieldoptions,$possoptions,@mailings);          $maxsize,$fields,$fieldtitles,$fieldoptions,$possoptions,@mailings,%lonstatus);
     if ($position eq 'top') {      if ($position eq 'top') {
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             foreach my $item (@contacts) {              foreach my $item (@contacts) {
Line 2963  sub print_contacts { Line 4156  sub print_contacts {
         foreach my $type (@mailings) {          foreach my $type (@mailings) {
             $otheremails{$type} = '';              $otheremails{$type} = '';
         }          }
       } elsif ($position eq 'lower') {
           if (ref($settings) eq 'HASH') {
               if (ref($settings->{'lonstatus'}) eq 'HASH') {
                   %lonstatus = %{$settings->{'lonstatus'}};
               }
           }
     } else {      } else {
         @mailings = ('helpdeskmail','otherdomsmail');          @mailings = ('helpdeskmail','otherdomsmail');
         foreach my $type (@mailings) {          foreach my $type (@mailings) {
Line 2975  sub print_contacts { Line 4174  sub print_contacts {
         ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();          ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();
     }      }
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         unless ($position eq 'top') {          unless (($position eq 'top') || ($position eq 'lower')) {
             foreach my $type (@mailings) {              foreach my $type (@mailings) {
                 if (exists($settings->{$type})) {                  if (exists($settings->{$type})) {
                     if (ref($settings->{$type}) eq 'HASH') {                      if (ref($settings->{$type}) eq 'HASH') {
Line 3108  sub print_contacts { Line 4307  sub print_contacts {
         $datatable .= '</td></tr>'."\n";          $datatable .= '</td></tr>'."\n";
         $rownum ++;          $rownum ++;
     }      }
     unless ($position eq 'top') {      unless (($position eq 'top') || ($position eq 'lower')) {
         foreach my $type (@mailings) {          foreach my $type (@mailings) {
             $css_class = $rownum%2?' class="LC_odd_row"':'';              $css_class = $rownum%2?' class="LC_odd_row"':'';
             $datatable .= '<tr'.$css_class.'>'.              $datatable .= '<tr'.$css_class.'>'.
Line 3155  sub print_contacts { Line 4354  sub print_contacts {
     }      }
     if ($position eq 'middle') {      if ($position eq 'middle') {
         my %choices;          my %choices;
         $choices{'reporterrors'} = &mt('E-mail error reports to [_1]',          my $corelink = &core_link_msu();
                                        &Apache::loncommon::modal_link('http://loncapa.org/core.html',          $choices{'reporterrors'} = &mt('E-mail error reports to [_1]',$corelink);
                                        &mt('LON-CAPA core group - MSU'),600,500));  
         $choices{'reportupdates'} = &mt('E-mail record of completed LON-CAPA updates to [_1]',          $choices{'reportupdates'} = &mt('E-mail record of completed LON-CAPA updates to [_1]',
                                         &Apache::loncommon::modal_link('http://loncapa.org/core.html',                                          $corelink);
                                         &mt('LON-CAPA core group - MSU'),600,500));          $choices{'reportstatus'} = &mt('E-mail status if errors above threshold to [_1]',$corelink);
         my @toggles = ('reporterrors','reportupdates');          my @toggles = ('reporterrors','reportupdates','reportstatus');
         my %defaultchecked = ('reporterrors'  => 'on',          my %defaultchecked = ('reporterrors'  => 'on',
                               'reportupdates' => 'on');                                'reportupdates' => 'on',
                                 'reportstatus'  => 'on');
         (my $reports,$rownum) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,          (my $reports,$rownum) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,
                                                    \%choices,$rownum);                                                     \%choices,$rownum);
         $datatable .= $reports;          $datatable .= $reports;
       } elsif ($position eq 'lower') {
           my (%current,%excluded,%weights);
           my ($defaults,$names) = &Apache::loncommon::lon_status_items();
           if ($lonstatus{'threshold'} =~ /^\d+$/) {
               $current{'errorthreshold'} = $lonstatus{'threshold'};
           } else {
               $current{'errorthreshold'} = $defaults->{'threshold'};
           }
           if ($lonstatus{'sysmail'} =~ /^\d+$/) {
               $current{'errorsysmail'} = $lonstatus{'sysmail'};
           } else {
               $current{'errorsysmail'} = $defaults->{'sysmail'};
           }
           if (ref($lonstatus{'weights'}) eq 'HASH') {
               foreach my $type ('E','W','N','U') {
                   if ($lonstatus{'weights'}{$type} =~ /^\d+$/) {
                       $weights{$type} = $lonstatus{'weights'}{$type};
                   } else {
                       $weights{$type} = $defaults->{$type};
                   }
               }
           } else {
               foreach my $type ('E','W','N','U') {
                   $weights{$type} = $defaults->{$type};
               }
           }
           if (ref($lonstatus{'excluded'}) eq 'ARRAY') {
               if (@{$lonstatus{'excluded'}} > 0) {
                   map {$excluded{$_} = 1; } @{$lonstatus{'excluded'}};
               }
           }
           foreach my $item ('errorthreshold','errorsysmail') {
               $css_class = $rownum%2?' class="LC_odd_row"':'';
               $datatable .= '<tr'.$css_class.'>'.
                             '<td class="LC_left_item"><span class="LC_nobreak">'.
                             $titles->{$item}.
                             '</span></td><td class="LC_left_item">'.
                             '<input type="text" name="'.$item.'" value="'.
                             $current{$item}.'" size="5" /></td></tr>';
               $rownum ++;
           }
           $css_class = $rownum%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'>'.
                         '<td class="LC_left_item">'.
                         '<span class="LC_nobreak">'.$titles->{'errorweights'}.
                         '</span></td><td class="LC_left_item"><table><tr>';
           foreach my $type ('E','W','N','U') {
               $datatable .= '<td>'.$names->{$type}.'<br />'.
                             '<input type="text" name="errorweights_'.$type.'" value="'.
                             $weights{$type}.'" size="5" /></td>';
           }
           $datatable .= '</tr></table></tr>';
           $rownum ++;
           $css_class = $rownum%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td class="LC_left_item">'.
                         $titles->{'errorexcluded'}.'</td>'.
                         '<td class="LC_left_item"><table>';
           my $numinrow = 4;
           my @ids = sort(values(%Apache::lonnet::serverhomeIDs));
           for (my $i=0; $i<@ids; $i++) {
               my $rem = $i%($numinrow);
               if ($rem == 0) {
                   if ($i > 0) {
                       $datatable .= '</tr>';
                   }
                   $datatable .= '<tr>';
               }
               my $check;
               if ($excluded{$ids[$i]}) {
                   $check = ' checked="checked" ';
               }
               $datatable .= '<td class="LC_left_item">'.
                             '<span class="LC_nobreak"><label>'.
                             '<input type="checkbox" name="errorexcluded" '.
                             'value="'.$ids[$i].'"'.$check.' />'.
                             $ids[$i].'</label></span></td>';
           }
           my $colsleft = $numinrow - @ids%($numinrow);
           if ($colsleft > 1 ) {
               $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                             '&nbsp;</td>';
           } elsif ($colsleft == 1) {
               $datatable .= '<td class="LC_left_item">&nbsp;</td>';
           }
           $datatable .= '</tr></table></td></tr>';
           $rownum ++;
     } elsif ($position eq 'bottom') {      } elsif ($position eq 'bottom') {
         my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);          my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
         my (@posstypes,%usertypeshash);          my (@posstypes,%usertypeshash);
Line 3206  sub print_contacts { Line 4491  sub print_contacts {
             my $optionsprefix = 'LC_options_helpdesk_';              my $optionsprefix = 'LC_options_helpdesk_';
   
             my $onclicktypes = "toggleHelpdeskRow(this.form,'overrides','$customclass','$optionsprefix');";              my $onclicktypes = "toggleHelpdeskRow(this.form,'overrides','$customclass','$optionsprefix');";
   
             $datatable .= &insttypes_row($settings,$types,$usertypes,$dom,              $datatable .= &insttypes_row($settings,$types,$usertypes,$dom,
                                          $numinrow,$othertitle,'overrides',                                           $numinrow,$othertitle,'overrides',
                                          \$rownum,$onclicktypes,$customclass);                                           \$rownum,$onclicktypes,$customclass);
Line 3240  sub print_contacts { Line 4524  sub print_contacts {
     return $datatable;      return $datatable;
 }  }
   
   sub core_link_msu {
       return &Apache::loncommon::modal_link('http://loncapa.org/core.html',
                                             &mt('LON-CAPA core group - MSU'),600,500);
   }
   
 sub overridden_helpdesk {  sub overridden_helpdesk {
     my ($checked,$otheremails,$bccemails,$includeloc,$includestr,$type,$rowid,      my ($checked,$otheremails,$bccemails,$includeloc,$includestr,$type,$rowid,
         $typetitle,$css_class,$rowstyle,$contacts,$short_titles) = @_;          $typetitle,$css_class,$rowstyle,$contacts,$short_titles) = @_;
Line 3418  sub print_helpsettings { Line 4707  sub print_helpsettings {
             push(@jsarray,('notinc','notexc'));              push(@jsarray,('notinc','notexc'));
         }          }
         my $hiddenstr = join("','",@jsarray);          my $hiddenstr = join("','",@jsarray);
         $datatable .= &helpsettings_javascript(\@roles_by_num,$maxnum,$hiddenstr,$formname);  
         my $context = 'domprefs';          my $context = 'domprefs';
         my $crstype = 'Course';          my $crstype = 'Course';
         my $prefix = 'helproles_';          my $prefix = 'helproles_';
Line 3740  sub helpdeskroles_access { Line 5028  sub helpdeskroles_access {
   
 sub radiobutton_prefs {  sub radiobutton_prefs {
     my ($settings,$toggles,$defaultchecked,$choices,$itemcount,$onclick,      my ($settings,$toggles,$defaultchecked,$choices,$itemcount,$onclick,
         $additional,$align) = @_;          $additional,$align,$firstval) = @_;
     return unless ((ref($toggles) eq 'ARRAY') && (ref($defaultchecked) eq 'HASH') &&      return unless ((ref($toggles) eq 'ARRAY') && (ref($defaultchecked) eq 'HASH') &&
                    (ref($choices) eq 'HASH'));                     (ref($choices) eq 'HASH'));
   
Line 3780  sub radiobutton_prefs { Line 5068  sub radiobutton_prefs {
         } else {          } else {
             $datatable .= '<td class="LC_right_item">';              $datatable .= '<td class="LC_right_item">';
         }          }
         $datatable .=          $datatable .= '<span class="LC_nobreak">';
             '<span class="LC_nobreak">'.          if ($firstval eq 'no') {
             '<label><input type="radio" name="'.              $datatable .=
             $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').                  '<label><input type="radio" name="'.
             '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.                  $item.'" '.$checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').
             $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>'.                  '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.
             '</span>'.$additional.                  $checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').'</label>';
             '</td>'.          } else {
             '</tr>';              $datatable .=
                   '<label><input type="radio" name="'.
                   $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').
                   '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.
                   $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>';
           }
           $datatable .= '</span>'.$additional.'</td></tr>';
         $itemcount ++;          $itemcount ++;
     }      }
     return ($datatable,$itemcount);      return ($datatable,$itemcount);
 }  }
   
 sub print_ltitools {  sub print_ltitools {
     my ($dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my $rownum = 0;      my (%rules,%encrypt,%privkeys,%linkprot);
     my $css_class;  
     my $itemcount = 1;  
     my $maxnum = 0;  
     my %ordered;  
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         foreach my $item (keys(%{$settings})) {          if ($position eq 'top') {
             if (ref($settings->{$item}) eq 'HASH') {              if (exists($settings->{'encrypt'})) {
                 my $num = $settings->{$item}{'order'};                  if (ref($settings->{'encrypt'}) eq 'HASH') {
                 $ordered{$num} = $item;                      foreach my $key (keys(%{$settings->{'encrypt'}})) {
             }                          $encrypt{'toolsec_'.$key} = $settings->{'encrypt'}{$key};
         }                      }
     }  
     my $confname = $dom.'-domainconfig';  
     my $switchserver = &check_switchserver($dom,$confname);  
     my $maxnum = scalar(keys(%ordered));  
     my $datatable = &ltitools_javascript($settings);  
     my %lt = &ltitools_names();  
     my @courseroles = ('cc','in','ta','ep','st');  
     my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner);  
     my @fields = ('fullname','firstname','lastname','email','user','roles');  
     if (keys(%ordered)) {  
         my @items = sort { $a <=> $b } keys(%ordered);  
         for (my $i=0; $i<@items; $i++) {  
             $css_class = $itemcount%2?' class="LC_odd_row"':'';  
             my $item = $ordered{$items[$i]};  
             my ($title,$key,$secret,$url,$imgsrc,$version);  
             if (ref($settings->{$item}) eq 'HASH') {  
                 $title = $settings->{$item}->{'title'};  
                 $url = $settings->{$item}->{'url'};  
                 $key = $settings->{$item}->{'key'};  
                 $secret = $settings->{$item}->{'secret'};  
                 my $image = $settings->{$item}->{'image'};  
                 if ($image ne '') {  
                     $imgsrc = '<img src="'.$image.'" alt="'.&mt('Tool Provider icon').'" />';  
                 }  
             }  
             my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'ltitools_".$item."'".');"';  
             $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'  
                          .'<select name="ltitools_'.$item.'"'.$chgstr.'>';  
             for (my $k=0; $k<=$maxnum; $k++) {  
                 my $vpos = $k+1;  
                 my $selstr;  
                 if ($k == $i) {  
                     $selstr = ' selected="selected" ';  
                 }  
                 $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';  
             }  
             $datatable .= '</select>'.('&nbsp;'x2).  
                 '<label><input type="checkbox" name="ltitools_del" value="'.$item.'" />'.  
                 &mt('Delete?').'</label></span></td>'.  
                 '<td colspan="2">'.  
                 '<fieldset><legend>'.&mt('Required settings').'</legend>'.  
                 '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="30" name="ltitools_title_'.$i.'" value="'.$title.'" /></span> '.  
                 ('&nbsp;'x2).  
                 '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_version_'.$i.'">'.  
                 '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '.  
                 ('&nbsp;'x2).  
                 '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_msgtype_'.$i.'">'.  
                 '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '.  
                 '<br /><br />'.  
                 '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="30" name="ltitools_url_'.$i.'"'.  
                 ' value="'.$url.'" /></span>'.  
                 ('&nbsp;'x2).  
                 '<span class="LC_nobreak">'.$lt{'key'}.  
                 '<input type="text" size="25" name="ltitools_key_'.$i.'" value="'.$key.'" /></span> '.  
                 ('&nbsp;'x2).  
                 '<span class="LC_nobreak">'.$lt{'secret'}.':'.  
                 '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="'.$secret.'" />'.  
                 '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_secret_'.$i.'.type='."'text'".' } else { this.form.ltitools_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'.  
                 '<input type="hidden" name="ltitools_id_'.$i.'" value="'.$item.'" /></span>'.  
                 '</fieldset>'.  
                 '<fieldset><legend>'.&mt('Optional settings').'</legend>'.  
                 '<span class="LC_nobreak">'.&mt('Display target:');  
             my %currdisp;  
             if (ref($settings->{$item}->{'display'}) eq 'HASH') {  
                 if ($settings->{$item}->{'display'}->{'target'} eq 'window') {  
                     $currdisp{'window'} = ' checked="checked"';  
                 } elsif ($settings->{$item}->{'display'}->{'target'} eq 'tab') {  
                     $currdisp{'tab'} = ' checked="checked"';  
                 } else {  
                     $currdisp{'iframe'} = ' checked="checked"';  
                 }  
                 if ($settings->{$item}->{'display'}->{'width'} =~ /^(\d+)$/) {  
                     $currdisp{'width'} = $1;  
                 }  
                 if ($settings->{$item}->{'display'}->{'height'} =~ /^(\d+)$/) {  
                      $currdisp{'height'} = $1;  
                 }  
                 $currdisp{'linktext'} = $settings->{$item}->{'display'}->{'linktext'};  
                 $currdisp{'explanation'} = $settings->{$item}->{'display'}->{'explanation'};  
             } else {  
                 $currdisp{'iframe'} = ' checked="checked"';  
             }  
             foreach my $disp ('iframe','tab','window') {  
                 $datatable .= '<label><input type="radio" name="ltitools_target_'.$i.'" value="'.$disp.'"'.$currdisp{$disp}.' />'.  
                               $lt{$disp}.'</label>'.('&nbsp;'x2);  
             }  
             $datatable .= ('&nbsp;'x4);  
             foreach my $dimen ('width','height') {  
                 $datatable .= '<label>'.$lt{$dimen}.'&nbsp;'.  
                               '<input type="text" name="ltitools_'.$dimen.'_'.$i.'" size="5" value="'.$currdisp{$dimen}.'" /></label>'.  
                               ('&nbsp;'x2);  
             }  
             $datatable .= '<br />'.  
                           '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.  
                           '<input type="text" name="ltitools_linktext_'.$i.'" size="25" value="'.$currdisp{'linktext'}.'" /></label></div>'.  
                           '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.  
                           '<textarea name="ltitools_explanation_'.$i.'" rows="5" cols="40">'.$currdisp{'explanation'}.  
                           '</textarea></div><div style=""></div><br />';  
             $datatable .= '<br />';  
             foreach my $extra ('passback','roster') {  
                 my $checkedon = '';  
                 my $checkedoff = ' checked="checked"';  
                 if ($settings->{$item}->{$extra}) {  
                     $checkedon = $checkedoff;  
                     $checkedoff = '';  
                 }  
                 $datatable .= $lt{$extra}.'&nbsp;'.  
                               '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="1"'.$checkedon.' />'.  
                               &mt('Yes').'</label>'.('&nbsp;'x2).  
                               '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="0"'.$checkedoff.' />'.  
                               &mt('No').'</label>'.('&nbsp;'x4);  
             }  
             $datatable .= '<br /><br /><span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;';  
             if ($imgsrc) {  
                 $datatable .= $imgsrc.  
                               '<label><input type="checkbox" name="ltitools_image_del"'.  
                               ' value="'.$item.'" />'.&mt('Delete?').'</label></span> '.  
                               '<span class="LC_nobreak">&nbsp;'.&mt('Replace:').'&nbsp;';  
             } else {  
                 $datatable .= '('.&mt('if larger than 21x21 pixels, image will be scaled').')&nbsp;';  
             }  
             if ($switchserver) {  
                 $datatable .= &mt('Upload to library server: [_1]',$switchserver);  
             } else {  
                 $datatable .= '<input type="file" name="ltitools_image_'.$i.'" value="" />';  
             }  
             $datatable .= '</span></fieldset>';  
             my (%checkedfields,%rolemaps);  
             if (ref($settings->{$item}) eq 'HASH') {  
                 if (ref($settings->{$item}->{'fields'}) eq 'HASH') {  
                     %checkedfields = %{$settings->{$item}->{'fields'}};  
                 }  
                 if (ref($settings->{$item}->{'roles'}) eq 'HASH') {  
                     %rolemaps = %{$settings->{$item}->{'roles'}};  
                     $checkedfields{'roles'} = 1;  
                 }                  }
             }              }
             $datatable .= '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'.              if (exists($settings->{'private'})) {
                           '<span class="LC_nobreak">';                  if (ref($settings->{'private'}) eq 'HASH') {
             foreach my $field (@fields) {                      if (ref($settings->{'private'}) eq 'HASH') {
                 my $checked;                          if (ref($settings->{'private'}{'keys'}) eq 'ARRAY') {
                 if ($checkedfields{$field}) {                              map { $privkeys{$_} = 1; } (@{$settings->{'private'}{'keys'}});
                     $checked = ' checked="checked"';  
                 }  
                 $datatable .= '<label>'.  
                               '<input type="checkbox" name="ltitools_fields_'.$i.'" value="'.$field.'"'.$checked.' />'.  
                               $lt{$field}.'</label>'.('&nbsp;' x2);  
             }  
             $datatable .= '</span></fieldset>'.  
                           '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';  
             foreach my $role (@courseroles) {  
                 my ($selected,$selectnone);  
                 if (!$rolemaps{$role}) {  
                     $selectnone = ' selected="selected"';  
                 }  
                 $datatable .= '<td align="center">'.  
                               &Apache::lonnet::plaintext($role,'Course').'<br />'.  
                               '<select name="ltitools_roles_'.$role.'_'.$i.'">'.  
                               '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';  
                 foreach my $ltirole (@ltiroles) {  
                     unless ($selectnone) {  
                         if ($rolemaps{$role} eq $ltirole) {  
                             $selected = ' selected="selected"';  
                         } else {  
                             $selected = '';  
                         }                          }
                     }                      }
                     $datatable .= '<option value="'.$ltirole.'"'.$selected.'>'.$ltirole.'</option>';  
                 }  
                 $datatable .= '</select></td>';  
             }  
             $datatable .= '</tr></table></fieldset>';  
             my %courseconfig;  
             if (ref($settings->{$item}) eq 'HASH') {  
                 if (ref($settings->{$item}->{'crsconf'}) eq 'HASH') {  
                     %courseconfig = %{$settings->{$item}->{'crsconf'}};  
                 }                  }
             }              }
             $datatable .= '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">';          } elsif ($position eq 'middle') {
             foreach my $item ('label','title','target','linktext','explanation') {              if (exists($settings->{'rules'})) {
                 my $checked;                  if (ref($settings->{'rules'}) eq 'HASH') {
                 if ($courseconfig{$item}) {                      %rules = %{$settings->{'rules'}};
                     $checked = ' checked="checked"';  
                 }                  }
                 $datatable .= '<label>'.  
                        '<input type="checkbox" name="ltitools_courseconfig_'.$i.'" value="'.$item.'"'.$checked.' />'.  
                        $lt{'crs'.$item}.'</label>'.('&nbsp;' x2)."\n";  
             }              }
             $datatable .= '</span></fieldset>'.          } else {
                           '<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'.              foreach my $key ('encrypt','private','rules') {
                           '<table><tr><th>'.&mt('Action').'</th><th>'.&mt('Name').'</th><th>'.&mt('Value').'</th></tr>';                  if (exists($settings->{$key})) {
             if (ref($settings->{$item}->{'custom'}) eq 'HASH') {                      delete($settings->{$key});
                 my %custom = %{$settings->{$item}->{'custom'}};  
                 if (keys(%custom) > 0) {  
                     foreach my $key (sort(keys(%custom))) {  
                         $datatable .= '<tr><td><span class="LC_nobreak">'.  
                                       '<label><input type="checkbox" name="ltitools_customdel_'.$i.'" value="'.  
                                       $key.'" />'.&mt('Delete').'</label></span></td><td>'.$key.'</td>'.  
                                       '<td><input type="text" name="ltitools_customval_'.$key.'_'.$i.'"'.  
                                       ' value="'.$custom{$key}.'" /></td></tr>';  
                     }  
                 }                  }
             }              }
             $datatable .= '<tr><td><span class="LC_nobreak">'.  
                           '<label><input type="checkbox" name="ltitools_customadd" value="'.$i.'" />'.  
                           &mt('Add').'</label></span></td><td><input type="text" name="ltitools_custom_name_'.$i.'" />'.  
                           '</td><td><input type="text" name="ltitools_custom_value_'.$i.'" /></td></tr>';  
             $datatable .= '</table></fieldset></td></tr>'."\n";  
             $itemcount ++;  
         }          }
     }      }
     $css_class = $itemcount%2?' class="LC_odd_row"':'';      my $datatable;
     my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'ltitools_add_pos'".');"';      my $itemcount = 1;
     $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".      if ($position eq 'top') {
                   '<input type="hidden" name="ltitools_maxnum" value="'.$maxnum.'" />'."\n".          $datatable = &secrets_form($dom,'toolsec',\%encrypt,\%privkeys,$rowtotal);
                   '<select name="ltitools_add_pos"'.$chgstr.'>';      } elsif ($position eq 'middle') {
     for (my $k=0; $k<$maxnum+1; $k++) {          $datatable = &password_rules('toolsecrets',\$itemcount,\%rules);
         my $vpos = $k+1;          $$rowtotal += $itemcount;
         my $selstr;      } else {
         if ($k == $maxnum) {          $datatable = &Apache::courseprefs::print_ltitools($dom,'',$settings,\$rowtotal,'','','domain');
             $selstr = ' selected="selected" ';  
         }  
         $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';  
     }      }
     $datatable .= '</select>&nbsp;'."\n".  
                   '<input type="checkbox" name="ltitools_add" value="1" />'.&mt('Add').'</td>'."\n".  
                   '<td colspan="2">'.  
                   '<fieldset><legend>'.&mt('Required settings').'</legend>'.  
                   '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="30" name="ltitools_add_title" value="" /></span> '."\n".  
                   ('&nbsp;'x2).  
                   '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_add_version">'.  
                   '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n".  
                   ('&nbsp;'x2).  
                   '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_add_msgtype">'.  
                   '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '.  
                   '<br />'.  
                   '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="30" name="ltitools_add_url" value="" /></span> '."\n".  
                   ('&nbsp;'x2).  
                   '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="ltitools_add_key" value="" /></span> '."\n".  
                   ('&nbsp;'x2).  
                   '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="ltitools_add_secret" value="" />'.  
                   '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_add_secret.type='."'text'".' } else { this.form.ltitools_add_secret.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n".  
                   '</fieldset>'.  
                   '<fieldset><legend>'.&mt('Optional settings').'</legend>'.  
                   '<span class="LC_nobreak">'.&mt('Display target:');  
     my %defaultdisp;  
     $defaultdisp{'iframe'} = ' checked="checked"';  
     foreach my $disp ('iframe','tab','window') {  
         $datatable .= '<label><input type="radio" name="ltitools_add_target" value="'.$disp.'"'.$defaultdisp{$disp}.' />'.  
                       $lt{$disp}.'</label>'.('&nbsp;'x2);  
     }  
     $datatable .= ('&nbsp;'x4);  
     foreach my $dimen ('width','height') {  
         $datatable .= '<label>'.$lt{$dimen}.'&nbsp;'.  
                       '<input type="text" name="ltitools_add_'.$dimen.'" size="5" /></label>'.  
                       ('&nbsp;'x2);  
     }  
     $datatable .= '<br />'.  
                   '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.  
                   '<input type="text" name="ltitools_add_linktext" size="5" /></label></div>'.  
                   '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.  
                   '<textarea name=ltitools_add_explanation" rows="5" cols="40"></textarea>'.  
                   '</div><div style=""></div><br />';  
     foreach my $extra ('passback','roster') {  
         $datatable .= $lt{$extra}.'&nbsp;'.  
                       '<label><input type="radio" name="ltitools_add_'.$extra.'" value="1" />'.  
                       &mt('Yes').'</label>'.('&nbsp;'x2).  
                       '<label><input type="radio" name="ltitools_add_'.$extra.'" value="0" checked="checked" />'.  
                       &mt('No').'</label>'.('&nbsp;'x4);  
     }  
     $datatable .= '<br /><br /><span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;'.  
                   '('.&mt('if larger than 21x21 pixels, image will be scaled').')&nbsp;';  
     if ($switchserver) {  
         $datatable .= &mt('Upload to library server: [_1]',$switchserver);  
     } else {  
         $datatable .= '<input type="file" name="ltitools_add_image" value="" />';  
     }  
     $datatable .= '</span></fieldset>'.  
                   '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'.  
                   '<span class="LC_nobreak">';  
     foreach my $field (@fields) {  
         $datatable .= '<label>'.  
                       '<input type="checkbox" name="ltitools_add_fields" value="'.$field.'" />'.  
                       $lt{$field}.'</label>'.('&nbsp;' x2);  
     }  
     $datatable .= '</span></fieldset>'.  
                   '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';  
     foreach my $role (@courseroles) {  
         my ($checked,$checkednone);  
         $datatable .= '<td align="center">'.  
                       &Apache::lonnet::plaintext($role,'Course').'<br />'.  
                       '<select name="ltitools_add_roles_'.$role.'">'.  
                       '<option value="" selected="selected">'.&mt('Select').'</option>';  
         foreach my $ltirole (@ltiroles) {  
             $datatable .= '<option value="'.$ltirole.'">'.$ltirole.'</option>';  
         }  
         $datatable .= '</select></td>';  
     }  
     $datatable .= '</tr></table></fieldset>'.  
                   '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">';  
     foreach my $item ('label','title','target','linktext','explanation') {  
         $datatable .= '<label>'.  
                       '<input type="checkbox" name="ltitools_courseconfig" value="'.$item.'" checked="checked" />'.  
                       $lt{'crs'.$item}.'</label>'.('&nbsp;' x2)."\n";  
     }  
     $datatable .= '</span></fieldset>'.  
                   '<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'.  
                   '<table><tr><th>'.&mt('Action').'</th><th>'.&mt('Name').'</th><th>'.&mt('Value').'</th></tr>'.  
                   '<tr><td><span class="LC_nobreak">'.  
                   '<label><input type="checkbox" name="ltitools_add_custom" value="1" />'.  
                   &mt('Add').'</label></span></td><td><input type="text" name="ltitools_add_custom_name" />'.  
                   '</td><td><input type="text" name="ltitools_add_custom_value" /></td></tr>'.  
                   '</table></fieldset></td></tr>'."\n".  
                   '</td>'."\n".  
                   '</tr>'."\n";  
     $itemcount ++;  
     return $datatable;      return $datatable;
 }  }
   
Line 4132  sub ltitools_names { Line 5141  sub ltitools_names {
                                           'title'          => 'Title',                                            'title'          => 'Title',
                                           'version'        => 'Version',                                            'version'        => 'Version',
                                           'msgtype'        => 'Message Type',                                            'msgtype'        => 'Message Type',
                                             'sigmethod'      => 'Signature Method',
                                           'url'            => 'URL',                                            'url'            => 'URL',
                                           'key'            => 'Key',                                            'key'            => 'Key',
                                             'lifetime'       => 'Nonce lifetime (s)',
                                           'secret'         => 'Secret',                                            'secret'         => 'Secret',
                                           'icon'           => 'Icon',                                            'icon'           => 'Icon',
                                           'user'           => 'Username:domain',                                            'user'           => 'User',
                                           'fullname'       => 'Full Name',                                            'fullname'       => 'Full Name',
                                           'firstname'      => 'First Name',                                            'firstname'      => 'First Name',
                                           'lastname'       => 'Last Name',                                            'lastname'       => 'Last Name',
Line 4156  sub ltitools_names { Line 5167  sub ltitools_names {
                                           'crstitle'       => 'Course title',                                            'crstitle'       => 'Course title',
                                           'crslinktext'    => 'Link Text',                                            'crslinktext'    => 'Link Text',
                                           'crsexplanation' => 'Explanation',                                            'crsexplanation' => 'Explanation',
                                             'crsappend'      => 'Provider URL',
                                         );                                          );
       return %lt;
   }
   
   sub secrets_form {
       my ($dom,$context,$encrypt,$privkeys,$rowtotal) = @_;
       my @ids=&Apache::lonnet::current_machine_ids();
       my %servers = &Apache::lonnet::get_servers($dom,'library');
       my $primary = &Apache::lonnet::domain($dom,'primary');
       my ($css_class,$extra,$numshown,$itemcount,$output);
       $itemcount = 0;
       foreach my $hostid (sort(keys(%servers))) {
           my ($showextra,$divsty,$switch);
           if ($hostid eq $primary) {
               if ($context eq 'ltisec') {
                   if (($encrypt->{'ltisec_consumers'}) || ($encrypt->{'ltisec_domlinkprot'})) {
                       $showextra = 1;
                   }
                   if ($encrypt->{'ltisec_crslinkprot'}) {
                       $showextra = 1;
                   }
               } else {
                   if (($encrypt->{'toolsec_crs'}) || ($encrypt->{'toolsec_dom'})) {
                       $showextra = 1;
                   }
               }
               unless (grep(/^\Q$hostid\E$/,@ids)) {
                   $switch = 1;
               }
               if ($showextra) {
                   $numshown ++;
                   $divsty = 'display:inline-block';
               } else {
                   $divsty = 'display:none';
               }
               $extra .= '<fieldset id="'.$context.'_info_'.$hostid.'" style="'.$divsty.'">'.
                         '<legend>'.$hostid.'</legend>';
               if ($switch) {
                   my $switchserver = '<a href="/adm/switchserver?otherserver='.$hostid.'&amp;role='.
                                      &HTML::Entities::encode($env{'request.role'},'\'<>"&').
                                      '&amp;destinationurl=/adm/domainprefs">'.&mt('Switch Server').'</a>';
                   if (exists($privkeys->{$hostid})) {
                       $extra .= '<div id="'.$context.'_divcurrprivkey_'.$hostid.'" style="display:inline-block" />'.
                                 '<span class="LC_nobreak">'.
                                 &mt('Encryption Key').': ['.&mt('not shown').'] '.('&nbsp;'x2).'</span></div>'.
                                 '<span class="LC_nobreak">'.&mt('Change?').
                                 '<label><input type="radio" value="0" name="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" checked="checked" />'.&mt('No').'</label>'.
                                 ('&nbsp;'x2).
                                 '<label><input type="radio" value="1" name="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" />'.&mt('Yes').
                                 '</label>&nbsp;&nbsp;</span><div id="'.$context.'_divchgprivkey_'.$hostid.'" style="display:none" />'.
                                 '<span class="LC_nobreak"> - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver).
                                 '</span></div>';
                   } else {
                       $extra .= '<span class="LC_nobreak">'.
                                 &mt('Key required').' - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver).
                                 '</span>'."\n";
                   }
               } elsif (exists($privkeys->{$hostid})) {
                   $extra .= '<div id="'.$context.'_divcurrprivkey_'.$hostid.'" style="display:inline-block" /><span class="LC_nobreak">'.
                             &mt('Encryption Key').': ['.&mt('not shown').'] '.('&nbsp;'x2).'</span></div>'.
                             '<span class="LC_nobreak">'.&mt('Change?').
                             '<label><input type="radio" value="0" name="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" checked="checked" />'.&mt('No').'</label>'.
                             ('&nbsp;'x2).
                             '<label><input type="radio" value="1" name="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" />'.&mt('Yes').
                             '</label>&nbsp;&nbsp;</span><div id="'.$context.'_divchgprivkey_'.$hostid.'" style="display:none" />'.
                             '<span class="LC_nobreak">'.&mt('New Key').':'.
                             '<input type="password" size="20" name="'.$context.'_privkey_'.$hostid.'" value="" autocomplete="new-password" />'.
                             '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.'.$context.'_privkey_'.$hostid.'.type='."'text'".' } else { this.form.'.$context.'_privkey_'.$hostid.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'.
                             '</span></div>';
               } else {
                   $extra .= '<span class="LC_nobreak">'.&mt('Encryption Key').':'.
                             '<input type="password" size="20" name="'.$context.'_privkey_'.$hostid.'" value="" autocomplete="new-password" />'.
                             '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.'.$context.'_privkey_'.$hostid.'.type='."'text'".' } else { this.form.'.$context.'_privkey_'.$hostid.'.type='."'password'".' }" />'.&mt('Visible input').'</label>';
               }
               $extra .= '</fieldset>';
           }
       }
       my (%choices,@toggles,%defaultchecked);
       if ($context eq 'ltisec') {
           %choices = &Apache::lonlocal::texthash (
                                                     ltisec_crslinkprot => 'Encrypt stored link protection secrets defined in courses',
                                                     ltisec_domlinkprot => 'Encrypt stored link protection secrets defined in domain',
                                                     ltisec_consumers   => 'Encrypt stored consumer secrets defined in domain',
                                                  );
           @toggles = qw(ltisec_crslinkprot ltisec_domlinkprot ltisec_consumers);
           %defaultchecked = (
                              'ltisec_crslinkprot' => 'off',
                              'ltisec_domlinkprot' => 'off',
                              'ltisec_consumers'   => 'off',
                             );
       } else {
           %choices = &Apache::lonlocal::texthash (
                                                     toolsec_crs => 'Encrypt stored external tool secrets defined in courses',
                                                     toolsec_dom => 'Encrypt stored external tool secrets defined in domain',
                                                  );
           @toggles = qw(toolsec_crs toolsec_dom);
           %defaultchecked = (
                              'toolsec_crs' => 'off',
                              'toolsec_dom' => 'off',
                             );
       }
       my ($onclick,$itemcount);
       $onclick = 'javascript:toggleLTIEncKey(this.form,'."'$context'".');';
       ($output,$itemcount) = &radiobutton_prefs($encrypt,\@toggles,\%defaultchecked,
                                                 \%choices,$itemcount,$onclick,'','left','no');
   
       $css_class = $itemcount%2?' class="LC_odd_row"':'';
       my $noprivkeysty = 'display:inline-block';
       if ($numshown) {
           $noprivkeysty = 'display:none';
       }
       $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.&mt('Encryption Key(s)').'</td>'.
                  '<td><div id="'.$context.'_noprivkey" style="'.$noprivkeysty.'" >'.
                  '<span class="LC_nobreak">'.&mt('Not in use').'</span></div>'.
                  $extra.
                  '</td></tr>';
       $itemcount ++;
       $$rowtotal += $itemcount;
       return $output;
   }
   
   sub print_lti {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my $itemcount = 1;
       my ($datatable,$css_class);
       my (%rules,%encrypt,%privkeys,%linkprot,%suggestions);
       if (ref($settings) eq 'HASH') {
           if ($position eq 'top') {
               if (exists($settings->{'encrypt'})) {
                   if (ref($settings->{'encrypt'}) eq 'HASH') {
                       foreach my $key (keys(%{$settings->{'encrypt'}})) {
                           if ($key eq 'consumers') {
                               $encrypt{'ltisec_'.$key} = $settings->{'encrypt'}{$key};
                           } else {
                               $encrypt{'ltisec_'.$key.'linkprot'} = $settings->{'encrypt'}{$key};
                           }
                       }
                   }
               }
               if (exists($settings->{'private'})) {
                   if (ref($settings->{'private'}) eq 'HASH') {
                       if (ref($settings->{'private'}) eq 'HASH') {
                           if (ref($settings->{'private'}{'keys'}) eq 'ARRAY') {
                               map { $privkeys{$_} = 1; } (@{$settings->{'private'}{'keys'}});
                           }
                       }
                   }
               }
           } elsif ($position eq 'upper') {
               if (exists($settings->{'rules'})) {
                   if (ref($settings->{'rules'}) eq 'HASH') {
                       %rules = %{$settings->{'rules'}};
                   }
               }
           } elsif ($position eq 'middle') {
               if (exists($settings->{'suggested'})) {
                   if (ref($settings->{'suggested'}) eq 'HASH') {
                       %suggestions = %{$settings->{'suggested'}};
                   }
               }
           } elsif ($position eq 'lower') {
               if (exists($settings->{'linkprot'})) {
                   if (ref($settings->{'linkprot'}) eq 'HASH') {
                       %linkprot = %{$settings->{'linkprot'}};
                       if ($linkprot{'lock'}) {
                           delete($linkprot{'lock'});
                       }
                   }
               }
           } else {
               foreach my $key ('encrypt','private','rules','linkprot','suggestions') {
                   if (exists($settings->{$key})) {
                       delete($settings->{$key});
                   }
               }
           }
       }
       if ($position eq 'top') {
           $datatable = &secrets_form($dom,'ltisec',\%encrypt,\%privkeys,$rowtotal);
       } elsif ($position eq 'upper') {
           $datatable = &password_rules('ltisecrets',\$itemcount,\%rules);
           $$rowtotal += $itemcount;
       } elsif ($position eq 'middle') {
           $datatable = &linkprot_suggestions(\%suggestions,\$itemcount);
           $$rowtotal += $itemcount;
       } elsif ($position eq 'lower') {
           $datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain');
       } else {
           my ($switchserver,$switchmessage);
           $switchserver = &check_switchserver($dom);
           $switchmessage = &mt("submit from domain's primary library server: [_1].",$switchserver);
           my $maxnum = 0;
           my %ordered;
           if (ref($settings) eq 'HASH') {
               foreach my $item (keys(%{$settings})) {
                   if (ref($settings->{$item}) eq 'HASH') {
                       my $num = $settings->{$item}{'order'};
                       if ($num eq '') {
                           $num = scalar(keys(%{$settings}));
                       }
                       $ordered{$num} = $item;
                   }
               }
           }
           $maxnum = scalar(keys(%ordered));
           my %lt = &lti_names();
           if (keys(%ordered)) {
               my @items = sort { $a <=> $b } keys(%ordered);
               for (my $i=0; $i<@items; $i++) {
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   my $item = $ordered{$items[$i]};
                   my ($key,$secret,$usable,$lifetime,$consumer,$requser,$crsinc,$current);
                   if (ref($settings->{$item}) eq 'HASH') {
                       $key = $settings->{$item}->{'key'};
                       $usable = $settings->{$item}->{'usable'};
                       $lifetime = $settings->{$item}->{'lifetime'};
                       $consumer = $settings->{$item}->{'consumer'};
                       $requser = $settings->{$item}->{'requser'};
                       $crsinc = $settings->{$item}->{'crsinc'};
                       $current = $settings->{$item};
                   }
                   my $onclickrequser = ' onclick="toggleLTI(this.form,'."'requser','$i'".');"';
                   my %checkedrequser = (
                                          yes => ' checked="checked"',
                                          no  => '',
                                        );
                   if (!$requser) {
                       $checkedrequser{'no'} = $checkedrequser{'yes'};
                       $checkedrequser{'yes'} = '';
                   }
                   my $onclickcrsinc = ' onclick="toggleLTI(this.form,'."'crsinc','$i'".');"';
                   my %checkedcrsinc = (
                                         yes => ' checked="checked"',
                                         no  => '',
                                       );
                   if (!$crsinc) {
                       $checkedcrsinc{'no'} = $checkedcrsinc{'yes'};
                       $checkedcrsinc{'yes'} = '';
                   }
                   my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_".$item."'".');"';
                   $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                                .'<select name="lti_pos_'.$item.'"'.$chgstr.'>';
                   for (my $k=0; $k<=$maxnum; $k++) {
                       my $vpos = $k+1;
                       my $selstr;
                       if ($k == $i) {
                           $selstr = ' selected="selected" ';
                       }
                       $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
                   }
                   $datatable .= '</select>'.('&nbsp;'x2).
                       '<label><input type="checkbox" name="lti_del" value="'.$item.'" />'.
                       &mt('Delete?').'</label></span></td>'.
                       '<td colspan="2">'.
                       '<fieldset><legend>'.&mt('Required settings').'</legend>'.
                       '<span class="LC_nobreak">'.$lt{'consumer'}.
                       ':<input type="text" size="15" name="lti_consumer_'.$i.'" value="'.$consumer.'" /></span> '.
                       ('&nbsp;'x2).
                       '<span class="LC_nobreak">'.$lt{'version'}.':<select name="lti_version_'.$i.'">'.
                       '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '.
                       ('&nbsp;'x2).
                       '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" name="lti_lifetime_'.$i.'"'.
                       'value="'.$lifetime.'" size="3" /></span><br /><br />';
                   if ($key ne '') {
                       $datatable .= '<span class="LC_nobreak">'.$lt{'key'};
                       if ($switchserver) {
                           $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']';
                       } else {
                           $datatable .= ':<input type="text" size="25" name="lti_key_'.$i.'" value="'.$key.'" autocomplete="off" />';
                       }
                       $datatable .= '</span> '.('&nbsp;'x2);
                   } elsif (!$switchserver) {
                       $datatable .= '<span class="LC_nobreak">'.$lt{'key'}.':'.
                                     '<input type="text" size="25" name="lti_key_'.$i.'" value="'.$key.'" autocomplete="off" />'.
                                     '</span> '.('&nbsp;'x2);
                   }
                   if ($switchserver) {
                       if ($usable ne '') {
                           $datatable .= '<div id="lti_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'.
                                         $lt{'secret'}.': ['.&mt('not shown').'] '.('&nbsp;'x2).'</span></div>'.
                                         '<span class="LC_nobreak">'.&mt('Change secret?').
                                         '<label><input type="radio" value="0" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" checked="checked" />'.&mt('No').'</label>'.
                                         ('&nbsp;'x2).
                                        '<label><input type="radio" value="1" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" />'.&mt('Yes').'</label>'.('&nbsp;'x2).
                                         '</span><div id="lti_divchgsecret_'.$i.'" style="display:none" />'.
                                         '<span class="LC_nobreak"> - '.$switchmessage.'</span>'.
                                         '</div>';
                       } elsif ($key eq '') {
                           $datatable .= '<span class="LC_nobreak">'.&mt('Key and Secret are required').' - '.$switchmessage.'</span>'."\n";
                       } else {
                           $datatable .= '<span class="LC_nobreak">'.&mt('Secret required').' - '.$switchmessage.'</span>'."\n";
                       }
                   } else {
                       if ($usable ne '') {
                           $datatable .= '<div id="lti_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'.
                                         $lt{'secret'}.': ['.&mt('not shown').'] '.('&nbsp;'x2).'</span></div>'.
                                         '<span class="LC_nobreak">'.&mt('Change?').
                                         '<label><input type="radio" value="0" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" checked="checked" />'.&mt('No').'</label>'.
                                         ('&nbsp;'x2).
                                         '<label><input type="radio" value="1" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" />'.&mt('Yes').
                                         '</label>&nbsp;&nbsp;</span><div id="lti_divchgsecret_'.$i.'" style="display:none" />'.
                                         '<span class="LC_nobreak">'.&mt('New Secret').':'.
                                         '<input type="password" size="20" name="lti_secret_'.$i.'" value="" autocomplete="new-password" />'.
                                         '<label><input type="checkbox" name="lti_visible_'.$i.'" id="lti_visible_'.$i.'" onclick="if (this.checked) { this.form.lti_secret_'.$i.'.type='."'text'".' } else { this.form.lti_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label></span></div>';
                       } else {
                           $datatable .=
                               '<span class="LC_nobreak">'.$lt{'secret'}.':'.
                               '<input type="password" size="20" name="lti_secret_'.$i.'" value="" autocomplete="new-password" />'.
                               '<label><input type="checkbox" name="lti_visible_'.$i.'" id="lti_visible_'.$i.'" onclick="if (this.checked) { this.form.lti_secret_'.$i.'.type='."'text'".' } else { this.form.lti_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>';
                       }
                   }
                   $datatable .= '<br /><br />'.
                       '<span class="LC_nobreak">'.$lt{'requser'}.':'.
                       '<label><input type="radio" name="lti_requser_'.$i.'" value="1"'.$onclickrequser.$checkedrequser{yes}.' />'.&mt('Yes').'</label>&nbsp;'."\n".
                       '<label><input type="radio" name="lti_requser_'.$i.'" value="0"'.$onclickrequser.$checkedrequser{no}.' />'.&mt('No').'</label></span>'."\n".
                       '<br /><br />'.
                       '<span class="LC_nobreak">'.$lt{'crsinc'}.':'.
                       '<label><input type="radio" name="lti_crsinc_'.$i.'" value="1"'.$onclickcrsinc.$checkedcrsinc{yes}.' />'.&mt('Yes').'</label>&nbsp;'."\n".
                       '<label><input type="radio" name="lti_crsinc_'.$i.'" value="0"'.$onclickcrsinc.$checkedcrsinc{no}.' />'.&mt('No').'</label></span>'."\n".
                       ('&nbsp;'x4).
                       '<input type="hidden" name="lti_id_'.$i.'" value="'.$item.'" /></span>'.
                       '</fieldset>'.&lti_options($i,$current,$itemcount,%lt).'</td></tr>';
                   $itemcount ++;
               }
           }
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_add'".');"';
           $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
                         '<input type="hidden" name="lti_maxnum" value="'.$maxnum.'" />'."\n".
                         '<select name="lti_pos_add"'.$chgstr.'>';
           for (my $k=0; $k<$maxnum+1; $k++) {
               my $vpos = $k+1;
               my $selstr;
               if ($k == $maxnum) {
                   $selstr = ' selected="selected" ';
               }
               $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
           }
           $datatable .= '</select>&nbsp;'."\n".
                         '<input type="checkbox" name="lti_add" value="1" />'.&mt('Add').'</span></td>'."\n".
                         '<td colspan="2">'.
                         '<fieldset><legend>'.&mt('Required settings').'</legend>'.
                         '<span class="LC_nobreak">'.$lt{'consumer'}.
                         ':<input type="text" size="15" name="lti_consumer_add" value="" /></span> '."\n".
                         ('&nbsp;'x2).
                         '<span class="LC_nobreak">'.$lt{'version'}.':<select name="lti_version_add">'.
                         '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n".
                         ('&nbsp;'x2).
                         '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="3" name="lti_lifetime_add" value="300" /></span><br /><br />'."\n";
           if ($switchserver) {
               $datatable .= '<span class="LC_nobreak">'.&mt('Key and Secret are required').' - '.$switchmessage.'</span>'."\n";
           } else {
               $datatable .= '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="lti_key_add" value="" autocomplete="off" /></span> '."\n".
                             ('&nbsp;'x2).
                             '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="lti_secret_add" value="" autocomplete="new-password" />'.
                             '<label><input type="checkbox" name="lti_add_visible" id="lti_add_visible" onclick="if (this.checked) { this.form.lti_secret_add.type='."'text'".' } else { this.form.lti_secret_add.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n";
           }
           $datatable .= '<br /><br />'.
                         '<span class="LC_nobreak">'.$lt{'requser'}.':'.
                         '<label><input type="radio" name="lti_requser_add" value="1" onclick="toggleLTI(this.form,'."'requser','add'".');" checked="checked" />'.&mt('Yes').'</label>&nbsp;'."\n".
                         '<label><input type="radio" name="lti_requser_add" value="0" onclick="toggleLTI(this.form,'."'requser','add'".');" />'.&mt('No').'</label></span>'."\n".
                         '<br /><br />'.
                         '<span class="LC_nobreak">'.$lt{'crsinc'}.':'.
                         '<label><input type="radio" name="lti_crsinc_add" value="1" onclick="toggleLTI(this.form,'."'crsinc','add'".');" checked="checked" />'.&mt('Yes').'</label>&nbsp;'."\n".
                         '<label><input type="radio" name="lti_crsinc_add" value="0" onclick="toggleLTI(this.form,'."'crsinc','add'".');" />'.&mt('No').'</label></span>'."\n".
                         '</fieldset>'.&lti_options('add',undef,$itemcount,%lt).
                         '</td>'."\n".
                         '</tr>'."\n";
           $itemcount ++;
       }
       $$rowtotal += $itemcount;
       return $datatable;
   }
   
   sub lti_names {
       my %lt = &Apache::lonlocal::texthash(
                                             'version'   => 'LTI Version',
                                             'url'       => 'URL',
                                             'key'       => 'Key',
                                             'lifetime'  => 'Nonce lifetime (s)',
                                             'consumer'  => 'Consumer',
                                             'secret'    => 'Secret',
                                             'requser'   => "User's identity sent",
                                             'crsinc'    => "Course's identity sent",
                                             'email'     => 'Email address',
                                             'sourcedid' => 'User ID',
                                             'other'     => 'Other',
                                             'passback'  => 'Can return grades to Consumer:',
                                             'roster'    => 'Can retrieve roster from Consumer:',
                                             'topmenu'   => 'Display LON-CAPA page header',
                                             'inlinemenu'=> 'Display LON-CAPA inline menu',
                                           );
     return %lt;      return %lt;
 }  }
   
   sub lti_options {
       my ($num,$current,$itemcount,%lt) = @_;
       my (%checked,%rolemaps,$crssecsrc,$userfield,$cidfield,$callback);
       $checked{'mapuser'}{'sourcedid'} = ' checked="checked"';
       $checked{'mapcrs'}{'course_offering_sourcedid'} = ' checked="checked"';
       $checked{'storecrs'}{'Y'} = ' checked="checked"';
       $checked{'makecrs'}{'N'} = ' checked="checked"';
       $checked{'mapcrstype'} = {};
       $checked{'makeuser'} = {};
       $checked{'selfenroll'} = {};
       $checked{'crssec'} = {};
       $checked{'crssecsrc'} = {};
       $checked{'lcauth'} = {};
       $checked{'menuitem'} = {};
       if ($num eq 'add') {
           $checked{'lcauth'}{'lti'} = ' checked="checked"';
       }
       my $userfieldsty = 'none';
       my $crsfieldsty = 'none';
       my $crssecfieldsty = 'none';
       my $secsrcfieldsty = 'none';
       my $callbacksty = 'none';
       my $passbacksty = 'none';
       my $optionsty = 'block';
       my $crssty = 'block';
       my $lcauthparm;
       my $lcauthparmstyle = 'display:none';
       my $lcauthparmtext;
       my $menusty;
       my $numinrow = 4;
       my %menutitles = &ltimenu_titles();
   
       if (ref($current) eq 'HASH') {
           if (!$current->{'requser'}) {
               $optionsty = 'none';
               $crssty = 'none';
           } elsif (!$current->{'crsinc'}) {
               $crssty = 'none';
           }
           if (($current->{'mapuser'} ne '') && ($current->{'mapuser'} ne 'lis_person_sourcedid')) {
               $checked{'mapuser'}{'sourcedid'} = '';
               if ($current->{'mapuser'} eq 'lis_person_contact_email_primary') {
                   $checked{'mapuser'}{'email'} = ' checked="checked"';
               } else {
                   $checked{'mapuser'}{'other'} = ' checked="checked"';
                   $userfield = $current->{'mapuser'};
                   $userfieldsty = 'inline-block';
               }
           }
           if (($current->{'mapcrs'} ne '') && ($current->{'mapcrs'} ne 'course_offering_sourcedid')) {
               $checked{'mapcrs'}{'course_offering_sourcedid'} = '';
               if ($current->{'mapcrs'} eq 'context_id') {
                   $checked{'mapcrs'}{'context_id'} = ' checked="checked"';
               } else {
                   $checked{'mapcrs'}{'other'} = ' checked="checked"';
                   $cidfield = $current->{'mapcrs'};
                   $crsfieldsty = 'inline-block';
               }
           }
           if (ref($current->{'mapcrstype'}) eq 'ARRAY') {
               foreach my $type (@{$current->{'mapcrstype'}}) {
                   $checked{'mapcrstype'}{$type} = ' checked="checked"';
               }
           }
           if (!$current->{'storecrs'}) {
               $checked{'storecrs'}{'N'} = $checked{'storecrs'}{'Y'};
               $checked{'storecrs'}{'Y'} = '';
           }
           if ($current->{'makecrs'}) {
               $checked{'makecrs'}{'Y'} = '  checked="checked"';
           }
           if (ref($current->{'makeuser'}) eq 'ARRAY') {
               foreach my $role (@{$current->{'makeuser'}}) {
                   $checked{'makeuser'}{$role} = ' checked="checked"';
               }
           }
           if ($current->{'lcauth'} =~ /^(internal|localauth|krb4|krb5|lti)$/) {
               $checked{'lcauth'}{$1} = ' checked="checked"';
               unless (($current->{'lcauth'} eq 'lti') || ($current->{'lcauth'} eq 'internal')) {
                   $lcauthparm = $current->{'lcauthparm'};
                   $lcauthparmstyle = 'display:table-row';
                   if ($current->{'lcauth'} eq 'localauth') {
                       $lcauthparmtext = &mt('Local auth argument');
                   } else {
                       $lcauthparmtext = &mt('Kerberos domain');
                   }
               }
           }
           if (ref($current->{'selfenroll'}) eq 'ARRAY') {
               foreach my $role (@{$current->{'selfenroll'}}) {
                   $checked{'selfenroll'}{$role} = ' checked="checked"';
               }
           }
           if (ref($current->{'maproles'}) eq 'HASH') {
               %rolemaps = %{$current->{'maproles'}};
           }
           if ($current->{'section'} ne '') {
               $checked{'crssec'}{'Y'} = '  checked="checked"';
               $crssecfieldsty = 'inline-block';
               if ($current->{'section'} eq 'course_section_sourcedid') {
                   $checked{'crssecsrc'}{'sourcedid'} = ' checked="checked"';
               } else {
                   $checked{'crssecsrc'}{'other'} = ' checked="checked"';
                   $crssecsrc = $current->{'section'};
                   $secsrcfieldsty = 'inline-block';
               }
           } else {
               $checked{'crssec'}{'N'} = ' checked="checked"';
           }
           if ($current->{'callback'} ne '') {
               $callback = $current->{'callback'};
               $checked{'callback'}{'Y'} = ' checked="checked"';
               $callbacksty = 'inline-block';
           } else {
               $checked{'callback'}{'N'} = ' checked="checked"';
           }
           if ($current->{'topmenu'}) {
               $checked{'topmenu'}{'Y'} = ' checked="checked"';
           } else {
               $checked{'topmenu'}{'N'} = ' checked="checked"';
           }
           if ($current->{'inlinemenu'}) {
               $checked{'inlinemenu'}{'Y'} = ' checked="checked"';
           } else {
               $checked{'inlinemenu'}{'N'} = ' checked="checked"';
           }
           if (($current->{'topmenu'}) || ($current->{'inlinemenu'})) {
               $menusty = 'inline-block';
               if (ref($current->{'lcmenu'}) eq 'ARRAY') {
                   foreach my $item (@{$current->{'lcmenu'}}) {
                       if (exists($menutitles{$item})) {
                           $checked{'menuitem'}{$item} = ' checked="checked"';
                       }
                   }
               }
           } else {
               $menusty = 'none';
           }
       } else {
           $checked{'makecrs'}{'N'} = ' checked="checked"';
           $checked{'crssec'}{'N'} = ' checked="checked"';
           $checked{'callback'}{'N'} = ' checked="checked"';
           $checked{'topmenu'}{'N'} = ' checked="checked"';
           $checked{'inlinemenu'}{'Y'} = ' checked="checked"';
           $checked{'menuitem'}{'grades'} = ' checked="checked"';
           $menusty = 'inline-block';
       }
       my @coursetypes = ('official','unofficial','community','textbook','lti');
       my %coursetypetitles = &Apache::lonlocal::texthash (
                                  official   => 'Official',
                                  unofficial => 'Unofficial',
                                  community  => 'Community',
                                  textbook   => 'Textbook',
                                  lti        => 'LTI Provider',
       );
       my @authtypes = ('internal','krb4','krb5','localauth');
       my %shortauth = (
                        internal => 'int',
                        krb4 => 'krb4',
                        krb5 => 'krb5',
                        localauth  => 'loc'
                       );
       my %authnames = &authtype_names();
       my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator);
       my @lticourseroles = qw(Learner Instructor TeachingAssistant Mentor);
       my @courseroles = ('cc','in','ta','ep','st');
       my $onclickuser = ' onclick="toggleLTI(this.form,'."'user','$num'".');"';
       my $onclickcrs = ' onclick="toggleLTI(this.form,'."'crs','$num'".');"';
       my $onclicksec = ' onclick="toggleLTI(this.form,'."'sec','$num'".');"';
       my $onclickcallback = ' onclick="toggleLTI(this.form,'."'callback','$num'".');"';
       my $onclicksecsrc = ' onclick="toggleLTI(this.form,'."'secsrc','$num'".')"';
       my $onclicklcauth = ' onclick="toggleLTI(this.form,'."'lcauth','$num'".')"';
       my $onclickmenu = ' onclick="toggleLTI(this.form,'."'lcmenu','$num'".');"';
       my $output = '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Logout options').'</legend>'.
                    '<div class="LC_floatleft"><span class="LC_nobreak">'.&mt('Callback to logout LON-CAPA on log out from Consumer').':&nbsp;'.
                    '<label><input type="radio" name="lti_callback_'.$num.'" value="0"'.
                    $checked{'callback'}{'N'}.$onclickcallback.' />'.&mt('No').'</label>'.('&nbsp;'x2).
                    '<label><input type="radio" name="lti_callback_'.$num.'" value="1"'.
                    $checked{'callback'}{'Y'}.$onclickcallback.' />'.&mt('Yes').'</label></span></div>'.
                    '<div class="LC_floatleft" style="display:'.$callbacksty.';" id="lti_callbackfield_'.$num.'">'.
                    '<span class="LC_nobreak">'.&mt('Parameter').': '.
                    '<input type="text" name="lti_callbackparam_'.$num.'" value="'.$callback.'" /></span>'.
                    '</div><div style="padding:0;clear:both;margin:0;border:0"></div></fieldset>'.
                    '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Mapping users').'</legend>'.
                    '<div class="LC_floatleft"><span class="LC_nobreak">'.&mt('LON-CAPA username').':&nbsp;';
       foreach my $option ('sourcedid','email','other') {
           $output .= '<label><input type="radio" name="lti_mapuser_'.$num.'" value="'.$option.'"'.
                      $checked{'mapuser'}{$option}.$onclickuser.' />'.$lt{$option}.'</label>'.
                      ($option eq 'other' ? '' : ('&nbsp;'x2) );
       }
       $output .= '</span></div>'.
                  '<div class="LC_floatleft" style="display:'.$userfieldsty.';" id="lti_userfield_'.$num.'">'.
                  '<input type="text" name="lti_customuser_'.$num.'" '.
                  'value="'.$userfield.'" /></div></fieldset>'.
                  '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Roles which may create user accounts').'</legend>';
       foreach my $ltirole (@ltiroles) {
           $output .= '<span class="LC_nobreak"><label><input type="checkbox" name="lti_makeuser_'.$num.'" value="'.$ltirole.'"'.
                      $checked{'makeuser'}{$ltirole}.' />'.$ltirole.'</label>&nbsp;</span> ';
       }
       $output .= '</fieldset>'.
                  '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('New user accounts created for LTI users').'</legend>'.
                  '<table>'.
                  &modifiable_userdata_row('lti','instdata_'.$num,$current,$numinrow,$itemcount).
                  '</table>'.
                  '<table class="LC_nested"><tr><td class="LC_left_item">LON-CAPA Authentication</td>'.
                  '<td class="LC_left_item">';
       foreach my $auth ('lti',@authtypes) {
           my $authtext;
           if ($auth eq 'lti') {
               $authtext = &mt('None');
           } else {
               $authtext = $authnames{$shortauth{$auth}};
           }
           $output .= '<span class="LC_nobreak"><label><input type="radio" name="lti_lcauth_'.$num.
                      '" value="'.$auth.'"'.$checked{'lcauth'}{$auth}.$onclicklcauth.' />'.
                      $authtext.'</label></span> &nbsp;';
       }
       $output .= '</td></tr>'.
                  '<tr id="lti_lcauth_parmrow_'.$num.'" style="'.$lcauthparmstyle.'">'.
                  '<td class="LC_right_item" colspan="2"><span class="LC_nobreak">'.
                  '<span id="lti_lcauth_parmtext_'.$num.'">'.$lcauthparmtext.'</span>'.
                  '<input type="text" name="lti_lcauthparm_'.$num.'" value="" /></span></td></tr>'.
                  '</table></fieldset>'.
                  '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.
                  &mt('LON-CAPA menu items (Course Coordinator can override)').'</legend>'.
                  '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{'topmenu'}.':&nbsp;'.
                  '<label><input type="radio" name="lti_topmenu_'.$num.'" value="0"'.
                  $checked{'topmenu'}{'N'}.$onclickmenu.' />'.&mt('No').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_topmenu_'.$num.'" value="1"'.
                  $checked{'topmenu'}{'Y'}.$onclickmenu.' />'.&mt('Yes').'</label></span></div>'.
                  '<div style="padding:0;clear:both;margin:0;border:0"></div>'.
                  '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{'inlinemenu'}.':&nbsp;'.
                  '<label><input type="radio" name="lti_inlinemenu_'.$num.'" value="0"'.
                  $checked{'inlinemenu'}{'N'}.$onclickmenu.' />'.&mt('No').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_inlinemenu_'.$num.'" value="1"'.
                  $checked{'inlinemenu'}{'Y'}.$onclickmenu.' />'.&mt('Yes').'</label></span></div>';
        $output .='<div style="padding:0;clear:both;margin:0;border:0"></div>'.
                  '<div class="LC_floatleft" style="display:'.$menusty.';" id="lti_menufield_'.$num.'">'.
                  '<span class="LC_nobreak">'.&mt('Menu items').':&nbsp;';
       foreach my $type ('fullname','coursetitle','role','logout','grades') {
           $output .= '<label><input type="checkbox" name="lti_menuitem_'.$num.'" value="'.$type.'"'.
                      $checked{'menuitem'}{$type}.' />'.$menutitles{$type}.'</label>'.
                      ('&nbsp;'x2);
       }
       $output .= '</span></div></fieldset>'.
                  '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Mapping courses').'</legend>'.
                  '<div class="LC_floatleft"><span class="LC_nobreak">'.
                  &mt('Unique course identifier').':&nbsp;';
       foreach my $option ('course_offering_sourcedid','context_id','other') {
           $output .= '<label><input type="radio" name="lti_mapcrs_'.$num.'" value="'.$option.'"'.
                      $checked{'mapcrs'}{$option}.$onclickcrs.' />'.$option.'</label>'.
                      ($option eq 'other' ? '' : ('&nbsp;'x2) );
       }
       $output .= '</span></div><div class="LC_floatleft" style="display:'.$crsfieldsty.';" id="lti_crsfield_'.$num.'">'.
                  '<input type="text" name="lti_mapcrsfield_'.$num.'" value="'.$cidfield.'" />'.
                  '</div><div style="padding:0;clear:both;margin:0;border:0"></div>'.
                  '<span class="LC_nobreak">'.&mt('LON-CAPA course type(s)').':&nbsp;';
       foreach my $type (@coursetypes) {
           $output .= '<label><input type="checkbox" name="lti_mapcrstype_'.$num.'" value="'.$type.'"'.
                      $checked{'mapcrstype'}{$type}.' />'.$coursetypetitles{$type}.'</label>'.
                      ('&nbsp;'x2);
       }
       $output .= '</span><br /><br />'.
                  '<span class="LC_nobreak">'.&mt('Store mapping of course identifier to LON-CAPA CourseID').':&nbsp;'.
                  '<label><input type="radio" name="lti_storecrs_'.$num.'" value="0"'.
                  $checked{'storecrs'}{'N'}.' />'.&mt('No').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_storecrs_'.$num.'" value="1"'.
                  $checked{'storecrs'}{'Y'}.' />'.&mt('Yes').'</label></span>'.
                  '</fieldset>'.
                  '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Mapping course roles').'</legend><table><tr>';
       foreach my $ltirole (@lticourseroles) {
           my ($selected,$selectnone);
           if ($rolemaps{$ltirole} eq '') {
               $selectnone = ' selected="selected"';
           }
           $output .= '<td style="text-align: center">'.$ltirole.'<br />'.
                      '<select name="lti_maprole_'.$ltirole.'_'.$num.'">'.
                      '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';
           foreach my $role (@courseroles) {
               unless ($selectnone) {
                   if ($rolemaps{$ltirole} eq $role) {
                       $selected = ' selected="selected"';
                   } else {
                       $selected = '';
                   }
               }
               $output .= '<option value="'.$role.'"'.$selected.'>'.
                          &Apache::lonnet::plaintext($role,'Course').
                          '</option>';
           }
           $output .= '</select></td>';
       }
       $output .= '</tr></table></fieldset>'.
                  '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Creating courses').'</legend>'.
                  '<span class="LC_nobreak">'.&mt('Course created (if absent) on Instructor access').':&nbsp;'.
                  '<label><input type="radio" name="lti_makecrs_'.$num.'" value="0"'.
                  $checked{'makecrs'}{'N'}.' />'.&mt('No').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_makecrs_'.$num.'" value="1"'.
                  $checked{'makecrs'}{'Y'}.' />'.&mt('Yes').'</label></span>'.
                  '</fieldset>'.
                  '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Roles which may self-enroll').'</legend>';
       foreach my $lticrsrole (@lticourseroles) {
           $output .= '<span class="LC_nobreak"><label><input type="checkbox" name="lti_selfenroll_'.$num.'" value="'.$lticrsrole.'"'.
                      $checked{'selfenroll'}{$lticrsrole}.' />'.$lticrsrole.'</label>&nbsp;</span> ';
       }
       $output .= '</fieldset>'.
                  '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Course options').'</legend>'.
                  '<div class="LC_floatleft"><span class="LC_nobreak">'.&mt('Assign users to sections').':&nbsp;'.
                  '<label><input type="radio" name="lti_crssec_'.$num.'" value="0"'.
                  $checked{'crssec'}{'N'}.$onclicksec.' />'.&mt('No').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_crssec_'.$num.'" value="1"'.
                  $checked{'crssec'}{'Y'}.$onclicksec.' />'.&mt('Yes').'</label></span></div>'.
                  '<div class="LC_floatleft" style="display:'.$crssecfieldsty.';" id="lti_crssecfield_'.$num.'">'.
                  '<span class="LC_nobreak">'.&mt('From').':<label>'.
                  '<input type="radio" name="lti_crssecsrc_'.$num.'" value="course_section_sourcedid"'.
                  $checked{'crssecsrc'}{'sourcedid'}.$onclicksecsrc.' />'.
                  &mt('Standard field').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_crssecsrc_'.$num.'" value="other"'.
                  $checked{'crssecsrc'}{'other'}.$onclicksecsrc.' />'.&mt('Other').
                  '</label></span></div><div class="LC_floatleft" style="display:'.$secsrcfieldsty.';" id="lti_secsrcfield_'.$num.'">'.
                  '<input type="text" name="lti_customsection_'.$num.'" value="'.$crssecsrc.'" />'.
                  '</div><div style="padding:0;clear:both;margin:0;border:0"></div>';
       my ($pb1p1chk,$pb1p0chk,$onclickpb);
       foreach my $extra ('roster','passback') {
           my $checkedon = '';
           my $checkedoff = ' checked="checked"';
           if ($extra eq 'passback') {
               $pb1p1chk = ' checked="checked"';
               $pb1p0chk = '';
               $onclickpb = ' onclick="toggleLTI(this.form,'."'passback','$num'".');"';
           } else {
               $onclickpb = '';
           }
           if (ref($current) eq 'HASH') {
               if (($current->{$extra})) {
                   $checkedon = $checkedoff;
                   $checkedoff = '';
                   if ($extra eq 'passback') {
                       $passbacksty = 'inline-block';
                   }
                   if ($current->{'passbackformat'} eq '1.0') {
                       $pb1p0chk =  ' checked="checked"';
                       $pb1p1chk = '';
                   }
               }
           }
           $output .= $lt{$extra}.'&nbsp;'.
                      '<label><input type="radio" name="lti_'.$extra.'_'.$num.'" value="0"'.$checkedoff.$onclickpb.' />'.
                      &mt('No').'</label>'.('&nbsp;'x2).
                      '<label><input type="radio" name="lti_'.$extra.'_'.$num.'" value="1"'.$checkedon.$onclickpb.' />'.
                      &mt('Yes').'</label><br />';
       }
       $output .= '<div class="LC_floatleft" style="display:'.$passbacksty.';" id="lti_passback_'.$num.'">'.
                  '<span class="LC_nobreak">'.&mt('Grade format').
                  '<label><input type="radio" name="lti_passbackformat_'.$num.'" value="1.1"'.$pb1p1chk.' />'.
                  &mt('Outcomes Service (1.1)').'</label>'.('&nbsp;'x2).
                  '<label><input type="radio" name="lti_passbackformat_'.$num.'" value="1.0"'.$pb1p0chk.'/>'.
                  &mt('Outcomes Extension (1.0)').'</label></span></div>'.
                  '<div style="padding:0;clear:both;margin:0;border:0"></div></fieldset>';
       $output .= '</span></div></fieldset>';
   #        '<fieldset><legend>'.&mt('Assigning author roles').'</legend>';
   #
   #    $output .= '</fieldset>'.
   #        '<fieldset><legend>'.&mt('Assigning domain roles').'</legend>';
       return $output;
   }
   
   sub ltimenu_titles {
       return &Apache::lonlocal::texthash(
                                           fullname    => 'Full name',
                                           coursetitle => 'Course title',
                                           role        => 'Role',
                                           logout      => 'Logout',
                                           grades      => 'Grades',
       );
   }
   
   sub linkprot_suggestions {
       my ($suggested,$itemcount) = @_;
       my $count = 0;
       my $next = 1;
       my %lt = &Apache::lonlocal::texthash(
                                             'name' => 'Suggested Launcher',
                                             'info' => 'Recommendations',
                                           );
       my ($datatable,$css_class,$dest);
       if (ref($suggested) eq 'HASH') {
           my @current = sort { $a <=> $b } keys(%{$suggested});
           $next += $current[-1];
           for (my $i=0; $i<@current; $i++) {
               my $num = $current[$i];
               my %values;
               if (ref($suggested->{$num}) eq 'HASH') {
                   %values = %{$suggested->{$num}};
               } else {
                   next;
               }
               $css_class = $$itemcount%2?' class="LC_odd_row"':'';
               $datatable .=
                   '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
                   '<label><input type="checkbox" name="linkprot_suggested_del" value="'.$i.'" />'."\n".
                   &mt('Delete?').'</label></span></td><td>'."\n".
                   '<div class="LC_floatleft"><fieldset><legend>'.$lt{'name'}.'</legend>'."\n".
                   '<input type="text" size="15" name="linkprot_suggested_name_'.$i.'" value="'.$values{'name'}.'" autocomplete="off" />'."\n".
                   '</fieldset></div>'.
                   '<div class="LC_floatleft"><fieldset><legend>'.$lt{'info'}.'</legend>'."\n".
                   '<textarea cols="55" rows="5" name="linkprot_suggested_info_'.$i.'">'.$values{'info'}.'</textarea>'.
                   '</fieldset></div>'.
                   '<div style="padding:0;clear:both;margin:0;border:0"></div>'."\n".
                   '<input type="hidden" name="linkprot_suggested_id_'.$i.'" value="'.$num.'" /></td></tr>'."\n";
               $$itemcount ++;
           }
       }
       $css_class = $$itemcount%2?' class="LC_odd_row"':'';
       $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
                     '<input type="hidden" name="linkprot_suggested_maxnum" value="'.$next.'" />'."\n".
                     '<input type="checkbox" name="linkprot_suggested_add" value="1" />'.&mt('Add').'</span></td>'."\n".
                     '<td>'."\n".
                     '<div class="LC_floatleft"><fieldset><legend>'.$lt{'name'}.'</legend>'."\n".
                     '<input type="text" size="15" name="linkprot_suggested_name_add" value="" autocomplete="off" />'."\n".
                     '</fieldset></div>'.
                     '<div class="LC_floatleft"><fieldset><legend>'.$lt{'info'}.'</legend>'."\n".
                     '<textarea cols="55" rows="5" name="linkprot_suggested_info_add"></textarea>'.
                     '</fieldset></div>'.
                     '<div style="padding:0;clear:both;margin:0;border:0"></div>'."\n".
                     '</td></tr>'."\n";
       return $datatable;
   }
   
 sub print_coursedefaults {  sub print_coursedefaults {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles);      my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles);
     my $itemcount = 1;      my $itemcount = 1;
     my %choices =  &Apache::lonlocal::texthash (      my %choices =  &Apache::lonlocal::texthash (
         uploadquota          => 'Default quota for files uploaded directly to course/community using Course Editor (MB)',          uploadquota          => 'Default quota for files uploaded directly to course/community using Course Editor (MB)',
           coursequota          => 'Default cumulative quota for all group portfolio spaces in course',
         anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys',          anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys',
         coursecredits        => 'Credits can be specified for courses',          coursecredits        => 'Credits can be specified for courses',
         uselcmath            => 'Math preview uses LON-CAPA previewer (javascript) in place of DragMath (Java)',          uselcmath            => 'Math preview uses LON-CAPA previewer (javascript) in place of DragMath (Java)',
         usejsme              => 'Molecule editor uses JSME (HTML5) in place of JME (Java)',          usejsme              => 'Molecule editor uses JSME (HTML5) in place of JME (Java)',
           inline_chem          => 'Use inline previewer for chemical reaction response in place of pop-up',
         texengine            => 'Default method to display mathematics',          texengine            => 'Default method to display mathematics',
         postsubmit           => 'Disable submit button/keypress following student submission',          postsubmit           => 'Disable submit button/keypress following student submission',
         canclone             => "People who may clone a course (besides course's owner and coordinators)",          canclone             => "People who may clone a course (besides course's owner and coordinators)",
         mysqltables          => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver',          mysqltables          => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver',
           ltiauth              => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication',
           domexttool           => 'External Tools defined in the domain may be used in courses/communities (by type)',
           exttool              => 'External Tools can be defined and configured in courses/communities (by type)',
     );      );
     my %staticdefaults = (      my %staticdefaults = (
                            anonsurvey_threshold => 10,                             anonsurvey_threshold => 10,
                            uploadquota          => 500,                             uploadquota          => 500,
                              coursequota          => 20,
                            postsubmit           => 60,                             postsubmit           => 60,
                            mysqltables          => 172800,                             mysqltables          => 172800,
                              domexttool           => 1,
                              exttool              => 0,
                          );                           );
     if ($position eq 'top') {      if ($position eq 'top') {
         %defaultchecked = (          %defaultchecked = (
                             'uselcmath'       => 'on',                              'uselcmath'       => 'on',
                             'usejsme'         => 'on',                              'usejsme'         => 'on',
                               'inline_chem'     => 'on',
                             'canclone'        => 'none',                              'canclone'        => 'none',
                           );                            );
         @toggles = ('uselcmath','usejsme');          @toggles = ('uselcmath','usejsme','inline_chem');
         my $deftex = $Apache::lonnet::deftex;          my $deftex = $Apache::lonnet::deftex;
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ($settings->{'texengine'}) {              if ($settings->{'texengine'}) {
Line 4227  sub print_coursedefaults { Line 6067  sub print_coursedefaults {
         my $currcanclone = 'none';          my $currcanclone = 'none';
         my $onclick;          my $onclick;
         my @cloneoptions = ('none','domain');          my @cloneoptions = ('none','domain');
         my %clonetitles = (          my %clonetitles = &Apache::lonlocal::texthash (
                              none     => 'No additional course requesters',                               none     => 'No additional course requesters',
                              domain   => "Any course requester in course's domain",                               domain   => "Any course requester in course's domain",
                              instcode => 'Course requests for official courses ...',                               instcode => 'Course requests for official courses ...',
Line 4265  sub print_coursedefaults { Line 6105  sub print_coursedefaults {
                     if ($checked) {                      if ($checked) {
                         $show = 'block';                          $show = 'block';
                     }                      }
                     $additional = '<div id="cloneinstcode" style="display:'.$show.'" />'.                      $additional = '<div id="cloneinstcode" style="display:'.$show.';" />'.
                                   &mt('Institutional codes for new and cloned course have identical:').                                    &mt('Institutional codes for new and cloned course have identical:').
                                   '<br />';                                    '<br />';
                     foreach my $item (@code_order) {                      foreach my $item (@code_order) {
Line 4292  sub print_coursedefaults { Line 6132  sub print_coursedefaults {
         $itemcount ++;          $itemcount ++;
     } else {      } else {
         $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';          $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
         my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql);          my ($currdefresponder,%defcredits,%curruploadquota,%currcoursequota,
               %deftimeout,%currmysql);
         my $currusecredits = 0;          my $currusecredits = 0;
         my $postsubmitclient = 1;          my $postsubmitclient = 1;
           my $ltiauth = 0;
           my %domexttool;
           my %exttool;
         my @types = ('official','unofficial','community','textbook');          my @types = ('official','unofficial','community','textbook');
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
               if ($settings->{'ltiauth'}) {
                   $ltiauth = 1;
               }
               if (ref($settings->{'domexttool'}) eq 'HASH') {
                   foreach my $type (@types) {
                       if ($settings->{'domexttool'}->{$type}) {
                           $domexttool{$type} = ' checked="checked"';
                       }
                   }
               } else {
                   foreach my $type (@types) {
                       if ($staticdefaults{'domexttool'}) {
                           $domexttool{$type} = ' checked="checked"';
                       }
                   }
               }
               if (ref($settings->{'exttool'}) eq 'HASH') {
                   foreach my $type (@types) {
                       if ($settings->{'exttool'}->{$type}) {
                           $exttool{$type} = ' checked="checked"';
                       }
                   }
               }
             $currdefresponder = $settings->{'anonsurvey_threshold'};              $currdefresponder = $settings->{'anonsurvey_threshold'};
             if (ref($settings->{'uploadquota'}) eq 'HASH') {              if (ref($settings->{'uploadquota'}) eq 'HASH') {
                 foreach my $type (keys(%{$settings->{'uploadquota'}})) {                  foreach my $type (keys(%{$settings->{'uploadquota'}})) {
                     $curruploadquota{$type} = $settings->{'uploadquota'}{$type};                      $curruploadquota{$type} = $settings->{'uploadquota'}{$type};
                 }                  }
             }              }
               if (ref($settings->{'coursequota'}) eq 'HASH') {
                   foreach my $type (keys(%{$settings->{'coursequota'}})) {
                       $currcoursequota{$type} = $settings->{'coursequota'}{$type};
                   }
               }
             if (ref($settings->{'coursecredits'}) eq 'HASH') {              if (ref($settings->{'coursecredits'}) eq 'HASH') {
                 foreach my $type (@types) {                  foreach my $type (@types) {
                     next if ($type eq 'community');                      next if ($type eq 'community');
Line 4348  sub print_coursedefaults { Line 6220  sub print_coursedefaults {
         } else {          } else {
             foreach my $type (@types) {              foreach my $type (@types) {
                 $deftimeout{$type} = $staticdefaults{'postsubmit'};                  $deftimeout{$type} = $staticdefaults{'postsubmit'};
                   if ($staticdefaults{'domexttool'}) {
                       $domexttool{$type} = ' checked="checked"';
                   }
             }              }
         }          }
         if (!$currdefresponder) {          if (!$currdefresponder) {
Line 4359  sub print_coursedefaults { Line 6234  sub print_coursedefaults {
             if ($curruploadquota{$type} eq '') {              if ($curruploadquota{$type} eq '') {
                 $curruploadquota{$type} = $staticdefaults{'uploadquota'};                  $curruploadquota{$type} = $staticdefaults{'uploadquota'};
             }              }
               if ($currcoursequota{$type} eq '') {
                   $currcoursequota{$type} = $staticdefaults{'coursequota'};
               }
         }          }
         $datatable .=          $datatable .=
                 '<tr'.$css_class.'><td><span class="LC_nobreak">'.                  '<tr'.$css_class.'><td><span class="LC_nobreak">'.
Line 4382  sub print_coursedefaults { Line 6260  sub print_coursedefaults {
         }          }
         $datatable .= '</tr></table></td></tr>'."\n";          $datatable .= '</tr></table></td></tr>'."\n";
         $itemcount ++;          $itemcount ++;
           $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
           $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                         $choices{'coursequota'}.
                         '</span></td>'.
                         '<td style="text-align: right" class="LC_right_item">'.
                         '<table><tr>';
           foreach my $type (@types) {
               $datatable .= '<td style="text-align: center">'.&mt($type).'<br />'.
                              '<input type="text" name="coursequota_'.$type.'"'.
                              ' value="'.$currcoursequota{$type}.'" size="5" /></td>';
           }
           $datatable .= '</tr></table></td></tr>'."\n";
           $itemcount ++;
         my $onclick = "toggleDisplay(this.form,'credits');";          my $onclick = "toggleDisplay(this.form,'credits');";
         my $display = 'none';          my $display = 'none';
         if ($currusecredits) {          if ($currusecredits) {
Line 4442  sub print_coursedefaults { Line 6333  sub print_coursedefaults {
         }          }
         $datatable .= '</tr></table></td></tr>'."\n";          $datatable .= '</tr></table></td></tr>'."\n";
         $itemcount ++;          $itemcount ++;
           %defaultchecked = ('ltiauth' => 'off');
           @toggles = ('ltiauth');
           $current = {
                          'ltiauth' => $ltiauth,
                      };
           ($table,$itemcount) =
               &radiobutton_prefs($current,\@toggles,\%defaultchecked,
                                  \%choices,$itemcount,undef,undef,'left');
           $datatable .= $table;
           $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
           $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                         $choices{'domexttool'}.
                         '</span></td>'.
                         '<td style="text-align: right" class="LC_right_item">'.
                         '<table><tr>';
           foreach my $type (@types) {
               $datatable .= '<td style="text-align: left">'.
                             '<span class="LC_nobreak">'.
                             '<input type="checkbox" name="domexttool"'.
                             ' value="'.$type.'"'.$domexttool{$type}.' />'.
                             &mt($type).'</span></td>'."\n";
           }
           $datatable .= '</tr></table></td></tr>'."\n";
           $itemcount ++;
           $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
           $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                         $choices{'exttool'}.
                         '</span></td>'.
                         '<td style="text-align: right" class="LC_right_item">'.
                         '<table><tr>';
           foreach my $type (@types) {
               $datatable .= '<td style="text-align: left">'.
                             '<span class="LC_nobreak">'.
                             '<input type="checkbox" name="exttool"'.
                             ' value="'.$type.'"'.$exttool{$type}.' />'.
                             &mt($type).'</span></td>'."\n";
           }
           $datatable .= '</tr></table></td></tr>'."\n";
     }      }
     $$rowtotal += $itemcount;      $$rowtotal += $itemcount;
     return $datatable;      return $datatable;
Line 4759  sub print_passwords { Line 6687  sub print_passwords {
                 $datatable .= '<span class="LC_nobreak"><label>'.                  $datatable .= '<span class="LC_nobreak"><label>'.
                               '<input type="checkbox" name="passwords_case_sensitive" value="'.                                '<input type="checkbox" name="passwords_case_sensitive" value="'.
                               $item.'"'.$checkedcase.' />'.$usertypes->{$item}.'</label>'.                                $item.'"'.$checkedcase.' />'.$usertypes->{$item}.'</label>'.
                               '<span>&nbsp;&nbsp; ';                                '</span>&nbsp;&nbsp; ';
             }              }
         }          }
         my $checkedcase;          my $checkedcase;
Line 4863  sub print_passwords { Line 6791  sub print_passwords {
                       &mt('(If you use the same account ...  reset a password from this page.)').'</span><br /><br />'.                        &mt('(If you use the same account ...  reset a password from this page.)').'</span><br /><br />'.
                       &mt('Include custom text:');                        &mt('Include custom text:');
         if ($customurl) {          if ($customurl) {
             my $link =  &Apache::loncommon::modal_link($customurl,&mt('Custom text file'),600,500,              my $link =  &Apache::loncommon::modal_link($customurl,&mt('custom text'),600,500,
                                                        undef,undef,undef,undef,'background-color:#ffffff');                                                         undef,undef,undef,undef,'background-color:#ffffff');
             $datatable .= '<span class="LC_nobreak">&nbsp;'.$link.              $datatable .= '<span class="LC_nobreak">&nbsp;'.$link.
                           '<label><input type="checkbox" name="passwords_custom_del"'.                            '<label><input type="checkbox" name="passwords_custom_del"'.
Line 4958  sub print_passwords { Line 6886  sub print_passwords {
             $itemcount ++;              $itemcount ++;
         }          }
     } elsif ($position eq 'lower') {      } elsif ($position eq 'lower') {
         my ($min,$max,%chars,$numsaved);          $datatable .= &password_rules('passwords',\$itemcount,$settings);
         $min = $Apache::lonnet::passwdmin;  
         if (ref($settings) eq 'HASH') {  
             if ($settings->{min}) {  
                 $min = $settings->{min};  
             }  
             if ($settings->{max}) {  
                 $max = $settings->{max};  
             }  
             if (ref($settings->{chars}) eq 'ARRAY') {  
                 map { $chars{$_} = 1; } (@{$settings->{chars}});  
             }  
             if ($settings->{numsaved}) {  
                 $numsaved = $settings->{numsaved};  
             }  
         }  
         my %rulenames = &Apache::lonlocal::texthash(  
                                                      uc => 'At least one upper case letter',  
                                                      lc => 'At least one lower case letter',  
                                                      num => 'At least one number',  
                                                      spec => 'At least one non-alphanumeric',  
                                                    );  
         $css_class = $itemcount%2?' class="LC_odd_row"':'';  
         $datatable .= '<tr'.$css_class.'><td>'.$titles{'min'}.'</td>'.  
                       '<td class="LC_left_item"><span class="LC_nobreak">'.  
                       '<input type="text" name="passwords_min" value="'.$min.'" size="3" '.  
                       'onblur="javascript:warnIntPass(this);" />'.  
                       '<span class="LC_fontsize_small"> '.&mt('(Enter an integer: 7 or larger)').'</span>'.  
                       '</span></td></tr>';  
         $itemcount ++;  
         $css_class = $itemcount%2?' class="LC_odd_row"':'';  
         $datatable .= '<tr'.$css_class.'><td>'.$titles{'max'}.'</td>'.  
                       '<td class="LC_left_item"><span class="LC_nobreak">'.  
                       '<input type="text" name="passwords_max" value="'.$max.'" size="3" '.  
                       'onblur="javascript:warnIntPass(this);" />'.  
                       '<span class="LC_fontsize_small"> '.&mt('(Leave blank for no maximum)').'</span>'.  
                       '</span></td></tr>';  
         $itemcount ++;  
         $css_class = $itemcount%2?' class="LC_odd_row"':'';  
         $datatable .= '<tr'.$css_class.'><td>'.$titles{'chars'}.'<br />'.  
                       '<span class="LC_nobreak LC_fontsize_small">'.&mt('(Leave unchecked if not required)').  
                       '</span></td>';  
         my $numinrow = 2;  
         my @possrules = ('uc','lc','num','spec');  
         $datatable .= '<td class="LC_left_item"><table>';  
         for (my $i=0; $i<@possrules; $i++) {  
             my ($rem,$checked);  
             if ($chars{$possrules[$i]}) {  
                 $checked = ' checked="checked"';  
             }  
             $rem = $i%($numinrow);  
             if ($rem == 0) {  
                 if ($i > 0) {  
                     $datatable .= '</tr>';  
                 }  
                 $datatable .= '<tr>';  
             }  
             $datatable .= '<td><span class="LC_nobreak"><label>'.  
                           '<input type="checkbox" name="passwords_chars" value="'.$possrules[$i].'"'.$checked.' />'.  
                           $rulenames{$possrules[$i]}.'</label></span></td>';  
         }  
         my $rem = @possrules%($numinrow);  
         my $colsleft = $numinrow - $rem;  
         if ($colsleft > 1 ) {  
             $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.  
                           '&nbsp;</td>';  
         } elsif ($colsleft == 1) {  
             $datatable .= '<td class="LC_left_item">&nbsp;</td>';  
         }  
         $datatable .='</table></td></tr>';  
         $itemcount ++;  
         $css_class = $itemcount%2?' class="LC_odd_row"':'';  
         $datatable .= '<tr'.$css_class.'><td>'.$titles{'numsaved'}.'</td>'.  
                       '<td class="LC_left_item"><span class="LC_nobreak">'.  
                       '<input type="text" name="passwords_numsaved" value="'.$numsaved.'" size="3" '.  
                       'onblur="javascript:warnIntPass(this);" />'.  
                       '<span class="LC_fontsize_small"> '.&mt('(Leave blank to not save previous passwords)').'</span>'.  
                       '</span></td></tr>';  
     } else {      } else {
         my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);          my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
         my %ownerchg = (          my %ownerchg = (
Line 5079  sub print_passwords { Line 6930  sub print_passwords {
                     $datatable .= '<span class="LC_nobreak"><label>'.                      $datatable .= '<span class="LC_nobreak"><label>'.
                                   '<input type="checkbox" name="passwords_crsowner_'.$item.'" value="'.                                    '<input type="checkbox" name="passwords_crsowner_'.$item.'" value="'.
                                   $type.'"'.$checked.' />'.$usertypes->{$type}.'</label>'.                                    $type.'"'.$checked.' />'.$usertypes->{$type}.'</label>'.
                                   '<span>&nbsp;&nbsp; ';                                    '</span>&nbsp;&nbsp; ';
                 }                  }
             }              }
             my $checked;              my $checked;
Line 5095  sub print_passwords { Line 6946  sub print_passwords {
     return $datatable;      return $datatable;
 }  }
   
   sub password_rules {
       my ($prefix,$itemcountref,$settings) = @_;
       my ($min,$max,%chars,$numsaved,$numinrow);
       my %titles;
       if ($prefix eq 'passwords') {
           %titles = &Apache::lonlocal::texthash (
               min            => 'Minimum password length',
               max            => 'Maximum password length',
               chars          => 'Required characters',
           );
       } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {
           %titles = &Apache::lonlocal::texthash (
               min            => 'Minimum secret length',
               max            => 'Maximum secret length',
               chars          => 'Required characters',
           );
       }
       $min = $Apache::lonnet::passwdmin;
       my $datatable;
       my $itemcount;
       if (ref($itemcountref)) {
           $itemcount = $$itemcountref;
       }
       if (ref($settings) eq 'HASH') {
           if ($settings->{min}) {
               $min = $settings->{min};
           }
           if ($settings->{max}) {
               $max = $settings->{max};
           }
           if (ref($settings->{chars}) eq 'ARRAY') {
               map { $chars{$_} = 1; } (@{$settings->{chars}});
           }
           if ($prefix eq 'passwords') {
               if ($settings->{numsaved}) {
                   $numsaved = $settings->{numsaved};
               }
           }
       }
       my %rulenames = &Apache::lonlocal::texthash(
                                                    uc => 'At least one upper case letter',
                                                    lc => 'At least one lower case letter',
                                                    num => 'At least one number',
                                                    spec => 'At least one non-alphanumeric',
                                                  );
       my $css_class = $itemcount%2?' class="LC_odd_row"':'';
       $datatable .= '<tr'.$css_class.'><td>'.$titles{'min'}.'</td>'.
                     '<td class="LC_left_item"><span class="LC_nobreak">'.
                     '<input type="text" name="'.$prefix.'_min" value="'.$min.'" size="3" '.
                     'onblur="javascript:warnInt'.$prefix.'(this);" />'.
                     '<span class="LC_fontsize_small"> '.&mt('(Enter an integer: 7 or larger)').'</span>'.
                     '</span></td></tr>';
       $itemcount ++;
       $css_class = $itemcount%2?' class="LC_odd_row"':'';
       $datatable .= '<tr'.$css_class.'><td>'.$titles{'max'}.'</td>'.
                     '<td class="LC_left_item"><span class="LC_nobreak">'.
                     '<input type="text" name="'.$prefix.'_max" value="'.$max.'" size="3" '.
                     'onblur="javascript:warnInt'.$prefix.'(this);" />'.
                     '<span class="LC_fontsize_small"> '.&mt('(Leave blank for no maximum)').'</span>'.
                     '</span></td></tr>';
       $itemcount ++;
       $css_class = $itemcount%2?' class="LC_odd_row"':'';
       $datatable .= '<tr'.$css_class.'><td>'.$titles{'chars'}.'<br />'.
                     '<span class="LC_nobreak LC_fontsize_small">'.&mt('(Leave unchecked if not required)').
                     '</span></td>';
       my $numinrow = 2;
       my @possrules = ('uc','lc','num','spec');
       $datatable .= '<td class="LC_left_item"><table>';
       for (my $i=0; $i<@possrules; $i++) {
           my ($rem,$checked);
           if ($chars{$possrules[$i]}) {
               $checked = ' checked="checked"';
           }
           $rem = $i%($numinrow);
           if ($rem == 0) {
               if ($i > 0) {
                   $datatable .= '</tr>';
               }
               $datatable .= '<tr>';
           }
           $datatable .= '<td><span class="LC_nobreak"><label>'.
                         '<input type="checkbox" name="'.$prefix.'_chars" value="'.$possrules[$i].'"'.$checked.' />'.
                         $rulenames{$possrules[$i]}.'</label></span></td>';
       }
       my $rem = @possrules%($numinrow);
       my $colsleft = $numinrow - $rem;
       if ($colsleft > 1 ) {
           $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                         '&nbsp;</td>';
       } elsif ($colsleft == 1) {
           $datatable .= '<td class="LC_left_item">&nbsp;</td>';
       }
       $datatable .='</table></td></tr>';
       $itemcount ++;
       if ($prefix eq 'passwords') {
           $titles{'numsaved'} = &mt('Number of previous passwords to save and disallow reuse');
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'numsaved'}.'</td>'.
                         '<td class="LC_left_item"><span class="LC_nobreak">'.
                         '<input type="text" name="'.$prefix.'_numsaved" value="'.$numsaved.'" size="3" '.
                         'onblur="javascript:warnInt'.$prefix.'(this);" />'.
                         '<span class="LC_fontsize_small"> '.&mt('(Leave blank to not save previous passwords)').'</span>'.
                         '</span></td></tr>';
           $itemcount ++;
       }
       if (ref($itemcountref)) {
           $$itemcountref += $itemcount;
       }
       return $datatable;
   }
   
   sub print_wafproxy {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my $css_class;
       my $itemcount = 0;
       my $datatable;
       my %servers = &Apache::lonnet::internet_dom_servers($dom);
       my (%othercontrol,%otherdoms,%aliases,%saml,%values,$setdom,$showdom);
       my %lt = &wafproxy_titles();
       foreach my $server (sort(keys(%servers))) {
           my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server});
           next if ($serverhome eq '');
           my $serverdom;
           if ($serverhome ne $server) {
               $serverdom = &Apache::lonnet::host_domain($serverhome);
               if (($serverdom ne '') && (&Apache::lonnet::domain($serverdom) ne '')) {
                   $othercontrol{$server} = $serverdom;
               }
           } else {
               $serverdom = &Apache::lonnet::host_domain($server);
               next if (($serverdom eq '') || (&Apache::lonnet::domain($serverdom) eq ''));
               if ($serverdom ne $dom) {
                   $othercontrol{$server} = $serverdom;
               } else {
                   $setdom = 1;
                   if (ref($settings) eq 'HASH') {
                       if (ref($settings->{'alias'}) eq 'HASH') {
                           $aliases{$dom} = $settings->{'alias'};
                           if ($aliases{$dom} ne '') {
                               $showdom = 1;
                           }
                       }
                       if (ref($settings->{'saml'}) eq 'HASH') {
                           $saml{$dom} = $settings->{'saml'};
                       }
                   }
               }
           }
       }
       if ($setdom) {
           %{$values{$dom}} = ();
           if (ref($settings) eq 'HASH') {
               foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') {
                   $values{$dom}{$item} = $settings->{$item};
               }
           }
       }
       if (keys(%othercontrol)) {
           %otherdoms = reverse(%othercontrol);
           foreach my $domain (keys(%otherdoms)) {
               %{$values{$domain}} = ();
               my %config = &Apache::lonnet::get_dom('configuration',['wafproxy'],$domain);
               if (ref($config{'wafproxy'}) eq 'HASH') {
                   $aliases{$domain} = $config{'wafproxy'}{'alias'};
                   if (exists($config{'wafproxy'}{'saml'})) {
                       $saml{$domain} = $config{'wafproxy'}{'saml'};
                   }
                   foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') {
                       $values{$domain}{$item} = $config{'wafproxy'}{$item};
                   }
               }
           }
       }
       if ($position eq 'top') {
           my %servers = &Apache::lonnet::internet_dom_servers($dom);
           my %aliasinfo;
           foreach my $server (sort(keys(%servers))) {
               $itemcount ++;
               my $dom_in_effect;
               my $aliasrows = '<tr>'.
                               '<td class="LC_left_item" style="vertical-align: baseline;">'.
                               &mt('Hostname').': '.
                               '<span class="LC_nobreak LC_cusr_emph">'.
                               &Apache::lonnet::hostname($server).'</span></td><td>&nbsp;</td>';
               if ($othercontrol{$server}) {
                   $dom_in_effect = $othercontrol{$server};
                   my ($current,$forsaml);
                   if (ref($aliases{$dom_in_effect}) eq 'HASH') {
                       $current = $aliases{$dom_in_effect}{$server};
                   }
                   if (ref($saml{$dom_in_effect}) eq 'HASH') {
                       if ($saml{$dom_in_effect}{$server}) {
                           $forsaml = 1;
                       }
                   }
                   $aliasrows .= '<td class="LC_left_item" style="vertical-align: baseline;">'.
                                 &mt('Alias').':&nbsp';
                   if ($current) {
                       $aliasrows .= $current;
                       if ($forsaml) {
                            $aliasrows .= '&nbsp;('.&mt('also for SSO Auth').')';
                       }
                   } else {
                       $aliasrows .= &mt('None');
                   }
                   $aliasrows .= '&nbsp;<span class="LC_small">('.
                                 &mt('controlled by domain: [_1]',
                                     '<b>'.$dom_in_effect.'</b>').')</span></td>';
               } else {
                   $dom_in_effect = $dom;
                   my ($current,$samlon,$samloff);
                   $samloff = ' checked="checked"';
                   if (ref($aliases{$dom}) eq 'HASH') {
                       if ($aliases{$dom}{$server}) {
                           $current = $aliases{$dom}{$server};
                       }
                   }
                   if (ref($saml{$dom}) eq 'HASH') {
                       if ($saml{$dom}{$server}) {
                           $samlon = $samloff;
                           undef($samloff);
                       }
                   }
                   $aliasrows .= '<td class="LC_left_item" style="vertical-align: baseline;">'.
                                 &mt('Alias').':&nbsp;'.
                                 '<input type="text" name="wafproxy_alias_'.$server.'" '.
                                 'value="'.$current.'" size="30" />'.
                                 ('&nbsp;'x2).'<span class="LC_nobreak">'.
                                 &mt('Alias used for SSO Auth').':&nbsp;<label>'.
                                 '<input type="radio" value="0"'.$samloff.' name="wafproxy_alias_saml_'.$server.'" />'.
                                 &mt('No').'</label>&nbsp;<label>'.
                                 '<input type="radio" value="1"'.$samlon.' name="wafproxy_alias_saml_'.$server.'" />'.
                                 &mt('Yes').'</label></span>'.
                                 '</td>';
               }
               $aliasrows .= '</tr>';
               $aliasinfo{$dom_in_effect} .= $aliasrows;
           }
           if ($aliasinfo{$dom}) {
               my ($onclick,$wafon,$wafoff,$showtable);
               $onclick = ' onclick="javascript:toggleWAF();"';
               $wafoff = ' checked="checked"';
               $showtable = ' style="display:none";';
               if ($showdom) {
                   $wafon = $wafoff;
                   $wafoff = '';
                   $showtable = ' style="display:inline;"';
               }
               $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
               $datatable = '<tr'.$css_class.'>'.
                            '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$dom.'</b>').'<br />'.
                            '<span class="LC_nobreak">'.&mt('WAF in use?').'&nbsp;<label>'.
                            '<input type="radio" name="wafproxy_'.$dom.'" value="1"'.$wafon.$onclick.' />'.
                            &mt('Yes').'</label>'.('&nbsp;'x2).'<label>'.
                            '<input type="radio" name="wafproxy_'.$dom.'" value="0"'.$wafoff.$onclick.' />'.
                            &mt('No').'</label></span></td>'.
                            '<td class="LC_left_item">'.
                            '<table id="wafproxy_table"'.$showtable.'>'.$aliasinfo{$dom}.
                            '</table></td></tr>';
               $itemcount++;
           }
           if (keys(%otherdoms)) {
               foreach my $key (sort(keys(%otherdoms))) {
                   $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
                   $datatable .= '<tr'.$css_class.'>'.
                                 '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$key.'</b>').'</td>'.
                                 '<td class="LC_left_item"><table>'.$aliasinfo{$key}.
                                 '</table></td></tr>';
                   $itemcount++;
               }
           }
       } else {
           my %ip_methods = &remoteip_methods();
           if ($setdom) {
               $itemcount ++;
               $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
               my ($nowafstyle,$wafstyle,$curr_remotip,$currwafdisplay,$vpndircheck,$vpnaliascheck,
                   $currwafvpn,$wafrangestyle,$alltossl,$ssltossl);
               $wafstyle = ' style="display:none;"';
               $nowafstyle = ' style="display:table-row;"';
               $currwafdisplay = ' style="display: none"';
               $wafrangestyle = ' style="display: none"';
               $curr_remotip = 'n';
               $ssltossl = ' checked="checked"';
               if ($showdom) {
                   $wafstyle = ' style="display:table-row;"';
                   $nowafstyle =  ' style="display:none;"';
                   if (keys(%{$values{$dom}})) {
                       if ($values{$dom}{remoteip} =~ /^[nmh]$/) {
                           $curr_remotip = $values{$dom}{remoteip};
                       }
                       if ($curr_remotip eq 'h') {
                           $currwafdisplay = ' style="display:table-row"';
                           $wafrangestyle = ' style="display:inline-block;"';
                       }
                       if ($values{$dom}{'sslopt'}) {
                           $alltossl = ' checked="checked"';
                           $ssltossl = '';
                       }
                   }
                   if (($values{$dom}{'vpnint'} ne '') || ($values{$dom}{'vpnext'} ne '')) {
                       $vpndircheck = ' checked="checked"';
                       $currwafvpn = ' style="display:table-row;"';
                       $wafrangestyle = ' style="display:inline-block;"';
                   } else {
                       $vpnaliascheck = ' checked="checked"';
                       $currwafvpn = ' style="display:none;"';
                   }
               }
               $datatable .= '<tr'.$css_class.' id="nowafproxyrow_'.$dom.'"'.$wafstyle.'>'.
                             '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$dom.'</b>').'</td>'.
                             '<td class="LC_right_item">'.&mt('WAF not in use, nothing to set').'</td>'.
                             '</tr>'.
                             '<tr'.$css_class.' id="wafproxyrow_'.$dom.'"'.$wafstyle.'>'.
                             '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$dom.'</b>').'<br /><br />'.
                             '<div id="wafproxyranges_'.$dom.'">'.&mt('Format for comma separated IP ranges').':<br />'.
                             &mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'<br />'.
                             &mt('Range(s) stored in CIDR notation').'</div></td>'.
                             '<td class="LC_left_item"><table>'.
                             '<tr>'.
                             '<td valign="top">'.$lt{'remoteip'}.':&nbsp;'.
                             '<select name="wafproxy_remoteip" id="wafproxy_remoteip" onchange="javascript:updateWAF();">';
               foreach my $option ('m','h','n') {
                   my $sel;
                   if ($option eq $curr_remotip) {
                      $sel = ' selected="selected"';
                   }
                   $datatable .= '<option value="'.$option.'"'.$sel.'>'.
                                 $ip_methods{$option}.'</option>';
               }
               $datatable .= '</select></td></tr>'."\n".
                             '<tr id="wafproxy_header"'.$currwafdisplay.'><td>'.
                             $lt{'ipheader'}.':&nbsp;'.
                             '<input type="text" value="'.$values{$dom}{'ipheader'}.'" '.
                             'name="wafproxy_ipheader" />'.
                             '</td></tr>'."\n".
                             '<tr id="wafproxy_trust"'.$currwafdisplay.'><td>'.
                             $lt{'trusted'}.':<br />'.
                             '<textarea name="wafproxy_trusted" rows="3" cols="80">'.
                             $values{$dom}{'trusted'}.'</textarea>'.
                             '</td></tr>'."\n".
                             '<tr><td><hr /></td></tr>'."\n".
                             '<tr>'.
                             '<td valign="top">'.$lt{'vpnaccess'}.':<br /><span class="LC_nobreak">'.
                             '<label><input type="radio" name="wafproxy_vpnaccess"'.$vpndircheck.' value="1" onclick="javascript:checkWAF();" />'.
                             $lt{'vpndirect'}.'</label>'.('&nbsp;'x2).
                             '<label><input type="radio" name="wafproxy_vpnaccess"'.$vpnaliascheck.' value="0" onclick="javascript:checkWAF();" />'.
                             $lt{'vpnaliased'}.'</label></span></td></tr>';
               foreach my $item ('vpnint','vpnext') {
                   $datatable .= '<tr id="wafproxy_show_'.$item.'"'.$currwafvpn.'>'.
                                 '<td valign="top">'.$lt{$item}.':<br />'.
                                 '<textarea name="wafproxy_'.$item.'" rows="3" cols="80">'.
                                 $values{$dom}{$item}.'</textarea>'.
                                 '</td></tr>'."\n";
               }
               $datatable .= '<tr><td><hr /></td></tr>'."\n".
                             '<tr>'.
                             '<td valign="top">'.$lt{'sslopt'}.':<br /><span class="LC_nobreak">'.
                             '<label><input type="radio" name="wafproxy_sslopt"'.$alltossl.' value="1" />'.
                             $lt{'alltossl'}.'</label>'.('&nbsp;'x2).
                             '<label><input type="radio" name="wafproxy_sslopt"'.$ssltossl.' value="0" />'.
                             $lt{'ssltossl'}.'</label></span></td></tr>'."\n".
                             '</table></td></tr>';
           }
           if (keys(%otherdoms)) {
               foreach my $domain (sort(keys(%otherdoms))) {
                   $itemcount ++;
                   $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
                   $datatable .= '<tr'.$css_class.'>'.
                                 '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$domain.'</b>').'</td>'.
                                 '<td class="LC_left_item"><table>';
                   foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
                       my $showval = &mt('None');
                       if ($item eq 'ssl') {
                           $showval = $lt{'ssltossl'};
                       }
                       if ($values{$domain}{$item}) {
                           $showval = $values{$domain}{$item};
                           if ($item eq 'ssl') {
                               $showval = $lt{'alltossl'};
                           } elsif ($item eq 'remoteip') {
                               $showval = $ip_methods{$values{$domain}{$item}};
                           }
                       }
                       $datatable .= '<tr>'.
                                     '<td>'.$lt{$item}.':&nbsp;'.$showval.'</td></tr>';
                   }
                   $datatable .= '</table></td></tr>';
               }
           }
       }
       $$rowtotal += $itemcount;
       return $datatable;
   }
   
   sub wafproxy_titles {
       return &Apache::lonlocal::texthash(
                  remoteip   => "Method for determining user's IP",
                  ipheader   => 'Request header containing remote IP',
                  trusted    => 'Trusted IP range(s)',
                  vpnaccess  => 'Access from institutional VPN',
                  vpndirect  => 'via regular hostname (no WAF)',
                  vpnaliased => 'via aliased hostname (WAF)',
                  vpnint     => 'Internal IP Range(s) for VPN sessions',
                  vpnext     => 'IP Range(s) for backend WAF connections',
                  sslopt     => 'Forwarding http/https',
                  alltossl   => 'WAF forwards both http and https requests to https',
                  ssltossl   => 'WAF forwards http requests to http and https to https',
              );
   }
   
   sub remoteip_methods {
       return &Apache::lonlocal::texthash(
                 m => 'Use Apache mod_remoteip',
                 h => 'Use headers parsed by LON-CAPA',
                 n => 'Not in use',
              );
   }
   
 sub print_usersessions {  sub print_usersessions {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my ($css_class,$datatable,%checked,%choices);      my ($css_class,$datatable,%checked,%choices);
Line 5109  sub print_usersessions { Line 7379  sub print_usersessions {
     if ($position eq 'top') {      if ($position eq 'top') {
         if (keys(%serverhomes) > 1) {          if (keys(%serverhomes) > 1) {
             my %spareid = &current_offloads_to($dom,$settings,\%servers);              my %spareid = &current_offloads_to($dom,$settings,\%servers);
             my $curroffloadnow;              my ($curroffloadnow,$curroffloadoth);
             if (ref($settings) eq 'HASH') {              if (ref($settings) eq 'HASH') {
                 if (ref($settings->{'offloadnow'}) eq 'HASH') {                  if (ref($settings->{'offloadnow'}) eq 'HASH') {
                     $curroffloadnow = $settings->{'offloadnow'};                      $curroffloadnow = $settings->{'offloadnow'};
                 }                  }
                   if (ref($settings->{'offloadoth'}) eq 'HASH') {
                       $curroffloadoth = $settings->{'offloadoth'};
                   }
             }              }
             $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,$curroffloadnow,$rowtotal);              my $other_insts = scalar(keys(%by_location));
               $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,
                                         $other_insts,$curroffloadnow,$curroffloadoth,$rowtotal);
         } else {          } else {
             $datatable .= '<tr'.$css_class.'><td colspan="2">'.              $datatable .= '<tr'.$css_class.'><td colspan="2">'.
                           &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.');                            &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.').
                             '</td></tr>';
         }          }
     } else {      } else {
         if (keys(%by_location) == 0) {          if (keys(%by_location) == 0) {
             $datatable .= '<tr'.$css_class.'><td colspan="2">'.              $datatable .= '<tr'.$css_class.'><td colspan="2">'.
                           &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.');                            &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.').
                             '</td></tr>';
         } else {          } else {
             my %lt = &usersession_titles();              my %lt = &usersession_titles();
             my $numinrow = 5;              my $numinrow = 5;
Line 5365  sub current_offloads_to { Line 7642  sub current_offloads_to {
 }  }
   
 sub spares_row {  sub spares_row {
     my ($dom,$servers,$spareid,$serverhomes,$altids,$curroffloadnow,$rowtotal) = @_;      my ($dom,$servers,$spareid,$serverhomes,$altids,$other_insts,
           $curroffloadnow,$curroffloadoth,$rowtotal) = @_;
     my $css_class;      my $css_class;
     my $numinrow = 4;      my $numinrow = 4;
     my $itemcount = 1;      my $itemcount = 1;
Line 5385  sub spares_row { Line 7663  sub spares_row {
                 }                  }
             }              }
             next unless (ref($spareid->{$server}) eq 'HASH');              next unless (ref($spareid->{$server}) eq 'HASH');
             my $checkednow;              my ($checkednow,$checkedoth);
             if (ref($curroffloadnow) eq 'HASH') {              if (ref($curroffloadnow) eq 'HASH') {
                 if ($curroffloadnow->{$server}) {                  if ($curroffloadnow->{$server}) {
                     $checkednow = ' checked="checked"';                      $checkednow = ' checked="checked"';
                 }                  }
             }              }
               if (ref($curroffloadoth) eq 'HASH') {
                   if ($curroffloadoth->{$server}) {
                       $checkedoth = ' checked="checked"';
                   }
               }
             $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';              $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
             $datatable .= '<tr'.$css_class.'>              $datatable .= '<tr'.$css_class.'>
                            <td rowspan="2">                             <td rowspan="2">
Line 5399  sub spares_row { Line 7682  sub spares_row {
                               ,'<b>'.$server.'</b>').'</span><br />'.                                ,'<b>'.$server.'</b>').'</span><br />'.
                           '<span class="LC_nobreak">'."\n".                            '<span class="LC_nobreak">'."\n".
                           '<label><input type="checkbox" name="offloadnow" value="'.$server.'"'.$checkednow.' />'.                            '<label><input type="checkbox" name="offloadnow" value="'.$server.'"'.$checkednow.' />'.
                           '&nbsp;'.&mt('Switch active users on next access').'</label></span>'.                            '&nbsp;'.&mt('Switch any active user on next access').'</label></span>'.
                             "\n";
               if ($other_insts) {
                   $datatable .= '<br />'.
                                 '<span class="LC_nobreak">'."\n".
                             '<label><input type="checkbox" name="offloadoth" value="'.$server.'"'.$checkedoth.' />'.
                             '&nbsp;'.&mt('Switch other institutions on next access').'</label></span>'.
                           "\n";                            "\n";
               }
             my (%current,%canselect);              my (%current,%canselect);
             my @choices =               my @choices = 
                 &possible_newspares($server,$spareid->{$server},$serverhomes,$altids);                  &possible_newspares($server,$spareid->{$server},$serverhomes,$altids);
Line 5826  sub loadbalance_rule_row { Line 8116  sub loadbalance_rule_row {
     }      }
     my $space;      my $space;
     if ($islast && $num == 1) {      if ($islast && $num == 1) {
         $space = '<div display="inline-block">&nbsp;</div>';          $space = '<div style="display:inline-block;">&nbsp;</div>';
     }      }
     my $output =      my $output =
         '<tr class="'.$css_class.'" id="balanceruletr_'.$balnum.'_'.$num.'"><td valign="top">'.$space.          '<tr class="'.$css_class.'" id="balanceruletr_'.$balnum.'_'.$num.'"><td valign="top">'.$space.
Line 5919  sub contact_titles { Line 8209  sub contact_titles {
                    'updatesmail'     => 'E-mail from nightly check of LON-CAPA module integrity/updates',                     'updatesmail'     => 'E-mail from nightly check of LON-CAPA module integrity/updates',
                    'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID',                     'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID',
                    'hostipmail'      => 'E-mail from nightly check of hostname/IP network changes',                     'hostipmail'      => 'E-mail from nightly check of hostname/IP network changes',
                      'errorthreshold'  => 'Error count threshold for status e-mail to admin(s)',
                      'errorsysmail'    => 'Error count threshold for e-mail to developer group',
                      'errorweights'    => 'Weights used to compute error count',
                      'errorexcluded'   => 'Servers with unsent updates excluded from count',
                  );                   );
     my %short_titles = &Apache::lonlocal::texthash (      my %short_titles = &Apache::lonlocal::texthash (
                            adminemail   => 'Admin E-mail address',                             adminemail   => 'Admin E-mail address',
Line 5961  sub tool_titles { Line 8255  sub tool_titles {
                      blog       => 'Blog',                       blog       => 'Blog',
                      webdav     => 'WebDAV',                       webdav     => 'WebDAV',
                      portfolio  => 'Portfolio',                       portfolio  => 'Portfolio',
                        timezone   => 'Can set time zone',
                      official   => 'Official courses (with institutional codes)',                       official   => 'Official courses (with institutional codes)',
                      unofficial => 'Unofficial courses',                       unofficial => 'Unofficial courses',
                      community  => 'Communities',                       community  => 'Communities',
Line 5975  sub courserequest_titles { Line 8270  sub courserequest_titles {
                                    unofficial => 'Unofficial',                                     unofficial => 'Unofficial',
                                    community  => 'Communities',                                     community  => 'Communities',
                                    textbook   => 'Textbook',                                     textbook   => 'Textbook',
                                      lti        => 'LTI Provider',
                                    norequest  => 'Not allowed',                                     norequest  => 'Not allowed',
                                    approval   => 'Approval by Dom. Coord.',                                     approval   => 'Approval by Dom. Coord.',
                                    validate   => 'With validation',                                     validate   => 'With validation',
Line 6088  sub print_usercreation { Line 8384  sub print_usercreation {
         }          }
     } else {      } else {
         my @contexts = ('author','course','domain');          my @contexts = ('author','course','domain');
         my @authtypes = ('int','krb4','krb5','loc');          my @authtypes = ('int','krb4','krb5','loc','lti');
         my %checked;          my %checked;
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if (ref($settings->{'authtypes'}) eq 'HASH') {              if (ref($settings->{'authtypes'}) eq 'HASH') {
Line 6650  sub noninst_users { Line 8946  sub noninst_users {
 sub captcha_choice {  sub captcha_choice {
     my ($context,$settings,$itemcount,$customcss,$rowstyle) = @_;      my ($context,$settings,$itemcount,$customcss,$rowstyle) = @_;
     my ($keyentry,$currpub,$currpriv,%checked,$rowname,$pubtext,$privtext,      my ($keyentry,$currpub,$currpriv,%checked,$rowname,$pubtext,$privtext,
         $vertext,$currver);           $vertext,$currver);
     my %lt = &captcha_phrases();      my %lt = &captcha_phrases();
     $keyentry = 'hidden';      $keyentry = 'hidden';
     my $colspan=2;      my $colspan=2;
Line 6739  sub user_formats_row { Line 9035  sub user_formats_row {
                    'username' => 'new usernames',                     'username' => 'new usernames',
                    'id'       => 'IDs',                     'id'       => 'IDs',
                );                 );
     unless ($type eq 'email') {      unless (($type eq 'email') || ($type eq 'unamemap')) {
         my $css_class = $rowcount%2?' class="LC_odd_row"':'';          my $css_class = $rowcount%2?' class="LC_odd_row"':'';
         $output = '<tr '.$css_class.'>'.          $output = '<tr '.$css_class.'>'.
                   '<td><span class="LC_nobreak">'.                    '<td><span class="LC_nobreak">'.
Line 6794  sub user_formats_row { Line 9090  sub user_formats_row {
     } elsif ($colsleft == 1) {      } elsif ($colsleft == 1) {
         $output .= '<td class="LC_left_item">&nbsp;</td>';          $output .= '<td class="LC_left_item">&nbsp;</td>';
     }      }
     $output .= '</tr></table>';      $output .= '</tr>';
     unless ($type eq 'email') {      unless (($type eq 'email') || ($type eq 'unamemap')) {
         $output .= '</td></tr>';          $output .= '</table></td></tr>';
     }      }
     return $output;      return $output;
 }  }
Line 6832  sub authtype_names { Line 9128  sub authtype_names {
                       krb4   => 'Kerberos 4',                        krb4   => 'Kerberos 4',
                       krb5   => 'Kerberos 5',                        krb5   => 'Kerberos 5',
                       loc    => 'Local',                        loc    => 'Local',
                         lti    => 'LTI',
                   );                    );
     return %lt;      return %lt;
 }  }
Line 6900  sub print_defaults { Line 9197  sub print_defaults {
                           '<td><span class="LC_nobreak">'.$titles->{$item}.                            '<td><span class="LC_nobreak">'.$titles->{$item}.
                           '</span></td><td class="LC_right_item" colspan="3">';                            '</span></td><td class="LC_right_item" colspan="3">';
             if ($item eq 'auth_def') {              if ($item eq 'auth_def') {
                 my @authtypes = ('internal','krb4','krb5','localauth');                  my @authtypes = ('internal','krb4','krb5','localauth','lti');
                 my %shortauth = (                  my %shortauth = (
                                  internal => 'int',                                   internal => 'int',
                                  krb4 => 'krb4',                                   krb4 => 'krb4',
                                  krb5 => 'krb5',                                   krb5 => 'krb5',
                                  localauth  => 'loc'                                   localauth  => 'loc',
                                    lti => 'lti',
                                 );                                  );
                 my %authnames = &authtype_names();                  my %authnames = &authtype_names();
                 foreach my $auth (@authtypes) {                  foreach my $auth (@authtypes) {
Line 6926  sub print_defaults { Line 9224  sub print_defaults {
             } elsif ($item eq 'lang_def') {              } elsif ($item eq 'lang_def') {
                 my $includeempty = 1;                  my $includeempty = 1;
                 $datatable .= &Apache::loncommon::select_language($item,$defaults{$item},$includeempty);                  $datatable .= &Apache::loncommon::select_language($item,$defaults{$item},$includeempty);
             } else {              } elsif ($item eq 'portal_def') {
                 my $size;  
                 if ($item eq 'portal_def') {  
                     $size = ' size="25"';  
                 }  
                 $datatable .= '<input type="text" name="'.$item.'" value="'.                  $datatable .= '<input type="text" name="'.$item.'" value="'.
                               $defaults{$item}.'"'.$size.' />';                                $defaults{$item}.'" size="25" onkeyup="portalExtras(this);" />';
                   my $portalsty = 'none';
                   if ($defaults{$item}) {
                       $portalsty = 'block';
                   }
                   foreach my $field ('email','web') {
                       my $checkedoff = ' checked="checked"';
                       my $checkedon;
                       if ($defaults{$item.'_'.$field}) {
                           $checkedon = $checkedoff;
                           $checkedoff = '';
                       }
                       $datatable .= '<div id="'.$item.'_'.$field.'_div" style="display:'.$portalsty.'">'.
                                 '<span class="LC_nobreak">'.$titles->{$field}.'&nbsp;'.
                                 '<label><input type="radio" name="'.$item.'_'.$field.'" value="1"'.$checkedon.'/>'.&mt('Yes').'</label>'.
                                 ('&nbsp;'x2).
                                 '<label><input type="radio" name="'.$item.'_'.$field.'" value="0"'.$checkedoff.'/>'.&mt('No').'</label>'.
                                 '</div>';
                   }
               } else {
                   $datatable .= '<input type="text" name="'.$item.'" value="'.$defaults{$item}.'" />';
             }              }
             $datatable .= '</td></tr>';              $datatable .= '</td></tr>';
             $rownum ++;              $rownum ++;
         }          }
     } else {      } elsif ($position eq 'middle') {
         my %defaults;          my %defaults;
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) {              if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) {
Line 6961  sub print_defaults { Line 9275  sub print_defaults {
                     $datatable .= '</select>&nbsp;'.&mt('Internal ID:').'&nbsp;<b>'.$item.'</b>&nbsp;'.                      $datatable .= '</select>&nbsp;'.&mt('Internal ID:').'&nbsp;<b>'.$item.'</b>&nbsp;'.
                                   '<input type="checkbox" name="inststatus_delete" value="'.$item.'" />'.                                    '<input type="checkbox" name="inststatus_delete" value="'.$item.'" />'.
                                   &mt('delete').'</span></td>'.                                    &mt('delete').'</span></td>'.
                                   '<td class="LC_left_item"><span class="LC_nobreak">'.&mt('Name displayed:').                                    '<td class="LC_left_item"><span class="LC_nobreak">'.&mt('Name displayed').':'.
                                   '<input type="text" size="20" name="inststatus_title_'.$item.'" value="'.$title.'" />'.                                    '<input type="text" size="20" name="inststatus_title_'.$item.'" value="'.$title.'" />'.
                                   '</span></td></tr>';                                    '</span></td></tr>';
                 }                  }
Line 6981  sub print_defaults { Line 9295  sub print_defaults {
                               '<input type="text" size="10" name="addinststatus" value="" />'.                                '<input type="text" size="10" name="addinststatus" value="" />'.
                               '&nbsp;'.&mt('(new)').                                '&nbsp;'.&mt('(new)').
                               '</span></td><td class="LC_left_item"><span class="LC_nobreak">'.                                '</span></td><td class="LC_left_item"><span class="LC_nobreak">'.
                               &mt('Name displayed:').                                &mt('Name displayed').':'.
                               '<input type="text" size="20" name="addinststatus_title" value="" /></span></td>'.                                '<input type="text" size="20" name="addinststatus_title" value="" /></span></td>'.
                               '</tr>'."\n";                                '</tr>'."\n";
                 $rownum ++;                  $rownum ++;
             }              }
         }          }
       } else {
           my ($unamemaprules,$ruleorder) =
               &Apache::lonnet::inst_userrules($dom,'unamemap');
           $css_class = $rownum%2?' class="LC_odd_row"':'';
           if ((ref($unamemaprules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
               my $numinrow = 2;
               $datatable .= '<tr'.$css_class.'><td>'.&mt('Available conversions').'</td><td><table>'.
                             &user_formats_row('unamemap',$settings,$unamemaprules,
                                               $ruleorder,$numinrow).
                             '</table></td></tr>';
           }
           if ($datatable eq '') {
               $datatable .= '<tr'.$css_class.'><td colspan="2">'.
                             &mt('No rules set for domain in customized localenroll.pm').
                             '</td></tr>';
           }
     }      }
     $$rowtotal += $rownum;      $$rowtotal += $rownum;
     return $datatable;      return $datatable;
Line 7012  sub defaults_titles { Line 9342  sub defaults_titles {
                    'timezone_def'  => 'Default timezone',                     'timezone_def'  => 'Default timezone',
                    'datelocale_def' => 'Default locale for dates',                     'datelocale_def' => 'Default locale for dates',
                    'portal_def'     => 'Portal/Default URL',                     'portal_def'     => 'Portal/Default URL',
                      'email'          => 'Email links use portal URL',
                      'web'            => 'Public web links use portal URL',
                    'intauth_cost'   => 'Encryption cost for bcrypt (positive integer)',                     'intauth_cost'   => 'Encryption cost for bcrypt (positive integer)',
                    'intauth_check'  => 'Check bcrypt cost if authenticated',                     'intauth_check'  => 'Check bcrypt cost if authenticated',
                    'intauth_switch' => 'Existing crypt-based switched to bcrypt on authentication',                     'intauth_switch' => 'Existing crypt-based switched to bcrypt on authentication',
Line 7244  sub legacy_scantronformat { Line 9576  sub legacy_scantronformat {
     my ($url,$error);      my ($url,$error);
     my @statinfo = &Apache::lonnet::stat_file($newurl);      my @statinfo = &Apache::lonnet::stat_file($newurl);
     if ((!@statinfo) || ($statinfo[0] eq 'no_such_dir')) {      if ((!@statinfo) || ($statinfo[0] eq 'no_such_dir')) {
           my $modified = [];
         (my $result,$url) =          (my $result,$url) =
             &publishlogo($r,'copy',$legacyfile,$dom,$confname,'scantron',              &Apache::lonconfigsettings::publishlogo($r,'copy',$legacyfile,$dom,$confname,
                          '','',$newfile);                                                      'scantron','','',$newfile,$modified);
         if ($result ne 'ok') {          if ($result eq 'ok') {
               &update_modify_urls($r,$modified);
           } else {
             $error = &mt("An error occurred publishing the [_1] bubblesheet format file in RES space. Error was: [_2].",$newfile,$result);              $error = &mt("An error occurred publishing the [_1] bubblesheet format file in RES space. Error was: [_2].",$newfile,$result);
         }          }
     }      }
Line 7694  sub serverstatus_pages { Line 10029  sub serverstatus_pages {
 sub defaults_javascript {  sub defaults_javascript {
     my ($settings) = @_;      my ($settings) = @_;
     return unless (ref($settings) eq 'HASH');      return unless (ref($settings) eq 'HASH');
       my $portal_js = <<"ENDPORTAL";
   
   function portalExtras(caller) {
       var x = caller.value;
       var y = new Array('email','web');
       for (var i=0; i<y.length; i++) {
           if (document.getElementById('portal_def_'+y[i]+'_div')) {
               var z = document.getElementById('portal_def_'+y[i]+'_div');
               if (x.length > 0) {
                   z.style.display = 'block';
               } else {
                   z.style.display = 'none';
               }
           }
       }
   }
   ENDPORTAL
     if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) {      if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) {
         my $maxnum = scalar(@{$settings->{'inststatusorder'}});          my $maxnum = scalar(@{$settings->{'inststatusorder'}});
         if ($maxnum eq '') {          if ($maxnum eq '') {
Line 7747  $jstext Line 10099  $jstext
     return;      return;
 }  }
   
   $portal_js
   
   // ]]>
   </script>
   
   ENDSCRIPT
       } else {
   return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   $portal_js
 // ]]>  // ]]>
 </script>  </script>
   
 ENDSCRIPT  ENDSCRIPT
     }      }
       return;
 }  }
   
 sub passwords_javascript {  sub passwords_javascript {
     my %intalert = &Apache::lonlocal::texthash (      my ($prefix) = @_;
         authcheck => 'Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.',      my %intalert;
         authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.',      if ($prefix eq 'passwords') {
         passmin => 'Warning: minimum password length must be a positive integer greater than 6.',          %intalert = &Apache::lonlocal::texthash (
         passmax => 'Warning: maximum password length must be a positive integer (or blank).',              authcheck => 'Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.',
         passexp => 'Warning: days before password expiration must be a positive integer (or blank).',              authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.',
         passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).',              passmin => 'Warning: minimum password length must be a positive integer greater than 6.',
     );              passmax => 'Warning: maximum password length must be a positive integer (or blank).',
               passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).',
           );
       } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {
           %intalert = &Apache::lonlocal::texthash (
               passmin => 'Warning: minimum secret length must be a positive integer greater than 6.',
               passmax => 'Warning: maximum secret length must be a positive integer (or blank).',
           );
       }
     &js_escape(\%intalert);      &js_escape(\%intalert);
     my $defmin = $Apache::lonnet::passwdmin;      my $defmin = $Apache::lonnet::passwdmin;
     my $intauthjs = <<"ENDSCRIPT";      my $intauthjs;
       if ($prefix eq 'passwords') { $intauthjs = <<"ENDSCRIPT";
   
 function warnIntAuth(field) {  function warnIntAuth(field) {
     if (field.name == 'intauth_check') {      if (field.name == 'intauth_check') {
Line 7785  function warnIntAuth(field) { Line 10158  function warnIntAuth(field) {
     return;      return;
 }  }
   
 function warnIntPass(field) {  ENDSCRIPT
   
        }
   
        $intauthjs .= <<"ENDSCRIPT";
   
   function warnInt$prefix(field) {
     field.value.replace(/^\s+/,'');      field.value.replace(/^\s+/,'');
     field.value.replace(/\s+\$/,'');      field.value.replace(/\s+\$/,'');
     var regexdigit=/^\\d+\$/;      var regexdigit=/^\\d+\$/;
     if (field.name == 'passwords_min') {      if (field.name == '${prefix}_min') {
         if (field.value == '') {          if (field.value == '') {
             alert('$intalert{passmin}');              alert('$intalert{passmin}');
             field.value = '$defmin';              field.value = '$defmin';
Line 7809  function warnIntPass(field) { Line 10188  function warnIntPass(field) {
             field.value = '';              field.value = '';
         }          }
         if (field.value != '') {          if (field.value != '') {
             if (field.name == 'passwords_expire') {              if (!regexdigit.test(field.value)) {
                 var regexpposnum=/^\\d+(|\\.\\d*)\$/;                  if (field.name == '${prefix}_max') {
                 if (!regexpposnum.test(field.value)) {                      alert('$intalert{passmax}');
                     alert('$intalert{passexp}');  
                     field.value = '';  
                 } else {                  } else {
                     var expval = parseFloat(field.value);                      if (field.name == '${prefix}_numsaved') {
                     if (expval == 0) {                          alert('$intalert{passnum}');
                         alert('$intalert{passexp}');  
                         field.value = '';  
                     }  
                 }  
             } else {  
                 if (!regexdigit.test(field.value)) {  
                     if (field.name == 'passwords_max') {  
                         alert('$intalert{passmax}');  
                     } else {  
                         if (field.name == 'passwords_numsaved') {  
                             alert('$intalert{passnum}');  
                         }  
                     }                      }
                 }                  }
                 field.value = '';                  field.value = '';
Line 7951  ENDSCRIPT Line 10316  ENDSCRIPT
 sub initialize_categories {  sub initialize_categories {
     my ($itemcount) = @_;      my ($itemcount) = @_;
     my ($datatable,$css_class,$chgstr);      my ($datatable,$css_class,$chgstr);
     my %default_names = (      my %default_names = &Apache::lonlocal::texthash (
                       instcode    => 'Official courses (with institutional codes)',                        instcode    => 'Official courses (with institutional codes)',
                       communities => 'Communities',                        communities => 'Communities',
                         );                          );
Line 7984  sub initialize_categories { Line 10349  sub initialize_categories {
                   .'<option value="0">1</option>'                    .'<option value="0">1</option>'
                   .'<option value="1">2</option>'                    .'<option value="1">2</option>'
                   .'<option value="2" selected="selected">3</option></select>&nbsp;'                    .'<option value="2" selected="selected">3</option></select>&nbsp;'
                   .&mt('Add category').'</span></td><tda<span class="LC_nobreak">>'.&mt('Name:')                    .&mt('Add category').'</span></td><td><span class="LC_nobreak">'.&mt('Name:')
                   .'&nbsp;<input type="text" size="20" name="addcategory_name" value="" /></span>'                    .'&nbsp;<input type="text" size="20" name="addcategory_name" value="" /></span>'
                   .'</td></tr>';                    .'</td></tr>';
     return $datatable;      return $datatable;
Line 8074  sub build_category_rows { Line 10439  sub build_category_rows {
   
 sub modifiable_userdata_row {  sub modifiable_userdata_row {
     my ($context,$item,$settings,$numinrow,$rowcount,$usertypes,$fieldsref,$titlesref,      my ($context,$item,$settings,$numinrow,$rowcount,$usertypes,$fieldsref,$titlesref,
         $rowid,$customcss,$rowstyle) = @_;          $rowid,$customcss,$rowstyle,$itemdesc) = @_;
     my ($role,$rolename,$statustype);      my ($role,$rolename,$statustype);
     $role = $item;      $role = $item;
     if ($context eq 'cancreate') {      if ($context eq 'cancreate') {
Line 8095  sub modifiable_userdata_row { Line 10460  sub modifiable_userdata_row {
         } else {          } else {
             $rolename = $role;              $rolename = $role;
         }          }
       } elsif ($context eq 'lti') {
           $rolename = &mt('Institutional data used (if available)');
     } else {      } else {
         if ($role eq 'cr') {          if ($role eq 'cr') {
             $rolename = &mt('Custom role');              $rolename = &mt('Custom role');
Line 8132  sub modifiable_userdata_row { Line 10499  sub modifiable_userdata_row {
     if ($rowid) {      if ($rowid) {
         $rowid = ' id="'.$rowid.'"';          $rowid = ' id="'.$rowid.'"';
     }      }
   
     $output = '<tr '.$css_class.$rowid.'>'.      $output = '<tr '.$css_class.$rowid.'>'.
               '<td><span class="LC_nobreak">'.$rolename.'</span></td>'.                '<td><span class="LC_nobreak">'.$rolename.'</span></td>'.
               '<td class="LC_left_item" colspan="2"><table>';                '<td class="LC_left_item" colspan="2"><table>';
     my $rem;      my $rem;
     my %checks;      my %checks;
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if (ref($settings->{$context}) eq 'HASH') {          my $hashref;
           if ($context eq 'lti') {
               if (ref($settings) eq 'HASH') {
                   $hashref = $settings->{'instdata'};
               }
           } elsif (ref($settings->{$context}) eq 'HASH') {
             if (ref($settings->{$context}->{$role}) eq 'HASH') {              if (ref($settings->{$context}->{$role}) eq 'HASH') {
                 my $hashref = $settings->{$context}->{$role};                  $hashref = $settings->{'lti_instdata'};
                 if ($role eq 'emailusername') {              }
                     if ($statustype) {              if ($role eq 'emailusername') {
                         if (ref($settings->{$context}->{$role}->{$statustype}) eq 'HASH') {                  if ($statustype) {
                             $hashref = $settings->{$context}->{$role}->{$statustype};                      if (ref($settings->{$context}->{$role}->{$statustype}) eq 'HASH') {
                             if (ref($hashref) eq 'HASH') {                           $hashref = $settings->{$context}->{$role}->{$statustype};
                                 foreach my $field (@fields) {  
                                     if ($hashref->{$field}) {  
                                         $checks{$field} = $hashref->{$field};  
                                     }  
                                 }  
                             }  
                         }  
                     }                      }
                 } else {                  }
                     if (ref($hashref) eq 'HASH') {              }
                         foreach my $field (@fields) {          }
                             if ($hashref->{$field}) {          if (ref($hashref) eq 'HASH') {
                                 $checks{$field} = ' checked="checked" ';              foreach my $field (@fields) {
                             }                  if ($hashref->{$field}) {
                         }                      if ($role eq 'emailusername') {
                           $checks{$field} = $hashref->{$field};
                       } else {
                           $checks{$field} = ' checked="checked" ';
                     }                      }
                 }                  }
             }              }
         }          }
     }      }
   
     my $total = scalar(@fields);      my $total = scalar(@fields);
     for (my $i=0; $i<$total; $i++) {      for (my $i=0; $i<$total; $i++) {
         $rem = $i%($numinrow);          $rem = $i%($numinrow);
Line 8181  sub modifiable_userdata_row { Line 10547  sub modifiable_userdata_row {
         unless ($role eq 'emailusername') {          unless ($role eq 'emailusername') {
             if (exists($checks{$fields[$i]})) {              if (exists($checks{$fields[$i]})) {
                 $check = $checks{$fields[$i]};                  $check = $checks{$fields[$i]};
             } else {              } elsif ($context ne 'lti') {
                 if ($role eq 'st') {                  if ($role eq 'st') {
                     if (ref($settings) ne 'HASH') {                      if (ref($settings) ne 'HASH') {
                         $check = ' checked="checked" ';                           $check = ' checked="checked" '; 
Line 8191  sub modifiable_userdata_row { Line 10557  sub modifiable_userdata_row {
         }          }
         $output .= '<td class="LC_left_item">'.          $output .= '<td class="LC_left_item">'.
                    '<span class="LC_nobreak">';                     '<span class="LC_nobreak">';
           my $prefix = 'canmodify';
         if ($role eq 'emailusername') {          if ($role eq 'emailusername') {
             unless ($checks{$fields[$i]} =~ /^(required|optional)$/) {              unless ($checks{$fields[$i]} =~ /^(required|optional)$/) {
                 $checks{$fields[$i]} = 'omit';                  $checks{$fields[$i]} = 'omit';
Line 8201  sub modifiable_userdata_row { Line 10568  sub modifiable_userdata_row {
                     $checked='checked="checked" ';                      $checked='checked="checked" ';
                 }                  }
                 $output .= '<label>'.                  $output .= '<label>'.
                            '<input type="radio" name="canmodify_'.$item.'_'.$fields[$i].'" value="'.$option.'" '.$checked.'/>'.                             '<input type="radio" name="'.$prefix.'_'.$item.'_'.$fields[$i].'" value="'.$option.'" '.$checked.'/>'.
                            &mt($option).'</label>'.('&nbsp;' x2);                             &mt($option).'</label>'.('&nbsp;' x2);
             }              }
             $output .= '<i>'.$fieldtitles{$fields[$i]}.'</i>';              $output .= '<i>'.$fieldtitles{$fields[$i]}.'</i>';
         } else {          } else {
               if ($context eq 'lti') {
                   $prefix = 'lti';
               }
             $output .= '<label>'.              $output .= '<label>'.
                        '<input type="checkbox" name="canmodify_'.$role.'" '.                         '<input type="checkbox" name="'.$prefix.'_'.$role.'" '.
                        'value="'.$fields[$i].'"'.$check.'/>'.$fieldtitles{$fields[$i]}.                         'value="'.$fields[$i].'"'.$check.'/>'.$fieldtitles{$fields[$i]}.
                        '</label>';                         '</label>';
         }          }
Line 8294  sub insttypes_row { Line 10664  sub insttypes_row {
                 $output .= '<td class="LC_left_item">'.                  $output .= '<td class="LC_left_item">'.
                            '<span class="LC_nobreak"><label>'.                             '<span class="LC_nobreak"><label>'.
                            '<input type="checkbox" name="'.$context.'" '.                             '<input type="checkbox" name="'.$context.'" '.
                            'value="'.$types->[$i].'"'.$check.$onclick.'/>'.                             'value="'.$types->[$i].'"'.$check.$onclick.' />'.
                            $usertypes->{$types->[$i]}.'</label></span></td>';                             $usertypes->{$types->[$i]}.'</label></span></td>';
             }              }
         }          }
Line 8409  sub usertype_update_row { Line 10779  sub usertype_update_row {
 sub modify_login {  sub modify_login {
     my ($r,$dom,$confname,$lastactref,%domconfig) = @_;      my ($r,$dom,$confname,$lastactref,%domconfig) = @_;
     my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl,      my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl,
         %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon);          %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon,
           %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso);
     %title = ( coursecatalog => 'Display course catalog',      %title = ( coursecatalog => 'Display course catalog',
                adminmail => 'Display administrator E-mail address',                 adminmail => 'Display administrator E-mail address',
                helpdesk  => 'Display "Contact Helpdesk" link',                 helpdesk  => 'Display "Contact Helpdesk" link',
                newuser => 'Link for visitors to create a user account',                 newuser => 'Link for visitors to create a user account',
                loginheader => 'Log-in box header');                 loginheader => 'Log-in box header',
                  saml => 'Dual SSO and non-SSO login');
     @offon = ('off','on');      @offon = ('off','on');
     if (ref($domconfig{login}) eq 'HASH') {      if (ref($domconfig{login}) eq 'HASH') {
         if (ref($domconfig{login}{loginvia}) eq 'HASH') {          if (ref($domconfig{login}{loginvia}) eq 'HASH') {
Line 8422  sub modify_login { Line 10794  sub modify_login {
                 $curr_loginvia{$lonhost} = $domconfig{login}{loginvia}{$lonhost};                  $curr_loginvia{$lonhost} = $domconfig{login}{loginvia}{$lonhost};
             }              }
         }          }
           if (ref($domconfig{login}{'saml'}) eq 'HASH') {
               foreach my $lonhost (keys(%{$domconfig{login}{'saml'}})) {
                   if (ref($domconfig{login}{'saml'}{$lonhost}) eq 'HASH') {
                       $currsaml{$lonhost} = $domconfig{login}{'saml'}{$lonhost};
                       $saml{$lonhost} = 1;
                       $samltext{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'text'};
                       $samlurl{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'url'};
                       $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'};
                       $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'};
                       $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'};
                       $samlwindow{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'window'};
                       $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'};
                   }
               }
           }
     }      }
     ($errors,%colchanges) = &modify_colors($r,$dom,$confname,['login'],      ($errors,%colchanges) = &modify_colors($r,$dom,$confname,['login'],
                                            \%domconfig,\%loginhash);                                             \%domconfig,\%loginhash);
Line 8560  sub modify_login { Line 10947  sub modify_login {
                 if ($addedfile ne '') {                  if ($addedfile ne '') {
                     push(@allnew,$addedfile);                      push(@allnew,$addedfile);
                 }                  }
                   my $modified = [];
                 foreach my $lang (@allnew) {                  foreach my $lang (@allnew) {
                     my $formelem = 'loginhelpurl_'.$lang;                      my $formelem = 'loginhelpurl_'.$lang;
                     if ($lang eq $env{'form.loginhelpurl_add_lang'}) {                      if ($lang eq $env{'form.loginhelpurl_add_lang'}) {
                         $formelem = 'loginhelpurl_add_file';                          $formelem = 'loginhelpurl_add_file';
                     }                      }
                     (my $result,$newurl{$lang}) = &publishlogo($r,'upload',$formelem,$dom,$confname,                      (my $result,$newurl{$lang}) =
                                                                "help/$lang",'','',$newfile{$lang});                          &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname,
                                                                   "help/$lang",'','',$newfile{$lang},
                                                                   $modified);
                     if ($result eq 'ok') {                      if ($result eq 'ok') {
                         $loginhash{'login'}{'helpurl'}{$lang} = $newurl{$lang};                          $loginhash{'login'}{'helpurl'}{$lang} = $newurl{$lang};
                         $changes{'helpurl'}{$lang} = 1;                          $changes{'helpurl'}{$lang} = 1;
Line 8579  sub modify_login { Line 10969  sub modify_login {
                         }                          }
                     }                      }
                 }                  }
                   &update_modify_urls($r,$modified);
             } else {              } else {
                 $error = &mt("Upload of custom log-in help file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2].  Error was: [_3].",$confname,$dom,$author_ok);                  $error = &mt("Upload of custom log-in help file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2].  Error was: [_3].",$confname,$dom,$author_ok);
             }              }
Line 8636  sub modify_login { Line 11027  sub modify_login {
             if ($switchserver) {              if ($switchserver) {
                 $error = &mt("Upload of custom markup is not permitted to this server: [_1]",$switchserver);                  $error = &mt("Upload of custom markup is not permitted to this server: [_1]",$switchserver);
             } elsif ($author_ok eq 'ok') {              } elsif ($author_ok eq 'ok') {
                   my $modified = [];
                 foreach my $lonhost (@newhosts) {                  foreach my $lonhost (@newhosts) {
                     my $formelem = 'loginheadtag_'.$lonhost;                      my $formelem = 'loginheadtag_'.$lonhost;
                     (my $result,$newheadtagurls{$lonhost}) = &publishlogo($r,'upload',$formelem,$dom,$confname,                      (my $result,$newheadtagurls{$lonhost}) =
                                                                           "login/headtag/$lonhost",'','',                          &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname,
                                                                           $env{'form.loginheadtag_'.$lonhost.'.filename'});                                                                  "login/headtag/$lonhost",'','',
                                                                   $env{'form.loginheadtag_'.$lonhost.'.filename'},
                                                                   $modified);
                     if ($result eq 'ok') {                      if ($result eq 'ok') {
                         $loginhash{'login'}{'headtag'}{$lonhost}{'url'} = $newheadtagurls{$lonhost};                          $loginhash{'login'}{'headtag'}{$lonhost}{'url'} = $newheadtagurls{$lonhost};
                         $changes{'headtag'}{$lonhost} = 1;                          $changes{'headtag'}{$lonhost} = 1;
Line 8657  sub modify_login { Line 11051  sub modify_login {
                         }                          }
                     }                      }
                 }                  }
                   &update_modify_urls($r,$modified);
             } else {              } else {
                 $error = &mt("Upload of custom markup file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2].  Error was: [_3].",$confname,$dom,$author_ok);                  $error = &mt("Upload of custom markup file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2].  Error was: [_3].",$confname,$dom,$author_ok);
             }              }
Line 8668  sub modify_login { Line 11063  sub modify_login {
             $errors .= '<li><span class="LC_error">'.$error.'</span></li>';              $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
         }          }
     }      }
       my @delsamlimg = &Apache::loncommon::get_env_multiple('form.saml_img_del');
       my @newsamlimgs;
       foreach my $lonhost (keys(%domservers)) {
           if ($env{'form.saml_'.$lonhost}) {
               if ($env{'form.saml_img_'.$lonhost.'.filename'}) {
                   push(@newsamlimgs,$lonhost);
               }
               foreach my $item ('text','alt','url','title','window','notsso') {
                   $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g;
               }
               if ($saml{$lonhost}) {
                   if ($env{'form.saml_window_'.$lonhost} ne '1') {
                       $env{'form.saml_window_'.$lonhost} = '';
                   }
                   if (grep(/^\Q$lonhost\E$/,@delsamlimg)) {
   #FIXME Need to obsolete published image
                       delete($currsaml{$lonhost}{'img'});
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_alt_'.$lonhost} ne $samlalt{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_text_'.$lonhost} ne $samltext{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_url_'.$lonhost} ne $samlurl{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_window_'.$lonhost} ne $samlwindow{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_notsso_'.$lonhost} ne $samlnotsso{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
               } else {
                   $changes{'saml'}{$lonhost} = 1;
               }
               foreach my $item ('text','alt','url','title','window','notsso') {
                   $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost};
               }
           } else {
               if ($saml{$lonhost}) {
                   $changes{'saml'}{$lonhost} = 1;
                   delete($currsaml{$lonhost});
               }
           }
       }
       foreach my $posshost (keys(%currsaml)) {
           unless (exists($domservers{$posshost})) {
               delete($currsaml{$posshost});
           }
       }
       %{$loginhash{'login'}{'saml'}} = %currsaml;
       if (@newsamlimgs) {
           my $error;
           my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
           if ($configuserok eq 'ok') {
               if ($switchserver) {
                   $error = &mt("Upload of SSO Button Image is not permitted to this server: [_1].",$switchserver);
               } elsif ($author_ok eq 'ok') {
                   my $modified = [];
                   foreach my $lonhost (@newsamlimgs) {
                       my $formelem = 'saml_img_'.$lonhost;
                       my ($result,$imgurl) =
                           &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname,
                                                                   "login/saml/$lonhost",'','',
                                                                   $env{'form.saml_img_'.$lonhost.'.filename'},
                                                                   $modified);
                       if ($result eq 'ok') {
                           $currsaml{$lonhost}{'img'} = $imgurl;
                           $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl;
                           $changes{'saml'}{$lonhost} = 1;
                       } else {
                           my $puberror = &mt("Upload of SSO button image failed for [_1] because an error occurred publishing the file in RES space. Error was: [_2].",
                                              $lonhost,$result);
                           $errors .= '<li><span class="LC_error">'.$puberror.'</span></li>';
                       }
                   }
                   &update_modify_urls($r,$modified);
               } else {
                   $error = &mt("Upload of SSO button image file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2].  Error was: [_3].",$confname,$dom,$author_ok);
               }
           } else {
               $error = &mt("Upload of SSO button image file(s) failed because a Domain Configuration user ([_1]) could not be created in domain: [_2].  Error was: [_3].",$confname,$dom,$configuserok);
           }
           if ($error) {
               &Apache::lonnet::logthis($error);
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       }
     &process_captcha('login',\%changes,$loginhash{'login'},$domconfig{'login'});      &process_captcha('login',\%changes,$loginhash{'login'},$domconfig{'login'});
   
     my $defaulthelpfile = '/adm/loginproblems.html';      my $defaulthelpfile = '/adm/loginproblems.html';
Line 8708  sub modify_login { Line 11196  sub modify_login {
         }          }
         if (keys(%changes) > 0 || $colchgtext) {          if (keys(%changes) > 0 || $colchgtext) {
             &Apache::loncommon::devalidate_domconfig_cache($dom);              &Apache::loncommon::devalidate_domconfig_cache($dom);
               if (exists($changes{'saml'})) {
                   my $hostid_in_use;
                   my @hosts = &Apache::lonnet::current_machine_ids();
                   if (@hosts > 1) {
                       foreach my $hostid (@hosts) {
                           if (&Apache::lonnet::host_domain($hostid) eq $dom) {
                               $hostid_in_use = $hostid;
                               last;
                           }
                       }
                   } else {
                       $hostid_in_use = $r->dir_config('lonHostID');
                   }
                   if (($hostid_in_use) &&
                       (&Apache::lonnet::host_domain($hostid_in_use) eq $dom)) {
                       &Apache::lonnet::devalidate_cache_new('samllanding',$hostid_in_use);
                   }
                   if (ref($lastactref) eq 'HASH') {
                       if (ref($changes{'saml'}) eq 'HASH') {
                           my %updates;
                           map { $updates{$_} = 1; } keys(%{$changes{'saml'}});
                           $lastactref->{'samllanding'} = \%updates;
                       }
                   }
               }
             if (ref($lastactref) eq 'HASH') {              if (ref($lastactref) eq 'HASH') {
                 $lastactref->{'domainconfig'} = 1;                  $lastactref->{'domainconfig'} = 1;
             }              }
Line 8787  sub modify_login { Line 11300  sub modify_login {
                             }                              }
                         }                          }
                     }                      }
                   } elsif ($item eq 'saml') {
                       if (ref($changes{$item}) eq 'HASH') {
                           my %notlt = (
                                          text   => 'Text for log-in by SSO',
                                          img    => 'SSO button image',
                                          alt    => 'Alt text for button image',
                                          url    => 'SSO URL',
                                          title  => 'Tooltip for SSO link',
                                          window => 'Pop-up window if iframe',
                                          notsso => 'Text for non-SSO log-in',
                                       );
                           foreach my $lonhost (sort(keys(%{$changes{$item}}))) {
                               if (ref($currsaml{$lonhost}) eq 'HASH') {
                                   $resulttext .= '<li>'.&mt("$title{$item} in use for [_1]","<b>$lonhost</b>").
                                                  '<ul>';
                                   foreach my $key ('text','img','alt','url','title','window','notsso') {
                                       if ($currsaml{$lonhost}{$key} eq '') {
                                           $resulttext .= '<li>'.&mt("$notlt{$key} not in use").'</li>';
                                       } else {
                                           my $value = "'$currsaml{$lonhost}{$key}'";
                                           if ($key eq 'img') {
                                               $value = '<img src="'.$currsaml{$lonhost}{$key}.'" />';
                                           } elsif ($key eq 'window') {
                                               $value = 'On';
                                           }
                                           $resulttext .= '<li>'.&mt("$notlt{$key} set to: [_1]",
                                                                     $value).'</li>';
                                       }
                                   }
                                   $resulttext .= '</ul></li>';
                               } else {
                                   $resulttext .= '<li>'.&mt("$title{$item} not in use for [_1]",$lonhost).'</li>';
                               }
                           }
                       }
                 } elsif ($item eq 'captcha') {                  } elsif ($item eq 'captcha') {
                     if (ref($loginhash{'login'}) eq 'HASH') {                      if (ref($loginhash{'login'}) eq 'HASH') {
                         my $chgtxt;                          my $chgtxt;
Line 8889  sub color_font_choices { Line 11437  sub color_font_choices {
     return %choices;      return %choices;
 }  }
   
   sub modify_ipaccess {
       my ($dom,$lastactref,%domconfig) = @_;
       my (@allpos,%changes,%confhash,$errors,$resulttext);
       my (@items,%deletions,%itemids,@warnings);
       my ($typeorder,$types) = &commblocktype_text();
       if ($env{'form.ipaccess_add'}) {
           my $name = $env{'form.ipaccess_name_add'};
           my ($newid,$error) = &get_ipaccess_id($dom,$name);
           if ($newid) {
               $itemids{'add'} = $newid;
               push(@items,'add');
               $changes{$newid} = 1;
           } else {
               $error = &mt('Failed to acquire unique ID for new IP access control item');
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       }
       if (ref($domconfig{'ipaccess'}) eq 'HASH') {
           my @todelete = &Apache::loncommon::get_env_multiple('form.ipaccess_del');
           if (@todelete) {
               map { $deletions{$_} = 1; } @todelete;
           }
           my $maxnum = $env{'form.ipaccess_maxnum'};
           for (my $i=0; $i<$maxnum; $i++) {
               my $itemid = $env{'form.ipaccess_id_'.$i};
               $itemid =~ s/\D+//g;
               if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                   if ($deletions{$itemid}) {
                       $changes{$itemid} = $domconfig{'ipaccess'}{$itemid}{'name'};
                   } else {
                       push(@items,$i);
                       $itemids{$i} = $itemid;
                   }
               }
           }
       }
       foreach my $idx (@items) {
           my $itemid = $itemids{$idx};
           next unless ($itemid);
           my %current;
           unless ($idx eq 'add') {
               if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                   %current = %{$domconfig{'ipaccess'}{$itemid}};
               }
           }
           my $position = $env{'form.ipaccess_pos_'.$itemid};
           $position =~ s/\D+//g;
           if ($position ne '') {
               $allpos[$position] = $itemid;
           }
           my $name = $env{'form.ipaccess_name_'.$idx};
           $name =~ s/^\s+|\s+$//g;
           $confhash{$itemid}{'name'} = $name;
           my $possrange = $env{'form.ipaccess_range_'.$idx};
           $possrange =~ s/^\s+|\s+$//g;
           unless ($possrange eq '') {
               $possrange =~ s/[\r\n]+/\s/g;
               $possrange =~ s/\s*-\s*/-/g;
               $possrange =~ s/\s+/,/g;
               $possrange =~ s/,+/,/g;
               if ($possrange ne '') {
                   my (@ok,$count);
                   $count = 0;
                   foreach my $poss (split(/\,/,$possrange)) {
                       $count ++;
                       $poss = &validate_ip_pattern($poss);
                       if ($poss ne '') {
                           push(@ok,$poss);
                       }
                   }
                   my $diff = $count - scalar(@ok);
                   if ($diff) {
                       $errors .= '<li><span class="LC_error">'.
                                  &mt('[quant,_1,IP] invalid and excluded from saved value for IP range(s) for [_2]',
                                      $diff,$name).
                                  '</span></li>';
                   }
                   if (@ok) {
                       my @cidr_list;
                       foreach my $item (@ok) {
                           @cidr_list = &Net::CIDR::cidradd($item,@cidr_list);
                       }
                       $confhash{$itemid}{'ip'} = join(',',@cidr_list);
                   }
               }
           }
           foreach my $field ('name','ip') {
               unless (($idx eq 'add') || ($changes{$itemid})) {
                   if ($current{$field} ne $confhash{$itemid}{$field}) {
                       $changes{$itemid} = 1;
                       last;
                   }
               }
           }
           $confhash{$itemid}{'commblocks'} = {};
   
           my %commblocks;
           map { $commblocks{$_} = 1; } &Apache::loncommon::get_env_multiple('form.ipaccess_block_'.$idx);
           foreach my $type (@{$typeorder}) {
               if ($commblocks{$type}) {
                   $confhash{$itemid}{'commblocks'}{$type} = 'on';
               }
               unless (($idx eq 'add') || ($changes{$itemid})) {
                   if (ref($current{'commblocks'}) eq 'HASH') {
                       if ($confhash{$itemid}{'commblocks'}{$type} ne $current{'commblocks'}{$type}) {
                           $changes{$itemid} = 1;
                       }
                   } elsif ($confhash{$itemid}{'commblocks'}{$type}) {
                       $changes{$itemid} = 1;
                   }
               }
           }
           $confhash{$itemid}{'courses'} = {};
           my %crsdeletions;
           my @delcrs = &Apache::loncommon::get_env_multiple('form.ipaccess_course_delete_'.$idx);
           if (@delcrs) {
               map { $crsdeletions{$_} = 1; } @delcrs;
           }
           if (ref($current{'courses'}) eq 'HASH') {
               foreach my $cid (sort(keys(%{$current{'courses'}}))) {
                   if ($crsdeletions{$cid}) {
                       $changes{$itemid} = 1;
                   } else {
                       $confhash{$itemid}{'courses'}{$cid} = 1;
                   }
               }
           }
           $env{'form.ipaccess_cnum_'.$idx} =~ s/^\s+|\s+$//g;
           $env{'form.ipaccess_cdom_'.$idx} =~ s/^\s+|\s+$//g;
           if (($env{'form.ipaccess_cnum_'.$idx} =~ /^$match_courseid$/) &&
               ($env{'form.ipaccess_cdom_'.$idx} =~ /^$match_domain$/)) {
               if (&Apache::lonnet::homeserver($env{'form.ipaccess_cnum_'.$idx},
                                               $env{'form.ipaccess_cdom_'.$idx}) eq 'no_host') {
                   $errors .= '<li><span class="LC_error">'.
                              &mt('Invalid courseID [_1] omitted from list of allowed courses',
                                  $env{'form.ipaccess_cdom_'.$idx}.'_'.$env{'form.ipaccess_cnum_'.$idx}).
                              '</span></li>';
               } else {
                   $confhash{$itemid}{'courses'}{$env{'form.ipaccess_cdom_'.$idx}.'_'.$env{'form.ipaccess_cnum_'.$idx}} = 1;
                   $changes{$itemid} = 1;
               }
           }
       }
       if (@allpos > 0) {
           my $idx = 0;
           foreach my $itemid (@allpos) {
               if ($itemid ne '') {
                   $confhash{$itemid}{'order'} = $idx;
                   unless ($changes{$itemid}) {
                       if (ref($domconfig{'ipaccess'}) eq 'HASH') {
                           if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                               if ($domconfig{'ipaccess'}{$itemid}{'order'} ne $idx) {
                                   $changes{$itemid} = 1;
                               }
                           }
                       }
                   }
                   $idx ++;
               }
           }
       }
       if (keys(%changes)) {
           my %defaultshash = (
                                 ipaccess => \%confhash,
                              );
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               my $cachetime = 1800;
               &Apache::lonnet::do_cache_new('ipaccess',$dom,\%confhash,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'ipaccess'} = 1;
               }
               $resulttext = &mt('Changes made:').'<ul>';
               my %bynum;
               foreach my $itemid (sort(keys(%changes))) {
                   if (ref($confhash{$itemid}) eq 'HASH') {
                       my $position = $confhash{$itemid}{'order'};
                       if ($position =~ /^\d+$/) {
                           $bynum{$position} = $itemid;
                       }
                   }
               }
               if (keys(%deletions)) {
                   foreach my $itemid (sort { $a <=> $b } keys(%deletions)) {
                       $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';
                   }
               }
               foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                   my $itemid = $bynum{$pos};
                   if (ref($confhash{$itemid}) eq 'HASH') {
                       $resulttext .= '<li><b>'.$confhash{$itemid}{'name'}.'</b><ul>';
                       my $position = $pos + 1;
                       $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                       if ($confhash{$itemid}{'ip'} eq '') {
                           $resulttext .= '<li>'.&mt('No IP Range(s) set').'</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('IP Range(s): [_1]',$confhash{$itemid}{'ip'}).'</li>';
                       }
                       if (keys(%{$confhash{$itemid}{'commblocks'}})) {
                           $resulttext .= '<li>'.&mt('Functionality Blocked: [_1]',
                                                     join(', ', map { $types->{$_}; } sort(keys(%{$confhash{$itemid}{'commblocks'}})))).
                                          '</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('No functionality blocked').'</li>';
                       }
                       if (keys(%{$confhash{$itemid}{'courses'}})) {
                           my @courses;
                           foreach my $cid (sort(keys(%{$confhash{$itemid}{'courses'}}))) {
                               my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1});
                               push(@courses,$courseinfo{'description'}.' ('.$cid.')');
                           }
                           $resulttext .= '<li>'.&mt('Courses/Communities allowed').':<ul><li>'.
                                                join('</li><li>',@courses).'</li></ul>';
                       } else {
                           $resulttext .= '<li>'.&mt('No courses allowed').'</li>';
                       }
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
           } else {
               $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
           }
       } else {
           $resulttext = &mt('No changes made');
       }
       if ($errors) {
           $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul></p>';
       }
       return $resulttext;
   }
   
   sub get_ipaccess_id {
       my ($domain,$location) = @_;
       # get lock on ipaccess db
       my $lockhash = {
                         lock => $env{'user.name'}.
                                 ':'.$env{'user.domain'},
                      };
       my $tries = 0;
       my $gotlock = &Apache::lonnet::newput_dom('ipaccess',$lockhash,$domain);
       my ($id,$error);
   
       while (($gotlock ne 'ok') && ($tries<10)) {
           $tries ++;
           sleep (0.1);
           $gotlock = &Apache::lonnet::newput_dom('ipaccess',$lockhash,$domain);
       }
       if ($gotlock eq 'ok') {
           my %currids = &Apache::lonnet::dump_dom('ipaccess',$domain);
           if ($currids{'lock'}) {
               delete($currids{'lock'});
               if (keys(%currids)) {
                   my @curr = sort { $a <=> $b } keys(%currids);
                   if ($curr[-1] =~ /^\d+$/) {
                       $id = 1 + $curr[-1];
                   }
               } else {
                   $id = 1;
               }
               if ($id) {
                   unless (&Apache::lonnet::newput_dom('ipaccess',{ $id => $location },$domain) eq 'ok') {
                       $error = 'nostore';
                   }
               } else {
                   $error = 'nonumber';
               }
           }
           my $dellockoutcome = &Apache::lonnet::del_dom('ipaccess',['lock'],$domain);
       } else {
           $error = 'nolock';
       }
       return ($id,$error);
   }
   
 sub modify_rolecolors {  sub modify_rolecolors {
     my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_;      my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_;
     my ($resulttext,%rolehash);      my ($resulttext,%rolehash);
Line 8996  sub modify_colors { Line 11821  sub modify_colors {
             $domconfig->{$role} = {};              $domconfig->{$role} = {};
         }          }
         foreach my $img (@images) {          foreach my $img (@images) {
             if (($role eq 'login') && (($img eq 'img') || ($img eq 'logo'))) {                if ($role eq 'login') {
                 if (defined($env{'form.login_showlogo_'.$img})) {                  if (($img eq 'img') || ($img eq 'logo')) {  
                     $confhash->{$role}{'showlogo'}{$img} = 1;                      if (defined($env{'form.login_showlogo_'.$img})) {
                 } else {                           $confhash->{$role}{'showlogo'}{$img} = 1;
                     $confhash->{$role}{'showlogo'}{$img} = 0;                      } else { 
                           $confhash->{$role}{'showlogo'}{$img} = 0;
                       }
                 }                  }
             }                   if ($env{'form.login_alt_'.$img} ne '') {
                       $confhash->{$role}{'alttext'}{$img} = $env{'form.login_alt_'.$img};
                   }
               }
     if ( ! $env{'form.'.$role.'_'.$img.'.filename'}       if ( ! $env{'form.'.$role.'_'.$img.'.filename'} 
  && !defined($domconfig->{$role}{$img})   && !defined($domconfig->{$role}{$img})
  && !$env{'form.'.$role.'_del_'.$img}   && !$env{'form.'.$role.'_del_'.$img}
Line 9019  sub modify_colors { Line 11849  sub modify_colors {
                         $error = &mt("Upload of [_1] image for $role page(s) is not permitted to this server: [_2]",$choices{$img},$switchserver);                          $error = &mt("Upload of [_1] image for $role page(s) is not permitted to this server: [_2]",$choices{$img},$switchserver);
                     } else {                      } else {
                         if ($author_ok eq 'ok') {                          if ($author_ok eq 'ok') {
                               my $modified = [];
                             my ($result,$logourl) =                               my ($result,$logourl) = 
                                 &publishlogo($r,'upload',$role.'_'.$img,                                  &Apache::lonconfigsettings::publishlogo($r,'upload',$role.'_'.$img,
                                            $dom,$confname,$img,$width,$height);                                                                          $dom,$confname,$img,$width,$height,
                                                                           '',$modified);
                             if ($result eq 'ok') {                              if ($result eq 'ok') {
                                 $confhash->{$role}{$img} = $logourl;                                  $confhash->{$role}{$img} = $logourl;
                                 $changes{$role}{'images'}{$img} = 1;                                  $changes{$role}{'images'}{$img} = 1;
                                   &update_modify_urls($r,$modified);
                             } else {                              } else {
                                 $error = &mt("Upload of [_1] image for $role page(s) failed because an error occurred publishing the file in RES space. Error was: [_2].",$choices{img},$result);                                  $error = &mt("Upload of [_1] image for $role page(s) failed because an error occurred publishing the file in RES space. Error was: [_2].",$choices{img},$result);
                             }                              }
Line 9046  sub modify_colors { Line 11879  sub modify_colors {
 # is confname an author?  # is confname an author?
                         if ($switchserver eq '') {                          if ($switchserver eq '') {
                             if ($author_ok eq 'ok') {                              if ($author_ok eq 'ok') {
                                   my $modified = [];
                                 my ($result,$logourl) =                                   my ($result,$logourl) = 
                                &publishlogo($r,'copy',$domconfig->{$role}{$img},                                      &Apache::lonconfigsettings::publishlogo($r,'copy',$domconfig->{$role}{$img},
                                             $dom,$confname,$img,$width,$height);                                                                              $dom,$confname,$img,$width,$height,
                                                                               '',$modified);
                                 if ($result eq 'ok') {                                  if ($result eq 'ok') {
                                     $confhash->{$role}{$img} = $logourl;                                      $confhash->{$role}{$img} = $logourl;
     $changes{$role}{'images'}{$img} = 1;      $changes{$role}{'images'}{$img} = 1;
                                       &update_modify_urls($r,$modified);
                                 }                                  }
                             }                              }
                         }                          }
Line 9077  sub modify_colors { Line 11913  sub modify_colors {
                             $changes{$role}{'images'}{$img} = 1;                              $changes{$role}{'images'}{$img} = 1;
                         }                           } 
                     }                      }
                     if (($role eq 'login') && (($img eq 'logo') || ($img eq 'img'))) {                      if ($role eq 'login') {
                         if (ref($domconfig->{'login'}{'showlogo'}) eq 'HASH') {                          if (($img eq 'logo') || ($img eq 'img')) {
                             if ($confhash->{$role}{'showlogo'}{$img} ne                               if (ref($domconfig->{'login'}{'showlogo'}) eq 'HASH') {
                                 $domconfig->{$role}{'showlogo'}{$img}) {                                  if ($confhash->{$role}{'showlogo'}{$img} ne 
                                 $changes{$role}{'showlogo'}{$img} = 1;                                       $domconfig->{$role}{'showlogo'}{$img}) {
                                       $changes{$role}{'showlogo'}{$img} = 1; 
                                   }
                               } else {
                                   if ($confhash->{$role}{'showlogo'}{$img} == 0) {
                                       $changes{$role}{'showlogo'}{$img} = 1;
                                   }
                             }                              }
                         } else {                          }
                             if ($confhash->{$role}{'showlogo'}{$img} == 0) {                          if ($img ne 'login') {
                                 $changes{$role}{'showlogo'}{$img} = 1;                              if (ref($domconfig->{$role}{'alttext'}) eq 'HASH') {
                                   if ($confhash->{$role}{'alttext'}{$img} ne
                                       $domconfig->{$role}{'alttext'}{$img}) {
                                       $changes{$role}{'alttext'}{$img} = 1;
                                   }
                               } else {
                                   if ($confhash->{$role}{'alttext'}{$img} ne '') {
                                       $changes{$role}{'alttext'}{$img} = 1;
                                   }
                             }                              }
                         }                          }
                     }                      }
Line 9196  sub default_change_checker { Line 12046  sub default_change_checker {
             if ($confhash->{$role}{'showlogo'}{$img} == 0) {              if ($confhash->{$role}{'showlogo'}{$img} == 0) {
                 $changes->{$role}{'showlogo'}{$img} = 1;                  $changes->{$role}{'showlogo'}{$img} = 1;
             }              }
               if (ref($confhash->{$role}{'alttext'}) eq 'HASH') {
                   if ($confhash->{$role}{'alttext'}{$img} ne '') {
                       $changes->{$role}{'alttext'}{$img} = 1;
                   }
               }
         }          }
     }      }
     if ($confhash->{$role}{'font'}) {      if ($confhash->{$role}{'font'}) {
Line 9234  sub display_colorchgs { Line 12089  sub display_colorchgs {
                             } else {                              } else {
                                 $resulttext .= '<li>'.&mt("$choices{$item} set to not be displayed").'</li>';                                  $resulttext .= '<li>'.&mt("$choices{$item} set to not be displayed").'</li>';
                             }                              }
                           } elsif (($role eq 'login') && ($key eq 'alttext')) {
                               if ($confhash->{$role}{$key}{$item} ne '') {
                                   $resulttext .= '<li>'.&mt("$choices{$key} for $choices{$item} set to [_1].",
                                                  $confhash->{$role}{$key}{$item}).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt("$choices{$key} for $choices{$item} deleted.").'</li>';
                               }
                         } elsif ($confhash->{$role}{$item} eq '') {                          } elsif ($confhash->{$role}{$item} eq '') {
                             $resulttext .= '<li>'.&mt("$choices{$item} set to default").'</li>';                              $resulttext .= '<li>'.&mt("$choices{$item} set to default").'</li>';
                         } else {                          } else {
Line 9319  sub check_authorstatus { Line 12181  sub check_authorstatus {
     return $author_ok;      return $author_ok;
 }  }
   
 sub publishlogo {  sub update_modify_urls {
     my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight,$savefileas) = @_;      my ($r,$modified) = @_;
     my ($output,$fname,$logourl);      if ((ref($modified) eq 'ARRAY') && (@{$modified})) {
     if ($action eq 'upload') {          push(@{$modified_urls},$modified);
         $fname=$env{'form.'.$formname.'.filename'};          unless ($registered_cleanup) {
         chop($env{'form.'.$formname});              my $handlers = $r->get_handlers('PerlCleanupHandler');
     } else {              $r->set_handlers('PerlCleanupHandler' => [\&notifysubscribed,@{$handlers}]);
         ($fname) = ($formname =~ /([^\/]+)$/);              $registered_cleanup=1;
     }  
     if ($savefileas ne '') {  
         $fname = $savefileas;  
     }  
     $fname=&Apache::lonnet::clean_filename($fname);  
 # See if there is anything left  
     unless ($fname) { return ('error: no uploaded file'); }  
     $fname="$subdir/$fname";  
     my $docroot=$r->dir_config('lonDocRoot');  
     my $filepath="$docroot/priv";  
     my $relpath = "$dom/$confname";  
     my ($fnamepath,$file,$fetchthumb);  
     $file=$fname;  
     if ($fname=~m|/|) {  
         ($fnamepath,$file) = ($fname =~ m|^(.*)/([^/]+)$|);  
     }  
     my @parts=split(/\//,"$filepath/$relpath/$fnamepath");  
     my $count;  
     for ($count=5;$count<=$#parts;$count++) {  
         $filepath.="/$parts[$count]";  
         if ((-e $filepath)!=1) {  
             mkdir($filepath,02770);  
         }  
     }  
     # Check for bad extension and disallow upload  
     if ($file=~/\.(\w+)$/ &&  
         (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {  
         $output =   
             &mt('Invalid file extension ([_1]) - reserved for internal use.',$1);   
     } elsif ($file=~/\.(\w+)$/ &&  
         !defined(&Apache::loncommon::fileembstyle($1))) {  
         $output = &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1);  
     } elsif ($file=~/\.(\d+)\.(\w+)$/) {  
         $output = &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2);  
     } elsif (-d "$filepath/$file") {  
         $output = &mt('Filename is a directory name - rename the file and re-upload');  
     } else {  
         my $source = $filepath.'/'.$file;  
         my $logfile;  
         if (!open($logfile,">>",$source.'.log')) {  
             return (&mt('No write permission to Authoring Space'));  
         }  
         print $logfile  
 "\n================= Publish ".localtime()." ================\n".  
 $env{'user.name'}.':'.$env{'user.domain'}."\n";  
 # Save the file  
         if (!open(FH,">",$source)) {  
             &Apache::lonnet::logthis('Failed to create '.$source);  
             return (&mt('Failed to create file'));  
         }  
         if ($action eq 'upload') {  
             if (!print FH ($env{'form.'.$formname})) {  
                 &Apache::lonnet::logthis('Failed to write to '.$source);  
                 return (&mt('Failed to write file'));  
             }  
         } else {  
             my $original = &Apache::lonnet::filelocation('',$formname);  
             if(!copy($original,$source)) {  
                 &Apache::lonnet::logthis('Failed to copy '.$original.' to '.$source);  
                 return (&mt('Failed to write file'));  
             }  
         }  
         close(FH);  
         chmod(0660, $source); # Permissions to rw-rw---.  
   
         my $targetdir=$docroot.'/res/'.$dom.'/'.$confname .'/'.$fnamepath;  
         my $copyfile=$targetdir.'/'.$file;  
   
         my @parts=split(/\//,$targetdir);  
         my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]";  
         for (my $count=5;$count<=$#parts;$count++) {  
             $path.="/$parts[$count]";  
             if (!-e $path) {  
                 print $logfile "\nCreating directory ".$path;  
                 mkdir($path,02770);  
             }  
         }  
         my $versionresult;  
         if (-e $copyfile) {  
             $versionresult = &logo_versioning($targetdir,$file,$logfile);  
         } else {  
             $versionresult = 'ok';  
         }  
         if ($versionresult eq 'ok') {  
             if (copy($source,$copyfile)) {  
                 print $logfile "\nCopied original source to ".$copyfile."\n";  
                 $output = 'ok';  
                 $logourl = '/res/'.$dom.'/'.$confname.'/'.$fname;  
                 push(@{$modified_urls},[$copyfile,$source]);  
                 my $metaoutput =   
                     &write_metadata($dom,$confname,$formname,$targetdir,$file,$logfile);  
                 unless ($registered_cleanup) {  
                     my $handlers = $r->get_handlers('PerlCleanupHandler');  
                     $r->set_handlers('PerlCleanupHandler' => [\&notifysubscribed,@{$handlers}]);  
                     $registered_cleanup=1;  
                 }  
             } else {  
                 print $logfile "\nUnable to write ".$copyfile.':'.$!."\n";  
                 $output = &mt('Failed to copy file to RES space').", $!";  
             }  
             if (($thumbwidth =~ /^\d+$/) && ($thumbheight =~ /^\d+$/)) {  
                 my $inputfile = $filepath.'/'.$file;  
                 my $outfile = $filepath.'/'.'tn-'.$file;  
                 my ($fullwidth,$fullheight) = &check_dimensions($inputfile);  
                 if ($fullwidth ne '' && $fullheight ne '') {   
                     if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) {  
                         my $thumbsize = $thumbwidth.'x'.$thumbheight;  
                         my @args = ('convert','-sample',$thumbsize,$inputfile,$outfile);  
                         system({$args[0]} @args);  
                         chmod(0660, $filepath.'/tn-'.$file);  
                         if (-e $outfile) {  
                             my $copyfile=$targetdir.'/tn-'.$file;  
                             if (copy($outfile,$copyfile)) {  
                                 print $logfile "\nCopied source to ".$copyfile."\n";  
                                 my $thumb_metaoutput =   
                                     &write_metadata($dom,$confname,$formname,  
                                                     $targetdir,'tn-'.$file,$logfile);  
                                 push(@{$modified_urls},[$copyfile,$outfile]);  
                                 unless ($registered_cleanup) {  
                                     my $handlers = $r->get_handlers('PerlCleanupHandler');  
                                     $r->set_handlers('PerlCleanupHandler' => [\&notifysubscribed,@{$handlers}]);  
                                     $registered_cleanup=1;  
                                 }  
                             } else {  
                                 print $logfile "\nUnable to write ".$copyfile.  
                                                ':'.$!."\n";  
                             }  
                         }  
                     }  
                 }  
             }  
         } else {  
             $output = $versionresult;  
         }  
     }  
     return ($output,$logourl);  
 }  
   
 sub logo_versioning {  
     my ($targetdir,$file,$logfile) = @_;  
     my $target = $targetdir.'/'.$file;  
     my ($maxversion,$fn,$extn,$output);  
     $maxversion = 0;  
     if ($file =~ /^(.+)\.(\w+)$/) {  
         $fn=$1;  
         $extn=$2;  
     }  
     opendir(DIR,$targetdir);  
     while (my $filename=readdir(DIR)) {  
         if ($filename=~/\Q$fn\E\.(\d+)\.\Q$extn\E$/) {  
             $maxversion=($1>$maxversion)?$1:$maxversion;  
         }  
     }  
     $maxversion++;  
     print $logfile "\nCreating old version ".$maxversion."\n";  
     my $copyfile=$targetdir.'/'.$fn.'.'.$maxversion.'.'.$extn;  
     if (copy($target,$copyfile)) {  
         print $logfile "Copied old target to ".$copyfile."\n";  
         $copyfile=$copyfile.'.meta';  
         if (copy($target.'.meta',$copyfile)) {  
             print $logfile "Copied old target metadata to ".$copyfile."\n";  
             $output = 'ok';  
         } else {  
             print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n";  
             $output = &mt('Failed to copy old meta').", $!, ";  
         }  
     } else {  
         print $logfile "Unable to write ".$copyfile.':'.$!."\n";  
         $output = &mt('Failed to copy old target').", $!, ";  
     }  
     return $output;  
 }  
   
 sub write_metadata {  
     my ($dom,$confname,$formname,$targetdir,$file,$logfile) = @_;  
     my (%metadatafields,%metadatakeys,$output);  
     $metadatafields{'title'}=$formname;  
     $metadatafields{'creationdate'}=time;  
     $metadatafields{'lastrevisiondate'}=time;  
     $metadatafields{'copyright'}='public';  
     $metadatafields{'modifyinguser'}=$env{'user.name'}.':'.  
                                          $env{'user.domain'};  
     $metadatafields{'authorspace'}=$confname.':'.$dom;  
     $metadatafields{'domain'}=$dom;  
     {  
         print $logfile "\nWrite metadata file for ".$targetdir.'/'.$file;  
         my $mfh;  
         if (open($mfh,">",$targetdir.'/'.$file.'.meta')) {  
             foreach (sort(keys(%metadatafields))) {  
                 unless ($_=~/\./) {  
                     my $unikey=$_;  
                     $unikey=~/^([A-Za-z]+)/;  
                     my $tag=$1;  
                     $tag=~tr/A-Z/a-z/;  
                     print $mfh "\n\<$tag";  
                     foreach (split(/\,/,$metadatakeys{$unikey})) {  
                         my $value=$metadatafields{$unikey.'.'.$_};  
                         $value=~s/\"/\'\'/g;  
                         print $mfh ' '.$_.'="'.$value.'"';  
                     }  
                     print $mfh '>'.  
                         &HTML::Entities::encode($metadatafields{$unikey},'<>&"')  
                             .'</'.$tag.'>';  
                 }  
             }  
             $output = 'ok';  
             print $logfile "\nWrote metadata";  
             close($mfh);  
         } else {  
             print $logfile "\nFailed to open metadata file";  
             $output = &mt('Could not write metadata');  
         }          }
     }      }
     return $output;  
 }  }
   
 sub notifysubscribed {  sub notifysubscribed {
Line 9591  sub subscribed_hosts { Line 12241  sub subscribed_hosts {
   
 sub check_switchserver {  sub check_switchserver {
     my ($dom,$confname) = @_;      my ($dom,$confname) = @_;
     my ($allowed,$switchserver);      my ($allowed,$switchserver,$home);
     my $home = &Apache::lonnet::homeserver($confname,$dom);      if ($confname eq '') {
     if ($home eq 'no_host') {  
         $home = &Apache::lonnet::domain($dom,'primary');          $home = &Apache::lonnet::domain($dom,'primary');
       } else {
           $home = &Apache::lonnet::homeserver($confname,$dom);
           if ($home eq 'no_host') {
               $home = &Apache::lonnet::domain($dom,'primary');
           }
     }      }
     my @ids=&Apache::lonnet::current_machine_ids();      my @ids=&Apache::lonnet::current_machine_ids();
     foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } }      foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } }
     if (!$allowed) {      if (!$allowed) {
  $switchserver='<a href="/adm/switchserver?otherserver='.$home.'&amp;role=dc./'.$dom.'/&amp;destinationurl=/adm/domainprefs">'.&mt('Switch Server').'</a>';   $switchserver='<a href="/adm/switchserver?otherserver='.$home.'&amp;role='.
                         &HTML::Entities::encode($env{'request.role'},'\'<>"&').
                         '&amp;destinationurl=/adm/domainprefs">'.&mt('Switch Server').'</a>';
     }      }
     return $switchserver;      return $switchserver;
 }  }
Line 9616  sub modify_quotas { Line 12272  sub modify_quotas {
         $context = $action;          $context = $action;
     }      }
     if ($context eq 'requestcourses') {      if ($context eq 'requestcourses') {
         @usertools = ('official','unofficial','community','textbook');          @usertools = ('official','unofficial','community','textbook','lti');
         @options =('norequest','approval','validate','autolimit');          @options =('norequest','approval','validate','autolimit');
         %validations = &Apache::lonnet::auto_courserequest_checks($dom);          %validations = &Apache::lonnet::auto_courserequest_checks($dom);
         %titles = &courserequest_titles();          %titles = &courserequest_titles();
Line 9631  sub modify_quotas { Line 12287  sub modify_quotas {
         @usertools = ('author');          @usertools = ('author');
         %titles = &authorrequest_titles();          %titles = &authorrequest_titles();
     } else {      } else {
         @usertools = ('aboutme','blog','webdav','portfolio');          @usertools = ('aboutme','blog','webdav','portfolio','timezone');
         %titles = &tool_titles();          %titles = &tool_titles();
     }      }
     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);      my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
Line 9665  sub modify_quotas { Line 12321  sub modify_quotas {
         my @approvalnotify = &Apache::loncommon::get_env_multiple('form.'.$context.'notifyapproval');          my @approvalnotify = &Apache::loncommon::get_env_multiple('form.'.$context.'notifyapproval');
         @approvalnotify = sort(@approvalnotify);          @approvalnotify = sort(@approvalnotify);
         $confhash{'notify'}{'approval'} = join(',',@approvalnotify);          $confhash{'notify'}{'approval'} = join(',',@approvalnotify);
         my @crstypes = ('official','unofficial','community','textbook');          my @crstypes = ('official','unofficial','community','textbook','lti');
         my @hasuniquecode = &Apache::loncommon::get_env_multiple('form.uniquecode');          my @hasuniquecode = &Apache::loncommon::get_env_multiple('form.uniquecode');
         foreach my $type (@hasuniquecode) {          foreach my $type (@hasuniquecode) {
             if (grep(/^\Q$type\E$/,@crstypes)) {              if (grep(/^\Q$type\E$/,@crstypes)) {
Line 9787  sub modify_quotas { Line 12443  sub modify_quotas {
                                             &Apache::lonnet::logthis($error);                                              &Apache::lonnet::logthis($error);
                                             $errors .= '<li><span class="LC_error">'.$error.'</span></li>';                                              $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
                                         }                                          }
                                     }                                       }
                                 } elsif ($domconfig{$action}{$type}{$key}{'image'}) {                                  } elsif ($domconfig{$action}{$type}{$key}{'image'}) {
                                     $confhash{$type}{$key}{'image'} =                                       $confhash{$type}{$key}{'image'} = 
                                         $domconfig{$action}{$type}{$key}{'image'};                                          $domconfig{$action}{$type}{$key}{'image'};
Line 10322  sub process_textbook_image { Line 12978  sub process_textbook_image {
             $error = &mt('Upload of textbook image is not permitted to this server: [_1]',              $error = &mt('Upload of textbook image is not permitted to this server: [_1]',
                          $switchserver);                           $switchserver);
         } elsif ($author_ok eq 'ok') {          } elsif ($author_ok eq 'ok') {
               my $modified = [];
             my ($result,$imageurl) =              my ($result,$imageurl) =
                 &publishlogo($r,'upload',$caller,$dom,$confname,                  &Apache::lonconfigsettings::publishlogo($r,'upload',$caller,$dom,$confname,
                              "$type/$cdom/$cnum/cover",$width,$height);                                                          "$type/$cdom/$cnum/cover",$width,$height,
                                                           '',$modified);
             if ($result eq 'ok') {              if ($result eq 'ok') {
                 $url = $imageurl;                  $url = $imageurl;
                   &update_modify_urls($r,$modified);
             } else {              } else {
                 $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);                  $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);
             }              }
Line 10341  sub process_textbook_image { Line 13000  sub process_textbook_image {
   
 sub modify_ltitools {  sub modify_ltitools {
     my ($r,$dom,$action,$lastactref,%domconfig) = @_;      my ($r,$dom,$action,$lastactref,%domconfig) = @_;
     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);      my (%currtoolsec,%secchanges,%newtoolsec,%newkeyset);
     my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext);      &fetch_secrets($dom,'toolsec',\%domconfig,\%currtoolsec,\%secchanges,\%newtoolsec,\%newkeyset);
   
     my $confname = $dom.'-domainconfig';      my $confname = $dom.'-domainconfig';
     my $servadm = $r->dir_config('lonAdmEMail');      my $servadm = $r->dir_config('lonAdmEMail');
     my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);      my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
     my (%posslti,%possfield);  
     my @courseroles = ('cc','in','ta','ep','st');      my ($resulttext,$ltitoolsoutput,$is_home,$errors,%ltitoolschg,%newtoolsenc,%newltitools);
     my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner);      my $toolserror =
     map { $posslti{$_} = 1; } @ltiroles;          &Apache::courseprefs::process_ltitools($r,$dom,$confname,$domconfig{'ltitools'},\%ltitoolschg,'domain',
     my @allfields = ('fullname','firstname','lastname','email','user','roles');                                                 $lastactref,$configuserok,$switchserver,$author_ok);
     map { $possfield{$_} = 1; } @allfields;  
     my %lt = &ltitools_names();      my $home = &Apache::lonnet::domain($dom,'primary');
     if ($env{'form.ltitools_add'}) {      unless (($home eq 'no_host') || ($home eq '')) {
         my $title = $env{'form.ltitools_add_title'};          my @ids=&Apache::lonnet::current_machine_ids();
         $title =~ s/(`)/'/g;          foreach my $id (@ids) { if ($id eq $home) { $is_home=1; last; } }
         ($newid,my $error) = &get_ltitools_id($dom,$title);      }
         if ($newid) {  
             my $position = $env{'form.ltitools_add_pos'};      if (keys(%ltitoolschg)) {
             $position =~ s/\D+//g;          foreach my $id (keys(%ltitoolschg)) {
             if ($position ne '') {              if (ref($ltitoolschg{$id}) eq 'HASH') {
                 $allpos[$position] = $newid;                  foreach my $inner (keys(%{$ltitoolschg{$id}})) {
                       if (($inner eq 'secret') || ($inner eq 'key')) {
                           if ($is_home) {
                               $newtoolsenc{$id}{$inner} = $ltitoolschg{$id}{$inner};
                           }
                       }
                   }
             }              }
             $changes{$newid} = 1;          }
             foreach my $item ('title','url','key','secret') {          $ltitoolsoutput = &Apache::courseprefs::store_ltitools($dom,'','domain',\%ltitoolschg,$domconfig{'ltitools'});
                 $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g;          if (keys(%ltitoolschg)) {
                 if ($env{'form.ltitools_add_'.$item}) {              %newltitools = %ltitoolschg;
                     if (($item eq 'key') || ($item eq 'secret')) {          }
                         $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item};      }
                     } else {      if (ref($domconfig{'ltitools'}) eq 'HASH') {
                         $confhash{$newid}{$item} = $env{'form.ltitools_add_'.$item};          foreach my $id (%{$domconfig{'ltitools'}}) {
               next if ($id !~ /^\d+$/);
               unless (exists($ltitoolschg{$id})) {
                   if (ref($domconfig{'ltitools'}{$id}) eq 'HASH') {
                       foreach my $inner (keys(%{$domconfig{'ltitools'}{$id}})) {
                           if (($inner eq 'secret') || ($inner eq 'key')) {
                               if ($is_home) {
                                   $newtoolsenc{$id}{$inner} = $domconfig{'ltitools'}{$id}{$inner};
                               }
                           } else {
                               $newltitools{$id}{$inner} = $domconfig{'ltitools'}{$id}{$inner};
                           }
                     }                      }
                   } else {
                       $newltitools{$id} = $domconfig{'ltitools'}{$id};
                 }                  }
             }              }
             if ($env{'form.ltitools_add_version'} eq 'LTI-1p0') {          }
                 $confhash{$newid}{'version'} = $env{'form.ltitools_add_version'};      }
       if ($toolserror) {
           $errors = '<li>'.$toolserror.'</li>';
       }
       if ((keys(%ltitoolschg) == 0) && (keys(%secchanges) == 0)) {
           $resulttext = &mt('No changes made.');
           if ($errors) {
               $resulttext .= '<br />'.&mt('The following errors occurred: ').'<ul>'.
                                    $errors.'</ul>';
           }
           return $resulttext;
       }
       my %ltitoolshash = (
                             $action => { %newltitools }
                          );
       if (keys(%secchanges)) {
           $ltitoolshash{'toolsec'} = \%newtoolsec;
       }
       my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash,$dom);
       if ($putresult eq 'ok') {
           my %keystore;
           if ($is_home) {
               my %toolsenchash = (
                                      $action => { %newtoolsenc }
                                  );
               &Apache::lonnet::put_dom('encconfig',\%toolsenchash,$dom,undef,1);
               my $cachetime = 24*60*60;
               &Apache::lonnet::do_cache_new('ltitoolsenc',$dom,\%newtoolsenc,$cachetime);
               &store_security($dom,'ltitools',\%secchanges,\%newkeyset,\%keystore,$lastactref);
           }
           $resulttext = &mt('Changes made:').'<ul>';
           if (keys(%secchanges) > 0) {
               $resulttext .= &lti_security_results($dom,'ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore);
           }
           if (keys(%ltitoolschg) > 0) {
               $resulttext .= $ltitoolsoutput;
           }
           my $cachetime = 24*60*60;
           &Apache::lonnet::do_cache_new('ltitools',$dom,\%newltitools,$cachetime);
           if (ref($lastactref) eq 'HASH') {
               $lastactref->{'ltitools'} = 1;
           }
       } else {
           $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
       }
       if ($errors) {
           $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul></p>';
       }
       return $resulttext;
   }
   
   sub fetch_secrets {
       my ($dom,$context,$domconfig,$currsec,$secchanges,$newsec,$newkeyset) = @_;
       my %keyset;
       %{$currsec} = ();
       $newsec->{'private'}{'keys'} = [];
       $newsec->{'encrypt'} = {};
       $newsec->{'rules'} = {};
       if ($context eq 'ltisec') {
           $newsec->{'linkprot'} = {};
       }
       if (ref($domconfig->{$context}) eq 'HASH') {
           %{$currsec} = %{$domconfig->{$context}};
           if ($context eq 'ltisec') {
               if (ref($currsec->{'linkprot'}) eq 'HASH') {
                   foreach my $id (keys(%{$currsec->{'linkprot'}})) {
                       unless ($id =~ /^\d+$/) {
                           delete($currsec->{'linkprot'}{$id});
                       }
                   }
             }              }
             if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') {          }
                 $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'};          if (ref($currsec->{'private'}) eq 'HASH') {
               if (ref($currsec->{'private'}{'keys'}) eq 'ARRAY') {
                   $newsec->{'private'}{'keys'} = $currsec->{'private'}{'keys'};
                   map { $keyset{$_} = 1; } @{$currsec->{'private'}{'keys'}};
             }              }
             foreach my $item ('width','height','linktext','explanation') {          }
                 $env{'form.ltitools_add_'.$item} =~ s/^\s+//;      }
                 $env{'form.ltitools_add_'.$item} =~ s/\s+$//;      my @items= ('crs','dom');
                 if (($item eq 'width') || ($item eq 'height')) {      if ($context eq 'ltisec') {
                     if ($env{'form.ltitools_add_'.$item} =~ /^\d+$/) {          push(@items,'consumers');
                         $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item};      }
       foreach my $item (@items) {
           my $formelement;
           if (($context eq 'toolsec') || ($item eq 'consumers')) {
               $formelement = 'form.'.$context.'_'.$item;
           } else {
               $formelement = 'form.'.$context.'_'.$item.'linkprot';
           }
           if ($env{$formelement}) {
               $newsec->{'encrypt'}{$item} = 1;
               if (ref($currsec->{'encrypt'}) eq 'HASH') {
                   unless ($currsec->{'encrypt'}{$item}) {
                       $secchanges->{'encrypt'} = 1;
                   }
               } else {
                   $secchanges->{'encrypt'} = 1;
               }
           } elsif (ref($currsec->{'encrypt'}) eq 'HASH') {
               if ($currsec->{'encrypt'}{$item}) {
                   $secchanges->{'encrypt'} = 1;
               }
           }
       }
       my $secrets;
       if ($context eq 'ltisec') {
           $secrets = 'ltisecrets';
       } else {
           $secrets = 'toolsecrets';
       }
       unless (exists($currsec->{'rules'})) {
           $currsec->{'rules'} = {};
       }
       &password_rule_changes($secrets,$newsec->{'rules'},$currsec->{'rules'},$secchanges);
   
       my @ids=&Apache::lonnet::current_machine_ids();
       my %servers = &Apache::lonnet::get_servers($dom,'library');
   
       foreach my $hostid (keys(%servers)) {
           if (($hostid ne '') && (grep(/^\Q$hostid\E$/,@ids))) {
               my $keyitem = 'form.'.$context.'_privkey_'.$hostid;
               if (exists($env{$keyitem})) {
                   $env{$keyitem} =~ s/(`)/'/g;
                   if ($keyset{$hostid}) {
                       if ($env{'form.'.$context.'_changeprivkey_'.$hostid}) {
                           if ($env{$keyitem} ne '') {
                               $secchanges->{'private'} = 1;
                               $newkeyset->{$hostid} = $env{$keyitem};
                           }
                     }                      }
                 } else {                  } elsif ($env{$keyitem} ne '') {
                     if ($env{'form.ltitools_add_'.$item} ne '') {                      unless (grep(/^\Q$hostid\E$/,@{$newsec->{'private'}{'keys'}})) {
                         $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item};                          push(@{$newsec->{'private'}{'keys'}},$hostid);
                     }                      }
                       $secchanges->{'private'} = 1;
                       $newkeyset->{$hostid} = $env{$keyitem};
                 }                  }
             }              }
             if ($env{'form.ltitools_add_target'} eq 'window') {          }
                 $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'};      }
             } elsif ($env{'form.ltitools_add_target'} eq 'tab') {  }
                 $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'};  
   sub store_security {
       my ($dom,$context,$secchanges,$newkeyset,$keystore) = @_;
       return unless ((ref($secchanges) eq 'HASH') && (ref($newkeyset) eq 'HASH') &&
                      (ref($keystore) eq 'HASH'));
       if (keys(%{$secchanges})) {
           if ($secchanges->{'private'}) {
               my $who = &escape($env{'user.name'}.':'.$env{'user.domain'});
               foreach my $hostid (keys(%{$newkeyset})) {
                   my $storehash = {
                                      key => $newkeyset->{$hostid},
                                      who => $env{'user.name'}.':'.$env{'user.domain'},
                                   };
                   $keystore->{$hostid} = &Apache::lonnet::store_dom($storehash,$context,'private',
                                                                     $dom,$hostid);
               }
           }
       }
   }
   
   sub lti_security_results {
       my ($dom,$context,$secchanges,$newsec,$newkeyset,$keystore) = @_;
       my $output;
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
       my $needs_update;
       foreach my $item (keys(%{$secchanges})) {
           if ($item eq 'encrypt') {
               $needs_update = 1;
               my %encrypted;
               if ($context eq 'lti') {
                   %encrypted = (
                                 crs  => {
                                           on => &mt('Encryption of stored link protection secrets defined in courses enabled'),
                                           off => &mt('Encryption of stored link protection secrets defined in courses disabled'),
                                         },
                                 dom => {
                                          on => &mt('Encryption of stored link protection secrets defined in domain enabled'),
                                          off => &mt('Encryption of stored link protection secrets defined in domain disabled'),
                                        },
                                 consumers => {
                                                on => &mt('Encryption of stored consumer secrets defined in domain enabled'),
                                                off => &mt('Encryption of stored consumer secrets defined in domain disabled'),
                                              },
                                );
             } else {              } else {
                 $confhash{$newid}{'display'}{'target'} = 'iframe';                  %encrypted = (
                                 crs  => {
                                           on => &mt('Encryption of stored external tool secrets defined in courses enabled'),
                                           off => &mt('Encryption of stored external tool secrets defined in courses disabled'),
                                         },
                                 dom => {
                                          on => &mt('Encryption of stored external tool secrets defined in domain enabled'),
                                          off => &mt('Encryption of stored external tool secrets defined in domain disabled'),
                                        },
                                );
             }              }
             foreach my $item ('passback','roster') {              my @types= ('crs','dom');
                 if ($env{'form.ltitools_add_'.$item}) {              if ($context eq 'lti') {
                     $confhash{$newid}{$item} = 1;                  foreach my $type (@types) {
                       undef($domdefaults{'linkprotenc_'.$type});
                   }
                   push(@types,'consumers');
                   undef($domdefaults{'ltienc_consumers'});
               } elsif ($context eq 'ltitools') {
                   foreach my $type (@types) {
                       undef($domdefaults{'toolenc_'.$type});
                 }                  }
             }              }
             if ($env{'form.ltitools_add_image.filename'} ne '') {              foreach my $type (@types) {
                 my ($imageurl,$error) =                  my $shown = $encrypted{$type}{'off'};
                     &process_ltitools_image($r,$dom,$confname,'ltitools_add_image',$newid,                  if (ref($newsec->{$item}) eq 'HASH') {
                                             $configuserok,$switchserver,$author_ok);                      if ($newsec->{$item}{$type}) {
                 if ($imageurl) {                          if ($context eq 'lti') {
                     $confhash{$newid}{'image'} = $imageurl;                              if ($type eq 'consumers') {
                                   $domdefaults{'ltienc_consumers'} = 1;
                               } else {
                                   $domdefaults{'linkprotenc_'.$type} = 1;
                               }
                           } elsif ($context eq 'ltitools') {
                               $domdefaults{'toolenc_'.$type} = 1;
                           }
                           $shown = $encrypted{$type}{'on'};
                       }
                 }                  }
                 if ($error) {                  $output .= '<li>'.$shown.'</li>';
                     &Apache::lonnet::logthis($error);              }
                     $errors .= '<li><span class="LC_error">'.$error.'</span></li>';          } elsif ($item eq 'rules') {
               my %titles = &Apache::lonlocal::texthash(
                                         min   => 'Minimum password length',
                                         max   => 'Maximum password length',
                                         chars => 'Required characters',
                            );
               foreach my $rule ('min','max') {
                   if ($newsec->{rules}{$rule} eq '') {
                       if ($rule eq 'min') {
                           $output .= '<li>'.&mt('[_1] not set.',$titles{$rule});
                                      ' '.&mt('Default of [_1] will be used',
                                              $Apache::lonnet::passwdmin).'</li>';
                       } else {
                           $output .= '<li>'.&mt('[_1] set to none',$titles{$rule}).'</li>';
                       }
                   } else {
                       $output .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$newsec->{rules}{$rule}).'</li>';
                 }                  }
             }              }
             my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_add_fields');              if (ref($newsec->{'rules'}{'chars'}) eq 'ARRAY') {
             foreach my $field (@fields) {                  if (@{$newsec->{'rules'}{'chars'}} > 0) {
                 if ($possfield{$field}) {                      my %rulenames = &Apache::lonlocal::texthash(
                     if ($field eq 'roles') {                                               uc => 'At least one upper case letter',
                         foreach my $role (@courseroles) {                                               lc => 'At least one lower case letter',
                             my $choice = $env{'form.ltitools_add_roles_'.$role};                                               num => 'At least one number',
                             if (($choice ne '') && ($posslti{$choice})) {                                               spec => 'At least one non-alphanumeric',
                                 $confhash{$newid}{'roles'}{$role} = $choice;                                      );
                                 if ($role eq 'cc') {                      my $needed = '<ul><li>'.
                                     $confhash{$newid}{'roles'}{'co'} = $choice;                                   join('</li><li>',map {$rulenames{$_} } @{$newsec->{'rules'}{'chars'}}).
                                 }                                   '</li></ul>';
                             }                      $output .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>';
                   } else {
                       $output .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                   }
               } else {
                   $output .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
               }
           } elsif ($item eq 'private') {
               $needs_update = 1;
               if ($context eq 'lti') {
                   undef($domdefaults{'ltiprivhosts'});
               } elsif ($context eq 'ltitools') {
                   undef($domdefaults{'toolprivhosts'});
               }
               if (keys(%{$newkeyset})) {
                   my @privhosts;
                   foreach my $hostid (sort(keys(%{$newkeyset}))) {
                       if ($keystore->{$hostid} eq 'ok') {
                           $output .= '<li>'.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'</li>';
                           unless (grep(/^\Q$hostid\E$/,@privhosts)) {
                               push(@privhosts,$hostid);
                         }                          }
                       }
                   }
                   if (@privhosts) {
                       if ($context eq 'lti') {
                           $domdefaults{'ltiprivhosts'} = \@privhosts;
                       } elsif ($context eq 'ltitools') {
                           $domdefaults{'toolprivhosts'} = \@privhosts;
                       }
                   }
               }
           } elsif ($item eq 'linkprot') {
               next;
           } elsif ($item eq 'suggested') {
               if ((ref($secchanges->{'suggested'}) eq 'HASH') &&
                   (ref($newsec->{'suggested'}) eq 'HASH')) {
                   my $suggestions;
                   foreach my $id (sort { $a <=> $b } keys(%{$secchanges->{'suggested'}})) {
                       if (ref($newsec->{'suggested'}->{$id}) eq 'HASH') {
                           my $name = $newsec->{'suggested'}->{$id}->{'name'};
                           my $info = $newsec->{'suggested'}->{$id}->{'info'};
                           $suggestions .= '<li>'.&mt('Launcher: [_1]',$name).'<br />'.
                                                  &mt('Recommend: [_1]','<pre>'.$info.'</pre>').
                                           '</li>';
                     } else {                      } else {
                         $confhash{$newid}{'fields'}{$field} = 1;                          $suggestions .= '<li>'.&mt('Recommendations deleted for Launcher: [_1]',
                                                      $newsec->{'suggested'}->{$id}).'</li>';
                       }
                   }
                   if ($suggestions) {
                       $output .= '<li>'.&mt('Hints in Courses for Link Protector Configuration').
                                  '<ul>'.$suggestions.'</ul>'.
                                  '</li>';
                   }
               }
           }
       }
       if ($needs_update) {
           my $cachetime = 24*60*60;
           &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
       }
       return $output;
   }
   
   sub modify_lti {
       my ($r,$dom,$action,$lastactref,%domconfig) = @_;
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my ($newid,@allpos,%changes,%confhash,%ltienc,$errors,$resulttext);
       my (%posslti,%posslticrs,%posscrstype);
       my @courseroles = ('cc','in','ta','ep','st');
       my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator);
       my @lticourseroles = qw(Instructor TeachingAssistant Mentor Learner);
       my @coursetypes = ('official','unofficial','community','textbook','placement','lti');
       my %coursetypetitles = &Apache::lonlocal::texthash (
                                  official   => 'Official',
                                  unofficial => 'Unofficial',
                                  community  => 'Community',
                                  textbook   => 'Textbook',
                                  placement  => 'Placement Test',
                                  lti        => 'LTI Provider',
       );
       my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();
       my %lt = &lti_names();
       map { $posslti{$_} = 1; } @ltiroles;
       map { $posslticrs{$_} = 1; } @lticourseroles;
       map { $posscrstype{$_} = 1; } @coursetypes;
   
       my %menutitles = &ltimenu_titles();
       my (%currltisec,%secchanges,%newltisec,%newltienc,%newkeyset);
   
       &fetch_secrets($dom,'ltisec',\%domconfig,\%currltisec,\%secchanges,\%newltisec,\%newkeyset);
   
       my (%linkprotchg,$linkprotoutput,$is_home);
       my $proterror = &Apache::courseprefs::process_linkprot($dom,'',$currltisec{'linkprot'},
                                                              \%linkprotchg,'domain');
       my $home = &Apache::lonnet::domain($dom,'primary');
       unless (($home eq 'no_host') || ($home eq '')) {
           my @ids=&Apache::lonnet::current_machine_ids();
           foreach my $id (@ids) { if ($id eq $home) { $is_home=1; } }
       }
   
       if (keys(%linkprotchg)) {
           $secchanges{'linkprot'} = 1;
           my %oldlinkprot;
           if (ref($currltisec{'linkprot'}) eq 'HASH') {
               %oldlinkprot = %{$currltisec{'linkprot'}};
           }
           foreach my $id (keys(%linkprotchg)) {
               if (ref($linkprotchg{$id}) eq 'HASH') {
                   foreach my $inner (keys(%{$linkprotchg{$id}})) {
                       if (($inner eq 'secret') || ($inner eq 'key')) {
                           if ($is_home) {
                               $newltienc{$id}{$inner} = $linkprotchg{$id}{$inner};
                           }
                       }
                   }
               } else {
                   $newltisec{'linkprot'}{$id} = $linkprotchg{$id};
               }
           }
           $linkprotoutput = &Apache::courseprefs::store_linkprot($dom,'','domain',\%linkprotchg,\%oldlinkprot);
           if (keys(%linkprotchg)) {
               %{$newltisec{'linkprot'}} = %linkprotchg;
           }
       }
       if (ref($currltisec{'linkprot'}) eq 'HASH') {
           foreach my $id (keys(%{$currltisec{'linkprot'}})) {
               next if ($id !~ /^\d+$/);
               unless (exists($linkprotchg{$id})) {
                   if (ref($currltisec{'linkprot'}{$id}) eq 'HASH') {
                       foreach my $inner (keys(%{$currltisec{'linkprot'}{$id}})) {
                           if (($inner eq 'secret') || ($inner eq 'key')) {
                               if ($is_home) {
                                   $newltienc{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner};
                               }
                           } else {
                               $newltisec{'linkprot'}{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner};
                           }
                     }                      }
                   } else {
                       $newltisec{'linkprot'}{$id} = $currltisec{'linkprot'}{$id};
                 }                  }
             }              }
             my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig');          }
             foreach my $item (@courseconfig) {      }
                 $confhash{$newid}{'crsconf'}{$item} = 1;      if ($proterror) {
           $errors .= '<li>'.$proterror.'</li>';
       }
   
       my (%delsuggested,%suggids,@suggested);;
       if (ref($currltisec{'suggested'}) eq 'HASH') {
           my $maxnum = $env{'form.linkprot_suggested_maxnum'};
           my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_suggested_del');
           for (my $i=0; $i<$maxnum; $i++) {
               my $itemid = $env{'form.linkprot_suggested_id_'.$i};
               $itemid =~ s/\D+//g;
               if ($itemid) {
                   if (ref($currltisec{'suggested'}->{$itemid}) eq 'HASH') {
                       push(@suggested,$i);
                       $suggids{$i} = $itemid;
                       if ((@todelete > 0) && (grep(/^$i$/,@todelete))) {
                           if (ref($currltisec{'suggested'}{$itemid}) eq 'HASH') {
                               $delsuggested{$itemid} = $currltisec{'suggested'}{$itemid}{'name'};
                           }
                       } else {
                           if ($env{'form.linkprot_suggested_name_'.$i} eq '') {
                               $delsuggested{$itemid} = $currltisec{'suggested'}{$itemid}{'name'};
                           } else {
                               $env{'form.linkprot_suggested_name_'.$i} =~ s/(`)/'/g;
                               $env{'form.linkprot_suggested_info_'.$i} =~ s/(`)/'/g;
                               $newltisec{'suggested'}{$itemid}{'name'} = $env{'form.linkprot_suggested_name_'.$i};
                               $newltisec{'suggested'}{$itemid}{'info'} = $env{'form.linkprot_suggested_info_'.$i};
                               if (($currltisec{'suggested'}{$itemid}{'name'} ne $newltisec{'suggested'}{$itemid}{'name'}) ||
                                   ($currltisec{'suggested'}{$itemid}{'info'} ne $newltisec{'suggested'}{$itemid}{'info'})) {
                                   $secchanges{'suggested'}{$itemid} = 1;
                               }
                           }
                       }
                   }
             }              }
             if ($env{'form.ltitools_add_custom'}) {          }
                 my $name = $env{'form.ltitools_add_custom_name'};      }
                 my $value = $env{'form.ltitools_add_custom_value'};      foreach my $key (keys(%delsuggested)) {
                 $value =~ s/(`)/'/g;          $newltisec{'suggested'}{$key} = $delsuggested{$key};
                 $name =~ s/(`)/'/g;          $secchanges{'suggested'}{$key} = 1;
                 $confhash{$newid}{'custom'}{$name} = $value;      }
       if (($env{'form.linkprot_suggested_add'}) &&
           ($env{'form.linkprot_suggested_name_add'} ne '')) {
           $env{'form.linkprot_suggested_name_add'} =~ s/(`)/'/g;
           $env{'form.linkprot_suggested_info_add'} =~ s/(`)/'/g;
           my ($newsuggid,$errormsg) = &get_lti_id($dom,$env{'form.linkprot_suggested_name_add'},'suggested');
           if ($newsuggid) {
               $newltisec{'suggested'}{$newsuggid}{'name'} = $env{'form.linkprot_suggested_name_add'};
               $newltisec{'suggested'}{$newsuggid}{'info'} = $env{'form.linkprot_suggested_info_add'};
               $secchanges{'suggested'}{$newsuggid} = 1;
           } else {
               my $error = &mt('Failed to acquire unique ID for new Link Protectors in Courses Suggestion');
               if ($errormsg) {
                   $error .= ' ('.$errormsg.')';
             }              }
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       }
       my (@items,%deletions,%itemids);
       if ($env{'form.lti_add'}) {
           my $consumer = $env{'form.lti_consumer_add'};
           $consumer =~ s/(`)/'/g;
           ($newid,my $errormsg) = &get_lti_id($dom,$consumer,'lti');
           if ($newid) {
               $itemids{'add'} = $newid;
               push(@items,'add');
               $changes{$newid} = 1;
         } else {          } else {
             my $error = &mt('Failed to acquire unique ID for new external tool');              my $error = &mt('Failed to acquire unique ID for new LTI configuration');
               if ($errormsg) {
                   $error .= ' ('.$errormsg.')';
               }
             $errors .= '<li><span class="LC_error">'.$error.'</span></li>';              $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
         }          }
     }      }
     if (ref($domconfig{$action}) eq 'HASH') {      if (ref($domconfig{$action}) eq 'HASH') {
         my %deletions;          my @todelete = &Apache::loncommon::get_env_multiple('form.lti_del');
         my @todelete = &Apache::loncommon::get_env_multiple('form.ltitools_del');  
         if (@todelete) {          if (@todelete) {
             map { $deletions{$_} = 1; } @todelete;              map { $deletions{$_} = 1; } @todelete;
         }          }
         my %customadds;          my $maxnum = $env{'form.lti_maxnum'};
         my @newcustom = &Apache::loncommon::get_env_multiple('form.ltitools_customadd');          for (my $i=0; $i<$maxnum; $i++) {
         if (@newcustom) {              my $itemid = $env{'form.lti_id_'.$i};
             map { $customadds{$_} = 1; } @newcustom;  
         }  
         my %imgdeletions;  
         my @todeleteimages = &Apache::loncommon::get_env_multiple('form.ltitools_image_del');  
         if (@todeleteimages) {  
             map { $imgdeletions{$_} = 1; } @todeleteimages;  
         }  
         my $maxnum = $env{'form.ltitools_maxnum'};  
         for (my $i=0; $i<=$maxnum; $i++) {  
             my $itemid = $env{'form.ltitools_id_'.$i};  
             $itemid =~ s/\D+//g;              $itemid =~ s/\D+//g;
             if (ref($domconfig{$action}{$itemid}) eq 'HASH') {              if (ref($domconfig{$action}{$itemid}) eq 'HASH') {
                 if ($deletions{$itemid}) {                  if ($deletions{$itemid}) {
                     if ($domconfig{$action}{$itemid}{'image'}) {                      $changes{$itemid} = $domconfig{$action}{$itemid}{'consumer'};
                         #FIXME need to obsolete item in RES space  
                     }  
                     $changes{$itemid} = $domconfig{$action}{$itemid}{'title'};  
                     next;  
                 } else {                  } else {
                     my $newpos = $env{'form.ltitools_'.$itemid};                      push(@items,$i);
                     $newpos =~ s/\D+//g;                      $itemids{$i} = $itemid;
                     foreach my $item ('title','url') {                  }
                         $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i};              }
                         if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) {          }
                             $changes{$itemid} = 1;      }
       my (%keystore,$secstored);
       if ($is_home) {
           &store_security($dom,'lti',\%secchanges,\%newkeyset,\%keystore);
       }
   
       my ($cipher,$privnum);
       if ((@items > 0) && ($is_home)) {
           ($cipher,$privnum) = &get_priv_creds($dom,$home,$secchanges{'encrypt'},
                                                $newltisec{'encrypt'},$keystore{$home});
       }
       foreach my $idx (@items) {
           my $itemid = $itemids{$idx};
           next unless ($itemid);
           my %currlti;
           unless ($idx eq 'add') {
               if (ref($domconfig{$action}) eq 'HASH') {
                   if (ref($domconfig{$action}{$itemid}) eq 'HASH') {
                       %currlti = %{$domconfig{$action}{$itemid}};
                   }
               }
           }
           my $position = $env{'form.lti_pos_'.$itemid};
           $position =~ s/\D+//g;
           if ($position ne '') {
               $allpos[$position] = $itemid;
           }
           foreach my $item ('consumer','lifetime','requser','crsinc') {
               my $formitem = 'form.lti_'.$item.'_'.$idx;
               $env{$formitem} =~ s/(`)/'/g;
               if ($item eq 'lifetime') {
                   $env{$formitem} =~ s/[^\d.]//g;
               }
               if ($env{$formitem} ne '') {
                   $confhash{$itemid}{$item} = $env{$formitem};
                   unless (($idx eq 'add') || ($changes{$itemid})) {
                       if ($currlti{$item} ne $confhash{$itemid}{$item}) {
                           $changes{$itemid} = 1;
                       }
                   }
               }
           }
           if ($env{'form.lti_version_'.$idx} eq 'LTI-1p0') {
               $confhash{$itemid}{'version'} = $env{'form.lti_version_'.$idx};
           }
           if ($confhash{$itemid}{'requser'}) {
               if ($env{'form.lti_mapuser_'.$idx} eq 'sourcedid') {
                   $confhash{$itemid}{'mapuser'} = 'lis_person_sourcedid';
               } elsif ($env{'form.lti_mapuser_'.$idx} eq 'email') {
                   $confhash{$itemid}{'mapuser'} = 'lis_person_contact_email_primary';
               } elsif ($env{'form.lti_mapuser_'.$idx} eq 'other') {
                   my $mapuser = $env{'form.lti_customuser_'.$idx};
                   $mapuser =~ s/(`)/'/g;
                   $mapuser =~ s/^\s+|\s+$//g;
                   $confhash{$itemid}{'mapuser'} = $mapuser;
               }
               my @possmakeuser = &Apache::loncommon::get_env_multiple('form.lti_makeuser_'.$idx);
               my @makeuser;
               foreach my $ltirole (sort(@possmakeuser)) {
                   if ($posslti{$ltirole}) {
                       push(@makeuser,$ltirole);
                   }
               }
               $confhash{$itemid}{'makeuser'} = \@makeuser;
               if (@makeuser) {
                   my $lcauth = $env{'form.lti_lcauth_'.$idx};
                   if ($lcauth =~ /^(internal|krb4|krb5|localauth)$/) {
                       $confhash{$itemid}{'lcauth'} = $lcauth;
                       if ($lcauth ne 'internal') {
                           my $lcauthparm = $env{'form.lti_lcauthparm_'.$idx};
                           $lcauthparm =~ s/^(\s+|\s+)$//g;
                           $lcauthparm =~ s/`//g;
                           if ($lcauthparm ne '') {
                               $confhash{$itemid}{'lcauthparm'} = $lcauthparm;
                         }                          }
                     }                      }
                     foreach my $item ('key','secret') {                  } else {
                         $encconfig{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i};                      $confhash{$itemid}{'lcauth'} = 'lti';
                         if ($domconfig{$action}{$itemid}{$item} ne $encconfig{$itemid}{$item}) {                  }
                             $changes{$itemid} = 1;              }
               my @possinstdata =  &Apache::loncommon::get_env_multiple('form.lti_instdata_'.$idx);
               if (@possinstdata) {
                   foreach my $field (@possinstdata) {
                       if (exists($fieldtitles{$field})) {
                           push(@{$confhash{$itemid}{'instdata'}});
                       }
                   }
               }
               if ($env{'form.lti_callback_'.$idx}) {
                   if ($env{'form.lti_callbackparam_'.$idx}) {
                       my $callback = $env{'form.lti_callbackparam_'.$idx};
                       $callback =~ s/^\s+|\s+$//g;
                       $confhash{$itemid}{'callback'} = $callback;
                   }
               }
               foreach my $field ('topmenu','inlinemenu') {
                   if ($env{'form.lti_'.$field.'_'.$idx}) {
                       $confhash{$itemid}{$field} = 1;
                   }
               }
               if ($env{'form.lti_topmenu_'.$idx} || $env{'form.lti_inlinemenu_'.$idx}) {
                   $confhash{$itemid}{lcmenu} = [];
                   my @possmenu = &Apache::loncommon::get_env_multiple('form.lti_menuitem_'.$idx);
                   foreach my $field (@possmenu) {
                       if (exists($menutitles{$field})) {
                           if ($field eq 'grades') {
                               next unless ($env{'form.lti_inlinemenu_'.$idx});
                         }                          }
                           push(@{$confhash{$itemid}{lcmenu}},$field);
                     }                      }
                     if ($env{'form.ltitools_version_'.$i} eq 'LTI-1p0') {                  }
                         $confhash{$itemid}{'version'} = $env{'form.ltitools_version_'.$i};              }
               if ($confhash{$itemid}{'crsinc'}) {
                   if (($env{'form.lti_mapcrs_'.$idx} eq 'course_offering_sourcedid') ||
                       ($env{'form.lti_mapcrs_'.$idx} eq 'context_id'))  {
                       $confhash{$itemid}{'mapcrs'} = $env{'form.lti_mapcrs_'.$idx};
                   } elsif ($env{'form.lti_mapcrs_'.$idx} eq 'other') {
                       my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx};
                       $mapcrs =~ s/(`)/'/g;
                       $mapcrs =~ s/^\s+|\s+$//g;
                       $confhash{$itemid}{'mapcrs'} = $mapcrs;
                   }
                   my @posstypes = &Apache::loncommon::get_env_multiple('form.lti_mapcrstype_'.$idx);
                   my @crstypes;
                   foreach my $type (sort(@posstypes)) {
                       if ($posscrstype{$type}) {
                           push(@crstypes,$type);
                     }                      }
                     if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') {                  }
                         $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i};                  $confhash{$itemid}{'mapcrstype'} = \@crstypes;
                   if ($env{'form.lti_storecrs_'.$idx}) {
                       $confhash{$itemid}{'storecrs'} = 1;
                   }
                   if ($env{'form.lti_makecrs_'.$idx}) {
                       $confhash{$itemid}{'makecrs'} = 1;
                   }
                   foreach my $ltirole (@lticourseroles) {
                       my $possrole = $env{'form.lti_maprole_'.$ltirole.'_'.$idx};
                       if (grep(/^\Q$possrole\E$/,@courseroles)) {
                           $confhash{$itemid}{'maproles'}{$ltirole} = $possrole;
                     }                      }
                     foreach my $size ('width','height') {                  }
                         $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//;                  my @possenroll = &Apache::loncommon::get_env_multiple('form.lti_selfenroll_'.$idx);
                         $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//;                  my @selfenroll;
                         if ($env{'form.ltitools_'.$size.'_'.$i} =~ /^\d+$/) {                  foreach my $type (sort(@possenroll)) {
                             $confhash{$itemid}{'display'}{$size} = $env{'form.ltitools_'.$size.'_'.$i};                      if ($posslticrs{$type}) {
                             if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') {                          push(@selfenroll,$type);
                                 if ($domconfig{$action}{$itemid}{'display'}{$size} ne $confhash{$itemid}{'display'}{$size}) {  
                                     $changes{$itemid} = 1;  
                                 }  
                             } else {  
                                 $changes{$itemid} = 1;  
                             }  
                         } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') {  
                             if ($domconfig{$action}{$itemid}{'display'}{$size} ne '') {  
                                 $changes{$itemid} = 1;  
                             }  
                         }  
                     }                      }
                     foreach my $item ('linktext','explanation') {                  }
                         $env{'form.ltitools_'.$item.'_'.$i} =~ s/^\s+//;                  $confhash{$itemid}{'selfenroll'} = \@selfenroll;
                         $env{'form.ltitools_'.$item.'_'.$i} =~ s/\s+$//;                  if ($env{'form.lti_crssec_'.$idx}) {
                         if ($env{'form.ltitools_'.$item.'_'.$i} ne '') {                      if ($env{'form.lti_crssecsrc_'.$idx} eq 'course_section_sourcedid') {
                             $confhash{$itemid}{'display'}{$item} = $env{'form.ltitools_'.$item.'_'.$i};                          $confhash{$itemid}{'section'} = $env{'form.lti_crssecsrc_'.$idx};
                             if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') {                      } elsif ($env{'form.lti_crssecsrc_'.$idx} eq 'other') {
                                 if ($domconfig{$action}{$itemid}{'display'}{$item} ne $confhash{$itemid}{'display'}{$item}) {                          my $section = $env{'form.lti_customsection_'.$idx};
                                     $changes{$itemid} = 1;                          $section =~ s/(`)/'/g;
                                 }                          $section =~ s/^\s+|\s+$//g;
                             } else {                          if ($section ne '') {
                                 $changes{$itemid} = 1;                              $confhash{$itemid}{'section'} = $section;
                             }  
                         } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') {  
                             if ($domconfig{$action}{$itemid}{'display'}{$item} ne '') {  
                                 $changes{$itemid} = 1;  
                             }  
                         }                          }
                     }                      }
                     if ($env{'form.ltitools_target_'.$i} eq 'window') {                  }
                         $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i};                  foreach my $field ('passback','roster') {
                     } elsif ($env{'form.ltitools_target_'.$i} eq 'tab') {                      if ($env{'form.lti_'.$field.'_'.$idx}) {
                         $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i};                          $confhash{$itemid}{$field} = 1;
                       }
                   }
                   if ($env{'form.lti_passback_'.$idx}) {
                       if ($env{'form.lti_passbackformat_'.$idx} eq '1.0') {
                           $confhash{$itemid}{'passbackformat'} = '1.0';
                     } else {                      } else {
                         $confhash{$itemid}{'display'}{'target'} = 'iframe';                          $confhash{$itemid}{'passbackformat'} = '1.1';
                     }                      }
                     if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') {                  }
                         if ($domconfig{$action}{$itemid}{'display'}{'target'} ne $confhash{$itemid}{'display'}{'target'}) {              }
               unless (($idx eq 'add') || ($changes{$itemid})) {
                   if ($confhash{$itemid}{'crsinc'}) {
                       foreach my $field ('mapcrs','storecrs','makecrs','section','passback','roster') {
                           if ($currlti{$field} ne $confhash{$itemid}{$field}) {
                             $changes{$itemid} = 1;                              $changes{$itemid} = 1;
                         }                          }
                     } else {  
                         $changes{$itemid} = 1;  
                     }                      }
                     foreach my $extra ('passback','roster') {                      unless ($changes{$itemid}) {
                         if ($env{'form.ltitools_'.$extra.'_'.$i}) {                          if ($currlti{'passback'} eq $confhash{$itemid}{'passback'}) {
                             $confhash{$itemid}{$extra} = 1;                              if ($currlti{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) {
                         }                                  $changes{$itemid} = 1;
                         if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) {                              }
                             $changes{$itemid} = 1;  
                         }                          }
                     }                      }
                     my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i);                      foreach my $field ('mapcrstype','selfenroll') {
                     foreach my $item ('label','title','target','linktext','explanation') {                          unless ($changes{$itemid}) {
                         if (grep(/^\Q$item\E$/,@courseconfig)) {                              if (ref($currlti{$field}) eq 'ARRAY') {
                             $confhash{$itemid}{'crsconf'}{$item} = 1;                                  if (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                             if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') {                                      my @diffs = &Apache::loncommon::compare_arrays($currlti{$field},
                                 if ($domconfig{$action}{$itemid}{'crsconf'}{$item} ne $confhash{$itemid}{'crsconf'}{$item}) {                                                                                     $confhash{$itemid}{$field});
                                       if (@diffs) {
                                           $changes{$itemid} = 1;
                                       }
                                   } elsif (@{$currlti{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{$field}} > 0) {
                                     $changes{$itemid} = 1;                                      $changes{$itemid} = 1;
                                 }                                  }
                             } else {  
                                 $changes{$itemid} = 1;  
                             }                              }
                         }                          }
                     }                      }
                     my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_fields_'.$i);                      unless ($changes{$itemid}) {
                     foreach my $field (@fields) {                          if (ref($currlti{'maproles'}) eq 'HASH') {
                         if ($possfield{$field}) {                              if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
                             if ($field eq 'roles') {                                  foreach my $ltirole (keys(%{$currlti{'maproles'}})) {
                                 foreach my $role (@courseroles) {                                      if ($currlti{'maproles'}{$ltirole} ne
                                     my $choice = $env{'form.ltitools_roles_'.$role.'_'.$i};                                          $confhash{$itemid}{'maproles'}{$ltirole}) {
                                     if (($choice ne '') && ($posslti{$choice})) {                                          $changes{$itemid} = 1;
                                         $confhash{$itemid}{'roles'}{$role} = $choice;                                          last;
                                         if ($role eq 'cc') {  
                                             $confhash{$itemid}{'roles'}{'co'} = $choice;  
                                         }  
                                     }                                      }
                                     if (ref($domconfig{$action}{$itemid}{'roles'}) eq 'HASH') {                                  }
                                         if ($domconfig{$action}{$itemid}{'roles'}{$role} ne $confhash{$itemid}{'roles'}{$role}) {                                  unless ($changes{$itemid}) {
                                       foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) {
                                           if ($confhash{$itemid}{'maproles'}{$ltirole} ne
                                               $currlti{'maproles'}{$ltirole}) {
                                             $changes{$itemid} = 1;                                              $changes{$itemid} = 1;
                                               last;
                                         }                                          }
                                     } elsif ($confhash{$itemid}{'roles'}{$role}) {  
                                         $changes{$itemid} = 1;  
                                     }                                      }
                                 }                                  }
                             } else {                              } elsif (keys(%{$currlti{'maproles'}}) > 0) {
                                 $confhash{$itemid}{'fields'}{$field} = 1;                                  $changes{$itemid} = 1;
                                 if (ref($domconfig{$action}{$itemid}{'fields'}) eq 'HASH') {                              }
                                     if ($domconfig{$action}{$itemid}{'fields'}{$field} ne $confhash{$itemid}{'fields'}{$field}) {                          } elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
                               unless ($changes{$itemid}) {
                                   if (keys(%{$confhash{$itemid}{'maproles'}}) > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               }
                           }
                       }
                   }
                   unless ($changes{$itemid}) {
                       foreach my $field ('mapuser','lcauth','lcauthparm','topmenu','inlinemenu','callback') {
                           if ($currlti{$field} ne $confhash{$itemid}{$field}) {
                               $changes{$itemid} = 1;
                           }
                       }
                       unless ($changes{$itemid}) {
                           foreach my $field ('makeuser','lcmenu') {
                               if (ref($currlti{$field}) eq 'ARRAY') {
                                   if (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                       my @diffs = &Apache::loncommon::compare_arrays($currlti{$field},
                                                                                      $confhash{$itemid}{$field});
                                       if (@diffs) {
                                         $changes{$itemid} = 1;                                          $changes{$itemid} = 1;
                                     }                                      }
                                 } else {                                  } elsif (@{$currlti{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{$field}} > 0) {
                                     $changes{$itemid} = 1;                                      $changes{$itemid} = 1;
                                 }                                  }
                             }                              }
                         }                          }
                     }                      }
                     $allpos[$newpos] = $itemid;  
                 }                  }
                 if ($imgdeletions{$itemid}) {              }
                     $changes{$itemid} = 1;          }
                     #FIXME need to obsolete item in RES space          if ($is_home) {
                 } elsif ($env{'form.ltitools_image_'.$i.'.filename'}) {              my $keyitem = 'form.lti_key_'.$idx;
                     my ($imgurl,$error) = &process_ltitools_image($r,$dom,$confname,'ltitools_image_'.$i,              $env{$keyitem} =~ s/(`)/'/g;
                                                                  $itemid,$configuserok,$switchserver,              if ($env{$keyitem} ne '') {
                                                                  $author_ok);                  $ltienc{$itemid}{'key'} = $env{$keyitem};
                     if ($imgurl) {                  unless ($changes{$itemid}) {
                         $confhash{$itemid}{'image'} = $imgurl;                      if ($currlti{'key'} ne $env{$keyitem}) {
                         $changes{$itemid} = 1;                          $changes{$itemid} = 1;
                     }                      }
                     if ($error) {                  }
                         &Apache::lonnet::logthis($error);              }
                         $errors .= '<li><span class="LC_error">'.$error.'</span></li>';              my $secretitem = 'form.lti_secret_'.$idx;
                     }              $env{$secretitem} =~ s/(`)/'/g;
                 } elsif ($domconfig{$action}{$itemid}{'image'}) {              if ($currlti{'usable'}) {
                     $confhash{$itemid}{'image'} =                  if ($env{'form.lti_changesecret_'.$idx}) {
                        $domconfig{$action}{$itemid}{'image'};                      if ($env{$secretitem} ne '') {
                 }                          if ($privnum && $cipher) {
                 if ($customadds{$i}) {                              $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem});
                     my $name = $env{'form.ltitools_custom_name_'.$i};                              $confhash{$itemid}{'cipher'} = $privnum;
                     $name =~ s/(`)/'/g;                          } else {
                     $name =~ s/^\s+//;                              $ltienc{$itemid}{'secret'} = $env{$secretitem};
                     $name =~ s/\s+$//;                          }
                     my $value = $env{'form.ltitools_custom_value_'.$i};  
                     $value =~ s/(`)/'/g;  
                     $value =~ s/^\s+//;  
                     $value =~ s/\s+$//;  
                     if ($name ne '') {  
                         $confhash{$itemid}{'custom'}{$name} = $value;  
                         $changes{$itemid} = 1;                          $changes{$itemid} = 1;
                     }                      }
                   } else {
                       $ltienc{$itemid}{'secret'} = $currlti{'secret'};
                       $confhash{$itemid}{'cipher'} = $currlti{'cipher'};
                 }                  }
                 my %customdels;                  if (ref($ltienc{$itemid}) eq 'HASH') {
                 my @customdeletions = &Apache::loncommon::get_env_multiple('form.ltitools_customdel_'.$i);                      if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'secret'} ne '')) {
                 if (@customdeletions) {                          $confhash{$itemid}{'usable'} = 1;
                     $changes{$itemid} = 1;                      }
                 }                  }
                 map { $customdels{$_} = 1; } @customdeletions;              } elsif ($env{$secretitem} ne '') {
                 if (ref($domconfig{$action}{$itemid}{'custom'}) eq 'HASH') {                  if ($privnum && $cipher) {
                     foreach my $key (keys(%{$domconfig{$action}{$itemid}{'custom'}})) {                      $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem});
                         unless ($customdels{$key}) {                      $confhash{$itemid}{'cipher'} = $privnum;
                             if ($env{'form.ltitools_customval_'.$key.'_'.$i} ne '') {                  } else {
                                 $confhash{$itemid}{'custom'}{$key} = $env{'form.ltitools_customval_'.$key.'_'.$i};                      $ltienc{$itemid}{'secret'} = $env{$secretitem};
                             }                  }
                             if ($domconfig{$action}{$itemid}{'custom'}{$key} ne $env{'form.ltitools_customval_'.$key.'_'.$i}) {                  if (ref($ltienc{$itemid}) eq 'HASH') {
                                 $changes{$itemid} = 1;                      if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'key'} ne '')) {
                             }                          $confhash{$itemid}{'usable'} = 1;
                         }  
                     }                      }
                 }                  }
                 unless ($changes{$itemid}) {                  $changes{$itemid} = 1;
                     foreach my $key (keys(%{$domconfig{$action}{$itemid}})) {              }
                         if (ref($domconfig{$action}{$itemid}{$key}) eq 'HASH') {          }
                             if (ref($confhash{$itemid}{$key}) eq 'HASH') {          unless ($changes{$itemid}) {
                                 foreach my $innerkey (keys(%{$domconfig{$action}{$itemid}{$key}})) {              foreach my $key (keys(%currlti)) {
                                     unless (exists($confhash{$itemid}{$key}{$innerkey})) {                  if (ref($currlti{$key}) eq 'HASH') {
                                         $changes{$itemid} = 1;                      if (ref($confhash{$itemid}{$key}) eq 'HASH') {
                                         last;                          foreach my $innerkey (keys(%{$currlti{$key}})) {
                                     }                              unless (exists($confhash{$itemid}{$key}{$innerkey})) {
                                 }  
                             } elsif (keys(%{$domconfig{$action}{$itemid}{$key}}) > 0) {  
                                 $changes{$itemid} = 1;                                  $changes{$itemid} = 1;
                                   last;
                             }                              }
                         }                          }
                         last if ($changes{$itemid});                      } elsif (keys(%{$currlti{$key}}) > 0) {
                           $changes{$itemid} = 1;
                     }                      }
                 }                  }
                   last if ($changes{$itemid});
             }              }
         }          }
     }      }
Line 10691  sub modify_ltitools { Line 13901  sub modify_ltitools {
             }              }
         }          }
     }      }
     my %ltitoolshash = (  
                           $action => { %confhash }      if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) {
                        );          return &mt('No changes made.');
     my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash,      }
                                              $dom);  
       my %ltihash = (
                         $action => { %confhash }
                     );
       my %ltienchash;
   
       if ($is_home) {
           %ltienchash = (
                            $action => { %ltienc }
                         );
       }
       if (keys(%secchanges)) {
           $ltihash{'ltisec'} = \%newltisec;
           if ($secchanges{'linkprot'}) {
               if ($is_home) {
                   $ltienchash{'linkprot'} = \%newltienc;
               }
           }
       }
       my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash,$dom);
     if ($putresult eq 'ok') {      if ($putresult eq 'ok') {
         my %ltienchash = (          if (keys(%ltienchash)) {
                              $action => { %encconfig }              &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1);
                          );          }
         &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom);          $resulttext = &mt('Changes made:').'<ul>';
           if (keys(%secchanges) > 0) {
               $resulttext .= &lti_security_results($dom,'lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore);
               if (exists($secchanges{'linkprot'})) {
                   $resulttext .= $linkprotoutput;
               }
           }
         if (keys(%changes) > 0) {          if (keys(%changes) > 0) {
             my $cachetime = 24*60*60;              my $cachetime = 24*60*60;
             my %ltiall = %confhash;              &Apache::lonnet::do_cache_new('lti',$dom,\%confhash,$cachetime);
             foreach my $id (keys(%ltiall)) {  
                 if (ref($encconfig{$id}) eq 'HASH') {  
                     foreach my $item ('key','secret') {  
                         $ltiall{$id}{$item} = $encconfig{$id}{$item};  
                     }  
                 }  
             }  
             &Apache::lonnet::do_cache_new('ltitools',$dom,\%ltiall,$cachetime);  
             if (ref($lastactref) eq 'HASH') {              if (ref($lastactref) eq 'HASH') {
                 $lastactref->{'ltitools'} = 1;                  $lastactref->{'lti'} = 1;
             }              }
             $resulttext = &mt('Changes made:').'<ul>';  
             my %bynum;              my %bynum;
             foreach my $itemid (sort(keys(%changes))) {              foreach my $itemid (sort(keys(%changes))) {
                 my $position = $confhash{$itemid}{'order'};                  if (ref($confhash{$itemid}) eq 'HASH') {
                 $bynum{$position} = $itemid;                      my $position = $confhash{$itemid}{'order'};
                       $bynum{$position} = $itemid;
                   }
             }              }
             foreach my $pos (sort { $a <=> $b } keys(%bynum)) {              foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                 my $itemid = $bynum{$pos};                  my $itemid = $bynum{$pos};
                 if (ref($confhash{$itemid}) ne 'HASH') {                  if (ref($confhash{$itemid}) eq 'HASH') {
                     $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';                      $resulttext .= '<li><b>'.$confhash{$itemid}{'consumer'}.'</b><ul>';
                 } else {  
                     $resulttext .= '<li><b>'.$confhash{$itemid}{'title'}.'</b>';  
                     if ($confhash{$itemid}{'image'}) {  
                         $resulttext .= '&nbsp;'.  
                                        '<img src="'.$confhash{$itemid}{'image'}.'"'.  
                                        ' alt="'.&mt('Tool Provider icon').'" />';  
                     }  
                     $resulttext .= '</li><ul>';  
                     my $position = $pos + 1;                      my $position = $pos + 1;
                     $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';                      $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                     foreach my $item ('version','msgtype','url') {                      foreach my $item ('version','lifetime') {
                         if ($confhash{$itemid}{$item} ne '') {                          if ($confhash{$itemid}{$item} ne '') {
                             $resulttext .= '<li>'.$lt{$item}.':&nbsp;'.$confhash{$itemid}{$item}.'</li>';                              $resulttext .= '<li>'.$lt{$item}.':&nbsp;'.$confhash{$itemid}{$item}.'</li>';
                         }                          }
                     }                      }
                     if ($encconfig{$itemid}{'key'} ne '') {                      if ($ltienc{$itemid}{'key'} ne '') {
                         $resulttext .= '<li>'.$lt{'key'}.':&nbsp;'.$encconfig{$itemid}{'key'}.'</li>';                          $resulttext .= '<li>'.$lt{'key'}.':&nbsp;'.$ltienc{$itemid}{'key'}.'</li>';
                     }  
                     if ($encconfig{$itemid}{'secret'} ne '') {  
                         $resulttext .= '<li>'.$lt{'secret'}.':&nbsp;';  
                         my $num = length($encconfig{$itemid}{'secret'});  
                         $resulttext .= ('*'x$num).'</li>';  
                     }  
                     $resulttext .= '<li>'.&mt('Configurable in course:');  
                     my @possconfig = ('label','title','target','linktext','explanation');  
                     my $numconfig = 0;  
                     if (ref($confhash{$itemid}{'crsconf'}) eq 'HASH') {  
                         foreach my $item (@possconfig) {  
                             if ($confhash{$itemid}{'crsconf'}{$item}) {  
                                 $numconfig ++;  
                                 $resulttext .= ' "'.$lt{'crs'.$item}.'"';  
                             }  
                         }  
                     }                      }
                     if (!$numconfig) {                      if ($ltienc{$itemid}{'secret'} ne '') {
                         $resulttext .= &mt('None');                          $resulttext .= '<li>'.$lt{'secret'}.':&nbsp;['.&mt('not shown').']</li>';
                     }                      }
                     $resulttext .= '</li>';                      if ($confhash{$itemid}{'requser'}) {
                     foreach my $item ('passback','roster') {                          if ($confhash{$itemid}{'callback'}) {
                         $resulttext .= '<li>'.$lt{$item}.'&nbsp;';                              $resulttext .= '<li>'.&mt('Callback setting').': '.$confhash{$itemid}{'callback'}.'</li>';
                         if ($confhash{$itemid}{$item}) {  
                             $resulttext .= &mt('Yes');  
                         } else {                          } else {
                             $resulttext .= &mt('No');                              $resulttext .= '<li>'.&mt('Callback to logout LON-CAPA on log out from Consumer').'</li>';
                         }  
                         $resulttext .= '</li>';  
                     }  
                     if (ref($confhash{$itemid}{'display'}) eq 'HASH') {  
                         my $displaylist;  
                         if ($confhash{$itemid}{'display'}{'target'}) {  
                             $displaylist = &mt('Display target').':&nbsp;'.  
                                            $confhash{$itemid}{'display'}{'target'}.',';  
                         }                          }
                         foreach my $size ('width','height') {                          if ($confhash{$itemid}{'mapuser'}) {
                             if ($confhash{$itemid}{'display'}{$size}) {                              my $shownmapuser;
                                 $displaylist .= ('&nbsp;'x2).$lt{$size}.':&nbsp;'.                              if ($confhash{$itemid}{'mapuser'} eq 'lis_person_sourcedid') {
                                                 $confhash{$itemid}{'display'}{$size}.',';                                  $shownmapuser = $lt{'sourcedid'}.' (lis_person_sourcedid)';
                               } elsif ($confhash{$itemid}{'mapuser'} eq 'lis_person_contact_email_primary') {
                                   $shownmapuser = $lt{'email'}.' (lis_person_contact_email_primary)';
                               } else {
                                   $shownmapuser = &mt('Other').' ('.$confhash{$itemid}{'mapuser'}.')';
                             }                              }
                               $resulttext .= '<li>'.&mt('LON-CAPA username').': '.$shownmapuser.'</li>';
                         }                          }
                         if ($displaylist) {                          if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') {
                             $displaylist =~ s/,$//;                              if (@{$confhash{$itemid}{'makeuser'}} > 0) {
                             $resulttext .= '<li>'.$displaylist.'</li>';                                  $resulttext .= '<li>'.&mt('Following roles may create user accounts: [_1]',
                         }                                                            join(', ',@{$confhash{$itemid}{'makeuser'}})).'<br />';
                         foreach my $item ('linktext','explanation') {                                  if ($confhash{$itemid}{'lcauth'} eq 'lti') {
                             if ($confhash{$itemid}{'display'}{$item}) {                                      $resulttext .= &mt('New users will only be able to authenticate via LTI').'</li>';
                                 $resulttext .= '<li>'.$lt{$item}.':&nbsp;'.$confhash{$itemid}{'display'}{$item}.'</li>';                                  } else {
                                       $resulttext .= &mt('New users will be assigned LON-CAPA authentication: [_1]',
                                                          $confhash{$itemid}{'lcauth'});
                                       if ($confhash{$itemid}{'lcauth'} eq 'internal') {
                                           $resulttext .= '; '.&mt('a randomly generated password will be created');
                                       } elsif ($confhash{$itemid}{'lcauth'} eq 'localauth') {
                                           if ($confhash{$itemid}{'lcauthparm'} ne '') {
                                               $resulttext .= ' '.&mt('with argument: [_1]',$confhash{$itemid}{'lcauthparm'});
                                           }
                                       } else {
                                           $resulttext .= '; '.&mt('Kerberos domain: [_1]',$confhash{$itemid}{'lcauthparm'});
                                       }
                                   }
                                   $resulttext .= '</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('User account creation not permitted.').'</li>';
                             }                              }
                         }                          }
                     }                          if (ref($confhash{$itemid}{'instdata'}) eq 'ARRAY') {
                     if (ref($confhash{$itemid}{'fields'}) eq 'HASH') {                              if (@{$confhash{$itemid}{'instdata'}} > 0) {
                         my $fieldlist;                                  $resulttext .= '<li>'.&mt('Institutional data will be used when creating a new user for: [_1]',
                         foreach my $field (@allfields) {                                                            join(', ',map { $fieldtitles{$_}; } @{$confhash{$itemid}{'instdata'}})).'</li>';
                             if ($confhash{$itemid}{'fields'}{$field}) {                              } else {
                                 $fieldlist .= ('&nbsp;'x2).$lt{$field}.',';                                  $resulttext .= '<li>'.&mt('No institutional data used when creating a new user.').'</li>';
                             }                              }
                         }                          }
                         if ($fieldlist) {                          foreach my $item ('topmenu','inlinemenu') {
                             $fieldlist =~ s/,$//;                              $resulttext .= '<li>'.$lt{$item}.':&nbsp;';
                             $resulttext .= '<li>'.&mt('Data sent').':'.$fieldlist.'</li>';                              if ($confhash{$itemid}{$item}) {
                         }                                  $resulttext .= &mt('Yes');
                     }                              } else {
                     if (ref($confhash{$itemid}{'roles'}) eq 'HASH') {                                  $resulttext .= &mt('No');
                         my $rolemaps;  
                         foreach my $role (@courseroles) {  
                             if ($confhash{$itemid}{'roles'}{$role}) {  
                                 $rolemaps .= ('&nbsp;'x2).&Apache::lonnet::plaintext($role,'Course').'='.  
                                              $confhash{$itemid}{'roles'}{$role}.',';  
                             }                              }
                               $resulttext .= '</li>';
                         }                          }
                         if ($rolemaps) {                          if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') {
                             $rolemaps =~ s/,$//;                              if (@{$confhash{$itemid}{'lcmenu'}} > 0) {
                             $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';                                  $resulttext .= '<li>'.&mt('Menu items:').' '.
                         }                                                 join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>';
                     }                              } else {
                     if (ref($confhash{$itemid}{'custom'}) eq 'HASH') {                                  $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>';
                         my $customlist;  
                         if (keys(%{$confhash{$itemid}{'custom'}})) {  
                             foreach my $key (sort(keys(%{$confhash{$itemid}{'custom'}}))) {  
                                 $customlist .= $key.':'.$confhash{$itemid}{'custom'}{$key}.('&nbsp;'x2);  
                             }                              }
                         }                          }
                         if ($customlist) {                          if ($confhash{$itemid}{'crsinc'}) {
                             $resulttext .= '<li>'.&mt('Custom items').':'.$customlist.'</li>';                              if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
                                   my $rolemaps;
                                   foreach my $role (@ltiroles) {
                                       if ($confhash{$itemid}{'maproles'}{$role}) {
                                           $rolemaps .= ('&nbsp;'x2).$role.'='.
                                                        &Apache::lonnet::plaintext($confhash{$itemid}{'maproles'}{$role},
                                                                                   'Course').',';
                                       }
                                   }
                                   if ($rolemaps) {
                                       $rolemaps =~ s/,$//;
                                       $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';
                                   }
                               }
                               if ($confhash{$itemid}{'mapcrs'}) {
                                   $resulttext .= '<li>'.&mt('Unique course identifier').': '.$confhash{$itemid}{'mapcrs'}.'</li>';
                               }
                               if (ref($confhash{$itemid}{'mapcrstype'}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{'mapcrstype'}} > 0) {
                                       $resulttext .= '<li>'.&mt('Mapping for the following LON-CAPA course types: [_1]',
                                                      join(', ',map { $coursetypetitles{$_}; } @coursetypes)).
                                                      '</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('No mapping to LON-CAPA courses').'</li>';
                                   }
                               }
                               if ($confhash{$itemid}{'storecrs'}) {
                                   $resulttext .= '<li>'.&mt('Store mapping of course identifier to LON-CAPA CourseID').': '.$confhash{$itemid}{'storecrs'}.'</li>';
                               }
                               if ($confhash{$itemid}{'makecrs'}) {
                                   $resulttext .= '<li>'.&mt('Instructor may create course (if absent).').'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('Instructor may not create course (if absent).').'</li>';
                               }
                               if (ref($confhash{$itemid}{'selfenroll'}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{'selfenroll'}} > 0) {
                                       $resulttext .= '<li>'.&mt('Self-enrollment for following roles: [_1]',
                                                                 join(', ',@{$confhash{$itemid}{'selfenroll'}})).
                                                      '</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('Self-enrollment not permitted').'</li>';
                                   }
                               }
                               if ($confhash{$itemid}{'section'}) {
                                   if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') {
                                       $resulttext .= '<li>'.&mt('User section from standard field:').
                                                            ' (course_section_sourcedid)'.'</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('User section from:').' '.
                                                             $confhash{$itemid}{'section'}.'</li>';
                                   }
                               } else {
                                   $resulttext .= '<li>'.&mt('No section assignment').'</li>';
                               }
                               foreach my $item ('passback','roster','topmenu','inlinemenu') {
                                   $resulttext .= '<li>'.$lt{$item}.':&nbsp;';
                                   if ($confhash{$itemid}{$item}) {
                                       $resulttext .= &mt('Yes');
                                       if ($item eq 'passback') {
                                           if ($confhash{$itemid}{'passbackformat'} eq '1.0') {
                                               $resulttext .= '&nbsp;('.&mt('Outcomes Extension (1.0)').')';
                                           } elsif ($confhash{$itemid}{'passbackformat'} eq '1.1') {
                                               $resulttext .= '&nbsp;('.&mt('Outcomes Service (1.1)').')';
                                           }
                                       }
                                   } else {
                                       $resulttext .= &mt('No');
                                   }
                                   $resulttext .= '</li>';
                               }
                               if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{'lcmenu'}} > 0) {
                                       $resulttext .= '<li>'.&mt('Menu items:').' '.
                                                      join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>';
                                   }
                               }
                         }                          }
                     }                      }
                     $resulttext .= '</ul></li>';                      $resulttext .= '</ul></li>';
                 }                  }
             }              }
             $resulttext .= '</ul>';              if (keys(%deletions)) {
         } else {                  foreach my $itemid (sort { $a <=> $b } keys(%deletions)) {
             $resulttext = &mt('No changes made.');                      $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';
                   }
               }
           }
           $resulttext .= '</ul>';
           if (ref($lastactref) eq 'HASH') {
               if (($secchanges{'encrypt'}) || ($secchanges{'private'}) || (exists($secchanges{'suggested'}))) {
                   &Apache::lonnet::get_domain_defaults($dom,1);
                   $lastactref->{'domdefaults'} = 1;
               }
         }          }
     } else {      } else {
         $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';          $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
Line 10847  sub modify_ltitools { Line 14139  sub modify_ltitools {
     return $resulttext;      return $resulttext;
 }  }
   
 sub process_ltitools_image {  sub get_priv_creds {
     my ($r,$dom,$confname,$caller,$itemid,$configuserok,$switchserver,$author_ok) = @_;      my ($dom,$home,$encchg,$encrypt,$storedsec) = @_;
     my $filename = $env{'form.'.$caller.'.filename'};      my ($needenc,$cipher,$privnum);
     my ($error,$url);      my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
     my ($width,$height) = (21,21);      if (($encchg) && (ref($encrypt) eq 'HASH')) {
     if ($configuserok eq 'ok') {          $needenc = $encrypt->{'consumers'}
         if ($switchserver) {      } else {
             $error = &mt('Upload of Tool Provider (LTI) icon is not permitted to this server: [_1]',          $needenc = $domdefs{'ltienc_consumers'};
                          $switchserver);      }
         } elsif ($author_ok eq 'ok') {      if ($needenc) {
             my ($result,$imageurl,$madethumb) =          if (($storedsec eq 'ok') || ((ref($domdefs{'ltiprivhosts'}) eq 'ARRAY') &&
                 &publishlogo($r,'upload',$caller,$dom,$confname,                                       (grep(/^\Q$home\E$/,@{$domdefs{'ltiprivhosts'}})))) {
                              "ltitools/$itemid/icon",$width,$height);                          my %privhash  = &Apache::lonnet::restore_dom('lti','private',$dom,$home,1);
             if ($result eq 'ok') {              my $privkey = $privhash{'key'};
                 if ($madethumb) {              $privnum = $privhash{'version'};
                     my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$});              if (($privnum) && ($privkey ne '')) {
                     my $imagethumb = "$path/tn-".$imagefile;                  $cipher = Crypt::CBC->new({'key'     => $privkey,
                     $url = $imagethumb;                                            'cipher'  => 'DES'});
                 } else {  
                     $url = $imageurl;  
                 }  
             } else {  
                 $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);  
             }              }
         } else {  
             $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3].  Error was: [_4].",$filename,$confname,$dom,$author_ok);  
         }          }
     } else {  
         $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3].  Error was: [_4].",$filename,$confname,$dom,$configuserok);  
     }      }
     return ($url,$error);      return ($cipher,$privnum);
 }  }
   
 sub get_ltitools_id {  sub get_lti_id {
     my ($cdom,$title) = @_;      my ($domain,$consumer,$dbname) = @_;
     # get lock on ltitools db      unless (($dbname eq 'lti') || ($dbname eq 'suggested')) {
           return ('','invalid db');
       }
       # get lock on db
     my $lockhash = {      my $lockhash = {
                       lock => $env{'user.name'}.                        lock => $env{'user.name'}.
                               ':'.$env{'user.domain'},                                ':'.$env{'user.domain'},
                    };                     };
     my $tries = 0;      my $tries = 0;
     my $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom);      my $gotlock = &Apache::lonnet::newput_dom($dbname,$lockhash,$domain);
     my ($id,$error);      my ($id,$error);
   
     while (($gotlock ne 'ok') && ($tries<10)) {      while (($gotlock ne 'ok') && ($tries<10)) {
         $tries ++;          $tries ++;
         sleep (0.1);          sleep (0.1);
         $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom);          $gotlock = &Apache::lonnet::newput_dom($dbname,$lockhash,$domain);
     }      }
     if ($gotlock eq 'ok') {      if ($gotlock eq 'ok') {
         my %currids = &Apache::lonnet::dump_dom('ltitools',$cdom);          my %currids = &Apache::lonnet::dump_dom($dbname,$domain);
         if ($currids{'lock'}) {          if ($currids{'lock'}) {
             delete($currids{'lock'});              delete($currids{'lock'});
             if (keys(%currids)) {              if (keys(%currids)) {
Line 10909  sub get_ltitools_id { Line 14195  sub get_ltitools_id {
                 $id = 1;                  $id = 1;
             }              }
             if ($id) {              if ($id) {
                 unless (&Apache::lonnet::newput_dom('ltitools',{ $id => $title },$cdom) eq 'ok') {                  unless (&Apache::lonnet::newput_dom($dbname,{ $id => $consumer },$domain) eq 'ok') {
                     $error = 'nostore';                      $error = 'nostore';
                 }                  }
             } else {              } else {
                 $error = 'nonumber';                  $error = 'nonumber';
             }              }
         }          }
         my $dellockoutcome = &Apache::lonnet::del_dom('ltitools',['lock'],$cdom);          my $dellockoutcome = &Apache::lonnet::del_dom($dbname,['lock'],$domain);
     } else {      } else {
         $error = 'nolock';          $error = 'nolock';
     }      }
Line 10936  sub modify_autoenroll { Line 14222  sub modify_autoenroll {
     my %title = ( run => 'Auto-enrollment active',      my %title = ( run => 'Auto-enrollment active',
                   sender => 'Sender for notification messages',                    sender => 'Sender for notification messages',
                   coowners => 'Automatic assignment of co-ownership to instructors of record (institutional data)',                    coowners => 'Automatic assignment of co-ownership to instructors of record (institutional data)',
                   failsafe => 'Failsafe for no drops if institutional data missing for a section');                    autofailsafe => 'Failsafe for no drops if institutional data missing for a section');
     my @offon = ('off','on');      my @offon = ('off','on');
     my $sender_uname = $env{'form.sender_uname'};      my $sender_uname = $env{'form.sender_uname'};
     my $sender_domain = $env{'form.sender_domain'};      my $sender_domain = $env{'form.sender_domain'};
Line 10946  sub modify_autoenroll { Line 14232  sub modify_autoenroll {
         $sender_domain = '';          $sender_domain = '';
     }      }
     my $coowners = $env{'form.autoassign_coowners'};      my $coowners = $env{'form.autoassign_coowners'};
       my $autofailsafe = $env{'form.autoenroll_autofailsafe'};
       $autofailsafe =~ s{^\s+|\s+$}{}g;
       if ($autofailsafe =~ /\D/) {
           undef($autofailsafe);
       }
     my $failsafe = $env{'form.autoenroll_failsafe'};      my $failsafe = $env{'form.autoenroll_failsafe'};
     $failsafe =~ s{^\s+|\s+$}{}g;      unless (($failsafe eq 'zero') || ($failsafe eq 'any')) {
     if ($failsafe =~ /\D/) {          $failsafe = 'off';
         undef($failsafe);          undef($autofailsafe);
     }      }
     my %autoenrollhash =  (      my %autoenrollhash =  (
                        autoenroll => { 'run' => $env{'form.autoenroll_run'},                         autoenroll => { 'run' => $env{'form.autoenroll_run'},
                                        'sender_uname' => $sender_uname,                                         'sender_uname' => $sender_uname,
                                        'sender_domain' => $sender_domain,                                         'sender_domain' => $sender_domain,
                                        'co-owners' => $coowners,                                         'co-owners' => $coowners,
                                        'autofailsafe' => $failsafe,                                         'autofailsafe' => $autofailsafe,
                                          'failsafe' => $failsafe,
                                 }                                  }
                      );                       );
     my $putresult = &Apache::lonnet::put_dom('configuration',\%autoenrollhash,      my $putresult = &Apache::lonnet::put_dom('configuration',\%autoenrollhash,
Line 10984  sub modify_autoenroll { Line 14276  sub modify_autoenroll {
         } elsif ($coowners) {          } elsif ($coowners) {
             $changes{'coowners'} = 1;              $changes{'coowners'} = 1;
         }          }
         if ($currautoenroll{'autofailsafe'} ne $failsafe) {          if ($currautoenroll{'autofailsafe'} ne $autofailsafe) {
             $changes{'autofailsafe'} = 1;              $changes{'autofailsafe'} = 1;
         }          }
           if ($currautoenroll{'failsafe'} ne $failsafe) {
               $changes{'failsafe'} = 1;
           }
         if (keys(%changes) > 0) {          if (keys(%changes) > 0) {
             $resulttext = &mt('Changes made:').'<ul>';              $resulttext = &mt('Changes made:').'<ul>';
             if ($changes{'run'}) {              if ($changes{'run'}) {
Line 11007  sub modify_autoenroll { Line 14302  sub modify_autoenroll {
                 }                  }
             }              }
             if ($changes{'autofailsafe'}) {              if ($changes{'autofailsafe'}) {
                 if ($failsafe ne '') {                  if ($autofailsafe ne '') {
                     $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section set to: [_1]',$failsafe).'</li>';                      $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section set to: [_1]',$autofailsafe).'</li>';
                 } else {                  } else {
                     $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section: deleted');                      $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section not in use').'</li>';
                 }                  }
               }
               if ($changes{'failsafe'}) {
                   if ($failsafe eq 'off') {
                       unless ($changes{'autofailsafe'}) {
                           $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section not in use').'</li>';
                       }
                   } elsif ($failsafe eq 'zero') {
                       $resulttext .= '<li>'.&mt('Failsafe applies if retrieved section enrollment is zero').'</li>';
                   } else {
                       $resulttext .= '<li>'.&mt('Failsafe applies if retrieved section enrollment is zero or greater').'</li>';
                   }
               }
               if (($changes{'autofailsafe'}) || ($changes{'failsafe'})) {
                 &Apache::lonnet::get_domain_defaults($dom,1);                  &Apache::lonnet::get_domain_defaults($dom,1);
                 if (ref($lastactref) eq 'HASH') {                  if (ref($lastactref) eq 'HASH') {
                     $lastactref->{'domdefaults'} = 1;                      $lastactref->{'domdefaults'} = 1;
Line 11038  sub modify_autoupdate { Line 14346  sub modify_autoupdate {
     }      }
     my @offon = ('off','on');      my @offon = ('off','on');
     my %title = &Apache::lonlocal::texthash (      my %title = &Apache::lonlocal::texthash (
                    run => 'Auto-update:',                      run        => 'Auto-update:',
                    classlists => 'Updates to user information in classlists?'                      classlists => 'Updates to user information in classlists?',
                       unexpired  => 'Skip updates for users without active or future roles?',
                       lastactive => 'Skip updates for inactive users?',
                 );                  );
     my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);      my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
     my %fieldtitles = &Apache::lonlocal::texthash (      my %fieldtitles = &Apache::lonlocal::texthash (
Line 11083  sub modify_autoupdate { Line 14393  sub modify_autoupdate {
     my %updatehash = (      my %updatehash = (
                       autoupdate => { run => $env{'form.autoupdate_run'},                        autoupdate => { run => $env{'form.autoupdate_run'},
                                       classlists => $env{'form.classlists'},                                        classlists => $env{'form.classlists'},
                                         unexpired  => $env{'form.unexpired'},
                                       fields => {%fields},                                        fields => {%fields},
                                       lockablenames => \@lockablenames,                                        lockablenames => \@lockablenames,
                                     }                                      }
                      );                       );
       my $lastactivedays;
       if ($env{'form.lastactive'}) {
           $lastactivedays = $env{'form.lastactivedays'};
           $lastactivedays =~ s/^\s+|\s+$//g;
           unless ($lastactivedays =~ /^\d+$/) {
               undef($lastactivedays);
               $env{'form.lastactive'} = 0;
           }
       }
       $updatehash{'autoupdate'}{'lastactive'} = $lastactivedays;
     foreach my $key (keys(%currautoupdate)) {      foreach my $key (keys(%currautoupdate)) {
         if (($key eq 'run') || ($key eq 'classlists')) {          if (($key eq 'run') || ($key eq 'classlists') || ($key eq 'unexpired') || ($key eq 'lastactive')) {
             if (exists($updatehash{autoupdate}{$key})) {              if (exists($updatehash{autoupdate}{$key})) {
                 if ($currautoupdate{$key} ne $updatehash{autoupdate}{$key}) {                  if ($currautoupdate{$key} ne $updatehash{autoupdate}{$key}) {
                     $changes{$key} = 1;                      $changes{$key} = 1;
Line 11134  sub modify_autoupdate { Line 14455  sub modify_autoupdate {
             $changes{'lockablenames'} = 1;              $changes{'lockablenames'} = 1;
         }          }
     }      }
       unless (grep(/^unexpired$/,keys(%currautoupdate))) {
           if ($updatehash{'autoupdate'}{'unexpired'}) {
               $changes{'unexpired'} = 1;
           }
       }
       unless (grep(/^lastactive$/,keys(%currautoupdate))) {
           if ($updatehash{'autoupdate'}{'lastactive'} ne '') {
               $changes{'lastactive'} = 1;
           }
       }
     foreach my $item (@{$types},'default') {      foreach my $item (@{$types},'default') {
         if (defined($fields{$item})) {          if (defined($fields{$item})) {
             if (ref($currautoupdate{'fields'}) eq 'HASH') {              if (ref($currautoupdate{'fields'}) eq 'HASH') {
Line 11196  sub modify_autoupdate { Line 14527  sub modify_autoupdate {
                     my $newvalue;                      my $newvalue;
                     if ($key eq 'run') {                      if ($key eq 'run') {
                         $newvalue = $offon[$env{'form.autoupdate_run'}];                          $newvalue = $offon[$env{'form.autoupdate_run'}];
                       } elsif ($key eq 'lastactive') {
                           $newvalue = $offon[$env{'form.lastactive'}];
                           unless ($lastactivedays eq '') {
                               $newvalue .= '; '.&mt('inactive = no activity in last [quant,_1,day]',$lastactivedays);
                           }
                     } else {                      } else {
                         $newvalue = $offon[$env{'form.'.$key}];                          $newvalue = $offon[$env{'form.'.$key}];
                     }                      }
Line 11509  sub modify_contacts { Line 14845  sub modify_contacts {
     my @contacts = ('supportemail','adminemail');      my @contacts = ('supportemail','adminemail');
     my @mailings = ('errormail','packagesmail','helpdeskmail','otherdomsmail',      my @mailings = ('errormail','packagesmail','helpdeskmail','otherdomsmail',
                     'lonstatusmail','requestsmail','updatesmail','idconflictsmail','hostipmail');                      'lonstatusmail','requestsmail','updatesmail','idconflictsmail','hostipmail');
     my @toggles = ('reporterrors','reportupdates');      my @toggles = ('reporterrors','reportupdates','reportstatus');
       my @lonstatus = ('threshold','sysmail','weights','excluded');
     my ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();      my ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();
     foreach my $type (@mailings) {      foreach my $type (@mailings) {
         @{$newsetting{$type}} =           @{$newsetting{$type}} = 
Line 11542  sub modify_contacts { Line 14879  sub modify_contacts {
             $contacts_hash{'contacts'}{$item} = $env{'form.'.$item};              $contacts_hash{'contacts'}{$item} = $env{'form.'.$item};
         }          }
     }      }
       my ($lonstatus_defs,$lonstatus_names) = &Apache::loncommon::lon_status_items();
       foreach my $item (@lonstatus) {
           if ($item eq 'excluded') {
               my (%serverhomes,@excluded);
               map { $serverhomes{$_} = 1; } values(%Apache::lonnet::serverhomeIDs);
               my @possexcluded = &Apache::loncommon::get_env_multiple('form.errorexcluded');
               if (@possexcluded) {
                   foreach my $id (sort(@possexcluded)) {
                       if ($serverhomes{$id}) {
                           push(@excluded,$id);
                       }
                   }
               }
               if (@excluded) {
                   $contacts_hash{'contacts'}{'lonstatus'}{$item} = \@excluded;
               }
           } elsif ($item eq 'weights') {
               foreach my $type ('E','W','N','U') {
                   $env{'form.error'.$item.'_'.$type} =~ s/^\s+|\s+$//g;
                   if ($env{'form.error'.$item.'_'.$type} =~ /^\d+$/) {
                       unless ($env{'form.error'.$item.'_'.$type} == $lonstatus_defs->{$type}) {
                           $contacts_hash{'contacts'}{'lonstatus'}{$item}{$type} =
                               $env{'form.error'.$item.'_'.$type};
                       }
                   }
               }
           } elsif (($item eq 'threshold') || ($item eq 'sysmail')) {
               $env{'form.error'.$item} =~ s/^\s+|\s+$//g;
               if ($env{'form.error'.$item} =~ /^\d+$/) {
                   unless ($env{'form.error'.$item} == $lonstatus_defs->{$item}) {
                       $contacts_hash{'contacts'}{'lonstatus'}{$item} = $env{'form.error'.$item};
                   }
               }
           }
       }
     if ((ref($fields) eq 'ARRAY') && (ref($possoptions) eq 'HASH')) {      if ((ref($fields) eq 'ARRAY') && (ref($possoptions) eq 'HASH')) {
         foreach my $field (@{$fields}) {          foreach my $field (@{$fields}) {
             if (ref($possoptions->{$field}) eq 'ARRAY') {              if (ref($possoptions->{$field}) eq 'ARRAY') {
Line 11596  sub modify_contacts { Line 14968  sub modify_contacts {
                     $contacts_hash{'contacts'}{'overrides'}{$type}{'include'} = $includeloc{$type}.':'.&escape($includestr{$type});                      $contacts_hash{'contacts'}{'overrides'}{$type}{'include'} = $includeloc{$type}.':'.&escape($includestr{$type});
                     $newsetting{'override_'.$type}{'include'} = $contacts_hash{'contacts'}{'overrides'}{$type}{'include'};                      $newsetting{'override_'.$type}{'include'} = $contacts_hash{'contacts'}{'overrides'}{$type}{'include'};
                 }                  }
             }                  }
         }          }
     }      }
     if (keys(%currsetting) > 0) {      if (keys(%currsetting) > 0) {
Line 11680  sub modify_contacts { Line 15052  sub modify_contacts {
                 }                  }
             }              }
         }          }
           if (ref($currsetting{'lonstatus'}) eq 'HASH') {
               foreach my $key ('excluded','weights','threshold','sysmail') {
                   if ($key eq 'excluded') {
                       if ((ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') &&
                           (ref($contacts_hash{contacts}{lonstatus}{excluded}) eq 'ARRAY')) {
                           if ((ref($currsetting{'lonstatus'}{$key}) eq 'ARRAY') &&
                               (@{$currsetting{'lonstatus'}{$key}})) {
                               my @diffs =
                                   &Apache::loncommon::compare_arrays($contacts_hash{contacts}{lonstatus}{excluded},
                                                                      $currsetting{'lonstatus'}{$key});
                               if (@diffs) {
                                   push(@{$changes{'lonstatus'}},$key);
                               }
                           } elsif (@{$contacts_hash{contacts}{lonstatus}{excluded}}) {
                               push(@{$changes{'lonstatus'}},$key);
                           }
                       } elsif ((ref($currsetting{'lonstatus'}{$key}) eq 'ARRAY') &&
                                (@{$currsetting{'lonstatus'}{$key}})) {
                           push(@{$changes{'lonstatus'}},$key);
                       }
                   } elsif ($key eq 'weights') {
                       if ((ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') &&
                           (ref($contacts_hash{contacts}{lonstatus}{$key}) eq 'HASH')) {
                           if (ref($currsetting{'lonstatus'}{$key}) eq 'HASH') {
                               foreach my $type ('E','W','N','U') {
                                   unless ($contacts_hash{contacts}{lonstatus}{$key}{$type} eq
                                           $currsetting{'lonstatus'}{$key}{$type}) {
                                       push(@{$changes{'lonstatus'}},$key);
                                       last;
                                   }
                               }
                           } else {
                               foreach my $type ('E','W','N','U') {
                                   if ($contacts_hash{contacts}{lonstatus}{$key}{$type} ne '') {
                                       push(@{$changes{'lonstatus'}},$key);
                                       last;
                                   }
                               }
                           }
                       } elsif (ref($currsetting{'lonstatus'}{$key}) eq 'HASH') {
                           foreach my $type ('E','W','N','U') {
                               if ($currsetting{'lonstatus'}{$key}{$type} ne '') {
                                   push(@{$changes{'lonstatus'}},$key);
                                   last;
                               }
                           }
                       }
                   } elsif (($key eq 'threshold') || ($key eq 'sysmail')) {
                       if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') {
                           if ($currsetting{'lonstatus'}{$key} =~ /^\d+$/) {
                               if ($currsetting{'lonstatus'}{$key} != $contacts_hash{contacts}{lonstatus}{$key}) {
                                   push(@{$changes{'lonstatus'}},$key);
                               }
                           } elsif ($contacts_hash{contacts}{lonstatus}{$key} =~ /^\d+$/) {
                               push(@{$changes{'lonstatus'}},$key);
                           }
                       } elsif ($currsetting{'lonstatus'}{$key} =~ /^\d+$/) {
                           push(@{$changes{'lonstatus'}},$key);
                       }
                   }
               }
           } else {
               if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') {
                   foreach my $key ('excluded','weights','threshold','sysmail') {
                       if (exists($contacts_hash{contacts}{lonstatus}{$key})) {
                           push(@{$changes{'lonstatus'}},$key);
                       }
                   }
               }
           }
     } else {      } else {
         my %default;          my %default;
         $default{'supportemail'} = $Apache::lonnet::perlvar{'lonSupportEMail'};          $default{'supportemail'} = $Apache::lonnet::perlvar{'lonSupportEMail'};
Line 11725  sub modify_contacts { Line 15167  sub modify_contacts {
                 }                  }
             }              }
         }          }
           if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') {
               foreach my $key ('excluded','weights','threshold','sysmail') {
                   if (exists($contacts_hash{contacts}{lonstatus}{$key})) {
                       push(@{$changes{'lonstatus'}},$key);
                   }
               }
           }
     }      }
     foreach my $item (@toggles) {      foreach my $item (@toggles) {
         if (($env{'form.'.$item} == 1) && ($currsetting{$item} == 0)) {          if (($env{'form.'.$item} == 1) && ($currsetting{$item} == 0)) {
Line 11850  sub modify_contacts { Line 15299  sub modify_contacts {
                 }                  }
             }              }
             my @offon = ('off','on');              my @offon = ('off','on');
               my $corelink = &core_link_msu();
             if ($changes{'reporterrors'}) {              if ($changes{'reporterrors'}) {
                 $resulttext .= '<li>'.                  $resulttext .= '<li>'.
                                &mt('E-mail error reports to [_1] set to "'.                                 &mt('E-mail error reports to [_1] set to "'.
                                    $offon[$env{'form.reporterrors'}].'".',                                     $offon[$env{'form.reporterrors'}].'".',
                                    &Apache::loncommon::modal_link('http://loncapa.org/core.html',                                     $corelink).
                                        &mt('LON-CAPA core group - MSU'),600,500)).  
                                '</li>';                                 '</li>';
             }              }
             if ($changes{'reportupdates'}) {              if ($changes{'reportupdates'}) {
                 $resulttext .= '<li>'.                  $resulttext .= '<li>'.
                                 &mt('E-mail record of completed LON-CAPA updates to [_1] set to "'.                                  &mt('E-mail record of completed LON-CAPA updates to [_1] set to "'.
                                     $offon[$env{'form.reportupdates'}].'".',                                      $offon[$env{'form.reportupdates'}].'".',
                                     &Apache::loncommon::modal_link('http://loncapa.org/core.html',                                      $corelink).
                                         &mt('LON-CAPA core group - MSU'),600,500)).  
                                 '</li>';                                  '</li>';
             }              }
               if ($changes{'reportstatus'}) {
                   $resulttext .= '<li>'.
                                   &mt('E-mail status if errors above threshold to [_1] set to "'.
                                       $offon[$env{'form.reportstatus'}].'".',
                                       $corelink).
                                   '</li>';
               }
               if (ref($changes{'lonstatus'}) eq 'ARRAY') {
                   $resulttext .= '<li>'.
                                  &mt('Nightly status check e-mail settings').':<ul>';
                   my (%defval,%use_def,%shown);
                   $defval{'threshold'} = $lonstatus_defs->{'threshold'};
                   $defval{'sysmail'} = $lonstatus_defs->{'sysmail'};
                   $defval{'weights'} =
                       join(', ',map { $lonstatus_names->{$_}.'='.$lonstatus_defs->{$_}; } ('E','W','N','U'));
                   $defval{'excluded'} = &mt('None');
                   if (ref($contacts_hash{'contacts'}{'lonstatus'}) eq 'HASH') {
                       foreach my $item ('threshold','sysmail','weights','excluded') {
                           if (exists($contacts_hash{'contacts'}{'lonstatus'}{$item})) {
                               if (($item eq 'threshold') || ($item eq 'sysmail')) {
                                   $shown{$item} = $contacts_hash{'contacts'}{'lonstatus'}{$item};
                               } elsif ($item eq 'weights') {
                                   if (ref($contacts_hash{'contacts'}{'lonstatus'}{$item}) eq 'HASH') {
                                       foreach my $type ('E','W','N','U') {
                                           $shown{$item} .= $lonstatus_names->{$type}.'=';
                                           if (exists($contacts_hash{'contacts'}{'lonstatus'}{$item}{$type})) {
                                               $shown{$item} .= $contacts_hash{'contacts'}{'lonstatus'}{$item}{$type};
                                           } else {
                                               $shown{$item} .= $lonstatus_defs->{$type};
                                           }
                                           $shown{$item} .= ', ';
                                       }
                                       $shown{$item} =~ s/, $//;
                                   } else {
                                       $shown{$item} = $defval{$item};
                                   }
                               } elsif ($item eq 'excluded') {
                                   if (ref($contacts_hash{'contacts'}{'lonstatus'}{$item}) eq 'ARRAY') {
                                       $shown{$item} = join(', ',@{$contacts_hash{'contacts'}{'lonstatus'}{$item}});
                                   } else {
                                       $shown{$item} = $defval{$item};
                                   }
                               }
                           } else {
                               $shown{$item} = $defval{$item};
                           }
                       }
                   } else {
                       foreach my $item ('threshold','weights','excluded','sysmail') {
                           $shown{$item} = $defval{$item};
                       }
                   }
                   foreach my $item ('threshold','weights','excluded','sysmail') {
                       $resulttext .= '<li>'.&mt($titles->{'error'.$item}.' -- [_1]',
                                             $shown{$item}).'</li>';
                   }
                   $resulttext .= '</ul></li>';
               }
             if ((ref($changes{'helpform'}) eq 'ARRAY') && (ref($fields) eq 'ARRAY')) {              if ((ref($changes{'helpform'}) eq 'ARRAY') && (ref($fields) eq 'ARRAY')) {
                 my (@optional,@required,@unused,$maxsizechg);                  my (@optional,@required,@unused,$maxsizechg);
                 foreach my $field (@{$changes{'helpform'}}) {                  foreach my $field (@{$changes{'helpform'}}) {
Line 11912  sub modify_contacts { Line 15418  sub modify_contacts {
         }          }
     } else {      } else {
         $resulttext = '<span class="LC_error">'.          $resulttext = '<span class="LC_error">'.
             &mt('An error occurred: [_1].',$putresult).'</span>';              &mt('An error occurred: [_1]',$putresult).'</span>';
     }      }
     return $resulttext;      return $resulttext;
 }  }
Line 12084  sub modify_passwords { Line 15590  sub modify_passwords {
     }      }
     if ($env{'form.passwords_customfile.filename'} ne '') {      if ($env{'form.passwords_customfile.filename'} ne '') {
         my $servadm = $r->dir_config('lonAdmEMail');          my $servadm = $r->dir_config('lonAdmEMail');
         my $servadm = $r->dir_config('lonAdmEMail');  
         my ($configuserok,$author_ok,$switchserver) =          my ($configuserok,$author_ok,$switchserver) =
             &config_check($dom,$confname,$servadm);              &config_check($dom,$confname,$servadm);
         my $error;          my $error;
Line 12093  sub modify_passwords { Line 15598  sub modify_passwords {
                 $error = &mt("Upload of file containing domain-specific text is not permitted to this server: [_1]",$switchserver);                  $error = &mt("Upload of file containing domain-specific text is not permitted to this server: [_1]",$switchserver);
             } else {              } else {
                 if ($author_ok eq 'ok') {                  if ($author_ok eq 'ok') {
                       my $modified = [];
                     my ($result,$customurl) =                      my ($result,$customurl) =
                         &publishlogo($r,'upload','passwords_customfile',$dom,                          &Apache::lonconfigsettings::publishlogo($r,'upload','passwords_customfile',$dom,
                                      $confname,'customtext/resetpw','','',$customfn);                                                                  $confname,'customtext/resetpw','','',$customfn,
                                                                   $modified);
                     if ($result eq 'ok') {                      if ($result eq 'ok') {
                         $newvalues{'resetcustom'} = $customurl;                          $newvalues{'resetcustom'} = $customurl;
                         $changes{'reset'} = 1;                          $changes{'reset'} = 1;
                           &update_modify_urls($r,$modified);
                     } else {                      } else {
                         $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$customfn,$result);                          $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$customfn,$result);
                     }                      }
Line 12151  sub modify_passwords { Line 15659  sub modify_passwords {
             $updatedefaults = 1;              $updatedefaults = 1;
         }          }
     }      }
     foreach my $rule ('min','max','numsaved') {      &password_rule_changes('passwords',\%newvalues,\%current,\%changes);
         $env{'form.passwords_'.$rule} =~ s/^\s+|\s+$//g;  
         my $ruleok;  
         if ($rule eq 'min') {  
             if ($env{'form.passwords_'.$rule} =~ /^\d+$/) {  
                 if ($env{'form.passwords_'.$rule} >= $Apache::lonnet::passwdmin) {  
                     $ruleok = 1;  
                 }  
             }  
         } elsif (($env{'form.passwords_'.$rule} =~ /^\d+$/) &&  
                  ($env{'form.passwords_'.$rule} ne '0')) {  
             $ruleok = 1;  
         }  
         if ($ruleok) {  
             $newvalues{$rule} = $env{'form.passwords_'.$rule};  
             if (exists($current{$rule})) {  
                 if ($newvalues{$rule} ne $current{$rule}) {  
                     $changes{'rules'} = 1;  
                 }  
             } elsif ($rule eq 'min') {  
                 if ($staticdefaults{$rule} ne $newvalues{$rule}) {  
                     $changes{'rules'} = 1;  
                 }  
             }  
         } elsif (exists($current{$rule})) {  
             $changes{'rules'} = 1;  
         }  
     }  
     my @posschars = &Apache::loncommon::get_env_multiple('form.passwords_chars');  
     my @chars;  
     foreach my $item (sort(@posschars)) {  
         if ($item =~ /^(uc|lc|num|spec)$/) {  
             push(@chars,$item);  
         }  
     }  
     $newvalues{'chars'} = \@chars;  
     unless ($changes{'rules'}) {  
         if (ref($current{'chars'}) eq 'ARRAY') {  
             my @diffs = &Apache::loncommon::compare_arrays($current{'chars'},\@chars);  
             if (@diffs > 0) {  
                 $changes{'rules'} = 1;  
             }  
         } else {  
             if (@chars > 0) {  
                 $changes{'rules'} = 1;  
             }  
         }  
     }  
     my %crsownerchg = (      my %crsownerchg = (
                         by => [],                          by => [],
                         for => [],                          for => [],
Line 12252  sub modify_passwords { Line 15713  sub modify_passwords {
                             $resulttext .= '<li>'.&mt('CAPTCHA validation set to use: original CAPTCHA').'</li>';                              $resulttext .= '<li>'.&mt('CAPTCHA validation set to use: original CAPTCHA').'</li>';
                         } elsif ($confighash{'passwords'}{'captcha'} eq 'recaptcha') {                          } elsif ($confighash{'passwords'}{'captcha'} eq 'recaptcha') {
                             $resulttext .= '<li>'.&mt('CAPTCHA validation set to use: reCAPTCHA').' '.                              $resulttext .= '<li>'.&mt('CAPTCHA validation set to use: reCAPTCHA').' '.
                                            &mt('version: [_1]',$confighash{'passwords'}{'recaptchaversion'}).'<br />'.                                             &mt('version: [_1]',$confighash{'passwords'}{'recaptchaversion'}).'<br />';
                                            &mt('Public key: [_1]',$confighash{'passwords'}{'recaptchapub'}).'</br>'.                              if (ref($confighash{'passwords'}{'recaptchakeys'}) eq 'HASH') {
                                            &mt('Private key: [_1]',$confighash{'passwords'}{'recaptchapriv'}).'</li>';                                  $resulttext .= &mt('Public key: [_1]',$confighash{'passwords'}{'recaptchakeys'}{'public'}).'</br>'.
                                                  &mt('Private key: [_1]',$confighash{'passwords'}{'recaptchakeys'}{'private'}).'</li>';
                               }
                         } else {                          } else {
                             $resulttext .= '<li>'.&mt('No CAPTCHA validation').'</li>';                              $resulttext .= '<li>'.&mt('No CAPTCHA validation').'</li>';
                         }                          }
Line 12324  sub modify_passwords { Line 15787  sub modify_passwords {
                                 $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'</li>';                                  $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'</li>';
                             }                              }
                         } else {                          } else {
                             $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA usedfor verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'</li>';                              $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'</li>';
                         }                          }
                         if ($confighash{'passwords'}{'resetremove'}) {                          if ($confighash{'passwords'}{'resetremove'}) {
                             $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" web form not shown').'</li>';                              $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" web form not shown').'</li>';
Line 12333  sub modify_passwords { Line 15796  sub modify_passwords {
                         }                          }
                         if ($confighash{'passwords'}{'resetcustom'}) {                          if ($confighash{'passwords'}{'resetcustom'}) {
                             my $customlink = &Apache::loncommon::modal_link($confighash{'passwords'}{'resetcustom'},                              my $customlink = &Apache::loncommon::modal_link($confighash{'passwords'}{'resetcustom'},
                                                                             $titles{custom},600,500);                                                                              &mt('custom text'),600,500,undef,undef,
                             $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" form includes [_1]',$customlink).'</li>';                                                                              undef,undef,'background-color:#ffffff');
                               $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" form includes: [_1]',$customlink).'</li>';
                         } else {                          } else {
                             $resulttext .= '<li>'.&mt('No custom text included in preamble to "Forgot Password" form').'</li>';                              $resulttext .= '<li>'.&mt('No custom text included in preamble to "Forgot Password" form').'</li>';
                         }                          }
Line 12380  sub modify_passwords { Line 15844  sub modify_passwords {
                                 $resulttext .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$confighash{'passwords'}{$rule}).'</li>';                                  $resulttext .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$confighash{'passwords'}{$rule}).'</li>';
                             }                              }
                         }                          }
                           if (ref($confighash{'passwords'}{'chars'}) eq 'ARRAY') {
                               if (@{$confighash{'passwords'}{'chars'}} > 0) {
                                   my %rulenames = &Apache::lonlocal::texthash(
                                                        uc => 'At least one upper case letter',
                                                        lc => 'At least one lower case letter',
                                                        num => 'At least one number',
                                                        spec => 'At least one non-alphanumeric',
                                                      );
                                   my $needed = '<ul><li>'.
                                                join('</li><li>',map {$rulenames{$_} } @{$confighash{'passwords'}{'chars'}}).
                                                '</li></ul>';
                                   $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                           }
                     } elsif ($key eq 'crsownerchg') {                      } elsif ($key eq 'crsownerchg') {
                         if (ref($confighash{'passwords'}{'crsownerchg'}) eq 'HASH') {                          if (ref($confighash{'passwords'}{'crsownerchg'}) eq 'HASH') {
                             if ((@{$confighash{'passwords'}{'crsownerchg'}{'by'}} == 0) ||                              if ((@{$confighash{'passwords'}{'crsownerchg'}{'by'}} == 0) ||
Line 12437  sub modify_passwords { Line 15919  sub modify_passwords {
     return $resulttext;      return $resulttext;
 }  }
   
   sub password_rule_changes {
       my ($prefix,$newvalues,$current,$changes) = @_;
       return unless ((ref($newvalues) eq 'HASH') &&
                      (ref($current) eq 'HASH') &&
                      (ref($changes) eq 'HASH'));
       my (@rules,%staticdefaults);
       if ($prefix eq 'passwords') {
           @rules = ('min','max','numsaved');
       } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {
           @rules = ('min','max');
       }
       $staticdefaults{'min'} = $Apache::lonnet::passwdmin;
       foreach my $rule (@rules) {
           $env{'form.'.$prefix.'_'.$rule} =~ s/^\s+|\s+$//g;
           my $ruleok;
           if ($rule eq 'min') {
               if ($env{'form.'.$prefix.'_'.$rule} =~ /^\d+$/) {
                   if ($env{'form.'.$prefix.'_'.$rule} >= $staticdefaults{$rule}) {
                       $ruleok = 1;
                   }
               }
           } elsif (($env{'form.'.$prefix.'_'.$rule} =~ /^\d+$/) &&
                    ($env{'form.'.$prefix.'_'.$rule} ne '0')) {
               $ruleok = 1;
           }
           if ($ruleok) {
               $newvalues->{$rule} = $env{'form.'.$prefix.'_'.$rule};
               if (exists($current->{$rule})) {
                   if ($newvalues->{$rule} ne $current->{$rule}) {
                       $changes->{'rules'} = 1;
                   }
               } elsif ($rule eq 'min') {
                   if ($staticdefaults{$rule} ne $newvalues->{$rule}) {
                       $changes->{'rules'} = 1;
                   }
               } else {
                   $changes->{'rules'} = 1;
               }
           } elsif (exists($current->{$rule})) {
               $changes->{'rules'} = 1;
           }
       }
       my @posschars = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_chars');
       my @chars;
       foreach my $item (sort(@posschars)) {
           if ($item =~ /^(uc|lc|num|spec)$/) {
               push(@chars,$item);
           }
       }
       $newvalues->{'chars'} = \@chars;
       unless ($changes->{'rules'}) {
           if (ref($current->{'chars'}) eq 'ARRAY') {
               my @diffs = &Apache::loncommon::compare_arrays($current->{'chars'},\@chars);
               if (@diffs > 0) {
                   $changes->{'rules'} = 1;
               }
           } else {
               if (@chars > 0) {
                   $changes->{'rules'} = 1;
               }
           }
       }
       return;
   }
   
 sub modify_usercreation {  sub modify_usercreation {
     my ($dom,%domconfig) = @_;      my ($dom,%domconfig) = @_;
     my ($resulttext,%curr_usercreation,%changes,%authallowed,%cancreate,%save_usercreate);      my ($resulttext,%curr_usercreation,%changes,%authallowed,%cancreate,%save_usercreate);
Line 12521  sub modify_usercreation { Line 16068  sub modify_usercreation {
     }      }
   
     my @authen_contexts = ('author','course','domain');      my @authen_contexts = ('author','course','domain');
     my @authtypes = ('int','krb4','krb5','loc');      my @authtypes = ('int','krb4','krb5','loc','lti');
     my %authhash;      my %authhash;
     foreach my $item (@authen_contexts) {      foreach my $item (@authen_contexts) {
         my @authallowed =  &Apache::loncommon::get_env_multiple('form.'.$item.'_auth');          my @authallowed =  &Apache::loncommon::get_env_multiple('form.'.$item.'_auth');
Line 12736  sub modify_selfcreation { Line 16283  sub modify_selfcreation {
 # Populate $cancreate{'selfcreate'} array reference with types of user, for which self-creation of user accounts  # Populate $cancreate{'selfcreate'} array reference with types of user, for which self-creation of user accounts
 # is permitted.  # is permitted.
 #  #
   
     my ($emailrules,$emailruleorder) = &Apache::lonnet::inst_userrules($dom,'email');      my ($emailrules,$emailruleorder) = &Apache::lonnet::inst_userrules($dom,'email');
   
     my (@statuses,%email_rule);      my (@statuses,%email_rule);
Line 13520  sub modify_selfcreation { Line 17066  sub modify_selfcreation {
 }  }
   
 sub process_captcha {  sub process_captcha {
     my ($container,$changes,$newsettings,$current) = @_;      my ($container,$changes,$newsettings,$currsettings) = @_;
     return unless ((ref($changes) eq 'HASH') && (ref($newsettings) eq 'HASH') || (ref($current) eq 'HASH'));      return unless ((ref($changes) eq 'HASH') && (ref($newsettings) eq 'HASH'));
     $newsettings->{'captcha'} = $env{'form.'.$container.'_captcha'};      $newsettings->{'captcha'} = $env{'form.'.$container.'_captcha'};
     unless ($newsettings->{'captcha'} eq 'recaptcha' || $newsettings->{'captcha'} eq 'notused') {      unless ($newsettings->{'captcha'} eq 'recaptcha' || $newsettings->{'captcha'} eq 'notused') {
         $newsettings->{'captcha'} = 'original';          $newsettings->{'captcha'} = 'original';
     }      }
     if ($current->{'captcha'} ne $newsettings->{'captcha'}) {      my %current;
       if (ref($currsettings) eq 'HASH') {
           %current = %{$currsettings};
       }
       if ($current{'captcha'} ne $newsettings->{'captcha'}) {
         if ($container eq 'cancreate') {          if ($container eq 'cancreate') {
             if (ref($changes->{'cancreate'}) eq 'ARRAY') {              if (ref($changes->{'cancreate'}) eq 'ARRAY') {
                 push(@{$changes->{'cancreate'}},'captcha');                  push(@{$changes->{'cancreate'}},'captcha');
Line 13556  sub process_captcha { Line 17106  sub process_captcha {
         }          }
         $newsettings->{'recaptchaversion'} = $newversion;          $newsettings->{'recaptchaversion'} = $newversion;
     }      }
     if (ref($current->{'recaptchakeys'}) eq 'HASH') {      if (ref($current{'recaptchakeys'}) eq 'HASH') {
         $currpub = $current->{'recaptchakeys'}{'public'};          $currpub = $current{'recaptchakeys'}{'public'};
         $currpriv = $current->{'recaptchakeys'}{'private'};          $currpriv = $current{'recaptchakeys'}{'private'};
         unless ($newsettings->{'captcha'} eq 'recaptcha') {          unless ($newsettings->{'captcha'} eq 'recaptcha') {
             $newsettings->{'recaptchakeys'} = {              $newsettings->{'recaptchakeys'} = {
                                                  public  => '',                                                   public  => '',
Line 13566  sub process_captcha { Line 17116  sub process_captcha {
                                               }                                                }
         }          }
     }      }
     if ($current->{'captcha'} eq 'recaptcha') {      if ($current{'captcha'} eq 'recaptcha') {
         $currversion = $current->{'recaptchaversion'};          $currversion = $current{'recaptchaversion'};
         if ($currversion ne '2') {          if ($currversion ne '2') {
             $currversion = 1;              $currversion = 1;
         }          }
Line 13710  sub modify_defaults { Line 17260  sub modify_defaults {
     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);      my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
     my @items = ('auth_def','auth_arg_def','lang_def','timezone_def','datelocale_def',      my @items = ('auth_def','auth_arg_def','lang_def','timezone_def','datelocale_def',
                  'portal_def');                   'portal_def');
     my @authtypes = ('internal','krb4','krb5','localauth');      my @authtypes = ('internal','krb4','krb5','localauth','lti');
     foreach my $item (@items) {      foreach my $item (@items) {
         $newvalues{$item} = $env{'form.'.$item};          $newvalues{$item} = $env{'form.'.$item};
         if ($item eq 'auth_def') {          if ($item eq 'auth_def') {
Line 13747  sub modify_defaults { Line 17297  sub modify_defaults {
             }              }
         } elsif ($item eq 'portal_def') {          } elsif ($item eq 'portal_def') {
             if ($newvalues{$item} ne '') {              if ($newvalues{$item} ne '') {
                 unless ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) {                  if ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) {
                       foreach my $field ('email','web') {
                           if ($env{'form.'.$item.'_'.$field}) {
                               $newvalues{$item.'_'.$field} = $env{'form.'.$item.'_'.$field};
                           }
                       }
                   } else {
                     push(@errors,$item);                      push(@errors,$item);
                 }                  }
             }              }
         }          }
         if (grep(/^\Q$item\E$/,@errors)) {          if (grep(/^\Q$item\E$/,@errors)) {
             $newvalues{$item} = $domdefaults{$item};              $newvalues{$item} = $domdefaults{$item};
               if ($item eq 'portal_def') {
                   if ($domdefaults{$item}) {
                       foreach my $field ('email','web') {
                           if (exists($domdefaults{$item.'_'.$field})) {
                               $newvalues{$item.'_'.$field} = $domdefaults{$item.'_'.$field};
                           }
                       }
                   }
               }
         } elsif ($domdefaults{$item} ne $newvalues{$item}) {          } elsif ($domdefaults{$item} ne $newvalues{$item}) {
             $changes{$item} = 1;              $changes{$item} = 1;
         }          }
           if ($item eq 'portal_def') {
               unless (grep(/^\Q$item\E$/,@errors)) {
                   if ($newvalues{$item} eq '') {
                       foreach my $field ('email','web') {
                           if (exists($domdefaults{$item.'_'.$field})) {
                               delete($domdefaults{$item.'_'.$field});
                           }
                       }
                   } else {
                       unless ($changes{$item}) {
                           foreach my $field ('email','web') {
                               if ($domdefaults{$item.'_'.$field} ne $newvalues{$item.'_'.$field}) {
                                   $changes{$item} = 1;
                                   last;
                               }
                           }
                       }
                       foreach my $field ('email','web') {
                           if ($newvalues{$item.'_'.$field}) {
                               $domdefaults{$item.'_'.$field} = $newvalues{$item.'_'.$field};
                           } elsif (exists($domdefaults{$item.'_'.$field})) {
                               delete($domdefaults{$item.'_'.$field});
                           }
                       }
                   }
               }
           }
         $domdefaults{$item} = $newvalues{$item};          $domdefaults{$item} = $newvalues{$item};
     }      }
     my %staticdefaults = (      my %staticdefaults = (
Line 13771  sub modify_defaults { Line 17363  sub modify_defaults {
             $newvalues{$item} = $staticdefaults{$item};              $newvalues{$item} = $staticdefaults{$item};
         }          }
     }      }
       my ($unamemaprules,$ruleorder);
       my @possunamemaprules = &Apache::loncommon::get_env_multiple('form.unamemap_rule');
       if (@possunamemaprules) {
           ($unamemaprules,$ruleorder) =
               &Apache::lonnet::inst_userrules($dom,'unamemap');
           if ((ref($unamemaprules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
               if (@{$ruleorder} > 0) {
                   my %possrules;
                   map { $possrules{$_} = 1; } @possunamemaprules;
                   foreach my $rule (@{$ruleorder}) {
                       if ($possrules{$rule}) {
                           push(@{$newvalues{'unamemap_rule'}},$rule);
                       }
                   }
               }
           }
       }
       if (ref($domdefaults{'unamemap_rule'}) eq 'ARRAY') {
           if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') {
               my @rulediffs = &Apache::loncommon::compare_arrays($domdefaults{'unamemap_rule'},
                                                                  $newvalues{'unamemap_rule'});
               if (@rulediffs) {
                   $changes{'unamemap_rule'} = 1;
                   $domdefaults{'unamemap_rule'} = $newvalues{'unamemap_rule'};
               }
           } elsif (@{$domdefaults{'unamemap_rule'}} > 0) {
               $changes{'unamemap_rule'} = 1;
               delete($domdefaults{'unamemap_rule'});
           }
       } elsif (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') {
           if (@{$newvalues{'unamemap_rule'}} > 0) {
               $changes{'unamemap_rule'} = 1;
               $domdefaults{'unamemap_rule'} = $newvalues{'unamemap_rule'};
           }
       }
     my %defaults_hash = (      my %defaults_hash = (
                          defaults => \%newvalues,                           defaults => \%newvalues,
                         );                          );
Line 13887  sub modify_defaults { Line 17514  sub modify_defaults {
                             $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>';                              $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>';
                         }                          }
                     }                      }
                   } elsif ($item eq 'unamemap_rule') {
                       if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') {
                           my @rulenames;
                           if (ref($unamemaprules) eq 'HASH') {
                               foreach my $rule (@{$newvalues{'unamemap_rule'}}) {
                                   if (ref($unamemaprules->{$rule}) eq 'HASH') {
                                       push(@rulenames,$unamemaprules->{$rule}->{'name'});
                                   }
                               }
                           }
                           if (@rulenames) {
                               $resulttext .= '<li>'.&mt('Mapping for missing usernames includes: [_1]',
                                                        '<ul><li>'.join('</li><li>',@rulenames).'</li></ul>').
                                              '</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('No mapping for missing usernames via standard log-in').'</li>';
                           }
                       } else {
                           $resulttext .= '<li>'.&mt('Mapping for missing usernames via standard log-in deleted').'</li>';
                       }
                 } else {                  } else {
                     my $value = $env{'form.'.$item};                      my $value = $env{'form.'.$item};
                     if ($value eq '') {                      if ($value eq '') {
Line 13898  sub modify_defaults { Line 17545  sub modify_defaults {
                                           krb4       => 'krb4',                                            krb4       => 'krb4',
                                           krb5       => 'krb5',                                            krb5       => 'krb5',
                                           localauth  => 'loc',                                            localauth  => 'loc',
                                             lti        => 'lti',
                         );                          );
                         $value = $authnames{$shortauth{$value}};                          $value = $authnames{$shortauth{$value}};
                     }                      }
                     $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$title->{$item},$value).'</li>';                      $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$title->{$item},$value).'</li>';
                     $mailmsgtext .= "$title->{$item} set to $value\n";                        $mailmsgtext .= "$title->{$item} set to $value\n";
                       if ($item eq 'portal_def') {
                           if ($env{'form.'.$item} ne '') {
                               foreach my $field ('email','web') {
                                   $value = $env{'form.'.$item.'_'.$field};
                                   if ($value) {
                                       $value = &mt('Yes');
                                   } else {
                                       $value = &mt('No');
                                   }
                                   $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$title->{$field},$value).'</li>';
                               }
                           }
                       }
                 }                  }
             }              }
             $resulttext .= '</ul>';              $resulttext .= '</ul>';
Line 13957  sub modify_scantron { Line 17618  sub modify_scantron {
                 $error = &mt("Upload of bubblesheet format file is not permitted to this server: [_1]",$switchserver);                  $error = &mt("Upload of bubblesheet format file is not permitted to this server: [_1]",$switchserver);
             } else {              } else {
                 if ($author_ok eq 'ok') {                  if ($author_ok eq 'ok') {
                       my $modified = [];
                     my ($result,$scantronurl) =                      my ($result,$scantronurl) =
                         &publishlogo($r,'upload','scantronformat',$dom,                          &Apache::lonconfigsettings::publishlogo($r,'upload','scantronformat',$dom,
                                      $confname,'scantron','','',$custom);                                                                  $confname,'scantron','','',$custom,
                                                                   $modified);
                     if ($result eq 'ok') {                      if ($result eq 'ok') {
                         $confhash{'scantron'}{'scantronformat'} = $scantronurl;                          $confhash{'scantron'}{'scantronformat'} = $scantronurl;
                         $changes{'scantronformat'} = 1;                          $changes{'scantronformat'} = 1;
                           &update_modify_urls($r,$modified);
                     } else {                      } else {
                         $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$custom,$result);                          $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$custom,$result);
                     }                      }
Line 14114  sub modify_scantron { Line 17778  sub modify_scantron {
         $resulttext = &mt('No changes made to bubblesheet format settings');          $resulttext = &mt('No changes made to bubblesheet format settings');
     }      }
     if ($errors) {      if ($errors) {
         $resulttext .= &mt('The following errors occurred: ').'<ul>'.          $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.
                        $errors.'</ul>';                         $errors.'</ul></p>';
     }      }
     return $resulttext;      return $resulttext;
 }  }
Line 14844  sub modify_coursedefaults { Line 18508  sub modify_coursedefaults {
     my ($resulttext,$errors,%changes,%defaultshash);      my ($resulttext,$errors,%changes,%defaultshash);
     my %defaultchecked = (      my %defaultchecked = (
                            'uselcmath'       => 'on',                             'uselcmath'       => 'on',
                            'usejsme'         => 'on'                             'usejsme'         => 'on',
                              'inline_chem'     => 'on',
                              'ltiauth'         => 'off',
                          );                           );
     my @toggles = ('uselcmath','usejsme');      my @toggles = ('uselcmath','usejsme','inline_chem','ltiauth');
     my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial',      my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial',
                    'uploadquota_community','uploadquota_textbook','mysqltables_official',                     'uploadquota_community','uploadquota_textbook','coursequota_official',
                    'mysqltables_unofficial','mysqltables_community','mysqltables_textbook');                     'coursequota_unofficial','coursequota_community','coursequota_textbook',
                      'mysqltables_official','mysqltables_unofficial','mysqltables_community',
                      'mysqltables_textbook');
     my @types = ('official','unofficial','community','textbook');      my @types = ('official','unofficial','community','textbook');
     my %staticdefaults = (      my %staticdefaults = (
                            anonsurvey_threshold => 10,                             anonsurvey_threshold => 10,
                            uploadquota          => 500,                             uploadquota          => 500,
                              coursequota          => 20,
                            postsubmit           => 60,                             postsubmit           => 60,
                            mysqltables          => 172800,                             mysqltables          => 172800,
                              domexttool           => 1,
                          );                           );
     my %texoptions = (      my %texoptions = (
                         MathJax  => 'MathJax',                          MathJax  => 'MathJax',
Line 14900  sub modify_coursedefaults { Line 18570  sub modify_coursedefaults {
                 }                  }
                 $defaultshash{'coursedefaults'}{$item} = $newdef;                  $defaultshash{'coursedefaults'}{$item} = $newdef;
             } else {              } else {
                 my ($setting,$type) = ($item =~ /^(uploadquota|mysqltables)_(\w+)$/);                  my ($setting,$type) = ($item =~ /^(uploadquota|coursequota|mysqltables)_(\w+)$/);
                 if (ref($domconfig{'coursedefaults'}{$setting}) eq 'HASH') {                  if (ref($domconfig{'coursedefaults'}{$setting}) eq 'HASH') {
                     $currdef = $domconfig{'coursedefaults'}{$setting}{$type};                      $currdef = $domconfig{'coursedefaults'}{$setting}{$type};
                 }                  }
Line 14912  sub modify_coursedefaults { Line 18582  sub modify_coursedefaults {
                     unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) {                      unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) {
                         $changes{$item} = 1;                          $changes{$item} = 1;
                     }                      }
                 } elsif ($item =~ /^(uploadquota|mysqltables)_/) {                  } elsif ($item =~ /^(uploadquota|coursequota|mysqltables)_/) {
                     my $setting = $1;                      my $setting = $1;
                     unless (($currdef eq '') && ($newdef == $staticdefaults{$setting})) {                      unless (($currdef eq '') && ($newdef == $staticdefaults{$setting})) {
                         $changes{$setting} = 1;                          $changes{$setting} = 1;
Line 15047  sub modify_coursedefaults { Line 18717  sub modify_coursedefaults {
                 $changes{'postsubmit'} = 1;                  $changes{'postsubmit'} = 1;
             }              }
         }          }
           my (%newdomexttool,%newexttool,%olddomexttool,%oldexttool);
           map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool');
           map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool');
           if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') {
               %olddomexttool = %{$domconfig{'coursedefaults'}{'domexttool'}};
           } else {
              foreach my $type (@types) {
                  if ($staticdefaults{'domexttool'}) {
                      $olddomexttool{$type} = 1;
                  } else {
                      $olddomexttool{$type} = 0;
                  }
               }
           }
           if (ref($domconfig{'coursedefaults'}{'exttool'}) eq 'HASH') {
               %oldexttool = %{$domconfig{'coursedefaults'}{'exttool'}};
           } else {
               foreach my $type (@types) {
                  if ($staticdefaults{'exttool'}) {
                      $oldexttool{$type} = 1;
                  } else {
                      $oldexttool{$type} = 0;
                  }
               }
           }
           foreach my $type (@types) {
               unless ($newdomexttool{$type}) {
                   $newdomexttool{$type} = 0;
               }
               unless ($newexttool{$type}) {
                   $newexttool{$type} = 0;
               }
               if ($newdomexttool{$type} != $olddomexttool{$type}) {
                   $changes{'domexttool'} = 1;
               }
               if ($newexttool{$type} != $oldexttool{$type}) {
                   $changes{'exttool'} = 1;
               }
           }
           $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool;
           $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool;
     }      }
     my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,      my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                              $dom);                                               $dom);
Line 15055  sub modify_coursedefaults { Line 18766  sub modify_coursedefaults {
             my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);              my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
             if (($changes{'uploadquota'}) || ($changes{'postsubmit'}) ||              if (($changes{'uploadquota'}) || ($changes{'postsubmit'}) ||
                 ($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) ||                  ($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) ||
                 ($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'})) {                  ($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'}) ||
                 foreach my $item ('uselcmath','usejsme','texengine') {                  ($changes{'inline_chem'}) || ($changes{'ltiauth'}) || ($changes{'domexttool'}) ||
                   ($changes{'exttool'}) || ($changes{'coursequota'})) {
                   foreach my $item ('uselcmath','usejsme','inline_chem','texengine','ltiauth') {
                     if ($changes{$item}) {                      if ($changes{$item}) {
                         $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item};                          $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item};
                     }                      }
Line 15087  sub modify_coursedefaults { Line 18800  sub modify_coursedefaults {
                         }                          }
                     }                      }
                 }                  }
                   if ($changes{'coursequota'}) {
                       if (ref($defaultshash{'coursedefaults'}{'coursequota'}) eq 'HASH') {
                           foreach my $type (@types) {
                               $domdefaults{$type.'coursequota'}=$defaultshash{'coursedefaults'}{'coursequota'}{$type};
                           }
                       }
                   }
                 if ($changes{'canclone'}) {                  if ($changes{'canclone'}) {
                     if (ref($defaultshash{'coursedefaults'}{'canclone'}) eq 'HASH') {                      if (ref($defaultshash{'coursedefaults'}{'canclone'}) eq 'HASH') {
                         if (ref($defaultshash{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') {                          if (ref($defaultshash{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') {
Line 15099  sub modify_coursedefaults { Line 18819  sub modify_coursedefaults {
                         $domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'};                          $domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'};
                     }                      }
                 }                  }
                   if ($changes{'domexttool'}) {
                       if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') {
                           foreach my $type (@types) {
                               $domdefaults{$type.'domexttool'}=$defaultshash{'coursedefaults'}{'domexttool'}{$type};
                           }
                       }
                   }
                   if ($changes{'exttool'}) {
                       if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') {
                           foreach my $type (@types) {
                               $domdefaults{$type.'exttool'}=$defaultshash{'coursedefaults'}{'exttool'}{$type};
                           }
                       }
                   }
                 my $cachetime = 24*60*60;                  my $cachetime = 24*60*60;
                 &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);                  &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
                 if (ref($lastactref) eq 'HASH') {                  if (ref($lastactref) eq 'HASH') {
Line 15119  sub modify_coursedefaults { Line 18853  sub modify_coursedefaults {
                     } else {                      } else {
                         $resulttext .= '<li>'.&mt('Molecule editor uses JME (Java), if supported by client OS.').'</li>';                          $resulttext .= '<li>'.&mt('Molecule editor uses JME (Java), if supported by client OS.').'</li>';
                     }                      }
                   } elsif ($item eq 'inline_chem') {
                       if ($env{'form.'.$item} eq '1') {
                           $resulttext .= '<li>'.&mt('Chemical Reaction Response uses inline previewer').'</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('Chemical Reaction Response uses pop-up previewer').'</li>';
                       }
                 } elsif ($item eq 'texengine') {                  } elsif ($item eq 'texengine') {
                     if ($defaultshash{'coursedefaults'}{'texengine'} ne '') {                      if ($defaultshash{'coursedefaults'}{'texengine'} ne '') {
                         $resulttext .= '<li>'.&mt('Default method to display mathematics set to: "[_1]"',                          $resulttext .= '<li>'.&mt('Default method to display mathematics set to: "[_1]"',
Line 15132  sub modify_coursedefaults { Line 18872  sub modify_coursedefaults {
                                        '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'official'}.'</b>').'</li>'.                                         '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'official'}.'</b>').'</li>'.
                                        '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'unofficial'}.'</b>').'</li>'.                                         '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'unofficial'}.'</b>').'</li>'.
                                        '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'textbook'}.'</b>').'</li>'.                                         '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'textbook'}.'</b>').'</li>'.
   
                                        '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'community'}.'</b>').'</li>'.                                         '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'community'}.'</b>').'</li>'.
                                        '</ul>'.                                         '</ul>'.
                                        '</li>';                                         '</li>';
                     } else {                      } else {
                         $resulttext .= '<li>'.&mt('Default quota for content uploaded via Course Editor remains default: [_1] MB',$staticdefaults{'uploadquota'}).'</li>';                          $resulttext .= '<li>'.&mt('Default quota for content uploaded via Course Editor remains default: [_1] MB',$staticdefaults{'uploadquota'}).'</li>';
                     }                      }
                   } elsif ($item eq 'coursequota') {
                       if (ref($defaultshash{'coursedefaults'}{'coursequota'}) eq 'HASH') {
                           $resulttext .= '<li>'.&mt('Default cumulative quota for all group portfolio spaces in course set as follows:').'<ul>'.
                                          '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'official'}.'</b>').'</li>'.
                                          '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'unofficial'}.'</b>').'</li>'.
                                          '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'textbook'}.'</b>').'</li>'.
                                          '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'community'}.'</b>').'</li>'.
                                          '</ul>'.
                                          '</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('Default cumulative quota for all group portfolio spaces in course remains default: [_1] MB',$staticdefaults{'coursequota'}).'</li>';
                       }
                 } elsif ($item eq 'mysqltables') {                  } elsif ($item eq 'mysqltables') {
                     if (ref($defaultshash{'coursedefaults'}{'mysqltables'}) eq 'HASH') {                      if (ref($defaultshash{'coursedefaults'}{'mysqltables'}) eq 'HASH') {
                         $resulttext .= '<li>'.&mt('Lifetime of "Temporary" MySQL tables (student performance data) on homeserver').'<ul>'.                          $resulttext .= '<li>'.&mt('Lifetime of "Temporary" MySQL tables (student performance data) on homeserver').'<ul>'.
Line 15215  sub modify_coursedefaults { Line 18966  sub modify_coursedefaults {
                     } else {                      } else {
                         $resulttext .= '<li>'.&mt('By default, only course owner and coordinators may clone a course.').'</li>';                          $resulttext .= '<li>'.&mt('By default, only course owner and coordinators may clone a course.').'</li>';
                     }                      }
                   } elsif ($item eq 'ltiauth') {
                       if ($env{'form.'.$item} eq '1') {
                           $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL need not require re-authentication').'</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL will require re-authentication').'</li>';
                       }
                   } elsif ($item eq 'domexttool') {
                       my @noyes = (&mt('no'),&mt('yes'));
                       if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') {
                           $resulttext .= '<li>'.&mt('External Tools defined in the domain may be used as follows:').'<ul>'.
                                          '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'official'}].'</b>').'</li>'.
                                          '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'unofficial'}].'</b>').'</li>'.
                                          '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'textbook'}].'</b>').'</li>'.
                                          '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'placement'}].'</b>').'</li>'.
                                          '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'community'}].'</b>').'</li>'.
                                          '</ul>'.
                                          '</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('External Tools defined in the domain may be used in all course types, by default').'</li>';
                       }
                   } elsif ($item eq 'exttool') {
                       my @noyes = (&mt('no'),&mt('yes'));
                       if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') {
                           $resulttext .= '<li>'.&mt('External Tools can be defined and configured in course containers as follows:').'<ul>'.
                                          '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'official'}].'</b>').'</li>'.
                                          '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'unofficial'}].'</b>').'</li>'.
                                          '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'textbook'}].'</b>').'</li>'.
                                          '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'placement'}].'</b>').'</li>'.
                                          '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'community'}].'</b>').'</li>'.
                                          '</ul>'.
                                          '</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('External Tools can not be defined in any course types, by default').'</li>';
                       }
                 }                  }
             }              }
             $resulttext .= '</ul>';              $resulttext .= '</ul>';
Line 15467  sub modify_selfenrollment { Line 19252  sub modify_selfenrollment {
     return $resulttext;      return $resulttext;
 }  }
   
   sub modify_wafproxy {
       my ($dom,$action,$lastactref,%domconfig) = @_;
       my %servers = &Apache::lonnet::internet_dom_servers($dom);
       my (%othercontrol,%canset,%values,%curralias,%currsaml,%currvalue,@warnings,
           %wafproxy,%changes,%expirecache,%expiresaml);
       foreach my $server (sort(keys(%servers))) {
           my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server});
           if ($serverhome eq $server) {
               my $serverdom = &Apache::lonnet::host_domain($server);
               if ($serverdom eq $dom) {
                   $canset{$server} = 1;
               }
           }
       }
       if (ref($domconfig{'wafproxy'}) eq 'HASH') {
           %{$values{$dom}} = ();
           if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') {
               %curralias = %{$domconfig{'wafproxy'}{'alias'}};
           }
           if (ref($domconfig{'wafproxy'}{'saml'}) eq 'HASH') {
               %currsaml = %{$domconfig{'wafproxy'}{'saml'}};
           }
           foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
               $currvalue{$item} = $domconfig{'wafproxy'}{$item};
           }
       }
       my $output;
       if (keys(%canset)) {
           %{$wafproxy{'alias'}} = ();
           %{$wafproxy{'saml'}} = ();
           foreach my $key (sort(keys(%canset))) {
               if ($env{'form.wafproxy_'.$dom}) {
                   $wafproxy{'alias'}{$key} = $env{'form.wafproxy_alias_'.$key};
                   $wafproxy{'alias'}{$key} =~ s/^\s+|\s+$//g;
                   if ($wafproxy{'alias'}{$key} ne $curralias{$key}) {
                       $changes{'alias'} = 1;
                   }
                   if ($env{'form.wafproxy_alias_saml_'.$key}) {
                       $wafproxy{'saml'}{$key} = 1;
                   }
                   if ($wafproxy{'saml'}{$key} ne $currsaml{$key}) {
                       $changes{'saml'} = 1;
                   }
               } else {
                   $wafproxy{'alias'}{$key} = '';
                   $wafproxy{'saml'}{$key} = '';
                   if ($curralias{$key}) {
                       $changes{'alias'} = 1;
                   }
                   if ($currsaml{$key}) {
                       $changes{'saml'} = 1;
                   }
               }
               if ($wafproxy{'alias'}{$key} eq '') {
                   if ($curralias{$key}) {
                       $expirecache{$key} = 1;
                   }
                   delete($wafproxy{'alias'}{$key});
               }
               if ($wafproxy{'saml'}{$key} eq '') {
                   if ($currsaml{$key}) {
                       $expiresaml{$key} = 1;
                   }
                   delete($wafproxy{'saml'}{$key});
               }
           }
           unless (keys(%{$wafproxy{'alias'}})) {
               delete($wafproxy{'alias'});
           }
           unless (keys(%{$wafproxy{'saml'}})) {
               delete($wafproxy{'saml'});
           }
           # Localization for values in %warn occurs in &mt() calls separately.
           my %warn = (
                        trusted => 'trusted IP range(s)',
                        vpnint => 'internal IP range(s) for VPN sessions(s)',
                        vpnext => 'IP range(s) for backend WAF connections',
                      );
           foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
               my $possible = $env{'form.wafproxy_'.$item};
               $possible =~ s/^\s+|\s+$//g;
               if ($possible ne '') {
                   if ($item eq 'remoteip') {
                       if ($possible =~ /^[mhn]$/) {
                           $wafproxy{$item} = $possible;
                       }
                   } elsif ($item eq 'ipheader') {
                       if ($wafproxy{'remoteip'} eq 'h') {
                           $wafproxy{$item} = $possible;
                       }
                   } elsif ($item eq 'sslopt') {
                       if ($possible =~ /^0|1$/) {
                           $wafproxy{$item} = $possible;
                       }
                   } else {
                       my (@ok,$count);
                       if (($item eq 'vpnint') || ($item eq 'vpnext')) {
                           unless ($env{'form.wafproxy_vpnaccess'}) {
                               $possible = '';
                           }
                       } elsif ($item eq 'trusted') {
                           unless ($wafproxy{'remoteip'} eq 'h') {
                               $possible = '';
                           }
                       }
                       unless ($possible eq '') {
                           $possible =~ s/[\r\n]+/\s/g;
                           $possible =~ s/\s*-\s*/-/g;
                           $possible =~ s/\s+/,/g;
                           $possible =~ s/,+/,/g;
                       }
                       $count = 0;
                       if ($possible ne '') {
                           foreach my $poss (split(/\,/,$possible)) {
                               $count ++;
                               $poss = &validate_ip_pattern($poss);
                               if ($poss ne '') {
                                   push(@ok,$poss);
                               }
                           }
                           my $diff = $count - scalar(@ok);
                           if ($diff) {
                               push(@warnings,'<li>'.
                                    &mt('[quant,_1,IP] invalid and excluded from saved value for [_2]',
                                        $diff,$warn{$item}).
                                    '</li>');
                           }
                           if (@ok) {
                               my @cidr_list;
                               foreach my $item (@ok) {
                                   @cidr_list = &Net::CIDR::cidradd($item,@cidr_list);
                               }
                               $wafproxy{$item} = join(',',@cidr_list);
                           }
                       }
                   }
                   if ($wafproxy{$item} ne $currvalue{$item}) {
                       $changes{$item} = 1;
                   }
               } elsif ($currvalue{$item}) {
                   $changes{$item} = 1;
               }
           }
       } else {
           if (keys(%curralias)) {
               $changes{'alias'} = 1;
           }
           if (keys(%currsaml)) {
               $changes{'saml'} = 1;
           }
           if (keys(%currvalue)) {
               foreach my $key (keys(%currvalue)) {
                   $changes{$key} = 1;
               }
           }
       }
       if (keys(%changes)) {
           my %defaultshash = (
                                 wafproxy => \%wafproxy,
                              );
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               my $cachetime = 24*60*60;
               my (%domdefaults,$updatedomdefs);
               foreach my $item ('ipheader','trusted','vpnint','vpnext','sslopt') {
                   if ($changes{$item}) {
                       unless ($updatedomdefs) {
                           %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                           $updatedomdefs = 1;
                       }
                       if ($wafproxy{$item}) {
                           $domdefaults{'waf_'.$item} = $wafproxy{$item};
                       } elsif (exists($domdefaults{'waf_'.$item})) {
                           delete($domdefaults{'waf_'.$item});
                       }
                   }
               }
               if ($updatedomdefs) {
                   &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'domdefaults'} = 1;
                   }
               }
               if ((exists($wafproxy{'alias'})) || (keys(%expirecache))) {
                   my %updates = %expirecache;
                   foreach my $key (keys(%expirecache)) {
                       &Apache::lonnet::devalidate_cache_new('proxyalias',$key);
                   }
                   if (ref($wafproxy{'alias'}) eq 'HASH') {
                       my $cachetime = 24*60*60;
                       foreach my $key (keys(%{$wafproxy{'alias'}})) {
                           $updates{$key} = 1;
                           &Apache::lonnet::do_cache_new('proxyalias',$key,$wafproxy{'alias'}{$key},
                                                         $cachetime);
                       }
                   }
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'proxyalias'} = \%updates;
                   }
               }
               if ((exists($wafproxy{'saml'})) || (keys(%expiresaml))) {
                   my %samlupdates = %expiresaml;
                   foreach my $key (keys(%expiresaml)) {
                       &Apache::lonnet::devalidate_cache_new('proxysaml',$key);
                   }
                   if (ref($wafproxy{'saml'}) eq 'HASH') {
                       my $cachetime = 24*60*60;
                       foreach my $key (keys(%{$wafproxy{'saml'}})) {
                           $samlupdates{$key} = 1;
                           &Apache::lonnet::do_cache_new('proxysaml',$key,$wafproxy{'saml'}{$key},
                                                         $cachetime);
                       }
                   }
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'proxysaml'} = \%samlupdates;
                   }
               }
               $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'<ul>';
               foreach my $item ('alias','saml','remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
                   if ($changes{$item}) {
                       if ($item eq 'alias') {
                           my $numaliased = 0;
                           if (ref($wafproxy{'alias'}) eq 'HASH') {
                               my $shown;
                               if (keys(%{$wafproxy{'alias'}})) {
                                   foreach my $server (sort(keys(%{$wafproxy{'alias'}}))) {
                                       $shown .= '<li>'.&mt('[_1] aliased by [_2]',
                                                            &Apache::lonnet::hostname($server),
                                                            $wafproxy{'alias'}{$server}).'</li>';
                                       $numaliased ++;
                                   }
                                   if ($numaliased) {
                                       $output .= '<li>'.&mt('Aliases for hostnames set to: [_1]',
                                                             '<ul>'.$shown.'</ul>').'</li>';
                                   }
                               }
                           }
                           unless ($numaliased) {
                               $output .= '<li>'.&mt('Aliases deleted for hostnames').'</li>';
                           }
                       } elsif ($item eq 'saml') {
                           my $shown;
                           if (ref($wafproxy{'saml'}) eq 'HASH') {
                               if (keys(%{$wafproxy{'saml'}})) {
                                   $shown = join(', ',sort(keys(%{$wafproxy{'saml'}})));
                               }
                           }
                           if ($shown) {
                               $output .= '<li>'.&mt('Alias used by SSO Auth for: [_1]',
                                                     $shown).'</li>';
                           } else {
                               $output .= '<li>'.&mt('No alias used for SSO Auth').'</li>';
                           }
                       } else {
                           if ($item eq 'remoteip') {
                               my %ip_methods = &remoteip_methods();
                               if ($wafproxy{$item} =~ /^[mh]$/) {
                                   $output .= '<li>'.&mt("Method for determining user's IP set to: [_1]",
                                                         $ip_methods{$wafproxy{$item}}).'</li>';
                               } else {
                                   if (($env{'form.wafproxy_'.$dom}) && (ref($wafproxy{'alias'}) eq 'HASH')) {
                                       $output .= '<li>'.&mt("No method in use to get user's real IP (will report IP used by WAF).").
                                                  '</li>';
                                   } else {
                                       $output .= '<li>'.&mt('WAF/Reverse Proxy not in use').'</li>';
                                   }
                               }
                           } elsif ($item eq 'ipheader') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('Request header with remote IP set to: [_1]',
                                                         $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('Request header with remote IP deleted').'</li>';
                               }
                           } elsif ($item eq 'trusted') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('Trusted IP range(s) set to: [_1]',
                                                         $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('Trusted IP range(s) deleted').'</li>';
                               }
                           } elsif ($item eq 'vpnint') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('Internal IP Range(s) for VPN sessions set to: [_1]',
                                                          $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('Internal IP Range(s) for VPN sessions deleted').'</li>';
                               }
                           } elsif ($item eq 'vpnext') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('IP Range(s) for backend WAF connections set to: [_1]',
                                                          $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('IP Range(s) for backend WAF connections deleted').'</li>';
                               }
                           } elsif ($item eq 'sslopt') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('WAF/Reverse Proxy expected to forward requests to https on LON-CAPA node, regardless of original protocol in web browser (http or https).').'</li>';
                               } else {
                                   $output .= '<li>'.&mt('WAF/Reverse Proxy expected to preserve original protocol in web browser (either http or https) when forwarding to LON-CAPA node.').'</li>';
                               }
                           }
                       }
                   }
               }
               $output .= '</ul>';
           } else {
               $output = '<span class="LC_error">'.
                         &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } elsif (keys(%canset)) {
           $output = &mt('No changes made to Web Application Firewall/Reverse Proxy settings');
       }
       if (@warnings) {
           $output .= '<br />'.&mt('Warnings:').'<ul>'.
                          join("\n",@warnings).'</ul>';
       }
       return $output;
   }
   
   sub validate_ip_pattern {
       my ($pattern) = @_;
       if ($pattern =~ /^([^-]+)\-([^-]+)$/) {
           my ($start,$end) = ($1,$2);
           if ((&Net::CIDR::cidrvalidate($start)) && (&Net::CIDR::cidrvalidate($end))) {
               if (($start !~ m{/}) && ($end !~ m{/})) {
                   return $start.'-'.$end;
               }
           }
       } elsif ($pattern ne '') {
           $pattern = &Net::CIDR::cidrvalidate($pattern);
           if ($pattern ne '') {
               return $pattern;
           }
       }
       return;
   }
   
 sub modify_usersessions {  sub modify_usersessions {
     my ($dom,$lastactref,%domconfig) = @_;      my ($dom,$lastactref,%domconfig) = @_;
     my @hostingtypes = ('version','excludedomain','includedomain');      my @hostingtypes = ('version','excludedomain','includedomain');
Line 15634  sub modify_usersessions { Line 19758  sub modify_usersessions {
         }          }
     }      }
     $defaultshash{'usersessions'}{'offloadnow'} = {};      $defaultshash{'usersessions'}{'offloadnow'} = {};
       $defaultshash{'usersessions'}{'offloadoth'} = {};
     my @offloadnow = &Apache::loncommon::get_env_multiple('form.offloadnow');      my @offloadnow = &Apache::loncommon::get_env_multiple('form.offloadnow');
     my @okoffload;      my @okoffload;
     if (@offloadnow) {      if (@offloadnow) {
Line 15650  sub modify_usersessions { Line 19775  sub modify_usersessions {
             }              }
         }          }
     }      }
       my @offloadoth = &Apache::loncommon::get_env_multiple('form.offloadoth');
       my @okoffloadoth;
       if (@offloadoth) {
           foreach my $server (@offloadoth) {
               if (&Apache::lonnet::hostname($server) ne '') {
                   unless (grep(/^\Q$server\E$/,@okoffloadoth)) {
                       push(@okoffloadoth,$server);
                   }
               }
           }
           if (@okoffloadoth) {
               foreach my $lonhost (@okoffloadoth) {
                   $defaultshash{'usersessions'}{'offloadoth'}{$lonhost} = 1;
               }
           }
       }
     if (ref($domconfig{'usersessions'}) eq 'HASH') {      if (ref($domconfig{'usersessions'}) eq 'HASH') {
         if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') {          if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') {
             if (ref($changes{'spares'}) eq 'HASH') {              if (ref($changes{'spares'}) eq 'HASH') {
Line 15660  sub modify_usersessions { Line 19801  sub modify_usersessions {
         } else {          } else {
             $savespares = 1;              $savespares = 1;
         }          }
         if (ref($domconfig{'usersessions'}{'offloadnow'}) eq 'HASH') {          foreach my $offload ('offloadnow','offloadoth') {
             foreach my $lonhost (keys(%{$domconfig{'usersessions'}{'offloadnow'}})) {              if (ref($domconfig{'usersessions'}{$offload}) eq 'HASH') {
                 unless ($defaultshash{'usersessions'}{'offloadnow'}{$lonhost}) {                  foreach my $lonhost (keys(%{$domconfig{'usersessions'}{$offload}})) {
                     $changes{'offloadnow'} = 1;                      unless ($defaultshash{'usersessions'}{$offload}{$lonhost}) {
                     last;                          $changes{$offload} = 1;
                 }  
             }  
             unless ($changes{'offloadnow'}) {  
                 foreach my $lonhost (keys(%{$defaultshash{'usersessions'}{'offloadnow'}})) {  
                     unless ($domconfig{'usersessions'}{'offloadnow'}{$lonhost}) {  
                         $changes{'offloadnow'} = 1;  
                         last;                          last;
                     }                      }
                 }                  }
                   unless ($changes{$offload}) {
                       foreach my $lonhost (keys(%{$defaultshash{'usersessions'}{$offload}})) {
                           unless ($domconfig{'usersessions'}{$offload}{$lonhost}) {
                               $changes{$offload} = 1;
                               last;
                           }
                       }
                   }
               } else {
                   if (($offload eq 'offloadnow') && (@okoffload)) {
                        $changes{'offloadnow'} = 1;
                   }
                   if (($offload eq 'offloadoth') && (@okoffloadoth)) {
                       $changes{'offloadoth'} = 1;
                   }
             }              }
         } elsif (@okoffload) {          }
       } else {
           if (@okoffload) {
             $changes{'offloadnow'} = 1;              $changes{'offloadnow'} = 1;
         }          }
     } elsif (@okoffload) {          if (@okoffloadoth) {
         $changes{'offloadnow'} = 1;              $changes{'offloadoth'} = 1;
           }
     }      }
     my $nochgmsg = &mt('No changes made to settings for user session hosting/offloading.');      my $nochgmsg = &mt('No changes made to settings for user session hosting/offloading.');
     if ((keys(%changes) > 0) || ($savespares)) {      if ((keys(%changes) > 0) || ($savespares)) {
Line 15696  sub modify_usersessions { Line 19849  sub modify_usersessions {
                 if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {                  if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {
                     $domdefaults{'offloadnow'} = $defaultshash{'usersessions'}{'offloadnow'};                      $domdefaults{'offloadnow'} = $defaultshash{'usersessions'}{'offloadnow'};
                 }                  }
                   if (ref($defaultshash{'usersessions'}{'offloadoth'}) eq 'HASH') {
                       $domdefaults{'offloadoth'} = $defaultshash{'usersessions'}{'offloadoth'};
                   }
             }              }
             my $cachetime = 24*60*60;              my $cachetime = 24*60*60;
             &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);              &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
Line 15756  sub modify_usersessions { Line 19912  sub modify_usersessions {
                                         }                                          }
                                     } else {                                      } else {
                                         if ($type eq 'version') {                                          if ($type eq 'version') {
                                             $newvalue .= ' '.&mt('(or later)');                                               $newvalue .= ' '.&mt('(or later)');
                                         }                                          }
                                         $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'</li>';                                          $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'</li>';
                                     }                                      }
Line 15769  sub modify_usersessions { Line 19925  sub modify_usersessions {
                 if ($changes{'offloadnow'}) {                  if ($changes{'offloadnow'}) {
                     if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {                      if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {
                         if (keys(%{$defaultshash{'usersessions'}{'offloadnow'}}) > 0) {                          if (keys(%{$defaultshash{'usersessions'}{'offloadnow'}}) > 0) {
                             $resulttext .= '<li>'.&mt('Switch active users on next access, for server(s):').'<ul>';                              $resulttext .= '<li>'.&mt('Switch any active user on next access, for server(s):').'<ul>';
                             foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadnow'}}))) {                              foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadnow'}}))) {
                                 $resulttext .= '<li>'.$lonhost.'</li>';                                  $resulttext .= '<li>'.$lonhost.'</li>';
                             }                              }
                             $resulttext .= '</ul>';                              $resulttext .= '</ul>';
                         } else {                          } else {
                             $resulttext .= '<li>'.&mt('No servers now set to switch active users on next access.');                              $resulttext .= '<li>'.&mt('No servers now set to switch any active user on next access.');
                         }                          }
                     } else {                      } else {
                         $resulttext .= '<li>'.&mt('No servers now set to switch active users on next access.').'</li>';                          $resulttext .= '<li>'.&mt('No servers now set to switch any active user on next access.').'</li>';
                       }
                   }
                   if ($changes{'offloadoth'}) {
                       if (ref($defaultshash{'usersessions'}{'offloadoth'}) eq 'HASH') {
                           if (keys(%{$defaultshash{'usersessions'}{'offloadoth'}}) > 0) {
                               $resulttext .= '<li>'.&mt('Switch other institutions on next access, for server(s):').'<ul>';
                               foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadoth'}}))) {
                                   $resulttext .= '<li>'.$lonhost.'</li>';
                               }
                               $resulttext .= '</ul>';
                           } else {
                               $resulttext .= '<li>'.&mt('No servers now set to switch other institutions on next access.');
                           }
                       } else {
                           $resulttext .= '<li>'.&mt('No servers now set to switch other institutions on next access.').'</li>';
                     }                      }
                 }                  }
                 $resulttext .= '</ul>';                  $resulttext .= '</ul>';
Line 16029  sub modify_loadbalancing { Line 20200  sub modify_loadbalancing {
                             }                              }
                         }                          }
                         if ($changes{'curr'}{$balancer}{'cookie'}) {                          if ($changes{'curr'}{$balancer}{'cookie'}) {
                             $resulttext .= '<li>'.&mt('Load Balancer: [_1] -- cookie use enabled',                              if ($currcookies{$balancer}) {
                                                       $balancer).'</li>';                                  $resulttext .= '<li>'.&mt('Load Balancer: [_1] -- cookie use disabled',
                                                             $balancer).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('Load Balancer: [_1] -- cookie use enabled',
                                                             $balancer).'</li>';
                               }
                         }                          }
                         if (keys(%toupdate)) {                      }
                             my %thismachine;                  }
                             my $updatedhere;                  if (keys(%toupdate)) {
                             my $cachetime = 60*60*24;                      my %thismachine;
                             map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();                      my $updatedhere;
                             foreach my $lonhost (keys(%toupdate)) {                      my $cachetime = 60*60*24;
                                 if ($thismachine{$lonhost}) {                      map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();
                                     unless ($updatedhere) {                      foreach my $lonhost (keys(%toupdate)) {
                                         &Apache::lonnet::do_cache_new('loadbalancing',$dom,                          if ($thismachine{$lonhost}) {
                                                                       $defaultshash{'loadbalancing'},                              unless ($updatedhere) {
                                                                       $cachetime);                                  &Apache::lonnet::do_cache_new('loadbalancing',$dom,
                                         $updatedhere = 1;                                                                $defaultshash{'loadbalancing'},
                                     }                                                                $cachetime);
                                 } else {                                  $updatedhere = 1;
                                     my $cachekey = &escape('loadbalancing').':'.&escape($dom);  
                                     &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]);  
                                 }  
                             }                              }
                           } else {
                               my $cachekey = &escape('loadbalancing').':'.&escape($dom);
                               &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]);
                         }                          }
                     }                      }
                 }                  }
Line 16619  function updateNewSpares(formname,lonhos Line 20795  function updateNewSpares(formname,lonhos
 function checkNewSpares(lonhost,type) {  function checkNewSpares(lonhost,type) {
     var newSpare = document.getElementById('newspare_'+type+'_'+lonhost);      var newSpare = document.getElementById('newspare_'+type+'_'+lonhost);
     var chosen =  newSpare.options[newSpare.selectedIndex].value;      var chosen =  newSpare.options[newSpare.selectedIndex].value;
     if (chosen != '') {       if (chosen != '') {
         var othertype;          var othertype;
         var othernewSpare;          var othernewSpare;
         if (type == 'primary') {          if (type == 'primary') {
Line 16753  function toggleDisplay(domForm,caller) { Line 20929  function toggleDisplay(domForm,caller) {
         var dispval = 'block';          var dispval = 'block';
         var selfcreateRegExp = /^cancreate_emailverified/;          var selfcreateRegExp = /^cancreate_emailverified/;
         if (caller == 'emailoptions') {          if (caller == 'emailoptions') {
             optionsElement = domForm.cancreate_email;               optionsElement = domForm.cancreate_email;
         }          }
         if (caller == 'studentsubmission') {          if (caller == 'studentsubmission') {
             optionsElement = domForm.postsubmit;              optionsElement = domForm.postsubmit;
Line 16808  sub devalidate_remote_domconfs { Line 20984  sub devalidate_remote_domconfs {
     my %servers = &Apache::lonnet::internet_dom_servers($dom);      my %servers = &Apache::lonnet::internet_dom_servers($dom);
     my %thismachine;      my %thismachine;
     map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();      map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();
     my @posscached = ('domainconfig','domdefaults','usersessions',      my @posscached = ('domainconfig','domdefaults','ltitools','usersessions',
                       'ltitools','directorysrch','passwdconf','cats');                        'directorysrch','passwdconf','cats','proxyalias','proxysaml',
                         'ipaccess');
       my %cache_by_lonhost;
       if (exists($cachekeys->{'samllanding'})) {
           if (ref($cachekeys->{'samllanding'}) eq 'HASH') {
               my %landing = %{$cachekeys->{'samllanding'}};
               my %domservers = &Apache::lonnet::get_servers($dom);
               if (keys(%domservers)) {
                   foreach my $server (keys(%domservers)) {
                       my @cached;
                       next if ($thismachine{$server});
                       if ($landing{$server}) {
                           push(@cached,&escape('samllanding').':'.&escape($server));
                       }
                       if (@cached) {
                           $cache_by_lonhost{$server} = \@cached;
                       }
                   }
               }
           }
       }
     if (keys(%servers)) {      if (keys(%servers)) {
         foreach my $server (keys(%servers)) {          foreach my $server (keys(%servers)) {
             next if ($thismachine{$server});              next if ($thismachine{$server});
             my @cached;              my @cached;
             foreach my $name (@posscached) {              foreach my $name (@posscached) {
                 if ($cachekeys->{$name}) {                  if ($cachekeys->{$name}) {
                     push(@cached,&escape($name).':'.&escape($dom));                      if (($name eq 'proxyalias') || ($name eq 'proxysaml')) {
                           if (ref($cachekeys->{$name}) eq 'HASH') {
                               foreach my $key (keys(%{$cachekeys->{$name}})) {
                                   push(@cached,&escape($name).':'.&escape($key));
                               }
                           }
                       } else {
                           push(@cached,&escape($name).':'.&escape($dom));
                       }
                 }                  }
             }              }
               if ((exists($cache_by_lonhost{$server})) &&
                   (ref($cache_by_lonhost{$server}) eq 'ARRAY')) {
                   push(@cached,@{$cache_by_lonhost{$server}});
               }
             if (@cached) {              if (@cached) {
                 &Apache::lonnet::remote_devalidate_cache($server,\@cached);                  &Apache::lonnet::remote_devalidate_cache($server,\@cached);
             }              }

Removed from v.1.160.6.102.2.1  
changed lines
  Added in v.1.160.6.118.2.19


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