Diff for /loncom/interface/domainprefs.pm between versions 1.160.6.114 and 1.428

version 1.160.6.114, 2021/12/16 19:46:34 version 1.428, 2023/07/29 20:33:25
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, placement, 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();
   use LONCAPA::SSL;
 use File::Copy;  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 Net::CIDR;
   use Crypt::CBC;
   
 my $registered_cleanup;  my $registered_cleanup;
 my $modified_urls;  my $modified_urls;
Line 218  sub handler { Line 222  sub handler {
                 'serverstatuses','requestcourses','helpsettings',                  'serverstatuses','requestcourses','helpsettings',
                 'coursedefaults','usersessions','loadbalancing',                  'coursedefaults','usersessions','loadbalancing',
                 'requestauthor','selfenrollment','inststatus',                  'requestauthor','selfenrollment','inststatus',
                 'passwords','wafproxy','ipaccess'],$dom);                  'ltitools','toolsec','ssl','trust','lti','ltisec',
                   'privacy','passwords','proctoring','wafproxy','ipaccess'],$dom);
       my %encconfig =
           &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1);
       my ($checked_is_home,$is_home);
       if (ref($domconfig{'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'}})) {
                   if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') &&
                       (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) {
                       $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};
                           }
                       }
                   }
               }
           }
       }
       if (ref($domconfig{'proctoring'}) eq 'HASH') {
           if (ref($encconfig{'proctoring'}) eq 'HASH') {
               foreach my $provider (keys(%{$domconfig{'proctoring'}})) {
                   if ((ref($domconfig{'proctoring'}{$provider}) eq 'HASH') &&
                       (ref($encconfig{'proctoring'}{$provider}) eq 'HASH')) {
                       foreach my $item ('key','secret') {
                           $domconfig{'proctoring'}{$provider}{$item} = $encconfig{'proctoring'}{$provider}{$item};
                       }
                   }
               }
           }
       }
     my @prefs_order = ('rolecolors','login','ipaccess','defaults','wafproxy','passwords',      my @prefs_order = ('rolecolors','login','ipaccess','defaults','wafproxy','passwords',
                        'quotas','autoenroll','autoupdate','autocreate','directorysrch',                         'quotas','autoenroll','autoupdate','autocreate','directorysrch',
                        'contacts','usercreation','selfcreation','usermodification',                         'contacts','privacy','usercreation','selfcreation',
                        'scantron','requestcourses','requestauthor','coursecategories',                         'usermodification','scantron','requestcourses','requestauthor',
                        'serverstatuses','helpsettings','coursedefaults',                         'coursecategories','serverstatuses','helpsettings','coursedefaults',
                        'selfenrollment','usersessions');                         'ltitools','proctoring','selfenrollment','usersessions','ssl',
                          'trust','lti');
     my %existing;      my %existing;
     if (ref($domconfig{'loadbalancing'}) eq 'HASH') {      if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
         %existing = %{$domconfig{'loadbalancing'}};          %existing = %{$domconfig{'loadbalancing'}};
Line 268  sub handler { Line 350  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,
                     },                      },
Line 298  sub handler { Line 382  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 483  sub handler { Line 567  sub handler {
                   print => \&print_selfenrollment,                    print => \&print_selfenrollment,
                   modify => \&modify_selfenrollment,                    modify => \&modify_selfenrollment,
                  },                   },
           'privacy' => 
                    {text   => 'Role assignments and user privacy',
                     help   => 'Domain_Configuration_User_Privacy',
                     header => [{col1 => 'Role assigned in different domain',
                                 col2 => 'Approval options'},
                                {col1 => 'Role assigned in different domain to user of type',
                                 col2 => 'User information available in that domain'},
                                {col1 => "Role assigned in user's domain",
                                 col2 => 'Information viewable by privileged user'},
                                {col1 => "Role assigned in user's domain",
                                 col2 => 'Information viewable by unprivileged user'}],
                     print => \&print_privacy,
                     modify => \&modify_privacy,
                    },
         'usersessions' =>          'usersessions' =>
                  {text  => 'User session hosting/offloading',                   {text  => 'User session hosting/offloading',
                   help  => 'Domain_Configuration_User_Sessions',                    help  => 'Domain_Configuration_User_Sessions',
Line 506  sub handler { Line 604  sub handler {
                   print => \&print_loadbalancing,                    print => \&print_loadbalancing,
                   modify => \&modify_loadbalancing,                    modify => \&modify_loadbalancing,
                  },                   },
          'ipaccess' =>          'ltitools' =>
                    {text => 'External Tools (LTI)',
                     help => 'Domain_Configuration_LTI_Tools',
                     header => [{col1 => 'Encryption of shared secrets',
                                 col2 => 'Settings'},
                                {col1 => 'Rules for shared secrets',
                                 col2 => 'Settings'},
                                {col1 => 'Providers',
                                 col2 => 'Settings',}],
                     print => \&print_ltitools,
                     modify => \&modify_ltitools,
                    },
           'proctoring' =>
                    {text => 'Remote Proctoring Integration',
                     help => 'Domain_Configuration_Proctoring',
                     header => [{col1 => 'Name',
                                 col2 => 'Configuration'}],
                     print => \&print_proctoring,
                     modify => \&modify_proctoring,
                    },
           'ssl' =>
                    {text  => 'LON-CAPA Network (SSL)',
                     help  => 'Domain_Configuration_Network_SSL',
                     header => [{col1 => 'Server',
                                 col2 => 'Certificate Status'},
                                {col1 => 'Connections to other servers',
                                 col2 => 'Rules'},
                                {col1 => 'Connections from other servers',
                                 col2 => 'Rules'},
                                {col1 => "Replicating domain's published content",
                                 col2 => 'Rules'}],
                     print => \&print_ssl,
                     modify => \&modify_ssl,
                    },
           'trust' =>
                    {text   => 'Trust Settings',
                     help   => 'Domain_Configuration_Trust',
                     header => [{col1 => "Access to this domain's content by others",
                                 col2 => 'Rules'},
                                {col1 => "Access to other domain's content by this domain",
                                 col2 => 'Rules'},
                                {col1 => "Enrollment in this domain's courses by others",
                                 col2 => 'Rules',},
                                {col1 => "Co-author roles in this domain for others",
                                 col2 => 'Rules',},
                                {col1 => "Co-author roles for this domain's users elsewhere",
                                 col2 => 'Rules',},
                                {col1 => "Domain roles in this domain assignable to others",
                                 col2 => 'Rules'},
                                {col1 => "Course catalog for this domain displayed elsewhere",
                                 col2 => 'Rules'},
                                {col1 => "Requests for creation of courses in this domain by others",
                                 col2 => 'Rules'},
                                {col1 => "Users in other domains can send messages to this domain",
                                 col2 => 'Rules'},],
                     print => \&print_trust,
                     modify => \&modify_trust,
                    },
           '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',
                                 col2 => 'Settings'},
                                {col1 => 'Consumers',
                                 col2 => 'Settings'},],
                     print => \&print_lti,
                     modify => \&modify_lti,
                    },
           'ipaccess' =>
                        {text => 'IP-based access control',                         {text => 'IP-based access control',
                         help => 'Domain_Configuration_IP_Access',                          help => 'Domain_Configuration_IP_Access',
                         header => [{col1 => 'Setting',                          header => [{col1 => 'Setting',
Line 521  sub handler { Line 691  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',
Line 701  sub process_changes { Line 871  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 'ltitools') {
           $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'proctoring') {
           $output = &modify_proctoring($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'ssl') {
           $output = &modify_ssl($dom,$lastactref,%domconfig);
       } elsif ($action eq 'trust') {
           $output = &modify_trust($dom,$lastactref,%domconfig);
       } elsif ($action eq 'lti') {
           $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'privacy') {
           $output = &modify_privacy($dom,$lastactref,%domconfig);
     } elsif ($action eq 'passwords') {      } elsif ($action eq 'passwords') {
         $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);          $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);
     } elsif ($action eq 'wafproxy') {      } elsif ($action eq 'wafproxy') {
Line 720  sub print_config_box { Line 902  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 737  sub print_config_box { Line 919  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 'proctoring') {
           $output .= &proctoring_javascript($settings);
     } elsif ($action eq 'wafproxy') {      } elsif ($action eq 'wafproxy') {
         $output .= &wafproxy_javascript($dom);          $output .= &wafproxy_javascript($dom);
     } elsif ($action eq 'autoupdate') {      } elsif ($action eq 'autoupdate') {
         $output .= &autoupdate_javascript();          $output .= &autoupdate_javascript();
       } elsif ($action eq 'autoenroll') {
           $output .= &autoenroll_javascript();
     } elsif ($action eq 'login') {      } elsif ($action eq 'login') {
         $output .= &saml_javascript();          $output .= &saml_javascript();
     } elsif ($action eq 'ipaccess') {      } elsif ($action eq 'ipaccess') {
Line 761  sub print_config_box { Line 952  sub print_config_box {
     if ($numheaders > 1) {      if ($numheaders > 1) {
         my $colspan = '';          my $colspan = '';
         my $rightcolspan = '';          my $rightcolspan = '';
         my $leftnobr = '';            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 < 5))) {              (($action eq 'login') && ($numheaders < 5))) {
Line 784  sub print_config_box { Line 975  sub print_config_box {
         $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 'ssl') ||
             ($action eq 'helpsettings') || ($action eq 'contacts') || ($action eq 'wafproxy')) {              ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') ||
               ($action eq 'contacts') || ($action eq 'privacy') || ($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 819  sub print_config_box { Line 1012  sub print_config_box {
             $rowtotal ++;              $rowtotal ++;
         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 'trust') || ($action eq 'contacts') || ($action eq 'defaults') ||
               ($action eq 'privacy') || ($action eq 'passwords') || ($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 'trust') {
                   $output .= $item->{'print'}->('shared',$dom,$settings,\$rowtotal);
             } elsif ($action eq 'passwords') {              } elsif ($action eq 'passwords') {
                 $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal);                  $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal);
             } else {              } else {
                 $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal);                  $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal);
             }              }
             $output .= '              if ($action eq 'trust') {
                   $output .= '
               </table>
             </td>
            </tr>';
                   my @trusthdrs = qw(2 3 4 5 6 7);
                   my @prefixes = qw(enroll othcoau coaurem domroles catalog reqcrs);
                   for (my $i=0; $i<@trusthdrs; $i++) {
                       $output .= '
            <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[$trusthdrs[$i]]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[$trusthdrs[$i]]->{'col2'}).'</td></tr>'.
                              $item->{'print'}->($prefixes[$i],$dom,$settings,\$rowtotal).'
               </table>
             </td>
            </tr>';
                   }
                   $output .= '
            <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[8]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[8]->{'col2'}).'</td></tr>'.
                              $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
               } else {
                   $output .= '
            </table>             </table>
           </td>            </td>
          </tr>           </tr>
Line 840  sub print_config_box { Line 1066  sub print_config_box {
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td>                <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td>
               <td class="LC_right_item">'.&mt($item->{'header'}->[2]->{'col2'}).'</td>                <td class="LC_right_item">'.&mt($item->{'header'}->[2]->{'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 'contacts') || ($action eq 'passwords')) {                  } elsif (($action eq 'contacts') || ($action eq 'privacy') || 
                 if ($action eq 'passwords') {                           ($action eq 'passwords') || ($action eq 'lti')) {
                     $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal);                      if ($action eq 'passwords') {
                 } else {                          $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal);
                     $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal);                      } else {
                 }                          $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal);
                 $output .= '                      }
                       $output .= '
              </tr>               </tr>
             </table>              </table>
            </td>             </td>
Line 859  sub print_config_box { Line 1086  sub print_config_box {
              <tr class="LC_info_row">               <tr class="LC_info_row">
               <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>                <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'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'}->[3]->{'col2'}).'</td></tr>'."\n";
                 if ($action eq 'passwords') {                      if ($action eq 'passwords') {
                     $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal);                          $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal);
                 } else {                      } else {
                     $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);                          $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
                 }                      }
                 $output .= '                      $output .= '
             </table>              </table>
           </td>            </td>
          </tr>           </tr>
          <tr>';           <tr>';
             } else {                  } else {
                 $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);                      $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
                   }
             }              }
             $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')) {                   ($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 'ssl') {
               $output .= $item->{'print'}->('connto',$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"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col2'}).'</td></tr>'.
                              $item->{'print'}->('connfrom',$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'}->[3]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td></tr>'.
                              $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'login') {          } elsif ($action eq 'login') {
             if ($numheaders == 5) {              if ($numheaders == 5) {
                 $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'                  $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'
Line 969  sub print_config_box { Line 1219  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.' valign="top">'.&mt($item->{'header'}->[4]->{'col1'}).'</td>                <td class="LC_left_item"'.$colspan.' style="vertical-align: top">'.&mt($item->{'header'}->[4]->{'col1'}).'</td>
               <td class="LC_right_item" valign="top">'.&mt($item->{'header'}->[4]->{'col2'}).'</td>                <td class="LC_right_item" style="vertical-align: top">'.&mt($item->{'header'}->[4]->{'col2'}).'</td>
              </tr>'.               </tr>'.
             &print_validation_rows('requestcourses',$dom,$settings,\$rowtotal);              &print_validation_rows('requestcourses',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'requestauthor') {          } elsif ($action eq 'requestauthor') {
Line 985  sub print_config_box { Line 1235  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.' valign="top">'.                <td class="LC_left_item"'.$colspan.' style="vertical-align: top">'.
                &mt($item->{'header'}->[2]->{'col1'}).'</td>                 &mt($item->{'header'}->[2]->{'col1'}).'</td>
               <td class="LC_right_item" valign="top">'.                <td class="LC_right_item" style="vertical-align: top">'.
                &mt($item->{'header'}->[2]->{'col2'}).'</td>                 &mt($item->{'header'}->[2]->{'col2'}).'</td>
              </tr>'.               </tr>'.
             &print_rolecolors($phase,'author',$dom,$confname,$settings,\$rowtotal).'              &print_rolecolors($phase,'author',$dom,$confname,$settings,\$rowtotal).'
Line 1015  sub print_config_box { Line 1265  sub print_config_box {
               <td class="LC_left_item" colspan="2">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>';                <td class="LC_left_item" colspan="2">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>';
         } elsif ($action eq 'serverstatuses') {          } elsif ($action eq 'serverstatuses') {
             $output .= '              $output .= '
               <td class="LC_left_item" valign="top">'.&mt($item->{'header'}->[0]->{'col1'}).                <td class="LC_left_item" style="vertical-align: top">'.&mt($item->{'header'}->[0]->{'col1'}).
               '<br />('.&mt('Automatic access for Dom. Coords.').')</td>';                '<br />('.&mt('Automatic access for Dom. Coords.').')</td>';
   
         } else {          } else {
             $output .= '              $output .= '
               <td class="LC_left_item" valign="top">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>';                <td class="LC_left_item" style="vertical-align: top">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>';
         }          }
         if (defined($item->{'header'}->[0]->{'col3'})) {          if (defined($item->{'header'}->[0]->{'col3'})) {
             $output .= '<td class="LC_left_item" valign="top">'.              $output .= '<td class="LC_left_item" style="vertical-align: top">'.
                        &mt($item->{'header'}->[0]->{'col2'});                         &mt($item->{'header'}->[0]->{'col2'});
             if ($action eq 'serverstatuses') {              if ($action eq 'serverstatuses') {
                 $output .= '<br />(<tt>'.&mt('user1:domain1,user2:domain2 etc.').'</tt>)';                  $output .= '<br />(<tt>'.&mt('user1:domain1,user2:domain2 etc.').'</tt>)';
             }               } 
         } else {          } else {
             $output .= '<td class="LC_right_item" valign="top">'.              $output .= '<td class="LC_right_item" style="vertical-align: top">'.
                        &mt($item->{'header'}->[0]->{'col2'});                         &mt($item->{'header'}->[0]->{'col2'});
         }          }
         $output .= '</td>';          $output .= '</td>';
         if ($item->{'header'}->[0]->{'col3'}) {          if ($item->{'header'}->[0]->{'col3'}) {
             if (defined($item->{'header'}->[0]->{'col4'})) {              if (defined($item->{'header'}->[0]->{'col4'})) {
                 $output .= '<td class="LC_left_item" valign="top">'.                  $output .= '<td class="LC_left_item" style="vertical-align: top">'.
                             &mt($item->{'header'}->[0]->{'col3'});                              &mt($item->{'header'}->[0]->{'col3'});
             } else {              } else {
                 $output .= '<td class="LC_right_item" valign="top">'.                  $output .= '<td class="LC_right_item" style="vertical-align: top">'.
                            &mt($item->{'header'}->[0]->{'col3'});                             &mt($item->{'header'}->[0]->{'col3'});
             }              }
             if ($action eq 'serverstatuses') {              if ($action eq 'serverstatuses') {
Line 1047  sub print_config_box { Line 1297  sub print_config_box {
             $output .= '</td>';              $output .= '</td>';
         }          }
         if ($item->{'header'}->[0]->{'col4'}) {          if ($item->{'header'}->[0]->{'col4'}) {
             $output .= '<td class="LC_right_item" valign="top">'.              $output .= '<td class="LC_right_item" style="vertical-align: top">'.
                        &mt($item->{'header'}->[0]->{'col4'});                         &mt($item->{'header'}->[0]->{'col4'});
         }          }
         $output .= '</tr>';          $output .= '</tr>';
Line 1055  sub print_config_box { Line 1305  sub print_config_box {
         if ($action eq 'quotas') {          if ($action eq 'quotas') {
             $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 'ipaccess')) {                   ($action eq 'proctoring') || ($action eq 'ipaccess')) {
             $output .= $item->{'print'}->($dom,$settings,\$rowtotal);              $output .= $item->{'print'}->($dom,$settings,\$rowtotal);
         }          }
     }      }
Line 1076  sub print_login { Line 1326  sub print_login {
         %lt = &login_file_options();          %lt = &login_file_options();
         $switchserver = &check_switchserver($dom,$confname);          $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'};
         $css_class = ' class="LC_odd_row"';          $css_class = ' class="LC_odd_row"';
         $datatable .= '<tr'.$css_class.'><td>'.$choice.'</td>'.          $datatable .= '<tr'.$css_class.'><td>'.$choice.'</td>'.
                       '<td align="right"><table><tr><th>'.$choices{'hostid'}.'</th>'.                        '<td style="text-align: right"><table><tr><th>'.$choices{'hostid'}.'</th>'.
                       '<th>'.$choices{'server'}.'</th>'.                        '<th>'.$choices{'server'}.'</th>'.
                       '<th>'.$choices{'serverpath'}.'</th>'.                        '<th>'.$choices{'serverpath'}.'</th>'.
                       '<th>'.$choices{'custompath'}.'</th>'.                        '<th>'.$choices{'custompath'}.'</th>'.
Line 1163  sub print_login { Line 1412  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 1204  sub print_login { Line 1454  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 1355  sub print_login { Line 1612  sub print_login {
         my $choice = $choices{'headtag'};          my $choice = $choices{'headtag'};
         $css_class = ' class="LC_odd_row"';          $css_class = ' class="LC_odd_row"';
         $datatable .= '<tr'.$css_class.'><td colspan="2">'.$choice.'</td>'.          $datatable .= '<tr'.$css_class.'><td colspan="2">'.$choice.'</td>'.
                       '<td align="left"><table><tr><th>'.$choices{'hostid'}.'</th>'.                        '<td style="text-align: left"><table><tr><th>'.$choices{'hostid'}.'</th>'.
                       '<th>'.$choices{'current'}.'</th>'.                        '<th>'.$choices{'current'}.'</th>'.
                       '<th>'.$choices{'action'}.'</th>'.                        '<th>'.$choices{'action'}.'</th>'.
                       '<th>'.$choices{'exempt'}.'</th></tr>'."\n";                        '<th>'.$choices{'exempt'}.'</th></tr>'."\n";
Line 1399  sub print_login { Line 1656  sub print_login {
                       '<table><tr><th>'.$choices{'hostid'}.'</th>'.                        '<table><tr><th>'.$choices{'hostid'}.'</th>'.
                       '<th>'.$choices{'samllanding'}.'</th>'.                        '<th>'.$choices{'samllanding'}.'</th>'.
                       '<th>'.$choices{'samloptions'}.'</th></tr>'."\n";                        '<th>'.$choices{'samloptions'}.'</th></tr>'."\n";
         my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso,%styleon,%styleoff);          my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso,%styleon,%styleoff);
         foreach my $lonhost (keys(%domservers)) {          foreach my $lonhost (keys(%domservers)) {
             $samlurl{$lonhost} = '/adm/sso';              $samlurl{$lonhost} = '/adm/sso';
             $styleon{$lonhost} = 'display:none';              $styleon{$lonhost} = 'display:none';
             $styleoff{$lonhost} = '';              $styleoff{$lonhost} = '';
         }          }
         if (ref($settings->{'saml'}) eq 'HASH') {          if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) {
             foreach my $lonhost (keys(%{$settings->{'saml'}})) {              foreach my $lonhost (keys(%{$settings->{'saml'}})) {
                 if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') {                  if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') {
                     $saml{$lonhost} = 1;                      $saml{$lonhost} = 1;
Line 1414  sub print_login { Line 1671  sub print_login {
                     $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'};                      $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'};
                     $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'};                      $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'};
                     $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'};                      $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'};
                       $samlwindow{$lonhost} = $settings->{'saml'}{$lonhost}{'window'};
                     $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'};                      $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'};
                     $styleon{$lonhost} = '';                      $styleon{$lonhost} = '';
                     $styleoff{$lonhost} = 'display:none';                      $styleoff{$lonhost} = 'display:none';
Line 1431  sub print_login { Line 1689  sub print_login {
                 $samlon = $samloff;                  $samlon = $samloff;
                 $samloff = ' ';                  $samloff = ' ';
             }              }
               my $samlwinon = '';
               my $samlwinoff = ' checked="checked"';
               if ($samlwindow{$lonhost}) {
                   $samlwinon = $samlwinoff;
                   $samlwinoff = '';
               }
             my $css_class = $itemcount%2?' class="LC_odd_row"':'';              my $css_class = $itemcount%2?' class="LC_odd_row"':'';
             $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.$domservers{$lonhost}.'</span></td>'.              $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.                            '<td><span class="LC_nobreak"><label><input type="radio" name="saml_'.$lonhost.'"'.$samloff.
Line 1440  sub print_login { Line 1704  sub print_login {
                           'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="1" />'.                            'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="1" />'.
                           &mt('Yes').'</label></span></td>'.                            &mt('Yes').'</label></span></td>'.
                           '<td id="samloptionson_'.$lonhost.'" style="'.$styleon{$lonhost}.'" width="100%">'.                            '<td id="samloptionson_'.$lonhost.'" style="'.$styleon{$lonhost}.'" width="100%">'.
                           '<table><tr><th colspan="5" align="center">'.&mt('SSO').'</th><th align="center">'.                            '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th></tr>'.
                           '<span class="LC_nobreak">'.&mt('Non-SSO').'</span></th></tr>'.  
                           '<tr><th>'.&mt('Text').'</th><th>'.&mt('Image').'</th>'.                            '<tr><th>'.&mt('Text').'</th><th>'.&mt('Image').'</th>'.
                           '<th>'.&mt('Alt Text').'</th><th>'.&mt('URL').'</th>'.                            '<th>'.&mt('Alt Text').'</th></tr>'.
                           '<th>'.&mt('Tool Tip').'</th><th>'.&mt('Text').'</th></tr>'.                            '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="20" value="'.
                           '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="8" value="'.  
                           $samltext{$lonhost}.'" /></td><td>';                            $samltext{$lonhost}.'" /></td><td>';
             if ($samlimg{$lonhost}) {              if ($samlimg{$lonhost}) {
                 $datatable .= '<img src="'.$samlimg{$lonhost}.'" /><br />'.                  $datatable .= '<img src="'.$samlimg{$lonhost}.'" /><br />'.
Line 1462  sub print_login { Line 1724  sub print_login {
                 $datatable .= '<input type="file" name="saml_img_'.$lonhost.'" />';                  $datatable .= '<input type="file" name="saml_img_'.$lonhost.'" />';
             }              }
             $datatable .= '</td>'.              $datatable .= '</td>'.
                           '<td><input type="text" name="saml_alt_'.$lonhost.'" size="20" '.                            '<td><input type="text" name="saml_alt_'.$lonhost.'" size="25" '.
                           'value="'.$samlalt{$lonhost}.'" /></td>'.                            'value="'.$samlalt{$lonhost}.'" /></td></tr></table><br />'.
                           '<td><input type="text" name="saml_url_'.$lonhost.'" size="8" '.                            '<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>'.                            'value="'.$samlurl{$lonhost}.'" /></td>'.
                           '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="15">'.                            '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="20">'.
                           $samltitle{$lonhost}.'</textarea></td>'.                            $samltitle{$lonhost}.'</textarea></td>'.
                           '<td><input type="text" name="saml_notsso_'.$lonhost.'" size="8" '.                            '<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>'.                            'value="'.$samlnotsso{$lonhost}.'" /></td></tr>'.
                           '</table></td>'.                            '</table></td>'.
                           '<td id="samloptionsoff_'.$lonhost.'" style="'.$styleoff{$lonhost}.'" width="100%">&nbsp;</td></tr>';                            '<td id="samloptionsoff_'.$lonhost.'" style="'.$styleoff{$lonhost}.'" width="100%">&nbsp;</td></tr>';
Line 1513  sub login_choices { Line 1783  sub login_choices {
             current       => "Current",              current       => "Current",
             samllanding   => "Dual login?",              samllanding   => "Dual login?",
             samloptions   => "Options",              samloptions   => "Options",
               alttext       => "Alt text",
         );          );
     return %choices;      return %choices;
 }  }
Line 1701  sub commblocktype_text { Line 1972  sub commblocktype_text {
         'printout' => 'Printouts',          'printout' => 'Printouts',
         'passwd' => 'Change Password',          'passwd' => 'Change Password',
         'grades' => 'Gradebook',          'grades' => 'Gradebook',
           'search' => 'Course search',
           'wishlist' => 'Stored links',
           'annotate' => 'Annotations',
     );      );
     my $typeorder = ['com','chat','boards','port','groups','blogs','about','printout','grades','passwd'];      my $typeorder = ['com','chat','boards','port','groups','blogs','about','wishlist','printout','grades','search','annotate','passwd'];
     return ($typeorder,\%types);      return ($typeorder,\%types);
 }  }
   
Line 1855  sub display_color_options { Line 2129  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 1863  sub display_color_options { Line 2137  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 1956  sub display_color_options { Line 2235  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 1977  sub display_color_options { Line 2261  sub display_color_options {
                   '<table border="0"><tr>';                    '<table border="0"><tr>';
   
     foreach my $item (@{$bgs}) {      foreach my $item (@{$bgs}) {
         $datatable .= '<td align="center">'.$choices->{$item};          $datatable .= '<td style="text-align: center">'.$choices->{$item};
  my $color = $designs->{'bgs'}{$item} ? $designs->{'bgs'}{$item} : $defaults->{'bgs'}{$item};   my $color = $designs->{'bgs'}{$item} ? $designs->{'bgs'}{$item} : $defaults->{'bgs'}{$item};
         if ($designs->{'bgs'}{$item}) {          if ($designs->{'bgs'}{$item}) {
             $datatable .= '&nbsp;';              $datatable .= '&nbsp;';
Line 2005  sub display_color_options { Line 2289  sub display_color_options {
                   '<table border="0"><tr>';                    '<table border="0"><tr>';
     foreach my $item (@{$links}) {      foreach my $item (@{$links}) {
  my $color = $designs->{'links'}{$item} ? $designs->{'links'}{$item} : $defaults->{'links'}{$item};   my $color = $designs->{'links'}{$item} ? $designs->{'links'}{$item} : $defaults->{'links'}{$item};
         $datatable .= '<td align="center">'.$choices->{$item}."\n";          $datatable .= '<td style="text-align: center">'.$choices->{$item}."\n";
         if ($designs->{'links'}{$item}) {          if ($designs->{'links'}{$item}) {
             $datatable.='&nbsp;';              $datatable.='&nbsp;';
         }          }
Line 2066  sub login_text_colors { Line 2350  sub login_text_colors {
     my ($img,$role,$logintext,$phase,$choices,$designs,$defaults) = @_;      my ($img,$role,$logintext,$phase,$choices,$designs,$defaults) = @_;
     my $color_menu = '<table border="0"><tr>';      my $color_menu = '<table border="0"><tr>';
     foreach my $item (@{$logintext}) {      foreach my $item (@{$logintext}) {
         $color_menu .= '<td align="center">'.$choices->{$item};          $color_menu .= '<td style="text-align: center">'.$choices->{$item};
         my $color = $designs->{'logintext'}{$item} ? $designs->{'logintext'}{$item} : $defaults->{'logintext'}{$item};          my $color = $designs->{'logintext'}{$item} ? $designs->{'logintext'}{$item} : $defaults->{'logintext'}{$item};
         $color_menu .= '<br /><input type="text" class="colorchooser" size="8" name="'.$role.'_'.$item.'" value="'.$color.          $color_menu .= '<br /><input type="text" class="colorchooser" size="8" name="'.$role.'_'.$item.'" value="'.$color.
                       '" onblur = "javascript:colchg_span('."'css_".$role.'_'.$item."'".',this);" /></td>';                        '" onblur = "javascript:colchg_span('."'css_".$role.'_'.$item."'".',this);" /></td>';
Line 2099  sub image_changes { Line 2383  sub image_changes {
                        $role.'_del_'.$img.'" value="1" />'.&mt('Delete?').                         $role.'_del_'.$img.'" value="1" />'.&mt('Delete?').
                        '</label>&nbsp;'.&mt('Replace:').'</span><br />';                         '</label>&nbsp;'.&mt('Replace:').'</span><br />';
         } else {          } else {
             $output .= '<td valign="middle">'.$logincolors.&mt('Upload:').'<br />';              $output .= '<td class="LC_middle">'.$logincolors.&mt('Upload:').'<br />';
         }          }
     }      }
     return $output;      return $output;
Line 2118  sub print_quotas { Line 2402  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','placement','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 2127  sub print_quotas { Line 2411  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 2231  sub print_quotas { Line 2515  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 2582  sub print_studentcode { Line 2869  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','placement','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 2832  $jstext{'templates'}; Line 3119  $jstext{'templates'};
 ENDSCRIPT  ENDSCRIPT
 }  }
   
   sub ltitools_javascript {
       my ($settings) = @_;
       my $togglejs = &ltitools_toggle_js();
       unless (ref($settings) eq 'HASH') {
           return $togglejs;
       }
       my (%ordered,$total,%jstext);
       $total = 0;
       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 ltitools = Array('."'".join("','",@jsarray)."'".');'."\n";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function reorderLTITools(form,item) {
       var changedVal;
   $jstext
       var newpos = 'ltitools_add_pos';
       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<ltitools.length; i++) {
           var elementName = 'ltitools_'+ltitools[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>
   
   $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 {  sub wafproxy_javascript {
     my ($dom) = @_;      my ($dom) = @_;
     return <<"ENDSCRIPT";      return <<"ENDSCRIPT";
Line 2947  function toggleWAF() { Line 3360  function toggleWAF() {
 ENDSCRIPT  ENDSCRIPT
 }  }
   
   sub proctoring_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}));
       } else {
           %ordered = (
                          0 => 'proctorio',
                          1 => 'examity',
                      );
           $total = 2; 
       }
       my @jsarray = ();
       foreach my $item (sort {$a <=> $b } (keys(%ordered))) {
           push(@jsarray,$ordered{$item});
       }
       my $jstext = '    var proctors = Array('."'".join("','",@jsarray)."'".');'."\n";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function reorderProctoring(form,item) {
       var changedVal;
   $jstext
       var maxh = $total;
       var current = new Array;
       var changedVal = form.elements[item].options[form.elements[item].selectedIndex].value;
       for (var i=0; i<proctors.length; i++) {
           var elementName = 'proctoring_pos_'+proctors[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;
   }
   
   function toggleProctoring(form,item) {
       var fieldsets = document.getElementsByClassName('proctoring_'+item);
       if (fieldsets.length) {
           var radioname = 'proctoring_available_'+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') {
                          setvis = 1;
                          break;
                       }
                   }
               }
               for (var j=0; j<fieldsets.length; j++) {
                   if (setvis) {
                       fieldsets[j].style.display = 'block';
                   } else {
                       fieldsets[j].style.display = 'none';
                   }
               }
           }
       }
       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 {  sub autoupdate_javascript {
     return <<"ENDSCRIPT";      return <<"ENDSCRIPT";
 <script type="text/javascript">  <script type="text/javascript">
Line 2982  function toggleLastActiveDays(form) { Line 3797  function toggleLastActiveDays(form) {
 ENDSCRIPT  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 {  sub saml_javascript {
     return <<"ENDSCRIPT";      return <<"ENDSCRIPT";
 <script type="text/javascript">  <script type="text/javascript">
Line 3095  ENDSCRIPT Line 3945  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 3129  sub print_autoenroll { Line 3984  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 3169  sub print_autoenroll { Line 4040  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;
 }  }
Line 3195  sub print_autoupdate { Line 4072  sub print_autoupdate {
                 $updateoff = ' ';                  $updateoff = ' ';
             }              }
         }          }
         $enable = '<tr class="LC_odd_row">'.           $enable = '<tr class="LC_odd_row">'.
                   '<td>'.&mt($choices{'run'}).'</td>'.                    '<td>'.$choices{'run'}.'</td>'.
                   '<td class="LC_left_item"><span class="LC_nobreak"><label>'.                    '<td class="LC_left_item"><span class="LC_nobreak"><label>'.
                   '<input type="radio" name="autoupdate_run"'.                    '<input type="radio" name="autoupdate_run"'.
                   $updateoff.'value="0" />'.&mt('No').'</label>&nbsp;'.                    $updateoff.'value="0" />'.&mt('No').'</label>&nbsp;'.
Line 3830  sub print_contacts { Line 4707  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 4047  sub print_helpsettings { Line 4923  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 4081  sub print_helpsettings { Line 4956  sub print_helpsettings {
             my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype);              my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype);
             $css_class = $itemcount%2?' class="LC_odd_row"':'';              $css_class = $itemcount%2?' class="LC_odd_row"':'';
             my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$num."_pos'".');"';              my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$num."_pos'".');"';
             $datatable .= '<tr '.$css_class.'><td valign="top"><b>'.$role.'</b><br />'.              $datatable .= '<tr '.$css_class.'><td style="vertical-align: top"><b>'.$role.'</b><br />'.
                           '<select name="helproles_'.$num.'_pos"'.$chgstr.'>';                            '<select name="helproles_'.$num.'_pos"'.$chgstr.'>';
             for (my $k=0; $k<=$maxnum; $k++) {              for (my $k=0; $k<=$maxnum; $k++) {
                 my $vpos = $k+1;                  my $vpos = $k+1;
Line 4120  sub print_helpsettings { Line 4995  sub print_helpsettings {
         &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent);          &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent);
         my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype);          my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype);
         my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$count."_pos'".');"';          my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$count."_pos'".');"';
         $datatable .= '<tr '.$css_class.'><td valign="top"><span class="LC_nobreak"><label>'.          $datatable .= '<tr '.$css_class.'><td style="vertical-align: top"><span class="LC_nobreak"><label>'.
                       '<input type="hidden" name="helproles_maxnum" value="'.$maxnum.'" />'."\n".                        '<input type="hidden" name="helproles_maxnum" value="'.$maxnum.'" />'."\n".
                       '<select name="helproles_'.$count.'_pos"'.$chgstr.'>';                        '<select name="helproles_'.$count.'_pos"'.$chgstr.'>';
         for (my $k=0; $k<$maxnum+1; $k++) {          for (my $k=0; $k<$maxnum+1; $k++) {
Line 4401  sub radiobutton_prefs { Line 5276  sub radiobutton_prefs {
     foreach my $item (@{$toggles}) {      foreach my $item (@{$toggles}) {
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $css_class = $itemcount%2?' class="LC_odd_row"':'';
         $datatable .=          $datatable .=
             '<tr'.$css_class.'><td valign="top">'.              '<tr'.$css_class.'><td style="vertical-align: top">'.
             '<span class="LC_nobreak">'.$choices->{$item}.              '<span class="LC_nobreak">'.$choices->{$item}.
             '</span></td>';              '</span></td>';
         if ($align eq 'left') {          if ($align eq 'left') {
Line 4418  sub radiobutton_prefs { Line 5293  sub radiobutton_prefs {
                 $checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').'</label>';                  $checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').'</label>';
         } else {          } else {
             $datatable .=              $datatable .=
             '<label><input type="radio" name="'.                  '<label><input type="radio" name="'.
             $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').                  $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').
             '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.                  '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.
             $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>';                  $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>';
         }          }
         $datatable .= '</span>'.$additional.'</td></tr>';          $datatable .= '</span>'.$additional.'</td></tr>';
         $itemcount ++;          $itemcount ++;
Line 4429  sub radiobutton_prefs { Line 5304  sub radiobutton_prefs {
     return ($datatable,$itemcount);      return ($datatable,$itemcount);
 }  }
   
   sub print_ltitools {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my (%rules,%encrypt,%privkeys,%linkprot);
       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'}})) {
                           $encrypt{'toolsec_'.$key} = $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 'middle') {
               if (exists($settings->{'rules'})) {
                   if (ref($settings->{'rules'}) eq 'HASH') {
                       %rules = %{$settings->{'rules'}};
                   }
               }
           } else {
               foreach my $key ('encrypt','private','rules') {
                   if (exists($settings->{$key})) {
                       delete($settings->{$key});
                   }
               }
           }
       }
       my $datatable;
       my $itemcount = 1;
       if ($position eq 'top') {
           $datatable = &secrets_form($dom,'toolsec',\%encrypt,\%privkeys,$rowtotal);
       } elsif ($position eq 'middle') {
           $datatable = &password_rules('toolsecrets',\$itemcount,\%rules);
           $$rowtotal += $itemcount;
       } else {
           $datatable = &Apache::courseprefs::print_ltitools($dom,'',$settings,\$rowtotal,'','','domain');
       }
       return $datatable;
   }
   
   sub ltitools_names {
       my %lt = &Apache::lonlocal::texthash(
                                             'title'          => 'Title',
                                             'version'        => 'Version',
                                             'msgtype'        => 'Message Type',
                                             'sigmethod'      => 'Signature Method',
                                             'url'            => 'URL',
                                             'key'            => 'Key',
                                             'lifetime'       => 'Nonce lifetime (s)',
                                             'secret'         => 'Secret',
                                             'icon'           => 'Icon',
                                             'user'           => 'User',
                                             'fullname'       => 'Full Name',
                                             'firstname'      => 'First Name',
                                             'lastname'       => 'Last Name',
                                             'email'          => 'E-mail',
                                             'roles'          => 'Role',
                                             'window'         => 'Window',
                                             'tab'            => 'Tab',
                                             'iframe'         => 'iFrame',
                                             'height'         => 'Height',
                                             'width'          => 'Width',
                                             'linktext'       => 'Default Link Text',
                                             'explanation'    => 'Default Explanation',
                                             'passback'       => 'Tool can return grades:',
                                             'roster'         => 'Tool can retrieve roster:',
                                             'crstarget'      => 'Display target',
                                             'crslabel'       => 'Course label',
                                             'crstitle'       => 'Course title',
                                             'crslinktext'    => 'Link Text',
                                             '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_proctoring {
       my ($dom,$settings,$rowtotal) = @_;
       my $itemcount = 1;
       my (%ordered,%providernames,%current,%currentdef);
       my $confname = $dom.'-domainconfig';
       my $switchserver = &check_switchserver($dom,$confname);
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   $ordered{$num} = $item;
               }
           }
       } else {
           %ordered = (
                        1 => 'proctorio',
                        2 => 'examity',
                      );
       }
       %providernames = &proctoring_providernames();
       my $maxnum = scalar(keys(%ordered));
       my (%requserfields,%optuserfields,%defaults,%extended,%crsconf,@courseroles,@ltiroles);
       my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data();
       if (ref($requref) eq 'HASH') {
           %requserfields = %{$requref};
       }
       if (ref($opturef) eq 'HASH') {
           %optuserfields = %{$opturef};
       }
       if (ref($defref) eq 'HASH') {
           %defaults = %{$defref};
       }
       if (ref($extref) eq 'HASH') {
           %extended = %{$extref};
       }
       if (ref($crsref) eq 'HASH') {
           %crsconf = %{$crsref};
       }
       if (ref($rolesref) eq 'ARRAY') {
           @courseroles = @{$rolesref};
       }
       if (ref($ltiref) eq 'ARRAY') {
           @ltiroles = @{$ltiref};
       }
       my $datatable;
       my $css_class;
       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 $provider = $ordered{$items[$i]};
               my $optionsty = 'none';
               my ($available,$version,$lifetime,$imgsrc,$userincdom,$showroles,
                   %checkedfields,%rolemaps,%inuse,%crsconfig,%current);
               if (ref($settings) eq 'HASH') {
                   if (ref($settings->{$provider}) eq 'HASH') {
                       %current = %{$settings->{$provider}};
                       if ($current{'available'}) {
                           $optionsty = 'block';
                           $available = 1;
                       }
                       if ($current{'lifetime'} =~ /^\d+$/) {
                           $lifetime = $current{'lifetime'};
                       }
                       if ($current{'version'} =~ /^\d+\.\d+$/) {
                           $version = $current{'version'};
                       }
                       if ($current{'image'} ne '') {
                           $imgsrc = '<img src="'.$current{'image'}.'" alt="'.&mt('Proctoring service icon').'" />';
                       }
                       if (ref($current{'fields'}) eq 'ARRAY') {
                           map { $checkedfields{$_} = 1; } @{$current{'fields'}};
                       }
                       $userincdom = $current{'incdom'};
                       if (ref($current{'roles'}) eq 'HASH') {
                           %rolemaps = %{$current{'roles'}};
                           $checkedfields{'roles'} = 1;
                       }
                       if (ref($current{'defaults'}) eq 'ARRAY') {
                           foreach my $val (@{$current{'defaults'}}) {
                               if (grep(/^\Q$val\E$/,@{$defaults{$provider}})) {
                                   $inuse{$val} = 1;
                               } else {
                                   foreach my $poss (keys(%{$extended{$provider}})) {
                                       if (ref($extended{$provider}{$poss}) eq 'ARRAY') {
                                           if (grep(/^\Q$val\E$/,@{$extended{$provider}{$poss}})) {
                                               $inuse{$poss} = $val;
                                               last;
                                           }
                                       }
                                   }
                               }
                           }
                       } elsif (ref($current{'defaults'}) eq 'HASH') {
                           foreach my $key (keys(%{$current{'defaults'}})) {
                               my $currval = $current{'defaults'}{$key}; 
                               if (grep(/^\Q$key\E$/,@{$defaults{$provider}})) {
                                   $inuse{$key} = 1;
                               } else {
                                   my $match;
                                   foreach my $poss (keys(%{$extended{$provider}})) {
                                       if (ref($extended{$provider}{$poss}) eq 'ARRAY') {
                                           if (grep(/^\Q$key\E$/,@{$extended{$provider}{$poss}})) {
                                               $inuse{$poss} = $key;
                                               last;
                                           }
                                       } elsif (ref($extended{$provider}{$poss}) eq 'HASH') {
                                           foreach my $inner (sort(keys(%{$extended{$provider}{$poss}}))) {
                                               if (ref($extended{$provider}{$poss}{$inner}) eq 'ARRAY') {
                                                   if (grep(/^\Q$currval\E$/,@{$extended{$provider}{$poss}{$inner}})) {
                                                       $currentdef{$inner} = $currval;
                                                       $match = 1;
                                                       last;
                                                   }
                                               } elsif ($inner eq $key) {
                                                   $currentdef{$key} = $currval;
                                                   $match = 1;
                                                   last;
                                               }
                                           }
                                       }
                                       last if ($match);
                                   }
                               }
                           }
                       }
                       if (ref($current{'crsconf'}) eq 'ARRAY') {
                           map { $crsconfig{$_} = 1; } @{$current{'crsconf'}};
                       }
                   }
               }
               my %lt = &proctoring_titles($provider);
               my %fieldtitles = &proctoring_fieldtitles($provider);
               my $onclickavailable = ' onclick="toggleProctoring(this.form,'."'$provider'".');"';
               my %checkedavailable = (
                                      yes => '',
                                      no  => ' checked="checked"',
                                    );
               if ($available) {
                   $checkedavailable{'yes'} = $checkedavailable{'no'};
                   $checkedavailable{'no'} = '';
               }
               my $chgstr = ' onchange="javascript:reorderProctoring(this.form,'."'proctoring_pos_".$provider."'".');"';
               $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                            .'<select name="proctoring_pos_'.$provider.'"'.$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>';
               }
               if ($version eq '') {
                   if ($provider eq 'proctorio') {
                       $version = '1.0';
                   } elsif ($provider eq 'examity') {
                       $version = '1.1';
                   }
               }
               if ($lifetime eq '') {
                   $lifetime = '300';
               }
               $datatable .=
                   '</select>'.('&nbsp;'x2).'<b>'.$providernames{$provider}.'</b></span><br />'.
                   '<span class="LC_nobreak">'.$lt{'avai'}.'&nbsp;'.
                   '<label><input type="radio" name="proctoring_available_'.$provider.'" value="1"'.$onclickavailable.$checkedavailable{yes}.' />'.&mt('Yes').'</label>&nbsp;'."\n".
                   '<label><input type="radio" name="proctoring_available_'.$provider.'" value="0"'.$onclickavailable.$checkedavailable{no}.' />'.&mt('No').'</label></span>'."\n".
                   '</td>'.
                   '<td colspan="2">'.
                   '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'base'}.'</legend>'.
                   '<span class="LC_nobreak">'.$lt{'version'}.':<select name="proctoring_'.$provider.'_version">'.
                   '<option value="'.$version.'" selected="selected">'.$version.'</option></select></span> '."\n".
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'sigmethod'}.':<select name="proctoring_'.$provider.'_sigmethod">'.
                   '<option value="HMAC-SHA1" selected="selected">HMAC-SHA1</option>'.
                   '<option value="HMAC-SHA256">HMAC-SHA256</option></select></span>'.
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="5" name="proctoring_'.$provider.'_lifetime" value="'.$lifetime.'" /></span> '."\n".
                   '<br />'.
                   '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="proctoring_'.$provider.'_url" value="'.$current{'url'}.'" /></span> '."\n".
                   '<br />'.
                   '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="proctoring_'.$provider.'_key" value="'.$current{'key'}.'" /></span> '."\n".
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="proctoring_'.$provider.'_secret" value="'.$current{'secret'}.'" />'.
                   '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.proctoring_'.$provider.'_secret.type='."'text'".' } else { this.form.proctoring_'.$provider.'_secret.type='."'password'".' }" />'.$lt{'visible'}.'</label></span><br />'."\n";
               $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;';
               if ($imgsrc) {
                   $datatable .= $imgsrc.
                                 '<label><input type="checkbox" name="proctoring_image_del"'.
                                 ' value="'.$provider.'" />'.&mt('Delete?').'</label></span> '.
                                 '<span class="LC_nobreak">&nbsp;'.&mt('Replace:');
               }
               $datatable .= '&nbsp;';
               if ($switchserver) {
                   $datatable .= &mt('Upload to library server: [_1]',$switchserver);
               } else {
                   $datatable .= '<input type="file" name="proctoring_image_'.$provider.'" value="" />';
               }
               unless ($imgsrc) {
                   $datatable .= '<br />('.&mt('if larger than 21x21 pixels, image will be scaled').')';
               }
               $datatable .= '</fieldset>'."\n";
               if (ref($requserfields{$provider}) eq 'ARRAY') {
                   if (@{$requserfields{$provider}} > 0) {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'requ'}.'</legend>';
                       foreach my $field (@{$requserfields{$provider}}) {
                           $datatable .= '<span class="LC_nobreak">'.
                                         '<label><input type="checkbox" name="proctoring_reqd_'.$provider.'" value="'.$field.'" checked="checked" disabled="disabled" />'.
                                         $lt{$field}.'</label>';
                           if ($field eq 'user') {
                               my $seluserdom = '';
                               my $unseluserdom = ' selected="selected"';
                               if ($userincdom) {
                                   $seluserdom = $unseluserdom;
                                   $unseluserdom = '';
                               }
                               $datatable .= ':&nbsp;'.
                                   '<select name="proctoring_userincdom_'.$provider.'">'.
                                   '<option value="0"'.$unseluserdom.'>'.$lt{'username'}.'</option>'.
                                   '<option value="1"'.$seluserdom.'>'.$lt{'uname:dom'}.'</option>'.
                                   '</select>&nbsp;';
                           } else {
                               $datatable .= '&nbsp;';
                               if ($field eq 'roles') {
                                   $showroles = 1; 
                               } 
                           }
                           $datatable .= '</span> ';
                       }
                   }
                   $datatable .= '</fieldset>'."\n";
               }
               if (ref($optuserfields{$provider}) eq 'ARRAY') {
                   if (@{$optuserfields{$provider}} > 0) {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'optu'}.'</legend>'; 
                       foreach my $field (@{$optuserfields{$provider}}) {
                           my $checked;
                           if ($checkedfields{$field}) {
                               $checked = ' checked="checked"';
                           }
                           $datatable .= '<span class="LC_nobreak">'.
                                         '<label><input type="checkbox" name="proctoring_optional_'.$provider.'" value="'.$field.'"'.$checked.' />'.$lt{$field}.'</label></span>&nbsp; ';
                       }
                       $datatable .= '</fieldset>'."\n";
                   }
               }
               if (ref($defaults{$provider}) eq 'ARRAY') {
                   if (@{$defaults{$provider}}) {
                       my (%options,@selectboxes);
                       if (ref($extended{$provider}) eq 'HASH') {
                           %options = %{$extended{$provider}};
                       }
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'defa'}.'</legend>';
                       my ($rem,$numinrow,$dropdowns);
                       if ($provider eq 'proctorio') { 
                           $datatable .= '<table>';
                           $numinrow = 4;
                       }
                       my $i = 0;
                       foreach my $field (@{$defaults{$provider}}) {
                           my $checked;
                           if ($inuse{$field}) {
                               $checked = ' checked="checked"';
                           }
                           if ($provider eq 'examity') {
                               if ($field eq 'display') {
                                   $datatable .= '<span class="LC_nobreak">'.&mt('Display target:');
                                   foreach my $option ('iframe','tab','window') {
                                       my $checkdisp;
                                       if ($currentdef{'target'} eq $option) {
                                           $checkdisp = ' checked="checked"';
                                       }
                                       $datatable .= '<label><input type="radio" name="proctoring_target_'.$provider.'" value="'.$option.'"'.$checkdisp.' />'.
                                       $fieldtitles{$option}.'</label>'.('&nbsp;'x2);
                                   }
                                   $datatable .= ('&nbsp;'x4);
                                   foreach my $dimen ('width','height') {
                                       $datatable .= '<label>'.$fieldtitles{$dimen}.'&nbsp;'.
                                                     '<input type="text" name="proctoring_'.$dimen.'_'.$provider.'" size="5" '.
                                                     'value="'.$currentdef{$dimen}.'" /></label>'.
                                                     ('&nbsp;'x2);
                                   }
                                   $datatable .= '</span><br />'.
                                                 '<div class="LC_left_float">'.$fieldtitles{'linktext'}.'<br />'.
                                                 '<input type="text" name="proctoring_linktext_'.$provider.'" '.
                                                 'size="25" value="'.$currentdef{'linktext'}.'" /></div>'.
                                                 '<div class="LC_left_float">'.$fieldtitles{'explanation'}.'<br />'.
                                                 '<textarea name="proctoring_explanation_'.$provider.'" rows="5" cols="40">'.
                                                 $currentdef{'explanation'}.
                                                 '</textarea></div><div style=""></div><br />';
                               }
                           } else {
                               if ((exists($options{$field})) && (ref($options{$field}) eq 'ARRAY')) {
                                   my ($output,$selnone);
                                   unless ($checked) {
                                       $selnone = ' selected="selected"';
                                   }
                                   $output .= '<span class="LC_nobreak">'.$fieldtitles{$field}.': '.
                                              '<select name="proctoring_defaults_'.$field.'_'.$provider.'">'.
                                              '<option value=""'.$selnone.'>'.&mt('Not in use').'</option>'; 
                                   foreach my $option (@{$options{$field}}) {
                                       my $sel; 
                                       if ($inuse{$field} eq $option) {
                                           $sel = ' selected="selected"';
                                       }
                                       $output .= '<option value="'.$option.'"'.$sel.'>'.$fieldtitles{$option}.'</option>';
                                   }
                                   $output .= '</select></span>';
                                   push(@selectboxes,$output);
                               } else {
                                   $rem = $i%($numinrow);
                                   if ($rem == 0) {
                                       if ($i > 0) {
                                           $datatable .= '</tr>';
                                       }
                                       $datatable .= '<tr>';
                                   }
                                   $datatable .= '<td class="LC_left_item">'.
                                                 '<span class="LC_nobreak">'.
                                                 '<label><input type="checkbox" name="proctoring_defaults_'.$provider.'" value="'.$field.'"'.$checked.' />'.
                                                 $fieldtitles{$field}.'</label></span></td>';
                                   $i++;
                               }
                           }
                       }
                       if ($provider eq 'proctorio') {
                           if ($numinrow) {
                               $rem = $i%$numinrow;
                           }
                           my $colsleft = $numinrow - $rem;
                           if ($colsleft > 1) {
                               $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">';
                           } else {
                               $datatable .= '<td class="LC_left_item">';
                           }
                           $datatable .= '&nbsp;'.
                                         '</td></tr></table>';
                           if (@selectboxes) {
                               $datatable .= '<hr /><table>';
                               $numinrow = 2;
                               for (my $i=0; $i<@selectboxes; $i++) {
                                   $rem = $i%($numinrow);
                                   if ($rem == 0) {
                                       if ($i > 0) {
                                           $datatable .= '</tr>';
                                       }
                                       $datatable .= '<tr>';
                                   }
                                   $datatable .= '<td class="LC_left_item">'.
                                                 $selectboxes[$i].'</td>';
                               }
                               if ($numinrow) {
                                   $rem = $i%$numinrow;
                               }
                               $colsleft = $numinrow - $rem;
                               if ($colsleft > 1) {
                                   $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">';
                               } else {
                                   $datatable .= '<td class="LC_left_item">';
                               }
                               $datatable .= '&nbsp;'.
                                             '</td></tr></table>';
                           }
                       }
                       $datatable .= '</fieldset>';
                   }
                   if (ref($crsconf{$provider}) eq 'ARRAY') {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
                                     '<legend>'.&mt('Configurable in course').'</legend>';
                       my ($rem,$numinrow);
                       if ($provider eq 'proctorio') {
                           $datatable .= '<table>';
                           $numinrow = 4;
                       }
                       my $i = 0;
                       foreach my $item (@{$crsconf{$provider}}) {
                           my $name;
                           if ($provider eq 'examity') {
                               $name = $lt{'crs'.$item};
                           } elsif ($provider eq 'proctorio') {
                               $name = $fieldtitles{$item};
                               $rem = $i%($numinrow);
                               if ($rem == 0) {
                                   if ($i > 0) {
                                       $datatable .= '</tr>';
                                   }
                                   $datatable .= '<tr>';
                               }
                               $datatable .= '<td class="LC_left_item>';
                           }
                           my $checked;
                           if ($crsconfig{$item}) {
                               $checked = ' checked="checked"';
                           }
                           $datatable .= '<span class="LC_nobreak"><label>'.
                                         '<input type="checkbox" name="proctoring_crsconf_'.$provider.'" value="'.$item.'"'.$checked.' />'.
                                         $name.'</label></span>';
                           if ($provider eq 'examity') {
                               $datatable .= '&nbsp; ';
                           }
                           $datatable .= "\n";
                           $i++;
                       }
                       if ($provider eq 'proctorio') {
                           if ($numinrow) {
                               $rem = $i%$numinrow;
                           }
                           my $colsleft = $numinrow - $rem;
                           if ($colsleft > 1) {
                               $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">';
                           } else {
                               $datatable .= '<td class="LC_left_item">';
                           }
                           $datatable .= '&nbsp;'.
                                         '</td></tr></table>';
                       }
                       $datatable .= '</fieldset>';
                   }
                   if ($showroles) {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
                                     '<legend>'.&mt('Role mapping').'</legend><table><tr>';
                       foreach my $role (@courseroles) {
                           my ($selected,$selectnone);
                           if (!$rolemaps{$role}) {
                               $selectnone = ' selected="selected"';
                           }
                           $datatable .= '<td style="text-align: center">'.
                                         &Apache::lonnet::plaintext($role,'Course').'<br />'.
                                         '<select name="proctoring_roles_'.$role.'_'.$provider.'">'.
                                         '<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>'.
                                     '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
                                     '<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></td><td>lms</td>'.
                                     '<td><input type="text" name="proctoring_customval_lms_'.$provider.'"'.
                                     ' value="Loncapa" disabled="disabled"/></td></tr>';
                       if ((ref($settings) eq 'HASH') && (ref($settings->{$provider}) eq 'HASH') &&
                           (ref($settings->{$provider}->{'custom'}) eq 'HASH')) {
                           my %custom = %{$settings->{$provider}->{'custom'}};
                           if (keys(%custom) > 0) {
                               foreach my $key (sort(keys(%custom))) {
                                   next if ($key eq 'lms');
                                   $datatable .= '<tr><td><span class="LC_nobreak">'.
                                                 '<label><input type="checkbox" name="proctoring_customdel_'.$provider.'" value="'.
                                                 $key.'" />'.&mt('Delete').'</label></span></td><td>'.$key.'</td>'.
                                                 '<td><input type="text" name="proctoring_customval_'.$key.'_'.$provider.'"'.
                                                 ' value="'.$custom{$key}.'" /></td></tr>';
                               }
                           }
                       }
                       $datatable .= '<tr><td><span class="LC_nobreak">'.
                                     '<label><input type="checkbox" name="proctoring_customadd" value="'.$provider.'" />'.
                                     &mt('Add more').'</label></span></td><td><input type="text" name="proctoring_custom_name_'.$provider.'" />'.
                                     '</td><td><input type="text" name="proctoring_custom_value_'.$provider.'" /></td></tr>'.
                                     '</table></fieldset></td></tr>'."\n";
                   }
                   $datatable .= '</td></tr>';
               }
               $itemcount ++;
           }
       }
       return $datatable;
   }
   
   sub proctoring_data {
       my $requserfields = {
                         proctorio => ['user'],
                         examity   => ['roles','user'],
                        };
       my $optuserfields = {
                           proctorio => ['fullname'],
                           examity   => ['fullname','firstname','lastname','email'],
                        };
       my $defaults = {
                     proctorio => ['recordvideo','recordaudio','recordscreen','recordwebtraffic',
                                   'recordroomstart','verifyvideo','verifyaudio','verifydesktop',
                                   'verifyid','verifysignature','fullscreen','clipboard','tabslinks',
                                   'closetabs','onescreen','print','downloads','cache','rightclick',
                                   'reentry','calculator','whiteboard'],
                     examity   => ['display'],
                   };
       my $extended = { 
                     proctorio => {
                                    verifyid => ['verifyidauto','verifyidlive'],
                                    fullscreen => ['fullscreenlenient','fullscreenmoderate','fullscreensever'],
                                    tabslinks => ['notabs','linksonly'],
                                    reentry => ['noreentry','agentreentry'],
                                    calculator => ['calculatorbasic','calculatorsci'],
                                  },
                     examity => {
                                  display => {
                                              target      => ['iframe','tab','window'],
                                              width       => '',
                                              height      => '',
                                              linktext    => '',
                                              explanation => '',
                                             },
                                },
                   };
       my $crsconf = {
                    proctorio => ['recordvideo','recordaudio','recordscreen','recordwebtraffic',
                                  'recordroomstart','verifyvideo','verifyaudio','verifydesktop',
                                  'verifyid','verifysignature','fullscreen','clipboard','tabslinks',
                                  'closetabs','onescreen','print','downloads','cache','rightclick',
                                  'reentry','calculator','whiteboard'],
                    examity => ['label','title','target','linktext','explanation','append'],
                  };
       my $courseroles = ['cc','in','ta','ep','st'];
       my $ltiroles = ['Instructor','ContentDeveloper','TeachingAssistant','Learner'];
       return ($requserfields,$optuserfields,$defaults,$extended,$crsconf,$courseroles,$ltiroles);
   }
   
   sub proctoring_titles {
       my ($item) = @_;
       my (%common_lt,%custom_lt);
       %common_lt = &Apache::lonlocal::texthash (
                        'avai'      => 'Available?',
                        'base'      => 'Basic Settings',
                        'requ'      => 'User data required to be sent on launch',
                        'optu'      => 'User data optionally sent on launch',
                        'udsl'      => 'User data sent on launch',
                        'defa'      => 'Defaults for items configurable in course',
                        'sigmethod' => 'Signature Method',
                        'key'       => 'Key',
                        'lifetime'  => 'Nonce lifetime (s)',
                        'secret'    => 'Secret',
                        'icon'      => 'Icon',
                        'fullname'  => 'Full Name',
                        'visible'   => 'Visible input',
                        'username'  => 'username',
                        'user'      => 'User',
                    );
       if ($item eq 'proctorio') {
           %custom_lt = &Apache::lonlocal::texthash (
                            'version'        => 'OAuth version',
                            'url'            => 'API URL',
                            'uname:dom'      => 'username-domain',
           );
       } elsif ($item eq 'examity') {
           %custom_lt = &Apache::lonlocal::texthash (
                            'version'        => 'LTI Version',
                            'url'            => 'URL',
                            'uname:dom'      => 'username:domain',
                            'msgtype'        => 'Message Type',
                            'firstname'      => 'First Name',
                            'lastname'       => 'Last Name',
                            'email'          => 'E-mail',
                            'roles'          => 'Role',
                            'crstarget'      => 'Display target',
                            'crslabel'       => 'Course label',
                            'crstitle'       => 'Course title', 
                            'crslinktext'    => 'Link Text',
                            'crsexplanation' => 'Explanation',
                            'crsappend'      => 'Provider URL',
           );
       }
       my %lt = (%common_lt,%custom_lt);
       return %lt;
   }
   
   sub proctoring_fieldtitles {
       my ($item) = @_;
       if ($item eq 'proctorio') {
           return &Apache::lonlocal::texthash (
                     'recordvideo' => 'Record video',
                     'recordaudio' => 'Record audio',
                     'recordscreen' => 'Record screen',
                     'recordwebtraffic' => 'Record web traffic',
                     'recordroomstart' => 'Record room scan',
                     'verifyvideo' => 'Verify webcam',
                     'verifyaudio' => 'Verify microphone',
                     'verifydesktop' => 'Verify desktop recording',
                     'verifyid' => 'Photo ID verification',
                     'verifysignature' => 'Require signature',
                     'fullscreen' => 'Fullscreen',
                     'clipboard' => 'Disable copy/paste',
                     'tabslinks' => 'New tabs/windows',
                     'closetabs' => 'Close other tabs',
                     'onescreen' => 'Limit to single screen',
                     'print' => 'Disable Printing',
                     'downloads' => 'Disable Downloads',
                     'cache' => 'Empty cache after exam',
                     'rightclick' => 'Disable right click',
                     'reentry' => 'Re-entry to exam',
                     'calculator' => 'Onscreen calculator',
                     'whiteboard' => 'Onscreen whiteboard',
                     'verifyidauto' => 'Automated verification',
                     'verifyidlive' => 'Live agent verification',
                     'fullscreenlenient' => 'Forced, but can navigate away for up to 30s',
                     'fullscreenmoderate' => 'Forced, but can navigate away for up to 15s',
                     'fullscreensever' => 'Forced, navigation away ends exam',
                     'notabs' => 'Disaallowed',
                     'linksonly' => 'Allowed from links in exam',
                     'noreentry' => 'Disallowed',
                     'agentreentry' => 'Agent required for re-entry',
                     'calculatorbasic' => 'Basic',
                     'calculatorsci' => 'Scientific',
           );
       } elsif ($item eq 'examity') {
           return &Apache::lonlocal::texthash (
                   'target'         => 'Display target',   
                   'window'         => 'Window',
                   'tab'            => 'Tab',
                   'iframe'         => 'iFrame',
                   'height'         => 'Height (pixels)',
                   'width'          => 'Width (pixels)',
                   'linktext'       => 'Default Link Text',
                   'explanation'    => 'Default Explanation',
                   'append'         => 'Provider URL',
           );
       }
   }
   
   sub proctoring_providernames {
       return (
                proctorio => 'Proctorio',
                examity   => 'Examity',
              );
   }
   
   sub print_lti {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my $itemcount = 1;
       my ($datatable,$css_class);
       my (%rules,%encrypt,%privkeys,%linkprot);
       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 'middle') {
               if (exists($settings->{'rules'})) {
                   if (ref($settings->{'rules'}) eq 'HASH') {
                       %rules = %{$settings->{'rules'}};
                   }
               }
           } 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') {
                   if (exists($settings->{$key})) {
                       delete($settings->{$key});
                   }
               }
           }
       }
       if ($position eq 'top') {
           $datatable = &secrets_form($dom,'ltisec',\%encrypt,\%privkeys,$rowtotal);
       } elsif ($position eq 'middle') {
           $datatable = &password_rules('ltisecrets',\$itemcount,\%rules);
           $$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;
   }
   
   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','placement','lti');
       my %coursetypetitles = &Apache::lonlocal::texthash (
                                  official   => 'Official',
                                  unofficial => 'Unofficial',
                                  community  => 'Community',
                                  textbook   => 'Textbook',
                                  placement  => 'Placement Test',
                                  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 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 (
           canuse_pdfforms      => 'Course/Community users can create/upload PDF forms',
         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 = (
                               'canuse_pdfforms' => 'off',
                             'uselcmath'       => 'on',                              'uselcmath'       => 'on',
                             'usejsme'         => 'on',                              'usejsme'         => 'on',
                               'inline_chem'     => 'on',
                             'canclone'        => 'none',                              'canclone'        => 'none',
                           );                            );
         @toggles = ('uselcmath','usejsme');          @toggles = ('canuse_pdfforms','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 4489  sub print_coursedefaults { Line 6853  sub print_coursedefaults {
         $datatable = $mathdisp.$datatable;          $datatable = $mathdisp.$datatable;
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $css_class = $itemcount%2?' class="LC_odd_row"':'';
         $datatable .=          $datatable .=
             '<tr'.$css_class.'><td valign="top">'.              '<tr'.$css_class.'><td style="vertical-align: top">'.
             '<span class="LC_nobreak">'.$choices{'canclone'}.              '<span class="LC_nobreak">'.$choices{'canclone'}.
             '</span></td><td class="LC_left_item">';              '</span></td><td class="LC_left_item">';
         my $currcanclone = 'none';          my $currcanclone = 'none';
Line 4533  sub print_coursedefaults { Line 6897  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 4560  sub print_coursedefaults { Line 6924  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 @types = ('official','unofficial','community','textbook');          my $ltiauth = 0;
           my %domexttool;
           my %exttool;
           my @types = ('official','unofficial','community','textbook','placement');
         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 4616  sub print_coursedefaults { Line 7012  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 4627  sub print_coursedefaults { Line 7026  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 4641  sub print_coursedefaults { Line 7043  sub print_coursedefaults {
         $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.          $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                       $choices{'uploadquota'}.                        $choices{'uploadquota'}.
                       '</span></td>'.                        '</span></td>'.
                       '<td align="right" class="LC_right_item">'.                        '<td style="text-align: right" class="LC_right_item">'.
                       '<table><tr>';                        '<table><tr>';
         foreach my $type (@types) {          foreach my $type (@types) {
             $datatable .= '<td align="center">'.&mt($type).'<br />'.              $datatable .= '<td style="text-align: center">'.&mt($type).'<br />'.
                            '<input type="text" name="uploadquota_'.$type.'"'.                             '<input type="text" name="uploadquota_'.$type.'"'.
                            ' value="'.$curruploadquota{$type}.'" size="5" /></td>';                             ' value="'.$curruploadquota{$type}.'" size="5" /></td>';
         }          }
         $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 4659  sub print_coursedefaults { Line 7074  sub print_coursedefaults {
                          '<i>'.&mt('Default credits').'</i><br /><table><tr>';                           '<i>'.&mt('Default credits').'</i><br /><table><tr>';
         foreach my $type (@types) {          foreach my $type (@types) {
             next if ($type eq 'community');              next if ($type eq 'community');
             $additional .= '<td align="center">'.&mt($type).'<br />'.              $additional .= '<td style="text-align: center">'.&mt($type).'<br />'.
                            '<input type="text" name="'.$type.'_credits"'.                             '<input type="text" name="'.$type.'_credits"'.
                            ' value="'.$defcredits{$type}.'" size="3" /></td>';                             ' value="'.$defcredits{$type}.'" size="3" /></td>';
         }          }
Line 4683  sub print_coursedefaults { Line 7098  sub print_coursedefaults {
                       '<i>'.&mt('Enter 0 to remain disabled until page reload.').'</i><br />'.                        '<i>'.&mt('Enter 0 to remain disabled until page reload.').'</i><br />'.
                       '<table><tr>';                        '<table><tr>';
         foreach my $type (@types) {          foreach my $type (@types) {
             $additional .= '<td align="center">'.&mt($type).'<br />'.              $additional .= '<td style="text-align: center">'.&mt($type).'<br />'.
                            '<input type="text" name="'.$type.'_timeout" value="'.                             '<input type="text" name="'.$type.'_timeout" value="'.
                            $deftimeout{$type}.'" size="5" /></td>';                             $deftimeout{$type}.'" size="5" /></td>';
         }          }
Line 4701  sub print_coursedefaults { Line 7116  sub print_coursedefaults {
         $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.          $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                       $choices{'mysqltables'}.                        $choices{'mysqltables'}.
                       '</span></td>'.                        '</span></td>'.
                       '<td align="right" class="LC_right_item">'.                        '<td style="text-align: right" class="LC_right_item">'.
                       '<table><tr>';                        '<table><tr>';
         foreach my $type (@types) {          foreach my $type (@types) {
             $datatable .= '<td align="center">'.&mt($type).'<br />'.              $datatable .= '<td style="text-align: center">'.&mt($type).'<br />'.
                            '<input type="text" name="mysqltables_'.$type.'"'.                             '<input type="text" name="mysqltables_'.$type.'"'.
                            ' value="'.$currmysql{$type}.'" size="8" /></td>';                             ' value="'.$currmysql{$type}.'" size="8" /></td>';
         }          }
         $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 4720  sub print_selfenrollment { Line 7172  sub print_selfenrollment {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my ($css_class,$datatable);      my ($css_class,$datatable);
     my $itemcount = 1;      my $itemcount = 1;
     my @types = ('official','unofficial','community','textbook');      my @types = ('official','unofficial','community','textbook','placement');
     if (($position eq 'top') || ($position eq 'middle')) {      if (($position eq 'top') || ($position eq 'middle')) {
         my ($rowsref,$titlesref) = &Apache::lonuserutils::get_selfenroll_titles();          my ($rowsref,$titlesref) = &Apache::lonuserutils::get_selfenroll_titles();
         my %descs = &Apache::lonuserutils::selfenroll_default_descs();          my %descs = &Apache::lonuserutils::selfenroll_default_descs();
Line 4941  sub print_validation_rows { Line 7393  sub print_validation_rows {
     return $datatable;      return $datatable;
 }  }
   
   sub print_privacy {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my ($datatable,$css_class,$numinrow,@items,%names,$othertitle,$usertypes,$types);
       my $itemcount = 0;
       if ($position eq 'top') {
           $numinrow = 2;
       } else {
           @items = ('domain','author','course','community');
           %names = &Apache::lonlocal::texthash (
                        domain => 'Assigned domain role(s)',
                        author => 'Assigned co-author role(s)',
                        course => 'Assigned course role(s)',
                        community => 'Assigned community role(s)',
                    );
           $numinrow = 4;
           ($othertitle,$usertypes,$types) =
               &Apache::loncommon::sorted_inst_types($dom);
       }
       if (($position eq 'top') || ($position eq 'middle')) {
           my (%by_ip,%by_location,@intdoms,@instdoms);
           &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
           if ($position eq 'top') {
               my %curr;
               my @options = ('none','user','domain','auto');
               my %titles = &Apache::lonlocal::texthash (
                   none   => 'Not allowed',
                   user   => 'User authorizes',
                   domain => 'DC authorizes',
                   auto   => 'Unrestricted',
                   instdom => 'Other domain shares institution/provider',
                   extdom => 'Other domain has different institution/provider',
                   notify => 'Notify when role needs authorization',
               );
               my %names = &Apache::lonlocal::texthash (
                   domain => 'Domain role',
                   author => 'Co-author role',
                   course => 'Course role',
                   community => 'Community role',
               );
               my $primary_id = &Apache::lonnet::domain($dom,'primary');
               my $intdom = &Apache::lonnet::internet_dom($primary_id);
               foreach my $domtype ('instdom','extdom') {
                   my (%checked,$skip);
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   $datatable .= '<tr'.$css_class.'><td>'.$titles{$domtype}.'</td>'.
                                 '<td class="LC_left_item">';
                   if ($domtype eq 'instdom') {
                       unless (@instdoms > 1) {
                           $datatable .= &mt('Nothing to set, as no domains besides [_1] are hosted by [_2]',$dom,$intdom);
                           $skip = 1;
                       }
                   } elsif ($domtype eq 'extdom') {
                       if (keys(%by_location) == 0) {
                           $datatable .= &mt('Nothing to set, as no other hosts besides [_1]',$intdom);
                           $skip = 1;
                       }
                   }
                   unless ($skip) {
                       foreach my $roletype ('domain','author','course','community') {
                           $checked{'auto'} = ' checked="checked"';
                           if (ref($settings) eq 'HASH') {
                               if (ref($settings->{approval}) eq 'HASH') {
                                   if (ref($settings->{approval}->{$domtype}) eq 'HASH') {
                                       if ($settings->{approval}->{$domtype}->{$roletype}=~ /^(none|user|domain)$/) {
                                           $checked{$1} = ' checked="checked"';
                                           $checked{'auto'} = '';
                                       }
                                   }
                               }
                           }
                           $datatable .= '<fieldset><legend>'.$names{$roletype}.'</legend>';
                           foreach my $option (@options) {
                               $datatable .= '<span class="LC_nobreak"><label>'.
                                             '<input type="radio" name="privacy_approval_'.$domtype.'_'.$roletype.'" '.
                                             'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}.
                                             '</label></span>&nbsp; ';
                           }
                           $datatable .= '</fieldset>';
                       }
                   }
                   $datatable .= '</td></tr>';
                   $itemcount ++;
               }
               $css_class = $itemcount%2?' class="LC_odd_row"':'';
               $datatable .= '<tr'.$css_class.'><td>'.$titles{'notify'}.'</td>'.
                             '<td class="LC_left_item">';
               if ((@instdoms > 1) || (keys(%by_location) > 0)) {
                   my %curr;
                   if (ref($settings) eq 'HASH') {
                       if ($settings->{'notify'} ne '') {
                           map {$curr{$_}=1;} split(/,/,$settings->{'notify'});
                       }
                   }
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   my ($numdc,$table,$rows) = &active_dc_picker($dom,$numinrow,'checkbox',
                                                                'privacy_notify',%curr);
                   if ($numdc > 0) {
                       $datatable .= $table;
                   } else {
                       $datatable .= &mt('There are no active Domain Coordinators');
                   }
               } else {
                   $datatable .= &mt('Nothing to set here, as there are no other domains');
               }
               $datatable .='</td></tr>';
           } elsif ($position eq 'middle') {
               if ((@instdoms > 1) || (keys(%by_location) > 0)) {
                   if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
                       foreach my $item (@{$types}) {
                           $datatable .= &modifiable_userdata_row('privacy','othdom_'.$item,$settings,
                                                                  $numinrow,$itemcount,'','','','','',
                                                                  '',$usertypes->{$item});
                           $itemcount ++;
                       }
                   }
                   $datatable .= &modifiable_userdata_row('privacy','othdom_default',$settings,
                                                          $numinrow,$itemcount,'','','','','',
                                                          '',$othertitle);
                   $itemcount ++;
               } else {
                   my (@insttypes,%insttitles);
                   if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
                       @insttypes = @{$types};
                       %insttitles = %{$usertypes};
                   }
                   foreach my $item (@insttypes,'default') {
                       my $title;
                       if ($item eq 'default') {
                           $title = $othertitle;
                       } else {
                           $title = $insttitles{$item};
                       }
                       $css_class = $itemcount%2?' class="LC_odd_row"':'';
                       $datatable .= '<tr'.$css_class.'>'.
                                     '<td class="LC_left_item">'.$title.'</td>'.
                                     '<td class="LC_left_item">'.
                                     &mt('Nothing to set here, as there are no other domains').
                                     '</td></tr>';
                       $itemcount ++;
                   }
               }
           }
       } else {
           my $prefix;
           if ($position eq 'lower') {
               $prefix = 'priv';
           } else {
               $prefix = 'unpriv';
           }
           foreach my $item (@items) {
               $datatable .= &modifiable_userdata_row('privacy',$prefix.'_'.$item,$settings,
                                                      $numinrow,$itemcount,'','','','','',
                                                      '',$names{$item});
               $itemcount ++;
           }
       }
       if (ref($rowtotal)) {
           $$rowtotal += $itemcount;
       }
       return $datatable;
   }
   
 sub print_passwords {  sub print_passwords {
     my ($position,$dom,$confname,$settings,$rowtotal) = @_;      my ($position,$dom,$confname,$settings,$rowtotal) = @_;
     my ($datatable,$css_class);      my ($datatable,$css_class);
Line 4962  sub print_passwords { Line 7576  sub print_passwords {
         min            => 'Minimum password length',          min            => 'Minimum password length',
         max            => 'Maximum password length',          max            => 'Maximum password length',
         chars          => 'Required characters',          chars          => 'Required characters',
           expire         => 'Password expiration (days)',
         numsaved       => 'Number of previous passwords to save and disallow reuse',          numsaved       => 'Number of previous passwords to save and disallow reuse',
     );      );
     if ($position eq 'top') {      if ($position eq 'top') {
Line 5226  sub print_passwords { Line 7841  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 5328  sub print_passwords { Line 7866  sub print_passwords {
         $datatable .= '<tr '.$css_class.'>'.          $datatable .= '<tr '.$css_class.'>'.
                       '<td>'.                        '<td>'.
                       &mt('Requirements').'<ul>'.                        &mt('Requirements').'<ul>'.
                       '<li>'.&mt("Course 'type' is not a Community").'</li>'.                        '<li>'.&mt("Course 'type' is not a Community or Placement Test").'</li>'.
                       '<li>'.&mt('User is Course Coordinator and also course owner').'</li>'.                        '<li>'.&mt('User is Course Coordinator and also course owner').'</li>'.
                       '<li>'.&mt("Student's only active roles are student role(s) in course(s) owned by this user").'</li>'.                        '<li>'.&mt("Student's only active roles are student role(s) in course(s) owned by this user").'</li>'.
                       '<li>'.&mt('User, course, and student share same domain').'</li>'.                        '<li>'.&mt('User, course, and student share same domain').'</li>'.
Line 5363  sub print_passwords { Line 7901  sub print_passwords {
     return $datatable;      return $datatable;
 }  }
   
   sub password_rules {
       my ($prefix,$itemcountref,$settings) = @_;
       my ($min,$max,%chars,$expire,$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->{expire}) {
                   $expire = $settings->{expire};
               }
               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{'expire'} = &mt('Password expiration (days)');
           $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{'expire'}.'</td>'.
                         '<td class="LC_left_item"><span class="LC_nobreak">'.
                         '<input type="text" name="'.$prefix.'_expire" value="'.$expire.'" size="4" '.
                         'onblur="javascript:warnInt'.$prefix.'(this);" />'.
                         '<span class="LC_fontsize_small"> '.&mt('(Leave blank for no expiration)').'</span>'.
                         '</span></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="'.$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 {  sub print_wafproxy {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my $css_class;      my $css_class;
Line 5672  sub remoteip_methods { Line 8333  sub remoteip_methods {
   
 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,$itemcount,%checked,%choices);
     my (%by_ip,%by_location,@intdoms);      my (%by_ip,%by_location,@intdoms,@instdoms);
     &build_location_hashes(\@intdoms,\%by_ip,\%by_location);      &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
   
     my @alldoms = &Apache::lonnet::all_domains();      my @alldoms = &Apache::lonnet::all_domains();
     my %serverhomes = %Apache::lonnet::serverhomeIDs;      my %serverhomes = %Apache::lonnet::serverhomeIDs;
     my %servers = &Apache::lonnet::internet_dom_servers($dom);      my %servers = &Apache::lonnet::internet_dom_servers($dom);
     my %altids = &id_for_thisdom(%servers);      my %altids = &id_for_thisdom(%servers);
     my $itemcount = 1;  
     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);
Line 5698  sub print_usersessions { Line 8358  sub print_usersessions {
                                       $other_insts,$curroffloadnow,$curroffloadoth,$rowtotal);                                        $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) {          my %titles = &usersession_titles();
             $datatable .= '<tr'.$css_class.'><td colspan="2">'.          my ($prefix,@types);
                           &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.');          if ($position eq 'bottom') {
               $prefix = 'remote';
               @types = ('version','excludedomain','includedomain');
         } else {          } else {
             my %lt = &usersession_titles();              $prefix = 'hosted';
             my $numinrow = 5;              @types = ('excludedomain','includedomain');
             my $prefix;          }
             my @types;          ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles);
             if ($position eq 'bottom') {      }
                 $prefix = 'remote';      $$rowtotal += $itemcount;
                 @types = ('version','excludedomain','includedomain');      return $datatable;
             } else {  }
                 $prefix = 'hosted';  
                 @types = ('excludedomain','includedomain');  sub rules_by_location {
             }      my ($settings,$prefix,$by_location,$by_ip,$types,$titles) = @_; 
             my (%current,%checkedon,%checkedoff);      my ($datatable,$itemcount,$css_class);
             my @lcversions = &Apache::lonnet::all_loncaparevs();      if (keys(%{$by_location}) == 0) {
             my @locations = sort(keys(%by_location));          $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
             foreach my $type (@types) {          $datatable = '<tr'.$css_class.'><td colspan="2">'.
                 $checkedon{$type} = '';                       &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.').
                 $checkedoff{$type} = ' checked="checked"';                       '</td></tr>';
             }          $itemcount = 1;
             if (ref($settings) eq 'HASH') {      } else {
                 if (ref($settings->{$prefix}) eq 'HASH') {          $itemcount = 0;
                     foreach my $key (keys(%{$settings->{$prefix}})) {          my $numinrow = 5;
                         $current{$key} = $settings->{$prefix}{$key};          my (%current,%checkedon,%checkedoff);
                         if ($key eq 'version') {          my @locations = sort(keys(%{$by_location}));
                             if ($current{$key} ne '') {          foreach my $type (@{$types}) {
                                 $checkedon{$key} = ' checked="checked"';              $checkedon{$type} = '';
                                 $checkedoff{$key} = '';              $checkedoff{$type} = ' checked="checked"';
                             }          }
                         } elsif (ref($current{$key}) eq 'ARRAY') {          if (ref($settings) eq 'HASH') {
               if (ref($settings->{$prefix}) eq 'HASH') {
                   foreach my $key (keys(%{$settings->{$prefix}})) {
                       $current{$key} = $settings->{$prefix}{$key};
                       if ($key eq 'version') {
                           if ($current{$key} ne '') {
                             $checkedon{$key} = ' checked="checked"';                              $checkedon{$key} = ' checked="checked"';
                             $checkedoff{$key} = '';                              $checkedoff{$key} = '';
                         }                          }
                       } elsif (ref($current{$key}) eq 'ARRAY') {
                           $checkedon{$key} = ' checked="checked"';
                           $checkedoff{$key} = '';
                     }                      }
                 }                  }
             }              }
             foreach my $type (@types) {          }
                 next if ($type ne 'version' && !@locations);          foreach my $type (@{$types}) {
                 $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';              next if ($type ne 'version' && !@locations);
                 $datatable .= '<tr'.$css_class.'>              $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
                                <td><span class="LC_nobreak">'.$lt{$type}.'</span><br />              $datatable .= '<tr'.$css_class.'>
                                <span class="LC_nobreak">&nbsp;                             <td><span class="LC_nobreak">'.$titles->{$type}.'</span><br />
                                <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label>&nbsp;                             <span class="LC_nobreak">&nbsp;
                                <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>';                             <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label>&nbsp;
                 if ($type eq 'version') {                             <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>';
                     my $selector = '<select name="'.$prefix.'_version">';              if ($type eq 'version') {
                     foreach my $version (@lcversions) {                  my @lcversions = &Apache::lonnet::all_loncaparevs();
                         my $selected = '';                  my $selector = '<select name="'.$prefix.'_version">';
                         if ($current{'version'} eq $version) {                  foreach my $version (@lcversions) {
                             $selected = ' selected="selected"';                      my $selected = '';
                         }                      if ($current{'version'} eq $version) {
                         $selector .= ' <option value="'.$version.'"'.                          $selected = ' selected="selected"';
                                      $selected.'>'.$version.'</option>';                      }
                     }                      $selector .= ' <option value="'.$version.'"'.
                     $selector .= '</select> ';                                   $selected.'>'.$version.'</option>';
                     $datatable .= &mt('remote server must be version: [_1] or later',$selector);                  }
                 } else {                  $selector .= '</select> ';
                     $datatable.= '<div><input type="button" value="'.&mt('check all').'" '.                  $datatable .= &mt('remote server must be version: [_1] or later',$selector);
                                  'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.              } else {
                                  ' />'.('&nbsp;'x2).                  $datatable.= '<div><input type="button" value="'.&mt('check all').'" '.
                                  '<input type="button" value="'.&mt('uncheck all').'" '.                               'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.
                                  'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.                               ' />'.('&nbsp;'x2).
                                  "\n".                               '<input type="button" value="'.&mt('uncheck all').'" '.
                                  '</div><div><table>';                               'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.
                     my $rem;                               "\n".
                     for (my $i=0; $i<@locations; $i++) {                               '</div><div><table>';
                         my ($showloc,$value,$checkedtype);                  my $rem;
                         if (ref($by_location{$locations[$i]}) eq 'ARRAY') {                  for (my $i=0; $i<@locations; $i++) {
                             my $ip = $by_location{$locations[$i]}->[0];                      my ($showloc,$value,$checkedtype);
                             if (ref($by_ip{$ip}) eq 'ARRAY') {                      if (ref($by_location->{$locations[$i]}) eq 'ARRAY') {
                                  $value = join(':',@{$by_ip{$ip}});                          my $ip = $by_location->{$locations[$i]}->[0];
                                 $showloc = join(', ',@{$by_ip{$ip}});                          if (ref($by_ip->{$ip}) eq 'ARRAY') {
                                 if (ref($current{$type}) eq 'ARRAY') {                              $value = join(':',@{$by_ip->{$ip}});
                                     foreach my $loc (@{$by_ip{$ip}}) {                                $showloc = join(', ',@{$by_ip->{$ip}});
                                         if (grep(/^\Q$loc\E$/,@{$current{$type}})) {                              if (ref($current{$type}) eq 'ARRAY') {
                                             $checkedtype = ' checked="checked"';                                  foreach my $loc (@{$by_ip->{$ip}}) {
                                             last;                                      if (grep(/^\Q$loc\E$/,@{$current{$type}})) {
                                         }                                          $checkedtype = ' checked="checked"';
                                           last;
                                     }                                      }
                                 }                                  }
                             }                              }
                         }                          }
                         $rem = $i%($numinrow);  
                         if ($rem == 0) {  
                             if ($i > 0) {  
                                 $datatable .= '</tr>';  
                             }  
                             $datatable .= '<tr>';  
                         }  
                         $datatable .= '<td class="LC_left_item">'.  
                                       '<span class="LC_nobreak"><label>'.  
                                       '<input type="checkbox" name="'.$prefix.'_'.$type.  
                                       '" value="'.$value.'"'.$checkedtype.' />'.$showloc.  
                                       '</label></span></td>';  
                     }                      }
                     $rem = @locations%($numinrow);                      $rem = $i%($numinrow);
                     my $colsleft = $numinrow - $rem;                      if ($rem == 0) {
                     if ($colsleft > 1 ) {                          if ($i > 0) {
                         $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.                              $datatable .= '</tr>';
                                       '&nbsp;</td>';                          }
                     } elsif ($colsleft == 1) {                          $datatable .= '<tr>';
                         $datatable .= '<td class="LC_left_item">&nbsp;</td>';                      }
                       $datatable .= '<td class="LC_left_item">'.
                                     '<span class="LC_nobreak"><label>'.
                                     '<input type="checkbox" name="'.$prefix.'_'.$type.
                                     '" value="'.$value.'"'.$checkedtype.' />'.$showloc.
                                     '</label></span></td>';
                   }
                   $rem = @locations%($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 .= '</tr></table>';
               }
               $datatable .= '</td></tr>';
               $itemcount ++;
           }
       }
       return ($datatable,$itemcount);
   }
   
   sub print_ssl {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my ($css_class,$datatable);
       my $itemcount = 1;
       if ($position eq 'top') {
           my $primary_id = &Apache::lonnet::domain($dom,'primary');
           my $intdom = &Apache::lonnet::internet_dom($primary_id);
           my $same_institution;
           if ($intdom ne '') {
               my $internet_names = &Apache::lonnet::get_internet_names($Apache::lonnet::perlvar{'lonHostID'});
               if (ref($internet_names) eq 'ARRAY') {
                   if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
                       $same_institution = 1;
                   }
               }
           }
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable = '<tr'.$css_class.'><td colspan="2">';
           if ($same_institution) {
               my %domservers = &Apache::lonnet::get_servers($dom);
               $datatable .= &LONCAPA::SSL::print_certstatus(\%domservers,'web','domprefs');
           } else {
               $datatable .= &mt("You need to be logged into one of your own domain's servers to display information about the status of LON-CAPA SSL certificates.");
           }
           $datatable .= '</td></tr>';
           $itemcount ++;
       } else {
           my %titles = &ssl_titles();
           my (%by_ip,%by_location,@intdoms,@instdoms);
           &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
           my @alldoms = &Apache::lonnet::all_domains();
           my %serverhomes = %Apache::lonnet::serverhomeIDs;
           my @domservers = &Apache::lonnet::get_servers($dom);
           my %servers = &Apache::lonnet::internet_dom_servers($dom);
           my %altids = &id_for_thisdom(%servers);
           if (($position eq 'connto') || ($position eq 'connfrom')) {
               my $legacy;
               unless (ref($settings) eq 'HASH') {
                   my $name;
                   if ($position eq 'connto') {
                       $name = 'loncAllowInsecure';
                   } else {
                       $name = 'londAllowInsecure';
                   }
                   my $primarylibserv = &Apache::lonnet::domain($dom,'primary');
                   my @ids=&Apache::lonnet::current_machine_ids();
                   if (($primarylibserv ne '') && (!grep(/^\Q$primarylibserv\E$/,@ids))) {
                       my %what = (
                                      $name => 1,
                                  );
                       my ($result,$returnhash) =
                           &Apache::lonnet::get_remote_globals($primarylibserv,\%what);
                       if ($result eq 'ok') {
                           if (ref($returnhash) eq 'HASH') {
                               $legacy = $returnhash->{$name};
                           }
                       }
                   } else {
                       $legacy = $Apache::lonnet::perlvar{$name};
                   }
               }
               foreach my $type ('dom','intdom','other') {
                   my %checked;
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   $datatable .= '<tr'.$css_class.'><td>'.$titles{$type}.'</td>'.
                                 '<td class="LC_right_item">';
                   my $skip; 
                   if ($type eq 'dom') {
                       unless (keys(%servers) > 1) {
                           $datatable .= &mt('Nothing to set here, as there are no other servers/VMs');    
                           $skip = 1;
                       }
                   }
                   if ($type eq 'intdom') {
                       unless (@instdoms > 1) {
                           $datatable .= &mt('Nothing to set here, as there are no other domains for this institution');
                           $skip = 1;
                       } 
                   } elsif ($type eq 'other') {
                       if (keys(%by_location) == 0) {
                           $datatable .= &mt('Nothing to set here, as there are no other institutions');
                           $skip = 1;
                       }
                   }
                   unless ($skip) {
                       $checked{'yes'} = ' checked="checked"'; 
                       if (ref($settings) eq 'HASH') {
                           if (ref($settings->{$position}) eq 'HASH') {
                               if ($settings->{$position}->{$type} =~ /^(no|req)$/) {
                                   $checked{$1} = $checked{'yes'};
                                   delete($checked{'yes'}); 
                               }
                           }
                       } else {
                           if ($legacy == 0) {
                               $checked{'req'} = $checked{'yes'};
                               delete($checked{'yes'});    
                           }
                       }
                       foreach my $option ('no','yes','req') {
                           $datatable .= '<span class="LC_nobreak"><label>'.
                                         '<input type="radio" name="'.$position.'_'.$type.'" '.
                                         'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}.
                                         '</label></span>'.('&nbsp;'x2);
                     }                      }
                     $datatable .= '</tr></table>';  
                 }                  }
                 $datatable .= '</td></tr>';                  $datatable .= '</td></tr>';
                   $itemcount ++; 
               }
           } else {
               my $prefix = 'replication';
               my @types = ('certreq','nocertreq');
               if (keys(%by_location) == 0) {
                   $datatable .= '<tr'.$css_class.'><td>'.
                                 &mt('Nothing to set here, as there are no other institutions').
                                 '</td></tr>';
                 $itemcount ++;                  $itemcount ++;
               } else {
                   ($datatable,$itemcount) = 
                       &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles);
             }              }
         }          }
     }      }
Line 5817  sub print_usersessions { Line 8615  sub print_usersessions {
     return $datatable;      return $datatable;
 }  }
   
   sub ssl_titles {
       return &Apache::lonlocal::texthash (
                  dom           => 'LON-CAPA servers/VMs from same domain',
                  intdom        => 'LON-CAPA servers/VMs from same "internet" domain',
                  other         => 'External LON-CAPA servers/VMs',
                  connto        => 'Connections to other servers',
                  connfrom      => 'Connections from other servers',
                  replication   => 'Replicating content to other institutions',
                  certreq       => 'Client certificate required, but specific domains exempt',
                  nocertreq     => 'No client certificate required, except for specific domains',
                  no            => 'SSL not used',
                  yes           => 'SSL Optional (used if available)',
                  req           => 'SSL Required',
       );
   }
   
   sub print_trust {
       my ($prefix,$dom,$settings,$rowtotal) = @_;
       my ($css_class,$datatable,%checked,%choices);
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
       my $itemcount = 1;
       my %titles = &trust_titles();
       my @types = ('exc','inc');
       if ($prefix eq 'top') {
           $prefix = 'content';
       } elsif ($prefix eq 'bottom') {
           $prefix = 'msg';
       }
       ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles);
       $$rowtotal += $itemcount;
       return $datatable;
   }
   
   sub trust_titles {
       return &Apache::lonlocal::texthash(
                  content  => "Access to this domain's content by others",
                  shared   => "Access to other domain's content by this domain",
                  enroll   => "Enrollment in this domain's courses by others", 
                  othcoau  => "Co-author roles in this domain for others",
                  coaurem  => "Co-author roles for this domain's users elsewhere", 
                  domroles => "Domain roles in this domain assignable to others",
                  catalog  => "Course Catalog for this domain displayed elsewhere",
                  reqcrs   => "Requests for creation of courses in this domain by others",
                  msg      => "Users in other domains can send messages to this domain",
                  exc      => "Allow all, but exclude specific domains",
                  inc      => "Deny all, but include specific domains",
              );
   } 
   
 sub build_location_hashes {  sub build_location_hashes {
     my ($intdoms,$by_ip,$by_location) = @_;      my ($intdoms,$by_ip,$by_location,$instdoms) = @_;
     return unless((ref($intdoms) eq 'ARRAY') && (ref($by_ip) eq 'HASH') &&      return unless((ref($intdoms) eq 'ARRAY') && (ref($by_ip) eq 'HASH') &&
                   (ref($by_location) eq 'HASH'));                     (ref($by_location) eq 'HASH') && (ref($instdoms) eq 'ARRAY'));
     my %iphost = &Apache::lonnet::get_iphost();      my %iphost = &Apache::lonnet::get_iphost();
     my $primary_id = &Apache::lonnet::domain($env{'request.role.domain'},'primary');      my $primary_id = &Apache::lonnet::domain($env{'request.role.domain'},'primary');
     my $primary_ip = &Apache::lonnet::get_host_ip($primary_id);      my $primary_ip = &Apache::lonnet::get_host_ip($primary_id);
Line 5837  sub build_location_hashes { Line 8685  sub build_location_hashes {
             foreach my $id (@{$iphost{$ip}}) {              foreach my $id (@{$iphost{$ip}}) {
                 my $location = &Apache::lonnet::internet_dom($id);                  my $location = &Apache::lonnet::internet_dom($id);
                 if ($location) {                  if ($location) {
                     next if (grep(/^\Q$location\E$/,@{$intdoms}));                      if (grep(/^\Q$location\E$/,@{$intdoms})) {
                           my $dom = &Apache::lonnet::host_domain($id);
                           unless (grep(/^\Q$dom\E/,@{$instdoms})) {
                               push(@{$instdoms},$dom);
                           }
                           next;
                       }
                     if (ref($by_ip->{$ip}) eq 'ARRAY') {                      if (ref($by_ip->{$ip}) eq 'ARRAY') {
                         unless(grep(/^\Q$location\E$/,@{$by_ip->{$ip}})) {                          unless(grep(/^\Q$location\E$/,@{$by_ip->{$ip}})) {
                             push(@{$by_ip->{$ip}},$location);                              push(@{$by_ip->{$ip}},$location);
Line 6157  sub print_loadbalancing { Line 9011  sub print_loadbalancing {
         my $disabled_div_style = 'display: block';          my $disabled_div_style = 'display: block';
         my $homedom_div_style = 'display: none';          my $homedom_div_style = 'display: none';
         $datatable .= '<tr class="'.$css_class[$cssidx].'">'.          $datatable .= '<tr class="'.$css_class[$cssidx].'">'.
                       '<td rowspan="'.$rownum.'" valign="top">'.                        '<td rowspan="'.$rownum.'" style="vertical-align: top">'.
                       '<p>';                        '<p>';
         if ($lonhost eq '') {          if ($lonhost eq '') {
             $datatable .= '<span class="LC_nobreak">';              $datatable .= '<span class="LC_nobreak">';
Line 6190  sub print_loadbalancing { Line 9044  sub print_loadbalancing {
                 $homedom_div_style = 'display: block';                  $homedom_div_style = 'display: block';
             }              }
         }          }
         $datatable .= '</p></td><td rowspan="'.$rownum.'" valign="top">'.          $datatable .= '</p></td><td rowspan="'.$rownum.'" style="vertical-align: top">'.
                   '<div id="loadbalancing_disabled_'.$balnum.'" style="'.                    '<div id="loadbalancing_disabled_'.$balnum.'" style="'.
                   $disabled_div_style.'">'.$disabledtext.'</div>'."\n".                    $disabled_div_style.'">'.$disabledtext.'</div>'."\n".
                   '<div id="loadbalancing_targets_'.$balnum.'" style="'.$targets_div_style.'">'.&mt('Offloads to:').'<br />';                    '<div id="loadbalancing_targets_'.$balnum.'" style="'.$targets_div_style.'">'.&mt('Offloads to:').'<br />';
Line 6419  sub loadbalance_rule_row { Line 9273  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 style="vertical-align: top">'.$space.
         '<div id="balanceruletitle_'.$balnum.'_'.$type.'" style="'.$style.'">'.$title.'</div></td>'."\n".          '<div id="balanceruletitle_'.$balnum.'_'.$type.'" style="'.$style.'">'.$title.'</div></td>'."\n".
         '<td valaign="top">'.$space.          '<td valaign="top">'.$space.
         '<div id="balancerule_'.$balnum.'_'.$type.'" style="'.$style.'">'."\n";          '<div id="balancerule_'.$balnum.'_'.$type.'" style="'.$style.'">'."\n";
Line 6558  sub tool_titles { Line 9412  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',
                      textbook   => 'Textbook courses',                       textbook   => 'Textbook courses',
                        placement  => 'Placement tests',
                  );                   );
     return %titles;      return %titles;
 }  }
Line 6572  sub courserequest_titles { Line 9428  sub courserequest_titles {
                                    unofficial => 'Unofficial',                                     unofficial => 'Unofficial',
                                    community  => 'Communities',                                     community  => 'Communities',
                                    textbook   => 'Textbook',                                     textbook   => 'Textbook',
                                      placement  => 'Placement tests',
                                      lti        => 'LTI Provider',
                                    norequest  => 'Not allowed',                                     norequest  => 'Not allowed',
                                    approval   => 'Approval by Dom. Coord.',                                     approval   => 'Approval by DC',
                                    validate   => 'With validation',                                     validate   => 'With validation',
                                    autolimit  => 'Numerical limit',                                     autolimit  => 'Numerical limit',
                                    unlimited  => '(blank for unlimited)',                                     unlimited  => '(blank for unlimited)',
Line 6662  sub print_usercreation { Line 9520  sub print_usercreation {
             }              }
             $datatable .= '<tr'.$css_class.'>'.              $datatable .= '<tr'.$css_class.'>'.
                          '<td><span class="LC_nobreak">'.$lt{$item}.                           '<td><span class="LC_nobreak">'.$lt{$item}.
                          '</span></td><td align="right">';                           '</span></td><td style="text-align: right">';
             my @options = ('any');              my @options = ('any');
             if (ref($rules) eq 'HASH') {              if (ref($rules) eq 'HASH') {
                 if (keys(%{$rules}) > 0) {                  if (keys(%{$rules}) > 0) {
Line 6685  sub print_usercreation { Line 9543  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 7105  sub noninst_users { Line 9963  sub noninst_users {
     }      }
     $output .= '</tr><tr>';      $output .= '</tr><tr>';
     foreach my $item ('approve','email','username') {      foreach my $item ('approve','email','username') {
         $output .= '<td valign="top">';          $output .= '<td style="vertical-align: top">';
         my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom);          my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom);
         if ($item eq 'approve') {          if ($item eq 'approve') {
             %choices = &Apache::lonlocal::texthash (              %choices = &Apache::lonlocal::texthash (
Line 7247  sub noninst_users { Line 10105  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 7336  sub user_formats_row { Line 10194  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 7391  sub user_formats_row { Line 10249  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 7429  sub authtype_names { Line 10287  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 7497  sub print_defaults { Line 10356  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 7523  sub print_defaults { Line 10383  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 7558  sub print_defaults { Line 10434  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" colspan="2"><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 7577  sub print_defaults { Line 10453  sub print_defaults {
                 $datatable .= '</select>&nbsp;'.&mt('Internal ID:').                  $datatable .= '</select>&nbsp;'.&mt('Internal ID:').
                               '<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" colspan="2"><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 7609  sub defaults_titles { Line 10501  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 7794  sub print_scantronformat { Line 10688  sub print_scantronformat {
         }          }
         $datatable .= '</span></td>';          $datatable .= '</span></td>';
         if (keys(%error) == 0) {           if (keys(%error) == 0) { 
             $datatable .= '<td valign="bottom">';              $datatable .= '<td style="vertical-align: bottom">';
             if (!$switchserver) {              if (!$switchserver) {
                 $datatable .= &mt('Upload:').'<br />';                  $datatable .= &mt('Upload:').'<br />';
             }              }
Line 7841  sub legacy_scantronformat { Line 10735  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 8029  sub print_coursecategories { Line 10926  sub print_coursecategories {
         my $toggle_catscomm_dom = ' checked="checked" ';          my $toggle_catscomm_dom = ' checked="checked" ';
         my $can_catcomm_comm = ' ';          my $can_catcomm_comm = ' ';
         my $can_catcomm_dom = ' checked="checked" ';          my $can_catcomm_dom = ' checked="checked" ';
           my $toggle_catsplace_place = ' ';
           my $toggle_catsplace_dom = ' checked="checked" ';
           my $can_catplace_place = ' ';
           my $can_catplace_dom = ' checked="checked" ';
   
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ($settings->{'togglecats'} eq 'crs') {              if ($settings->{'togglecats'} eq 'crs') {
Line 8047  sub print_coursecategories { Line 10948  sub print_coursecategories {
                 $can_catcomm_comm = $can_catcomm_dom;                  $can_catcomm_comm = $can_catcomm_dom;
                 $can_catcomm_dom = ' ';                  $can_catcomm_dom = ' ';
             }              }
               if ($settings->{'togglecatsplace'} eq 'place') {
                   $toggle_catsplace_place = $toggle_catsplace_dom;
                   $toggle_catsplace_dom = ' ';
               }
               if ($settings->{'categorizeplace'} eq 'place') {
                   $can_catplace_place = $can_catplace_dom;
                   $can_catplace_dom = ' ';
               }
         }          }
         my %title = &Apache::lonlocal::texthash (          my %title = &Apache::lonlocal::texthash (
                      togglecats     => 'Show/Hide a course in catalog',                       togglecats      => 'Show/Hide a course in catalog',
                      togglecatscomm => 'Show/Hide a community in catalog',                       togglecatscomm  => 'Show/Hide a community in catalog',
                      categorize     => 'Assign a category to a course',                       togglecatsplace => 'Show/Hide a placement test in catalog',
                      categorizecomm => 'Assign a category to a community',                       categorize      => 'Assign a category to a course',
                        categorizecomm  => 'Assign a category to a community',
                        categorizeplace => 'Assign a category to a placement test',
                     );                      );
         my %level = &Apache::lonlocal::texthash (          my %level = &Apache::lonlocal::texthash (
                      dom  => 'Set in Domain',                       dom   => 'Set in Domain',
                      crs  => 'Set in Course',                       crs   => 'Set in Course',
                      comm => 'Set in Community',                       comm  => 'Set in Community',
                        place => 'Set in Placement Test',
                     );                      );
         $datatable = '<tr class="LC_odd_row">'.          $datatable = '<tr class="LC_odd_row">'.
                   '<td>'.$title{'togglecats'}.'</td>'.                    '<td>'.$title{'togglecats'}.'</td>'.
Line 8087  sub print_coursecategories { Line 10999  sub print_coursecategories {
                   $can_catcomm_dom.' value="dom" />'.$level{'dom'}.'</label>&nbsp;'.                    $can_catcomm_dom.' value="dom" />'.$level{'dom'}.'</label>&nbsp;'.
                   '<label><input type="radio" name="categorizecomm"'.                    '<label><input type="radio" name="categorizecomm"'.
                   $can_catcomm_comm.'value="comm" />'.$level{'comm'}.'</label></span></td>'.                    $can_catcomm_comm.'value="comm" />'.$level{'comm'}.'</label></span></td>'.
                     '</tr><tr class="LC_odd_row">'.
                     '<td>'.$title{'togglecatsplace'}.'</td>'.
                     '<td class="LC_right_item"><span class="LC_nobreak"><label>'.
                     '<input type="radio" name="togglecatsplace"'.
                     $toggle_catsplace_dom.' value="dom" />'.$level{'dom'}.'</label>&nbsp;'.
                     '<label><input type="radio" name="togglecatscomm"'.
                     $toggle_catsplace_place.' value="comm" />'.$level{'place'}.'</label></span></td>'.
                     '</tr><tr>'.
                     '<td>'.$title{'categorizeplace'}.'</td>'.
                     '<td class="LC_right_item"><span class="LC_nobreak">'.
                     '<label><input type="radio" name="categorizeplace"'.
                     $can_catplace_dom.' value="dom" />'.$level{'dom'}.'</label>&nbsp;'.
                     '<label><input type="radio" name="categorizeplace"'.
                     $can_catplace_place.'value="place" />'.$level{'place'}.'</label></span></td>'.
                   '</tr>';                    '</tr>';
         $$rowtotal += 4;          $$rowtotal += 6;
     } else {      } else {
         my $css_class;          my $css_class;
         my $itemcount = 1;          my $itemcount = 1;
Line 8113  sub print_coursecategories { Line 11039  sub print_coursecategories {
                     my %default_names = (                      my %default_names = (
                           instcode    => &mt('Official courses'),                            instcode    => &mt('Official courses'),
                           communities => &mt('Communities'),                            communities => &mt('Communities'),
                             placement   => &mt('Placement Tests'),
                     );                      );
   
                     if ((!grep(/^instcode$/,@{$cats[0]})) ||                       if ((!grep(/^instcode$/,@{$cats[0]})) || 
                         ($cathash->{'instcode::0'} eq '') ||                          ($cathash->{'instcode::0'} eq '') ||
                         (!grep(/^communities$/,@{$cats[0]})) ||                           (!grep(/^communities$/,@{$cats[0]})) || 
                         ($cathash->{'communities::0'} eq '')) {                          ($cathash->{'communities::0'} eq '') ||
                           (!grep(/^placement$/,@{$cats[0]})) ||
                           ($cathash->{'placement::0'} eq '')) {
                         $maxnum ++;                          $maxnum ++;
                     }                      }
                     my $lastidx;                      my $lastidx;
Line 8139  sub print_coursecategories { Line 11068  sub print_coursecategories {
                             $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';                              $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
                         }                          }
                         $datatable .= '</select></span></td><td>';                          $datatable .= '</select></span></td><td>';
                         if ($parent eq 'instcode' || $parent eq 'communities') {                          if ($parent eq 'instcode' || $parent eq 'communities' || $parent eq 'placement') {
                             $datatable .=  '<span class="LC_nobreak">'                              $datatable .=  '<span class="LC_nobreak">'
                                            .$default_names{$parent}.'</span>';                                             .$default_names{$parent}.'</span>';
                             if ($parent eq 'instcode') {                              if ($parent eq 'instcode') {
Line 8162  sub print_coursecategories { Line 11091  sub print_coursecategories {
                             $datatable .= '<label><input type="radio" name="'                              $datatable .= '<label><input type="radio" name="'
                                           .$parent.'" value="0" />'                                            .$parent.'" value="0" />'
                                           .&mt('Do not display').'</label></span>';                                            .&mt('Do not display').'</label></span>';
                             if ($parent eq 'communities') {                              if (($parent eq 'communities') || ($parent eq 'placement')) {
                                 $datatable .= '</td></tr></table>';                                  $datatable .= '</td></tr></table>';
                             }                              }
                             $datatable .= '</td>';                              $datatable .= '</td>';
Line 8194  sub print_coursecategories { Line 11123  sub print_coursecategories {
                                   .'<input type="text" size="20" name="addcategory_name" value="" /></td>'                                    .'<input type="text" size="20" name="addcategory_name" value="" /></td>'
                                   .'</tr>'."\n";                                    .'</tr>'."\n";
                     $itemcount ++;                      $itemcount ++;
                     foreach my $default ('instcode','communities') {                      foreach my $default ('instcode','communities','placement') {
                         if ((!grep(/^\Q$default\E$/,@{$cats[0]})) || ($cathash->{$default.'::0'} eq '')) {                          if ((!grep(/^\Q$default\E$/,@{$cats[0]})) || ($cathash->{$default.'::0'} eq '')) {
                             $css_class = $itemcount%2?' class="LC_odd_row"':'';                              $css_class = $itemcount%2?' class="LC_odd_row"':'';
                             my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','$lastidx'".');"';                              my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','$lastidx'".');"';
Line 8283  sub print_serverstatuses { Line 11212  sub print_serverstatuses {
   
 sub serverstatus_pages {  sub serverstatus_pages {
     return ('userstatus','lonstatus','loncron','server-status','codeversions',      return ('userstatus','lonstatus','loncron','server-status','codeversions',
             'checksums','clusterstatus','metadata_keywords','metadata_harvest',              'checksums','clusterstatus','certstatus','metadata_keywords',
             'takeoffline','takeonline','showenv','toggledebug','ping','domconf',              'metadata_harvest','takeoffline','takeonline','showenv','toggledebug',
             'uniquecodes','diskusage','coursecatalog');              'ping','domconf','uniquecodes','diskusage','coursecatalog');
 }  }
   
 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 8344  $jstext Line 11290  $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).',
               passexp => 'Warning: days before password expiration 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 8382  function warnIntAuth(field) { Line 11350  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 8406  function warnIntPass(field) { Line 11380  function warnIntPass(field) {
             field.value = '';              field.value = '';
         }          }
         if (field.value != '') {          if (field.value != '') {
             if (field.name == 'passwords_expire') {              if (field.name == '${prefix}_expire') {
                 var regexpposnum=/^\\d+(|\\.\\d*)\$/;                  var regexpposnum=/^\\d+(|\\.\\d*)\$/; 
                 if (!regexpposnum.test(field.value)) {                  if (!regexpposnum.test(field.value)) {
                     alert('$intalert{passexp}');                      alert('$intalert{passexp}');
                     field.value = '';                      field.value = '';
Line 8420  function warnIntPass(field) { Line 11394  function warnIntPass(field) {
                 }                  }
             } else {              } else {
                 if (!regexdigit.test(field.value)) {                  if (!regexdigit.test(field.value)) {
                     if (field.name == 'passwords_max') {                      if (field.name == '${prefix}_max') {
                         alert('$intalert{passmax}');                          alert('$intalert{passmax}');
                     } else {                      } else {
                         if (field.name == 'passwords_numsaved') {                          if (field.name == '${prefix}_numsaved') {
                             alert('$intalert{passnum}');                              alert('$intalert{passnum}');
                         }                          }
                     }                      }
Line 8463  sub coursecategories_javascript { Line 11437  sub coursecategories_javascript {
     }      }
     my $instcode_reserved = &mt('The name: [_1] is a reserved category.','"instcode"');      my $instcode_reserved = &mt('The name: [_1] is a reserved category.','"instcode"');
     my $communities_reserved = &mt('The name: [_1] is a reserved category.','"communities"');      my $communities_reserved = &mt('The name: [_1] is a reserved category.','"communities"');
       my $placement_reserved = &mt('The name: [_1] is a reserved category.','"placement"');
     my $choose_again = "\n".&mt('Please use a different name for the new top level category.');       my $choose_again = "\n".&mt('Please use a different name for the new top level category.'); 
     &js_escape(\$instcode_reserved);      &js_escape(\$instcode_reserved);
     &js_escape(\$communities_reserved);      &js_escape(\$communities_reserved);
       &js_escape(\$placement_reserved);
     &js_escape(\$choose_again);      &js_escape(\$choose_again);
     $output = <<"ENDSCRIPT";      $output = <<"ENDSCRIPT";
 <script type="text/javascript">  <script type="text/javascript">
Line 8535  function categoryCheck(form) { Line 11511  function categoryCheck(form) {
         alert('$communities_reserved\\n$choose_again');          alert('$communities_reserved\\n$choose_again');
         return false;          return false;
     }      }
       if (form.elements['addcategory_name'].value == 'placement') {
           alert('$placement_reserved\\n$choose_again');
           return false;
       }
     return true;      return true;
 }  }
   
Line 8551  sub initialize_categories { Line 11531  sub initialize_categories {
     my %default_names = &Apache::lonlocal::texthash (      my %default_names = &Apache::lonlocal::texthash (
                       instcode    => 'Official courses (with institutional codes)',                        instcode    => 'Official courses (with institutional codes)',
                       communities => 'Communities',                        communities => 'Communities',
                         placement   => 'Placement Tests',
                         );                          );
     my $select0 = ' selected="selected"';      my %selnum = (
     my $select1 = '';                     instcode    => '0',
     foreach my $default ('instcode','communities') {                     communities => '1',
                      placement   => '2',
                    );
       my %selected;
       foreach my $default ('instcode','communities','placement') {
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $css_class = $itemcount%2?' class="LC_odd_row"':'';
         $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','0'".');"';          $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','0'".');"';
         if ($default eq 'communities') {          map { $selected{$selnum{$_}} = '' } keys(%selnum);
             $select1 = $select0;          $selected{$selnum{$default}} = ' selected="selected"';
             $select0 = '';  
         }  
         $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'          $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                      .'<select name="'.$default.'_pos">'                       .'<select name="'.$default.'_pos"'.$chgstr.'>'
                      .'<option value="0"'.$select0.'>1</option>'                       .'<option value="0"'.$selected{'0'}.'>1</option>'
                      .'<option value="1"'.$select1.'>2</option>'                       .'<option value="1"'.$selected{'1'}.'>2</option>'
                      .'<option value="2">3</option></select>&nbsp;'                       .'<option value="2"'.$selected{'2'}.'>3</option>'
                        .'<option value="3">4</option></select>&nbsp;'
                      .$default_names{$default}                       .$default_names{$default}
                      .'</span></td><td><span class="LC_nobreak">'                       .'</span></td><td><span class="LC_nobreak">'
                      .'<label><input type="radio" name="'.$default.'" value="1" checked="checked" />'                       .'<label><input type="radio" name="'.$default.'" value="1" checked="checked" />'
Line 8580  sub initialize_categories { Line 11564  sub initialize_categories {
                   .'<select name="addcategory_pos"'.$chgstr.'>'                    .'<select name="addcategory_pos"'.$chgstr.'>'
                   .'<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">3</option>'
                     .'<option value="3" selected="selected">4</option></select>&nbsp;'
                   .&mt('Add category').'</span></td><td><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>';
Line 8671  sub build_category_rows { Line 11656  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 8692  sub modifiable_userdata_row { Line 11677  sub modifiable_userdata_row {
         } else {          } else {
             $rolename = $role;              $rolename = $role;
         }          }
       } elsif ($context eq 'lti') {
           $rolename = &mt('Institutional data used (if available)');
       } elsif ($context eq 'privacy') {
           $rolename = $itemdesc;
     } else {      } else {
         if ($role eq 'cr') {          if ($role eq 'cr') {
             $rolename = &mt('Custom role');              $rolename = &mt('Custom role');
Line 8729  sub modifiable_userdata_row { Line 11718  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 ($context eq 'privacy') {
               my ($key,$inner) = split(/_/,$role);
               if (ref($settings) eq 'HASH') {
                   if (ref($settings->{$key}) eq 'HASH') {
                       $hashref = $settings->{$key}->{$inner};
                   }
               }
           } 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 8778  sub modifiable_userdata_row { Line 11773  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 eq 'privacy') {
                   if ($role =~ /^priv_(domain|course)$/) {
                       if (ref($settings) ne 'HASH') {
                           $check = ' checked="checked" ';
                       }
                   } elsif ($role =~ /^priv_(author|community)$/) {
                       if (ref($settings) ne 'HASH') {
                           unless ($fields[$i] eq 'id') {
                               $check = ' checked="checked" ';
                           }
                       }
                   } elsif ($role =~ /^(unpriv|othdom)_/) {
                       if (ref($settings) ne 'HASH') {
                           if (($fields[$i] eq 'lastname') || ($fields[$i] eq 'firstname')) {
                               $check = ' checked="checked" ';
                           }
                       }
                   }
               } 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 8788  sub modifiable_userdata_row { Line 11801  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 8798  sub modifiable_userdata_row { Line 11812  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';
               } elsif ($context eq 'privacy') {
                   $prefix = 'privacy';
               }
             $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 8891  sub insttypes_row { Line 11910  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 9007  sub modify_login { Line 12026  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,%samlnotsso);          %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',
Line 9031  sub modify_login { Line 12050  sub modify_login {
                     $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'};                      $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'};
                     $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'};                      $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'};
                     $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'};                      $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'};
                       $samlwindow{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'window'};
                     $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'};                      $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'};
                 }                  }
             }              }
Line 9173  sub modify_login { Line 12193  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 9192  sub modify_login { Line 12215  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 9249  sub modify_login { Line 12273  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 9270  sub modify_login { Line 12297  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 9288  sub modify_login { Line 12316  sub modify_login {
             if ($env{'form.saml_img_'.$lonhost.'.filename'}) {              if ($env{'form.saml_img_'.$lonhost.'.filename'}) {
                 push(@newsamlimgs,$lonhost);                  push(@newsamlimgs,$lonhost);
             }              }
             foreach my $item ('text','alt','url','title','notsso') {              foreach my $item ('text','alt','url','title','window','notsso') {
                 $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g;                  $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g;
             }              }
             if ($saml{$lonhost}) {              if ($saml{$lonhost}) {
                   if ($env{'form.saml_window_'.$lonhost} ne '1') {
                       $env{'form.saml_window_'.$lonhost} = '';
                   }
                 if (grep(/^\Q$lonhost\E$/,@delsamlimg)) {                  if (grep(/^\Q$lonhost\E$/,@delsamlimg)) {
 #FIXME Need to obsolete published image  #FIXME Need to obsolete published image
                     delete($currsaml{$lonhost}{'img'});                      delete($currsaml{$lonhost}{'img'});
Line 9309  sub modify_login { Line 12340  sub modify_login {
                 if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) {                  if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) {
                     $changes{'saml'}{$lonhost} = 1;                      $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}) {                  if ($env{'form.saml_notsso_'.$lonhost} ne $samlnotsso{$lonhost}) {
                     $changes{'saml'}{$lonhost} = 1;                      $changes{'saml'}{$lonhost} = 1;
                 }                  }
             } else {              } else {
                 $changes{'saml'}{$lonhost} = 1;                  $changes{'saml'}{$lonhost} = 1;
             }              }
             foreach my $item ('text','alt','url','title','notsso') {              foreach my $item ('text','alt','url','title','window','notsso') {
                 $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost};                  $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost};
             }              }
         } else {          } else {
Line 9338  sub modify_login { Line 12372  sub modify_login {
             if ($switchserver) {              if ($switchserver) {
                 $error = &mt("Upload of SSO Button Image is not permitted to this server: [_1].",$switchserver);                  $error = &mt("Upload of SSO Button Image is not permitted to this server: [_1].",$switchserver);
             } elsif ($author_ok eq 'ok') {              } elsif ($author_ok eq 'ok') {
                   my $modified = [];
                 foreach my $lonhost (@newsamlimgs) {                  foreach my $lonhost (@newsamlimgs) {
                     my $formelem = 'saml_img_'.$lonhost;                      my $formelem = 'saml_img_'.$lonhost;
                     my ($result,$imgurl) = &publishlogo($r,'upload',$formelem,$dom,$confname,                      my ($result,$imgurl) =
                                                         "login/saml/$lonhost",'','',                          &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname,
                                                         $env{'form.saml_img_'.$lonhost.'.filename'});                                                                  "login/saml/$lonhost",'','',
                                                                   $env{'form.saml_img_'.$lonhost.'.filename'},
                                                                   $modified);
                     if ($result eq 'ok') {                      if ($result eq 'ok') {
                         $currsaml{$lonhost}{'img'} = $imgurl;                          $currsaml{$lonhost}{'img'} = $imgurl;
                         $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl;                          $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl;
Line 9353  sub modify_login { Line 12390  sub modify_login {
                         $errors .= '<li><span class="LC_error">'.$puberror.'</span></li>';                          $errors .= '<li><span class="LC_error">'.$puberror.'</span></li>';
                     }                      }
                 }                  }
                   &update_modify_urls($r,$modified);
             } else {              } 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);                  $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);
             }              }
Line 9516  sub modify_login { Line 12554  sub modify_login {
                                        alt    => 'Alt text for button image',                                         alt    => 'Alt text for button image',
                                        url    => 'SSO URL',                                         url    => 'SSO URL',
                                        title  => 'Tooltip for SSO link',                                         title  => 'Tooltip for SSO link',
                                          window => 'Pop-up window if iframe',
                                        notsso => 'Text for non-SSO log-in',                                         notsso => 'Text for non-SSO log-in',
                                     );                                      );
                         foreach my $lonhost (sort(keys(%{$changes{$item}}))) {                          foreach my $lonhost (sort(keys(%{$changes{$item}}))) {
                             if (ref($currsaml{$lonhost}) eq 'HASH') {                              if (ref($currsaml{$lonhost}) eq 'HASH') {
                                 $resulttext .= '<li>'.&mt("$title{$item} in use for [_1]","<b>$lonhost</b>").                                  $resulttext .= '<li>'.&mt("$title{$item} in use for [_1]","<b>$lonhost</b>").
                                                '<ul>';                                                 '<ul>';
                                 foreach my $key ('text','img','alt','url','title','notsso') {                                  foreach my $key ('text','img','alt','url','title','window','notsso') {
                                     if ($currsaml{$lonhost}{$key} eq '') {                                      if ($currsaml{$lonhost}{$key} eq '') {
                                         $resulttext .= '<li>'.&mt("$notlt{$key} not in use").'</li>';                                          $resulttext .= '<li>'.&mt("$notlt{$key} not in use").'</li>';
                                     } else {                                      } else {
                                         my $value = "'$currsaml{$lonhost}{$key}'";                                          my $value = "'$currsaml{$lonhost}{$key}'";
                                         if ($key eq 'img') {                                          if ($key eq 'img') {
                                             $value = '<img src="'.$currsaml{$lonhost}{$key}.'" />';                                              $value = '<img src="'.$currsaml{$lonhost}{$key}.'" />';
                                           } elsif ($key eq 'window') {
                                               $value = 'On';
                                         }                                          }
                                         $resulttext .= '<li>'.&mt("$notlt{$key} set to: [_1]",                                          $resulttext .= '<li>'.&mt("$notlt{$key} set to: [_1]",
                                                                   $value).'</li>';                                                                    $value).'</li>';
Line 10026  sub modify_colors { Line 13067  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 10049  sub modify_colors { Line 13095  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 10076  sub modify_colors { Line 13125  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 10107  sub modify_colors { Line 13159  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 10226  sub default_change_checker { Line 13292  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 10264  sub display_colorchgs { Line 13335  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 {
                             my $newitem = $confhash->{$role}{$item};                              my $newitem = $confhash->{$role}{$item};
                             if ($key eq 'images') {                              if ($key eq 'images') {
                                 $newitem = '<img src="'.$confhash->{$role}{$item}.'" alt="'.$choices{$item}.'" valign="bottom" />';                                  $newitem = '<img src="'.$confhash->{$role}{$item}.'" alt="'.$choices{$item}.'" style="vertical-align: bottom" />';
                             }                              }
                             $resulttext .= '<li>'.&mt("$choices{$item} set to [_1]",$newitem).'</li>';                              $resulttext .= '<li>'.&mt("$choices{$item} set to [_1]",$newitem).'</li>';
                         }                          }
Line 10349  sub check_authorstatus { Line 13427  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 10621  sub subscribed_hosts { Line 13487  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 10646  sub modify_quotas { Line 13518  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','placement','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 10661  sub modify_quotas { Line 13533  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 10695  sub modify_quotas { Line 13567  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','placement','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 10817  sub modify_quotas { Line 13689  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 11352  sub process_textbook_image { Line 14224  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 11369  sub process_textbook_image { Line 14244  sub process_textbook_image {
     return ($url,$error);      return ($url,$error);
 }  }
   
   sub modify_ltitools {
       my ($r,$dom,$action,$lastactref,%domconfig) = @_;
       my (%currtoolsec,%secchanges,%newtoolsec,%newkeyset);
       &fetch_secrets($dom,'toolsec',\%domconfig,\%currtoolsec,\%secchanges,\%newtoolsec,\%newkeyset);
   
       my $confname = $dom.'-domainconfig';
       my $servadm = $r->dir_config('lonAdmEMail');
       my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
   
       my ($resulttext,$ltitoolsoutput,$is_home,$errors,%ltitoolschg,%newtoolsenc,%newltitools);
       my $toolserror =
           &Apache::courseprefs::process_ltitools($r,$dom,$confname,$domconfig{'ltitools'},\%ltitoolschg,'domain',
                                                  $lastactref,$configuserok,$switchserver,$author_ok);
   
       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; last; } }
       }
   
       if (keys(%ltitoolschg)) {
           foreach my $id (keys(%ltitoolschg)) {
               if (ref($ltitoolschg{$id}) eq 'HASH') {
                   foreach my $inner (keys(%{$ltitoolschg{$id}})) {
                       if (($inner eq 'secret') || ($inner eq 'key')) {
                           if ($is_home) {
                               $newtoolsenc{$id}{$inner} = $ltitoolschg{$id}{$inner};
                           }
                       }
                   }
               }
           }
           $ltitoolsoutput = &Apache::courseprefs::store_ltitools($dom,'','domain',\%ltitoolschg,$domconfig{'ltitools'});
           if (keys(%ltitoolschg)) {
               %newltitools = %ltitoolschg;
           }
       }
       if (ref($domconfig{'ltitools'}) eq 'HASH') {
           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 ($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 (ref($currsec->{'private'}) eq 'HASH') {
               if (ref($currsec->{'private'}{'keys'}) eq 'ARRAY') {
                   $newsec->{'private'}{'keys'} = $currsec->{'private'}{'keys'};
                   map { $keyset{$_} = 1; } @{$currsec->{'private'}{'keys'}};
               }
           }
       }
       my @items= ('crs','dom');
       if ($context eq 'ltisec') {
           push(@items,'consumers');
       }
       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};
                           }
                       }
                   } elsif ($env{$keyitem} ne '') {
                       unless (grep(/^\Q$hostid\E$/,@{$newsec->{'private'}{'keys'}})) {
                           push(@{$newsec->{'private'}{'keys'}},$hostid);
                       }
                       $secchanges->{'private'} = 1;
                       $newkeyset->{$hostid} = $env{$keyitem};
                   }
               }
           }
       }
   }
   
   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 {
                   %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'),
                                        },
                                );
   
               }
               my @types= ('crs','dom');
               if ($context eq 'lti') {
                   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});
                   }
               }
               foreach my $type (@types) {
                   my $shown = $encrypted{$type}{'off'};
                   if (ref($newsec->{$item}) eq 'HASH') {
                       if ($newsec->{$item}{$type}) {
                           if ($context eq 'lti') {
                               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'};
                       }
                   }
                   $output .= '<li>'.$shown.'</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>';
                   }
               }
               if (ref($newsec->{'rules'}{'chars'}) eq 'ARRAY') {
                   if (@{$newsec->{'rules'}{'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{$_} } @{$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;
           }
       }
       if ($needs_update) {
           my $cachetime = 24*60*60;
           &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
       }
       return $output;
   }
   
   sub modify_proctoring {
       my ($r,$dom,$action,$lastactref,%domconfig) = @_;
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my (@allpos,%changes,%confhash,%encconfhash,$errors,$resulttext,%imgdeletions);
       my $confname = $dom.'-domainconfig';
       my $servadm = $r->dir_config('lonAdmEMail');
       my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
       my %providernames = &proctoring_providernames();
       my $maxnum = scalar(keys(%providernames));
   
       my (%requserfields,%optuserfields,%defaults,%extended,%crsconf,@courseroles,@ltiroles);
       my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data();
       if (ref($requref) eq 'HASH') {
           %requserfields = %{$requref};
       }
       if (ref($opturef) eq 'HASH') {
           %optuserfields = %{$opturef};
       }
       if (ref($defref) eq 'HASH') {
           %defaults = %{$defref};
       }
       if (ref($extref) eq 'HASH') {
           %extended = %{$extref};
       }
       if (ref($crsref) eq 'HASH') {
           %crsconf = %{$crsref};
       }
       if (ref($rolesref) eq 'ARRAY') {
           @courseroles = @{$rolesref};
       }
       if (ref($ltiref) eq 'ARRAY') {
           @ltiroles = @{$ltiref};
       }
   
       if (ref($domconfig{$action}) eq 'HASH') {
           my @todeleteimages = &Apache::loncommon::get_env_multiple('form.proctoring_image_del');
           if (@todeleteimages) {
               map { $imgdeletions{$_} = 1; } @todeleteimages;
           }
       }
       my %customadds;
       my @newcustom = &Apache::loncommon::get_env_multiple('form.proctoring_customadd');
       if (@newcustom) {
           map { $customadds{$_} = 1; } @newcustom;
       }
       foreach my $provider (sort(keys(%providernames))) {
           $confhash{$provider} = {};
           my $pos = $env{'form.proctoring_pos_'.$provider};
           $pos =~ s/\D+//g;
           $allpos[$pos] = $provider;
           my (%current,%currentenc);
           my $showroles = 0;
           if (ref($domconfig{$action}) eq 'HASH') {
               if (ref($domconfig{$action}{$provider}) eq 'HASH') {
                   %current = %{$domconfig{$action}{$provider}};
                   foreach my $item ('key','secret') { 
                       $currentenc{$item} = $current{$item};
                       delete($current{$item});
                   }
               }
           }
           if ($env{'form.proctoring_available_'.$provider}) {
               $confhash{$provider}{'available'} = 1;
               unless ($current{'available'}) {
                   $changes{$provider} = 1;
               }
           } else {
               %{$confhash{$provider}} = %current;
               %{$encconfhash{$provider}} = %currentenc;
               $confhash{$provider}{'available'} = 0;
               if ($current{'available'}) {
                   $changes{$provider} = 1;
               }
           }
           if ($confhash{$provider}{'available'}) {
               foreach my $field ('lifetime','version','sigmethod','url','key','secret') {
                   my $possval = $env{'form.proctoring_'.$provider.'_'.$field};
                   if ($field eq 'lifetime') {
                       if ($possval =~ /^\d+$/) {
                           $confhash{$provider}{$field} = $possval;
                       }
                   } elsif ($field eq 'version') {
                       if ($possval =~ /^\d+\.\d+$/) {
                           $confhash{$provider}{$field} = $possval;
                       }
                   } elsif ($field eq 'sigmethod') {
                       if ($possval =~ /^\QHMAC-SHA\E(1|256)$/) {
                           $confhash{$provider}{$field} = $possval;
                       }
                   } elsif ($field eq 'url') {
                       $confhash{$provider}{$field} = $possval;
                   } elsif (($field eq 'key') || ($field eq 'secret')) {
                       $encconfhash{$provider}{$field} = $possval;
                       unless ($currentenc{$field} eq $possval) {
                           $changes{$provider} = 1;
                       }
                   }
                   unless (($field eq 'key') || ($field eq 'secret')) {
                       unless ($current{$field} eq $confhash{$provider}{$field}) {
                           $changes{$provider} = 1;
                       }
                   }
               }
               if ($imgdeletions{$provider}) {
                   $changes{$provider} = 1;
               } elsif ($env{'form.proctoring_image_'.$provider.'.filename'} ne '') {
                   my ($imageurl,$error) =
                       &process_proctoring_image($r,$dom,$confname,'proctoring_image_'.$provider,$provider,
                                                 $configuserok,$switchserver,$author_ok);
                   if ($imageurl) {
                       $confhash{$provider}{'image'} = $imageurl;
                       $changes{$provider} = 1; 
                   }
                   if ($error) {
                       &Apache::lonnet::logthis($error);
                       $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
                   }
               } elsif (exists($current{'image'})) {
                   $confhash{$provider}{'image'} = $current{'image'};
               }
               if (ref($requserfields{$provider}) eq 'ARRAY') {
                   if (@{$requserfields{$provider}} > 0) {
                       if (grep(/^user$/,@{$requserfields{$provider}})) {
                           if ($env{'form.proctoring_userincdom_'.$provider}) {
                               $confhash{$provider}{'incdom'} = 1;
                           }
                           unless ($current{'incdom'} eq $confhash{$provider}{'incdom'}) {
                               $changes{$provider} = 1;
                           }
                       }
                       if (grep(/^roles$/,@{$requserfields{$provider}})) {
                           $showroles = 1;
                       }
                   }
               }
               $confhash{$provider}{'fields'} = [];
               if (ref($optuserfields{$provider}) eq 'ARRAY') {
                   if (@{$optuserfields{$provider}} > 0) {
                       my @optfields = &Apache::loncommon::get_env_multiple('form.proctoring_optional_'.$provider);
                       foreach my $field (@{$optuserfields{$provider}}) {
                           if (grep(/^\Q$field\E$/,@optfields)) {
                               push(@{$confhash{$provider}{'fields'}},$field);
                           }
                       }
                   }
                   if (ref($current{'fields'}) eq 'ARRAY') {
                       unless ($changes{$provider}) {
                           my @new = sort(@{$confhash{$provider}{'fields'}});
                           my @old = sort(@{$current{'fields'}}); 
                           my @diffs = &Apache::loncommon::compare_arrays(\@new,\@old);
                           if (@diffs) {
                               $changes{$provider} = 1;
                           }
                       }
                   } elsif (@{$confhash{$provider}{'fields'}}) {
                       $changes{$provider} = 1;
                   }
               }
               if (ref($defaults{$provider}) eq 'ARRAY') {  
                   if (@{$defaults{$provider}} > 0) {
                       my %options;
                       if (ref($extended{$provider}) eq 'HASH') {
                           %options = %{$extended{$provider}};
                       }
                       my @checked = &Apache::loncommon::get_env_multiple('form.proctoring_defaults_'.$provider);
                       foreach my $field (@{$defaults{$provider}}) {
                           if ((exists($options{$field})) && (ref($options{$field}) eq 'ARRAY')) {
                               my $poss = $env{'form.proctoring_defaults_'.$field.'_'.$provider};
                               if (grep(/^\Q$poss\E$/,@{$options{$field}})) {
                                   push(@{$confhash{$provider}{'defaults'}},$poss); 
                               }
                           } elsif ((exists($options{$field})) && (ref($options{$field}) eq 'HASH')) {
                               foreach my $inner (keys(%{$options{$field}})) {
                                   if (ref($options{$field}{$inner}) eq 'ARRAY') {
                                       my $poss = $env{'form.proctoring_'.$inner.'_'.$provider};
                                       if (grep(/^\Q$poss\E$/,@{$options{$field}{$inner}})) {
                                           $confhash{$provider}{'defaults'}{$inner} = $poss;
                                       }
                                   } else {
                                       $confhash{$provider}{'defaults'}{$inner} = $env{'form.proctoring_'.$inner.'_'.$provider};
                                   }
                               }
                           } else {
                               if (grep(/^\Q$field\E$/,@checked)) {
                                   push(@{$confhash{$provider}{'defaults'}},$field);
                               }
                           }
                       }
                       if (ref($confhash{$provider}{'defaults'}) eq 'ARRAY') {
                           if (ref($current{'defaults'}) eq 'ARRAY') {
                               unless ($changes{$provider}) {
                                   my @new = sort(@{$confhash{$provider}{'defaults'}});
                                   my @old = sort(@{$current{'defaults'}});
                                   my @diffs = &Apache::loncommon::compare_arrays(\@new,\@old);
                                   if (@diffs) {
                                       $changes{$provider} = 1; 
                                   }
                               }
                           } elsif (ref($current{'defaults'}) eq 'ARRAY') {
                               if (@{$current{'defaults'}}) {
                                   $changes{$provider} = 1;
                               }
                           }
                       } elsif (ref($confhash{$provider}{'defaults'}) eq 'HASH') {
                           if (ref($current{'defaults'}) eq 'HASH') {
                               unless ($changes{$provider}) {
                                   foreach my $key (keys(%{$confhash{$provider}{'defaults'}})) {
                                       unless ($confhash{$provider}{'defaults'}{$key} eq $current{'defaults'}{$key}) {
                                           $changes{$provider} = 1;
                                           last;
                                       }
                                   }
                               }
                               unless ($changes{$provider}) {
                                   foreach my $key (keys(%{$current{'defaults'}})) {
                                       unless ($current{'defaults'}{$key} eq $confhash{$provider}{'defaults'}{$key}) {
                                           $changes{$provider} = 1;
                                           last;
                                       }
                                   }
                               }
                           } elsif (keys(%{$confhash{$provider}{'defaults'}})) {
                               $changes{$provider} = 1;
                           }
                       }
                   }
               }
               if (ref($crsconf{$provider}) eq 'ARRAY') {
                   if (@{$crsconf{$provider}} > 0) {
                       $confhash{$provider}{'crsconf'} = [];
                       my @checked = &Apache::loncommon::get_env_multiple('form.proctoring_crsconf_'.$provider);
                       foreach my $crsfield (@{$crsconf{$provider}}) {
                           if (grep(/^\Q$crsfield\E$/,@checked)) {
                               push(@{$confhash{$provider}{'crsconf'}},$crsfield);
                           }
                       }
                       if (ref($current{'crsconf'}) eq 'ARRAY') {
                           unless ($changes{$provider}) {  
                               my @new = sort(@{$confhash{$provider}{'crsconf'}});
                               my @old = sort(@{$current{'crsconf'}});
                               my @diffs = &Apache::loncommon::compare_arrays(\@new,\@old);
                               if (@diffs) {
                                   $changes{$provider} = 1;
                               }
                           }
                       } elsif (@{$confhash{$provider}{'crsconf'}}) {
                           $changes{$provider} = 1;
                       }
                   }
               }
               if ($showroles) {
                   $confhash{$provider}{'roles'} = {};
                   foreach my $role (@courseroles) {
                       my $poss = $env{'form.proctoring_roles_'.$role.'_'.$provider};
                       if (grep(/^\Q$poss\E$/,@ltiroles)) {
                           $confhash{$provider}{'roles'}{$role} = $poss;
                       }
                   }
                   unless ($changes{$provider}) {
                       if (ref($current{'roles'}) eq 'HASH') {
                           foreach my $role (keys(%{$current{'roles'}})) {
                               unless ($current{'roles'}{$role} eq $confhash{$provider}{'roles'}{$role}) {
                                   $changes{$provider} = 1;
                                   last
                               }
                           }
                           unless ($changes{$provider}) {
                               foreach my $role (keys(%{$confhash{$provider}{'roles'}})) {
                                   unless ($confhash{$provider}{'roles'}{$role} eq $current{'roles'}{$role}) {
                                       $changes{$provider} = 1;
                                       last;
                                   }
                               }
                           }
                       } elsif (keys(%{$confhash{$provider}{'roles'}})) {
                           $changes{$provider} = 1;
                       }
                   }
               }
               if (ref($current{'custom'}) eq 'HASH') {
                   my @customdels = &Apache::loncommon::get_env_multiple('form.proctoring_customdel_'.$provider);
                   foreach my $key (keys(%{$current{'custom'}})) {
                       if (grep(/^\Q$key\E$/,@customdels)) {
                           $changes{$provider} = 1;
                       } else {
                           $confhash{$provider}{'custom'}{$key} = $env{'form.proctoring_customval_'.$key.'_'.$provider};
                           if ($confhash{$provider}{'custom'}{$key} ne $current{'custom'}{$key}) {
                               $changes{$provider} = 1;
                           }
                       }
                   }
               }
               if ($customadds{$provider}) {
                   my $name = $env{'form.proctoring_custom_name_'.$provider};
                   $name =~ s/(`)/'/g;
                   $name =~ s/^\s+//;
                   $name =~ s/\s+$//;
                   my $value = $env{'form.proctoring_custom_value_'.$provider};
                   $value =~ s/(`)/'/g;
                   $value =~ s/^\s+//;
                   $value =~ s/\s+$//;
                   if ($name ne '') {
                       $confhash{$provider}{'custom'}{$name} = $value;
                       $changes{$provider} = 1;
                   }
               }
           }
       }
       if (@allpos > 0) {
           my $idx = 0;
           foreach my $provider (@allpos) {
               if ($provider ne '') {
                   $confhash{$provider}{'order'} = $idx;
                   unless ($changes{$provider}) {
                       if (ref($domconfig{$action}) eq 'HASH') {
                           if (ref($domconfig{$action}{$provider}) eq 'HASH') {
                               if ($domconfig{$action}{$provider}{'order'} ne $idx) {
                                   $changes{$provider} = 1;
                               }
                           }
                       }
                   }
                   $idx ++;
               }
           }
       }
       my %proc_hash = (
                             $action => { %confhash }
                          );
       my $putresult = &Apache::lonnet::put_dom('configuration',\%proc_hash,
                                                $dom);
       if ($putresult eq 'ok') {
           my %proc_enchash = (
                                $action => { %encconfhash }
                            );
           &Apache::lonnet::put_dom('encconfig',\%proc_enchash,$dom,undef,1);
           if (keys(%changes) > 0) {
               my $cachetime = 24*60*60;
               my %procall = %confhash;
               foreach my $provider (keys(%procall)) {
                   if (ref($encconfhash{$provider}) eq 'HASH') {
                       foreach my $key ('key','secret') {
                           $procall{$provider}{$key} = $encconfhash{$provider}{$key};
                       }
                   }
               }
               &Apache::lonnet::do_cache_new('proctoring',$dom,\%procall,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'proctoring'} = 1;
               }
               $resulttext = &mt('Configuration for Provider(s) with changes:').'<ul>';
               my %bynum;
               foreach my $provider (sort(keys(%changes))) {
                   my $position = $confhash{$provider}{'order'};
                   $bynum{$position} = $provider;
               }
               foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                   my $provider = $bynum{$pos};
                   my %lt = &proctoring_titles($provider);
                   my %fieldtitles = &proctoring_fieldtitles($provider);
                   if (!$confhash{$provider}{'available'}) {
                       $resulttext .= '<li>'.&mt('Proctoring integration unavailable for: [_1]','<b>'.$providernames{$provider}.'</b>').'</li>';
                   } else {
                       $resulttext .= '<li>'.&mt('Proctoring integration available for: [_1]','<b>'.$providernames{$provider}.'</b>');
                       if ($confhash{$provider}{'image'}) {
                           $resulttext .= '&nbsp;'.
                                          '<img src="'.$confhash{$provider}{'image'}.'"'.
                                          ' alt="'.&mt('Proctoring icon').'" />';
                       }
                       $resulttext .= '<ul>';
                       my $position = $pos + 1;
                       $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                       foreach my $key ('version','sigmethod','url','lifetime') {
                           if ($confhash{$provider}{$key} ne '') {
                               $resulttext .= '<li>'.$lt{$key}.':&nbsp;'.$confhash{$provider}{$key}.'</li>';
                           }
                       }
                       if ($encconfhash{$provider}{'key'} ne '') {
                           $resulttext .= '<li>'.$lt{'key'}.':&nbsp;'.$encconfhash{$provider}{'key'}.'</li>';
                       }
                       if ($encconfhash{$provider}{'secret'} ne '') {
                           $resulttext .= '<li>'.$lt{'secret'}.':&nbsp;';
                           my $num = length($encconfhash{$provider}{'secret'});
                           $resulttext .= ('*'x$num).'</li>';
                       }
                       my (@fields,$showroles);
                       if (ref($requserfields{$provider}) eq 'ARRAY') {
                           push(@fields,@{$requserfields{$provider}});
                       }
                       if (ref($confhash{$provider}{'fields'}) eq 'ARRAY') {
                           push(@fields,@{$confhash{$provider}{'fields'}});
                       } elsif (ref($confhash{$provider}{'fields'}) eq 'HASH') {
                           push(@fields,(keys(%{$confhash{$provider}{'fields'}})));
                       }
                       if (@fields) {
                           if (grep(/^roles$/,@fields)) {
                               $showroles = 1;
                           }
                           $resulttext .= '<li>'.$lt{'udsl'}.':&nbsp;"'.
                                          join('", "', map { $lt{$_}; } @fields).'"</li>';
                       }
                       if (ref($requserfields{$provider}) eq 'ARRAY') {
                           if (grep(/^user$/,@{$requserfields{$provider}})) {
                               if ($confhash{$provider}{'incdom'}) {
                                   $resulttext .= '<li>'.&mt('[_1] sent as [_2]',$lt{'user'},$lt{'uname:dom'}).'</li>';
                               } else { 
                                   $resulttext .= '<li>'.&mt('[_1] sent as [_2]',$lt{'user'},$lt{'username'}).'</li>';
                               }
                           }
                       }
                       if (ref($confhash{$provider}{'defaults'}) eq 'ARRAY') {
                           if (@{$confhash{$provider}{'defaults'}} > 0) {
                               $resulttext .= '<li>'.$lt{'defa'};
                               foreach my $field (@{$confhash{$provider}{'defaults'}}) {
                                   $resulttext .= ' "'.$fieldtitles{$field}.'",';
                               }
                               $resulttext =~ s/,$//;
                               $resulttext .= '</li>';
                           }
                       } elsif (ref($confhash{$provider}{'defaults'}) eq 'HASH') {
                           if (keys(%{$confhash{$provider}{'defaults'}})) {
                               $resulttext .= '<li>'.$lt{'defa'}.':&nbsp;<ul>';
                               foreach my $key (sort(keys(%{$confhash{$provider}{'defaults'}}))) {
                                   if ($confhash{$provider}{'defaults'}{$key} ne '') {
                                       $resulttext .= '<li>'.$fieldtitles{$key}.' = '.$confhash{$provider}{'defaults'}{$key}.'</li>';
                                   }
                               }
                               $resulttext .= '</ul></li>';
                           }
                       }
                       if (ref($crsconf{$provider}) eq 'ARRAY') {
                           if (@{$crsconf{$provider}} > 0) {
                               $resulttext .= '<li>'.&mt('Configurable in course:');
                               my $numconfig = 0;
                               if (ref($confhash{$provider}{'crsconf'}) eq 'ARRAY') {
                                   if (@{$confhash{$provider}{'crsconf'}} > 0) {
                                       foreach my $field (@{$confhash{$provider}{'crsconf'}}) {
                                           $numconfig ++;
                                           if ($provider eq 'examity') {
                                               $resulttext .= ' "'.$lt{'crs'.$field}.'",';
                                           } else {
                                               $resulttext .= ' "'.$fieldtitles{$field}.'",';
                                           }
                                       }
                                       $resulttext =~ s/,$//;
                                   }
                               }
                               if (!$numconfig) {
                                   $resulttext .= '&nbsp;'.&mt('None');
                               }
                               $resulttext .= '</li>';
                           }
                       }
                       if ($showroles) {
                           if (ref($confhash{$provider}{'roles'}) eq 'HASH') {
                               my $rolemaps;
                               foreach my $role (@courseroles) {
                                   if ($confhash{$provider}{'roles'}{$role}) {
                                       $rolemaps .= ('&nbsp;'x2).&Apache::lonnet::plaintext($role,'Course').'='.
                                                    $confhash{$provider}{'roles'}{$role}.',';
                                   }
                               }
                               if ($rolemaps) {
                                   $rolemaps =~ s/,$//;
                                   $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';
                               }
                           }
                       }
                       if (ref($confhash{$provider}{'custom'}) eq 'HASH') {
                           my $customlist;
                           if (keys(%{$confhash{$provider}{'custom'}})) {
                               foreach my $key (sort(keys(%{$confhash{$provider}{'custom'}}))) {
                                   $customlist .= $key.'='.$confhash{$provider}{'custom'}{$key}.', ';
                               }
                               $customlist =~ s/,$//;
                           }
                           if ($customlist) {
                               $resulttext .= '<li>'.&mt('Custom items').': '.$customlist.'</li>';
                           }
                       } 
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
           } else {
               $resulttext = &mt('No changes made.');
           }
       } else {
           $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
       }
       if ($errors) {
           $resulttext .= &mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul>';
       }
       return $resulttext;
   }
   
   sub process_proctoring_image {
       my ($r,$dom,$confname,$caller,$provider,$configuserok,$switchserver,$author_ok) = @_;
       my $filename = $env{'form.'.$caller.'.filename'};
       my ($error,$url);
       my ($width,$height) = (21,21);
       if ($configuserok eq 'ok') {
           if ($switchserver) {
               $error = &mt('Upload of Remote Proctoring Provider icon is not permitted to this server: [_1]',
                            $switchserver);
           } elsif ($author_ok eq 'ok') {
               my $modified = [];
               my ($result,$imageurl,$madethumb) =
                   &Apache::lonconfigsettings::publishlogo($r,'upload',$caller,$dom,$confname,
                                                           "proctoring/$provider/icon",$width,$height,
                                                           '',$modified);
               if ($result eq 'ok') {
                   if ($madethumb) {
                       my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$});
                       my $imagethumb = "$path/tn-".$imagefile;
                       $url = $imagethumb;
                   } else {
                       $url = $imageurl;
                   }
                   &update_modify_urls($r,$modified);
               } 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);
   }
   
   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 (%{$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};
                   }
               }
           }
       }
       if ($proterror) {
           $errors .= '<li>'.$proterror.'</li>';
       }
       my (@items,%deletions,%itemids);
       if ($env{'form.lti_add'}) {
           my $consumer = $env{'form.lti_consumer_add'};
           $consumer =~ s/(`)/'/g;
           ($newid,my $error) = &get_lti_id($dom,$consumer);
           if ($newid) {
               $itemids{'add'} = $newid;
               push(@items,'add');
               $changes{$newid} = 1;
           } else {
               my $error = &mt('Failed to acquire unique ID for new LTI configuration');
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       }
       if (ref($domconfig{$action}) eq 'HASH') {
           my @todelete = &Apache::loncommon::get_env_multiple('form.lti_del');
           if (@todelete) {
               map { $deletions{$_} = 1; } @todelete;
           }
           my $maxnum = $env{'form.lti_maxnum'};
           for (my $i=0; $i<$maxnum; $i++) {
               my $itemid = $env{'form.lti_id_'.$i};
               $itemid =~ s/\D+//g;
               if (ref($domconfig{$action}{$itemid}) eq 'HASH') {
                   if ($deletions{$itemid}) {
                       $changes{$itemid} = $domconfig{$action}{$itemid}{'consumer'};
                   } else {
                       push(@items,$i);
                       $itemids{$i} = $itemid;
                   }
               }
           }
       }
       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;
                           }
                       }
                   } else {
                       $confhash{$itemid}{'lcauth'} = 'lti';
                   }
               }
               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 ($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);
                       }
                   }
                   $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;
                       }
                   }
                   my @possenroll = &Apache::loncommon::get_env_multiple('form.lti_selfenroll_'.$idx);
                   my @selfenroll;
                   foreach my $type (sort(@possenroll)) {
                       if ($posslticrs{$type}) {
                           push(@selfenroll,$type);
                       }
                   }
                   $confhash{$itemid}{'selfenroll'} = \@selfenroll;
                   if ($env{'form.lti_crssec_'.$idx}) {
                       if ($env{'form.lti_crssecsrc_'.$idx} eq 'course_section_sourcedid') {
                           $confhash{$itemid}{'section'} = $env{'form.lti_crssecsrc_'.$idx};
                       } elsif ($env{'form.lti_crssecsrc_'.$idx} eq 'other') {
                           my $section = $env{'form.lti_customsection_'.$idx};
                           $section =~ s/(`)/'/g;
                           $section =~ s/^\s+|\s+$//g;
                           if ($section ne '') {
                               $confhash{$itemid}{'section'} = $section;
                           }
                       }
                   }
                   foreach my $field ('passback','roster') {
                       if ($env{'form.lti_'.$field.'_'.$idx}) {
                           $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 {
                           $confhash{$itemid}{'passbackformat'} = '1.1';
                       }
                   }
               }
               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;
                           }
                       }
                       unless ($changes{$itemid}) {
                           if ($currlti{'passback'} eq $confhash{$itemid}{'passback'}) {
                               if ($currlti{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) {
                                   $changes{$itemid} = 1;
                               }
                           }
                       }
                       foreach my $field ('mapcrstype','selfenroll') {
                           unless ($changes{$itemid}) {
                               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;
                                       }
                                   } elsif (@{$currlti{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               }
                           }
                       }
                       unless ($changes{$itemid}) {
                           if (ref($currlti{'maproles'}) eq 'HASH') {
                               if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
                                   foreach my $ltirole (keys(%{$currlti{'maproles'}})) {
                                       if ($currlti{'maproles'}{$ltirole} ne 
                                           $confhash{$itemid}{'maproles'}{$ltirole}) {
                                           $changes{$itemid} = 1;
                                           last;
                                       }
                                   }
                                   unless ($changes{$itemid}) {
                                       foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) {
                                           if ($confhash{$itemid}{'maproles'}{$ltirole} ne 
                                               $currlti{'maproles'}{$ltirole}) {
                                               $changes{$itemid} = 1;
                                               last;
                                           }
                                       }
                                   }
                               } elsif (keys(%{$currlti{'maproles'}}) > 0) {
                                   $changes{$itemid} = 1;
                               }
                           } 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;
                                       }
                                   } elsif (@{$currlti{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               }
                           }
                       }
                   }
               }
           }
           if ($is_home) {
               my $keyitem = 'form.lti_key_'.$idx;
               $env{$keyitem} =~ s/(`)/'/g;
               if ($env{$keyitem} ne '') {
                   $ltienc{$itemid}{'key'} = $env{$keyitem};
                   unless ($changes{$itemid}) {
                       if ($currlti{'key'} ne $env{$keyitem}) {
                           $changes{$itemid} = 1;
                       }
                   }
               }
               my $secretitem = 'form.lti_secret_'.$idx;
               $env{$secretitem} =~ s/(`)/'/g;
               if ($currlti{'usable'}) {
                   if ($env{'form.lti_changesecret_'.$idx}) {
                       if ($env{$secretitem} ne '') {
                           if ($privnum && $cipher) {
                               $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem});
                               $confhash{$itemid}{'cipher'} = $privnum;
                           } else {
                               $ltienc{$itemid}{'secret'} = $env{$secretitem};
                           }
                           $changes{$itemid} = 1;
                       }
                   } else {
                       $ltienc{$itemid}{'secret'} = $currlti{'secret'};
                       $confhash{$itemid}{'cipher'} = $currlti{'cipher'};
                   }
                   if (ref($ltienc{$itemid}) eq 'HASH') {
                       if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'secret'} ne '')) {
                           $confhash{$itemid}{'usable'} = 1;
                       }
                   }
               } elsif ($env{$secretitem} ne '') {
                   if ($privnum && $cipher) {
                       $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem});
                       $confhash{$itemid}{'cipher'} = $privnum;
                   } else {
                       $ltienc{$itemid}{'secret'} = $env{$secretitem};
                   }
                   if (ref($ltienc{$itemid}) eq 'HASH') {
                       if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'key'} ne '')) {
                           $confhash{$itemid}{'usable'} = 1;
                       }
                   }
                   $changes{$itemid} = 1;
               }
           }
           unless ($changes{$itemid}) {
               foreach my $key (keys(%currlti)) {
                   if (ref($currlti{$key}) eq 'HASH') {
                       if (ref($confhash{$itemid}{$key}) eq 'HASH') {
                           foreach my $innerkey (keys(%{$currlti{$key}})) {
                               unless (exists($confhash{$itemid}{$key}{$innerkey})) {
                                   $changes{$itemid} = 1;
                                   last;
                               }
                           }
                       } elsif (keys(%{$currlti{$key}}) > 0) {
                           $changes{$itemid} = 1;
                       }
                   }
                   last if ($changes{$itemid});
               }
           }
       }
       if (@allpos > 0) {
           my $idx = 0;
           foreach my $itemid (@allpos) {
               if ($itemid ne '') {
                   $confhash{$itemid}{'order'} = $idx;
                   if (ref($domconfig{$action}) eq 'HASH') {
                       if (ref($domconfig{$action}{$itemid}) eq 'HASH') {
                           if ($domconfig{$action}{$itemid}{'order'} ne $idx) {
                               $changes{$itemid} = 1;
                           }
                       }
                   }
                   $idx ++;
               }
           }
       }
   
       if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) {
           return &mt('No changes made.');
       }
   
       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 (keys(%ltienchash)) {
               &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1);
           }
           $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) {
               my $cachetime = 24*60*60;
               &Apache::lonnet::do_cache_new('lti',$dom,\%confhash,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'lti'} = 1;
               }
               my %bynum;
               foreach my $itemid (sort(keys(%changes))) {
                   if (ref($confhash{$itemid}) eq 'HASH') {
                       my $position = $confhash{$itemid}{'order'};
                       $bynum{$position} = $itemid;
                   }
               }
               foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                   my $itemid = $bynum{$pos};
                   if (ref($confhash{$itemid}) eq 'HASH') {
                       $resulttext .= '<li><b>'.$confhash{$itemid}{'consumer'}.'</b><ul>';
                       my $position = $pos + 1;
                       $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                       foreach my $item ('version','lifetime') {
                           if ($confhash{$itemid}{$item} ne '') {
                               $resulttext .= '<li>'.$lt{$item}.':&nbsp;'.$confhash{$itemid}{$item}.'</li>';
                           }
                       }
                       if ($ltienc{$itemid}{'key'} ne '') {
                           $resulttext .= '<li>'.$lt{'key'}.':&nbsp;'.$ltienc{$itemid}{'key'}.'</li>';
                       }
                       if ($ltienc{$itemid}{'secret'} ne '') {
                           $resulttext .= '<li>'.$lt{'secret'}.':&nbsp;['.&mt('not shown').']</li>';
                       }
                       if ($confhash{$itemid}{'requser'}) {
                           if ($confhash{$itemid}{'callback'}) {
                               $resulttext .= '<li>'.&mt('Callback setting').': '.$confhash{$itemid}{'callback'}.'</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('Callback to logout LON-CAPA on log out from Consumer').'</li>';
                           }
                           if ($confhash{$itemid}{'mapuser'}) {
                               my $shownmapuser;
                               if ($confhash{$itemid}{'mapuser'} eq 'lis_person_sourcedid') {
                                   $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 (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') {
                               if (@{$confhash{$itemid}{'makeuser'}} > 0) { 
                                   $resulttext .= '<li>'.&mt('Following roles may create user accounts: [_1]',
                                                             join(', ',@{$confhash{$itemid}{'makeuser'}})).'<br />';
                                   if ($confhash{$itemid}{'lcauth'} eq 'lti') {
                                       $resulttext .= &mt('New users will only be able to authenticate via LTI').'</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 (@{$confhash{$itemid}{'instdata'}} > 0) {
                                   $resulttext .= '<li>'.&mt('Institutional data will be used when creating a new user for: [_1]',
                                                             join(', ',map { $fieldtitles{$_}; } @{$confhash{$itemid}{'instdata'}})).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('No institutional data used when creating a new user.').'</li>';
                               }
                           }
                           foreach my $item ('topmenu','inlinemenu') {
                               $resulttext .= '<li>'.$lt{$item}.':&nbsp;';
                               if ($confhash{$itemid}{$item}) {
                                   $resulttext .= &mt('Yes');
                               } 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>';
                               }
                           }
                           if ($confhash{$itemid}{'crsinc'}) {
                               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>';
                   }
               }
               if (keys(%deletions)) {
                   foreach my $itemid (sort { $a <=> $b } keys(%deletions)) {
                       $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';
                   }
               }
           }
           $resulttext .= '</ul>';
           if (ref($lastactref) eq 'HASH') {
               if (($secchanges{'encrypt'}) || ($secchanges{'private'})) {
                   $lastactref->{'domdefaults'} = 1;
               }
           }
       } else {
           $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
       }
       if ($errors) {
           $resulttext .= &mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul>';
       }
       return $resulttext;
   }
   
   sub get_priv_creds {
       my ($dom,$home,$encchg,$encrypt,$storedsec) = @_;
       my ($needenc,$cipher,$privnum);
       my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
       if (($encchg) && (ref($encrypt) eq 'HASH')) {
           $needenc = $encrypt->{'consumers'} 
       } else {
           $needenc = $domdefs{'ltienc_consumers'};
       }
       if ($needenc) {
           if (($storedsec eq 'ok') || ((ref($domdefs{'ltiprivhosts'}) eq 'ARRAY') &&
                                        (grep(/^\Q$home\E$/,@{$domdefs{'ltiprivhosts'}})))) {
                           my %privhash  = &Apache::lonnet::restore_dom('lti','private',$dom,$home,1);
               my $privkey = $privhash{'key'};
               $privnum = $privhash{'version'};
               if (($privnum) && ($privkey ne '')) {
                   $cipher = Crypt::CBC->new({'key'     => $privkey,
                                             'cipher'  => 'DES'});
               }
           }
       }
       return ($cipher,$privnum);
   }
   
   sub get_lti_id {
       my ($domain,$consumer) = @_;
       # get lock on lti db
       my $lockhash = {
                         lock => $env{'user.name'}.
                                 ':'.$env{'user.domain'},
                      };
       my $tries = 0;
       my $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain);
       my ($id,$error);
   
       while (($gotlock ne 'ok') && ($tries<10)) {
           $tries ++;
           sleep (0.1);
           $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain);
       }
       if ($gotlock eq 'ok') {
           my %currids = &Apache::lonnet::dump_dom('lti',$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('lti',{ $id => $consumer },$domain) eq 'ok') {
                       $error = 'nostore';
                   }
               } else {
                   $error = 'nonumber';
               }
           }
           my $dellockoutcome = &Apache::lonnet::del_dom('lti',['lock'],$domain);
       } else {
           $error = 'nolock';
       }
       return ($id,$error);
   }
   
 sub modify_autoenroll {  sub modify_autoenroll {
     my ($dom,$lastactref,%domconfig) = @_;      my ($dom,$lastactref,%domconfig) = @_;
     my ($resulttext,%changes);      my ($resulttext,%changes);
Line 11382  sub modify_autoenroll { Line 15918  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 11392  sub modify_autoenroll { Line 15928  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 11430  sub modify_autoenroll { Line 15972  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 11453  sub modify_autoenroll { Line 15998  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 {
                       $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 {                  } else {
                     $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section: deleted');                      $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 12106  sub modify_contacts { Line 16664  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 12561  sub modify_contacts { Line 17119  sub modify_contacts {
     return $resulttext;      return $resulttext;
 }  }
   
   sub modify_privacy {
       my ($dom,$lastactref,%domconfig) = @_;
       my ($resulttext,%current,%changes);
       if (ref($domconfig{'privacy'}) eq 'HASH') {
           %current = %{$domconfig{'privacy'}};
       }
       my @fields = ('lastname','firstname','middlename','generation','permanentemail','id');
       my @items = ('domain','author','course','community');
       my %names = &Apache::lonlocal::texthash (
                      domain => 'Assigned domain role(s)',
                      author => 'Assigned co-author role(s)',
                      course => 'Assigned course role(s)',
                      community => 'Assigned community role(s)',
                   );
       my %roles = &Apache::lonlocal::texthash (
                      domain => 'Domain role',
                      author => 'Co-author role',
                      course => 'Course role',
                      community => 'Community role',
                   );
       my %titles = &Apache::lonlocal::texthash (
                     approval => 'Approval for role in different domain',
                     othdom   => 'User information available in other domain',
                     priv     => 'Information viewable by privileged user in same domain',
                     unpriv   => 'Information viewable by unprivileged user in same domain',
                     instdom  => 'Other domain shares institution/provider',
                     extdom   => 'Other domain has different institution/provider',
                     none     => 'Not allowed',
                     user     => 'User authorizes',
                     domain   => 'Domain Coordinator authorizes',
                     auto     => 'Unrestricted',
                     notify   => 'Notify when role needs authorization',
       );
       my %fieldnames = &Apache::lonlocal::texthash (
                           id => 'Student/Employee ID',
                           permanentemail => 'E-mail address',
                           lastname => 'Last Name',
                           firstname => 'First Name',
                           middlename => 'Middle Name',
                           generation => 'Generation',
       );
       my ($othertitle,$usertypes,$types) =
           &Apache::loncommon::sorted_inst_types($dom);
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
   
       my %privacyhash = (
                          'approval' => {
                                          instdom => {},
                                          extdom  => {},
                                        },
                          'othdom'   => {},
                          'priv'     => {},
                          'unpriv'   => {},
                         );
       foreach my $item (@items) {
           if (@instdoms > 1) {
               if ($env{'form.privacy_approval_instdom_'.$item} =~ /^(none|user|domain|auto)$/) {
                   $privacyhash{'approval'}{'instdom'}{$item} = $env{'form.privacy_approval_instdom_'.$item};
               }
               if (ref($current{'approval'}) eq 'HASH') {
                   if (ref($current{'approval'}{'instdom'}) eq 'HASH') {
                       unless ($privacyhash{'approval'}{'instdom'}{$item} eq $current{'approval'}{'instdom'}{$item}) {
                           $changes{'approval'} = 1;
                       }
                   }
               } elsif ($privacyhash{'approval'}{'instdom'}{$item} ne 'auto') {
                   $changes{'approval'} = 1;
               }
           }
           if (keys(%by_location) > 0) {
               if ($env{'form.privacy_approval_extdom_'.$item} =~ /^(none|user|domain|auto)$/) {
                   $privacyhash{'approval'}{'extdom'}{$item} = $env{'form.privacy_approval_extdom_'.$item};
               }
               if (ref($current{'approval'}) eq 'HASH') {
                   if (ref($current{'approval'}{'extdom'}) eq 'HASH') {
                       unless ($privacyhash{'approval'}{'extdom'}{$item} eq $current{'approval'}{'extdom'}{$item}) {
                           $changes{'approval'} = 1;
                       }
                   }
               } elsif ($privacyhash{'approval'}{'extdom'}{$item} ne 'auto') {
                   $changes{'approval'} = 1;
               }
           }
           foreach my $status ('priv','unpriv') {
               my @possibles = sort(&Apache::loncommon::get_env_multiple('form.privacy_'.$status.'_'.$item));
               my @newvalues;
               foreach my $field (@possibles) {
                   if (grep(/^\Q$field\E$/,@fields)) {
                       $privacyhash{$status}{$item}{$field} = 1;
                       push(@newvalues,$field);
                   }
               }
               @newvalues = sort(@newvalues);
               if (ref($current{$status}) eq 'HASH') {
                   if (ref($current{$status}{$item}) eq 'HASH') {
                       my @currvalues = sort(keys(%{$current{$status}{$item}}));
                       my @diffs = &Apache::loncommon::compare_arrays(\@currvalues,\@newvalues);
                       if (@diffs > 0) {
                           $changes{$status} = 1;
                       }
                   }
               } else {
                   my @stdfields;
                   foreach my $field (@fields) {
                       if ($field eq 'id') {
                           next if ($status eq 'unpriv');
                           next if (($status eq 'priv') && ($item eq 'community'));
                       }
                       push(@stdfields,$field);
                   }
                   my @diffs = &Apache::loncommon::compare_arrays(\@stdfields,\@newvalues);
                   if (@diffs > 0) {
                       $changes{$status} = 1;
                   }
               }
           }
       }
       if ((@instdoms > 1) || (keys(%by_location) > 0)) {
           my @statuses;
           if (ref($types) eq 'ARRAY') {
               @statuses = @{$types};
           }
           foreach my $type (@statuses,'default') {
               my @possfields = &Apache::loncommon::get_env_multiple('form.privacy_othdom_'.$type);
               my @newvalues;
               foreach my $field (sort(@possfields)) {
                   if (grep(/^\Q$field\E$/,@fields)) {
                       $privacyhash{'othdom'}{$type}{$field} = 1;
                       push(@newvalues,$field);
                   }
               }
               @newvalues = sort(@newvalues);
               if (ref($current{'othdom'}) eq 'HASH') {
                   if (ref($current{'othdom'}{$type}) eq 'HASH') {
                       my @currvalues = sort(keys(%{$current{'othdom'}{$type}}));
                       my @diffs = &Apache::loncommon::compare_arrays(\@currvalues,\@newvalues);
                       if (@diffs > 0) {
                           $changes{'othdom'} = 1;
                       }
                   }
               } else {
                   my @stdfields = ('lastname','firstname','middlename','generation','permanentemail');
                   my @diffs = &Apache::loncommon::compare_arrays(\@stdfields,\@newvalues);
                   if (@diffs > 0) {
                       $changes{'othdom'} = 1;
                   }
               }
           }
           my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']);
           my %notify;
           foreach my $possdc (&Apache::loncommon::get_env_multiple('form.privacy_notify')) {
               if (exists($domcoords{$possdc})) {
                   $notify{$possdc} = 1;
               }
           }
           my $notify = join(',',sort(keys(%notify)));
           if ($current{'notify'} ne $notify) {
               $changes{'notify'} = 1;
           }
           $privacyhash{'notify'} = $notify;
       }
       my %confighash = (
                           privacy => \%privacyhash,
                        );
       my $putresult = &Apache::lonnet::put_dom('configuration',\%confighash,$dom);
       if ($putresult eq 'ok') {
           if (keys(%changes) > 0) {
               $resulttext = &mt('Changes made: ').'<ul>';
               foreach my $key ('approval','notify','othdom','priv','unpriv') {
                   if ($changes{$key}) {
                       $resulttext .= '<li>'.$titles{$key}.':<ul>';
                       if ($key eq 'approval') {
                           if (keys(%{$privacyhash{$key}{instdom}})) {
                               $resulttext .= '<li>'.$titles{'instdom'}.'<ul>';
                               foreach my $item (@items) {
                                   $resulttext .=  '<li>'.$roles{$item}.': '.$titles{$privacyhash{$key}{instdom}{$item}}.'</li>';
                               }
                               $resulttext .= '</ul></li>';
                           }
                           if (keys(%{$privacyhash{$key}{extdom}})) {
                               $resulttext .= '<li>'.$titles{'extdom'}.'<ul>';
                               foreach my $item (@items) {
                                   $resulttext .=  '<li>'.$roles{$item}.': '.$titles{$privacyhash{$key}{extdom}{$item}}.'</li>';
                               }
                               $resulttext .= '</ul></li>';
                           }
                       } elsif ($key eq 'notify') {
                           if ($privacyhash{$key}) {
                               foreach my $dc (split(/,/,$privacyhash{$key})) {
                                   my ($dcname,$dcdom) = split(/:/,$dc);
                                   $resulttext .= '<li>'.&Apache::loncommon::plainname($dcname,$dcdom).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('No DCs to notify').'</li>';
                           }
                       } elsif ($key eq 'othdom') {
                           my @statuses;
                           if (ref($types) eq 'ARRAY') {
                               @statuses = @{$types};
                           }
                           if (ref($privacyhash{$key}) eq 'HASH') {
                               foreach my $status (@statuses,'default') {
                                   if ($status eq 'default') {
                                       $resulttext .= '<li>'.$othertitle.': ';
                                   } elsif (ref($usertypes) eq 'HASH') {
                                       $resulttext .= '<li>'.$usertypes->{$status}.': ';
                                   } else {
                                       next;
                                   }
                                   if (ref($privacyhash{$key}{$status}) eq 'HASH') {
                                       if (keys(%{$privacyhash{$key}{$status}})) {
                                           $resulttext .= join(', ', map { $fieldnames{$_}; } (sort(keys(%{$privacyhash{$key}{$status}}))));
                                       } else {
                                           $resulttext .= &mt('none');
                                       }
                                   }
                                   $resulttext .= '</li>';
                               }
                           }
                       } else {
                           foreach my $item (@items) {
                               if (ref($privacyhash{$key}{$item}) eq 'HASH') {
                                   $resulttext .= '<li>'.$names{$item}.': ';
                                   if (keys(%{$privacyhash{$key}{$item}})) {
                                       $resulttext .= join(', ', map { $fieldnames{$_}; } (sort(keys(%{$privacyhash{$key}{$item}}))));
                                   } else {
                                       $resulttext .= &mt('none');
                                   }
                                   $resulttext .= '</li>';
                               }
                           }
                       }
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
               if ($changes{'approval'}) {
                   my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                   delete($domdefaults{'userapprovals'});
                   if (ref($privacyhash{'approval'}) eq 'HASH') {
                       foreach my $domtype ('instdom','extdom') {
                           if (ref($privacyhash{'approval'}{$domtype}) eq 'HASH') {
                               foreach my $roletype ('domain','author','course','community') {
                                   if ($privacyhash{'approval'}{$domtype}{$roletype} eq 'user') {
                                       $domdefaults{'userapprovals'} = 1;
                                       last;
                                   }
                               }
                           }
                           last if ($domdefaults{'userapprovals'});               
                       }
                   }
                   my $cachetime = 24*60*60;
                   &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'domdefaults'} = 1;
                   }
               }
           } else {
               $resulttext = &mt('No changes made to user information settings');
           }
       } else {
           $resulttext = '<span class="LC_error">'.
               &mt('An error occurred: [_1]',$putresult).'</span>';
       }
       return $resulttext;
   }
   
 sub modify_passwords {  sub modify_passwords {
     my ($r,$dom,$confname,$lastactref,%domconfig) = @_;      my ($r,$dom,$confname,$lastactref,%domconfig) = @_;
     my ($resulttext,%current,%changes,%newvalues,@oktypes,$errors,      my ($resulttext,%current,%changes,%newvalues,@oktypes,$errors,
Line 12586  sub modify_passwords { Line 17413  sub modify_passwords {
         min            => 'Minimum password length',          min            => 'Minimum password length',
         max            => 'Maximum password length',          max            => 'Maximum password length',
         chars          => 'Required characters',          chars          => 'Required characters',
           expire         => 'Password expiration (days)',
         numsaved       => 'Number of previous passwords to save',          numsaved       => 'Number of previous passwords to save',
         reset          => 'Resetting Forgotten Password',          reset          => 'Resetting Forgotten Password',
         intauth        => 'Encryption of Stored Passwords (Internal Auth)',          intauth        => 'Encryption of Stored Passwords (Internal Auth)',
Line 12728  sub modify_passwords { Line 17556  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 12737  sub modify_passwords { Line 17564  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 12795  sub modify_passwords { Line 17625  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;  
                 }  
             } else {  
                 $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 13016  sub modify_passwords { Line 17797  sub modify_passwords {
                             $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$titles{$key.'_'.$item},$value).'</li>';                              $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$titles{$key.'_'.$item},$value).'</li>';
                         }                          }
                     } elsif ($key eq 'rules') {                      } elsif ($key eq 'rules') {
                         foreach my $rule ('min','max','numsaved') {                          foreach my $rule ('min','max','expire','numsaved') {
                             if ($confighash{'passwords'}{$rule} eq '') {                              if ($confighash{'passwords'}{$rule} eq '') {
                                 if ($rule eq 'min') {                                  if ($rule eq 'min') {
                                     $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule});                                      $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule});
Line 13104  sub modify_passwords { Line 17885  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','expire','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 'expire') {
               if (($env{'form.'.$prefix.'_'.$rule} =~ /^\d+(|\.\d*)$/) &&
                   ($env{'form.'.$prefix.'_'.$rule} ne '0')) {
                   $ruleok = 1;
               }
           } elsif ($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 13188  sub modify_usercreation { Line 18039  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 13403  sub modify_selfcreation { Line 18254  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 14381  sub modify_defaults { Line 19231  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 14418  sub modify_defaults { Line 19268  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 14442  sub modify_defaults { Line 19334  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 14462  sub modify_defaults { Line 19389  sub modify_defaults {
     my @allpos;      my @allpos;
     my %alltypes;      my %alltypes;
     my @inststatusguest;      my @inststatusguest;
     if (ref($currinststatus) eq 'HASH') {      if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {
         if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {          foreach my $type (@{$currinststatus->{'inststatusguest'}}) {
             foreach my $type (@{$currinststatus->{'inststatusguest'}}) {              unless (grep(/^\Q$type\E$/,@todelete)) {
                 unless (grep(/^\Q$type\E$/,@todelete)) {                  push(@inststatusguest,$type);
                     push(@inststatusguest,$type);  
                 }  
             }              }
         }          }
     }      }
Line 14558  sub modify_defaults { Line 19483  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 14569  sub modify_defaults { Line 19514  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 14628  sub modify_scantron { Line 19587  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 14782  sub modify_scantron { Line 19744  sub modify_scantron {
                 &mt('An error occurred: [_1]',$putresult).'</span>';                  &mt('An error occurred: [_1]',$putresult).'</span>';
         }          }
     } else {      } else {
         $resulttext = &mt('No changes made to bubblesheet format settings');          $resulttext = &mt('No changes made to bubblesheet format file'); 
     }      }
     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 14815  sub modify_coursecategories { Line 19777  sub modify_coursecategories {
         if ($domconfig{'coursecategories'}{'categorizecomm'} ne $env{'form.categorizecomm'}) {          if ($domconfig{'coursecategories'}{'categorizecomm'} ne $env{'form.categorizecomm'}) {
             $changes{'categorizecomm'} = 1;              $changes{'categorizecomm'} = 1;
             $domconfig{'coursecategories'}{'categorizecomm'} = $env{'form.categorizecomm'};              $domconfig{'coursecategories'}{'categorizecomm'} = $env{'form.categorizecomm'};
   
           }
           if ($domconfig{'coursecategories'}{'togglecatsplace'} ne $env{'form.togglecatsplace'}) {
               $changes{'togglecatsplace'} = 1;
               $domconfig{'coursecategories'}{'togglecatsplace'} = $env{'form.togglecatsplace'};
           }
           if ($domconfig{'coursecategories'}{'categorizeplace'} ne $env{'form.categorizeplace'}) {
               $changes{'categorizeplace'} = 1;
               $domconfig{'coursecategories'}{'categorizeplace'} = $env{'form.categorizeplace'};
         }          }
         foreach my $item (@catitems) {          foreach my $item (@catitems) {
             if (grep(/^\Q$env{'form.coursecat_'.$item}\E$/,@cattypes)) {              if (grep(/^\Q$env{'form.coursecat_'.$item}\E$/,@cattypes)) {
Line 14829  sub modify_coursecategories { Line 19800  sub modify_coursecategories {
         $changes{'categorize'} = 1;          $changes{'categorize'} = 1;
         $changes{'togglecatscomm'} = 1;          $changes{'togglecatscomm'} = 1;
         $changes{'categorizecomm'} = 1;          $changes{'categorizecomm'} = 1;
           $changes{'togglecatsplace'} = 1;
           $changes{'categorizeplace'} = 1;
         $domconfig{'coursecategories'} = {          $domconfig{'coursecategories'} = {
                                              togglecats => $env{'form.togglecats'},                                               togglecats => $env{'form.togglecats'},
                                              categorize => $env{'form.categorize'},                                               categorize => $env{'form.categorize'},
                                              togglecatscomm => $env{'form.togglecatscomm'},                                               togglecatscomm => $env{'form.togglecatscomm'},
                                              categorizecomm => $env{'form.categorizecomm'},                                               categorizecomm => $env{'form.categorizecomm'},
                                                togglecatsplace => $env{'form.togglecatsplace'},
                                                categorizeplace => $env{'form.categorizeplace'},
                                          };                                           };
         foreach my $item (@catitems) {          foreach my $item (@catitems) {
             if ($env{'form.coursecat_'.$item} ne 'std') {              if ($env{'form.coursecat_'.$item} ne 'std') {
Line 14851  sub modify_coursecategories { Line 19826  sub modify_coursecategories {
         if (($domconfig{'coursecategories'}{'cats'}{'communities::0'} ne '')  && ($env{'form.communities'} == 0)) {          if (($domconfig{'coursecategories'}{'cats'}{'communities::0'} ne '')  && ($env{'form.communities'} == 0)) {
             push(@deletecategory,'communities::0');              push(@deletecategory,'communities::0');
         }          }
           if (($domconfig{'coursecategories'}{'cats'}{'placement::0'} ne '')  && ($env{'form.placement'} == 0)) {
               push(@deletecategory,'placement::0');
           }
     }      }
     my (@predelcats,@predeltrails,%predelallitems,%sort_by_deltrail);      my (@predelcats,@predeltrails,%predelallitems,%sort_by_deltrail);
     if (ref($cathash) eq 'HASH') {      if (ref($cathash) eq 'HASH') {
Line 14913  sub modify_coursecategories { Line 19891  sub modify_coursecategories {
             $adds{$newitem} = 1;              $adds{$newitem} = 1;
         }          }
     }      }
       if ($env{'form.placement'} eq '1') {
           if (ref($cathash) eq 'HASH') {
               my $newitem = 'placement::0';
               if ($cathash->{$newitem} eq '') {
                   $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.placement_pos'};
                   $adds{$newitem} = 1;
               }
           } else {
               my $newitem = 'placement::0';
               $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.placement_pos'};
               $adds{$newitem} = 1;
           }
       }
     if ($env{'form.addcategory_name'} ne '') {      if ($env{'form.addcategory_name'} ne '') {
         if (($env{'form.addcategory_name'} ne 'instcode') &&          if (($env{'form.addcategory_name'} ne 'instcode') &&
             ($env{'form.addcategory_name'} ne 'communities')) {              ($env{'form.addcategory_name'} ne 'communities') &&
               ($env{'form.addcategory_name'} ne 'placement')) {
             my $newitem = &escape($env{'form.addcategory_name'}).'::0';              my $newitem = &escape($env{'form.addcategory_name'}).'::0';
             $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos'};              $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos'};
             $adds{$newitem} = 1;              $adds{$newitem} = 1;
Line 15514  sub modify_coursedefaults { Line 20506  sub modify_coursedefaults {
     my ($dom,$lastactref,%domconfig) = @_;      my ($dom,$lastactref,%domconfig) = @_;
     my ($resulttext,$errors,%changes,%defaultshash);      my ($resulttext,$errors,%changes,%defaultshash);
     my %defaultchecked = (      my %defaultchecked = (
                              'canuse_pdfforms' => 'off',
                            'uselcmath'       => 'on',                             'uselcmath'       => 'on',
                            'usejsme'         => 'on'                             'usejsme'         => 'on',
                              'inline_chem'     => 'on',
                              'ltiauth'         => 'off',
                          );                           );
     my @toggles = ('uselcmath','usejsme');      my @toggles = ('canuse_pdfforms','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','uploadquota_placement',
                    'mysqltables_unofficial','mysqltables_community','mysqltables_textbook');                     'coursequota_official','coursequota_unofficial','coursequota_community',
     my @types = ('official','unofficial','community','textbook');                     'coursequota_textbook','coursequota_placement','mysqltables_official',
                      'mysqltables_unofficial','mysqltables_community','mysqltables_textbook',
                      'mysqltables_placement');
       my @types = ('official','unofficial','community','textbook','placement');
     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 15571  sub modify_coursedefaults { Line 20571  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 15583  sub modify_coursedefaults { Line 20583  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 15718  sub modify_coursedefaults { Line 20718  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);
     if ($putresult eq 'ok') {      if ($putresult eq 'ok') {
         if (keys(%changes) > 0) {          if (keys(%changes) > 0) {
             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{'canuse_pdfforms'}) || ($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 ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine',
                                     'ltiauth') {
                     if ($changes{$item}) {                      if ($changes{$item}) {
                         $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item};                          $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item};
                     }                      }
Line 15758  sub modify_coursedefaults { Line 20802  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 15770  sub modify_coursedefaults { Line 20821  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 15778  sub modify_coursedefaults { Line 20843  sub modify_coursedefaults {
             }              }
             $resulttext = &mt('Changes made:').'<ul>';              $resulttext = &mt('Changes made:').'<ul>';
             foreach my $item (sort(keys(%changes))) {              foreach my $item (sort(keys(%changes))) {
                 if ($item eq 'uselcmath') {                  if ($item eq 'canuse_pdfforms') {
                       if ($env{'form.'.$item} eq '1') {
                           $resulttext .= '<li>'.&mt("Course/Community users can create/upload PDF forms set to 'on'").'</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('Course/Community users can create/upload PDF forms set to "off"').'</li>';
                       }
                   } elsif ($item eq 'uselcmath') {
                     if ($env{'form.'.$item} eq '1') {                      if ($env{'form.'.$item} eq '1') {
                         $resulttext .= '<li>'.&mt('Math preview uses LON-CAPA previewer (javascript), if supported by browser.').'</li>';                          $resulttext .= '<li>'.&mt('Math preview uses LON-CAPA previewer (javascript), if supported by browser.').'</li>';
                     } else {                      } else {
Line 15790  sub modify_coursedefaults { Line 20861  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 15803  sub modify_coursedefaults { Line 20880  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('Placement tests: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'placement'}.'</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('Placement tests: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'placement'}.'</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>'.
                                        '<li>'.&mt('Official courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'official'}.'</b>').'</li>'.                                         '<li>'.&mt('Official courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'official'}.'</b>').'</li>'.
                                        '<li>'.&mt('Unofficial courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'unofficial'}.'</b>').'</li>'.                                         '<li>'.&mt('Unofficial courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'unofficial'}.'</b>').'</li>'.
                                        '<li>'.&mt('Textbook courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'textbook'}.'</b>').'</li>'.                                         '<li>'.&mt('Textbook courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'textbook'}.'</b>').'</li>'.
                                          '<li>'.&mt('Placement tests: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'placement'}.'</b>').'</li>'.
                                        '<li>'.&mt('Communities: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'community'}.'</b>').'</li>'.                                         '<li>'.&mt('Communities: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'community'}.'</b>').'</li>'.
                                        '</ul>'.                                         '</ul>'.
                                        '</li>';                                         '</li>';
Line 15851  sub modify_coursedefaults { Line 20942  sub modify_coursedefaults {
                                     $resulttext .= &mt('Unofficial courses');                                      $resulttext .= &mt('Unofficial courses');
                                 } elsif ($type eq 'textbook') {                                  } elsif ($type eq 'textbook') {
                                     $resulttext .= &mt('Textbook courses');                                      $resulttext .= &mt('Textbook courses');
                                   } elsif ($type eq 'placement') {
                                       $resulttext .= &mt('Placement tests');
                                 }                                  }
                                 $resulttext .= ' -- '.$display.'</li>';                                  $resulttext .= ' -- '.$display.'</li>';
                             }                              }
Line 15886  sub modify_coursedefaults { Line 20979  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 15902  sub modify_coursedefaults { Line 21029  sub modify_coursedefaults {
 sub modify_selfenrollment {  sub modify_selfenrollment {
     my ($dom,$lastactref,%domconfig) = @_;      my ($dom,$lastactref,%domconfig) = @_;
     my ($resulttext,$errors,%changes,%selfenrollhash,%ordered);      my ($resulttext,$errors,%changes,%selfenrollhash,%ordered);
     my @types = ('official','unofficial','community','textbook');      my @types = ('official','unofficial','community','textbook','placement');
     my %titles = &tool_titles();      my %titles = &tool_titles();
     my %descs = &Apache::lonuserutils::selfenroll_default_descs();      my %descs = &Apache::lonuserutils::selfenroll_default_descs();
     ($ordered{'admin'},my $titlesref) = &Apache::lonuserutils::get_selfenroll_titles();      ($ordered{'admin'},my $titlesref) = &Apache::lonuserutils::get_selfenroll_titles();
Line 16357  sub modify_wafproxy { Line 21484  sub modify_wafproxy {
                 }                  }
             }              }
             $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'<ul>';              $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'<ul>';
             foreach my $item ('alias','remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {              foreach my $item ('alias','saml','remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
                 if ($changes{$item}) {                  if ($changes{$item}) {
                     if ($item eq 'alias') {                      if ($item eq 'alias') {
                         my $numaliased = 0;                          my $numaliased = 0;
Line 16444  sub modify_wafproxy { Line 21571  sub modify_wafproxy {
                     }                      }
                 }                  }
             }              }
               $output .= '</ul>';
         } else {          } else {
             $output = '<span class="LC_error">'.              $output = '<span class="LC_error">'.
                       &mt('An error occurred: [_1]',$putresult).'</span>';                        &mt('An error occurred: [_1]',$putresult).'</span>';
Line 16487  sub modify_usersessions { Line 21615  sub modify_usersessions {
                 );                  );
     my @prefixes = ('remote','hosted','spares');      my @prefixes = ('remote','hosted','spares');
     my @lcversions = &Apache::lonnet::all_loncaparevs();      my @lcversions = &Apache::lonnet::all_loncaparevs();
     my (%by_ip,%by_location,@intdoms);      my (%by_ip,%by_location,@intdoms,@instdoms);
     &build_location_hashes(\@intdoms,\%by_ip,\%by_location);      &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
     my @locations = sort(keys(%by_location));      my @locations = sort(keys(%by_location));
     my (%defaultshash,%changes);      my (%defaultshash,%changes);
     foreach my $prefix (@prefixes) {      foreach my $prefix (@prefixes) {
Line 16777  sub modify_usersessions { Line 21905  sub modify_usersessions {
                         } else {                          } else {
                             foreach my $type (@{$types{$prefix}}) {                              foreach my $type (@{$types{$prefix}}) {
                                 if (defined($changes{$prefix}{$type})) {                                  if (defined($changes{$prefix}{$type})) {
                                     my $newvalue;                                      my ($newvalue,$notinuse);
                                     if (ref($defaultshash{'usersessions'}) eq 'HASH') {                                      if (ref($defaultshash{'usersessions'}) eq 'HASH') {
                                         if (ref($defaultshash{'usersessions'}{$prefix})) {                                          if (ref($defaultshash{'usersessions'}{$prefix})) {
                                             if ($type eq 'version') {                                              if ($type eq 'version') {
                                                 $newvalue = $defaultshash{'usersessions'}{$prefix}{$type};                                                  $newvalue = $defaultshash{'usersessions'}{$prefix}{$type};
                                             } elsif (ref($defaultshash{'usersessions'}{$prefix}{$type}) eq 'ARRAY') {                                              } else {
                                                 if (@{$defaultshash{'usersessions'}{$prefix}{$type}} > 0) {                                                  if (ref($defaultshash{'usersessions'}{$prefix}{$type}) eq 'ARRAY') {
                                                     $newvalue = join(', ',@{$defaultshash{'usersessions'}{$prefix}{$type}});                                                      if (@{$defaultshash{'usersessions'}{$prefix}{$type}} > 0) {
                                                           $newvalue = join(', ',@{$defaultshash{'usersessions'}{$prefix}{$type}});
                                                       }
                                                   } else {
                                                       $notinuse = 1;
                                                 }                                                  }
                                             }                                              }
                                         }                                          }
Line 16792  sub modify_usersessions { Line 21924  sub modify_usersessions {
                                     if ($newvalue eq '') {                                      if ($newvalue eq '') {
                                         if ($type eq 'version') {                                          if ($type eq 'version') {
                                             $resulttext .= '<li>'.&mt('[_1] set to: off',$lt{$type}).'</li>';                                              $resulttext .= '<li>'.&mt('[_1] set to: off',$lt{$type}).'</li>';
                                           } elsif ($notinuse) {
                                               $resulttext .= '<li>'.&mt('[_1] set to: not in use',$lt{$type}).'</li>';
                                         } else {                                          } else {
                                             $resulttext .= '<li>'.&mt('[_1] set to: none',$lt{$type}).'</li>';                                              $resulttext .= '<li>'.&mt('[_1] set to: none',$lt{$type}).'</li>';
                                         }                                          }
                                     } 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 16851  sub modify_usersessions { Line 21985  sub modify_usersessions {
     return $resulttext;      return $resulttext;
 }  }
   
   sub modify_ssl {
       my ($dom,$lastactref,%domconfig) = @_;
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
       my @locations = sort(keys(%by_location));
       my %servers = &Apache::lonnet::internet_dom_servers($dom);
       my (%defaultshash,%changes);
       my $action = 'ssl';
       my @prefixes = ('connto','connfrom','replication');
       foreach my $prefix (@prefixes) {
           $defaultshash{$action}{$prefix} = {};
       }
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my $resulttext;
       my %iphost = &Apache::lonnet::get_iphost();
       my @reptypes = ('certreq','nocertreq');
       my @connecttypes = ('dom','intdom','other');
       my %types = (
                     connto      => \@connecttypes,
                     connfrom    => \@connecttypes,
                     replication => \@reptypes,
                   );
       foreach my $prefix (sort(keys(%types))) {
           foreach my $type (@{$types{$prefix}}) {
               if (($prefix eq 'connto') || ($prefix eq 'connfrom')) {
                   my $value = 'yes';
                   if ($env{'form.'.$prefix.'_'.$type} =~ /^(no|req)$/) {
                       $value = $env{'form.'.$prefix.'_'.$type};
                   }
                   if (ref($domconfig{$action}) eq 'HASH') {
                       if (ref($domconfig{$action}{$prefix}) eq 'HASH') {
                           if ($domconfig{$action}{$prefix}{$type} ne '') {
                               if ($value ne $domconfig{$action}{$prefix}{$type}) {
                                   $changes{$prefix}{$type} = 1;
                               }
                               $defaultshash{$action}{$prefix}{$type} = $value;
                           } else {
                               $defaultshash{$action}{$prefix}{$type} = $value;
                               $changes{$prefix}{$type} = 1;
                           }
                       } else {
                           $defaultshash{$action}{$prefix}{$type} = $value;
                           $changes{$prefix}{$type} = 1;
                       }
                   } else {
                       $defaultshash{$action}{$prefix}{$type} = $value;
                       $changes{$prefix}{$type} = 1;
                   }
                   if (($type eq 'dom') && (keys(%servers) == 1)) {
                       delete($changes{$prefix}{$type});
                   } elsif (($type eq 'intdom') && (@instdoms == 1)) {
                       delete($changes{$prefix}{$type});
                   } elsif (($type eq 'other') && (keys(%by_location) == 0)) { 
                       delete($changes{$prefix}{$type});
                   }
               } elsif ($prefix eq 'replication') {
                   if (@locations > 0) {
                       my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'};
                       my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type);
                       my @okvals;
                       foreach my $val (@vals) {
                           if ($val =~ /:/) {
                               my @items = split(/:/,$val);
                               foreach my $item (@items) {
                                   if (ref($by_location{$item}) eq 'ARRAY') {
                                       push(@okvals,$item);
                                   }
                               }
                           } else {
                               if (ref($by_location{$val}) eq 'ARRAY') {
                                   push(@okvals,$val);
                               }
                           }
                       }
                       @okvals = sort(@okvals);
                       if (ref($domconfig{$action}) eq 'HASH') {
                           if (ref($domconfig{$action}{$prefix}) eq 'HASH') {
                               if (ref($domconfig{$action}{$prefix}{$type}) eq 'ARRAY') {
                                   if ($inuse == 0) {
                                       $changes{$prefix}{$type} = 1;
                                   } else {
                                       $defaultshash{$action}{$prefix}{$type} = \@okvals;
                                       my @changed = &Apache::loncommon::compare_arrays($domconfig{$action}{$prefix}{$type},$defaultshash{$action}{$prefix}{$type});
                                       if (@changed > 0) {
                                           $changes{$prefix}{$type} = 1;
                                       }
                                   }
                               } else {
                                   if ($inuse == 1) {
                                       $defaultshash{$action}{$prefix}{$type} = \@okvals;
                                       $changes{$prefix}{$type} = 1;
                                   }
                               }
                           } else {
                               if ($inuse == 1) {
                                   $defaultshash{$action}{$prefix}{$type} = \@okvals;
                                   $changes{$prefix}{$type} = 1;
                               }
                           }
                       } else {
                           if ($inuse == 1) {
                               $defaultshash{$action}{$prefix}{$type} = \@okvals;
                               $changes{$prefix}{$type} = 1;
                           }
                       }
                   }
               }
           }
       }
       if (keys(%changes)) {
           foreach my $prefix (keys(%changes)) {
               if (ref($changes{$prefix}) eq 'HASH') {
                   if (scalar(keys(%{$changes{$prefix}})) == 0) {
                       delete($changes{$prefix});
                   }
               } else {
                   delete($changes{$prefix});
               }
           }
       }
       my $nochgmsg = &mt('No changes made to LON-CAPA SSL settings');
       if (keys(%changes) > 0) {
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               if (ref($defaultshash{$action}) eq 'HASH') {
                   if (ref($defaultshash{$action}{'replication'}) eq 'HASH') {
                       $domdefaults{'replication'} = $defaultshash{$action}{'replication'};
                   }
                   if (ref($defaultshash{$action}{'connto'}) eq 'HASH') {
                       $domdefaults{'connto'} = $defaultshash{$action}{'connto'};
                   }
                   if (ref($defaultshash{$action}{'connfrom'}) eq 'HASH') {
                       $domdefaults{'connfrom'} = $defaultshash{$action}{'connfrom'};
                   }
               }
               my $cachetime = 24*60*60;
               &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'domdefaults'} = 1;
               }
               if (keys(%changes) > 0) {
                   my %titles = &ssl_titles();
                   $resulttext = &mt('Changes made:').'<ul>';
                   foreach my $prefix (@prefixes) {
                       if (ref($changes{$prefix}) eq 'HASH') {
                           $resulttext .= '<li>'.$titles{$prefix}.'<ul>';
                           foreach my $type (@{$types{$prefix}}) {
                               if (defined($changes{$prefix}{$type})) {
                                   my ($newvalue,$notinuse);
                                   if (ref($defaultshash{$action}) eq 'HASH') {
                                       if (ref($defaultshash{$action}{$prefix})) {
                                           if (($prefix eq 'connto') || ($prefix eq 'connfrom')) {
                                               $newvalue = $titles{$defaultshash{$action}{$prefix}{$type}};
                                           } else {
                                               if (ref($defaultshash{$action}{$prefix}{$type}) eq 'ARRAY') {
                                                   if (@{$defaultshash{$action}{$prefix}{$type}} > 0) {
                                                       $newvalue = join(', ',@{$defaultshash{$action}{$prefix}{$type}});
                                                   }
                                               } else {
                                                   $notinuse = 1;
                                               }
                                           }
                                       }
                                       if ($notinuse) {
                                           $resulttext .= '<li>'.&mt('[_1] set to: not in use',$titles{$type}).'</li>';
                                       } elsif ($newvalue eq '') {
                                           $resulttext .= '<li>'.&mt('[_1] set to: none',$titles{$type}).'</li>';
                                       } else {
                                           $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$titles{$type},$newvalue).'</li>';
                                       }
                                   }
                               }
                           }
                           $resulttext .= '</ul>';
                       }
                   }
               } else {
                   $resulttext = $nochgmsg;
               }
           } else {
               $resulttext = '<span class="LC_error">'.
                             &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } else {
           $resulttext = $nochgmsg;
       }
       return $resulttext;
   }
   
   sub modify_trust {
       my ($dom,$lastactref,%domconfig) = @_;
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
       my @locations = sort(keys(%by_location));
       my @prefixes = qw(content shared enroll othcoau coaurem domroles catalog reqcrs msg);
       my @types = ('exc','inc');
       my (%defaultshash,%changes);
       foreach my $prefix (@prefixes) {
           $defaultshash{'trust'}{$prefix} = {};
       }
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my $resulttext;
       foreach my $prefix (@prefixes) {
           foreach my $type (@types) {
               my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'};
               my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type);
               my @okvals;
               foreach my $val (@vals) {
                   if ($val =~ /:/) {
                       my @items = split(/:/,$val);
                       foreach my $item (@items) {
                           if (ref($by_location{$item}) eq 'ARRAY') {
                               push(@okvals,$item);
                           }
                       }
                   } else {
                       if (ref($by_location{$val}) eq 'ARRAY') {
                           push(@okvals,$val);
                       }
                   }
               }
               @okvals = sort(@okvals);
               if (ref($domconfig{'trust'}) eq 'HASH') {
                   if (ref($domconfig{'trust'}{$prefix}) eq 'HASH') {
                       if (ref($domconfig{'trust'}{$prefix}{$type}) eq 'ARRAY') {
                           if ($inuse == 0) {
                               $changes{$prefix}{$type} = 1;
                           } else {
                               $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                               my @changed = &Apache::loncommon::compare_arrays($domconfig{'trust'}{$prefix}{$type},$defaultshash{'trust'}{$prefix}{$type});
                               if (@changed > 0) {
                                   $changes{$prefix}{$type} = 1;
                               }
                           }
                       } else {
                           if ($inuse == 1) {
                               $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                               $changes{$prefix}{$type} = 1;
                           }
                       }
                   } else {
                       if ($inuse == 1) {
                           $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                           $changes{$prefix}{$type} = 1;
                       }
                   }
               } else {
                   if ($inuse == 1) {
                       $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                       $changes{$prefix}{$type} = 1;
                   }
               }
           }
       }
       my $nochgmsg = &mt('No changes made to trust settings.');
       if (keys(%changes) > 0) {
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               if (ref($defaultshash{'trust'}) eq 'HASH') {
                   foreach my $prefix (@prefixes) {
                       if (ref($defaultshash{'trust'}{$prefix}) eq 'HASH') {
                           $domdefaults{'trust'.$prefix} = $defaultshash{'trust'}{$prefix};
                       }
                   }
               }
               my $cachetime = 24*60*60;
               &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
               &Apache::lonnet::do_cache_new('trust',$dom,$defaultshash{'trust'},3600);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'domdefaults'} = 1;
                   $lastactref->{'trust'} = 1;
               }
               if (keys(%changes) > 0) {
                   my %lt = &trust_titles();
                   $resulttext = &mt('Changes made:').'<ul>';
                   foreach my $prefix (@prefixes) {
                       if (ref($changes{$prefix}) eq 'HASH') {
                           $resulttext .= '<li>'.$lt{$prefix}.'<ul>';
                           foreach my $type (@types) {
                               if (defined($changes{$prefix}{$type})) {
                                   my ($newvalue,$notinuse);
                                   if (ref($defaultshash{'trust'}) eq 'HASH') {
                                       if (ref($defaultshash{'trust'}{$prefix})) {
                                           if (ref($defaultshash{'trust'}{$prefix}{$type}) eq 'ARRAY') {
                                               if (@{$defaultshash{'trust'}{$prefix}{$type}} > 0) {
                                                   $newvalue = join(', ',@{$defaultshash{'trust'}{$prefix}{$type}});
                                               }
                                           } else {
                                               $notinuse = 1;
                                           }
                                       }
                                   }
                                   if ($notinuse) {
                                       $resulttext .= '<li>'.&mt('[_1] set to: not in use',$lt{$type}).'</li>';
                                   } elsif ($newvalue eq '') {
                                       $resulttext .= '<li>'.&mt('[_1] set to: none',$lt{$type}).'</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'</li>';
                                   }
                               }
                           }
                           $resulttext .= '</ul>';
                       }
                   }
                   $resulttext .= '</ul>';
               } else {
                   $resulttext = $nochgmsg;
               }
           } else {
               $resulttext = '<span class="LC_error">'.
                             &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } else {
           $resulttext = $nochgmsg;
       }
       return $resulttext;
   }
   
 sub modify_loadbalancing {  sub modify_loadbalancing {
     my ($dom,%domconfig) = @_;      my ($dom,%domconfig) = @_;
     my $primary_id = &Apache::lonnet::domain($dom,'primary');      my $primary_id = &Apache::lonnet::domain($dom,'primary');
Line 17085  sub modify_loadbalancing { Line 22539  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>';
                               }
                         }                          }
                     }                      }
                 }                  }
Line 17675  function updateNewSpares(formname,lonhos Line 23134  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 17809  function toggleDisplay(domForm,caller) { Line 23268  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 17864  sub devalidate_remote_domconfs { Line 23323  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',
                       'directorysrch','passwdconf','cats','proxyalias','proxysaml',                        'directorysrch','passwdconf','cats','proxyalias','proxysaml',
                       'ipaccess');                        'ipaccess','trust');
     my %cache_by_lonhost;      my %cache_by_lonhost;
     if (exists($cachekeys->{'samllanding'})) {      if (exists($cachekeys->{'samllanding'})) {
         if (ref($cachekeys->{'samllanding'}) eq 'HASH') {          if (ref($cachekeys->{'samllanding'}) eq 'HASH') {

Removed from v.1.160.6.114  
changed lines
  Added in v.1.428


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