Diff for /loncom/interface/domainprefs.pm between versions 1.160.6.84.2.8 and 1.416

version 1.160.6.84.2.8, 2017/11/01 08:56:25 version 1.416, 2022/11/11 02:30:19
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 170  use Apache::loncoursequeueadmin(); Line 170  use Apache::loncoursequeueadmin();
 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;
   
 my $registered_cleanup;  my $registered_cleanup;
 my $modified_urls;  my $modified_urls;
Line 217  sub handler { Line 220  sub handler {
                 'serverstatuses','requestcourses','helpsettings',                  'serverstatuses','requestcourses','helpsettings',
                 'coursedefaults','usersessions','loadbalancing',                  'coursedefaults','usersessions','loadbalancing',
                 'requestauthor','selfenrollment','inststatus',                  'requestauthor','selfenrollment','inststatus',
                 'ltitools'],$dom);                  'ltitools','ssl','trust','lti','ltisec','privacy','passwords',
                   'proctoring','wafproxy','ipaccess'],$dom);
       my %encconfig =
           &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1);
     if (ref($domconfig{'ltitools'}) eq 'HASH') {      if (ref($domconfig{'ltitools'}) eq 'HASH') {
         my %encconfig =  
             &Apache::lonnet::get_dom('encconfig',['ltitools'],$dom);  
         if (ref($encconfig{'ltitools'}) eq 'HASH') {          if (ref($encconfig{'ltitools'}) eq 'HASH') {
             foreach my $id (keys(%{$domconfig{'ltitools'}})) {              foreach my $id (keys(%{$domconfig{'ltitools'}})) {
                 if (ref($domconfig{'ltitools'}{$id}) eq 'HASH') {                  if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') &&
                       (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) {
                     foreach my $item ('key','secret') {                      foreach my $item ('key','secret') {
                         $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item};                          $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item};
                     }                      }
Line 231  sub handler { Line 236  sub handler {
             }              }
         }          }
     }      }
     my @prefs_order = ('rolecolors','login','defaults','quotas','autoenroll',      if (ref($domconfig{'lti'}) eq 'HASH') {
                        'autoupdate','autocreate','directorysrch','contacts',          if (ref($encconfig{'lti'}) eq 'HASH') {
                        'usercreation','selfcreation','usermodification','scantron',              foreach my $id (keys(%{$domconfig{'lti'}})) {
                        'requestcourses','requestauthor','coursecategories',                  if ((ref($domconfig{'lti'}{$id}) eq 'HASH') &&
                        'serverstatuses','helpsettings','coursedefaults',                      (ref($encconfig{'lti'}{$id}) eq 'HASH')) {
                        'ltitools','selfenrollment','usersessions');                      foreach my $item ('key','secret') {
                           $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item};
                       }
                   }
               }
           }
       }
       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',
                          'quotas','autoenroll','autoupdate','autocreate','directorysrch',
                          'contacts','privacy','usercreation','selfcreation',
                          'usermodification','scantron','requestcourses','requestauthor',
                          'coursecategories','serverstatuses','helpsettings','coursedefaults',
                          '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 267  sub handler { Line 314  sub handler {
                                  {col1 => 'Log-in Help',                                   {col1 => 'Log-in Help',
                                   col2 => 'Value'},                                    col2 => 'Value'},
                                  {col1 => 'Custom HTML in document head',                                   {col1 => 'Custom HTML in document head',
                                   col2 => 'Value'}],                                    col2 => 'Value'},
                                    {col1 => 'SSO',
                                     col2 => 'Dual login: SSO and non-SSO options'},
                                   ],
                       print => \&print_login,                        print => \&print_login,
                       modify => \&modify_login,                        modify => \&modify_login,
                     },                      },
Line 276  sub handler { Line 326  sub handler {
                       help => 'Domain_Configuration_LangTZAuth',                        help => 'Domain_Configuration_LangTZAuth',
                       header => [{col1 => 'Setting',                        header => [{col1 => 'Setting',
                                   col2 => 'Value'},                                    col2 => 'Value'},
                                  {col1 => 'Internal Authentication',  
                                   col2 => 'Value'},  
                                  {col1 => 'Institutional user types',                                   {col1 => 'Institutional user types',
                                   col2 => 'Assignable to e-mail usernames'}],                                    col2 => 'Name displayed'},
                                    {col1 => 'Mapping for missing usernames via standard log-in',
                                     col2 => 'Rules in use'}],
                       print => \&print_defaults,                        print => \&print_defaults,
                       modify => \&modify_defaults,                        modify => \&modify_defaults,
                     },                      },
           'wafproxy' =>
                       { text => 'Web Application Firewall/Reverse Proxy',
                         help => 'Domain_Configuration_WAF_Proxy',
                         header => [{col1 => 'Domain(s)',
                                     col2 => 'Servers and WAF/Reverse Proxy alias(es)',
                                    },
                                    {col1 => 'Domain(s)',
                                     col2 => 'WAF Configuration',}],
                         print => \&print_wafproxy,
                         modify => \&modify_wafproxy,
                       },
           'passwords' =>
                       { text => 'Passwords (Internal authentication)',
                         help => 'Domain_Configuration_Passwords',
                         header => [{col1 => 'Resetting Forgotten Password',
                                     col2 => 'Settings'},
                                    {col1 => 'Encryption of Stored Passwords (Internal Auth)',
                                     col2 => 'Settings'},
                                    {col1 => 'Rules for LON-CAPA Passwords',
                                     col2 => 'Settings'},
                                    {col1 => 'Course Owner Changing Student Passwords',
                                     col2 => 'Settings'}],
                         print => \&print_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 337  sub handler { Line 412  sub handler {
                                 col2 => 'Value',},                                  col2 => 'Value',},
                                {col1 => 'Recipient(s) for notifications',                                 {col1 => 'Recipient(s) for notifications',
                                 col2 => 'Value',},                                  col2 => 'Value',},
                                  {col1 => 'Nightly status check e-mail',
                                   col2 => 'Settings',},
                                {col1 => 'Ask helpdesk form settings',                                 {col1 => 'Ask helpdesk form settings',
                                 col2 => 'Value',},],                                  col2 => 'Value',},],
                     print => \&print_contacts,                      print => \&print_contacts,
Line 361  sub handler { Line 438  sub handler {
                                 col2 => 'Enabled?'},                                  col2 => 'Enabled?'},
                                {col1 => 'Institutional user type (login/SSO self-creation)',                                 {col1 => 'Institutional user type (login/SSO self-creation)',
                                 col2 => 'Information user can enter'},                                  col2 => 'Information user can enter'},
                                {col1 => 'Self-creation with e-mail as username',                                 {col1 => 'Self-creation with e-mail verification',
                                 col2 => 'Settings'}],                                  col2 => 'Settings'}],
                     print => \&print_selfcreation,                      print => \&print_selfcreation,
                     modify => \&modify_selfcreation,                      modify => \&modify_selfcreation,
Line 377  sub handler { Line 454  sub handler {
                     modify => \&modify_usermodification,                      modify => \&modify_usermodification,
                   },                    },
         'scantron' =>          'scantron' =>
                   { text => 'Bubblesheet format file',                    { text => 'Bubblesheet format',
                     help => 'Domain_Configuration_Scantron_Format',                      help => 'Domain_Configuration_Scantron_Format',
                     header => [ {col1 => 'Item',                      header => [ {col1 => 'Bubblesheet format file',
                                  col2 => '',                                   col2 => ''},
                               }],                                  {col1 => 'Bubblesheet data upload formats',
                                    col2 => 'Settings'}],
                     print => \&print_scantron,                      print => \&print_scantron,
                     modify => \&modify_scantron,                      modify => \&modify_scantron,
                   },                    },
Line 466  sub handler { Line 544  sub handler {
                   print => \&print_selfenrollment,                    print => \&print_selfenrollment,
                   modify => \&modify_selfenrollment,                    modify => \&modify_selfenrollment,
                  },                   },
           'privacy' => 
                    {text   => 'Availability of User Information',
                     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 489  sub handler { Line 581  sub handler {
                   print => \&print_loadbalancing,                    print => \&print_loadbalancing,
                   modify => \&modify_loadbalancing,                    modify => \&modify_loadbalancing,
                  },                   },
         'ltitools' =>          'ltitools' => 
                  {text => 'External Tools (LTI)',                   {text => 'External Tools (LTI)',
                   help => 'Domain_Configuration_LTI_Tools',                    help => 'Domain_Configuration_LTI_Tools',
                   header => [{col1 => 'Setting',                    header => [{col1 => 'Setting',
Line 497  sub handler { Line 589  sub handler {
                   print => \&print_ltitools,                    print => \&print_ltitools,
                   modify => \&modify_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',
                           help => 'Domain_Configuration_IP_Access',
                           header => [{col1 => 'Setting',
                                       col2 => 'Value'},],
                           print  => \&print_ipaccess,
                           modify => \&modify_ipaccess,
                          },
     );      );
     if (keys(%servers) > 1) {      if (keys(%servers) > 1) {
         $prefs{'login'}  = { text   => 'Log-in page options',          $prefs{'login'}  = { text   => 'Log-in page options',
Line 504  sub handler { Line 664  sub handler {
                             header => [{col1 => 'Log-in Service',                              header => [{col1 => 'Log-in Service',
                                         col2 => 'Server Setting',},                                          col2 => 'Server Setting',},
                                        {col1 => 'Log-in Page Items',                                         {col1 => 'Log-in Page Items',
                                         col2 => ''},                                          col2 => 'Settings'},
                                        {col1 => 'Log-in Help',                                         {col1 => 'Log-in Help',
                                         col2 => 'Value'},                                          col2 => 'Value'},
                                        {col1 => 'Custom HTML in document head',                                         {col1 => 'Custom HTML in document head',
                                         col2 => 'Value'}],                                          col2 => 'Value'},
                                          {col1 => 'SSO',
                                           col2 => 'Dual login: SSO and non-SSO options'},
                                         ],
                             print => \&print_login,                              print => \&print_login,
                             modify => \&modify_login,                              modify => \&modify_login,
                            };                             };
Line 549  $javascript_validations Line 712  $javascript_validations
 </script>  </script>
 $coursebrowserjs  $coursebrowserjs
 END  END
           } elsif (grep(/^ipaccess$/,@actions)) {
               $js .= &Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'});
           }
           if (grep(/^selfcreation$/,@actions)) {
               $js .= &selfcreate_javascript();
         }          }
         if (grep(/^contacts$/,@actions)) {          if (grep(/^contacts$/,@actions)) {
             $js .= &contacts_javascript();              $js .= &contacts_javascript();
         }          }
           if (grep(/^scantron$/,@actions)) {
               $js .= &scantron_javascript();
           }
         &Apache::lonconfigsettings::display_settings($r,$dom,$phase,$context,\@prefs_order,\%prefs,\%domconfig,$confname,$js);          &Apache::lonconfigsettings::display_settings($r,$dom,$phase,$context,\@prefs_order,\%prefs,\%domconfig,$confname,$js);
     } else {      } else {
 # check if domconfig user exists for the domain.  # check if domconfig user exists for the domain.
Line 646  sub process_changes { Line 817  sub process_changes {
     } elsif ($action eq 'usercreation') {      } elsif ($action eq 'usercreation') {
         $output = &modify_usercreation($dom,%domconfig);          $output = &modify_usercreation($dom,%domconfig);
     } elsif ($action eq 'selfcreation') {      } elsif ($action eq 'selfcreation') {
         $output = &modify_selfcreation($dom,%domconfig);          $output = &modify_selfcreation($dom,$lastactref,%domconfig);
     } elsif ($action eq 'usermodification') {      } elsif ($action eq 'usermodification') {
         $output = &modify_usermodification($dom,%domconfig);          $output = &modify_usermodification($dom,%domconfig);
     } elsif ($action eq 'contacts') {      } elsif ($action eq 'contacts') {
Line 675  sub process_changes { Line 846  sub process_changes {
         $output = &modify_loadbalancing($dom,%domconfig);          $output = &modify_loadbalancing($dom,%domconfig);
     } elsif ($action eq 'ltitools') {      } elsif ($action eq 'ltitools') {
         $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);          $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq '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,%domconfig);
       } elsif ($action eq 'passwords') {
           $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);
       } elsif ($action eq 'wafproxy') {
           $output = &modify_wafproxy($dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'ipaccess') {
           $output = &modify_ipaccess($dom,$lastactref,%domconfig);
     }      }
     return $output;      return $output;
 }  }
Line 687  sub print_config_box { Line 874  sub print_config_box {
         $output = &coursecategories_javascript($settings);          $output = &coursecategories_javascript($settings);
     } elsif ($action eq 'defaults') {      } elsif ($action eq 'defaults') {
         $output = &defaults_javascript($settings);           $output = &defaults_javascript($settings); 
       } elsif ($action eq 'passwords') {
           $output = &passwords_javascript($action);
     } elsif ($action eq 'helpsettings') {      } elsif ($action eq 'helpsettings') {
         my (%privs,%levelscurrent);          my (%privs,%levelscurrent);
         my %full=();          my %full=();
Line 701  sub print_config_box { Line 890  sub print_config_box {
         &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);
         $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 .= &ltitools_javascript($settings);
       } elsif ($action eq 'lti') {
           $output .= &passwords_javascript('secrets')."\n".
                      &lti_javascript($dom,$settings);
       } elsif ($action eq 'proctoring') {
           $output .= &proctoring_javascript($settings);
       } elsif ($action eq 'wafproxy') {
           $output .= &wafproxy_javascript($dom);
       } elsif ($action eq 'autoupdate') {
           $output .= &autoupdate_javascript();
       } elsif ($action eq 'autoenroll') {
           $output .= &autoenroll_javascript();
       } elsif ($action eq 'login') {
           $output .= &saml_javascript();
       } elsif ($action eq 'ipaccess') {
           $output .= &ipaccess_javascript($settings);
     }      }
     $output .=      $output .=
          '<table class="LC_nested_outer">           '<table class="LC_nested_outer">
           <tr>            <tr>
            <th align="left" valign="middle"><span class="LC_nobreak">'.             <th class="LC_left_item LC_middle"><span class="LC_nobreak">'.
            &mt($item->{text}).'&nbsp;'.             &mt($item->{text}).'&nbsp;'.
            &Apache::loncommon::help_open_topic($item->{'help'}).'</span></th>'."\n".             &Apache::loncommon::help_open_topic($item->{'help'}).'</span></th>'."\n".
           '</tr>';            '</tr>';
Line 719  sub print_config_box { Line 925  sub print_config_box {
     if ($numheaders > 1) {      if ($numheaders > 1) {
         my $colspan = '';          my $colspan = '';
         my $rightcolspan = '';          my $rightcolspan = '';
           my $leftnobr = '';
         if (($action eq 'rolecolors') || ($action eq 'defaults') ||          if (($action eq 'rolecolors') || ($action eq 'defaults') ||
             ($action eq 'directorysrch') ||              ($action eq 'directorysrch') ||
             (($action eq 'login') && ($numheaders < 4))) {              (($action eq 'login') && ($numheaders < 5))) {
             $colspan = ' colspan="2"';              $colspan = ' colspan="2"';
         }          }
         if ($action eq 'usersessions') {          if ($action eq 'usersessions') {
             $rightcolspan = ' colspan="3"';               $rightcolspan = ' colspan="3"'; 
         }          }
           if ($action eq 'passwords') {
               $leftnobr = ' LC_nobreak';
           }
         $output .= '          $output .= '
           <tr>            <tr>
            <td>             <td>
             <table class="LC_nested">              <table class="LC_nested">
              <tr class="LC_info_row">               <tr class="LC_info_row">
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[0]->{'col1'}).'</td>                <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[0]->{'col1'}).'</td>
               <td class="LC_right_item"'.$rightcolspan.'>'.&mt($item->{'header'}->[0]->{'col2'}).'</td>                <td class="LC_right_item"'.$rightcolspan.'>'.&mt($item->{'header'}->[0]->{'col2'}).'</td>
              </tr>';               </tr>';
         $rowtotal ++;          $rowtotal ++;
         if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||          if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||
             ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||              ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||
             ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'directorysrch') ||              ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') ||
             ($action eq 'helpsettings') || ($action eq 'contacts')) {              ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') ||
               ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') || ($action eq 'lti')) {
             $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);              $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);
           } elsif ($action eq 'passwords') {
               $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal);
         } elsif ($action eq 'coursecategories') {          } elsif ($action eq 'coursecategories') {
             $output .= $item->{'print'}->('top',$dom,$item,$settings,\$rowtotal);              $output .= $item->{'print'}->('top',$dom,$item,$settings,\$rowtotal);
           } elsif ($action eq 'scantron') {
               $output .= $item->{'print'}->($r,'top',$dom,$confname,$settings,\$rowtotal);
         } elsif ($action eq 'login') {          } elsif ($action eq 'login') {
             if ($numheaders == 4) {              if ($numheaders == 5) {
                 $colspan = ' colspan="2"';                  $colspan = ' colspan="2"';
                 $output .= &print_login('service',$dom,$confname,$phase,$settings,\$rowtotal);                  $output .= &print_login('service',$dom,$confname,$phase,$settings,\$rowtotal);
             } else {              } else {
Line 769  sub print_config_box { Line 984  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 'defaults')) {              ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'defaults') ||
               ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti')) {
             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') {
                   $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 788  sub print_config_box { Line 1037  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);
             } else {                  } elsif (($action eq 'contacts') || ($action eq 'privacy') || 
                 $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);                           ($action eq 'passwords') || ($action eq 'lti')) {
                       if ($action eq 'passwords') {
                           $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal);
                       } else {
                           $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal);
                       }
                       $output .= '
                </tr>
               </table>
              </td>
             </tr>
             <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">
                 <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";
                       if ($action eq 'passwords') {
                           $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal);
                       } else {
                           $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
                       }
                       $output .= '
               </table>
             </td>
            </tr>
            <tr>';
                   } else {
                       $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')) {
             $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);              $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
           } elsif ($action eq 'scantron') {
               $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 == 4) {              if ($numheaders == 5) {
                 $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'                  $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'
            </table>             </table>
           </td>            </td>
Line 823  sub print_config_box { Line 1125  sub print_config_box {
            <td>             <td>
             <table class="LC_nested">              <table class="LC_nested">
              <tr class="LC_info_row">';               <tr class="LC_info_row">';
             if ($numheaders == 4) {              if ($numheaders == 5) {
                 $output .= '                  $output .= '
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>                <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>
               <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td>                <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td>
Line 835  sub print_config_box { Line 1137  sub print_config_box {
              </tr>';               </tr>';
             }              }
             $rowtotal ++;              $rowtotal ++;
             $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal);              $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal).'
              </table>
             </td>
            </tr>
            <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">';
               if ($numheaders == 5) {
                   $output .= '
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[4]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[4]->{'col2'}).'</td>
                </tr>';
               } else {
                   $output .= '
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td>
                </tr>';
               }
               $rowtotal ++;
               $output .= &print_login('saml',$dom,$confname,$phase,$settings,\$rowtotal);
         } elsif ($action eq 'requestcourses') {          } elsif ($action eq 'requestcourses') {
             $output .= &print_requestmail($dom,$action,$settings,\$rowtotal);              $output .= &print_requestmail($dom,$action,$settings,\$rowtotal);
             $rowtotal ++;              $rowtotal ++;
Line 868  sub print_config_box { Line 1190  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 884  sub print_config_box { Line 1206  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 914  sub print_config_box { Line 1236  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 946  sub print_config_box { Line 1268  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 954  sub print_config_box { Line 1276  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 'ltitools')) {                   ($action eq 'ltitools') || ($action eq 'proctoring') ||
                    ($action eq 'ipaccess')) {
             $output .= $item->{'print'}->($dom,$settings,\$rowtotal);              $output .= $item->{'print'}->($dom,$settings,\$rowtotal);
         } elsif ($action eq 'scantron') {  
             $output .= &print_scantronformat($r,$dom,$confname,$settings,\$rowtotal);  
         }          }
     }      }
     $output .= '      $output .= '
Line 971  sub print_config_box { Line 1292  sub print_config_box {
   
 sub print_login {  sub print_login {
     my ($caller,$dom,$confname,$phase,$settings,$rowtotal) = @_;      my ($caller,$dom,$confname,$phase,$settings,$rowtotal) = @_;
     my ($css_class,$datatable);      my ($css_class,$datatable,$switchserver,%lt);
     my %choices = &login_choices();      my %choices = &login_choices();
       if (($caller eq 'help') || ($caller eq 'headtag') || ($caller eq 'saml')) {
           %lt = &login_file_options();
           $switchserver = &check_switchserver($dom,$confname);
       }
     if ($caller eq 'service') {      if ($caller eq 'service') {
         my %servers = &Apache::lonnet::internet_dom_servers($dom);          my %servers = &Apache::lonnet::internet_dom_servers($dom);
         my $choice = $choices{'disallowlogin'};          my $choice = $choices{'disallowlogin'};
         $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 1060  sub print_login { Line 1384  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 1101  sub print_login { Line 1426  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 1167  sub print_login { Line 1499  sub print_login {
         $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext);          $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext);
         $datatable .= '</tr></table></td></tr>';          $datatable .= '</tr></table></td></tr>';
     } elsif ($caller eq 'help') {      } elsif ($caller eq 'help') {
         my ($defaulturl,$defaulttype,%url,%type,%lt,%langchoices);          my ($defaulturl,$defaulttype,%url,%type,%langchoices);
         my $switchserver = &check_switchserver($dom,$confname);  
         my $itemcount = 1;          my $itemcount = 1;
         $defaulturl = '/adm/loginproblems.html';          $defaulturl = '/adm/loginproblems.html';
         $defaulttype = 'default';          $defaulttype = 'default';
         %lt = &Apache::lonlocal::texthash (  
                      del     => 'Delete?',  
                      rep     => 'Replace:',  
                      upl     => 'Upload:',  
                      default => 'Default',  
                      custom  => 'Custom',  
                                              );  
         %langchoices = &Apache::lonlocal::texthash(&get_languages_hash());          %langchoices = &Apache::lonlocal::texthash(&get_languages_hash());
         my @currlangs;          my @currlangs;
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
Line 1260  sub print_login { Line 1584  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 1275  sub print_login { Line 1599  sub print_login {
                 }                  }
             }              }
         }          }
         my %lt = &Apache::lonlocal::texthash(  
                                                del  => 'Delete?',  
                                                rep  => 'Replace:',  
                                                upl  => 'Upload:',  
                                                curr => 'View contents',  
                                                none => 'None',  
         );  
         my $switchserver = &check_switchserver($dom,$confname);  
         foreach my $lonhost (sort(keys(%domservers))) {          foreach my $lonhost (sort(keys(%domservers))) {
             my $exempt = &check_exempt_addresses($currexempt{$lonhost});              my $exempt = &check_exempt_addresses($currexempt{$lonhost});
             $datatable .= '<tr><td>'.$domservers{$lonhost}.'</td>';              $datatable .= '<tr><td>'.$domservers{$lonhost}.'</td>';
Line 1303  sub print_login { Line 1619  sub print_login {
             } else {              } else {
                 $datatable .= '<input type="file" name="loginheadtag_'.$lonhost.'" />';                  $datatable .= '<input type="file" name="loginheadtag_'.$lonhost.'" />';
             }              }
             $datatable .= '</td><td><input type="textbox" name="loginheadtagexempt_'.$lonhost.'" value="'.$exempt.'" /></td></tr>';              $datatable .= '</td><td><input type="text" name="loginheadtagexempt_'.$lonhost.'" value="'.$exempt.'" /></td></tr>';
           }
           $datatable .= '</table></td></tr>';
       } elsif ($caller eq 'saml') {
           my %domservers = &Apache::lonnet::get_servers($dom);
           $datatable .= '<tr><td colspan="3" style="text-align: left">'.
                         '<table><tr><th>'.$choices{'hostid'}.'</th>'.
                         '<th>'.$choices{'samllanding'}.'</th>'.
                         '<th>'.$choices{'samloptions'}.'</th></tr>'."\n";
           my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso,%styleon,%styleoff);
           foreach my $lonhost (keys(%domservers)) {
               $samlurl{$lonhost} = '/adm/sso';
               $styleon{$lonhost} = 'display:none';
               $styleoff{$lonhost} = '';
           }
           if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) {
               foreach my $lonhost (keys(%{$settings->{'saml'}})) {
                   if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') {
                       $saml{$lonhost} = 1;
                       $samltext{$lonhost} = $settings->{'saml'}{$lonhost}{'text'};
                       $samlimg{$lonhost} = $settings->{'saml'}{$lonhost}{'img'};
                       $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'};
                       $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'};
                       $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'};
                       $samlwindow{$lonhost} = $settings->{'saml'}{$lonhost}{'window'};
                       $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'};
                       $styleon{$lonhost} = '';
                       $styleoff{$lonhost} = 'display:none';
                   } else {
                       $styleon{$lonhost} = 'display:none';
                       $styleoff{$lonhost} = '';
                   }
               }
           }
           my $itemcount = 1;
           foreach my $lonhost (sort(keys(%domservers))) {
               my $samlon = ' ';
               my $samloff = ' checked="checked" ';
               if ($saml{$lonhost}) {
                   $samlon = $samloff;
                   $samloff = ' ';
               }
               my $samlwinon = '';
               my $samlwinoff = ' checked="checked"';
               if ($samlwindow{$lonhost}) {
                   $samlwinon = $samlwinoff;
                   $samlwinoff = '';
               }
               my $css_class = $itemcount%2?' class="LC_odd_row"':'';
               $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.$domservers{$lonhost}.'</span></td>'.
                             '<td><span class="LC_nobreak"><label><input type="radio" name="saml_'.$lonhost.'"'.$samloff.
                             'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="0" />'.
                             &mt('No').'</label>'.('&nbsp;'x2).
                             '<label><input type="radio" name="saml_'.$lonhost.'"'.$samlon.
                             'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="1" />'.
                             &mt('Yes').'</label></span></td>'.
                             '<td id="samloptionson_'.$lonhost.'" style="'.$styleon{$lonhost}.'" width="100%">'.
                             '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th></tr>'.
                             '<tr><th>'.&mt('Text').'</th><th>'.&mt('Image').'</th>'.
                             '<th>'.&mt('Alt Text').'</th></tr>'.
                             '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="20" value="'.
                             $samltext{$lonhost}.'" /></td><td>';
               if ($samlimg{$lonhost}) {
                   $datatable .= '<img src="'.$samlimg{$lonhost}.'" /><br />'.
                                 '<span class="LC_nobreak"><label>'.
                                 '<input type="checkbox" name="saml_img_del" value="'.$lonhost.'" />'.
                                 $lt{'del'}.'</label>&nbsp;'.$lt{'rep'}.'</span>';
               } else {
                   $datatable .= $lt{'upl'};
               }
               $datatable .='<br />';
               if ($switchserver) {
                   $datatable .= &mt('Upload to library server: [_1]',$switchserver);
               } else {
                   $datatable .= '<input type="file" name="saml_img_'.$lonhost.'" />';
               }
               $datatable .= '</td>'.
                             '<td><input type="text" name="saml_alt_'.$lonhost.'" size="25" '.
                             'value="'.$samlalt{$lonhost}.'" /></td></tr></table><br />'.
                             '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th><th align="center">'.
                             '<span class="LC_nobreak">'.&mt('Non-SSO').'</span></th></tr>'.
                             '<tr><th>'.&mt('URL').'</th><th>'.&mt('Tool Tip').'</th>'.
                             '<th>'.&mt('Pop-up if iframe').'</th><th>'.&mt('Text').'</th></tr>'.
                             '<tr'.$css_class.'>'.
                             '<td><input type="text" name="saml_url_'.$lonhost.'" size="30" '.
                             'value="'.$samlurl{$lonhost}.'" /></td>'.
                             '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="20">'.
                             $samltitle{$lonhost}.'</textarea></td>'.
                             '<td><label><input type="radio" name="saml_window_'.$lonhost.'" value=""'.$samlwinoff.'>'.
                             &mt('No').'</label>'.('&nbsp;'x2).'<label><input type="radio" '.
                             'name="saml_window_'.$lonhost.'" value="1"'.$samlwinon.'>'.&mt('Yes').'</label></td>'.
                             '<td><input type="text" name="saml_notsso_'.$lonhost.'" size="12" '.
                             'value="'.$samlnotsso{$lonhost}.'" /></td></tr>'.
                             '</table></td>'.
                             '<td id="samloptionsoff_'.$lonhost.'" style="'.$styleoff{$lonhost}.'" width="100%">&nbsp;</td></tr>';
              $itemcount ++;
         }          }
         $datatable .= '</table></td></tr>';          $datatable .= '</table></td></tr>';
     }      }
Line 1342  sub login_choices { Line 1753  sub login_choices {
             headtag       => "Custom markup",              headtag       => "Custom markup",
             action        => "Action",              action        => "Action",
             current       => "Current",              current       => "Current",
               samllanding   => "Dual login?",
               samloptions   => "Options",
               alttext       => "Alt text",
         );          );
     return %choices;      return %choices;
 }  }
   
   sub login_file_options {
         return &Apache::lonlocal::texthash(
                                              del     => 'Delete?',
                                              rep     => 'Replace:',
                                              upl     => 'Upload:',
                                              curr    => 'View contents',
                                              default => 'Default',
                                              custom  => 'Custom',
                                              none    => 'None',
         );
   }
   
   sub print_ipaccess {
       my ($dom,$settings,$rowtotal) = @_;
       my $css_class;
       my $itemcount = 0;
       my $datatable;
       my %ordered;
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   if ($num eq '') {
                       $num = scalar(keys(%{$settings}));
                   }
                   $ordered{$num} = $item;
               }
           }
       }
       my $maxnum = scalar(keys(%ordered));
       if (keys(%ordered)) {
           my @items = sort { $a <=> $b } keys(%ordered);
           for (my $i=0; $i<@items; $i++) {
               $css_class = $itemcount%2?' class="LC_odd_row"':'';
               my $item = $ordered{$items[$i]};
               my ($name,$ipranges,%commblocks,%courses);
               if (ref($settings->{$item}) eq 'HASH') {
                   $name = $settings->{$item}->{'name'};
                   $ipranges = $settings->{$item}->{'ip'};
                   if (ref($settings->{$item}->{'commblocks'}) eq 'HASH') {
                       %commblocks = %{$settings->{$item}->{'commblocks'}};
                   }
                   if (ref($settings->{$item}->{'courses'}) eq 'HASH') {
                       %courses = %{$settings->{$item}->{'courses'}};
                   }
               }
               my $chgstr = ' onchange="javascript:reorderIPaccess(this.form,'."'ipaccess_pos_".$item."'".');"';
               $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                            .'<select name="ipaccess_pos_'.$item.'"'.$chgstr.'>';
               for (my $k=0; $k<=$maxnum; $k++) {
                   my $vpos = $k+1;
                   my $selstr;
                   if ($k == $i) {
                       $selstr = ' selected="selected" ';
                   }
                   $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
               }
               $datatable .= '</select>'.('&nbsp;'x2).
                   '<label><input type="checkbox" name="ipaccess_del" value="'.$item.'" />'.
                   &mt('Delete?').'</label></span></td>'.
                   '<td colspan="2"><input type="hidden" name="ipaccess_id_'.$i.'" value="'.$item.'" />'.
                   &ipaccess_options($i,$itemcount,$dom,$name,$ipranges,\%commblocks,\%courses).
                   '</td></tr>';
               $itemcount ++;
           }
       }
       $css_class = $itemcount%2?' class="LC_odd_row"':'';
       my $chgstr = ' onchange="javascript:reorderIPaccess(this.form,'."'ipaccess_pos_add'".');"';
       $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
                     '<input type="hidden" name="ipaccess_maxnum" value="'.$maxnum.'" />'."\n".
                     '<select name="ipaccess_pos_add"'.$chgstr.'>';
       for (my $k=0; $k<$maxnum+1; $k++) {
           my $vpos = $k+1;
           my $selstr;
           if ($k == $maxnum) {
               $selstr = ' selected="selected" ';
           }
           $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
       }
       $datatable .= '</select>&nbsp;'."\n".
                     '<input type="checkbox" name="ipaccess_add" value="1" />'.&mt('Add').'</span></td>'."\n".
                     '<td colspan="2">'.
                     &ipaccess_options('add',$itemcount,$dom).
                     '</td>'."\n".
                     '</tr>'."\n";
       $$rowtotal ++;
       return $datatable;
   }
   
   sub ipaccess_options {
       my ($num,$itemcount,$dom,$name,$ipranges,$blocksref,$coursesref) = @_;
       my (%currblocks,%currcourses,$output);
       if (ref($blocksref) eq 'HASH') {
           %currblocks = %{$blocksref};
       }
       if (ref($coursesref) eq 'HASH') {
           %currcourses = %{$coursesref};
       }
       $output = '<fieldset><legend>'.&mt('Location(s)').'</legend>'.
                 '<span class="LC_nobreak">'.&mt('Name').':&nbsp;'.
                 '<input type="text" name="ipaccess_name_'.$num.'" value="'.$name.'" />'.
                 '</span></fieldset>'.
                 '<fieldset><legend>'.&mt('IP Range(s)').'</legend>'.
                 &mt('Format for each IP range').': '.&mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'<br />'.
                 &mt('Range(s) will be stored as IP netblock(s) in CIDR notation (comma separated)').'<br />'.
                 '<textarea name="ipaccess_range_'.$num.'" rows="3" cols="80">'.
                 $ipranges.'</textarea></fieldset>'.
                 '<fieldset><legend>'.&mt('Functionality Blocked?').'</legend>'.
                 &blocker_checkboxes($num,$blocksref).'</fieldset>'.
                 '<fieldset><legend>'.&mt('Courses/Communities allowed').'</legend>'.
                 '<table>';
       foreach my $cid (sort(keys(%currcourses))) {
           my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1});
           $output .= '<tr><td><span class="LC_nobreak">'.
                      '<label><input type="checkbox" name="ipaccess_course_delete_'.$num.'" value="'.$cid.'" />'.
                      &mt('Delete?').'&nbsp;<span class="LC_cusr_emph">'.$courseinfo{'description'}.'</span></label></span>'.
                      ' <span class="LC_fontsize_medium">('.$cid.')</span></td></tr>';
       }
       $output .= '<tr><td><span class="LC_nobreak">'.&mt('Add').':&nbsp;'.
                  '<input type="text" name="ipaccess_cdesc_'.$num.'" value="" onfocus="this.blur();opencrsbrowser('."'display','ipaccess_cnum_$num','ipaccess_cdom_$num','ipaccess_cdesc_$num'".');" />'.
                   &Apache::loncommon::selectcourse_link('display','ipaccess_cnum_'.$num,'ipaccess_cdom_'.$num,'ipaccess_cdesc_'.$num,$dom,undef,'Course/Community').
                  '<input type="hidden" name="ipaccess_cnum_'.$num.'" value="" />'.
                  '<input type="hidden" name="ipaccess_cdom_'.$num.'" value="" />'.
                  '</span></td></tr></table>'."\n".
                  '</fieldset>';
       return $output;
   }
   
   sub blocker_checkboxes {
       my ($num,$blocks) = @_;
       my ($typeorder,$types) = &commblocktype_text();
       my $numinrow = 6;
       my $output = '<table>';
       for (my $i=0; $i<@{$typeorder}; $i++) {
           my $block = $typeorder->[$i];
           my $blockstatus;
           if (ref($blocks) eq 'HASH') {
               if ($blocks->{$block} eq 'on') {
                   $blockstatus = 'checked="checked"';
               }
           }
           my $rem = $i%($numinrow);
           if ($rem == 0) {
               if ($i > 0) {
                   $output .= '</tr>';
               }
               $output .= '<tr>';
           }
           if ($i == scalar(@{$typeorder})-1) {
               my $colsleft = $numinrow-$rem;
               if ($colsleft > 1) {
                   $output .= '<td colspan="'.$colsleft.'">';
               } else {
                   $output .= '<td>';
               }
           } else {
               $output .= '<td>';
           }
           my $item = 'ipaccess_block_'.$num;
           if ($blockstatus) {
               $blockstatus = ' '.$blockstatus;
           }
           $output .= '<span class="LC_nobreak"><label>'."\n".
                      '<input type="checkbox" name="'.$item.'"'.
                      $blockstatus.' value="'.$block.'"'.' />'.
                      $types->{$block}.'</label></span>'."\n".
                      '<br /></td>';
       }
       $output .= '</tr></table>';
       return $output;
   }
   
   sub commblocktype_text {
       my %types = &Apache::lonlocal::texthash(
           'com' => 'Messaging',
           'chat' => 'Chat Room',
           'boards' => 'Discussion',
           'port' => 'Portfolio',
           'groups' => 'Groups',
           'blogs' => 'Blogs',
           'about' => 'User Information',
           'printout' => 'Printouts',
           'passwd' => 'Change Password',
           'grades' => 'Gradebook',
           'search' => 'Course search',
           'wishlist' => 'Stored links',
           'annotate' => 'Annotations',
       );
       my $typeorder = ['com','chat','boards','port','groups','blogs','about','wishlist','printout','grades','search','annotate','passwd'];
       return ($typeorder,\%types);
   }
   
 sub print_rolecolors {  sub print_rolecolors {
     my ($phase,$role,$dom,$confname,$settings,$rowtotal) = @_;      my ($phase,$role,$dom,$confname,$settings,$rowtotal) = @_;
     my %choices = &color_font_choices();      my %choices = &color_font_choices();
Line 1463  sub display_color_options { Line 2069  sub display_color_options {
     my $datatable = '<tr'.$css_class.'>'.      my $datatable = '<tr'.$css_class.'>'.
         '<td>'.$choices->{'font'}.'</td>';          '<td>'.$choices->{'font'}.'</td>';
     if (!$is_custom->{'font'}) {      if (!$is_custom->{'font'}) {
         $datatable .=  '<td>'.&mt('Default in use:').'&nbsp;<span id="css_default_'.$role.'_font" style="color: '.$defaults->{'font'}.';">'.$defaults->{'font'}.'</span></td>';          $datatable .=  '<td>'.&mt('Default in use:').'&nbsp;<span class="css_default_'.$role.'_font" style="color: '.$defaults->{'font'}.';">'.$defaults->{'font'}.'</span></td>';
     } else {      } else {
         $datatable .= '<td>&nbsp;</td>';          $datatable .= '<td>&nbsp;</td>';
     }      }
Line 1472  sub display_color_options { Line 2078  sub display_color_options {
     $datatable .= '<td><span class="LC_nobreak">'.      $datatable .= '<td><span class="LC_nobreak">'.
                   '<input type="text" class="colorchooser" size="10" name="'.$role.'_font"'.                    '<input type="text" class="colorchooser" size="10" name="'.$role.'_font"'.
                   ' value="'.$current_color.'" />&nbsp;'.                    ' value="'.$current_color.'" />&nbsp;'.
                   '&nbsp;</td></tr>';                    '&nbsp;</span></td></tr>';
     unless ($role eq 'login') {       unless ($role eq 'login') { 
         $datatable .= '<tr'.$css_class.'>'.          $datatable .= '<tr'.$css_class.'>'.
                       '<td>'.$choices->{'fontmenu'}.'</td>';                        '<td>'.$choices->{'fontmenu'}.'</td>';
         if (!$is_custom->{'fontmenu'}) {          if (!$is_custom->{'fontmenu'}) {
             $datatable .=  '<td>'.&mt('Default in use:').'&nbsp;<span id="css_default_'.$role.'_font" style="color: '.$defaults->{'fontmenu'}.';">'.$defaults->{'fontmenu'}.'</span></td>';              $datatable .=  '<td>'.&mt('Default in use:').'&nbsp;<span class="css_default_'.$role.'_font" style="color: '.$defaults->{'fontmenu'}.';">'.$defaults->{'fontmenu'}.'</span></td>';
         } else {          } else {
             $datatable .= '<td>&nbsp;</td>';              $datatable .= '<td>&nbsp;</td>';
         }          }
Line 1487  sub display_color_options { Line 2093  sub display_color_options {
                       '<input class="colorchooser" type="text" size="10" name="'                        '<input class="colorchooser" type="text" size="10" name="'
       .$role.'_fontmenu"'.        .$role.'_fontmenu"'.
                       ' value="'.$current_color.'" />&nbsp;'.                        ' value="'.$current_color.'" />&nbsp;'.
                       '&nbsp;</td></tr>';                        '&nbsp;</span></td></tr>';
     }      }
     my $switchserver = &check_switchserver($dom,$confname);      my $switchserver = &check_switchserver($dom,$confname);
     foreach my $img (@{$images}) {      foreach my $img (@{$images}) {
Line 1495  sub display_color_options { Line 2101  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 1503  sub display_color_options { Line 2109  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 1546  sub display_color_options { Line 2157  sub display_color_options {
                         if ($fullwidth ne '' && $fullheight ne '') {                          if ($fullwidth ne '' && $fullheight ne '') {
                             if ($fullwidth > $width && $fullheight > $height) {                               if ($fullwidth > $width && $fullheight > $height) { 
                                 my $size = $width.'x'.$height;                                  my $size = $width.'x'.$height;
                                 system("convert -sample $size $input $output");                                  my @args = ('convert','-sample',$size,$input,$output);
                                   system({$args[0]} @args);
                                 $showfile = "/$imgdir/tn-".$filename;                                  $showfile = "/$imgdir/tn-".$filename;
                             }                              }
                         }                          }
Line 1595  sub display_color_options { Line 2207  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 1604  sub display_color_options { Line 2221  sub display_color_options {
     my $bgs_def;      my $bgs_def;
     foreach my $item (@{$bgs}) {      foreach my $item (@{$bgs}) {
         if (!$is_custom->{$item}) {          if (!$is_custom->{$item}) {
             $bgs_def .= '<td><span class="LC_nobreak">'.$choices->{$item}.'</span>&nbsp;<span id="css_default_'.$role.'_'.$item.'" style="background-color: '.$defaults->{'bgs'}{$item}.';">&nbsp;&nbsp;&nbsp;</span><br />'.$defaults->{'bgs'}{$item}.'</td>';              $bgs_def .= '<td><span class="LC_nobreak">'.$choices->{$item}.'</span>&nbsp;<span class="css_default_'.$role.'_'.$item.'" style="background-color: '.$defaults->{'bgs'}{$item}.';">&nbsp;&nbsp;&nbsp;</span><br />'.$defaults->{'bgs'}{$item}.'</td>';
         }          }
     }      }
     if ($bgs_def) {      if ($bgs_def) {
Line 1616  sub display_color_options { Line 2233  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 1632  sub display_color_options { Line 2249  sub display_color_options {
     my $links_def;      my $links_def;
     foreach my $item (@{$links}) {      foreach my $item (@{$links}) {
         if (!$is_custom->{$item}) {          if (!$is_custom->{$item}) {
             $links_def .= '<td>'.$choices->{$item}.'<br /><span id="css_default_'.$role.'_'.$item.'" style="color: '.$defaults->{'links'}{$item}.';">'.$defaults->{'links'}{$item}.'</span></td>';              $links_def .= '<td>'.$choices->{$item}.'<br /><span class="css_default_'.$role.'_'.$item.'" style="color: '.$defaults->{'links'}{$item}.';">'.$defaults->{'links'}{$item}.'</span></td>';
         }          }
     }      }
     if ($links_def) {      if ($links_def) {
Line 1644  sub display_color_options { Line 2261  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 1705  sub login_text_colors { Line 2322  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 1718  sub image_changes { Line 2335  sub image_changes {
     my ($is_custom,$alt_text,$img_import,$showfile,$fullsize,$role,$img,$imgfile,$logincolors) = @_;      my ($is_custom,$alt_text,$img_import,$showfile,$fullsize,$role,$img,$imgfile,$logincolors) = @_;
     my $output;      my $output;
     if ($img eq 'login') {      if ($img eq 'login') {
             # suppress image for Log-in header          $output = '</td><td>'.$logincolors; # suppress image for Log-in header
     } elsif (!$is_custom) {      } elsif (!$is_custom) {
         if ($img ne 'domlogo') {          if ($img ne 'domlogo') {
             $output .= &mt('Default image:').'<br />';              $output = &mt('Default image:').'<br />';
         } else {          } else {
             $output .= &mt('Default in use:').'<br />';              $output = &mt('Default in use:').'<br />';
         }          }
     }      }
     if ($img eq 'login') { # suppress image for Log-in header      if ($img ne 'login') {
         $output .= '<td>'.$logincolors;  
     } else {  
         if ($img_import) {          if ($img_import) {
             $output .= '<input type="hidden" name="'.$role.'_import_'.$img.'" value="'.$imgfile.'" />';              $output .= '<input type="hidden" name="'.$role.'_import_'.$img.'" value="'.$imgfile.'" />';
         }          }
Line 1740  sub image_changes { Line 2355  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 1759  sub print_quotas { Line 2374  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 1768  sub print_quotas { Line 2383  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 1872  sub print_quotas { Line 2487  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 2172  sub print_quotas { Line 2790  sub print_quotas {
 }  }
   
 sub print_requestmail {  sub print_requestmail {
     my ($dom,$action,$settings,$rowtotal) = @_;      my ($dom,$action,$settings,$rowtotal,$customcss,$rowstyle) = @_;
     my ($now,$datatable,%currapp);      my ($now,$datatable,%currapp);
     $now = time;      $now = time;
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
Line 2184  sub print_requestmail { Line 2802  sub print_requestmail {
     }      }
     my $numinrow = 2;      my $numinrow = 2;
     my $css_class;      my $css_class;
     $css_class = ($$rowtotal%2? ' class="LC_odd_row"':'');      if ($$rowtotal%2) {
           $css_class = 'LC_odd_row';
       }
       if ($customcss) {
           $css_class .= " $customcss";
       }
       $css_class =~ s/^\s+//;
       if ($css_class) {
           $css_class = ' class="'.$css_class.'"';
       }
       if ($rowstyle) {
           $css_class .= ' style="'.$rowstyle.'"';
       }
     my $text;      my $text;
     if ($action eq 'requestcourses') {      if ($action eq 'requestcourses') {
         $text = &mt('Receive notification of course requests requiring approval');          $text = &mt('Receive notification of course requests requiring approval');
Line 2211  sub print_studentcode { Line 2841  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 2307  sub print_textbookcourses { Line 2937  sub print_textbookcourses {
                               ('&nbsp;'x2).                                ('&nbsp;'x2).
                               '<span class="LC_nobreak">'.&mt('Thumbnail:');                                '<span class="LC_nobreak">'.&mt('Thumbnail:');
                 if ($image) {                  if ($image) {
                     $datatable .= '<span class="LC_nobreak">'.                      $datatable .= $imgsrc.
                                   $imgsrc.  
                                   '<label><input type="checkbox" name="'.$type.'_image_del"'.                                    '<label><input type="checkbox" name="'.$type.'_image_del"'.
                                   ' value="'.$key.'" />'.&mt('Delete?').'</label></span> '.                                    ' value="'.$key.'" />'.&mt('Delete?').'</label></span> '.
                                   '<span class="LC_nobreak">&nbsp;'.&mt('Replace:').'&nbsp;';                                    '<span class="LC_nobreak">&nbsp;'.&mt('Replace:').'&nbsp;';
Line 2339  sub print_textbookcourses { Line 2968  sub print_textbookcourses {
         $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';          $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
     }      }
     $datatable .= '</select>&nbsp;'."\n".      $datatable .= '</select>&nbsp;'."\n".
                   '<input type="checkbox" name="'.$type.'_addbook" value="1" />'.&mt('Add').'</td>'."\n".                    '<input type="checkbox" name="'.$type.'_addbook" value="1" />'.&mt('Add').'</span></td>'."\n".
                   '<td colspan="2">'.                    '<td colspan="2">'.
                   '<span class="LC_nobreak">'.&mt('Subject:').'<input type="text" size="15" name="'.$type.'_addbook_subject" value="" /></span> '."\n".                    '<span class="LC_nobreak">'.&mt('Subject:').'<input type="text" size="15" name="'.$type.'_addbook_subject" value="" /></span> '."\n".
                   ('&nbsp;'x2).                    ('&nbsp;'x2).
Line 2356  sub print_textbookcourses { Line 2985  sub print_textbookcourses {
         } else {          } else {
             $datatable .= '<input type="file" name="'.$type.'_addbook_image" value="" />';              $datatable .= '<input type="file" name="'.$type.'_addbook_image" value="" />';
         }          }
           $datatable .= '</span>'."\n";
     }      }
     $datatable .= '</span>'."\n".      $datatable .= '<span class="LC_nobreak">'.&mt('LON-CAPA course:').'&nbsp;'.
                   '<span class="LC_nobreak">'.&mt('LON-CAPA course:').'&nbsp;'.  
                   &Apache::loncommon::select_dom_form($env{'request.role.domain'},$type.'_addbook_cdom').                    &Apache::loncommon::select_dom_form($env{'request.role.domain'},$type.'_addbook_cdom').
                   '<input type="text" size="25" name="'.$type.'_addbook_cnum" value="" />'.                    '<input type="text" size="25" name="'.$type.'_addbook_cnum" value="" />'.
                   &Apache::loncommon::selectcourse_link                    &Apache::loncommon::selectcourse_link
                       ('display',$type.'_addbook_cnum',$type.'_addbook_cdom',undef,undef,undef,'Course');                        ('display',$type.'_addbook_cnum',$type.'_addbook_cdom',undef,undef,undef,'Course').
                   '</span></td>'."\n".                    '</span></td>'."\n".
                   '</tr>'."\n";                    '</tr>'."\n";
     $itemcount ++;      $itemcount ++;
Line 2464  ENDSCRIPT Line 3093  ENDSCRIPT
   
 sub ltitools_javascript {  sub ltitools_javascript {
     my ($settings) = @_;      my ($settings) = @_;
     return unless(ref($settings) eq 'HASH');      my $togglejs = &ltitools_toggle_js();
       unless (ref($settings) eq 'HASH') {
           return $togglejs;
       }
     my (%ordered,$total,%jstext);      my (%ordered,$total,%jstext);
     $total = 0;      $total = 0;
     foreach my $item (keys(%{$settings})) {      foreach my $item (keys(%{$settings})) {
Line 2482  sub ltitools_javascript { Line 3114  sub ltitools_javascript {
     return <<"ENDSCRIPT";      return <<"ENDSCRIPT";
 <script type="text/javascript">  <script type="text/javascript">
 // <![CDATA[  // <![CDATA[
 function reorderLTI(form,item) {  function reorderLTITools(form,item) {
     var changedVal;      var changedVal;
 $jstext  $jstext
     var newpos = 'ltitools_add_pos';      var newpos = 'ltitools_add_pos';
Line 2527  $jstext Line 3159  $jstext
 // ]]>  // ]]>
 </script>  </script>
   
   $togglejs
   
   ENDSCRIPT
   }
   
   sub ltitools_toggle_js {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   
   function toggleLTITools(form,setting,item) {
       var radioname = '';
       var divid = '';
       if ((setting == 'passback') || (setting == 'roster')) {
           radioname = 'ltitools_'+setting+'_'+item;
           divid = 'ltitools_'+setting+'time_'+item;
           var num = form.elements[radioname].length;
           if (num) {
               var setvis = '';
               for (var i=0; i<num; i++) {
                   if (form.elements[radioname][i].checked) {
                       if (form.elements[radioname][i].value == '1') {
                           if (document.getElementById(divid)) {
                               document.getElementById(divid).style.display = 'inline-block';
                           }
                           setvis = 1;
                       }
                       break;
                   }
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       if (setting == 'user') {
           divid = 'ltitools_'+setting+'_div_'+item;
           var checkid = 'ltitools_'+setting+'_field_'+item;
           if (document.getElementById(divid)) {
               if (document.getElementById(checkid)) {
                   if (document.getElementById(checkid).checked) {
                       document.getElementById(divid).style.display = 'inline-block';
                   } else {
                       document.getElementById(divid).style.display = 'none';
                   }
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub wafproxy_javascript {
       my ($dom) = @_;
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function updateWAF() {
       if (document.getElementById('wafproxy_remoteip')) {
           var wafremote = 0;
           if (document.display.wafproxy_remoteip.options[document.display.wafproxy_remoteip.selectedIndex].value == 'h') {
               wafremote = 1;
           }
           var fields = new Array('header','trust');
           for (var i=0; i<fields.length; i++) {
               if (document.getElementById('wafproxy_'+fields[i])) {
                   if (wafremote == 1) {
                       document.getElementById('wafproxy_'+fields[i]).style.display = 'table-row';
                   }
                   else {
                       document.getElementById('wafproxy_'+fields[i]).style.display = 'none';
                   }
               }
           }
           if (document.getElementById('wafproxyranges_$dom')) {
               if (wafremote == 1) {
                   document.getElementById('wafproxyranges_$dom').style.display = 'inline-block';
               } else {
                   for (var i=0; i<document.display.wafproxy_vpnaccess.length; i++) {
                       if (document.display.wafproxy_vpnaccess[i].checked) {
                           if (document.display.wafproxy_vpnaccess[i].value == 0) {
                               document.getElementById('wafproxyranges_$dom').style.display = 'none';
                           }
                       }
                   }
               }
           }
       }
       return;
   }
   
   function checkWAF() {
       if (document.getElementById('wafproxy_remoteip')) {
           var wafvpn = 0;
           for (var i=0; i<document.display.wafproxy_vpnaccess.length; i++) {
               if (document.display.wafproxy_vpnaccess[i].checked) {
                   if (document.display.wafproxy_vpnaccess[i].value == 1) {
                       wafvpn = 1;
                   }
                   break;
               }
           }
           var vpn = new Array('vpnint','vpnext');
           for (var i=0; i<vpn.length; i++) {
               if (document.getElementById('wafproxy_show_'+vpn[i])) {
                   if (wafvpn == 1) {
                       document.getElementById('wafproxy_show_'+vpn[i]).style.display = 'table-row';
                   }
                   else {
                       document.getElementById('wafproxy_show_'+vpn[i]).style.display = 'none';
                   }
               }
           }
           if (document.getElementById('wafproxyranges_$dom')) {
               if (wafvpn == 1) {
                   document.getElementById('wafproxyranges_$dom').style.display = 'inline-block';
               }
               else if (document.display.wafproxy_remoteip.options[document.display.wafproxy_remoteip.selectedIndex].value != 'h') {
                   document.getElementById('wafproxyranges_$dom').style.display = 'none';
               }
           }
       }
       return;
   }
   
   function toggleWAF() {
       if (document.getElementById('wafproxy_table')) {
           var wafproxy = 0;
           for (var i=0; i<document.display.wafproxy_${dom}.length; i++) {
                if (document.display.wafproxy_${dom}[i].checked) {
                    if (document.display.wafproxy_${dom}[i].value == 1) {
                        wafproxy = 1;
                        break;
                   }
               }
           }
           if (wafproxy == 1) {
               document.getElementById('wafproxy_table').style.display='inline';
           }
           else {
              document.getElementById('wafproxy_table').style.display='none';
           }
           if (document.getElementById('wafproxyrow_${dom}')) {
               if (wafproxy == 1) {
                   document.getElementById('wafproxyrow_${dom}').style.display = 'table-row';
               }
               else {
                   document.getElementById('wafproxyrow_${dom}').style.display = 'none';
               }
           }
           if (document.getElementById('nowafproxyrow_$dom')) {
               if (wafproxy == 1) {
                   document.getElementById('nowafproxyrow_${dom}').style.display = 'none';
               }
               else {
                   document.getElementById('nowafproxyrow_${dom}').style.display = 'table-row';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub 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);
       unless (ref($settings) eq 'HASH') {
           return $togglejs;
       }
       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";
       my $linkprot_js = &Apache::courseprefs::linkprot_javascript();
       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;
   }
   
   function toggleLTIEncKey(form) {
       var shownhosts = new Array();
       var hiddenhosts = new Array();
       var forcourse = new Array($course_servers);
       var fromdomain = '$primary';
       var crsradio = form.elements['ltisec_crslinkprot'];
       if (crsradio.length) {
           for (var i=0; i<crsradio.length; i++) {
               if (crsradio[i].checked) {
                   if (crsradio[i].value == 1) {
                       if (forcourse.length > 0) {
                           for (var j=0; j<forcourse.length; j++) {
                               if (!shownhosts.includes(forcourse[j])) {
                                   shownhosts.push(forcourse[j]);
                               }
                           }
                       }
                   } else {
                       if (forcourse.length > 0) {
                           for (var j=0; j<forcourse.length; j++) {
                               if (!hiddenhosts.includes(forcourse[j])) { 
                                   hiddenhosts.push(forcourse[j]);
                               }
                           }
                       } 
                   }
               }
           }
       }
       var domradio = form.elements['ltisec_domlinkprot'];
       if (domradio.length) {
           for (var i=0; i<domradio.length; i++) {
               if (domradio[i].checked) {
                   if (domradio[i].value == 1) {
                       if (!shownhosts.includes(fromdomain)) { 
                           shownhosts.push(fromdomain);
                       }
                   } else {
                       if (!hiddenhosts.includes(fromdomain)) {
                           hiddenhosts.push(fromdomain);
                       }
                   }
               }
           }
       }
       var consumersradio = form.elements['ltisec_consumers'];
       if (consumersradio.length) {
           for (var i=0; i<consumersradio.length; i++) {
               if (consumersradio[i].checked) {
                   if (consumersradio[i].value == 1) {
                       if (!shownhosts.includes(fromdomain)) {     
                           shownhosts.push(fromdomain);
                       }
                   } else {
                       if (!hiddenhosts.includes(fromdomain)) {
                           hiddenhosts.push(fromdomain);
                       }
                   }
               }
           }
       }
       if (shownhosts.length > 0) {
           for (var i=0; i<shownhosts.length; i++) {
               if (document.getElementById('ltisec_info_'+shownhosts[i])) {
                   document.getElementById('ltisec_info_'+shownhosts[i]).style.display = 'block';                 
               }
           }
           if (document.getElementById('ltisec_noprivkey')) {
               document.getElementById('ltisec_noprivkey').style.display = 'none';
           }
       } else {
           if (document.getElementById('ltisec_noprivkey')) {
               document.getElementById('ltisec_noprivkey').style.display = 'inline-block';
           }
       }
       if (hiddenhosts.length > 0) {
           for (var i=0; i<hiddenhosts.length; i++) {
               if (!shownhosts.includes(hiddenhosts[i])) {
                   if (document.getElementById('ltisec_info_'+hiddenhosts[i])) {
                       document.getElementById('ltisec_info_'+hiddenhosts[i]).style.display = 'none';
                   }
               }
           }
       }
       return;
   }
   
   function togglePrivKey(form,hostid) {
       var radioname = '';
       var currdivid = '';
       var newdivid = '';
       if ((document.getElementById('ltisec_divcurrprivkey_'+hostid)) &&
           (document.getElementById('ltisec_divchgprivkey_'+hostid))) {
           currdivid = document.getElementById('ltisec_divcurrprivkey_'+hostid);
           newdivid = document.getElementById('ltisec_divchgprivkey_'+hostid);
           radioname = form.elements['ltisec_changeprivkey_'+hostid];
           if (radioname) {
               if (radioname.length > 0) {
                   var setvis;
                   for (var i=0; i<radioname.length; i++) {
                       if (radioname[i].checked == true) {
                           if (radioname[i].value == 1) {
                               newdivid.style.display = 'inline-block';
                               currdivid.style.display = 'none';
                               setvis = 1;
                           }
                           break;
                       }
                   }
                   if (!setvis) {
                       newdivid.style.display = 'none';
                       currdivid.style.display = 'inline-block';
                   }
               }
           }
       }
   }
   
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub autoupdate_javascript {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function toggleLastActiveDays(form) {
       var radioname = 'lastactive';
       var divid = 'lastactive_div';
       var num = form.elements[radioname].length;
       if (num) {
           var setvis = '';
           for (var i=0; i<num; i++) {
               if (form.elements[radioname][i].checked) {
                   if (form.elements[radioname][i].value == '1') {
                       if (document.getElementById(divid)) {
                           document.getElementById(divid).style.display = 'inline-block';
                       }
                       setvis = 1;
                   }
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub autoenroll_javascript {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function toggleFailsafe(form) {
       var radioname = 'autoenroll_failsafe';
       var divid = 'autoenroll_failsafe_div';
       var num = form.elements[radioname].length;
       if (num) {
           var setvis = '';
           for (var i=0; i<num; i++) {
               if (form.elements[radioname][i].checked) {
                   if ((form.elements[radioname][i].value == 'zero') || (form.elements[radioname][i].value == 'any')) {
                       if (document.getElementById(divid)) {
                           document.getElementById(divid).style.display = 'inline-block';
                       }
                       setvis = 1;
                   }
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(divid)) {
                   document.getElementById(divid).style.display = 'none';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub saml_javascript {
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function toggleSamlOptions(form,hostid) { 
       var radioname = 'saml_'+hostid;
       var tablecellon = 'samloptionson_'+hostid;
       var tablecelloff = 'samloptionsoff_'+hostid;
       var num = form.elements[radioname].length;
       if (num) {
           var setvis = ''; 
           for (var i=0; i<num; i++) {
               if (form.elements[radioname][i].checked) {
                   if (form.elements[radioname][i].value == '1') { 
                       if (document.getElementById(tablecellon)) {
                           document.getElementById(tablecellon).style.display='';
                       }
                       if (document.getElementById(tablecelloff)) {
                           document.getElementById(tablecelloff).style.display='none';
                       }
                       setvis = 1;
                   }
                   break;
               }
           }
           if (!setvis) {
               if (document.getElementById(tablecellon)) {
                   document.getElementById(tablecellon).style.display='none';
               }
               if (document.getElementById(tablecelloff)) {
                   document.getElementById(tablecelloff).style.display='';
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub ipaccess_javascript {
       my ($settings) = @_;
       my (%ordered,$total,%jstext);
       $total = 0;
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   $ordered{$num} = $item;
               }
           }
           $total = scalar(keys(%{$settings}));
       }
       my @jsarray = ();
       foreach my $item (sort {$a <=> $b } (keys(%ordered))) {
           push(@jsarray,$ordered{$item});
       }
       my $jstext = '    var ipaccess = Array('."'".join("','",@jsarray)."'".');'."\n";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function reorderIPaccess(form,item) {
       var changedVal;
   $jstext
       var newpos = 'ipaccess_pos_add';
       var maxh = 1 + $total;
       var current = new Array;
       var newitemVal = form.elements[newpos].options[form.elements[newpos].selectedIndex].value;
       if (item == newpos) {
           changedVal = newitemVal;
       } else {
           changedVal = form.elements[item].options[form.elements[item].selectedIndex].value;
           current[newitemVal] = newpos;
       }
       for (var i=0; i<ipaccess.length; i++) {
           var elementName = 'ipaccess_pos_'+ipaccess[i];
           if (elementName != item) {
               if (form.elements[elementName]) {
                   var currVal = form.elements[elementName].options[form.elements[elementName].selectedIndex].value;
                   current[currVal] = elementName;
               }
           }
       }
       var oldVal;
       for (var j=0; j<maxh; j++) {
           if (current[j] == undefined) {
               oldVal = j;
           }
       }
       if (oldVal < changedVal) {
           for (var k=oldVal+1; k<=changedVal ; k++) {
              var elementName = current[k];
              form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex - 1;
           }
       } else {
           for (var k=changedVal; k<oldVal; k++) {
               var elementName = current[k];
               form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex + 1;
           }
       }
       return;
   }
   // ]]>
   </script>
   
 ENDSCRIPT  ENDSCRIPT
 }  }
   
 sub print_autoenroll {  sub print_autoenroll {
     my ($dom,$settings,$rowtotal) = @_;      my ($dom,$settings,$rowtotal) = @_;
     my $autorun = &Apache::lonnet::auto_run(undef,$dom),      my $autorun = &Apache::lonnet::auto_run(undef,$dom),
     my ($defdom,$runon,$runoff,$coownerson,$coownersoff,$failsafe);      my ($defdom,$runon,$runoff,$coownerson,$coownersoff,
           $failsafe,$autofailsafe,$failsafesty,%failsafechecked);
       $failsafesty = 'none';
       %failsafechecked = (
           off => ' checked="checked"',
       );
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if (exists($settings->{'run'})) {          if (exists($settings->{'run'})) {
             if ($settings->{'run'} eq '0') {              if ($settings->{'run'} eq '0') {
Line 2567  sub print_autoenroll { Line 4067  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 2607  sub print_autoenroll { Line 4123  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" /></td></tr>';                    '<span class="LC_nobreak"><label><input type="radio" name="autoenroll_failsafe" value="zero" onclick="toggleFailsafe(this.form)"'.$failsafechecked{'zero'}.' />'.&mt('Retrieved section enrollment is zero').'</label></span><br />'.
                     '<span class="LC_nobreak"><label><input type="radio" name="autoenroll_failsafe" value="any" onclick="toggleFailsafe(this.form)"'.$failsafechecked{'any'}.' />'.&mt('Retrieved section enrollment is zero or greater').'</label></span>'.
                     '<div class="LC_floatleft" style="display:'.$failsafesty.';" id="autoenroll_failsafe_div">'.
                     '<span class="LC_nobreak">'.
                     &mt('Threshold for number of students in section to drop: [_1]',
                         '<input type="text" name="autoenroll_autofailsafe" value="'.$autofailsafe.'" size="4" />').
                     '</span></div></td></tr>';
     $$rowtotal += 4;      $$rowtotal += 4;
     return $datatable;      return $datatable;
 }  }
   
 sub print_autoupdate {  sub print_autoupdate {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my $datatable;      my ($enable,$datatable);
     if ($position eq 'top') {      if ($position eq 'top') {
           my %choices = &Apache::lonlocal::texthash (
                             run        => 'Auto-update active?',
                             classlists => 'Update information in classlists?',
                             unexpired  => 'Skip updates for users without active or future roles?',
                             lastactive => 'Skip updates for inactive users?',
           );
           my $itemcount = 0;
         my $updateon = ' ';          my $updateon = ' ';
         my $updateoff = ' checked="checked" ';          my $updateoff = ' checked="checked" ';
         my $classlistson = ' ';  
         my $classlistsoff = ' checked="checked" ';  
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ($settings->{'run'} eq '1') {              if ($settings->{'run'} eq '1') {
                 $updateon = $updateoff;                  $updateon = $updateoff;
                 $updateoff = ' ';                  $updateoff = ' ';
             }              }
             if ($settings->{'classlists'} eq '1') {  
                 $classlistson = $classlistsoff;  
                 $classlistsoff = ' ';  
             }  
         }          }
         my %title = (          $enable = '<tr class="LC_odd_row">'.
                    run => 'Auto-update active?',                    '<td>'.$choices{'run'}.'</td>'.
                    classlists => 'Update information in classlists?',                    '<td class="LC_left_item"><span class="LC_nobreak"><label>'.
                     );  
         $datatable = '<tr class="LC_odd_row">'.   
                   '<td>'.&mt($title{'run'}).'</td>'.  
                   '<td class="LC_right_item"><span class="LC_nobreak"><label>'.  
                   '<input type="radio" name="autoupdate_run"'.                    '<input type="radio" name="autoupdate_run"'.
                   $updateon.' value="1" />'.&mt('Yes').'</label>&nbsp;'.                    $updateoff.'value="0" />'.&mt('No').'</label>&nbsp;'.
                   '<label><input type="radio" name="autoupdate_run"'.                    '<label><input type="radio" name="autoupdate_run"'.
                   $updateoff.'value="0" />'.&mt('No').'</label></span></td>'.                    $updateon.'value="1" />'.&mt('Yes').'</label></span></td>'.
                   '</tr><tr>'.  
                   '<td>'.&mt($title{'classlists'}).'</td>'.  
                   '<td class="LC_right_item"><span class="LC_nobreak">'.  
                   '<label><input type="radio" name="classlists"'.  
                   $classlistson.' value="1" />'.&mt('Yes').'</label>&nbsp;'.  
                   '<label><input type="radio" name="classlists"'.  
                   $classlistsoff.'value="0" />'.&mt('No').'</label></span></td>'.  
                   '</tr>';                    '</tr>';
         $$rowtotal += 2;          my @toggles = ('classlists','unexpired');
           my %defaultchecked = ('classlists' => 'off',
                                 'unexpired'  => 'off'
                                 );
           $$rowtotal ++;
           ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,
                                                        \%choices,$itemcount,'','','left','no');
           $datatable = $enable.$datatable;
           $$rowtotal += $itemcount;
           my $lastactiveon = ' ';
           my $lastactiveoff = ' checked="checked" ';
           my $lastactivestyle = 'none';
           my $lastactivedays;
           my $onclick = ' onclick="javascript:toggleLastActiveDays(this.form);"';
           if (ref($settings) eq 'HASH') {
               if ($settings->{'lastactive'} =~ /^\d+$/) {
                   $lastactiveon = $lastactiveoff;
                   $lastactiveoff = ' ';
                   $lastactivestyle = 'inline-block';
                   $lastactivedays = $settings->{'lastactive'};
               }
           }
           my $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'>'.
                         '<td>'.$choices{'lastactive'}.'</td>'.
                         '<td class="LC_left_item"><span class="LC_nobreak"><label>'.
                         '<input type="radio" name="lastactive"'.
                         $lastactiveoff.'value="0"'.$onclick.' />'.&mt('No').'</label>'.
                         '&nbsp;<label>'.
                         '<input type="radio" name="lastactive"'.
                         $lastactiveon.' value="1"'.$onclick.' />'.&mt('Yes').'</label>'.
                         '<div id="lastactive_div" style="display:'.$lastactivestyle.';">'.
                         ':&nbsp;'.&mt('inactive = no activity in last [_1] days',
                             '<input type="text" size="5" name="lastactivedays" value="'.
                             $lastactivedays.'" />').
                         '</span></td>'.
                         '</tr>';
           $$rowtotal ++;
     } elsif ($position eq 'middle') {      } elsif ($position eq 'middle') {
         my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);          my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
         my $numinrow = 3;          my $numinrow = 3;
Line 2891  sub print_contacts { Line 4440  sub print_contacts {
     my $datatable;      my $datatable;
     my @contacts = ('adminemail','supportemail');      my @contacts = ('adminemail','supportemail');
     my (%checked,%to,%otheremails,%bccemails,%includestr,%includeloc,%currfield,      my (%checked,%to,%otheremails,%bccemails,%includestr,%includeloc,%currfield,
         $maxsize,$fields,$fieldtitles,$fieldoptions,$possoptions,@mailings);          $maxsize,$fields,$fieldtitles,$fieldoptions,$possoptions,@mailings,%lonstatus);
     if ($position eq 'top') {      if ($position eq 'top') {
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             foreach my $item (@contacts) {              foreach my $item (@contacts) {
Line 2902  sub print_contacts { Line 4451  sub print_contacts {
         }          }
     } elsif ($position eq 'middle') {      } elsif ($position eq 'middle') {
         @mailings = ('errormail','packagesmail','lonstatusmail','requestsmail',          @mailings = ('errormail','packagesmail','lonstatusmail','requestsmail',
                      'updatesmail','idconflictsmail');                       'updatesmail','idconflictsmail','hostipmail');
         foreach my $type (@mailings) {          foreach my $type (@mailings) {
             $otheremails{$type} = '';              $otheremails{$type} = '';
         }          }
       } elsif ($position eq 'lower') {
           if (ref($settings) eq 'HASH') {
               if (ref($settings->{'lonstatus'}) eq 'HASH') {
                   %lonstatus = %{$settings->{'lonstatus'}};
               }
           }
     } else {      } else {
         @mailings = ('helpdeskmail','otherdomsmail');          @mailings = ('helpdeskmail','otherdomsmail');
         foreach my $type (@mailings) {          foreach my $type (@mailings) {
Line 2918  sub print_contacts { Line 4473  sub print_contacts {
         ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();          ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();
     }      }
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         unless ($position eq 'top') {          unless (($position eq 'top') || ($position eq 'lower')) {
             foreach my $type (@mailings) {              foreach my $type (@mailings) {
                 if (exists($settings->{$type})) {                  if (exists($settings->{$type})) {
                     if (ref($settings->{$type}) eq 'HASH') {                      if (ref($settings->{$type}) eq 'HASH') {
Line 2979  sub print_contacts { Line 4534  sub print_contacts {
             $checked{'requestsmail'}{'adminemail'} = ' checked="checked" ';              $checked{'requestsmail'}{'adminemail'} = ' checked="checked" ';
             $checked{'updatesmail'}{'adminemail'} = ' checked="checked" ';              $checked{'updatesmail'}{'adminemail'} = ' checked="checked" ';
             $checked{'idconflictsmail'}{'adminemail'} = ' checked="checked" ';              $checked{'idconflictsmail'}{'adminemail'} = ' checked="checked" ';
               $checked{'hostipmail'}{'adminemail'} = ' checked="checked" ';
         } elsif ($position eq 'bottom') {          } elsif ($position eq 'bottom') {
             $checked{'helpdeskmail'}{'supportemail'} = ' checked="checked" ';              $checked{'helpdeskmail'}{'supportemail'} = ' checked="checked" ';
             $checked{'otherdomsmail'}{'supportemail'} = ' checked="checked" ';              $checked{'otherdomsmail'}{'supportemail'} = ' checked="checked" ';
Line 3039  sub print_contacts { Line 4595  sub print_contacts {
                     if ($currfield{$field} eq 'no') {                      if ($currfield{$field} eq 'no') {
                         $display = ' style="display:none"';                          $display = ' style="display:none"';
                     }                      }
                     $datatable .= '</td></tr><tr id="help_screenshotsize"'.$display.' />'.                      $datatable .= '</td></tr><tr id="help_screenshotsize"'.$display.'>'.
                                   '<td>'.&mt('Maximum size for upload (MB)').'</td><td>'.                                    '<td>'.&mt('Maximum size for upload (MB)').'</td><td>'.
                                   '<input type="text" size="5" name="helpform_maxsize" value="'.$maxsize.'" />';                                    '<input type="text" size="5" name="helpform_maxsize" value="'.$maxsize.'" />';
                 }                  }
Line 3050  sub print_contacts { Line 4606  sub print_contacts {
         $datatable .= '</td></tr>'."\n";          $datatable .= '</td></tr>'."\n";
         $rownum ++;          $rownum ++;
     }      }
     unless ($position eq 'top') {      unless (($position eq 'top') || ($position eq 'lower')) {
         foreach my $type (@mailings) {          foreach my $type (@mailings) {
             $css_class = $rownum%2?' class="LC_odd_row"':'';              $css_class = $rownum%2?' class="LC_odd_row"':'';
             $datatable .= '<tr'.$css_class.'>'.              $datatable .= '<tr'.$css_class.'>'.
Line 3084  sub print_contacts { Line 4640  sub print_contacts {
                               'value="'.$bccemails{$type}.'"  /></fieldset>'.                                'value="'.$bccemails{$type}.'"  /></fieldset>'.
                               '<fieldset><legend>'.&mt('Optional added text').'</legend>'.                                '<fieldset><legend>'.&mt('Optional added text').'</legend>'.
                               &mt('Text automatically added to e-mail:').' '.                                &mt('Text automatically added to e-mail:').' '.
                               '<input type="text" name="'.$type.'_includestr" value="'.$includestr{$type}.'" /><br >'.                                '<input type="text" name="'.$type.'_includestr" value="'.$includestr{$type}.'" /><br />'.
                               '<span class="LC_nobreak">'.&mt('Location:').'&nbsp;'.                                '<span class="LC_nobreak">'.&mt('Location:').'&nbsp;'.
                               '<label><input type="radio" name="'.$type.'_includeloc" value="s"'.$locchecked{'s'}.' />'.&mt('in subject').'</label>'.                                '<label><input type="radio" name="'.$type.'_includeloc" value="s"'.$locchecked{'s'}.' />'.&mt('in subject').'</label>'.
                               ('&nbsp;'x2).                                ('&nbsp;'x2).
Line 3097  sub print_contacts { Line 4653  sub print_contacts {
     }      }
     if ($position eq 'middle') {      if ($position eq 'middle') {
         my %choices;          my %choices;
         $choices{'reporterrors'} = &mt('E-mail error reports to [_1]',          my $corelink = &core_link_msu();
                                        &Apache::loncommon::modal_link('http://loncapa.org/core.html',          $choices{'reporterrors'} = &mt('E-mail error reports to [_1]',$corelink);
                                        &mt('LON-CAPA core group - MSU'),600,500));  
         $choices{'reportupdates'} = &mt('E-mail record of completed LON-CAPA updates to [_1]',          $choices{'reportupdates'} = &mt('E-mail record of completed LON-CAPA updates to [_1]',
                                         &Apache::loncommon::modal_link('http://loncapa.org/core.html',                                          $corelink);
                                         &mt('LON-CAPA core group - MSU'),600,500));          $choices{'reportstatus'} = &mt('E-mail status if errors above threshold to [_1]',$corelink);
         my @toggles = ('reporterrors','reportupdates');          my @toggles = ('reporterrors','reportupdates','reportstatus');
         my %defaultchecked = ('reporterrors'  => 'on',          my %defaultchecked = ('reporterrors'  => 'on',
                               'reportupdates' => 'on');                                'reportupdates' => 'on',
                                 'reportstatus'  => 'on');
         (my $reports,$rownum) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,          (my $reports,$rownum) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,
                                                    \%choices,$rownum);                                                     \%choices,$rownum);
         $datatable .= $reports;          $datatable .= $reports;
       } elsif ($position eq 'lower') {
           my (%current,%excluded,%weights);
           my ($defaults,$names) = &Apache::loncommon::lon_status_items();
           if ($lonstatus{'threshold'} =~ /^\d+$/) {
               $current{'errorthreshold'} = $lonstatus{'threshold'};
           } else {
               $current{'errorthreshold'} = $defaults->{'threshold'};
           }
           if ($lonstatus{'sysmail'} =~ /^\d+$/) {
               $current{'errorsysmail'} = $lonstatus{'sysmail'};
           } else {
               $current{'errorsysmail'} = $defaults->{'sysmail'};
           }
           if (ref($lonstatus{'weights'}) eq 'HASH') {
               foreach my $type ('E','W','N','U') {
                   if ($lonstatus{'weights'}{$type} =~ /^\d+$/) {
                       $weights{$type} = $lonstatus{'weights'}{$type};
                   } else {
                       $weights{$type} = $defaults->{$type};
                   }
               }
           } else {
               foreach my $type ('E','W','N','U') {
                   $weights{$type} = $defaults->{$type};
               }
           }
           if (ref($lonstatus{'excluded'}) eq 'ARRAY') {
               if (@{$lonstatus{'excluded'}} > 0) {
                   map {$excluded{$_} = 1; } @{$lonstatus{'excluded'}};
               }
           }
           foreach my $item ('errorthreshold','errorsysmail') { 
               $css_class = $rownum%2?' class="LC_odd_row"':'';
               $datatable .= '<tr'.$css_class.'>'.
                             '<td class="LC_left_item"><span class="LC_nobreak">'.
                             $titles->{$item}.
                             '</span></td><td class="LC_left_item">'.
                             '<input type="text" name="'.$item.'" value="'.
                             $current{$item}.'" size="5" /></td></tr>';
               $rownum ++;
           }
           $css_class = $rownum%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'>'.
                         '<td class="LC_left_item">'.
                         '<span class="LC_nobreak">'.$titles->{'errorweights'}.
                         '</span></td><td class="LC_left_item"><table><tr>';
           foreach my $type ('E','W','N','U') {
               $datatable .= '<td>'.$names->{$type}.'<br />'.
                             '<input type="text" name="errorweights_'.$type.'" value="'.
                             $weights{$type}.'" size="5" /></td>';
           }
           $datatable .= '</tr></table></tr>';
           $rownum ++;
           $css_class = $rownum%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td class="LC_left_item">'.
                         $titles->{'errorexcluded'}.'</td>'.
                         '<td class="LC_left_item"><table>';
           my $numinrow = 4;
           my @ids = sort(values(%Apache::lonnet::serverhomeIDs));
           for (my $i=0; $i<@ids; $i++) {
               my $rem = $i%($numinrow);
               if ($rem == 0) {
                   if ($i > 0) {
                       $datatable .= '</tr>';
                   }
                   $datatable .= '<tr>';
               }
               my $check;
               if ($excluded{$ids[$i]}) {
                   $check = ' checked="checked" ';
               }
               $datatable .= '<td class="LC_left_item">'.
                             '<span class="LC_nobreak"><label>'.
                             '<input type="checkbox" name="errorexcluded" '.
                             'value="'.$ids[$i].'"'.$check.' />'.
                             $ids[$i].'</label></span></td>';
           }
           my $colsleft = $numinrow - @ids%($numinrow);
           if ($colsleft > 1 ) {
               $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                             '&nbsp;</td>';
           } elsif ($colsleft == 1) {
               $datatable .= '<td class="LC_left_item">&nbsp;</td>';
           }
           $datatable .= '</tr></table></td></tr>';
           $rownum ++;
     } elsif ($position eq 'bottom') {      } elsif ($position eq 'bottom') {
         my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);          my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
         my (@posstypes,%usertypeshash);          my (@posstypes,%usertypeshash);
Line 3136  sub print_contacts { Line 4778  sub print_contacts {
                             $includeloc{'override_'.$key} = '';                              $includeloc{'override_'.$key} = '';
                             $includestr{'override_'.$key} = '';                              $includestr{'override_'.$key} = '';
                             if ($settings->{'overrides'}{$key}{'include'} ne '') {                              if ($settings->{'overrides'}{$key}{'include'} ne '') {
                                 ($includeloc{'override_'.$key},$includestr{'override_'.$key}) =                                  ($includeloc{'override_'.$key},$includestr{'override_'.$key}) = 
                                     split(/:/,$settings->{'overrides'}{$key}{'include'},2);                                      split(/:/,$settings->{'overrides'}{$key}{'include'},2);
                                 $includestr{'override_'.$key} = &unescape($includestr{'override_'.$key});                                  $includestr{'override_'.$key} = &unescape($includestr{'override_'.$key});
                             }                              }
Line 3148  sub print_contacts { Line 4790  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 3182  sub print_contacts { Line 4824  sub print_contacts {
     return $datatable;      return $datatable;
 }  }
   
   sub core_link_msu {
       return &Apache::loncommon::modal_link('http://loncapa.org/core.html',
                                             &mt('LON-CAPA core group - MSU'),600,500);
   }
   
 sub overridden_helpdesk {  sub overridden_helpdesk {
     my ($checked,$otheremails,$bccemails,$includeloc,$includestr,$type,$rowid,      my ($checked,$otheremails,$bccemails,$includeloc,$includestr,$type,$rowid,
         $typetitle,$css_class,$rowstyle,$contacts,$short_titles) = @_;          $typetitle,$css_class,$rowstyle,$contacts,$short_titles) = @_;
Line 3210  sub overridden_helpdesk { Line 4857  sub overridden_helpdesk {
             }              }
             my $title;              my $title;
             if (ref($short_titles) eq 'HASH') {              if (ref($short_titles) eq 'HASH') {
                 $title = $short_titles->{$item};                  $title = $short_titles->{$item}; 
             }              }
             $output .= '<label>'.              $output .= '<label>'.
                        '<input type="checkbox" name="override_'.$type.'"'.$check.                         '<input type="checkbox" name="override_'.$type.'"'.$check.
Line 3232  sub overridden_helpdesk { Line 4879  sub overridden_helpdesk {
                'value="'.$bccemails.'"  /></fieldset>'.                 'value="'.$bccemails.'"  /></fieldset>'.
                '<fieldset><legend>'.&mt('Optional added text').'</legend>'.                 '<fieldset><legend>'.&mt('Optional added text').'</legend>'.
                &mt('Text automatically added to e-mail:').' '.                 &mt('Text automatically added to e-mail:').' '.
                '<input type="text" name="override_'.$type.'_includestr" value="'.$includestr.'" /><br >'.                 '<input type="text" name="override_'.$type.'_includestr" value="'.$includestr.'" /><br />'.
                '<span class="LC_nobreak">'.&mt('Location:').'&nbsp;'.                 '<span class="LC_nobreak">'.&mt('Location:').'&nbsp;'.
                '<label><input type="radio" name="override_'.$type.'_includeloc" value="s"'.$locchecked{'s'}.' />'.&mt('in subject').'</label>'.                 '<label><input type="radio" name="override_'.$type.'_includeloc" value="s"'.$locchecked{'s'}.' />'.&mt('in subject').'</label>'.
                ('&nbsp;'x2).                 ('&nbsp;'x2).
Line 3361  sub print_helpsettings { Line 5008  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 3395  sub print_helpsettings { Line 5041  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 3434  sub print_helpsettings { Line 5080  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 3464  sub print_helpsettings { Line 5110  sub print_helpsettings {
                                                                 \@templateroles,$newcust).                                                                  \@templateroles,$newcust).
                       &Apache::lonuserutils::custom_role_table('Course',\%full,\%levels,                        &Apache::lonuserutils::custom_role_table('Course',\%full,\%levels,
                                                                \%levelscurrent,$newcust).                                                                 \%levelscurrent,$newcust).
                       '</fieldset></td></tr>';                        '</fieldset>'.
                         &helpsettings_javascript(\@roles_by_num,$maxnum,$hiddenstr,$formname).
                         '</td></tr>';
         $count ++;          $count ++;
         $$rowtotal += $count;          $$rowtotal += $count;
     }      }
Line 3681  sub helpdeskroles_access { Line 5329  sub helpdeskroles_access {
   
 sub radiobutton_prefs {  sub radiobutton_prefs {
     my ($settings,$toggles,$defaultchecked,$choices,$itemcount,$onclick,      my ($settings,$toggles,$defaultchecked,$choices,$itemcount,$onclick,
         $additional,$align) = @_;          $additional,$align,$firstval) = @_;
     return unless ((ref($toggles) eq 'ARRAY') && (ref($defaultchecked) eq 'HASH') &&      return unless ((ref($toggles) eq 'ARRAY') && (ref($defaultchecked) eq 'HASH') &&
                    (ref($choices) eq 'HASH'));                     (ref($choices) eq 'HASH'));
   
Line 3713  sub radiobutton_prefs { Line 5361  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 3721  sub radiobutton_prefs { Line 5369  sub radiobutton_prefs {
         } else {          } else {
             $datatable .= '<td class="LC_right_item">';              $datatable .= '<td class="LC_right_item">';
         }          }
         $datatable .=          $datatable .= '<span class="LC_nobreak">';
             '<span class="LC_nobreak">'.          if ($firstval eq 'no') {
             '<label><input type="radio" name="'.              $datatable .=
             $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').                  '<label><input type="radio" name="'.
             '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.                  $item.'" '.$checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').
             $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>'.                  '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.
             '</span>'.$additional.                  $checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').'</label>';
             '</td>'.          } else {
             '</tr>';              $datatable .=
                   '<label><input type="radio" name="'.
                   $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').
                   '</label>&nbsp;<label><input type="radio" name="'.$item.'" '.
                   $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>';
           }
           $datatable .= '</span>'.$additional.'</td></tr>';
         $itemcount ++;          $itemcount ++;
     }      }
     return ($datatable,$itemcount);      return ($datatable,$itemcount);
Line 3753  sub print_ltitools { Line 5407  sub print_ltitools {
     my $confname = $dom.'-domainconfig';      my $confname = $dom.'-domainconfig';
     my $switchserver = &check_switchserver($dom,$confname);      my $switchserver = &check_switchserver($dom,$confname);
     my $maxnum = scalar(keys(%ordered));      my $maxnum = scalar(keys(%ordered));
     my $datatable = &ltitools_javascript($settings);      my $datatable;
     my %lt = &ltitools_names();      my %lt = &ltitools_names();
     my @courseroles = ('cc','in','ta','ep','st');      my @courseroles = ('cc','in','ta','ep','st');
     my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner);      my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner);
     my @fields = ('fullname','firstname','lastname','email','user','roles');      my @fields = ('fullname','firstname','lastname','email','roles','user');
     if (keys(%ordered)) {      if (keys(%ordered)) {
         my @items = sort { $a <=> $b } keys(%ordered);          my @items = sort { $a <=> $b } keys(%ordered);
         for (my $i=0; $i<@items; $i++) {          for (my $i=0; $i<@items; $i++) {
             $css_class = $itemcount%2?' class="LC_odd_row"':'';              $css_class = $itemcount%2?' class="LC_odd_row"':'';
             my $item = $ordered{$items[$i]};              my $item = $ordered{$items[$i]};
             my ($title,$key,$secret,$url,$imgsrc,$version);              my ($title,$key,$secret,$url,$lifetime,$imgsrc,%sigsel);
             if (ref($settings->{$item}) eq 'HASH') {              if (ref($settings->{$item}) eq 'HASH') {
                 $title = $settings->{$item}->{'title'};                  $title = $settings->{$item}->{'title'};
                 $url = $settings->{$item}->{'url'};                  $url = $settings->{$item}->{'url'};
                 $key = $settings->{$item}->{'key'};                  $key = $settings->{$item}->{'key'};
                 $secret = $settings->{$item}->{'secret'};                  $secret = $settings->{$item}->{'secret'};
                   $lifetime = $settings->{$item}->{'lifetime'};
                 my $image = $settings->{$item}->{'image'};                  my $image = $settings->{$item}->{'image'};
                 if ($image ne '') {                  if ($image ne '') {
                     $imgsrc = '<img src="'.$image.'" alt="'.&mt('Tool Provider icon').'" />';                      $imgsrc = '<img src="'.$image.'" alt="'.&mt('Tool Provider icon').'" />';
                 }                  }
                   if ($settings->{$item}->{'sigmethod'} eq 'HMAC-256') {
                       $sigsel{'HMAC-256'} = ' selected="selected"';
                   } else {
                       $sigsel{'HMAC-SHA1'} = ' selected="selected"';
                   }
             }              }
             my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'ltitools_".$item."'".');"';              my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_".$item."'".');"';
             $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'              $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                          .'<select name="ltitools_'.$item.'"'.$chgstr.'>';                           .'<select name="ltitools_'.$item.'"'.$chgstr.'>';
             for (my $k=0; $k<=$maxnum; $k++) {              for (my $k=0; $k<=$maxnum; $k++) {
Line 3790  sub print_ltitools { Line 5450  sub print_ltitools {
                 &mt('Delete?').'</label></span></td>'.                  &mt('Delete?').'</label></span></td>'.
                 '<td colspan="2">'.                  '<td colspan="2">'.
                 '<fieldset><legend>'.&mt('Required settings').'</legend>'.                  '<fieldset><legend>'.&mt('Required settings').'</legend>'.
                 '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="30" name="ltitools_title_'.$i.'" value="'.$title.'" /></span> '.                  '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="20" name="ltitools_title_'.$i.'" value="'.$title.'" /></span> '.
                 ('&nbsp;'x2).                  ('&nbsp;'x2).
                 '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_version_'.$i.'">'.                  '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_version_'.$i.'">'.
                 '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '.                  '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '.
                 ('&nbsp;'x2).                  ('&nbsp;'x2).
                 '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_msgtype_'.$i.'">'.                  '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_msgtype_'.$i.'">'.
                 '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '.                  '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '.
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'sigmethod'}.':<select name="ltitools_sigmethod_'.$i.'">'.
                   '<option value="HMAC-SHA1"'.$sigsel{'HMAC-SHA1'}.'>HMAC-SHA1</option>'.
                   '<option value="HMAC-SHA256"'.$sigsel{'HMAC-SHA256'}.'>HMAC-SHA256</option></select></span>'.
                 '<br /><br />'.                  '<br /><br />'.
                 '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="30" name="ltitools_url_'.$i.'"'.                  '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="ltitools_url_'.$i.'"'.
                 ' value="'.$url.'" /></span>'.                  ' value="'.$url.'" /></span>'.
                 ('&nbsp;'x2).                  ('&nbsp;'x2).
                 '<span class="LC_nobreak">'.$lt{'key'}.                  '<span class="LC_nobreak">'.$lt{'key'}.':'.
                 '<input type="text" size="25" name="ltitools_key_'.$i.'" value="'.$key.'" /></span> '.                  '<input type="text" size="25" name="ltitools_key_'.$i.'" value="'.$key.'" /></span> '.
                 ('&nbsp;'x2).                  ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'lifetime'}.':'.
                   '<input type="text" size="5" name="ltitools_lifetime_'.$i.'" value="'.$lifetime.'" /></span> '.
                   ('&nbsp;'x2).
                 '<span class="LC_nobreak">'.$lt{'secret'}.':'.                  '<span class="LC_nobreak">'.$lt{'secret'}.':'.
                 '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="'.$secret.'" />'.                  '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="'.$secret.'" />'.
                 '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_secret_'.$i.'.type='."'text'".' } else { this.form.ltitools_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'.                  '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_secret_'.$i.'.type='."'text'".' } else { this.form.ltitools_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'.
Line 3841  sub print_ltitools { Line 5508  sub print_ltitools {
                               '<input type="text" name="ltitools_'.$dimen.'_'.$i.'" size="5" value="'.$currdisp{$dimen}.'" /></label>'.                                '<input type="text" name="ltitools_'.$dimen.'_'.$i.'" size="5" value="'.$currdisp{$dimen}.'" /></label>'.
                               ('&nbsp;'x2);                                ('&nbsp;'x2);
             }              }
             $datatable .= '<br />'.              $datatable .= '</span><br />'.
                           '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.                            '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.
                           '<input type="text" name="ltitools_linktext_'.$i.'" size="25" value="'.$currdisp{'linktext'}.'" /></label></div>'.                            '<input type="text" name="ltitools_linktext_'.$i.'" size="25" value="'.$currdisp{'linktext'}.'" /></div>'.
                           '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.                            '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.
                           '<textarea name="ltitools_explanation_'.$i.'" rows="5" cols="40">'.$currdisp{'explanation'}.                            '<textarea name="ltitools_explanation_'.$i.'" rows="5" cols="40">'.$currdisp{'explanation'}.
                           '</textarea></div><div style=""></div><br />';                            '</textarea></div><div style=""></div><br />';
             $datatable .= '<br />';              my %units = (
                             'passback' => 'days',
                             'roster'   => 'seconds',
                           );
             foreach my $extra ('passback','roster') {              foreach my $extra ('passback','roster') {
                   my $validsty = 'none';
                   my $currvalid;
                 my $checkedon = '';                  my $checkedon = '';
                 my $checkedoff = ' checked="checked"';                  my $checkedoff = ' checked="checked"';
                 if ($settings->{$item}->{$extra}) {                  if ($settings->{$item}->{$extra}) {
                     $checkedon = $checkedoff;                      $checkedon = $checkedoff;
                     $checkedoff = '';                      $checkedoff = '';
                 }                      $validsty = 'inline-block';
                 $datatable .= $lt{$extra}.'&nbsp;'.                      if ($settings->{$item}->{$extra.'valid'} =~ /^\d+\.?\d*$/) {
                               '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="1"'.$checkedon.' />'.                          $currvalid = $settings->{$item}->{$extra.'valid'};
                               &mt('Yes').'</label>'.('&nbsp;'x2).                      }
                               '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="0"'.$checkedoff.' />'.                  }
                               &mt('No').'</label>'.('&nbsp;'x4);                  my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','$i'".');"';
                   $datatable .= '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{$extra}.'&nbsp;'.
                                 '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="0"'.$checkedoff.$onclick.' />'.
                                 &mt('No').'</label>'.('&nbsp;'x2).
                                 '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="1"'.$checkedon.$onclick.' />'.
                                 &mt('Yes').'</label></span></div>'.
                                 '<div class="LC_floatleft" style="display:'.$validsty.';" id="ltitools_'.$extra.'time_'.$i.'">'.
                                 '<span class="LC_nobreak">'.
                                 &mt("at least [_1] $units{$extra} after launch",
                                     '<input type="text" name="ltitools_'.$extra.'valid_'.$i.'" value="'.$currvalid.'" />').
                                 '</span></div><div style="padding:0;clear:both;margin:0;border:0"></div>';
             }              }
             $datatable .= '<br /><br /><span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;';              $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;';
             if ($imgsrc) {              if ($imgsrc) {
                 $datatable .= $imgsrc.                  $datatable .= $imgsrc.
                               '<label><input type="checkbox" name="ltitools_image_del"'.                                '<label><input type="checkbox" name="ltitools_image_del"'.
Line 3876  sub print_ltitools { Line 5558  sub print_ltitools {
                 $datatable .= '<input type="file" name="ltitools_image_'.$i.'" value="" />';                  $datatable .= '<input type="file" name="ltitools_image_'.$i.'" value="" />';
             }              }
             $datatable .= '</span></fieldset>';              $datatable .= '</span></fieldset>';
             my (%checkedfields,%rolemaps);              my (%checkedfields,%rolemaps,$userincdom);
             if (ref($settings->{$item}) eq 'HASH') {              if (ref($settings->{$item}) eq 'HASH') {
                 if (ref($settings->{$item}->{'fields'}) eq 'HASH') {                  if (ref($settings->{$item}->{'fields'}) eq 'HASH') {
                     %checkedfields = %{$settings->{$item}->{'fields'}};                      %checkedfields = %{$settings->{$item}->{'fields'}};
                 }                  }
                   $userincdom = $settings->{$item}->{'incdom'};
                 if (ref($settings->{$item}->{'roles'}) eq 'HASH') {                  if (ref($settings->{$item}->{'roles'}) eq 'HASH') {
                     %rolemaps = %{$settings->{$item}->{'roles'}};                      %rolemaps = %{$settings->{$item}->{'roles'}};
                     $checkedfields{'roles'} = 1;                      $checkedfields{'roles'} = 1;
Line 3888  sub print_ltitools { Line 5571  sub print_ltitools {
             }              }
             $datatable .= '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'.              $datatable .= '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'.
                           '<span class="LC_nobreak">';                            '<span class="LC_nobreak">';
               my $userfieldstyle = 'display:none;';
               my $seluserdom = '';
               my $unseluserdom = ' selected="selected"';
             foreach my $field (@fields) {              foreach my $field (@fields) {
                 my $checked;                  my ($checked,$onclick,$id,$spacer);
                 if ($checkedfields{$field}) {                  if ($checkedfields{$field}) {
                     $checked = ' checked="checked"';                      $checked = ' checked="checked"';
                 }                  }
                   if ($field eq 'user') {
                       $id = ' id="ltitools_user_field_'.$i.'"';
                       $onclick = ' onclick="toggleLTITools(this.form,'."'$field','$i'".')"';
                       if ($checked) {
                           $userfieldstyle = 'display:inline-block';
                           if ($userincdom) {
                               $seluserdom = $unseluserdom;
                               $unseluserdom = '';
                           }
                       }
                   } else {
                       $spacer = ('&nbsp;' x2);
                   }
                 $datatable .= '<label>'.                  $datatable .= '<label>'.
                               '<input type="checkbox" name="ltitools_fields_'.$i.'" value="'.$field.'"'.$checked.' />'.                                '<input type="checkbox" name="ltitools_fields_'.$i.'" value="'.$field.'"'.$id.$checked.$onclick.' />'.
                               $lt{$field}.'</label>'.('&nbsp;' x2);                                $lt{$field}.'</label>'.$spacer;
             }              }
             $datatable .= '</span></fieldset>'.              $datatable .= '</span>';
               $datatable .= '<div style="'.$userfieldstyle.'" id="ltitools_user_div_'.$i.'">'.
                             '<span class="LC_nobreak"> : '.
                             '<select name="ltitools_userincdom_'.$i.'">'.
                             '<option value="">'.&mt('Select').'</option>'.
                             '<option value="0"'.$unseluserdom.'>'.&mt('username').'</option>'.
                             '<option value="1"'.$seluserdom.'>'.&mt('username:domain').'</option>'.
                             '</select></span></div>';
               $datatable .= '</fieldset>'.
                           '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';                            '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';
             foreach my $role (@courseroles) {              foreach my $role (@courseroles) {
                 my ($selected,$selectnone);                  my ($selected,$selectnone);
                 if (!$rolemaps{$role}) {                  if (!$rolemaps{$role}) {
                     $selectnone = ' selected="selected"';                      $selectnone = ' selected="selected"';
                 }                  }
                 $datatable .= '<td align="center">'.                  $datatable .= '<td style="text-align: center">'. 
                               &Apache::lonnet::plaintext($role,'Course').'<br />'.                                &Apache::lonnet::plaintext($role,'Course').'<br />'.
                               '<select name="ltitools_roles_'.$role.'_'.$i.'">'.                                '<select name="ltitools_roles_'.$role.'_'.$i.'">'.
                               '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';                                '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';
Line 3928  sub print_ltitools { Line 5635  sub print_ltitools {
                 }                  }
             }              }
             $datatable .= '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">';              $datatable .= '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">';
             foreach my $item ('label','title','target','linktext','explanation') {              foreach my $item ('label','title','target','linktext','explanation','append') {
                 my $checked;                  my $checked;
                 if ($courseconfig{$item}) {                  if ($courseconfig{$item}) {
                     $checked = ' checked="checked"';                      $checked = ' checked="checked"';
                 }                  }
                 $datatable .= '<label>'.                  $datatable .= '<label>'.
                        '<input type="checkbox" name="ltitools_courseconfig_'.$i.'" value="'.$item.'"'.$checked.' />'.                         '<input type="checkbox" name="ltitools_courseconfig_'.$i.'" value="'.$item.'"'.$checked.' />'.
                        $lt{'crs'.$item}.'</label>'.('&nbsp;' x2)."\n";                         $lt{'crs'.$item}.'</label>&nbsp; '."\n";
             }              }
             $datatable .= '</span></fieldset>'.              $datatable .= '</span></fieldset>'.
                           '<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'.                            '<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'.
Line 3961  sub print_ltitools { Line 5668  sub print_ltitools {
         }          }
     }      }
     $css_class = $itemcount%2?' class="LC_odd_row"':'';      $css_class = $itemcount%2?' class="LC_odd_row"':'';
     my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'ltitools_add_pos'".');"';      my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_add_pos'".');"';
     $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".      $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n".
                   '<input type="hidden" name="ltitools_maxnum" value="'.$maxnum.'" />'."\n".                    '<input type="hidden" name="ltitools_maxnum" value="'.$maxnum.'" />'."\n".
                   '<select name="ltitools_add_pos"'.$chgstr.'>';                    '<select name="ltitools_add_pos"'.$chgstr.'>';
Line 3974  sub print_ltitools { Line 5681  sub print_ltitools {
         $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';          $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
     }      }
     $datatable .= '</select>&nbsp;'."\n".      $datatable .= '</select>&nbsp;'."\n".
                   '<input type="checkbox" name="ltitools_add" value="1" />'.&mt('Add').'</td>'."\n".                    '<input type="checkbox" name="ltitools_add" value="1" />'.&mt('Add').'</span></td>'."\n".
                   '<td colspan="2">'.                    '<td colspan="2">'.
                   '<fieldset><legend>'.&mt('Required settings').'</legend>'.                    '<fieldset><legend>'.&mt('Required settings').'</legend>'.
                   '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="30" name="ltitools_add_title" value="" /></span> '."\n".                    '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="20" name="ltitools_add_title" value="" /></span> '."\n".
                   ('&nbsp;'x2).                    ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_add_version">'.                    '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_add_version">'.
                   '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n".                    '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n".
                   ('&nbsp;'x2).                    ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_add_msgtype">'.                    '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_add_msgtype">'.
                   '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '.                    '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '.
                     '<span class="LC_nobreak">'.$lt{'sigmethod'}.':<select name="ltitools_add_sigmethod">'.
                     '<option value="HMAC-SHA1" selected="selected">HMAC-SHA1</option>'.
                     '<option value="HMAC-SHA256">HMAC-SHA256</option></select></span>'.
                   '<br />'.                    '<br />'.
                   '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="30" name="ltitools_add_url" value="" /></span> '."\n".                    '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="ltitools_add_url" value="" /></span> '."\n".
                   ('&nbsp;'x2).                    ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="ltitools_add_key" value="" /></span> '."\n".                    '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="ltitools_add_key" value="" /></span> '."\n".
                   ('&nbsp;'x2).                    ('&nbsp;'x2).
                     '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="5" name="ltitools_add_lifetime" value="300" /></span> '."\n".
                     ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="ltitools_add_secret" value="" />'.                    '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="ltitools_add_secret" value="" />'.
                   '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_add_secret.type='."'text'".' } else { this.form.ltitools_add_secret.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n".                    '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_add_secret.type='."'text'".' } else { this.form.ltitools_add_secret.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n".
                   '</fieldset>'.                    '</fieldset>'.
Line 4006  sub print_ltitools { Line 5718  sub print_ltitools {
                       '<input type="text" name="ltitools_add_'.$dimen.'" size="5" /></label>'.                        '<input type="text" name="ltitools_add_'.$dimen.'" size="5" /></label>'.
                       ('&nbsp;'x2);                        ('&nbsp;'x2);
     }      }
     $datatable .= '<br />'.      $datatable .= '</span><br />'.
                   '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.                    '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'.
                   '<input type="text" name="ltitools_add_linktext" size="5" /></label></div>'.                    '<input type="text" name="ltitools_add_linktext" size="5" /></div>'.
                   '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.                    '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'.
                   '<textarea name=ltitools_add_explanation" rows="5" cols="40"></textarea>'.                    '<textarea name="ltitools_add_explanation" rows="5" cols="40"></textarea>'.
                   '</div><div style=""></div><br />';                    '</div><div style=""></div><br />';
       my %units = (
                     'passback' => 'days',
                     'roster'   => 'seconds',
                   );
       my %defaulttimes = (
                        'passback' => '7',
                        'roster'   => '300',
                      );
     foreach my $extra ('passback','roster') {      foreach my $extra ('passback','roster') {
         $datatable .= $lt{$extra}.'&nbsp;'.          my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','add'".');"';
                       '<label><input type="radio" name="ltitools_add_'.$extra.'" value="1" />'.          $datatable .= '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{$extra}.'&nbsp;'.
                       &mt('Yes').'</label>'.('&nbsp;'x2).                        '<label><input type="radio" name="ltitools_'.$extra.'_add" value="0" checked="checked"'.$onclick.' />'.
                       '<label><input type="radio" name="ltitools_add_'.$extra.'" value="0" checked="checked" />'.                        &mt('No').'</label></span>'.('&nbsp;'x2).'<span class="LC_nobreak">'.
                       &mt('No').'</label>'.('&nbsp;'x4);                        '<label><input type="radio" name="ltitools_'.$extra.'_add" value="1"'.$onclick.' />'.
                         &mt('Yes').'</label></span></div>'.
                         '<div class="LC_floatleft" style="display:none;" id="ltitools_'.$extra.'time_add">'.
                         '<span class="LC_nobreak">'.
                         &mt("at least [_1] $units{$extra} after launch",
                             '<input type="text" name="ltitools_'.$extra.'valid_add" value="'.$defaulttimes{$extra}.'" />').
                         '</span></div><div style="padding:0;clear:both;margin:0;border:0"></div>';
     }      }
     $datatable .= '<br /><br /><span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;'.      $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;'.
                   '('.&mt('if larger than 21x21 pixels, image will be scaled').')&nbsp;';                    '('.&mt('if larger than 21x21 pixels, image will be scaled').')&nbsp;';
     if ($switchserver) {      if ($switchserver) {
         $datatable .= &mt('Upload to library server: [_1]',$switchserver);          $datatable .= &mt('Upload to library server: [_1]',$switchserver);
Line 4030  sub print_ltitools { Line 5756  sub print_ltitools {
                   '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'.                    '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'.
                   '<span class="LC_nobreak">';                    '<span class="LC_nobreak">';
     foreach my $field (@fields) {      foreach my $field (@fields) {
           my ($id,$onclick,$spacer);
           if ($field eq 'user') {
               $id = ' id="ltitools_user_field_add"';
               $onclick = ' onclick="toggleLTITools(this.form,'."'$field','add'".')"';
           } else {
               $spacer = ('&nbsp;' x2);
           }
         $datatable .= '<label>'.          $datatable .= '<label>'.
                       '<input type="checkbox" name="ltitools_add_fields" value="'.$field.'" />'.                        '<input type="checkbox" name="ltitools_add_fields" value="'.$field.'"'.$id.$onclick.' />'.
                       $lt{$field}.'</label>'.('&nbsp;' x2);                        $lt{$field}.'</label>'.$spacer;
     }      }
     $datatable .= '</span></fieldset>'.      $datatable .= '</span>'.
                   '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';                    '<div style="display:none;" id="ltitools_user_div_add">'.
                     '<span class="LC_nobreak"> : '.
                     '<select name="ltitools_userincdom_add">'.
                     '<option value="" selected="selected">'.&mt('Select').'</option>'.
                     '<option value="0">'.&mt('username').'</option>'.
                     '<option value="1">'.&mt('username:domain').'</option>'.
                     '</select></span></div></fieldset>';
       $datatable .= '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>';
     foreach my $role (@courseroles) {      foreach my $role (@courseroles) {
         my ($checked,$checkednone);          my ($checked,$checkednone);
         $datatable .= '<td align="center">'.          $datatable .= '<td style="text-align: center">'.
                       &Apache::lonnet::plaintext($role,'Course').'<br />'.                        &Apache::lonnet::plaintext($role,'Course').'<br />'.
                       '<select name="ltitools_add_roles_'.$role.'">'.                        '<select name="ltitools_add_roles_'.$role.'">'.
                       '<option value="" selected="selected">'.&mt('Select').'</option>';                        '<option value="" selected="selected">'.&mt('Select').'</option>';
Line 4049  sub print_ltitools { Line 5789  sub print_ltitools {
     }      }
     $datatable .= '</tr></table></fieldset>'.      $datatable .= '</tr></table></fieldset>'.
                   '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">';                    '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">';
     foreach my $item ('label','title','target','linktext','explanation') {      foreach my $item ('label','title','target','linktext','explanation','append') {
         $datatable .= '<label>'.          $datatable .= '<label>'.
                       '<input type="checkbox" name="ltitools_courseconfig" value="'.$item.'" checked="checked" />'.                        '<input type="checkbox" name="ltitools_courseconfig" value="'.$item.'" checked="checked" />'.
                       $lt{'crs'.$item}.'</label>'.('&nbsp;' x2)."\n";                        $lt{'crs'.$item}.'</label>'.('&nbsp;' x2)."\n";
Line 4061  sub print_ltitools { Line 5801  sub print_ltitools {
                   '<label><input type="checkbox" name="ltitools_add_custom" value="1" />'.                    '<label><input type="checkbox" name="ltitools_add_custom" value="1" />'.
                   &mt('Add').'</label></span></td><td><input type="text" name="ltitools_add_custom_name" />'.                    &mt('Add').'</label></span></td><td><input type="text" name="ltitools_add_custom_name" />'.
                   '</td><td><input type="text" name="ltitools_add_custom_value" /></td></tr>'.                    '</td><td><input type="text" name="ltitools_add_custom_value" /></td></tr>'.
                   '</table></fieldset></td></tr>'."\n".                    '</table></fieldset>'."\n".
                   '</td>'."\n".                    '</td>'."\n".
                   '</tr>'."\n";                    '</tr>'."\n";
     $itemcount ++;      $itemcount ++;
Line 4073  sub ltitools_names { Line 5813  sub ltitools_names {
                                           'title'          => 'Title',                                            'title'          => 'Title',
                                           'version'        => 'Version',                                            'version'        => 'Version',
                                           'msgtype'        => 'Message Type',                                            'msgtype'        => 'Message Type',
                                             'sigmethod'      => 'Signature Method',
                                           'url'            => 'URL',                                            'url'            => 'URL',
                                           'key'            => 'Key',                                            'key'            => 'Key',
                                             'lifetime'       => 'Nonce lifetime (s)',
                                           'secret'         => 'Secret',                                            'secret'         => 'Secret',
                                           'icon'           => 'Icon',                                            'icon'           => 'Icon',   
                                           'user'           => 'Username:domain',                                            'user'           => 'User',
                                           'fullname'       => 'Full Name',                                            'fullname'       => 'Full Name',
                                           'firstname'      => 'First Name',                                            'firstname'      => 'First Name',
                                           'lastname'       => 'Last Name',                                            'lastname'       => 'Last Name',
Line 4094  sub ltitools_names { Line 5836  sub ltitools_names {
                                           'roster'         => 'Tool can retrieve roster:',                                            'roster'         => 'Tool can retrieve roster:',
                                           'crstarget'      => 'Display target',                                            'crstarget'      => 'Display target',
                                           'crslabel'       => 'Course label',                                            'crslabel'       => 'Course label',
                                           'crstitle'       => 'Course title',                                            'crstitle'       => 'Course title', 
                                           'crslinktext'    => 'Link Text',                                            'crslinktext'    => 'Link Text',
                                           'crsexplanation' => 'Explanation',                                            'crsexplanation' => 'Explanation',
                                             'crsappend'      => 'Provider URL',
                                         );                                          );
       return %lt;
   }
   
   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;      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') {
           my @ids=&Apache::lonnet::current_machine_ids();
           my %servers = &Apache::lonnet::get_servers($dom,'library');
           my $primary = &Apache::lonnet::domain($dom,'primary');
           my ($extra,$numshown);
           foreach my $hostid (sort(keys(%servers))) {
               my ($showextra,$divsty,$switch);
               if ($hostid eq $primary) {
                   if (($encrypt{'ltisec_consumers'}) || ($encrypt{'ltisec_domlinkprot'})) {
                       $showextra = 1;
                   }
               }
               if ($encrypt{'ltisec_crslinkprot'}) {
                   $showextra = 1;
               }
               unless (grep(/^\Q$hostid\E$/,@ids)) {
                   $switch = 1;
               }
               if ($showextra) {
                   $numshown ++;
                   $divsty = 'display:inline-block'; 
               } else {
                   $divsty = 'display:none';
               } 
               $extra .= '<fieldset id="ltisec_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="ltisec_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="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" checked="checked" />'.&mt('No').'</label>'.
                                 ('&nbsp;'x2).
                                 '<label><input type="radio" value="1" name="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" />'.&mt('Yes').
                                 '</label>&nbsp;&nbsp;</span><div id="ltisec_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="ltisec_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="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" checked="checked" />'.&mt('No').'</label>'.
                             ('&nbsp;'x2).
                             '<label><input type="radio" value="1" name="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" />'.&mt('Yes').
                             '</label>&nbsp;&nbsp;</span><div id="ltisec_divchgprivkey_'.$hostid.'" style="display:none" />'.
                             '<span class="LC_nobreak">'.&mt('New Key').':'.
                             '<input type="password" size="20" name="ltisec_privkey_'.$hostid.'" value="" autocomplete="new-password" />'.
                             '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltisec_privkey_'.$hostid.'.type='."'text'".' } else { this.form.ltisec_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="ltisec_privkey_'.$hostid.'" value="" autocomplete="new-password" />'.
                             '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltisec_privkey_'.$hostid.'.type='."'text'".' } else { this.form.ltisec_privkey_'.$hostid.'.type='."'password'".' }" />'.&mt('Visible input').'</label>';
               }
               $extra .= '</fieldset>';
           }
           my %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',
                                                     );
           my @toggles = qw(ltisec_crslinkprot ltisec_domlinkprot ltisec_consumers);
           my %defaultchecked = (
                                  'ltisec_crslinkprot' => 'off',
                                  'ltisec_domlinkprot' => 'off',
                                  'ltisec_consumers'   => 'off',
                                );
           my ($onclick,$itemcount);
           $onclick = 'javascript:toggleLTIEncKey(this.form);';
           ($datatable,$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'; 
           }
           $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.&mt('Encryption Key(s)').'</td>'.
                         '<td><div id="ltisec_noprivkey" style="'.$noprivkeysty.'" >'.
                         '<span class="LC_nobreak">'.&mt('Not in use').'</span></div>'.
                         $extra.
                         '</td></tr>';
           $itemcount ++;                
           $$rowtotal += $itemcount;
       } elsif ($position eq 'middle') {
           $datatable = &password_rules('secrets',\$itemcount,\%rules);
           $$rowtotal += $itemcount;
       } elsif ($position eq 'lower') {
            $datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain');
       } else {
           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,$lifetime,$consumer,$requser,$crsinc,$current);
                   if (ref($settings->{$item}) eq 'HASH') {
                       $key = $settings->{$item}->{'key'};
                       $secret = $settings->{$item}->{'secret'};
                       $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>'.
                       ('&nbsp;'x2).
                       '<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).
                       '<span class="LC_nobreak">'.$lt{'key'}.
                       ':<input type="text" size="25" name="lti_key_'.$i.'" value="'.$key.'" /></span> '.
                       ('&nbsp;'x2).
                       '<span class="LC_nobreak">'.$lt{'secret'}.':'.
                       '<input type="password" size="20" name="lti_secret_'.$i.'" value="'.$secret.'" />'.
                       '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.lti_secret_'.$i.'.type='."'text'".' } else { this.form.lti_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'.
                       '<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> '."\n".
                         ('&nbsp;'x2).
                         '<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".
                         ('&nbsp;'x4).
                         '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="lti_key_add" value="" /></span> '."\n".
                         ('&nbsp;'x2).
                         '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="lti_secret_add" value="" />'.
                         '<label><input type="checkbox" name="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".
                         '</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 check_switchserver {
       my ($home) = @_;
       my $switchserver;
       if ($home ne '') {
           my $allowed;
           my @ids=&Apache::lonnet::current_machine_ids();
           foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } }
           if (!$allowed) {
               $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;
   }
   
 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)',
         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',
         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',
     );      );
     my %staticdefaults = (      my %staticdefaults = (
                            anonsurvey_threshold => 10,                             anonsurvey_threshold => 10,
Line 4124  sub print_coursedefaults { Line 7206  sub print_coursedefaults {
                          );                           );
     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;
           if (ref($settings) eq 'HASH') {
               if ($settings->{'texengine'}) {
                   if ($settings->{'texengine'} =~ /^(MathJax|mimetex|tth)$/) {
                       $deftex = $settings->{'texengine'};
                   }
               }
           }
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           my $mathdisp = '<tr'.$css_class.'><td style="vertical-align: top">'.
                          '<span class="LC_nobreak">'.$choices{'texengine'}.
                          '</span></td><td class="LC_right_item">'.
                          '<select name="texengine">'."\n";
           my %texoptions = (
                               MathJax  => 'MathJax',
                               mimetex  => &mt('Convert to Images'),
                               tth      => &mt('TeX to HTML'),
                            );
           foreach my $renderer ('MathJax','mimetex','tth') {
               my $selected = '';
               if ($renderer eq $deftex) {
                   $selected = ' selected="selected"';
               }
               $mathdisp .= '<option value="'.$renderer.'"'.$selected.'>'.$texoptions{$renderer}.'</option>'."\n";
           }
           $mathdisp .= '</select></td></tr>'."\n";
           $itemcount ++;
         ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,          ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked,
                                                      \%choices,$itemcount);                                                       \%choices,$itemcount);
           $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';
         my $onclick;          my $onclick;
         my @cloneoptions = ('none','domain');          my @cloneoptions = ('none','domain');
         my %clonetitles = (          my %clonetitles = &Apache::lonlocal::texthash (
                              none     => 'No additional course requesters',                               none     => 'No additional course requesters',
                              domain   => "Any course requester in course's domain",                               domain   => "Any course requester in course's domain",
                              instcode => 'Course requests for official courses ...',                               instcode => 'Course requests for official courses ...',
Line 4177  sub print_coursedefaults { Line 7289  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 4207  sub print_coursedefaults { Line 7319  sub print_coursedefaults {
         my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql);          my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql);
         my $currusecredits = 0;          my $currusecredits = 0;
         my $postsubmitclient = 1;          my $postsubmitclient = 1;
         my @types = ('official','unofficial','community','textbook');          my $ltiauth = 0;
           my @types = ('official','unofficial','community','textbook','placement');
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
               if ($settings->{'ltiauth'}) {
                   $ltiauth = 1;
               }
             $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'}})) {
Line 4285  sub print_coursedefaults { Line 7401  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>';
         }          }
Line 4303  sub print_coursedefaults { Line 7419  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 4327  sub print_coursedefaults { Line 7443  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 4345  sub print_coursedefaults { Line 7461  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;
           $itemcount ++;
     }      }
     $$rowtotal += $itemcount;      $$rowtotal += $itemcount;
     return $datatable;      return $datatable;
Line 4364  sub print_selfenrollment { Line 7489  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 4548  sub print_validation_rows { Line 7673  sub print_validation_rows {
                               '</label></span> ';                                '</label></span> ';
             }              }
         } elsif ($item eq 'markup') {          } elsif ($item eq 'markup') {
             $datatable .= '<textarea name="'.$caller.'_validation_markup" cols="50" rows="5" wrap="soft">'.              $datatable .= '<textarea name="'.$caller.'_validation_markup" cols="50" rows="5">'.
                            $currvalidation{$item}.                             $currvalidation{$item}.
                               '</textarea>';                                '</textarea>';
         }          }
Line 4570  sub print_validation_rows { Line 7695  sub print_validation_rows {
         my ($numdc,$dctable,$rows) = &active_dc_picker($dom,$numinrow,'radio',          my ($numdc,$dctable,$rows) = &active_dc_picker($dom,$numinrow,'radio',
                                                        'validationdc',%currhash);                                                         'validationdc',%currhash);
         my $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';          my $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
         $datatable .= '</td></tr><tr'.$css_class.'><td>';          $datatable .= '<tr'.$css_class.'><td>';
         if ($numdc > 1) {          if ($numdc > 1) {
             $datatable .= &mt('Course creation processed as: (choose Dom. Coord.)');              $datatable .= &mt('Course creation processed as: (choose Dom. Coord.)');
         } else {          } else {
Line 4585  sub print_validation_rows { Line 7710  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;
       unless ($position eq 'top') {
           @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',
               );
               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 ++;
               }
           } 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 {
       my ($position,$dom,$confname,$settings,$rowtotal) = @_;
       my ($datatable,$css_class);
       my $itemcount = 0;
       my %titles = &Apache::lonlocal::texthash (
           captcha        => '"Forgot Password" CAPTCHA validation',
           link           => 'Reset link expiration (hours)',
           case           => 'Case-sensitive usernames/e-mail',
           prelink        => 'Information required (form 1)',
           postlink       => 'Information required (form 2)',
           emailsrc       => 'LON-CAPA e-mail address type(s)',
           customtext     => 'Domain specific text (HTML)',
           intauth_cost   => 'Encryption cost for bcrypt (positive integer)',
           intauth_check  => 'Check bcrypt cost if authenticated',
           intauth_switch => 'Existing crypt-based switched to bcrypt on authentication',
           permanent      => 'Permanent e-mail address',
           critical       => 'Critical notification address',
           notify         => 'Notification address',
           min            => 'Minimum password length',
           max            => 'Maximum password length',
           chars          => 'Required characters',
           expire         => 'Password expiration (days)',
           numsaved       => 'Number of previous passwords to save and disallow reuse',
       );
       if ($position eq 'top') {
           my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
           my $shownlinklife = 2;
           my $prelink = 'both';
           my (%casesens,%postlink,%emailsrc,$nostdtext,$customurl);
           if (ref($settings) eq 'HASH') {
               if ($settings->{resetlink} =~ /^\d+(|\.\d*)$/) {
                   $shownlinklife = $settings->{resetlink};
               }
               if (ref($settings->{resetcase}) eq 'ARRAY') {
                   map { $casesens{$_} = 1; } (@{$settings->{resetcase}});
               }
               if ($settings->{resetprelink} =~ /^(both|either)$/) {
                   $prelink = $settings->{resetprelink};
               }
               if (ref($settings->{resetpostlink}) eq 'HASH') {
                   %postlink = %{$settings->{resetpostlink}};
               }
               if (ref($settings->{resetemail}) eq 'ARRAY') {
                   map { $emailsrc{$_} = 1; } (@{$settings->{resetemail}});
               }
               if ($settings->{resetremove}) {
                   $nostdtext = 1;
               }
               if ($settings->{resetcustom}) {
                   $customurl = $settings->{resetcustom};
               }
           } else {
               if (ref($types) eq 'ARRAY') {
                   foreach my $item (@{$types}) {
                       $casesens{$item} = 1;
                       $postlink{$item} = ['username','email'];
                   }
               }
               $casesens{'default'} = 1;
               $postlink{'default'} = ['username','email'];
               $prelink = 'both';
               %emailsrc = (
                             permanent => 1,
                             critical  => 1,
                             notify    => 1,
               );
           }
           $datatable = &captcha_choice('passwords',$settings,$$rowtotal);
           $itemcount ++;
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'link'}.'</td>'.
                         '<td class="LC_left_item">'.
                         '<input type="textbox" value="'.$shownlinklife.'" '.
                         'name="passwords_link" size="3" /></td></tr>';
           $itemcount ++;
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'case'}.'</td>'.
                         '<td class="LC_left_item">';
           if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
               foreach my $item (@{$types}) {
                   my $checkedcase;
                   if ($casesens{$item}) {
                       $checkedcase = ' checked="checked"';
                   }
                   $datatable .= '<span class="LC_nobreak"><label>'.
                                 '<input type="checkbox" name="passwords_case_sensitive" value="'.
                                 $item.'"'.$checkedcase.' />'.$usertypes->{$item}.'</label>'.
                                 '</span>&nbsp;&nbsp; ';
               }
           }
           my $checkedcase;
           if ($casesens{'default'}) {
               $checkedcase = ' checked="checked"';
           }
           $datatable .= '<span class="LC_nobreak"><label><input type="checkbox" '.
                         'name="passwords_case_sensitive" value="default"'.$checkedcase.' />'.
                         $othertitle.'</label></span></td>';
           $itemcount ++;
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           my %checkedpre = (
                                both => ' checked="checked"',
                                either => '',
                            );
           if ($prelink eq 'either') {
               $checkedpre{either} = ' checked="checked"';
               $checkedpre{both} = '';
           }
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'prelink'}.'</td>'.
                         '<td class="LC_left_item"><span class="LC_nobreak">'.
                         '<label><input type="radio" name="passwords_prelink" value="both"'.$checkedpre{'both'}.' />'.
                         &mt('Both username and e-mail address').'</label></span>&nbsp;&nbsp; '.
                         '<span class="LC_nobreak"><label>'.
                         '<input type="radio" name="passwords_prelink" value="either"'.$checkedpre{'either'}.' />'.
                         &mt('Either username or e-mail address').'</label></span></td></tr>';
           $itemcount ++;
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'postlink'}.'</td>'.
                         '<td class="LC_left_item">';
           my %postlinked;
           if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
               foreach my $item (@{$types}) {
                   undef(%postlinked);
                   $datatable .= '<fieldset style="display: inline-block;">'.
                                 '<legend>'.$usertypes->{$item}.'</legend>';
                   if (ref($postlink{$item}) eq 'ARRAY') {
                       map { $postlinked{$_} = 1; } (@{$postlink{$item}});
                   }
                   foreach my $field ('email','username') {
                       my $checked;
                       if ($postlinked{$field}) {
                           $checked = ' checked="checked"';
                       }
                       $datatable .= '<span class="LC_nobreak"><label>'.
                                     '<input type="checkbox" name="passwords_postlink_'.$item.'" value="'.
                                     $field.'"'.$checked.' />'.$field.'</label>'.
                                     '<span>&nbsp;&nbsp; ';
                   }
                   $datatable .= '</fieldset>';
               }
           }
           if (ref($postlink{'default'}) eq 'ARRAY') {
               map { $postlinked{$_} = 1; } (@{$postlink{'default'}});
           }
           $datatable .= '<fieldset style="display: inline-block;">'.
                         '<legend>'.$othertitle.'</legend>';
           foreach my $field ('email','username') {
               my $checked;
               if ($postlinked{$field}) {
                   $checked = ' checked="checked"';
               }
               $datatable .= '<span class="LC_nobreak"><label>'.
                             '<input type="checkbox" name="passwords_postlink_default" value="'.
                             $field.'"'.$checked.' />'.$field.'</label>'.
                             '<span>&nbsp;&nbsp; ';
           }
           $datatable .= '</fieldset></td></tr>';
           $itemcount ++;
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'emailsrc'}.'</td>'.
                         '<td class="LC_left_item">';
           foreach my $type ('permanent','critical','notify') {
               my $checkedemail;
               if ($emailsrc{$type}) {
                   $checkedemail = ' checked="checked"';
               }
               $datatable .= '<span class="LC_nobreak"><label>'.
                             '<input type="checkbox" name="passwords_emailsrc" value="'.
                             $type.'"'.$checkedemail.' />'.$titles{$type}.'</label>'.
                             '<span>&nbsp;&nbsp; ';
           }
           $datatable .= '</td></tr>';
           $itemcount ++;
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           my $switchserver = &check_switchserver($dom,$confname);
           my ($showstd,$noshowstd);
           if ($nostdtext) {
               $noshowstd = ' checked="checked"';
           } else {
               $showstd = ' checked="checked"';
           }
           $datatable .= '<tr'.$css_class.'><td>'.$titles{'customtext'}.'</td>'.
                         '<td class="LC_left_item"><span class="LC_nobreak">'.
                         &mt('Retain standard text:').
                         '<label><input type="radio" name="passwords_stdtext" value="1"'.$showstd.' />'.
                         &mt('Yes').'</label>'.'&nbsp;'.
                         '<label><input type="radio" name="passwords_stdtext" value="0"'.$noshowstd.' />'.
                         &mt('No').'</label></span><br />'.
                         '<span class="LC_fontsize_small">'.
                         &mt('(If you use the same account ...  reset a password from this page.)').'</span><br /><br />'.
                         &mt('Include custom text:');
           if ($customurl) {
               my $link =  &Apache::loncommon::modal_link($customurl,&mt('custom text'),600,500,
                                                          undef,undef,undef,undef,'background-color:#ffffff');
               $datatable .= '<span class="LC_nobreak">&nbsp;'.$link.
                             '<label><input type="checkbox" name="passwords_custom_del"'.
                             ' value="1" />'.&mt('Delete?').'</label></span>'.
                             ' <span class="LC_nobreak">&nbsp;'.&mt('Replace:').'</span>';
           }
           if ($switchserver) {
               $datatable .= '<span class="LC_nobreak">&nbsp;'.&mt('Upload to library server: [_1]',$switchserver).'</span>';
           } else {
               $datatable .='<span class="LC_nobreak">&nbsp;'.
                            '<input type="file" name="passwords_customfile" /></span>';
           }
           $datatable .= '</td></tr>';
       } elsif ($position eq 'middle') {
           my %domconf = &Apache::lonnet::get_dom('configuration',['defaults'],$dom);
           my @items = ('intauth_cost','intauth_check','intauth_switch');
           my %defaults;
           if (ref($domconf{'defaults'}) eq 'HASH') {
               %defaults = %{$domconf{'defaults'}};
               if ($defaults{'intauth_cost'} !~ /^\d+$/) {
                   $defaults{'intauth_cost'} = 10;
               }
               if ($defaults{'intauth_check'} !~ /^(0|1|2)$/) {
                   $defaults{'intauth_check'} = 0;
               }
               if ($defaults{'intauth_switch'} !~ /^(0|1|2)$/) {
                   $defaults{'intauth_switch'} = 0;
               }
           } else {
               %defaults = (
                             'intauth_cost'   => 10,
                             'intauth_check'  => 0,
                             'intauth_switch' => 0,
                           );
           }
           foreach my $item (@items) {
               if ($itemcount%2) {
                   $css_class = '';
               } else {
                   $css_class = ' class="LC_odd_row" ';
               }
               $datatable .= '<tr'.$css_class.'>'.
                             '<td><span class="LC_nobreak">'.$titles{$item}.
                             '</span></td><td class="LC_left_item" colspan="3">';
               if ($item eq 'intauth_switch') {
                   my @options = (0,1,2);
                   my %optiondesc = &Apache::lonlocal::texthash (
                                      0 => 'No',
                                      1 => 'Yes',
                                      2 => 'Yes, and copy existing passwd file to passwd.bak file',
                                    );
                   $datatable .= '<table width="100%">';
                   foreach my $option (@options) {
                       my $checked = ' ';
                       if ($defaults{$item} eq $option) {
                           $checked = ' checked="checked"';
                       }
                       $datatable .= '<tr><td class="LC_left_item"><span class="LC_nobreak">'.
                                     '<label><input type="radio" name="'.$item.
                                     '" value="'.$option.'"'.$checked.' />'.
                                     $optiondesc{$option}.'</label></span></td></tr>';
                   }
                   $datatable .= '</table>';
               } elsif ($item eq 'intauth_check') {
                   my @options = (0,1,2);
                   my %optiondesc = &Apache::lonlocal::texthash (
                                      0 => 'No',
                                      1 => 'Yes, allow login then update passwd file using default cost (if higher)',
                                      2 => 'Yes, disallow login if stored cost is less than domain default',
                                    );
                   $datatable .= '<table width="100%">';
                   foreach my $option (@options) {
                       my $checked = ' ';
                       my $onclick;
                       if ($defaults{$item} eq $option) {
                           $checked = ' checked="checked"';
                       }
                       if ($option == 2) {
                           $onclick = ' onclick="javascript:warnIntAuth(this);"';
                       }
                       $datatable .= '<tr><td class="LC_left_item"><span class="LC_nobreak">'.
                                     '<label><input type="radio" name="'.$item.
                                     '" value="'.$option.'"'.$checked.$onclick.' />'.
                                     $optiondesc{$option}.'</label></span></td></tr>';
                   }
                   $datatable .= '</table>';
               } else {
                   $datatable .= '<input type="text" name="'.$item.'" value="'.
                                 $defaults{$item}.'" size="3" onblur="javascript:warnIntAuth(this);" />';
               }
               $datatable .= '</td></tr>';
               $itemcount ++;
           }
       } elsif ($position eq 'lower') {
           $datatable .= &password_rules('passwords',\$itemcount,$settings);
       } else {
           my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
           my %ownerchg = (
                             by  => {},
                             for => {},
                          );
           my %ownertitles = &Apache::lonlocal::texthash (
                               by  => 'Course owner status(es) allowed',
                               for => 'Student status(es) allowed',
                             );
           if (ref($settings) eq 'HASH') {
               if (ref($settings->{crsownerchg}) eq 'HASH') {
                   if (ref($settings->{crsownerchg}{'by'}) eq 'ARRAY') {
                       map { $ownerchg{by}{$_} = 1; } (@{$settings->{crsownerchg}{'by'}});
                   }
                   if (ref($settings->{crsownerchg}{'for'}) eq 'ARRAY') {
                       map { $ownerchg{for}{$_} = 1; } (@{$settings->{crsownerchg}{'for'}});
                   }
               }
           }
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable .= '<tr '.$css_class.'>'.
                         '<td>'.
                         &mt('Requirements').'<ul>'.
                         '<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("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>'.
                         '</ul>'.
                         '</td>'.
                         '<td class="LC_left_item">';
           foreach my $item ('by','for') {
               $datatable .= '<fieldset style="display: inline-block;">'.
                             '<legend>'.$ownertitles{$item}.'</legend>';
               if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
                   foreach my $type (@{$types}) {
                       my $checked;
                       if ($ownerchg{$item}{$type}) {
                           $checked = ' checked="checked"';
                       }
                       $datatable .= '<span class="LC_nobreak"><label>'.
                                     '<input type="checkbox" name="passwords_crsowner_'.$item.'" value="'.
                                     $type.'"'.$checked.' />'.$usertypes->{$type}.'</label>'.
                                     '</span>&nbsp;&nbsp; ';
                   }
               }
               my $checked;
               if ($ownerchg{$item}{'default'}) {
                   $checked = ' checked="checked"';
               }
               $datatable .= '<span class="LC_nobreak"><label><input type="checkbox" '.
                             'name="passwords_crsowner_'.$item.'" value="default"'.$checked.' />'.
                             $othertitle.'</label></span></fieldset>';
           }
           $datatable .= '</td></tr>';
       }
       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 'secrets') {
           %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 {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my $css_class;
       my $itemcount = 0;
       my $datatable;
       my %servers = &Apache::lonnet::internet_dom_servers($dom);
       my (%othercontrol,%otherdoms,%aliases,%saml,%values,$setdom,$showdom);
       my %lt = &wafproxy_titles();
       foreach my $server (sort(keys(%servers))) {
           my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server});
           next if ($serverhome eq '');
           my $serverdom;
           if ($serverhome ne $server) {
               $serverdom = &Apache::lonnet::host_domain($serverhome);
               if (($serverdom ne '') && (&Apache::lonnet::domain($serverdom) ne '')) {
                   $othercontrol{$server} = $serverdom;
               }
           } else {
               $serverdom = &Apache::lonnet::host_domain($server);
               next if (($serverdom eq '') || (&Apache::lonnet::domain($serverdom) eq ''));
               if ($serverdom ne $dom) {
                   $othercontrol{$server} = $serverdom;
               } else {
                   $setdom = 1;
                   if (ref($settings) eq 'HASH') {
                       if (ref($settings->{'alias'}) eq 'HASH') {
                           $aliases{$dom} = $settings->{'alias'};
                           if ($aliases{$dom} ne '') {
                               $showdom = 1;
                           }
                       }
                       if (ref($settings->{'saml'}) eq 'HASH') {
                           $saml{$dom} = $settings->{'saml'};
                       }
                   }
               }
           }
       }
       if ($setdom) {
           %{$values{$dom}} = ();
           if (ref($settings) eq 'HASH') {
               foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') {
                   $values{$dom}{$item} = $settings->{$item};
               }
           }
       }
       if (keys(%othercontrol)) {
           %otherdoms = reverse(%othercontrol);
           foreach my $domain (keys(%otherdoms)) {
               %{$values{$domain}} = ();
               my %config = &Apache::lonnet::get_dom('configuration',['wafproxy'],$domain);
               if (ref($config{'wafproxy'}) eq 'HASH') {
                   $aliases{$domain} = $config{'wafproxy'}{'alias'};
                   if (exists($config{'wafproxy'}{'saml'})) { 
                       $saml{$domain} = $config{'wafproxy'}{'saml'};
                   }
                   foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') {
                       $values{$domain}{$item} = $config{'wafproxy'}{$item};
                   }
               }
           }
       }
       if ($position eq 'top') {
           my %servers = &Apache::lonnet::internet_dom_servers($dom);
           my %aliasinfo;
           foreach my $server (sort(keys(%servers))) {
               $itemcount ++;
               my $dom_in_effect;
               my $aliasrows = '<tr>'.
                               '<td class="LC_left_item" style="vertical-align: baseline;">'.
                               &mt('Hostname').':&nbsp;'.
                               '<i>'.&Apache::lonnet::hostname($server).'</i></td><td>&nbsp;</td>';
               if ($othercontrol{$server}) {
                   $dom_in_effect = $othercontrol{$server};
                   my ($current,$forsaml);
                   if (ref($aliases{$dom_in_effect}) eq 'HASH') {
                       $current = $aliases{$dom_in_effect}{$server};
                   }
                   if (ref($saml{$dom_in_effect}) eq 'HASH') {
                       if ($saml{$dom_in_effect}{$server}) {
                           $forsaml = 1;
                       }
                   }
                   $aliasrows .= '<td class="LC_left_item" style="vertical-align: baseline;">'.
                                 &mt('Alias').':&nbsp';
                   if ($current) {
                       $aliasrows .= $current;
                       if ($forsaml) {
                            $aliasrows .= '&nbsp;('.&mt('also for SSO Auth').')';
                       }
                   } else {
                       $aliasrows .= &mt('None');
                   }
                   $aliasrows .= '&nbsp;<span class="LC_small">('.
                                 &mt('controlled by domain: [_1]',
                                     '<b>'.$dom_in_effect.'</b>').')</span></td>';
               } else {
                   $dom_in_effect = $dom;
                   my ($current,$samlon,$samloff);
                   $samloff = ' checked="checked"';
                   if (ref($aliases{$dom}) eq 'HASH') {
                       if ($aliases{$dom}{$server}) {
                           $current = $aliases{$dom}{$server};
                       }
                   }
                   if (ref($saml{$dom}) eq 'HASH') {
                       if ($saml{$dom}{$server}) {
                           $samlon = $samloff;
                           undef($samloff);
                       }
                   }
                   $aliasrows .= '<td class="LC_left_item" style="vertical-align: baseline;">'.
                                 &mt('Alias').':&nbsp;'.
                                 '<input type="text" name="wafproxy_alias_'.$server.'" '.
                                 'value="'.$current.'" size="30" />'.
                                 ('&nbsp;'x2).'<span class="LC_nobreak">'.
                                 &mt('Alias used for SSO Auth').':&nbsp;<label>'.
                                 '<input type="radio" value="0"'.$samloff.' name="wafproxy_alias_saml_'.$server.'" />'.
                                 &mt('No').'</label>&nbsp;<label>'. 
                                 '<input type="radio" value="1"'.$samlon.' name="wafproxy_alias_saml_'.$server.'" />'.
                                 &mt('Yes').'</label></span>'.
                                 '</td>'; 
               }
               $aliasrows .= '</tr>';
               $aliasinfo{$dom_in_effect} .= $aliasrows;
           }
           if ($aliasinfo{$dom}) {
               my ($onclick,$wafon,$wafoff,$showtable);
               $onclick = ' onclick="javascript:toggleWAF();"';
               $wafoff = ' checked="checked"';
               $showtable = ' style="display:none";';
               if ($showdom) {
                   $wafon = $wafoff;
                   $wafoff = '';
                   $showtable = ' style="display:inline;"';
               }
               $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
               $datatable = '<tr'.$css_class.'>'.
                            '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$dom.'</b>').'<br />'.
                            '<span class="LC_nobreak">'.&mt('WAF in use?').'&nbsp;<label>'.
                            '<input type="radio" name="wafproxy_'.$dom.'" value="1"'.$wafon.$onclick.' />'.
                            &mt('Yes').'</label>'.('&nbsp;'x2).'<label>'.
                            '<input type="radio" name="wafproxy_'.$dom.'" value="0"'.$wafoff.$onclick.' />'.
                            &mt('No').'</label></span></td>'.
                            '<td class="LC_left_item">'.
                            '<table id="wafproxy_table"'.$showtable.'>'.$aliasinfo{$dom}.
                            '</table></td></tr>';
               $itemcount++;
           }
           if (keys(%otherdoms)) {
               foreach my $key (sort(keys(%otherdoms))) {
                   $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
                   $datatable .= '<tr'.$css_class.'>'.
                                 '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$key.'</b>').'</td>'.
                                 '<td class="LC_left_item"><table>'.$aliasinfo{$key}.
                                 '</table></td></tr>';
                   $itemcount++;
               }
           }
       } else {
           my %ip_methods = &remoteip_methods();
           if ($setdom) {
               $itemcount ++;
               $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
               my ($nowafstyle,$wafstyle,$curr_remotip,$currwafdisplay,$vpndircheck,$vpnaliascheck,
                   $currwafvpn,$wafrangestyle,$alltossl,$ssltossl);
               $wafstyle = ' style="display:none;"';
               $nowafstyle = ' style="display:table-row;"';
               $currwafdisplay = ' style="display: none"';
               $wafrangestyle = ' style="display: none"';
               $curr_remotip = 'n';
               $ssltossl = ' checked="checked"';
               if ($showdom) {
                   $wafstyle = ' style="display:table-row;"';
                   $nowafstyle =  ' style="display:none;"';
                   if (keys(%{$values{$dom}})) {
                       if ($values{$dom}{remoteip} =~ /^[nmh]$/) {
                           $curr_remotip = $values{$dom}{remoteip};
                       }
                       if ($curr_remotip eq 'h') {
                           $currwafdisplay = ' style="display:table-row"';
                           $wafrangestyle = ' style="display:inline-block;"';
                       }
                       if ($values{$dom}{'sslopt'}) {
                           $alltossl = ' checked="checked"';
                           $ssltossl = '';
                       }
                   }
                   if (($values{$dom}{'vpnint'} ne '') || ($values{$dom}{'vpnext'} ne '')) {
                       $vpndircheck = ' checked="checked"';
                       $currwafvpn = ' style="display:table-row;"';
                       $wafrangestyle = ' style="display:inline-block;"';
                   } else {
                       $vpnaliascheck = ' checked="checked"';
                       $currwafvpn = ' style="display:none;"';
                   }
               }
               $datatable .= '<tr'.$css_class.' id="nowafproxyrow_'.$dom.'"'.$wafstyle.'>'.
                             '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$dom.'</b>').'</td>'.
                             '<td class="LC_right_item">'.&mt('WAF not in use, nothing to set').'</td>'.
                             '</tr>'.
                             '<tr'.$css_class.' id="wafproxyrow_'.$dom.'"'.$wafstyle.'>'.
                             '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$dom.'</b>').'<br /><br />'.
                             '<div id="wafproxyranges_'.$dom.'">'.&mt('Format for comma separated IP ranges').':<br />'.
                             &mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'<br />'.
                             &mt('Range(s) stored in CIDR notation').'</div></td>'.
                             '<td class="LC_left_item"><table>'.
                             '<tr>'.
                             '<td valign="top">'.$lt{'remoteip'}.':&nbsp;'.
                             '<select name="wafproxy_remoteip" id="wafproxy_remoteip" onchange="javascript:updateWAF();">';
               foreach my $option ('m','h','n') {
                   my $sel;
                   if ($option eq $curr_remotip) {
                      $sel = ' selected="selected"';
                   }
                   $datatable .= '<option value="'.$option.'"'.$sel.'>'.
                                 $ip_methods{$option}.'</option>';
               }
               $datatable .= '</select></td></tr>'."\n".
                             '<tr id="wafproxy_header"'.$currwafdisplay.'><td>'.
                             $lt{'ipheader'}.':&nbsp;'.
                             '<input type="text" value="'.$values{$dom}{'ipheader'}.'" '.
                             'name="wafproxy_ipheader" />'.
                             '</td></tr>'."\n".
                             '<tr id="wafproxy_trust"'.$currwafdisplay.'><td>'.
                             $lt{'trusted'}.':<br />'.
                             '<textarea name="wafproxy_trusted" rows="3" cols="80">'.
                             $values{$dom}{'trusted'}.'</textarea>'.
                             '</td></tr>'."\n".
                             '<tr><td><hr /></td></tr>'."\n".
                             '<tr>'.
                             '<td valign="top">'.$lt{'vpnaccess'}.':<br /><span class="LC_nobreak">'.
                             '<label><input type="radio" name="wafproxy_vpnaccess"'.$vpndircheck.' value="1" onclick="javascript:checkWAF();" />'.
                             $lt{'vpndirect'}.'</label>'.('&nbsp;'x2).
                             '<label><input type="radio" name="wafproxy_vpnaccess"'.$vpnaliascheck.' value="0" onclick="javascript:checkWAF();" />'.
                             $lt{'vpnaliased'}.'</label></span></td></tr>';
               foreach my $item ('vpnint','vpnext') {
                   $datatable .= '<tr id="wafproxy_show_'.$item.'"'.$currwafvpn.'>'.
                                 '<td valign="top">'.$lt{$item}.':<br />'.
                                 '<textarea name="wafproxy_'.$item.'" rows="3" cols="80">'.
                                 $values{$dom}{$item}.'</textarea>'.
                                 '</td></tr>'."\n";
               }
               $datatable .= '<tr><td><hr /></td></tr>'."\n".
                             '<tr>'.
                             '<td valign="top">'.$lt{'sslopt'}.':<br /><span class="LC_nobreak">'.
                             '<label><input type="radio" name="wafproxy_sslopt"'.$alltossl.' value="1" />'.
                             $lt{'alltossl'}.'</label>'.('&nbsp;'x2).
                             '<label><input type="radio" name="wafproxy_sslopt"'.$ssltossl.' value="0" />'.
                             $lt{'ssltossl'}.'</label></span></td></tr>'."\n".
                             '</table></td></tr>';
           }
           if (keys(%otherdoms)) {
               foreach my $domain (sort(keys(%otherdoms))) {
                   $itemcount ++;
                   $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
                   $datatable .= '<tr'.$css_class.'>'.
                                 '<td class="LC_left_item">'.&mt('Domain: [_1]','<b>'.$domain.'</b>').'</td>'.
                                 '<td class="LC_left_item"><table>';
                   foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
                       my $showval = &mt('None');
                       if ($item eq 'ssl') {
                           $showval = $lt{'ssltossl'};
                       }
                       if ($values{$domain}{$item}) {
                           $showval = $values{$domain}{$item};
                           if ($item eq 'ssl') {
                               $showval = $lt{'alltossl'};
                           } elsif ($item eq 'remoteip') {
                               $showval = $ip_methods{$values{$domain}{$item}};
                           }
                       }
                       $datatable .= '<tr>'.
                                     '<td>'.$lt{$item}.':&nbsp;'.$showval.'</td></tr>';
                   }
                   $datatable .= '</table></td></tr>';
               }
           }
       }
       $$rowtotal += $itemcount;
       return $datatable;
   }
   
   sub wafproxy_titles {
       return &Apache::lonlocal::texthash(
                  remoteip   => "Method for determining user's IP",
                  ipheader   => 'Request header containing remote IP',
                  trusted    => 'Trusted IP range(s)',
                  vpnaccess  => 'Access from institutional VPN',
                  vpndirect  => 'via regular hostname (no WAF)',
                  vpnaliased => 'via aliased hostname (WAF)',
                  vpnint     => 'Internal IP Range(s) for VPN sessions',
                  vpnext     => 'IP Range(s) for backend WAF connections',
                  sslopt     => 'Forwarding http/https',
                  alltossl   => 'WAF forwards both http and https requests to https',
                  ssltossl   => 'WAF forwards http requests to http and https to https',
              );
   }
   
   sub remoteip_methods {
       return &Apache::lonlocal::texthash(
                 m => 'Use Apache mod_remoteip',
                 h => 'Use headers parsed by LON-CAPA',
                 n => 'Not in use',
              );
   }
   
 sub print_usersessions {  sub print_usersessions {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my ($css_class,$datatable,%checked,%choices);      my ($css_class,$datatable,$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);
             my $curroffloadnow;              my ($curroffloadnow,$curroffloadoth);
             if (ref($settings) eq 'HASH') {              if (ref($settings) eq 'HASH') {
                 if (ref($settings->{'offloadnow'}) eq 'HASH') {                  if (ref($settings->{'offloadnow'}) eq 'HASH') {
                     $curroffloadnow = $settings->{'offloadnow'};                      $curroffloadnow = $settings->{'offloadnow'};
                 }                  }
                   if (ref($settings->{'offloadoth'}) eq 'HASH') {
                       $curroffloadoth = $settings->{'offloadoth'};
                   }
             }              }
             $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,$curroffloadnow,$rowtotal);              my $other_insts = scalar(keys(%by_location));
               $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,
                                         $other_insts,$curroffloadnow,$curroffloadoth,$rowtotal);
         } else {          } else {
             $datatable .= '<tr'.$css_class.'><td colspan="2">'.              $datatable .= '<tr'.$css_class.'><td colspan="2">'.
                           &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.');                            &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.').
                             '</td></tr>';
         }          }
     } else {      } else {
         if (keys(%by_location) == 0) {          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 .= '</select> ';                      $selector .= ' <option value="'.$version.'"'.
                     $datatable .= &mt('remote server must be version: [_1] or later',$selector);                                   $selected.'>'.$version.'</option>';
                 } else {                  }
                     $datatable.= '<div><input type="button" value="'.&mt('check all').'" '.                  $selector .= '</select> ';
                                  'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.                  $datatable .= &mt('remote server must be version: [_1] or later',$selector);
                                  ' />'.('&nbsp;'x2).              } else {
                                  '<input type="button" value="'.&mt('uncheck all').'" '.                  $datatable.= '<div><input type="button" value="'.&mt('check all').'" '.
                                  'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.                               'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.
                                  "\n".                               ' />'.('&nbsp;'x2).
                                  '</div><div><table>';                               '<input type="button" value="'.&mt('uncheck all').'" '.
                     my $rem;                               'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.
                     for (my $i=0; $i<@locations; $i++) {                               "\n".
                         my ($showloc,$value,$checkedtype);                               '</div><div><table>';
                         if (ref($by_location{$locations[$i]}) eq 'ARRAY') {                  my $rem;
                             my $ip = $by_location{$locations[$i]}->[0];                  for (my $i=0; $i<@locations; $i++) {
                             if (ref($by_ip{$ip}) eq 'ARRAY') {                      my ($showloc,$value,$checkedtype);
                                  $value = join(':',@{$by_ip{$ip}});                      if (ref($by_location->{$locations[$i]}) eq 'ARRAY') {
                                 $showloc = join(', ',@{$by_ip{$ip}});                          my $ip = $by_location->{$locations[$i]}->[0];
                                 if (ref($current{$type}) eq 'ARRAY') {                          if (ref($by_ip->{$ip}) eq 'ARRAY') {
                                     foreach my $loc (@{$by_ip{$ip}}) {                                $value = join(':',@{$by_ip->{$ip}});
                                         if (grep(/^\Q$loc\E$/,@{$current{$type}})) {                              $showloc = join(', ',@{$by_ip->{$ip}});
                                             $checkedtype = ' checked="checked"';                              if (ref($current{$type}) eq 'ARRAY') {
                                             last;                                  foreach my $loc (@{$by_ip->{$ip}}) {
                                         }                                      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 4727  sub print_usersessions { Line 8907  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 4747  sub build_location_hashes { Line 8977  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 4855  sub current_offloads_to { Line 9091  sub current_offloads_to {
 }  }
   
 sub spares_row {  sub spares_row {
     my ($dom,$servers,$spareid,$serverhomes,$altids,$curroffloadnow,$rowtotal) = @_;      my ($dom,$servers,$spareid,$serverhomes,$altids,$other_insts,
           $curroffloadnow,$curroffloadoth,$rowtotal) = @_;
     my $css_class;      my $css_class;
     my $numinrow = 4;      my $numinrow = 4;
     my $itemcount = 1;      my $itemcount = 1;
Line 4875  sub spares_row { Line 9112  sub spares_row {
                 }                  }
             }              }
             next unless (ref($spareid->{$server}) eq 'HASH');              next unless (ref($spareid->{$server}) eq 'HASH');
             my $checkednow;              my ($checkednow,$checkedoth);
             if (ref($curroffloadnow) eq 'HASH') {              if (ref($curroffloadnow) eq 'HASH') {
                 if ($curroffloadnow->{$server}) {                  if ($curroffloadnow->{$server}) {
                     $checkednow = ' checked="checked"';                      $checkednow = ' checked="checked"';
                 }                  }
             }              }
               if (ref($curroffloadoth) eq 'HASH') {
                   if ($curroffloadoth->{$server}) {
                       $checkedoth = ' checked="checked"';
                   }
               }
             $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';              $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
             $datatable .= '<tr'.$css_class.'>              $datatable .= '<tr'.$css_class.'>
                            <td rowspan="2">                             <td rowspan="2">
Line 4889  sub spares_row { Line 9131  sub spares_row {
                               ,'<b>'.$server.'</b>').'</span><br />'.                                ,'<b>'.$server.'</b>').'</span><br />'.
                           '<span class="LC_nobreak">'."\n".                            '<span class="LC_nobreak">'."\n".
                           '<label><input type="checkbox" name="offloadnow" value="'.$server.'"'.$checkednow.' />'.                            '<label><input type="checkbox" name="offloadnow" value="'.$server.'"'.$checkednow.' />'.
                           '&nbsp;'.&mt('Switch active users on next access').'</label></span>'.                            '&nbsp;'.&mt('Switch any active user on next access').'</label></span>'.
                             "\n";
               if ($other_insts) {
                   $datatable .= '<br />'.
                                 '<span class="LC_nobreak">'."\n".
                             '<label><input type="checkbox" name="offloadoth" value="'.$server.'"'.$checkedoth.' />'.
                             '&nbsp;'.&mt('Switch other institutions on next access').'</label></span>'.
                           "\n";                            "\n";
               }
             my (%current,%canselect);              my (%current,%canselect);
             my @choices =               my @choices = 
                 &possible_newspares($server,$spareid->{$server},$serverhomes,$altids);                  &possible_newspares($server,$spareid->{$server},$serverhomes,$altids);
Line 5014  sub print_loadbalancing { Line 9263  sub print_loadbalancing {
     my $numinrow = 1;      my $numinrow = 1;
     my $datatable;      my $datatable;
     my %servers = &Apache::lonnet::internet_dom_servers($dom);      my %servers = &Apache::lonnet::internet_dom_servers($dom);
     my (%currbalancer,%currtargets,%currrules,%existing);      my (%currbalancer,%currtargets,%currrules,%existing,%currcookies);
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         %existing = %{$settings};          %existing = %{$settings};
     }      }
     if ((keys(%servers) > 1) || (keys(%existing) > 0)) {      if ((keys(%servers) > 1) || (keys(%existing) > 0)) {
         &get_loadbalancers_config(\%servers,\%existing,\%currbalancer,          &get_loadbalancers_config(\%servers,\%existing,\%currbalancer,
                                   \%currtargets,\%currrules);                                    \%currtargets,\%currrules,\%currcookies);
     } else {      } else {
         return;          return;
     }      }
Line 5054  sub print_loadbalancing { Line 9303  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 5087  sub print_loadbalancing { Line 9336  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 5097  sub print_loadbalancing { Line 9346  sub print_loadbalancing {
         my %hostherechecked = (          my %hostherechecked = (
                                   no => ' checked="checked"',                                    no => ' checked="checked"',
                               );                                );
           my %balcookiechecked = (
                                     no => ' checked="checked"', 
                                  );
         foreach my $sparetype (@sparestypes) {          foreach my $sparetype (@sparestypes) {
             my $targettable;              my $targettable;
             for (my $i=0; $i<$numspares; $i++) {              for (my $i=0; $i<$numspares; $i++) {
Line 5152  sub print_loadbalancing { Line 9404  sub print_loadbalancing {
                 }                  }
             }              }
         }          }
           if ($currcookies{$lonhost}) {
               %balcookiechecked = (
                                       yes => ' checked="checked"',
                                   );
           }
         $datatable .= &mt('Hosting on balancer itself').'<br />'.          $datatable .= &mt('Hosting on balancer itself').'<br />'.
                       '<label><input type="radio" name="loadbalancing_target_'.$balnum.'_hosthere" value="no"'.                        '<label><input type="radio" name="loadbalancing_target_'.$balnum.'_hosthere" value="no"'.
                       $hostherechecked{'no'}.' />'.&mt('No').'</label><br />';                        $hostherechecked{'no'}.' />'.&mt('No').'</label><br />';
Line 5160  sub print_loadbalancing { Line 9417  sub print_loadbalancing {
                           'value="'.$sparetype.'"'.$hostherechecked{$sparetype}.' /><i>'.$typetitles{$sparetype}.                            'value="'.$sparetype.'"'.$hostherechecked{$sparetype}.' /><i>'.$typetitles{$sparetype}.
                           '</i></label><br />';                            '</i></label><br />';
         }          }
         $datatable .= '</div></td></tr>'.          $datatable .= &mt('Use balancer cookie').'<br />'.
                         '<label><input type="radio" name="loadbalancing_cookie_'.$balnum.'" value="1"'.
                         $balcookiechecked{'yes'}.' />'.&mt('Yes').'</label><br />'.
                         '<label><input type="radio" name="loadbalancing_cookie_'.$balnum.'" value="0"'.
                         $balcookiechecked{'no'}.' />'.&mt('No').'</label><br />'.
                         '</div></td></tr>'.
                       &loadbalancing_rules($dom,$intdom,$currrules{$lonhost},                        &loadbalancing_rules($dom,$intdom,$currrules{$lonhost},
                                            $othertitle,$usertypes,$types,\%servers,                                             $othertitle,$usertypes,$types,\%servers,
                                            \%currbalancer,$lonhost,                                             \%currbalancer,$lonhost,
Line 5174  sub print_loadbalancing { Line 9436  sub print_loadbalancing {
 }  }
   
 sub get_loadbalancers_config {  sub get_loadbalancers_config {
     my ($servers,$existing,$currbalancer,$currtargets,$currrules) = @_;      my ($servers,$existing,$currbalancer,$currtargets,$currrules,$currcookies) = @_;
     return unless ((ref($servers) eq 'HASH') &&      return unless ((ref($servers) eq 'HASH') &&
                    (ref($existing) eq 'HASH') && (ref($currbalancer) eq 'HASH') &&                     (ref($existing) eq 'HASH') && (ref($currbalancer) eq 'HASH') &&
                    (ref($currtargets) eq 'HASH') && (ref($currrules) eq 'HASH'));                     (ref($currtargets) eq 'HASH') && (ref($currrules) eq 'HASH') &&
                      (ref($currcookies) eq 'HASH'));
     if (keys(%{$existing}) > 0) {      if (keys(%{$existing}) > 0) {
         my $oldlonhost;          my $oldlonhost;
         foreach my $key (sort(keys(%{$existing}))) {          foreach my $key (sort(keys(%{$existing}))) {
Line 5196  sub get_loadbalancers_config { Line 9459  sub get_loadbalancers_config {
                 $currbalancer->{$key} = 1;                  $currbalancer->{$key} = 1;
                 $currtargets->{$key} = $existing->{$key}{'targets'};                  $currtargets->{$key} = $existing->{$key}{'targets'};
                 $currrules->{$key} = $existing->{$key}{'rules'};                  $currrules->{$key} = $existing->{$key}{'rules'};
                   if ($existing->{$key}{'cookie'}) {
                       $currcookies->{$key} = 1;
                   }
             }              }
         }          }
     } else {      } else {
Line 5299  sub loadbalance_rule_row { Line 9565  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 5391  sub contact_titles { Line 9657  sub contact_titles {
                    'requestsmail'    => 'E-mail from course requests requiring approval',                     'requestsmail'    => 'E-mail from course requests requiring approval',
                    'updatesmail'     => 'E-mail from nightly check of LON-CAPA module integrity/updates',                     'updatesmail'     => 'E-mail from nightly check of LON-CAPA module integrity/updates',
                    'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID',                     'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID',
                      'hostipmail'      => 'E-mail from nightly check of hostname/IP network changes',
                      'errorthreshold'  => 'Error count threshold for status e-mail to admin(s)',
                      'errorsysmail'    => 'Error count threshold for e-mail to developer group',
                      'errorweights'    => 'Weights used to compute error count',
                      'errorexcluded'   => 'Servers with unsent updates excluded from count',
                  );                   );
     my %short_titles = &Apache::lonlocal::texthash (      my %short_titles = &Apache::lonlocal::texthash (
                            adminemail   => 'Admin E-mail address',                             adminemail   => 'Admin E-mail address',
Line 5433  sub tool_titles { Line 9704  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 5447  sub courserequest_titles { Line 9720  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 5537  sub print_usercreation { Line 9812  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 5560  sub print_usercreation { Line 9835  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 5611  sub print_usercreation { Line 9886  sub print_usercreation {
   
 sub print_selfcreation {  sub print_selfcreation {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my (@selfcreate,$createsettings,$processing,$datatable);      my (@selfcreate,$createsettings,$processing,$emailoptions,$emailverified,
           $emaildomain,$datatable);
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if (ref($settings->{'cancreate'}) eq 'HASH') {          if (ref($settings->{'cancreate'}) eq 'HASH') {
             $createsettings = $settings->{'cancreate'};              $createsettings = $settings->{'cancreate'};
Line 5628  sub print_selfcreation { Line 9904  sub print_selfcreation {
                 if (ref($createsettings->{'selfcreateprocessing'}) eq 'HASH') {                  if (ref($createsettings->{'selfcreateprocessing'}) eq 'HASH') {
                     $processing = $createsettings->{'selfcreateprocessing'};                      $processing = $createsettings->{'selfcreateprocessing'};
                 }                  }
                   if (ref($createsettings->{'emailoptions'}) eq 'HASH') {
                       $emailoptions = $createsettings->{'emailoptions'};
                   }
                   if (ref($createsettings->{'emailverified'}) eq 'HASH') {
                       $emailverified = $createsettings->{'emailverified'};
                   }
                   if (ref($createsettings->{'emaildomain'}) eq 'HASH') {
                       $emaildomain = $createsettings->{'emaildomain'};
                   }
             }              }
         }          }
     }      }
Line 5667  sub print_selfcreation { Line 9952  sub print_selfcreation {
         $datatable .= '<tr'.$css_class.'>'.          $datatable .= '<tr'.$css_class.'>'.
                      '<td class="LC_left_item">'.&mt('Mapping of Shibboleth environment variable names to user data fields (SSO auth)').'</td>'.                       '<td class="LC_left_item">'.&mt('Mapping of Shibboleth environment variable names to user data fields (SSO auth)').'</td>'.
                      '<td class="LC_left_item">'."\n".                       '<td class="LC_left_item">'."\n".
                      '<table><tr><td>'."\n";                       '<table>'."\n";
         for (my $i=0; $i<@fields; $i++) {          for (my $i=0; $i<@fields; $i++) {
             $rem = $i%($numperrow);              $rem = $i%($numperrow);
             if ($rem == 0) {              if ($rem == 0) {
Line 5718  sub print_selfcreation { Line 10003  sub print_selfcreation {
         }          }
     } else {      } else {
         my %choices = &Apache::lonlocal::texthash (          my %choices = &Apache::lonlocal::texthash (
                                                       cancreate_email => 'E-mail address as username',                            'cancreate_email' => 'Non-institutional username (via e-mail verification)',
                                                   );                                                    );
         my @toggles = sort(keys(%choices));          my @toggles = sort(keys(%choices));
         my %defaultchecked = (          my %defaultchecked = (
                                'cancreate_email' => 'off',                                 'cancreate_email' => 'off',
                              );                               );
         my $itemcount = 0;          my $customclass = 'LC_selfcreate_email';
           my $classprefix = 'LC_canmodify_emailusername_';
           my $optionsprefix = 'LC_options_emailusername_';
         my $display = 'none';          my $display = 'none';
           my $rowstyle = 'display:none';
         if (grep(/^\Qemail\E$/,@selfcreate)) {          if (grep(/^\Qemail\E$/,@selfcreate)) {
             $display = 'block';              $display = 'block';
               $rowstyle = 'display:table-row';
         }          }
         my $onclick = "toggleDisplay(this.form,'emailoptions');";          my $onclick = "toggleRows(this.form,'cancreate_email','selfassign','$customclass','$classprefix','$optionsprefix');";
         my $additional = '<div id="emailoptions" style="display: '.$display.'">';          ($datatable,$$rowtotal) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked,
                                                        \%choices,$$rowtotal,$onclick);
           $datatable .= &print_requestmail($dom,'selfcreation',$createsettings,$rowtotal,$customclass,
                                            $rowstyle);
           $$rowtotal ++;
           $datatable .= &captcha_choice('cancreate',$createsettings,$$rowtotal,$customclass,
                                         $rowstyle);
           $$rowtotal ++;
           my (@ordered,@posstypes,%usertypeshash);
         my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);          my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
         my (@ordered,%usertypeshash);          my ($emailrules,$emailruleorder) =
         if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') {              &Apache::lonnet::inst_userrules($dom,'email');
             @ordered = @{$domdefaults{'inststatusguest'}};          my $primary_id = &Apache::lonnet::domain($dom,'primary');
         }          my $intdom = &Apache::lonnet::internet_dom($primary_id);
         if (@ordered) {          if (ref($types) eq 'ARRAY') {
             unless (grep(/^default$/,@ordered)) {              @posstypes = @{$types};
                 push(@ordered,'default');          }
           if (@posstypes) {
               unless (grep(/^default$/,@posstypes)) {
                   push(@posstypes,'default');
             }              }
             if (ref($usertypes) eq 'HASH') {              if (ref($usertypes) eq 'HASH') {
                 %usertypeshash = %{$usertypes};                  %usertypeshash = %{$usertypes};
             }              }
               my $currassign;
               if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') {
                   $currassign = {
                                     selfassign => $domdefaults{'inststatusguest'},
                                 };
                   @ordered = @{$domdefaults{'inststatusguest'}};
               } else {
                   $currassign = { selfassign => [] };
               }
               my $onclicktypes = "toggleDataRow(this.form,'selfassign','$customclass','$optionsprefix',);".
                                  "toggleDataRow(this.form,'selfassign','$customclass','$classprefix',1);";
               $datatable .= &insttypes_row($currassign,$types,$usertypes,$dom,
                                            $numinrow,$othertitle,'selfassign',
                                            $rowtotal,$onclicktypes,$customclass,
                                            $rowstyle);
               $$rowtotal ++;
             $usertypeshash{'default'} = $othertitle;              $usertypeshash{'default'} = $othertitle;
             $additional .= '<table><tr>';              foreach my $status (@posstypes) {
             foreach my $status (@ordered) {                  my $css_class;
                 $additional .= '<th>'.$usertypeshash{$status}.'</th>';                  if ($$rowtotal%2) {
             }                      $css_class = 'LC_odd_row ';
             $additional .= '</tr><tr>';                  }
             foreach my $status (@ordered) {                  $css_class .= $customclass;
                 $additional .= '<td>'.&email_as_username($rowtotal,$processing,$status).'</td>';                  my $rowid = $optionsprefix.$status;
                   my $hidden = 1;
                   my $currstyle = 'display:none';
                   if (grep(/^\Q$status\E$/,@ordered)) {
                       $currstyle = $rowstyle;
                       $hidden = 0; 
                   }
                   $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain,
                                                $emailrules,$emailruleorder,$settings,$status,$rowid,
                                                $usertypeshash{$status},$css_class,$currstyle,$intdom);
                   unless ($hidden) {
                       $$rowtotal ++;
                   }
             }              }
             $additional .= '</tr></table>';  
         } else {          } else {
               my $css_class;
               if ($$rowtotal%2) {
                   $css_class = 'LC_odd_row ';
               }
               $css_class .= $customclass;
             $usertypeshash{'default'} = $othertitle;              $usertypeshash{'default'} = $othertitle;
             $additional .= &email_as_username($rowtotal,$processing);              $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain,
                                            $emailrules,$emailruleorder,$settings,'default','',
                                            $othertitle,$css_class,$rowstyle,$intdom);
               $$rowtotal ++;
         }          }
         $additional .= '</div>'."\n";  
   
         ($datatable,$itemcount) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked,  
                                                      \%choices,$$rowtotal,$onclick,$additional);  
         $$rowtotal ++;  
         $datatable .= &print_requestmail($dom,'selfcreation',$createsettings,$rowtotal);  
         $$rowtotal ++;  
         my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info();          my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info();
         $numinrow = 1;          $numinrow = 1;
         foreach my $status (@ordered) {          if (@posstypes) {
             $datatable .= &modifiable_userdata_row('cancreate','emailusername_'.$status,$settings,              foreach my $status (@posstypes) {
                                                    $numinrow,$$rowtotal,\%usertypeshash,$infofields,$infotitles);                  my $rowid = $classprefix.$status;
             $$rowtotal ++;                  my $datarowstyle = 'display:none';
         }                  if (grep(/^\Q$status\E$/,@ordered)) { 
         my ($emailrules,$emailruleorder) =                      $datarowstyle = $rowstyle; 
             &Apache::lonnet::inst_userrules($dom,'email');                  }
         if (ref($emailrules) eq 'HASH') {                  $datatable .= &modifiable_userdata_row('cancreate','emailusername_'.$status,$settings,
             if (keys(%{$emailrules}) > 0) {                                                         $numinrow,$$rowtotal,\%usertypeshash,$infofields,
                 $datatable .= &user_formats_row('email',$settings,$emailrules,                                                         $infotitles,$rowid,$customclass,$datarowstyle);
                                                 $emailruleorder,$numinrow,$$rowtotal);                  unless ($datarowstyle eq 'display:none') {
                 $$rowtotal ++;                      $$rowtotal ++;
                   }
             }              }
           } else {
               $datatable .= &modifiable_userdata_row('cancreate','emailusername_default',$settings,
                                                      $numinrow,$$rowtotal,\%usertypeshash,$infofields,
                                                      $infotitles,'',$customclass,$rowstyle);
         }          }
         $datatable .= &captcha_choice('cancreate',$createsettings,$$rowtotal);  
     }      }
     return $datatable;      return $datatable;
 }  }
   
 sub email_as_username {  sub selfcreate_javascript {
     my ($rowtotal,$processing,$type) = @_;      return <<"ENDSCRIPT";
     my %choices =  
         &Apache::lonlocal::texthash (  <script type="text/javascript">
                                       automatic => 'Automatic approval',  // <![CDATA[
                                       approval  => 'Queued for approval',  
                                     );  function toggleRows(form,radio,checkbox,target,prefix,altprefix) {
     my $output;      var x = document.getElementsByClassName(target);
     foreach my $option ('automatic','approval') {      var insttypes = 0;
         my $checked;      var insttypeRegExp = new RegExp(prefix);
         if (ref($processing) eq 'HASH') {      if ((x.length != undefined) && (x.length > 0)) {
             if ($type eq '') {             if (form.elements[radio].length != undefined) {
                 if (!exists($processing->{'default'})) {              for (var i=0; i<form.elements[radio].length; i++) {
                     if ($option eq 'automatic') {                  if (form.elements[radio][i].checked) {
                         $checked = ' checked="checked"';                      if (form.elements[radio][i].value == 1) {
                           for (var j=0; j<x.length; j++) {
                               if (x[j].id == 'undefined') {
                                   x[j].style.display = 'table-row';
                               } else if (insttypeRegExp.test(x[j].id)) {
                                   insttypes ++;
                               } else {
                                   x[j].style.display = 'table-row';
                               }
                           }
                       } else {
                           for (var j=0; j<x.length; j++) {
                               x[j].style.display = 'none';
                           }
                     }                      }
                       break;
                   }
               }
               if (insttypes > 0) {
                   toggleDataRow(form,checkbox,target,altprefix);
                   toggleDataRow(form,checkbox,target,prefix,1);
               }
           }
       }
       return;
   }
   
   function toggleDataRow(form,checkbox,target,prefix,docount) {
       if (form.elements[checkbox].length != undefined) {
           var count = 0;
           if (docount) {
               for (var i=0; i<form.elements[checkbox].length; i++) {
                   if (form.elements[checkbox][i].checked) {
                       count ++;
                   }
               }
           }
           for (var i=0; i<form.elements[checkbox].length; i++) {
               var type = form.elements[checkbox][i].value;
               if (document.getElementById(prefix+type)) {
                   if (form.elements[checkbox][i].checked) {
                       document.getElementById(prefix+type).style.display = 'table-row';
                       if (count % 2 == 1) {
                           document.getElementById(prefix+type).className = target+' LC_odd_row';
                       } else {
                           document.getElementById(prefix+type).className = target;
                       }
                       count ++;
                 } else {                  } else {
                     if ($processing->{'default'} eq $option) {                      document.getElementById(prefix+type).style.display = 'none';
                         $checked = ' checked="checked"';                  }
               }
           }
       }
       return;
   }
   
   function toggleEmailOptions(form,radio,prefix,altprefix,status) {
       var caller = radio+'_'+status;
       if (form.elements[caller].length != undefined) {
           for (var i=0; i<form.elements[caller].length; i++) {
               if (form.elements[caller][i].checked) {
                   if (document.getElementById(altprefix+'_inst_'+status)) {
                       var curr = form.elements[caller][i].value;
                       if (prefix) {
                           document.getElementById(prefix+'_'+status).style.display = 'none';
                       }
                       document.getElementById(altprefix+'_inst_'+status).style.display = 'none';
                       document.getElementById(altprefix+'_noninst_'+status).style.display = 'none';
                       if (curr == 'custom') {
                           if (prefix) { 
                               document.getElementById(prefix+'_'+status).style.display = 'inline';
                           }
                       } else if (curr == 'inst') {
                           document.getElementById(altprefix+'_inst_'+status).style.display = 'inline';
                       } else if (curr == 'noninst') {
                           document.getElementById(altprefix+'_noninst_'+status).style.display = 'inline';
                     }                      }
                       break;
                 }                  }
             } else {              }
                 if (!exists($processing->{$type})) {          }
                     if ($option eq 'automatic') {      }
                         $checked = ' checked="checked"';  }
   
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   sub noninst_users {
       my ($processing,$emailverified,$emailoptions,$emaildomain,$emailrules,
           $emailruleorder,$settings,$type,$rowid,$typetitle,$css_class,$rowstyle,$intdom) = @_; 
       my $class = 'LC_left_item';
       if ($css_class) {
           $css_class = ' class="'.$css_class.'"'; 
       }
       if ($rowid) {
           $rowid = ' id="'.$rowid.'"';
       }
       if ($rowstyle) {
           $rowstyle = ' style="'.$rowstyle.'"';
       }
       my ($output,$description);
       if ($type eq 'default') {
           $description = &mt('Requests for: [_1]',$typetitle);
       } else {
           $description = &mt('Requests for: [_1] (status self-reported)',$typetitle);
       }
       $output = '<tr'.$css_class.$rowid.$rowstyle.'>'.
                 "<td>$description</td>\n".           
                 '<td class="'.$class.'" colspan="2">'.
                 '<table><tr>';
       my %headers = &Apache::lonlocal::texthash( 
                 approve  => 'Processing',
                 email    => 'E-mail',
                 username => 'Username',
       );
       foreach my $item ('approve','email','username') {
           $output .= '<th>'.$headers{$item}.'</th>';
       }
       $output .= '</tr><tr>';
       foreach my $item ('approve','email','username') {
           $output .= '<td style="vertical-align: top">';
           my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom);
           if ($item eq 'approve') {
               %choices = &Apache::lonlocal::texthash (
                                                        automatic => 'Automatically approved',
                                                        approval  => 'Queued for approval',
                                                      );
               @options = ('automatic','approval');
               $hashref = $processing;
               $defoption = 'automatic';
               $name = 'cancreate_emailprocess_'.$type;
           } elsif ($item eq 'email') {
               %choices = &Apache::lonlocal::texthash (
                                                        any     => 'Any e-mail',
                                                        inst    => 'Institutional only',
                                                        noninst => 'Non-institutional only',
                                                        custom  => 'Custom restrictions',
                                                      );
               @options = ('any','inst','noninst');
               my $showcustom;
               if (ref($emailrules) eq 'HASH') {
                   if (keys(%{$emailrules}) > 0) {
                       push(@options,'custom');
                       $showcustom = 'cancreate_emailrule';
                       if (ref($settings) eq 'HASH') {
                           if (ref($settings->{'email_rule'}) eq 'ARRAY') {
                               foreach my $rule (@{$settings->{'email_rule'}}) {
                                   if (exists($emailrules->{$rule})) {
                                       $hascustom ++;
                                   }
                               }
                           } elsif (ref($settings->{'email_rule'}) eq 'HASH') {
                               if (ref($settings->{'email_rule'}{$type}) eq 'ARRAY') {
                                   foreach my $rule (@{$settings->{'email_rule'}{$type}}) {
                                       if (exists($emailrules->{$rule})) {
                                           $hascustom ++;
                                       }
                                   }
                               }
                           }
                       }
                   }
               }
               $onclick = ' onclick="toggleEmailOptions(this.form,'."'cancreate_emailoptions','$showcustom',".
                                                        "'cancreate_emaildomain','$type'".');"';
               $hashref = $emailoptions;
               $defoption = 'any';
               $name = 'cancreate_emailoptions_'.$type;
           } elsif ($item eq 'username') {
               %choices = &Apache::lonlocal::texthash (
                                                        all    => 'Same as e-mail',
                                                        first  => 'Omit @domain',
                                                        free   => 'Free to choose',
                                                      );
               @options = ('all','first','free');
               $hashref = $emailverified;
               $defoption = 'all';
               $name = 'cancreate_usernameoptions_'.$type;
           }
           foreach my $option (@options) {
               my $checked;
               if (ref($hashref) eq 'HASH') {
                   if ($type eq '') {
                       if (!exists($hashref->{'default'})) {
                           if ($option eq $defoption) {
                               $checked = ' checked="checked"';
                           }
                       } else {
                           if ($hashref->{'default'} eq $option) {
                               $checked = ' checked="checked"';
                           }
                     }                      }
                 } else {                  } else {
                     if ($processing->{$type} eq $option) {                      if (!exists($hashref->{$type})) {
                         $checked = ' checked="checked"';                          if ($option eq $defoption) {
                               $checked = ' checked="checked"';
                           }
                       } else {
                           if ($hashref->{$type} eq $option) {
                               $checked = ' checked="checked"';
                           }
                       }
                   }
               } elsif (($item eq 'email') && ($hascustom)) {
                   if ($option eq 'custom') {
                       $checked = ' checked="checked"';
                   }
               } elsif ($option eq $defoption) {
                   $checked = ' checked="checked"';
               }
               $output .= '<span class="LC_nobreak"><label>'.
                          '<input type="radio" name="'.$name.'"'.
                          $checked.' value="'.$option.'"'.$onclick.' />'.
                          $choices{$option}.'</label></span><br />';
               if ($item eq 'email') {
                   if ($option eq 'custom') {
                       my $id = 'cancreate_emailrule_'.$type;
                       my $display = 'none';
                       if ($checked) {
                           $display = 'inline';
                       }
                       my $numinrow = 2;
                       $output .= '<fieldset id="'.$id.'" style="display:'.$display.';">'.
                                  '<legend>'.&mt('Disallow').'</legend><table>'.
                                  &user_formats_row('email',$settings,$emailrules,
                                                    $emailruleorder,$numinrow,'',$type);
                                 '</table></fieldset>';
                   } elsif (($option eq 'inst') || ($option eq 'noninst')) {
                       my %text = &Apache::lonlocal::texthash (
                                                                inst    => 'must end:',
                                                                noninst => 'cannot end:',
                                                              );
                       my $value;
                       if (ref($emaildomain) eq 'HASH') {
                           if (ref($emaildomain->{$type}) eq 'HASH') {
                               $value = $emaildomain->{$type}->{$option}; 
                           }
                     }                      }
                       if ($value eq '') {
                           $value = '@'.$intdom;
                       }
                       my $condition = 'cancreate_emaildomain_'.$option.'_'.$type;
                       my $display = 'none';
                       if ($checked) {
                           $display = 'inline';
                       }
                       $output .= '<div id="'.$condition.'" style="display:'.$display.';">'.
                                  '<span class="LC_domprefs_email">'.$text{$option}.'</span> '.
                                  '<input type="text" name="'.$condition.'" value="'.$value.'" size="10" />'.
                                  '</div>';
                 }                  }
             }              }
         } elsif ($option eq 'automatic') {  
             $checked = ' checked="checked"';   
         }  
         my $name = 'cancreate_emailprocess';  
         if (($type ne '') && ($type ne 'default')) {  
             $name .= '_'.$type;  
         }  
         $output .= '<span class="LC_nobreak"><label>'.  
                    '<input type="radio" name="'.$name.'"'.  
                    $checked.' value="'.$option.'" />'.  
                    $choices{$option}.'</label></span>';  
         if ($type eq '') {  
             $output .= '&nbsp;';  
         } else {  
             $output .= '<br />';  
         }          }
           $output .= '</td>'."\n";
     }      }
     $$rowtotal ++;      $output .= "</tr></table></td></tr>\n";
     return $output;      return $output;
 }  }
   
 sub captcha_choice {  sub captcha_choice {
     my ($context,$settings,$itemcount) = @_;      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;
     if ($context eq 'cancreate') {      if ($context eq 'cancreate') {
         $rowname = &mt('CAPTCHA validation');          $rowname = &mt('CAPTCHA validation');
     } elsif ($context eq 'login') {      } elsif ($context eq 'login') {
         $rowname =  &mt('"Contact helpdesk" CAPTCHA validation');          $rowname =  &mt('"Contact helpdesk" CAPTCHA validation');
       } elsif ($context eq 'passwords') {
           $rowname = &mt('"Forgot Password" CAPTCHA validation');
           $colspan=1;
     }      }
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if ($settings->{'captcha'}) {          if ($settings->{'captcha'}) {
Line 5872  sub captcha_choice { Line 10432  sub captcha_choice {
     } else {      } else {
         $checked{'original'} = ' checked="checked"';          $checked{'original'} = ' checked="checked"';
     }      }
     my $css_class = $itemcount%2?' class="LC_odd_row"':'';      my $css_class;
       if ($itemcount%2) {
           $css_class = 'LC_odd_row';
       }
       if ($customcss) {
           $css_class .= " $customcss";
       }
       $css_class =~ s/^\s+//;
       if ($css_class) {
           $css_class = ' class="'.$css_class.'"';
       }
       if ($rowstyle) {
           $css_class .= ' style="'.$rowstyle.'"';
       }
     my $output = '<tr'.$css_class.'>'.      my $output = '<tr'.$css_class.'>'.
                  '<td class="LC_left_item">'.$rowname.'</td><td class="LC_left_item" colspan="2">'."\n".                   '<td class="LC_left_item">'.$rowname.'</td><td class="LC_left_item" colspan="'.$colspan.'">'."\n".
                  '<table><tr><td>'."\n";                   '<table><tr><td>'."\n";
     foreach my $option ('original','recaptcha','notused') {      foreach my $option ('original','recaptcha','notused') {
         $output .= '<span class="LC_nobreak"><label><input type="radio" name="'.$context.'_captcha" value="'.          $output .= '<span class="LC_nobreak"><label><input type="radio" name="'.$context.'_captcha" value="'.
Line 5891  sub captcha_choice { Line 10464  sub captcha_choice {
 # specified for use with the key should be broad enough to accommodate all servers in the LON-CAPA domain.  # specified for use with the key should be broad enough to accommodate all servers in the LON-CAPA domain.
 #  #
     $output .= '</td></tr>'."\n".      $output .= '</td></tr>'."\n".
                '<tr><td>'."\n".                 '<tr><td class="LC_zero_height">'."\n".
                '<span class="LC_nobreak"><span id="'.$context.'_recaptchapubtxt">'.$pubtext.'</span>&nbsp;'."\n".                 '<span class="LC_nobreak"><span id="'.$context.'_recaptchapubtxt">'.$pubtext.'</span>&nbsp;'."\n".
                '<input type="'.$keyentry.'" id="'.$context.'_recaptchapub" name="'.$context.'_recaptchapub" value="'.                 '<input type="'.$keyentry.'" id="'.$context.'_recaptchapub" name="'.$context.'_recaptchapub" value="'.
                $currpub.'" size="40" /></span><br />'."\n".                 $currpub.'" size="40" /></span><br />'."\n".
Line 5907  sub captcha_choice { Line 10480  sub captcha_choice {
 }  }
   
 sub user_formats_row {  sub user_formats_row {
     my ($type,$settings,$rules,$ruleorder,$numinrow,$rowcount) = @_;      my ($type,$settings,$rules,$ruleorder,$numinrow,$rowcount,$status) = @_;
     my $output;      my $output;
     my %text = (      my %text = (
                    'username' => 'new usernames',                     'username' => 'new usernames',
                    'id'       => 'IDs',                     'id'       => 'IDs',
                    'email'    => 'self-created accounts (e-mail)',  
                );                 );
     my $css_class = $rowcount%2?' class="LC_odd_row"':'';      unless (($type eq 'email') || ($type eq 'unamemap')) {
     $output = '<tr '.$css_class.'>'.          my $css_class = $rowcount%2?' class="LC_odd_row"':'';
               '<td><span class="LC_nobreak">';          $output = '<tr '.$css_class.'>'.
     if ($type eq 'email') {                    '<td><span class="LC_nobreak">'.
         $output .= &mt("Formats disallowed for $text{$type}: ");                    &mt("Format rules to check for $text{$type}: ").
     } else {                    '</td><td class="LC_left_item" colspan="2"><table>';
         $output .= &mt("Format rules to check for $text{$type}: ");  
     }      }
     $output .= '</span></td>'.  
                '<td class="LC_left_item" colspan="2"><table>';  
     my $rem;      my $rem;
     if (ref($ruleorder) eq 'ARRAY') {      if (ref($ruleorder) eq 'ARRAY') {
         for (my $i=0; $i<@{$ruleorder}; $i++) {          for (my $i=0; $i<@{$ruleorder}; $i++) {
Line 5941  sub user_formats_row { Line 10510  sub user_formats_row {
                         if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}})) {                          if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}})) {
                             $check = ' checked="checked" ';                              $check = ' checked="checked" ';
                         }                          }
                       } elsif ((ref($settings->{$type.'_rule'}) eq 'HASH') && ($status ne '')) {
                           if (ref($settings->{$type.'_rule'}->{$status}) eq 'ARRAY') {
                               if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}->{$status}})) {
                                   $check = ' checked="checked" ';
                               }
                           }
                     }                      }
                 }                  }
                   my $name = $type.'_rule';
                   if ($type eq 'email') {
                       $name .= '_'.$status;
                   }
                 $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="'.$type.'_rule" '.                             '<input type="checkbox" name="'.$name.'" '.
                            'value="'.$ruleorder->[$i].'"'.$check.'/>'.                             'value="'.$ruleorder->[$i].'"'.$check.'/>'.
                            $rules->{$ruleorder->[$i]}{'name'}.'</label></span></td>';                             $rules->{$ruleorder->[$i]}{'name'}.'</label></span></td>';
             }              }
         }          }
         $rem = @{$ruleorder}%($numinrow);          $rem = @{$ruleorder}%($numinrow);
     }      }
     my $colsleft = $numinrow - $rem;      my $colsleft;
       if ($rem) {
           $colsleft = $numinrow - $rem;
       }
     if ($colsleft > 1 ) {      if ($colsleft > 1 ) {
         $output .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.          $output .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                    '&nbsp;</td>';                     '&nbsp;</td>';
     } 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></td></tr>';      $output .= '</tr>';
       unless (($type eq 'email') || ($type eq 'unamemap')) {
           $output .= '</table></td></tr>';
       }
     return $output;      return $output;
 }  }
   
Line 5994  sub authtype_names { Line 10579  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 6062  sub print_defaults { Line 10648  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 6088  sub print_defaults { Line 10675  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';
             $datatable .= '</td></tr>';                  if ($defaults{$item}) {
             $rownum ++;                      $portalsty = 'block';
         }                  }
     } elsif ($position eq 'middle') {                  foreach my $field ('email','web') {
         my @items = ('intauth_cost','intauth_check','intauth_switch');                      my $checkedoff = ' checked="checked"';
         my %defaults;                      my $checkedon;
         if (ref($settings) eq 'HASH') {                      if ($defaults{$item.'_'.$field}) {
             %defaults = %{$settings};                          $checkedon = $checkedoff;
             if ($defaults{'intauth_cost'} !~ /^\d+$/) {                          $checkedoff = '';
                 $defaults{'intauth_cost'} = 10;                      } 
             }                      $datatable .= '<div id="'.$item.'_'.$field.'_div" style="display:'.$portalsty.'">'.
             if ($defaults{'intauth_check'} !~ /^(0|1|2)$/) {                                '<span class="LC_nobreak">'.$titles->{$field}.'&nbsp;'.
                 $defaults{'intauth_check'} = 0;                                '<label><input type="radio" name="'.$item.'_'.$field.'" value="1"'.$checkedon.'/>'.&mt('Yes').'</label>'.
             }                                ('&nbsp;'x2).
             if ($defaults{'intauth_switch'} !~ /^(0|1|2)$/) {                                '<label><input type="radio" name="'.$item.'_'.$field.'" value="0"'.$checkedoff.'/>'.&mt('No').'</label>'.
                 $defaults{'intauth_switch'} = 0;                                '</div>';
             }  
         } else {  
             %defaults = (  
                           'intauth_cost'   => 10,  
                           'intauth_check'  => 0,  
                           'intauth_switch' => 0,  
                         );  
         }  
         foreach my $item (@items) {  
             if ($rownum%2) {  
                 $css_class = '';  
             } else {  
                 $css_class = ' class="LC_odd_row" ';  
             }  
             $datatable .= '<tr'.$css_class.'>'.  
                           '<td><span class="LC_nobreak">'.$titles->{$item}.  
                           '</span></td><td class="LC_left_item" colspan="3">';  
             if ($item eq 'intauth_switch') {  
                 my @options = (0,1,2);  
                 my %optiondesc = &Apache::lonlocal::texthash (  
                                    0 => 'No',  
                                    1 => 'Yes',  
                                    2 => 'Yes, and copy existing passwd file to passwd.bak file',  
                                  );  
                 $datatable .= '<table width="100%">';  
                 foreach my $option (@options) {  
                     my $checked = ' ';  
                     if ($defaults{$item} eq $option) {  
                         $checked = ' checked="checked"';  
                     }  
                     $datatable .= '<tr><td class="LC_left_item"><span class="LC_nobreak">'.  
                                   '<label><input type="radio" name="'.$item.  
                                   '" value="'.$option.'"'.$checked.' />'.  
                                   $optiondesc{$option}.'</label></span></td></tr>';  
                 }  
                 $datatable .= '</table>';  
             } elsif ($item eq 'intauth_check') {  
                 my @options = (0,1,2);  
                 my %optiondesc = &Apache::lonlocal::texthash (  
                                    0 => 'No',  
                                    1 => 'Yes, allow login then update passwd file using default cost (if higher)',  
                                    2 => 'Yes, disallow login if stored cost is less than domain default',  
                                  );  
                 $datatable .= '<table wisth="100%">';  
                 foreach my $option (@options) {  
                     my $checked = ' ';  
                     my $onclick;  
                     if ($defaults{$item} eq $option) {  
                         $checked = ' checked="checked"';  
                     }  
                     if ($option == 2) {  
                         $onclick = ' onclick="javascript:warnIntAuth(this);"';  
                     }  
                     $datatable .= '<tr><td class="LC_left_item"><span class="LC_nobreak">'.  
                                   '<label><input type="radio" name="'.$item.  
                                   '" value="'.$option.'"'.$checked.$onclick.' />'.  
                                   $optiondesc{$option}.'</label></span></td></tr>';  
                 }                  }
                 $datatable .= '</table>';  
             } else {              } else {
                 $datatable .= '<input type="text" name="'.$item.'" value="'.                  $datatable .= '<input type="text" name="'.$item.'" value="'.$defaults{$item}.'" />';
                               $defaults{$item}.'" size="3" onblur="javascript:warnIntAuth(this);" />';  
             }              }
             $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')) {
                 (ref($settings->{'inststatusguest'}) eq 'ARRAY')) {  
                 my $maxnum = @{$settings->{'inststatusorder'}};                  my $maxnum = @{$settings->{'inststatusorder'}};
                 for (my $i=0; $i<$maxnum; $i++) {                  for (my $i=0; $i<$maxnum; $i++) {
                     $css_class = $rownum%2?' class="LC_odd_row"':'';                      $css_class = $rownum%2?' class="LC_odd_row"':'';
                     my $item = $settings->{'inststatusorder'}->[$i];                      my $item = $settings->{'inststatusorder'}->[$i];
                     my $title = $settings->{'inststatustypes'}->{$item};                      my $title = $settings->{'inststatustypes'}->{$item};
                     my $guestok;  
                     if (grep(/^\Q$item\E$/,@{$settings->{'inststatusguest'}})) {  
                         $guestok = 1;  
                     }  
                     my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'$item'".');"';                      my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'$item'".');"';
                     $datatable .= '<tr'.$css_class.'>'.                      $datatable .= '<tr'.$css_class.'>'.
                                   '<td><span class="LC_nobreak">'.                                    '<td><span class="LC_nobreak">'.
Line 6204  sub print_defaults { Line 10723  sub print_defaults {
                         }                          }
                         $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';                          $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
                     }                      }
                     my ($checkedon,$checkedoff);  
                     $checkedoff = ' checked="checked"';  
                     if ($guestok) {  
                         $checkedon = $checkedoff;  
                         $checkedoff = '';   
                     }  
                     $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>'.                                    '</span></td></tr>';
                                   '<td class="LC_right_item"><span class="LC_nobreak">'.  
                                   '<label><input type="radio" value="1" name="inststatus_guest_'.$item.'"'.$checkedon.' />'.  
                                   &mt('Yes').'</label>'.('&nbsp;'x2).  
                                   '<label><input type="radio" value="0" name="inststatus_guest_'.$item.'"'.$checkedoff.' />'.  
                                   &mt('No').'</label></span></td></tr>';  
                 }                  }
                 $css_class = $rownum%2?' class="LC_odd_row"':'';                  $css_class = $rownum%2?' class="LC_odd_row"':'';
                 my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'addinststatus_pos'".');"';                  my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'addinststatus_pos'".');"';
Line 6237  sub print_defaults { Line 10745  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>'.
                               '<td class="LC_right_item"><span class="LC_nobreak">'.  
                               '<label><input type="radio" value="1" name="addinststatus_guest" />'.  
                               &mt('Yes').'</label>'.('&nbsp;'x2).  
                               '<label><input type="radio" value="0" name="addinststatus_guest" />'.  
                               &mt('No').'</label></span></td></tr>';  
                               '</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 6274  sub defaults_titles { Line 10793  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 6291  sub defaults_titles { Line 10812  sub defaults_titles {
     return (\%titles);      return (\%titles);
 }  }
   
   sub print_scantron {
       my ($r,$position,$dom,$confname,$settings,$rowtotal) = @_;
       if ($position eq 'top') {
           return &print_scantronformat($r,$dom,$confname,$settings,\$rowtotal);
       } else {
           return &print_scantronconfig($dom,$settings,\$rowtotal);
       }
   }
   
   sub scantron_javascript {
       return <<"ENDSCRIPT";
   
   <script type="text/javascript">
   // <![CDATA[
   
   function toggleScantron(form) {
       var csvfieldset = new Array();
       if (document.getElementById('scantroncsv_cols')) {
           csvfieldset.push(document.getElementById('scantroncsv_cols'));
       }
       if (document.getElementById('scantroncsv_options')) {
           csvfieldset.push(document.getElementById('scantroncsv_options'));
       }
       if (csvfieldset.length) {
           if (document.getElementById('scantronconfcsv')) {
               var scantroncsv = document.getElementById('scantronconfcsv');
               if (scantroncsv.checked) {
                   for (var i=0; i<csvfieldset.length; i++) {
                       csvfieldset[i].style.display = 'block';
                   }
               } else {
                   for (var i=0; i<csvfieldset.length; i++) {
                       csvfieldset[i].style.display = 'none';
                   }
                   var csvselects = document.getElementsByClassName('scantronconfig_csv');
                   if (csvselects.length) {
                       for (var j=0; j<csvselects.length; j++) {
                           csvselects[j].selectedIndex = 0;
                       }
                   }
               }
           }
       }
       return;
   }
   // ]]>
   </script>
   
   ENDSCRIPT
   
   }
   
 sub print_scantronformat {  sub print_scantronformat {
     my ($r,$dom,$confname,$settings,$rowtotal) = @_;      my ($r,$dom,$confname,$settings,$rowtotal) = @_;
     my $itemcount = 1;      my $itemcount = 1;
Line 6317  sub print_scantronformat { Line 10890  sub print_scantronformat {
             if ($configuserok eq 'ok') {              if ($configuserok eq 'ok') {
                 if ($author_ok eq 'ok') {                  if ($author_ok eq 'ok') {
                     my %legacyfile = (                      my %legacyfile = (
  default => $Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab',    default => $Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab',
  custom  => $Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab',    custom  => $Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab',
                     );                      );
                     my %md5chk;                      my %md5chk;
                     foreach my $type (keys(%legacyfile)) {                      foreach my $type (keys(%legacyfile)) {
Line 6327  sub print_scantronformat { Line 10900  sub print_scantronformat {
                     }                      }
                     if ($md5chk{'default'} ne $md5chk{'custom'}) {                      if ($md5chk{'default'} ne $md5chk{'custom'}) {
                         foreach my $type (keys(%legacyfile)) {                          foreach my $type (keys(%legacyfile)) {
                             ($scantronurls{$type},my $error) =                               ($scantronurls{$type},my $error) =
                                 &legacy_scantronformat($r,$dom,$confname,                                  &legacy_scantronformat($r,$dom,$confname,
                                                  $type,$legacyfile{$type},                                                   $type,$legacyfile{$type},
                                                  $scantronurls{$type},                                                   $scantronurls{$type},
Line 6338  sub print_scantronformat { Line 10911  sub print_scantronformat {
                         }                          }
                         if (keys(%error) == 0) {                          if (keys(%error) == 0) {
                             $is_custom = 1;                              $is_custom = 1;
                             $confhash{'scantron'}{'scantronformat'} =                               $confhash{'scantron'}{'scantronformat'} =
                                 $scantronurls{'custom'};                                  $scantronurls{'custom'};
                             my $putresult =                               my $putresult =
                                 &Apache::lonnet::put_dom('configuration',                                  &Apache::lonnet::put_dom('configuration',
                                                          \%confhash,$dom);                                                           \%confhash,$dom);
                             if ($putresult ne 'ok') {                              if ($putresult ne 'ok') {
                                 $error{'custom'} =                                   $error{'custom'} =
                                     '<span class="LC_error">'.                                      '<span class="LC_error">'.
                                     &mt('An error occurred updating the domain configuration: [_1]',$putresult).'</span>';                                      &mt('An error occurred updating the domain configuration: [_1]',$putresult).'</span>';
                             }                              }
Line 6407  sub print_scantronformat { Line 10980  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 6464  sub legacy_scantronformat { Line 11037  sub legacy_scantronformat {
     return ($url,$error);      return ($url,$error);
 }  }
   
   sub print_scantronconfig {
       my ($dom,$settings,$rowtotal) = @_;
       my $itemcount = 2;
       my $is_checked = ' checked="checked"';
       my %optionson = (
                        hdr => ' checked="checked"',
                        pad => ' checked="checked"',
                        rem => ' checked="checked"',
                       );
       my %optionsoff = (
                         hdr => '',
                         pad => '',
                         rem => '',
                        );
       my $currcsvsty = 'none';
       my ($datatable,%csvfields,%checked,%onclick,%csvoptions);
       my @fields = &scantroncsv_fields();
       my %titles = &scantronconfig_titles();
       if (ref($settings) eq 'HASH') {
           if (ref($settings->{config}) eq 'HASH') {
               if ($settings->{config}->{dat}) {
                   $checked{'dat'} = $is_checked;
               }
               if (ref($settings->{config}->{csv}) eq 'HASH') {
                   if (ref($settings->{config}->{csv}->{fields}) eq 'HASH') {
                       %csvfields = %{$settings->{config}->{csv}->{fields}};
                       if (keys(%csvfields) > 0) {
                           $checked{'csv'} = $is_checked;
                           $currcsvsty = 'block';
                       }
                   }
                   if (ref($settings->{config}->{csv}->{options}) eq 'HASH') {
                       %csvoptions = %{$settings->{config}->{csv}->{options}};
                       foreach my $option (keys(%optionson)) {
                           unless ($csvoptions{$option}) {
                               $optionsoff{$option} = $optionson{$option};
                               $optionson{$option} = '';
                           }
                       }
                   }
               }
           } else {
               $checked{'dat'} = $is_checked;
           }
       } else {
           $checked{'dat'} = $is_checked;
       }
       $onclick{'csv'} = ' onclick="toggleScantron(this.form);"';
       my $css_class = $itemcount%2? ' class="LC_odd_row"':'';
       $datatable = '<tr '.$css_class.'><td>'.&mt('Supported formats').'</td>'.
                    '<td class="LC_left_item" valign="top"><span class="LC_nobreak">';
       foreach my $item ('dat','csv') {
           my $id;
           if ($item eq 'csv') {
               $id = 'id="scantronconfcsv" ';
           }
           $datatable .= '<label><input type="checkbox" name="scantronconfig" '.$id.'value="'.$item.'"'.$checked{$item}.$onclick{$item}.' />'.
                         $titles{$item}.'</label>'.('&nbsp;'x3);
           if ($item eq 'csv') {
               $datatable .= '<fieldset style="display:'.$currcsvsty.'" id="scantroncsv_cols">'.
                             '<legend>'.&mt('CSV Column Mapping').'</legend>'.
                             '<table><tr><th>'.&mt('Field').'</th><th>'.&mt('Location').'</th></tr>'."\n";
               foreach my $col (@fields) {
                   my $selnone;
                   if ($csvfields{$col} eq '') {
                       $selnone = ' selected="selected"';
                   }
                   $datatable .= '<tr><td>'.$titles{$col}.'</td>'.
                                 '<td><select name="scantronconfig_csv_'.$col.'" class="scantronconfig_csv">'.
                                 '<option value=""'.$selnone.'></option>';
                   for (my $i=0; $i<20; $i++) {
                       my $shown = $i+1;
                       my $sel;
                       unless ($selnone) {
                           if (exists($csvfields{$col})) {
                               if ($csvfields{$col} == $i) {
                                   $sel = ' selected="selected"';
                               }
                           }
                       }
                       $datatable .= '<option value="'.$i.'"'.$sel.'>'.$shown.'</option>';
                   }
                   $datatable .= '</select></td></tr>';
              }
              $datatable .= '</table></fieldset>'.
                            '<fieldset style="display:'.$currcsvsty.'" id="scantroncsv_options">'.
                            '<legend>'.&mt('CSV Options').'</legend>';
              foreach my $option ('hdr','pad','rem') {
                  $datatable .= '<span class="LC_nobreak">'.$titles{$option}.':'.
                            '<label><input type="radio" name="scantroncsv_'.$option.'" value="1"'.$optionson{$option}.' />'.
                            &mt('Yes').'</label>'.('&nbsp;'x2)."\n".
                            '<label><input type="radio" name="scantroncsv_'.$option.'" value="0"'.$optionsoff{$option}.' />'.&mt('No').'</label></span><br />';
              }
              $datatable .= '</fieldset>';
              $itemcount ++;
           }
       }
       $datatable .= '</td></tr>';
       $$rowtotal ++;
       return $datatable;
   }
   
   sub scantronconfig_titles {
       return &Apache::lonlocal::texthash(
                                             dat => 'Standard format (.dat)',
                                             csv => 'Comma separated values (.csv)',
                                             hdr => 'Remove first line in file (contains column titles)',
                                             pad => 'Prepend 0s to PaperID',
                                             rem => 'Remove leading spaces (except Question Response columns)',
                                             CODE => 'CODE',
                                             ID   => 'Student ID',
                                             PaperID => 'Paper ID',
                                             FirstName => 'First Name',
                                             LastName => 'Last Name',
                                             FirstQuestion => 'First Question Response',
                                             Section => 'Section',
       );
   }
   
   sub scantroncsv_fields {
       return ('PaperID','LastName','FirstName','ID','Section','CODE','FirstQuestion');
   }
   
 sub print_coursecategories {  sub print_coursecategories {
     my ($position,$dom,$hdritem,$settings,$rowtotal) = @_;      my ($position,$dom,$hdritem,$settings,$rowtotal) = @_;
     my $datatable;      my $datatable;
Line 6506  sub print_coursecategories { Line 11202  sub print_coursecategories {
                              '<input type="radio" name="coursecat_'.$item.'" value="'.$type.'"'.$ischecked.                               '<input type="radio" name="coursecat_'.$item.'" value="'.$type.'"'.$ischecked.
                              ' />'.$lt{$type}.'</label>&nbsp;';                               ' />'.$lt{$type}.'</label>&nbsp;';
            }             }
            $datatable .= '</td></tr>';             $datatable .= '</span></td></tr>';
            $itemcount ++;             $itemcount ++;
         }          }
         $$rowtotal += $itemcount;          $$rowtotal += $itemcount;
Line 6519  sub print_coursecategories { Line 11215  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 6537  sub print_coursecategories { Line 11237  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 6577  sub print_coursecategories { Line 11288  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 6603  sub print_coursecategories { Line 11328  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 6629  sub print_coursecategories { Line 11357  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 6652  sub print_coursecategories { Line 11380  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 6684  sub print_coursecategories { Line 11412  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 6717  sub print_coursecategories { Line 11445  sub print_coursecategories {
                 $datatable .= &initialize_categories($itemcount);                  $datatable .= &initialize_categories($itemcount);
             }              }
         } else {          } else {
             $datatable .= '<td class="LC_right_item">'.$hdritem->{'header'}->[1]->{'col2'}.'</td>'              $datatable .= '<tr><td class="LC_right_item">'.$hdritem->{'header'}->[1]->{'col2'}.'</td></tr>'
                           .&initialize_categories($itemcount);                            .&initialize_categories($itemcount);
         }          }
         $$rowtotal += $itemcount;          $$rowtotal += $itemcount;
Line 6765  sub print_serverstatuses { Line 11493  sub print_serverstatuses {
                       '<span class="LC_nobreak">'.                        '<span class="LC_nobreak">'.
                       '<input type="text" name="'.$type.'_machines" '.                        '<input type="text" name="'.$type.'_machines" '.
                       'value="'.$machineaccess{$type}.'" size="10" />'.                        'value="'.$machineaccess{$type}.'" size="10" />'.
                       '</td></tr>'."\n";                        '</span></td></tr>'."\n";
     }      }
     $$rowtotal += $rownum;      $$rowtotal += $rownum;
     return $datatable;      return $datatable;
Line 6773  sub print_serverstatuses { Line 11501  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) = @_;
     my $intauthcheck = &mt('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.');      return unless (ref($settings) eq 'HASH');
     my $intauthcost = &mt('Warning: bcrypt encryption cost for internal authentication must be an integer.');      my $portal_js = <<"ENDPORTAL";
     &js_escape(\$intauthcheck);  
     &js_escape(\$intauthcost);  
     my $intauthjs = <<"ENDSCRIPT";  
   
 function warnIntAuth(field) {  function portalExtras(caller) {
     if (field.name == 'intauth_check') {      var x = caller.value;
         if (field.value == '2') {      var y = new Array('email','web');
             alert('$intauthcheck');      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 (field.name == 'intauth_cost') {              if (x.length > 0) {
         field.value.replace(/\s/g,'');                  z.style.display = 'block';
         if (field.value != '') {              } else {
             var regexdigit=/^\\d+\$/;                  z.style.display = 'none';
             if (!regexdigit.test(field.value)) {  
                 alert('$intauthcost');  
             }              }
         }          }
     }      }
     return;  
 }  }
   ENDPORTAL
 ENDSCRIPT  
   
     if (ref($settings) ne 'HASH') {  
         return &Apache::lonhtmlcommon::scripttag($intauthjs);  
     }  
     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 6862  $jstext Line 11579  $jstext
     return;      return;
 }  }
   
 $intauthjs  $portal_js
   
 // ]]>  // ]]>
 </script>  </script>
   
 ENDSCRIPT  ENDSCRIPT
     } else {      } else {
         return &Apache::lonhtmlcommon::scripttag($intauthjs);  return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   $portal_js
   // ]]>
   </script>
   
   ENDSCRIPT
       }
       return;
   }
   
   sub passwords_javascript {
       my ($prefix) = @_;
       my %intalert;
       if ($prefix eq 'passwords') {
           %intalert = &Apache::lonlocal::texthash (
               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.',
               authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.',
               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 'secrets') {
           %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);
       my $defmin = $Apache::lonnet::passwdmin;
       my $intauthjs; 
       if ($prefix eq 'passwords') { $intauthjs = <<"ENDSCRIPT";
   
   function warnIntAuth(field) {
       if (field.name == 'intauth_check') {
           if (field.value == '2') {
               alert('$intalert{authcheck}');
           }
     }      }
       if (field.name == 'intauth_cost') {
           field.value.replace(/\s/g,'');
           if (field.value != '') {
               var regexdigit=/^\\d+\$/;
               if (!regexdigit.test(field.value)) {
                   alert('$intalert{authcost}');
               }
           }
       }
       return;
   }
   
   ENDSCRIPT
   
        }
   
        $intauthjs .= <<"ENDSCRIPT";
   
   function warnInt$prefix(field) {
       field.value.replace(/^\s+/,'');
       field.value.replace(/\s+\$/,'');
       var regexdigit=/^\\d+\$/;
       if (field.name == '${prefix}_min') {
           if (field.value == '') {
               alert('$intalert{passmin}');
               field.value = '$defmin';
           } else {
               if (!regexdigit.test(field.value)) {
                   alert('$intalert{passmin}');
                   field.value = '$defmin';
               }
               var minval = parseInt(field.value,10);
               if (minval < $defmin) {
                   alert('$intalert{passmin}');
                   field.value = '$defmin';
               }
           }
       } else {
           if (field.value == '0') {
               field.value = '';
           }
           if (field.value != '') {
               if (field.name == '${prefix}_expire') {
                   var regexpposnum=/^\\d+(|\\.\\d*)\$/; 
                   if (!regexpposnum.test(field.value)) {
                       alert('$intalert{passexp}');
                       field.value = '';
                   } else {
                       var expval = parseFloat(field.value);
                       if (expval == 0) {
                           alert('$intalert{passexp}');
                           field.value = '';
                       }
                   }
               } else {
                   if (!regexdigit.test(field.value)) {
                       if (field.name == '${prefix}_max') {
                           alert('$intalert{passmax}');
                       } else {
                           if (field.name == '${prefix}_numsaved') {
                               alert('$intalert{passnum}');
                           }
                       }
                       field.value = '';
                   }
               }
           }
       }
       return;
   }
   
   ENDSCRIPT
       return &Apache::lonhtmlcommon::scripttag($intauthjs);
 }  }
   
 sub coursecategories_javascript {  sub coursecategories_javascript {
Line 6897  sub coursecategories_javascript { Line 11726  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 6969  function categoryCheck(form) { Line 11800  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 6982  ENDSCRIPT Line 11817  ENDSCRIPT
 sub initialize_categories {  sub initialize_categories {
     my ($itemcount) = @_;      my ($itemcount) = @_;
     my ($datatable,$css_class,$chgstr);      my ($datatable,$css_class,$chgstr);
     my %default_names = (      my %default_names = &Apache::lonlocal::texthash (
                       instcode    => 'Official courses (with institutional codes)',                        instcode    => 'Official courses (with institutional codes)',
                       communities => 'Communities',                        communities => 'Communities',
                         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 7014  sub initialize_categories { Line 11853  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>'
                   .&mt('Add category').'</td><td>'.&mt('Name:')                    .'<option value="3" selected="selected">4</option></select>&nbsp;'
                   .'&nbsp;<input type="text" size="20" name="addcategory_name" value="" /></td></tr>';                    .&mt('Add category').'</span></td><td><span class="LC_nobreak">'.&mt('Name:')
                     .'&nbsp;<input type="text" size="20" name="addcategory_name" value="" /></span>'
                     .'</td></tr>';
     return $datatable;      return $datatable;
 }  }
   
Line 7071  sub build_category_rows { Line 11912  sub build_category_rows {
                             pop(@{$path});                              pop(@{$path});
                         }                          }
                     } else {                      } else {
                         $text .= &mt('Add subcategory:').'&nbsp;</span><input type="textbox" size="20" name="addcategory_name_';                          $text .= &mt('Add subcategory:').'&nbsp;</span><input type="text" size="20" name="addcategory_name_';
                         if ($j == $numchildren) {                          if ($j == $numchildren) {
                             $text .= $name;                              $text .= $name;
                         } else {                          } else {
Line 7094  sub build_category_rows { Line 11935  sub build_category_rows {
                 my $colspan;                  my $colspan;
                 if ($parent ne 'instcode') {                  if ($parent ne 'instcode') {
                     $colspan = $maxdepth - $depth - 1;                      $colspan = $maxdepth - $depth - 1;
                     $text .= '<td colspan="'.$colspan.'">'.&mt('Add subcategory:').'<input type="textbox" size="20" name="subcat_'.$name.'" value="" /></td>';                      $text .= '<td colspan="'.$colspan.'">'.&mt('Add subcategory:').'<input type="text" size="20" name="subcat_'.$name.'" value="" /></td>';
                 }                  }
             }              }
         }          }
Line 7103  sub build_category_rows { Line 11944  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,$itemdesc) = @_;
     my ($role,$rolename,$statustype);      my ($role,$rolename,$statustype);
     $role = $item;      $role = $item;
     if ($context eq 'cancreate') {      if ($context eq 'cancreate') {
         if ($item =~ /^emailusername_(.+)$/) {          if ($item =~ /^(emailusername)_(.+)$/) {
             $statustype = $1;              $role = $1;
             $role = 'emailusername';              $statustype = $2;
             if (ref($usertypes) eq 'HASH') {              if (ref($usertypes) eq 'HASH') {
                 if ($usertypes->{$statustype}) {                  if ($usertypes->{$statustype}) {
                     $rolename = &mt('Data provided by [_1]',$usertypes->{$statustype});                      $rolename = &mt('Data provided by [_1]',$usertypes->{$statustype});
Line 7124  sub modifiable_userdata_row { Line 11966  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 7144  sub modifiable_userdata_row { Line 11990  sub modifiable_userdata_row {
         %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();          %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();
     }      }
     my $output;      my $output;
     my $css_class = $rowcount%2?' class="LC_odd_row"':'';      my $css_class;
     $output = '<tr '.$css_class.'>'.      if ($rowcount%2) {
           $css_class = 'LC_odd_row';
       }
       if ($customcss) {
           $css_class .= " $customcss";
       }
       $css_class =~ s/^\s+//;
       if ($css_class) {
           $css_class = ' class="'.$css_class.'"';
       }
       if ($rowstyle) {
           $css_class .= ' style="'.$rowstyle.'"';
       }
       if ($rowid) {
           $rowid = ' id="'.$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;
       my %current;
     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" ';
                     }                      }
                 }                  }
             }              }
         }          }
     }      }
         
     for (my $i=0; $i<@fields; $i++) {      my $total = scalar(@fields);
         my $rem = $i%($numinrow);      for (my $i=0; $i<$total; $i++) {
           $rem = $i%($numinrow);
         if ($rem == 0) {          if ($rem == 0) {
             if ($i > 0) {              if ($i > 0) {
                 $output .= '</tr>';                  $output .= '</tr>';
Line 7191  sub modifiable_userdata_row { Line 12063  sub modifiable_userdata_row {
         my $check = ' ';          my $check = ' ';
         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 7202  sub modifiable_userdata_row { Line 12092  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 7212  sub modifiable_userdata_row { Line 12103  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>';
         }          }
         $output .= '</span></td>';          $output .= '</span></td>';
         $rem = @fields%($numinrow);  
     }      }
     my $colsleft = $numinrow - $rem;      $rem = $total%$numinrow;
     if ($colsleft > 1 ) {      my $colsleft;
       if ($rem) {
           $colsleft = $numinrow - $rem;
       }
       if ($colsleft > 1) {
         $output .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.          $output .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                    '&nbsp;</td>';                     '&nbsp;</td>';
     } elsif ($colsleft == 1) {      } elsif ($colsleft == 1) {
Line 7243  sub insttypes_row { Line 12142  sub insttypes_row {
                       cansearch => 'Users allowed to search',                        cansearch => 'Users allowed to search',
                       statustocreate => 'Institutional affiliation(s) able to create own account (login/SSO)',                        statustocreate => 'Institutional affiliation(s) able to create own account (login/SSO)',
                       lockablenames => 'User preference to lock name',                        lockablenames => 'User preference to lock name',
                         selfassign    => 'Self-reportable affiliations',
                       overrides     => "Override domain's helpdesk settings based on requester's affiliation",                        overrides     => "Override domain's helpdesk settings based on requester's affiliation",
              );               );
     my $showdom;      my $showdom;
Line 7301  sub insttypes_row { Line 12201  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.'/>'.                             'value="'.$types->[$i].'"'.$check.$onclick.' />'.
                            $usertypes->{$types->[$i]}.'</label></span></td>';                             $usertypes->{$types->[$i]}.'</label></span></td>';
             }              }
         }          }
Line 7314  sub insttypes_row { Line 12214  sub insttypes_row {
         } else {          } else {
             $output .= '<td class="LC_left_item">';              $output .= '<td class="LC_left_item">';
         }          }
         $output .= '&nbsp;';          $output .= '&nbsp;';  
     } else {      } else {
         if (($rem == 0) && (@{$types} > 0)) {          if ($rem == 0) {
             $output .= '<tr>';              $output .= '<tr>';
         }          }
         if ($colsleft > 1) {          if ($colsleft > 1) {
Line 7336  sub insttypes_row { Line 12236  sub insttypes_row {
         }          }
         $output .= '<span class="LC_nobreak"><label>'.          $output .= '<span class="LC_nobreak"><label>'.
                    '<input type="checkbox" name="'.$context.'" '.                     '<input type="checkbox" name="'.$context.'" '.
                    'value="default"'.$defcheck.'/>'.                     'value="default"'.$defcheck.$onclick.' />'.
                    $othertitle.'</label></span>';                     $othertitle.'</label></span>';
     }      }
     $output .= '</td></tr></table></td></tr>';      $output .= '</td></tr></table></td></tr>';
Line 7416  sub usertype_update_row { Line 12316  sub usertype_update_row {
 sub modify_login {  sub modify_login {
     my ($r,$dom,$confname,$lastactref,%domconfig) = @_;      my ($r,$dom,$confname,$lastactref,%domconfig) = @_;
     my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl,      my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl,
         %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon);          %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon,
           %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso);
     %title = ( coursecatalog => 'Display course catalog',      %title = ( coursecatalog => 'Display course catalog',
                adminmail => 'Display administrator E-mail address',                 adminmail => 'Display administrator E-mail address',
                helpdesk  => 'Display "Contact Helpdesk" link',                 helpdesk  => 'Display "Contact Helpdesk" link',
                newuser => 'Link for visitors to create a user account',                 newuser => 'Link for visitors to create a user account',
                loginheader => 'Log-in box header');                 loginheader => 'Log-in box header',
                  saml => 'Dual SSO and non-SSO login');
     @offon = ('off','on');      @offon = ('off','on');
     if (ref($domconfig{login}) eq 'HASH') {      if (ref($domconfig{login}) eq 'HASH') {
         if (ref($domconfig{login}{loginvia}) eq 'HASH') {          if (ref($domconfig{login}{loginvia}) eq 'HASH') {
Line 7429  sub modify_login { Line 12331  sub modify_login {
                 $curr_loginvia{$lonhost} = $domconfig{login}{loginvia}{$lonhost};                  $curr_loginvia{$lonhost} = $domconfig{login}{loginvia}{$lonhost};
             }              }
         }          }
           if (ref($domconfig{login}{'saml'}) eq 'HASH') {
               foreach my $lonhost (keys(%{$domconfig{login}{'saml'}})) {
                   if (ref($domconfig{login}{'saml'}{$lonhost}) eq 'HASH') {
                       $currsaml{$lonhost} = $domconfig{login}{'saml'}{$lonhost};
                       $saml{$lonhost} = 1;
                       $samltext{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'text'};
                       $samlurl{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'url'};
                       $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'};
                       $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'};
                       $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'};
                       $samlwindow{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'window'};
                       $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'};
                   }
               }
           }
     }      }
     ($errors,%colchanges) = &modify_colors($r,$dom,$confname,['login'],      ($errors,%colchanges) = &modify_colors($r,$dom,$confname,['login'],
                                            \%domconfig,\%loginhash);                                             \%domconfig,\%loginhash);
Line 7675  sub modify_login { Line 12592  sub modify_login {
             $errors .= '<li><span class="LC_error">'.$error.'</span></li>';              $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
         }          }
     }      }
       my @delsamlimg = &Apache::loncommon::get_env_multiple('form.saml_img_del');
       my @newsamlimgs;
       foreach my $lonhost (keys(%domservers)) {
           if ($env{'form.saml_'.$lonhost}) {
               if ($env{'form.saml_img_'.$lonhost.'.filename'}) {
                   push(@newsamlimgs,$lonhost);
               }
               foreach my $item ('text','alt','url','title','window','notsso') {
                   $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g;
               }
               if ($saml{$lonhost}) {
                   if ($env{'form.saml_window_'.$lonhost} ne '1') {
                       $env{'form.saml_window_'.$lonhost} = '';
                   }
                   if (grep(/^\Q$lonhost\E$/,@delsamlimg)) {
   #FIXME Need to obsolete published image
                       delete($currsaml{$lonhost}{'img'});
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_alt_'.$lonhost} ne $samlalt{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_text_'.$lonhost} ne $samltext{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_url_'.$lonhost} ne $samlurl{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_window_'.$lonhost} ne $samlwindow{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
                   if ($env{'form.saml_notsso_'.$lonhost} ne $samlnotsso{$lonhost}) {
                       $changes{'saml'}{$lonhost} = 1;
                   }
               } else {
                   $changes{'saml'}{$lonhost} = 1;
               }
               foreach my $item ('text','alt','url','title','window','notsso') {
                   $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost};
               }
           } else {
               if ($saml{$lonhost}) {  
                   $changes{'saml'}{$lonhost} = 1;
                   delete($currsaml{$lonhost});
               }
           }
       }
       foreach my $posshost (keys(%currsaml)) {
           unless (exists($domservers{$posshost})) { 
               delete($currsaml{$posshost}); 
           }
       }
       %{$loginhash{'login'}{'saml'}} = %currsaml;
       if (@newsamlimgs) {
           my $error;
           my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
           if ($configuserok eq 'ok') {
               if ($switchserver) {
                   $error = &mt("Upload of SSO Button Image is not permitted to this server: [_1].",$switchserver);
               } elsif ($author_ok eq 'ok') {
                   foreach my $lonhost (@newsamlimgs) {
                       my $formelem = 'saml_img_'.$lonhost;
                       my ($result,$imgurl) = &publishlogo($r,'upload',$formelem,$dom,$confname,
                                                           "login/saml/$lonhost",'','',
                                                           $env{'form.saml_img_'.$lonhost.'.filename'});
                       if ($result eq 'ok') {
                           $currsaml{$lonhost}{'img'} = $imgurl;
                           $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl;
                           $changes{'saml'}{$lonhost} = 1;
                       } else {
                           my $puberror = &mt("Upload of SSO button image failed for [_1] because an error occurred publishing the file in RES space. Error was: [_2].",
                                              $lonhost,$result);
                           $errors .= '<li><span class="LC_error">'.$puberror.'</span></li>';
                       }
                   }
               } else {
                   $error = &mt("Upload of SSO button image file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2].  Error was: [_3].",$confname,$dom,$author_ok);
               }
           } else {
               $error = &mt("Upload of SSO button image file(s) failed because a Domain Configuration user ([_1]) could not be created in domain: [_2].  Error was: [_3].",$confname,$dom,$configuserok);
           }
           if ($error) {
               &Apache::lonnet::logthis($error);
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       }
     &process_captcha('login',\%changes,$loginhash{'login'},$domconfig{'login'});      &process_captcha('login',\%changes,$loginhash{'login'},$domconfig{'login'});
   
     my $defaulthelpfile = '/adm/loginproblems.html';      my $defaulthelpfile = '/adm/loginproblems.html';
Line 7715  sub modify_login { Line 12721  sub modify_login {
         }          }
         if (keys(%changes) > 0 || $colchgtext) {          if (keys(%changes) > 0 || $colchgtext) {
             &Apache::loncommon::devalidate_domconfig_cache($dom);              &Apache::loncommon::devalidate_domconfig_cache($dom);
               if (exists($changes{'saml'})) {
                   my $hostid_in_use;
                   my @hosts = &Apache::lonnet::current_machine_ids();
                   if (@hosts > 1) {
                       foreach my $hostid (@hosts) {
                           if (&Apache::lonnet::host_domain($hostid) eq $dom) {
                               $hostid_in_use = $hostid;
                               last;
                           }
                       }
                   } else {
                       $hostid_in_use = $r->dir_config('lonHostID');
                   }
                   if (($hostid_in_use) &&
                       (&Apache::lonnet::host_domain($hostid_in_use) eq $dom)) {
                       &Apache::lonnet::devalidate_cache_new('samllanding',$hostid_in_use);
                   }
                   if (ref($lastactref) eq 'HASH') {
                       if (ref($changes{'saml'}) eq 'HASH') {
                           my %updates;
                           map { $updates{$_} = 1; } keys(%{$changes{'saml'}});
                           $lastactref->{'samllanding'} = \%updates;
                       }
                   }
               }
             if (ref($lastactref) eq 'HASH') {              if (ref($lastactref) eq 'HASH') {
                 $lastactref->{'domainconfig'} = 1;                  $lastactref->{'domainconfig'} = 1;
             }              }
Line 7794  sub modify_login { Line 12825  sub modify_login {
                             }                              }
                         }                          }
                     }                      }
                   } elsif ($item eq 'saml') {
                       if (ref($changes{$item}) eq 'HASH') {
                           my %notlt = (
                                          text   => 'Text for log-in by SSO',
                                          img    => 'SSO button image',
                                          alt    => 'Alt text for button image',
                                          url    => 'SSO URL',
                                          title  => 'Tooltip for SSO link',
                                          window => 'Pop-up window if iframe',
                                          notsso => 'Text for non-SSO log-in',
                                       );
                           foreach my $lonhost (sort(keys(%{$changes{$item}}))) {
                               if (ref($currsaml{$lonhost}) eq 'HASH') {
                                   $resulttext .= '<li>'.&mt("$title{$item} in use for [_1]","<b>$lonhost</b>").
                                                  '<ul>';
                                   foreach my $key ('text','img','alt','url','title','window','notsso') {
                                       if ($currsaml{$lonhost}{$key} eq '') {
                                           $resulttext .= '<li>'.&mt("$notlt{$key} not in use").'</li>';
                                       } else {
                                           my $value = "'$currsaml{$lonhost}{$key}'";
                                           if ($key eq 'img') {
                                               $value = '<img src="'.$currsaml{$lonhost}{$key}.'" />';
                                           } elsif ($key eq 'window') {
                                               $value = 'On';
                                           }
                                           $resulttext .= '<li>'.&mt("$notlt{$key} set to: [_1]",
                                                                     $value).'</li>';
                                       }
                                   }
                                   $resulttext .= '</ul></li>';
                               } else {
                                   $resulttext .= '<li>'.&mt("$title{$item} not in use for [_1]",$lonhost).'</li>';
                               }
                           }
                       }
                 } elsif ($item eq 'captcha') {                  } elsif ($item eq 'captcha') {
                     if (ref($loginhash{'login'}) eq 'HASH') {                      if (ref($loginhash{'login'}) eq 'HASH') {
                         my $chgtxt;                          my $chgtxt;
Line 7896  sub color_font_choices { Line 12962  sub color_font_choices {
     return %choices;      return %choices;
 }  }
   
   sub modify_ipaccess {
       my ($dom,$lastactref,%domconfig) = @_;
       my (@allpos,%changes,%confhash,$errors,$resulttext);
       my (@items,%deletions,%itemids,@warnings);
       my ($typeorder,$types) = &commblocktype_text();
       if ($env{'form.ipaccess_add'}) {
           my $name = $env{'form.ipaccess_name_add'};
           my ($newid,$error) = &get_ipaccess_id($dom,$name);
           if ($newid) {
               $itemids{'add'} = $newid;
               push(@items,'add');
               $changes{$newid} = 1;
           } else {
               $error = &mt('Failed to acquire unique ID for new IP access control item');
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       }
       if (ref($domconfig{'ipaccess'}) eq 'HASH') {
           my @todelete = &Apache::loncommon::get_env_multiple('form.ipaccess_del');
           if (@todelete) {
               map { $deletions{$_} = 1; } @todelete;
           }
           my $maxnum = $env{'form.ipaccess_maxnum'};
           for (my $i=0; $i<$maxnum; $i++) {
               my $itemid = $env{'form.ipaccess_id_'.$i};
               $itemid =~ s/\D+//g;
               if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                   if ($deletions{$itemid}) {
                       $changes{$itemid} = $domconfig{'ipaccess'}{$itemid}{'name'};
                   } else {
                       push(@items,$i);
                       $itemids{$i} = $itemid;
                   }
               }
           }
       }
       foreach my $idx (@items) {
           my $itemid = $itemids{$idx};
           next unless ($itemid);
           my %current;
           unless ($idx eq 'add') {
               if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                   %current = %{$domconfig{'ipaccess'}{$itemid}};
               }
           }
           my $position = $env{'form.ipaccess_pos_'.$itemid};
           $position =~ s/\D+//g;
           if ($position ne '') {
               $allpos[$position] = $itemid;
           }
           my $name = $env{'form.ipaccess_name_'.$idx};
           $name =~ s/^\s+|\s+$//g;
           $confhash{$itemid}{'name'} = $name;
           my $possrange = $env{'form.ipaccess_range_'.$idx};
           $possrange =~ s/^\s+|\s+$//g;
           unless ($possrange eq '') {
               $possrange =~ s/[\r\n]+/\s/g;
               $possrange =~ s/\s*-\s*/-/g;
               $possrange =~ s/\s+/,/g;
               $possrange =~ s/,+/,/g;
               if ($possrange ne '') {
                   my (@ok,$count);
                   $count = 0; 
                   foreach my $poss (split(/\,/,$possrange)) {
                       $count ++;
                       $poss = &validate_ip_pattern($poss);
                       if ($poss ne '') {
                           push(@ok,$poss);
                       }
                   }
                   my $diff = $count - scalar(@ok);
                   if ($diff) {
                       $errors .= '<li><span class="LC_error">'.
                                  &mt('[quant,_1,IP] invalid and excluded from saved value for IP range(s) for [_2]',
                                      $diff,$name).
                                  '</span></li>';
                   }
                   if (@ok) {
                       my @cidr_list;
                       foreach my $item (@ok) {
                           @cidr_list = &Net::CIDR::cidradd($item,@cidr_list);
                       }
                       $confhash{$itemid}{'ip'} = join(',',@cidr_list);
                   }
               }
           }
           foreach my $field ('name','ip') {
               unless (($idx eq 'add') || ($changes{$itemid})) {
                   if ($current{$field} ne $confhash{$itemid}{$field}) {
                       $changes{$itemid} = 1;
                       last;
                   }
               }
           }
           $confhash{$itemid}{'commblocks'} = {};
            
           my %commblocks;
           map { $commblocks{$_} = 1; } &Apache::loncommon::get_env_multiple('form.ipaccess_block_'.$idx);  
           foreach my $type (@{$typeorder}) {
               if ($commblocks{$type}) {
                   $confhash{$itemid}{'commblocks'}{$type} = 'on';
               }
               unless (($idx eq 'add') || ($changes{$itemid})) {
                   if (ref($current{'commblocks'}) eq 'HASH') {
                       if ($confhash{$itemid}{'commblocks'}{$type} ne $current{'commblocks'}{$type}) {
                           $changes{$itemid} = 1;
                       }
                   } elsif ($confhash{$itemid}{'commblocks'}{$type}) {
                       $changes{$itemid} = 1;
                   }
               }
           }
           $confhash{$itemid}{'courses'} = {};
           my %crsdeletions;
           my @delcrs = &Apache::loncommon::get_env_multiple('form.ipaccess_course_delete_'.$idx);
           if (@delcrs) {
               map { $crsdeletions{$_} = 1; } @delcrs;
           }
           if (ref($current{'courses'}) eq 'HASH') {
               foreach my $cid (sort(keys(%{$current{'courses'}}))) {
                   if ($crsdeletions{$cid}) {
                       $changes{$itemid} = 1;
                   } else {
                       $confhash{$itemid}{'courses'}{$cid} = 1;
                   }
               }
           }
           $env{'form.ipaccess_cnum_'.$idx} =~ s/^\s+|\s+$//g;
           $env{'form.ipaccess_cdom_'.$idx} =~ s/^\s+|\s+$//g;
           if (($env{'form.ipaccess_cnum_'.$idx} =~ /^$match_courseid$/) && 
               ($env{'form.ipaccess_cdom_'.$idx} =~ /^$match_domain$/)) {
               if (&Apache::lonnet::homeserver($env{'form.ipaccess_cnum_'.$idx},
                                               $env{'form.ipaccess_cdom_'.$idx}) eq 'no_host') {
                   $errors .= '<li><span class="LC_error">'.
                              &mt('Invalid courseID [_1] omitted from list of allowed courses',
                                  $env{'form.ipaccess_cdom_'.$idx}.'_'.$env{'form.ipaccess_cnum_'.$idx}).
                              '</span></li>';
               } else {
                   $confhash{$itemid}{'courses'}{$env{'form.ipaccess_cdom_'.$idx}.'_'.$env{'form.ipaccess_cnum_'.$idx}} = 1;
                   $changes{$itemid} = 1;
               }
           }
       }
       if (@allpos > 0) {
           my $idx = 0;
           foreach my $itemid (@allpos) {
               if ($itemid ne '') {
                   $confhash{$itemid}{'order'} = $idx;
                   unless ($changes{$itemid}) {
                       if (ref($domconfig{'ipaccess'}) eq 'HASH') {
                           if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                               if ($domconfig{'ipaccess'}{$itemid}{'order'} ne $idx) {
                                   $changes{$itemid} = 1;
                               }
                           }
                       }
                   }
                   $idx ++;
               }
           }
       }
       if (keys(%changes)) {
           my %defaultshash = (
                                 ipaccess => \%confhash,
                              );
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               my $cachetime = 1800;
               &Apache::lonnet::do_cache_new('ipaccess',$dom,\%confhash,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'ipaccess'} = 1;
               }
               $resulttext = &mt('Changes made:').'<ul>';
               my %bynum;
               foreach my $itemid (sort(keys(%changes))) {
                   if (ref($confhash{$itemid}) eq 'HASH') {
                       my $position = $confhash{$itemid}{'order'};
                       if ($position =~ /^\d+$/) {
                           $bynum{$position} = $itemid;
                       }
                   }
               }
               if (keys(%deletions)) {
                   foreach my $itemid (sort { $a <=> $b } keys(%deletions)) {
                       $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';
                   }
               }
               foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                   my $itemid = $bynum{$pos};
                   if (ref($confhash{$itemid}) eq 'HASH') {
                       $resulttext .= '<li><b>'.$confhash{$itemid}{'name'}.'</b><ul>';
                       my $position = $pos + 1;
                       $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                       if ($confhash{$itemid}{'ip'} eq '') {
                           $resulttext .= '<li>'.&mt('No IP Range(s) set').'</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('IP Range(s): [_1]',$confhash{$itemid}{'ip'}).'</li>';
                       }
                       if (keys(%{$confhash{$itemid}{'commblocks'}})) {
                           $resulttext .= '<li>'.&mt('Functionality Blocked: [_1]',
                                                     join(', ', map { $types->{$_}; } sort(keys(%{$confhash{$itemid}{'commblocks'}})))).
                                          '</li>';
                       } else {
                           $resulttext .= '<li>'.&mt('No functionality blocked').'</li>';
                       }
                       if (keys(%{$confhash{$itemid}{'courses'}})) {
                           my @courses;
                           foreach my $cid (sort(keys(%{$confhash{$itemid}{'courses'}}))) {
                               my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1}); 
                               push(@courses,$courseinfo{'description'}.' ('.$cid.')');
                           }
                           $resulttext .= '<li>'.&mt('Courses/Communities allowed').':<ul><li>'.
                                                join('</li><li>',@courses).'</li></ul>';
                       } else {
                           $resulttext .= '<li>'.&mt('No courses allowed').'</li>';
                       }
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
           } else {
               $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
           }
       } else {
           $resulttext = &mt('No changes made');
       }
       if ($errors) {
           $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul></p>';
       }
       return $resulttext;
   }
   
   sub get_ipaccess_id {
       my ($domain,$location) = @_;
       # get lock on ipaccess db
       my $lockhash = {
                         lock => $env{'user.name'}.
                                 ':'.$env{'user.domain'},
                      };
       my $tries = 0;
       my $gotlock = &Apache::lonnet::newput_dom('ipaccess',$lockhash,$domain);
       my ($id,$error);
   
       while (($gotlock ne 'ok') && ($tries<10)) {
           $tries ++;
           sleep (0.1);
           $gotlock = &Apache::lonnet::newput_dom('ipaccess',$lockhash,$domain);
       }
       if ($gotlock eq 'ok') {
           my %currids = &Apache::lonnet::dump_dom('ipaccess',$domain);
           if ($currids{'lock'}) {
               delete($currids{'lock'});
               if (keys(%currids)) {
                   my @curr = sort { $a <=> $b } keys(%currids);
                   if ($curr[-1] =~ /^\d+$/) {
                       $id = 1 + $curr[-1];
                   }
               } else {
                   $id = 1;
               }
               if ($id) {
                   unless (&Apache::lonnet::newput_dom('ipaccess',{ $id => $location },$domain) eq 'ok') {
                       $error = 'nostore';
                   }
               } else {
                   $error = 'nonumber';
               }
           }
           my $dellockoutcome = &Apache::lonnet::del_dom('ipaccess',['lock'],$domain);
       } else {
           $error = 'nolock';
       }
       return ($id,$error);
   }
   
 sub modify_rolecolors {  sub modify_rolecolors {
     my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_;      my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_;
     my ($resulttext,%rolehash);      my ($resulttext,%rolehash);
Line 8003  sub modify_colors { Line 13346  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 8084  sub modify_colors { Line 13432  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 8203  sub default_change_checker { Line 13565  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 8241  sub display_colorchgs { Line 13608  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 8299  sub check_configuser { Line 13673  sub check_configuser {
     my ($configuserok,%currroles);      my ($configuserok,%currroles);
     if ($uhome eq 'no_host') {      if ($uhome eq 'no_host') {
         srand( time() ^ ($$ + ($$ << 15))  ); # Seed rand.          srand( time() ^ ($$ + ($$ << 15))  ); # Seed rand.
         my $configpass = &LONCAPA::Enrollment::create_password();          my $configpass = &LONCAPA::Enrollment::create_password($dom);
         $configuserok =           $configuserok = 
             &Apache::lonnet::modifyuser($dom,$confname,'','internal',              &Apache::lonnet::modifyuser($dom,$confname,'','internal',
                              $configpass,'','','','','',undef,$servadm);                               $configpass,'','','','','',undef,$servadm);
Line 8328  sub check_authorstatus { Line 13702  sub check_authorstatus {
   
 sub publishlogo {  sub publishlogo {
     my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight,$savefileas) = @_;      my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight,$savefileas) = @_;
     my ($output,$fname,$logourl);      my ($output,$fname,$logourl,$madethumb);
     if ($action eq 'upload') {      if ($action eq 'upload') {
         $fname=$env{'form.'.$formname.'.filename'};          $fname=$env{'form.'.$formname.'.filename'};
         chop($env{'form.'.$formname});          chop($env{'form.'.$formname});
Line 8373  sub publishlogo { Line 13747  sub publishlogo {
     } else {      } else {
         my $source = $filepath.'/'.$file;          my $source = $filepath.'/'.$file;
         my $logfile;          my $logfile;
         if (!open($logfile,">>$source".'.log')) {          if (!open($logfile,">>",$source.'.log')) {
             return (&mt('No write permission to Authoring Space'));              return (&mt('No write permission to Authoring Space'));
         }          }
         print $logfile          print $logfile
 "\n================= Publish ".localtime()." ================\n".  "\n================= Publish ".localtime()." ================\n".
 $env{'user.name'}.':'.$env{'user.domain'}."\n";  $env{'user.name'}.':'.$env{'user.domain'}."\n";
 # Save the file  # Save the file
         if (!open(FH,'>'.$source)) {          if (!open(FH,">",$source)) {
             &Apache::lonnet::logthis('Failed to create '.$source);              &Apache::lonnet::logthis('Failed to create '.$source);
             return (&mt('Failed to create file'));              return (&mt('Failed to create file'));
         }          }
Line 8441  $env{'user.name'}.':'.$env{'user.domain' Line 13815  $env{'user.name'}.':'.$env{'user.domain'
                 if ($fullwidth ne '' && $fullheight ne '') {                   if ($fullwidth ne '' && $fullheight ne '') { 
                     if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) {                      if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) {
                         my $thumbsize = $thumbwidth.'x'.$thumbheight;                          my $thumbsize = $thumbwidth.'x'.$thumbheight;
                         system("convert -sample $thumbsize $inputfile $outfile");                          my @args = ('convert','-sample',$thumbsize,$inputfile,$outfile);
                           system({$args[0]} @args);
                         chmod(0660, $filepath.'/tn-'.$file);                          chmod(0660, $filepath.'/tn-'.$file);
                         if (-e $outfile) {                          if (-e $outfile) {
                             my $copyfile=$targetdir.'/tn-'.$file;                              my $copyfile=$targetdir.'/tn-'.$file;
Line 8456  $env{'user.name'}.':'.$env{'user.domain' Line 13831  $env{'user.name'}.':'.$env{'user.domain'
                                     $r->set_handlers('PerlCleanupHandler' => [\&notifysubscribed,@{$handlers}]);                                      $r->set_handlers('PerlCleanupHandler' => [\&notifysubscribed,@{$handlers}]);
                                     $registered_cleanup=1;                                      $registered_cleanup=1;
                                 }                                  }
                                   $madethumb = 1;
                             } else {                              } else {
                                 print $logfile "\nUnable to write ".$copyfile.                                  print $logfile "\nUnable to write ".$copyfile.
                                                ':'.$!."\n";                                                 ':'.$!."\n";
Line 8468  $env{'user.name'}.':'.$env{'user.domain' Line 13844  $env{'user.name'}.':'.$env{'user.domain'
             $output = $versionresult;              $output = $versionresult;
         }          }
     }      }
     return ($output,$logourl);      return ($output,$logourl,$madethumb);
 }  }
   
 sub logo_versioning {  sub logo_versioning {
Line 8520  sub write_metadata { Line 13896  sub write_metadata {
     {      {
         print $logfile "\nWrite metadata file for ".$targetdir.'/'.$file;          print $logfile "\nWrite metadata file for ".$targetdir.'/'.$file;
         my $mfh;          my $mfh;
         if (open($mfh,'>'.$targetdir.'/'.$file.'.meta')) {          if (open($mfh,">",$targetdir.'/'.$file.'.meta')) {
             foreach (sort(keys(%metadatafields))) {              foreach (sort(keys(%metadatafields))) {
                 unless ($_=~/\./) {                  unless ($_=~/\./) {
                     my $unikey=$_;                      my $unikey=$_;
Line 8554  sub notifysubscribed { Line 13930  sub notifysubscribed {
         next unless (ref($targetsource) eq 'ARRAY');          next unless (ref($targetsource) eq 'ARRAY');
         my ($target,$source)=@{$targetsource};          my ($target,$source)=@{$targetsource};
         if ($source ne '') {          if ($source ne '') {
             if (open(my $logfh,'>>'.$source.'.log')) {              if (open(my $logfh,">>",$source.'.log')) {
                 print $logfh "\nCleanup phase: Notifications\n";                  print $logfh "\nCleanup phase: Notifications\n";
                 my @subscribed=&subscribed_hosts($target);                  my @subscribed=&subscribed_hosts($target);
                 foreach my $subhost (@subscribed) {                  foreach my $subhost (@subscribed) {
Line 8580  sub notifysubscribed { Line 13956  sub notifysubscribed {
 sub subscribed_hosts {  sub subscribed_hosts {
     my ($target) = @_;      my ($target) = @_;
     my @subscribed;      my @subscribed;
     if (open(my $fh,"<$target.subscription")) {      if (open(my $fh,"<","$target.subscription")) {
         while (my $subline=<$fh>) {          while (my $subline=<$fh>) {
             if ($subline =~ /^($match_lonid):/) {              if ($subline =~ /^($match_lonid):/) {
                 my $host = $1;                  my $host = $1;
Line 8622  sub modify_quotas { Line 13998  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 8637  sub modify_quotas { Line 14013  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 8671  sub modify_quotas { Line 14047  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 8781  sub modify_quotas { Line 14157  sub modify_quotas {
                                     my ($cdom,$cnum) = split(/_/,$key);                                      my ($cdom,$cnum) = split(/_/,$key);
                                     if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') {                                      if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') {
                                         $errors .= '<li><span class="LC_error">'.&mt('Image not saved: could not find textbook course').'</li>';                                          $errors .= '<li><span class="LC_error">'.&mt('Image not saved: could not find textbook course').'</li>';
                                     } else {                                       } else {
                                         my ($imgurl,$error) = &process_textbook_image($r,$dom,$confname,$type.'_image_'.$i,                                          my ($imgurl,$error) = &process_textbook_image($r,$dom,$confname,$type.'_image_'.$i,
                                                                                       $cdom,$cnum,$type,$configuserok,                                                                                        $cdom,$cnum,$type,$configuserok,
                                                                                       $switchserver,$author_ok);                                                                                        $switchserver,$author_ok);
Line 8793  sub modify_quotas { Line 14169  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 8826  sub modify_quotas { Line 14202  sub modify_quotas {
                     if ($type eq 'textbooks') {                      if ($type eq 'textbooks') {
                         if ($env{'form.'.$type.'_addbook_image.filename'} ne '') {                          if ($env{'form.'.$type.'_addbook_image.filename'} ne '') {
                             my ($cdom,$cnum) = split(/_/,$newbook{$type});                              my ($cdom,$cnum) = split(/_/,$newbook{$type});
                             my ($imageurl,$error) =                              if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') {
                                 &process_textbook_image($r,$dom,$confname,$type.'_addbook_image',$cdom,$cnum,$type,                                  $errors .= '<li><span class="LC_error">'.&mt('Image not saved: could not find textbook course').'</li>';
                                                         $configuserok,$switchserver,$author_ok);                              } else {
                             if ($imageurl) {                                  my ($imageurl,$error) =
                                 $confhash{$type}{$newbook{$type}}{'image'} = $imageurl;                                      &process_textbook_image($r,$dom,$confname,$type.'_addbook_image',$cdom,$cnum,$type,
                             }                                                              $configuserok,$switchserver,$author_ok);
                             if ($error) {                                  if ($imageurl) {
                                 &Apache::lonnet::logthis($error);                                      $confhash{$type}{$newbook{$type}}{'image'} = $imageurl;
                                 $errors .= '<li><span class="LC_error">'.$error.'</span></li>';                                  }
                                   if ($error) {
                                       &Apache::lonnet::logthis($error);
                                       $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
                                   }
                             }                              }
                         }                          }
                     }                      }
Line 9354  sub modify_ltitools { Line 14734  sub modify_ltitools {
     map { $posslti{$_} = 1; } @ltiroles;      map { $posslti{$_} = 1; } @ltiroles;
     my @allfields = ('fullname','firstname','lastname','email','user','roles');      my @allfields = ('fullname','firstname','lastname','email','user','roles');
     map { $possfield{$_} = 1; } @allfields;      map { $possfield{$_} = 1; } @allfields;
     my %lt = &ltitools_names();      my %lt = &ltitools_names(); 
     if ($env{'form.ltitools_add'}) {      if ($env{'form.ltitools_add'}) {
         my $title = $env{'form.ltitools_add_title'};          my $title = $env{'form.ltitools_add_title'};
         $title =~ s/(`)/'/g;          $title =~ s/(`)/'/g;
Line 9366  sub modify_ltitools { Line 14746  sub modify_ltitools {
                 $allpos[$position] = $newid;                  $allpos[$position] = $newid;
             }              }
             $changes{$newid} = 1;              $changes{$newid} = 1;
             foreach my $item ('title','url','key','secret') {              foreach my $item ('title','url','key','secret','lifetime') {
                 $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g;                  $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g;
                   if ($item eq 'lifetime') {
                       $env{'form.ltitools_add_'.$item} =~ s/[^\d.]//g;
                   }
                 if ($env{'form.ltitools_add_'.$item}) {                  if ($env{'form.ltitools_add_'.$item}) {
                     if (($item eq 'key') || ($item eq 'secret')) {                      if (($item eq 'key') || ($item eq 'secret')) {
                         $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item};                          $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item};
Line 9382  sub modify_ltitools { Line 14765  sub modify_ltitools {
             if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') {              if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') {
                 $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'};                  $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'};
             }              }
               if ($env{'form.ltitools_add_sigmethod'} eq 'HMAC-SHA256') {
                   $confhash{$newid}{'sigmethod'} = $env{'form.ltitools_add_sigmethod'};
               } else {
                   $confhash{$newid}{'sigmethod'} = 'HMAC-SHA1';
               }
             foreach my $item ('width','height','linktext','explanation') {              foreach my $item ('width','height','linktext','explanation') {
                 $env{'form.ltitools_add_'.$item} =~ s/^\s+//;                  $env{'form.ltitools_add_'.$item} =~ s/^\s+//;
                 $env{'form.ltitools_add_'.$item} =~ s/\s+$//;                  $env{'form.ltitools_add_'.$item} =~ s/\s+$//;
Line 9391  sub modify_ltitools { Line 14779  sub modify_ltitools {
                     }                      }
                 } else {                  } else {
                     if ($env{'form.ltitools_add_'.$item} ne '') {                      if ($env{'form.ltitools_add_'.$item} ne '') {
                         $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item};                          $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; 
                     }                      }
                 }                  }
             }              }
Line 9403  sub modify_ltitools { Line 14791  sub modify_ltitools {
                 $confhash{$newid}{'display'}{'target'} = 'iframe';                  $confhash{$newid}{'display'}{'target'} = 'iframe';
             }              }
             foreach my $item ('passback','roster') {              foreach my $item ('passback','roster') {
                 if ($env{'form.ltitools_add_'.$item}) {                  if ($env{'form.ltitools_'.$item.'_add'}) {
                     $confhash{$newid}{$item} = 1;                      $confhash{$newid}{$item} = 1;
                       if ($env{'form.ltitools_'.$item.'valid_add'} ne '') {
                           my $lifetime = $env{'form.ltitools_'.$item.'valid_add'};
                           $lifetime =~ s/^\s+|\s+$//g;
                           if ($lifetime =~ /^\d+\.?\d*$/) {
                               $confhash{$newid}{$item.'valid'} = $lifetime;
                           }
                       }
                 }                  }
             }              }
             if ($env{'form.ltitools_add_image.filename'} ne '') {              if ($env{'form.ltitools_add_image.filename'} ne '') {
Line 9428  sub modify_ltitools { Line 14823  sub modify_ltitools {
                             if (($choice ne '') && ($posslti{$choice})) {                              if (($choice ne '') && ($posslti{$choice})) {
                                 $confhash{$newid}{'roles'}{$role} = $choice;                                  $confhash{$newid}{'roles'}{$role} = $choice;
                                 if ($role eq 'cc') {                                  if ($role eq 'cc') {
                                     $confhash{$newid}{'roles'}{'co'} = $choice;                                      $confhash{$newid}{'roles'}{'co'} = $choice; 
                                 }                                  }
                             }                              }
                         }                          }
Line 9437  sub modify_ltitools { Line 14832  sub modify_ltitools {
                     }                      }
                 }                  }
             }              }
               if (ref($confhash{$newid}{'fields'}) eq 'HASH') {
                   if ($confhash{$newid}{'fields'}{'user'}) {
                       if ($env{'form.ltitools_userincdom_add'}) {
                           $confhash{$newid}{'incdom'} = 1;
                       }
                   }
               }
             my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig');              my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig');
             foreach my $item (@courseconfig) {              foreach my $item (@courseconfig) {
                 $confhash{$newid}{'crsconf'}{$item} = 1;                  $confhash{$newid}{'crsconf'}{$item} = 1;
Line 9449  sub modify_ltitools { Line 14851  sub modify_ltitools {
                 $confhash{$newid}{'custom'}{$name} = $value;                  $confhash{$newid}{'custom'}{$name} = $value;
             }              }
         } else {          } else {
             my $error = &mt('Failed to acquire unique ID for new external tool');              my $error = &mt('Failed to acquire unique ID for new external tool');   
             $errors .= '<li><span class="LC_error">'.$error.'</span></li>';              $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
         }          }
     }      }
Line 9463  sub modify_ltitools { Line 14865  sub modify_ltitools {
         my @newcustom = &Apache::loncommon::get_env_multiple('form.ltitools_customadd');          my @newcustom = &Apache::loncommon::get_env_multiple('form.ltitools_customadd');
         if (@newcustom) {          if (@newcustom) {
             map { $customadds{$_} = 1; } @newcustom;              map { $customadds{$_} = 1; } @newcustom;
         }          } 
         my %imgdeletions;          my %imgdeletions;
         my @todeleteimages = &Apache::loncommon::get_env_multiple('form.ltitools_image_del');          my @todeleteimages = &Apache::loncommon::get_env_multiple('form.ltitools_image_del');
         if (@todeleteimages) {          if (@todeleteimages) {
Line 9483  sub modify_ltitools { Line 14885  sub modify_ltitools {
                 } else {                  } else {
                     my $newpos = $env{'form.ltitools_'.$itemid};                      my $newpos = $env{'form.ltitools_'.$itemid};
                     $newpos =~ s/\D+//g;                      $newpos =~ s/\D+//g;
                     foreach my $item ('title','url') {                      foreach my $item ('title','url','lifetime') {
                         $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i};                          $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i};
                         if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) {                          if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) {
                             $changes{$itemid} = 1;                              $changes{$itemid} = 1;
Line 9501  sub modify_ltitools { Line 14903  sub modify_ltitools {
                     if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') {                      if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') {
                         $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i};                          $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i};
                     }                      }
                       if ($env{'form.ltitools_sigmethod_'.$i} eq 'HMAC-SHA256') {
                           $confhash{$itemid}{'sigmethod'} = $env{'form.ltitools_sigmethod_'.$i};
                       } else {
                           $confhash{$itemid}{'sigmethod'} = 'HMAC-SHA1'; 
                       }
                       if ($domconfig{$action}{$itemid}{'sigmethod'} eq '') {
                           if ($confhash{$itemid}{'sigmethod'} ne 'HMAC-SHA1') {
                               $changes{$itemid} = 1;
                           }
                       } elsif ($domconfig{$action}{$itemid}{'sigmethod'} ne $confhash{$itemid}{'sigmethod'}) {
                           $changes{$itemid} = 1;
                       }
                     foreach my $size ('width','height') {                      foreach my $size ('width','height') {
                         $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//;                          $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//;
                         $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//;                          $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//;
Line 9554  sub modify_ltitools { Line 14968  sub modify_ltitools {
                     foreach my $extra ('passback','roster') {                      foreach my $extra ('passback','roster') {
                         if ($env{'form.ltitools_'.$extra.'_'.$i}) {                          if ($env{'form.ltitools_'.$extra.'_'.$i}) {
                             $confhash{$itemid}{$extra} = 1;                              $confhash{$itemid}{$extra} = 1;
                               if ($env{'form.ltitools_'.$extra.'valid_'.$i} ne '') {
                                   my $lifetime = $env{'form.ltitools_'.$extra.'valid_'.$i};
                                   $lifetime =~ s/^\s+|\s+$//g;
                                   if ($lifetime =~ /^\d+\.?\d*$/) {
                                       $confhash{$itemid}{$extra.'valid'} = $lifetime;
                                   }
                               }
                         }                          }
                         if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) {                          if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) {
                             $changes{$itemid} = 1;                              $changes{$itemid} = 1;
                         }                          }
                           if ($domconfig{$action}{$itemid}{$extra.'valid'} ne $confhash{$itemid}{$extra.'valid'}) {
                               $changes{$itemid} = 1;
                           }
                     }                      }
                     my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i);                      my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i);
                     foreach my $item ('label','title','target','linktext','explanation') {                      foreach my $item ('label','title','target','linktext','explanation','append') {
                         if (grep(/^\Q$item\E$/,@courseconfig)) {                          if (grep(/^\Q$item\E$/,@courseconfig)) {
                             $confhash{$itemid}{'crsconf'}{$item} = 1;                              $confhash{$itemid}{'crsconf'}{$item} = 1;
                             if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') {                              if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') {
Line 9604  sub modify_ltitools { Line 15028  sub modify_ltitools {
                             }                              }
                         }                          }
                     }                      }
                       if (ref($confhash{$itemid}{'fields'}) eq 'HASH') {
                           if ($confhash{$itemid}{'fields'}{'user'}) {
                               if ($env{'form.ltitools_userincdom_'.$i}) {
                                   $confhash{$itemid}{'incdom'} = 1;
                               }
                               if ($domconfig{$action}{$itemid}{'incdom'} ne $confhash{$itemid}{'incdom'}) {
                                   $changes{$itemid} = 1;
                               }
                           }
                       }
                     $allpos[$newpos] = $itemid;                      $allpos[$newpos] = $itemid;
                 }                  }
                 if ($imgdeletions{$itemid}) {                  if ($imgdeletions{$itemid}) {
Line 9640  sub modify_ltitools { Line 15074  sub modify_ltitools {
                     }                      }
                 }                  }
                 my %customdels;                  my %customdels;
                 my @customdeletions = &Apache::loncommon::get_env_multiple('form.ltitools_customdel_'.$i);                  my @customdeletions = &Apache::loncommon::get_env_multiple('form.ltitools_customdel_'.$i); 
                 if (@customdeletions) {                  if (@customdeletions) {
                     $changes{$itemid} = 1;                      $changes{$itemid} = 1;
                 }                  }
Line 9649  sub modify_ltitools { Line 15083  sub modify_ltitools {
                     foreach my $key (keys(%{$domconfig{$action}{$itemid}{'custom'}})) {                      foreach my $key (keys(%{$domconfig{$action}{$itemid}{'custom'}})) {
                         unless ($customdels{$key}) {                          unless ($customdels{$key}) {
                             if ($env{'form.ltitools_customval_'.$key.'_'.$i} ne '') {                              if ($env{'form.ltitools_customval_'.$key.'_'.$i} ne '') {
                                 $confhash{$itemid}{'custom'}{$key} = $env{'form.ltitools_customval_'.$key.'_'.$i};                                  $confhash{$itemid}{'custom'}{$key} = $env{'form.ltitools_customval_'.$key.'_'.$i}; 
                             }                              }
                             if ($domconfig{$action}{$itemid}{'custom'}{$key} ne $env{'form.ltitools_customval_'.$key.'_'.$i}) {                              if ($domconfig{$action}{$itemid}{'custom'}{$key} ne $env{'form.ltitools_customval_'.$key.'_'.$i}) {
                                 $changes{$itemid} = 1;                                  $changes{$itemid} = 1;
Line 9702  sub modify_ltitools { Line 15136  sub modify_ltitools {
         my %ltienchash = (          my %ltienchash = (
                              $action => { %encconfig }                               $action => { %encconfig }
                          );                           );
         &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom);          &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1);
         if (keys(%changes) > 0) {          if (keys(%changes) > 0) {
             my $cachetime = 24*60*60;              my $cachetime = 24*60*60;
             my %ltiall = %confhash;              my %ltiall = %confhash;
Line 9724  sub modify_ltitools { Line 15158  sub modify_ltitools {
                 $bynum{$position} = $itemid;                  $bynum{$position} = $itemid;
             }              }
             foreach my $pos (sort { $a <=> $b } keys(%bynum)) {              foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                 my $itemid = $bynum{$pos};                  my $itemid = $bynum{$pos}; 
                 if (ref($confhash{$itemid}) ne 'HASH') {                  if (ref($confhash{$itemid}) ne 'HASH') {
                     $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';                      $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';
                 } else {                  } else {
Line 9737  sub modify_ltitools { Line 15171  sub modify_ltitools {
                     $resulttext .= '</li><ul>';                      $resulttext .= '</li><ul>';
                     my $position = $pos + 1;                      my $position = $pos + 1;
                     $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';                      $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                     foreach my $item ('version','msgtype','url') {                      foreach my $item ('version','msgtype','sigmethod','url','lifetime') {
                         if ($confhash{$itemid}{$item} ne '') {                          if ($confhash{$itemid}{$item} ne '') {
                             $resulttext .= '<li>'.$lt{$item}.':&nbsp;'.$confhash{$itemid}{$item}.'</li>';                              $resulttext .= '<li>'.$lt{$item}.':&nbsp;'.$confhash{$itemid}{$item}.'</li>';
                         }                          }
Line 9751  sub modify_ltitools { Line 15185  sub modify_ltitools {
                         $resulttext .= ('*'x$num).'</li>';                          $resulttext .= ('*'x$num).'</li>';
                     }                      }
                     $resulttext .= '<li>'.&mt('Configurable in course:');                      $resulttext .= '<li>'.&mt('Configurable in course:');
                     my @possconfig = ('label','title','target','linktext','explanation');                      my @possconfig = ('label','title','target','linktext','explanation','append');
                     my $numconfig = 0;                      my $numconfig = 0; 
                     if (ref($confhash{$itemid}{'crsconf'}) eq 'HASH') {                      if (ref($confhash{$itemid}{'crsconf'}) eq 'HASH') { 
                         foreach my $item (@possconfig) {                          foreach my $item (@possconfig) {
                             if ($confhash{$itemid}{'crsconf'}{$item}) {                              if ($confhash{$itemid}{'crsconf'}{$item}) {
                                 $numconfig ++;                                  $numconfig ++;
Line 9762  sub modify_ltitools { Line 15196  sub modify_ltitools {
                         }                          }
                     }                      }
                     if (!$numconfig) {                      if (!$numconfig) {
                         $resulttext .= &mt('None');                          $resulttext .= '&nbsp;'.&mt('None');
                     }                      }
                     $resulttext .= '</li>';                      $resulttext .= '</li>';
                     foreach my $item ('passback','roster') {                      foreach my $item ('passback','roster') {
                         $resulttext .= '<li>'.$lt{$item}.'&nbsp;';                          $resulttext .= '<li>'.$lt{$item}.'&nbsp;';
                         if ($confhash{$itemid}{$item}) {                          if ($confhash{$itemid}{$item}) {
                             $resulttext .= &mt('Yes');                              $resulttext .= &mt('Yes');
                               if ($confhash{$itemid}{$item.'valid'}) {
                                   if ($item eq 'passback') {
                                       $resulttext .= ' '.&mt('valid for at least [quant,_1,day] after launch',
                                                              $confhash{$itemid}{$item.'valid'});
                                   } else {
                                       $resulttext .= ' '.&mt('valid for at least [quant,_1,second] after launch',
                                                              $confhash{$itemid}{$item.'valid'});
                                   }
                               }
                         } else {                          } else {
                             $resulttext .= &mt('No');                              $resulttext .= &mt('No');
                         }                          }
Line 9780  sub modify_ltitools { Line 15223  sub modify_ltitools {
                             $displaylist = &mt('Display target').':&nbsp;'.                              $displaylist = &mt('Display target').':&nbsp;'.
                                            $confhash{$itemid}{'display'}{'target'}.',';                                             $confhash{$itemid}{'display'}{'target'}.',';
                         }                          }
                         foreach my $size ('width','height') {                          foreach my $size ('width','height') { 
                             if ($confhash{$itemid}{'display'}{$size}) {                              if ($confhash{$itemid}{'display'}{$size}) {
                                 $displaylist .= ('&nbsp;'x2).$lt{$size}.':&nbsp;'.                                  $displaylist .= ('&nbsp;'x2).$lt{$size}.':&nbsp;'.
                                                 $confhash{$itemid}{'display'}{$size}.',';                                                  $confhash{$itemid}{'display'}{$size}.',';
Line 9805  sub modify_ltitools { Line 15248  sub modify_ltitools {
                         }                          }
                         if ($fieldlist) {                          if ($fieldlist) {
                             $fieldlist =~ s/,$//;                              $fieldlist =~ s/,$//;
                               if ($confhash{$itemid}{'fields'}{'user'}) {
                                   if ($confhash{$itemid}{'incdom'}) {
                                       $fieldlist .= ' ('.&mt('username:domain').')';
                                   } else {
                                       $fieldlist .= ' ('.&mt('username').')';
                                   }
                               }
                             $resulttext .= '<li>'.&mt('Data sent').':'.$fieldlist.'</li>';                              $resulttext .= '<li>'.&mt('Data sent').':'.$fieldlist.'</li>';
                         }                          }
                     }                      }
Line 9817  sub modify_ltitools { Line 15267  sub modify_ltitools {
                             }                              }
                         }                          }
                         if ($rolemaps) {                          if ($rolemaps) {
                             $rolemaps =~ s/,$//;                              $rolemaps =~ s/,$//; 
                             $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';                              $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';
                         }                          }
                     }                      }
Line 9826  sub modify_ltitools { Line 15276  sub modify_ltitools {
                         if (keys(%{$confhash{$itemid}{'custom'}})) {                          if (keys(%{$confhash{$itemid}{'custom'}})) {
                             foreach my $key (sort(keys(%{$confhash{$itemid}{'custom'}}))) {                              foreach my $key (sort(keys(%{$confhash{$itemid}{'custom'}}))) {
                                 $customlist .= $key.':'.$confhash{$itemid}{'custom'}{$key}.('&nbsp;'x2);                                  $customlist .= $key.':'.$confhash{$itemid}{'custom'}{$key}.('&nbsp;'x2);
                             }                              } 
                         }                          }
                         if ($customlist) {                          if ($customlist) {
                             $resulttext .= '<li>'.&mt('Custom items').':'.$customlist.'</li>';                              $resulttext .= '<li>'.&mt('Custom items').': '.$customlist.'</li>';
                         }                          }
                     }                      } 
                     $resulttext .= '</ul></li>';                      $resulttext .= '</ul></li>';
                 }                  }
             }              }
Line 9892  sub get_ltitools_id { Line 15342  sub get_ltitools_id {
     my $tries = 0;      my $tries = 0;
     my $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom);      my $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom);
     my ($id,$error);      my ($id,$error);
    
     while (($gotlock ne 'ok') && ($tries<10)) {      while (($gotlock ne 'ok') && ($tries<10)) {
         $tries ++;          $tries ++;
         sleep (0.1);          sleep (0.1);
Line 9925  sub get_ltitools_id { Line 15375  sub get_ltitools_id {
     return ($id,$error);      return ($id,$error);
 }  }
   
   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 ($result,$imageurl,$madethumb) =
                   &publishlogo($r,'upload',$caller,$dom,$confname,
                                "proctoring/$provider/icon",$width,$height);
               if ($result eq 'ok') {
                   if ($madethumb) {
                       my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$});
                       my $imagethumb = "$path/tn-".$imagefile;
                       $url = $imagethumb;
                   } else {
                       $url = $imageurl;
                   }
               } else {
                   $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);
               }
           } else {
               $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3].  Error was: [_4].",$filename,$confname,$dom,$author_ok);
           }
       } else {
           $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3].  Error was: [_4].",$filename,$confname,$dom,$configuserok);
       }
       return ($url,$error);
   }
   
   sub modify_lti {
       my ($r,$dom,$action,$lastactref,%domconfig) = @_;
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my ($newid,@allpos,%changes,%confhash,%encconfig,$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,%keyset,%newkeyset);
       $newltisec{'private'}{'keys'} = [];
       $newltisec{'encrypt'} = {};
       $newltisec{'rules'} = {};
       $newltisec{'linkprot'} = {};
       if (ref($domconfig{'ltisec'}) eq 'HASH') {
           %currltisec = %{$domconfig{'ltisec'}};
           if (ref($currltisec{'linkprot'}) eq 'HASH') {
               foreach my $id (keys(%{$currltisec{'linkprot'}})) {
                   unless ($id =~ /^\d+$/) {
                       delete($currltisec{'linkprot'}{$id});
                   }
               }
           }
           if (ref($currltisec{'private'}) eq 'HASH') {
               if (ref($currltisec{'private'}{'keys'}) eq 'ARRAY') {
                   $newltisec{'private'}{'keys'} = $currltisec{'private'}{'keys'};
                   map { $keyset{$_} = 1; } @{$currltisec{'private'}{'keys'}};
               }
           }
       }
       foreach my $item ('crs','dom','consumers') {
           my $formelement;
           if ($item eq 'consumers') { 
               $formelement = 'form.ltisec_'.$item;
           } else {
               $formelement = 'form.ltisec_'.$item.'linkprot';
           }
           if ($env{$formelement}) {
               $newltisec{'encrypt'}{$item} = 1;
               if (ref($currltisec{'encrypt'}) eq 'HASH') {
                   unless ($currltisec{'encrypt'}{$item}) {
                       $secchanges{'encrypt'} = 1;
                   }
               } else {
                   $secchanges{'encrypt'} = 1;
               }
           } elsif (ref($currltisec{'encrypt'}) eq 'HASH') {
               if ($currltisec{'encrypt'}{$item}) {
                   $secchanges{'encrypt'} = 1;
               }
           }
       }
       unless (exists($currltisec{'rules'})) {
           $currltisec{'rules'} = {};
       }
       &password_rule_changes('secrets',$newltisec{'rules'},$currltisec{'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 $newkey;
               my $keyitem = 'form.ltisec_privkey_'.$hostid;
               if (exists($env{$keyitem})) {
                   $env{$keyitem} =~ s/(`)/'/g;
                   if ($keyset{$hostid}) {
                       if ($env{'form.ltisec_changeprivkey_'.$hostid}) {
                           if ($env{$keyitem} ne '') {
                               $secchanges{'private'} = 1;
                               $newkeyset{$hostid} = $env{$keyitem};
                           }
                       }
                   } elsif ($env{$keyitem} ne '') {
                       unless (grep(/^\Q$hostid\E$/,@{$newltisec{'private'}{'keys'}})) { 
                           push(@{$newltisec{'private'}{'keys'}},$hostid);
                       }
                       $secchanges{'private'} = 1;
                       $newkeyset{$hostid} = $env{$keyitem};
                   }
               }
           }
       }
   
       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;
                   }
               }
           }
       }
       foreach my $idx (@items) {
           my $itemid = $itemids{$idx};
           next unless ($itemid);
           my $position = $env{'form.lti_pos_'.$itemid};
           $position =~ s/\D+//g;
           if ($position ne '') {
               $allpos[$position] = $itemid;
           }
           foreach my $item ('consumer','key','secret','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 '') {
                   if (($item eq 'key') || ($item eq 'secret')) {
                       $encconfig{$itemid}{$item} = $env{$formitem};
                   } else {
                       $confhash{$itemid}{$item} = $env{$formitem};
                       unless (($idx eq 'add') || ($changes{$itemid})) {
                           if ($domconfig{$action}{$itemid}{$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 ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) {
                               $changes{$itemid} = 1;
                           }
                       }
                       unless ($changes{$itemid}) {
                           if ($domconfig{$action}{$itemid}{'passback'} eq $confhash{$itemid}{'passback'}) {
                               if ($domconfig{$action}{$itemid}{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) {
                                   $changes{$itemid} = 1;
                               }
                           }
                       }
                       foreach my $field ('mapcrstype','selfenroll') {
                           unless ($changes{$itemid}) {
                               if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') {
                                   if (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                       my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field},
                                                                                      $confhash{$itemid}{$field});
                                       if (@diffs) {
                                           $changes{$itemid} = 1;
                                       }
                                   } elsif (@{$domconfig{$action}{$itemid}{$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($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') {
                               if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
                                   foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) {
                                       if ($domconfig{$action}{$itemid}{'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 
                                               $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) {
                                               $changes{$itemid} = 1;
                                               last;
                                           }
                                       }
                                   }
                               } elsif (keys(%{$domconfig{$action}{$itemid}{'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 ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) {
                               $changes{$itemid} = 1;
                           }
                       }
                       unless ($changes{$itemid}) {
                           foreach my $field ('makeuser','lcmenu') {
                               if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') {
                                   if (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                       my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field},
                                                                                      $confhash{$itemid}{$field});
                                       if (@diffs) {
                                           $changes{$itemid} = 1;
                                       }
                                   } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') {
                                   if (@{$confhash{$itemid}{$field}} > 0) {
                                       $changes{$itemid} = 1;
                                   }
                               }
                           }
                       }
                   }
               }
           }
       }
       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 ++;
               }
           }
       }
       my %ltihash = (
                         $action => { %confhash }
                     );
       my %ltienchash = (
                            $action => { %encconfig }
                        );
       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') {
           my %keystore;
           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,'lti','private',
                                                                       $dom,$hostid);
                   }
               }
               if (ref($lastactref) eq 'HASH') {
                   if (($secchanges{'encrypt'}) || ($secchanges{'private'})) {
                       $lastactref->{'domdefaults'} = 1;
                   }
               }
           }
           &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1);
           if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) {
               return &mt('No changes made.');
           }
           $resulttext = &mt('Changes made:').'<ul>';
           if (keys(%secchanges) > 0) {
               foreach my $item (keys(%secchanges)) {
                   if ($item eq 'encrypt') {
                       my %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'),
                                              },
                               );
                       foreach my $type ('crs','dom','consumers') {
                           my $shown = $encrypted{$type}{'off'};
                           if (ref($newltisec{$item}) eq 'HASH') {
                               if ($newltisec{$item}{$type}) {
                                   $shown = $encrypted{$type}{'on'}; 
                               }
                           }
                           $resulttext .= '<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 ($newltisec{rules}{$rule} eq '') {
                                if ($rule eq 'min') {
                                    $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule});
                                                   ' '.&mt('Default of [_1] will be used',
                                                               $Apache::lonnet::passwdmin).'</li>';
                                } else {
                                    $resulttext .= '<li>'.&mt('[_1] set to none',$titles{$rule}).'</li>';
                                }
                            } else {
                                $resulttext .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$newltisec{rules}{$rule}).'</li>';
                            }
                        }
                        if (ref($newltisec{'rules'}{'chars'}) eq 'ARRAY') {
                            if (@{$newltisec{'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{$_} } @{$newltisec{'rules'}{'chars'}}).
                                             '</li></ul>';
                                $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>';
                            } else {
                                $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                            }
                        } else {
                            $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                        }
                   } elsif ($item eq 'private') {
                       if (keys(%newkeyset)) {
                           foreach my $hostid (sort(keys(%newkeyset))) {
                               if ($keystore{$hostid} eq 'ok') {
                                   $resulttext .= '<li>'.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'</li>';
                               }
                           }
                       }
                   } elsif ($item eq 'linkprot') {
                       $resulttext .= $linkprotoutput;
                   }
               }
           }
           if (keys(%changes) > 0) {
               my $cachetime = 24*60*60;
               my %ltiall = %confhash;
               foreach my $id (keys(%ltiall)) {
                   if (ref($encconfig{$id}) eq 'HASH') {
                       foreach my $item ('key','secret') {
                           $ltiall{$id}{$item} = $encconfig{$id}{$item};
                       }
                   }
               }
               &Apache::lonnet::do_cache_new('lti',$dom,\%ltiall,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'lti'} = 1;
               }
               my %bynum;
               foreach my $itemid (sort(keys(%changes))) {
                   my $position = $confhash{$itemid}{'order'};
                   $bynum{$position} = $itemid;
               }
               foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                   my $itemid = $bynum{$pos};
                   if (ref($confhash{$itemid}) ne 'HASH') {
                       $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>';
                   } else {
                       $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 ($encconfig{$itemid}{'key'} ne '') {
                           $resulttext .= '<li>'.$lt{'key'}.':&nbsp;'.$encconfig{$itemid}{'key'}.'</li>';
                       }
                       if ($encconfig{$itemid}{'secret'} ne '') {
                           $resulttext .= '<li>'.$lt{'secret'}.':&nbsp;';
                           my $num = length($encconfig{$itemid}{'secret'});
                           $resulttext .= ('*'x$num).'</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>';
                   }
               }
           }
           $resulttext .= '</ul>';
       } 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_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 9938  sub modify_autoenroll { Line 16737  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 9948  sub modify_autoenroll { Line 16747  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 9986  sub modify_autoenroll { Line 16791  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 10009  sub modify_autoenroll { Line 16817  sub modify_autoenroll {
                 }                  }
             }              }
             if ($changes{'autofailsafe'}) {              if ($changes{'autofailsafe'}) {
                 if ($failsafe ne '') {                  if ($autofailsafe ne '') {
                     $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section set to: [_1]',$failsafe).'</li>';                      $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section set to: [_1]',$autofailsafe).'</li>';
                 } else {                  } else {
                     $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section: deleted');                      $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section not in use').'</li>';
                 }                  }
               }
               if ($changes{'failsafe'}) {
                   if ($failsafe eq 'off') {
                       unless ($changes{'autofailsafe'}) {
                           $resulttext .= '<li>'.&mt('Failsafe for no drops if institutional data missing for a section not in use').'</li>';
                       }
                   } elsif ($failsafe eq 'zero') {
                       $resulttext .= '<li>'.&mt('Failsafe applies if retrieved section enrollment is zero').'</li>';
                   } else {
                       $resulttext .= '<li>'.&mt('Failsafe applies if retrieved section enrollment is zero or greater').'</li>';
                   }
               }
               if (($changes{'autofailsafe'}) || ($changes{'failsafe'})) {
                 &Apache::lonnet::get_domain_defaults($dom,1);                  &Apache::lonnet::get_domain_defaults($dom,1);
                 if (ref($lastactref) eq 'HASH') {                  if (ref($lastactref) eq 'HASH') {
                     $lastactref->{'domdefaults'} = 1;                      $lastactref->{'domdefaults'} = 1;
Line 10040  sub modify_autoupdate { Line 16861  sub modify_autoupdate {
     }      }
     my @offon = ('off','on');      my @offon = ('off','on');
     my %title = &Apache::lonlocal::texthash (      my %title = &Apache::lonlocal::texthash (
                    run => 'Auto-update:',                      run        => 'Auto-update:',
                    classlists => 'Updates to user information in classlists?'                      classlists => 'Updates to user information in classlists?',
                       unexpired  => 'Skip updates for users without active or future roles?',
                       lastactive => 'Skip updates for inactive users?',
                 );                  );
     my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);      my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
     my %fieldtitles = &Apache::lonlocal::texthash (      my %fieldtitles = &Apache::lonlocal::texthash (
Line 10085  sub modify_autoupdate { Line 16908  sub modify_autoupdate {
     my %updatehash = (      my %updatehash = (
                       autoupdate => { run => $env{'form.autoupdate_run'},                        autoupdate => { run => $env{'form.autoupdate_run'},
                                       classlists => $env{'form.classlists'},                                        classlists => $env{'form.classlists'},
                                         unexpired  => $env{'form.unexpired'},
                                       fields => {%fields},                                        fields => {%fields},
                                       lockablenames => \@lockablenames,                                        lockablenames => \@lockablenames,
                                     }                                      }
                      );                       );
       my $lastactivedays;
       if ($env{'form.lastactive'}) {
           $lastactivedays = $env{'form.lastactivedays'};
           $lastactivedays =~ s/^\s+|\s+$//g;
           unless ($lastactivedays =~ /^\d+$/) {
               undef($lastactivedays);
               $env{'form.lastactive'} = 0;
           }
       }
       $updatehash{'autoupdate'}{'lastactive'} = $lastactivedays;
     foreach my $key (keys(%currautoupdate)) {      foreach my $key (keys(%currautoupdate)) {
         if (($key eq 'run') || ($key eq 'classlists')) {          if (($key eq 'run') || ($key eq 'classlists') || ($key eq 'unexpired') || ($key eq 'lastactive')) {
             if (exists($updatehash{autoupdate}{$key})) {              if (exists($updatehash{autoupdate}{$key})) {
                 if ($currautoupdate{$key} ne $updatehash{autoupdate}{$key}) {                  if ($currautoupdate{$key} ne $updatehash{autoupdate}{$key}) {
                     $changes{$key} = 1;                      $changes{$key} = 1;
Line 10136  sub modify_autoupdate { Line 16970  sub modify_autoupdate {
             $changes{'lockablenames'} = 1;              $changes{'lockablenames'} = 1;
         }          }
     }      }
       unless (grep(/^unexpired$/,keys(%currautoupdate))) {
           if ($updatehash{'autoupdate'}{'unexpired'}) {
               $changes{'unexpired'} = 1;
           }
       }
       unless (grep(/^lastactive$/,keys(%currautoupdate))) {
           if ($updatehash{'autoupdate'}{'lastactive'} ne '') {
               $changes{'lastactive'} = 1;
           }
       }
     foreach my $item (@{$types},'default') {      foreach my $item (@{$types},'default') {
         if (defined($fields{$item})) {          if (defined($fields{$item})) {
             if (ref($currautoupdate{'fields'}) eq 'HASH') {              if (ref($currautoupdate{'fields'}) eq 'HASH') {
Line 10198  sub modify_autoupdate { Line 17042  sub modify_autoupdate {
                     my $newvalue;                      my $newvalue;
                     if ($key eq 'run') {                      if ($key eq 'run') {
                         $newvalue = $offon[$env{'form.autoupdate_run'}];                          $newvalue = $offon[$env{'form.autoupdate_run'}];
                       } elsif ($key eq 'lastactive') {
                           $newvalue = $offon[$env{'form.lastactive'}];
                           unless ($lastactivedays eq '') {
                               $newvalue .= '; '.&mt('inactive = no activity in last [quant,_1,day]',$lastactivedays);
                           }
                     } else {                      } else {
                         $newvalue = $offon[$env{'form.'.$key}];                          $newvalue = $offon[$env{'form.'.$key}];
                     }                      }
Line 10510  sub modify_contacts { Line 17359  sub modify_contacts {
     my (%others,%to,%bcc,%includestr,%includeloc);      my (%others,%to,%bcc,%includestr,%includeloc);
     my @contacts = ('supportemail','adminemail');      my @contacts = ('supportemail','adminemail');
     my @mailings = ('errormail','packagesmail','helpdeskmail','otherdomsmail',      my @mailings = ('errormail','packagesmail','helpdeskmail','otherdomsmail',
                     'lonstatusmail','requestsmail','updatesmail','idconflictsmail');                      'lonstatusmail','requestsmail','updatesmail','idconflictsmail','hostipmail');
     my @toggles = ('reporterrors','reportupdates');      my @toggles = ('reporterrors','reportupdates','reportstatus');
       my @lonstatus = ('threshold','sysmail','weights','excluded');
     my ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();      my ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields();
     foreach my $type (@mailings) {      foreach my $type (@mailings) {
         @{$newsetting{$type}} =           @{$newsetting{$type}} = 
Line 10544  sub modify_contacts { Line 17394  sub modify_contacts {
             $contacts_hash{'contacts'}{$item} = $env{'form.'.$item};              $contacts_hash{'contacts'}{$item} = $env{'form.'.$item};
         }          }
     }      }
       my ($lonstatus_defs,$lonstatus_names) = &Apache::loncommon::lon_status_items();
       foreach my $item (@lonstatus) {
           if ($item eq 'excluded') {
               my (%serverhomes,@excluded);
               map { $serverhomes{$_} = 1; } values(%Apache::lonnet::serverhomeIDs);
               my @possexcluded = &Apache::loncommon::get_env_multiple('form.errorexcluded');
               if (@possexcluded) {
                   foreach my $id (sort(@possexcluded)) {
                       if ($serverhomes{$id}) {
                           push(@excluded,$id);
                       }
                   }
               }
               if (@excluded) {
                   $contacts_hash{'contacts'}{'lonstatus'}{$item} = \@excluded;
               }
           } elsif ($item eq 'weights') {
               foreach my $type ('E','W','N','U') {
                   $env{'form.error'.$item.'_'.$type} =~ s/^\s+|\s+$//g;
                   if ($env{'form.error'.$item.'_'.$type} =~ /^\d+$/) {
                       unless ($env{'form.error'.$item.'_'.$type} == $lonstatus_defs->{$type}) {
                           $contacts_hash{'contacts'}{'lonstatus'}{$item}{$type} =
                               $env{'form.error'.$item.'_'.$type};
                       }
                   }
               }
           } elsif (($item eq 'threshold') || ($item eq 'sysmail')) {
               $env{'form.error'.$item} =~ s/^\s+|\s+$//g;
               if ($env{'form.error'.$item} =~ /^\d+$/) {
                   unless ($env{'form.error'.$item} == $lonstatus_defs->{$item}) {
                       $contacts_hash{'contacts'}{'lonstatus'}{$item} = $env{'form.error'.$item};
                   }
               }
           }
       }
     if ((ref($fields) eq 'ARRAY') && (ref($possoptions) eq 'HASH')) {      if ((ref($fields) eq 'ARRAY') && (ref($possoptions) eq 'HASH')) {
         foreach my $field (@{$fields}) {          foreach my $field (@{$fields}) {
             if (ref($possoptions->{$field}) eq 'ARRAY') {              if (ref($possoptions->{$field}) eq 'ARRAY') {
Line 10598  sub modify_contacts { Line 17483  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 10656  sub modify_contacts { Line 17541  sub modify_contacts {
             }              }
         }          }
         if (@statuses) {          if (@statuses) {
             if (ref($currsetting{'overrides'}) eq 'HASH') {              if (ref($currsetting{'overrides'}) eq 'HASH') { 
                 foreach my $key (keys(%{$currsetting{'overrides'}})) {                  foreach my $key (keys(%{$currsetting{'overrides'}})) {
                     if (ref($currsetting{'overrides'}{$key}) eq 'HASH') {                      if (ref($currsetting{'overrides'}{$key}) eq 'HASH') {
                         if (ref($newsetting{'override_'.$key}) eq 'HASH') {                          if (ref($newsetting{'override_'.$key}) eq 'HASH') {
                             foreach my $item (@contacts,'bcc','others','include') {                              foreach my $item (@contacts,'bcc','others','include') {
                                 if ($currsetting{'overrides'}{$key}{$item} ne $newsetting{'override_'.$key}{$item}) {                                  if ($currsetting{'overrides'}{$key}{$item} ne $newsetting{'override_'.$key}{$item}) { 
                                     push(@{$changes{'overrides'}},$key);                                      push(@{$changes{'overrides'}},$key);
                                     last;                                      last;
                                 }                                  }
Line 10678  sub modify_contacts { Line 17563  sub modify_contacts {
                 }                  }
             } else {              } else {
                 foreach my $key (@overrides) {                  foreach my $key (@overrides) {
                     push(@{$changes{'overrides'}},$key);                      push(@{$changes{'overrides'}},$key); 
                   }
               }
           }
           if (ref($currsetting{'lonstatus'}) eq 'HASH') {
               foreach my $key ('excluded','weights','threshold','sysmail') {
                   if ($key eq 'excluded') {
                       if ((ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') &&
                           (ref($contacts_hash{contacts}{lonstatus}{excluded}) eq 'ARRAY')) {
                           if ((ref($currsetting{'lonstatus'}{$key}) eq 'ARRAY') &&
                               (@{$currsetting{'lonstatus'}{$key}})) {
                               my @diffs =
                                   &Apache::loncommon::compare_arrays($contacts_hash{contacts}{lonstatus}{excluded},
                                                                      $currsetting{'lonstatus'}{$key});
                               if (@diffs) {
                                   push(@{$changes{'lonstatus'}},$key);
                               }
                           } elsif (@{$contacts_hash{contacts}{lonstatus}{excluded}}) {
                               push(@{$changes{'lonstatus'}},$key);
                           }
                       } elsif ((ref($currsetting{'lonstatus'}{$key}) eq 'ARRAY') &&
                                (@{$currsetting{'lonstatus'}{$key}})) {
                           push(@{$changes{'lonstatus'}},$key);
                       }
                   } elsif ($key eq 'weights') {
                       if ((ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') &&
                           (ref($contacts_hash{contacts}{lonstatus}{$key}) eq 'HASH')) {
                           if (ref($currsetting{'lonstatus'}{$key}) eq 'HASH') {
                               foreach my $type ('E','W','N','U') {
                                   unless ($contacts_hash{contacts}{lonstatus}{$key}{$type} eq
                                           $currsetting{'lonstatus'}{$key}{$type}) {
                                       push(@{$changes{'lonstatus'}},$key);
                                       last;
                                   }
                               }
                           } else {
                               foreach my $type ('E','W','N','U') {
                                   if ($contacts_hash{contacts}{lonstatus}{$key}{$type} ne '') {
                                       push(@{$changes{'lonstatus'}},$key);
                                       last;
                                   }
                               }
                           }
                       } elsif (ref($currsetting{'lonstatus'}{$key}) eq 'HASH') {
                           foreach my $type ('E','W','N','U') {
                               if ($currsetting{'lonstatus'}{$key}{$type} ne '') {
                                   push(@{$changes{'lonstatus'}},$key);
                                   last;
                               }
                           }
                       }
                   } elsif (($key eq 'threshold') || ($key eq 'sysmail')) {
                       if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') {
                           if ($currsetting{'lonstatus'}{$key} =~ /^\d+$/) {
                               if ($currsetting{'lonstatus'}{$key} != $contacts_hash{contacts}{lonstatus}{$key}) {
                                   push(@{$changes{'lonstatus'}},$key);
                               }
                           } elsif ($contacts_hash{contacts}{lonstatus}{$key} =~ /^\d+$/) {
                               push(@{$changes{'lonstatus'}},$key);
                           }
                       } elsif ($currsetting{'lonstatus'}{$key} =~ /^\d+$/) {
                           push(@{$changes{'lonstatus'}},$key);
                       }
                   }
               }
           } else {
               if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') {
                   foreach my $key ('excluded','weights','threshold','sysmail') {
                       if (exists($contacts_hash{contacts}{lonstatus}{$key})) {
                           push(@{$changes{'lonstatus'}},$key);
                       }
                 }                  }
             }              }
         }          }
Line 10693  sub modify_contacts { Line 17648  sub modify_contacts {
         $default{'lonstatusmail'} = 'adminemail';          $default{'lonstatusmail'} = 'adminemail';
         $default{'requestsmail'} = 'adminemail';          $default{'requestsmail'} = 'adminemail';
         $default{'updatesmail'} = 'adminemail';          $default{'updatesmail'} = 'adminemail';
           $default{'hostipmail'} = 'adminemail';
         foreach my $item (@contacts) {          foreach my $item (@contacts) {
            if ($to{$item} ne $default{$item}) {             if ($to{$item} ne $default{$item}) {
                $changes{$item} = 1;                 $changes{$item} = 1;
Line 10726  sub modify_contacts { Line 17682  sub modify_contacts {
                 }                  }
             }              }
         }          }
           if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') {
               foreach my $key ('excluded','weights','threshold','sysmail') {
                   if (exists($contacts_hash{contacts}{lonstatus}{$key})) {
                       push(@{$changes{'lonstatus'}},$key);
                   }
               }
           }
     }      }
     foreach my $item (@toggles) {      foreach my $item (@toggles) {
         if (($env{'form.'.$item} == 1) && ($currsetting{$item} == 0)) {          if (($env{'form.'.$item} == 1) && ($currsetting{$item} == 0)) {
Line 10782  sub modify_contacts { Line 17745  sub modify_contacts {
                             $resulttext .= $bcctext.': <span class="LC_cusr_emph">'.$bcc{$type}.'</span>';                              $resulttext .= $bcctext.': <span class="LC_cusr_emph">'.$bcc{$type}.'</span>';
                         } elsif (!@text) {                          } elsif (!@text) {
                             $resulttext .= &mt('No one');                              $resulttext .= &mt('No one');
                         }                          }   
                         if ($includestr{$type} ne '') {                          if ($includestr{$type} ne '') {
                             if ($includeloc{$type} eq 'b') {                              if ($includeloc{$type} eq 'b') {
                                 $resulttext .= '<br />'.&mt('Text automatically added to e-mail body:').' '.$includestr{$type};                                  $resulttext .= '<br />'.&mt('Text automatically added to e-mail body:').' '.$includestr{$type};
Line 10806  sub modify_contacts { Line 17769  sub modify_contacts {
                             if (ref($newsetting{'override_'.$type}) eq 'HASH') {                              if (ref($newsetting{'override_'.$type}) eq 'HASH') {
                                 my @text;                                  my @text;
                                 foreach my $item (@contacts) {                                  foreach my $item (@contacts) {
                                     if ($newsetting{'override_'.$type}{$item}) {                                      if ($newsetting{'override_'.$type}{$item}) { 
                                         push(@text,$short_titles->{$item});                                          push(@text,$short_titles->{$item});
                                     }                                      }
                                 }                                  }
                                 if ($newsetting{'override_'.$type}{'others'} ne '') {                                  if ($newsetting{'override_'.$type}{'others'} ne '') {
                                     push(@text,$newsetting{'override_'.$type}{'others'});                                      push(@text,$newsetting{'override_'.$type}{'others'});
                                 }                                  }
     
                                 if (@text) {                                  if (@text) {
                                     $resulttext .= &mt('Helpdesk e-mail sent to: [_1]',                                      $resulttext .= &mt('Helpdesk e-mail sent to: [_1]',
                                                        '<span class="LC_cusr_emph">'.join(', ',@text).'</span>');                                                         '<span class="LC_cusr_emph">'.join(', ',@text).'</span>');
Line 10851  sub modify_contacts { Line 17814  sub modify_contacts {
                 }                  }
             }              }
             my @offon = ('off','on');              my @offon = ('off','on');
               my $corelink = &core_link_msu();
             if ($changes{'reporterrors'}) {              if ($changes{'reporterrors'}) {
                 $resulttext .= '<li>'.                  $resulttext .= '<li>'.
                                &mt('E-mail error reports to [_1] set to "'.                                 &mt('E-mail error reports to [_1] set to "'.
                                    $offon[$env{'form.reporterrors'}].'".',                                     $offon[$env{'form.reporterrors'}].'".',
                                    &Apache::loncommon::modal_link('http://loncapa.org/core.html',                                     $corelink).
                                        &mt('LON-CAPA core group - MSU'),600,500)).  
                                '</li>';                                 '</li>';
             }              }
             if ($changes{'reportupdates'}) {              if ($changes{'reportupdates'}) {
                 $resulttext .= '<li>'.                  $resulttext .= '<li>'.
                                 &mt('E-mail record of completed LON-CAPA updates to [_1] set to "'.                                  &mt('E-mail record of completed LON-CAPA updates to [_1] set to "'.
                                     $offon[$env{'form.reportupdates'}].'".',                                      $offon[$env{'form.reportupdates'}].'".',
                                     &Apache::loncommon::modal_link('http://loncapa.org/core.html',                                      $corelink).
                                         &mt('LON-CAPA core group - MSU'),600,500)).  
                                 '</li>';                                  '</li>';
             }              }
               if ($changes{'reportstatus'}) {
                   $resulttext .= '<li>'.
                                   &mt('E-mail status if errors above threshold to [_1] set to "'.
                                       $offon[$env{'form.reportstatus'}].'".',
                                       $corelink).
                                   '</li>';
               }
               if (ref($changes{'lonstatus'}) eq 'ARRAY') {
                   $resulttext .= '<li>'.
                                  &mt('Nightly status check e-mail settings').':<ul>';
                   my (%defval,%use_def,%shown);
                   $defval{'threshold'} = $lonstatus_defs->{'threshold'};
                   $defval{'sysmail'} = $lonstatus_defs->{'sysmail'};
                   $defval{'weights'} =
                       join(', ',map { $lonstatus_names->{$_}.'='.$lonstatus_defs->{$_}; } ('E','W','N','U'));
                   $defval{'excluded'} = &mt('None');
                   if (ref($contacts_hash{'contacts'}{'lonstatus'}) eq 'HASH') {
                       foreach my $item ('threshold','sysmail','weights','excluded') {
                           if (exists($contacts_hash{'contacts'}{'lonstatus'}{$item})) {
                               if (($item eq 'threshold') || ($item eq 'sysmail')) {
                                   $shown{$item} = $contacts_hash{'contacts'}{'lonstatus'}{$item};
                               } elsif ($item eq 'weights') {
                                   if (ref($contacts_hash{'contacts'}{'lonstatus'}{$item}) eq 'HASH') {
                                       foreach my $type ('E','W','N','U') {
                                           $shown{$item} .= $lonstatus_names->{$type}.'=';
                                           if (exists($contacts_hash{'contacts'}{'lonstatus'}{$item}{$type})) {
                                               $shown{$item} .= $contacts_hash{'contacts'}{'lonstatus'}{$item}{$type};
                                           } else {
                                               $shown{$item} .= $lonstatus_defs->{$type};
                                           }
                                           $shown{$item} .= ', ';
                                       }
                                       $shown{$item} =~ s/, $//;
                                   } else {
                                       $shown{$item} = $defval{$item};
                                   }
                               } elsif ($item eq 'excluded') {
                                   if (ref($contacts_hash{'contacts'}{'lonstatus'}{$item}) eq 'ARRAY') {
                                       $shown{$item} = join(', ',@{$contacts_hash{'contacts'}{'lonstatus'}{$item}});
                                   } else {
                                       $shown{$item} = $defval{$item};
                                   }
                               }
                           } else {
                               $shown{$item} = $defval{$item};
                           }
                       }
                   } else {
                       foreach my $item ('threshold','weights','excluded','sysmail') {
                           $shown{$item} = $defval{$item};
                       }
                   }
                   foreach my $item ('threshold','weights','excluded','sysmail') {
                       $resulttext .= '<li>'.&mt($titles->{'error'.$item}.' -- [_1]',
                                             $shown{$item}).'</li>';
                   }
                   $resulttext .= '</ul></li>';
               }
             if ((ref($changes{'helpform'}) eq 'ARRAY') && (ref($fields) eq 'ARRAY')) {              if ((ref($changes{'helpform'}) eq 'ARRAY') && (ref($fields) eq 'ARRAY')) {
                 my (@optional,@required,@unused,$maxsizechg);                  my (@optional,@required,@unused,$maxsizechg);
                 foreach my $field (@{$changes{'helpform'}}) {                  foreach my $field (@{$changes{'helpform'}}) {
Line 10918  sub modify_contacts { Line 17938  sub modify_contacts {
     return $resulttext;      return $resulttext;
 }  }
   
   sub modify_privacy {
       my ($dom,%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',
       );
       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 %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','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 '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>';
                   }
               }
           } 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 {
       my ($r,$dom,$confname,$lastactref,%domconfig) = @_;
       my ($resulttext,%current,%changes,%newvalues,@oktypes,$errors,
           $updatedefaults,$updateconf);
       my $customfn = 'resetpw.html';
       if (ref($domconfig{'passwords'}) eq 'HASH') {
           %current = %{$domconfig{'passwords'}};
       }
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);
       if (ref($types) eq 'ARRAY') {
           @oktypes = @{$types};
       }
       push(@oktypes,'default');
   
       my %titles = &Apache::lonlocal::texthash (
           intauth_cost   => 'Encryption cost for bcrypt (positive integer)',
           intauth_check  => 'Check bcrypt cost if authenticated',
           intauth_switch => 'Existing crypt-based switched to bcrypt on authentication',
           permanent      => 'Permanent e-mail address',
           critical       => 'Critical notification address',
           notify         => 'Notification address',
           min            => 'Minimum password length',
           max            => 'Maximum password length',
           chars          => 'Required characters',
           expire         => 'Password expiration (days)',
           numsaved       => 'Number of previous passwords to save',
           reset          => 'Resetting Forgotten Password',
           intauth        => 'Encryption of Stored Passwords (Internal Auth)',
           rules          => 'Rules for LON-CAPA Passwords',
           crsownerchg    => 'Course Owner Changing Student Passwords',
           username       => 'Username',
           email          => 'E-mail address',
       );
   
   #
   # Retrieve current domain configuration for internal authentication from $domconfig{'defaults'}.
   #
       my (%curr_defaults,%save_defaults);
       if (ref($domconfig{'defaults'}) eq 'HASH') {
           foreach my $key (keys(%{$domconfig{'defaults'}})) {
               if ($key =~ /^intauth_(cost|check|switch)$/) {
                   $curr_defaults{$key} = $domconfig{'defaults'}{$key};
               } else {
                   $save_defaults{$key} = $domconfig{'defaults'}{$key};
               }
           }
       }
       my %staticdefaults = (
           'resetlink'      => 2,
           'resetcase'      => \@oktypes,
           'resetprelink'   => 'both',
           'resetemail'     => ['critical','notify','permanent'],
           'intauth_cost'   => 10,
           'intauth_check'  => 0,
           'intauth_switch' => 0,
       );
       $staticdefaults{'min'} = $Apache::lonnet::passwdmin;
       foreach my $type (@oktypes) {
           $staticdefaults{'resetpostlink'}{$type} = ['email','username'];
       }
       my $linklife = $env{'form.passwords_link'};
       $linklife =~ s/^\s+|\s+$//g;
       if (($linklife =~ /^\d+(|\.\d*)$/) && ($linklife > 0)) {
           $newvalues{'resetlink'} = $linklife;
           if ($current{'resetlink'}) {
               if ($current{'resetlink'} ne $linklife) {
                   $changes{'reset'} = 1;
               }
           } elsif (!ref($domconfig{passwords}) eq 'HASH') {
               if ($staticdefaults{'resetlink'} ne $linklife) {
                   $changes{'reset'} = 1;
               }
           }
       } elsif ($current{'resetlink'}) {
           $changes{'reset'} = 1;
       }
       my @casesens;
       my @posscase = &Apache::loncommon::get_env_multiple('form.passwords_case_sensitive');
       foreach my $case (sort(@posscase)) {
           if (grep(/^\Q$case\E$/,@oktypes)) {
               push(@casesens,$case);
           }
       }
       $newvalues{'resetcase'} = \@casesens;
       if (ref($current{'resetcase'}) eq 'ARRAY') {
           my @diffs = &Apache::loncommon::compare_arrays($current{'resetcase'},\@casesens);
           if (@diffs > 0) {
               $changes{'reset'} = 1;
           }
       } elsif (!ref($domconfig{passwords}) eq 'HASH') {
           my @diffs = &Apache::loncommon::compare_arrays($staticdefaults{'resetcase'},\@casesens);
           if (@diffs > 0) {
               $changes{'reset'} = 1;
           }
       }
       if ($env{'form.passwords_prelink'} =~ /^(both|either)$/) {
           $newvalues{'resetprelink'} = $env{'form.passwords_prelink'};
           if (exists($current{'resetprelink'})) {
               if ($current{'resetprelink'} ne $newvalues{'resetprelink'}) {
                   $changes{'reset'} = 1;
               }
           } elsif (!ref($domconfig{passwords}) eq 'HASH') {
               if ($staticdefaults{'resetprelink'} ne $newvalues{'resetprelink'}) {
                   $changes{'reset'} = 1;
               }
           }
       } elsif ($current{'resetprelink'}) {
           $changes{'reset'} = 1;
       }
       foreach my $type (@oktypes) {
           my @possplink = &Apache::loncommon::get_env_multiple('form.passwords_postlink_'.$type);
           my @postlink;
           foreach my $item (sort(@possplink)) {
               if ($item =~ /^(email|username)$/) {
                   push(@postlink,$item);
               }
           }
           $newvalues{'resetpostlink'}{$type} = \@postlink;
           unless ($changes{'reset'}) {
               if (ref($current{'resetpostlink'}) eq 'HASH') {
                   if (ref($current{'resetpostlink'}{$type}) eq 'ARRAY') {
                       my @diffs = &Apache::loncommon::compare_arrays($current{'resetpostlink'}{$type},\@postlink);
                       if (@diffs > 0) {
                           $changes{'reset'} = 1;
                       }
                   } else {
                       $changes{'reset'} = 1;
                   }
               } elsif (!ref($domconfig{passwords}) eq 'HASH') {
                   my @diffs = &Apache::loncommon::compare_arrays($staticdefaults{'resetpostlink'}{$type},\@postlink);
                   if (@diffs > 0) {
                       $changes{'reset'} = 1;
                   }
               }
           }
       }
       my @possemailsrc = &Apache::loncommon::get_env_multiple('form.passwords_emailsrc');
       my @resetemail;
       foreach my $item (sort(@possemailsrc)) {
           if ($item =~ /^(permanent|critical|notify)$/) {
               push(@resetemail,$item);
           }
       }
       $newvalues{'resetemail'} = \@resetemail;
       unless ($changes{'reset'}) {
           if (ref($current{'resetemail'}) eq 'ARRAY') {
               my @diffs = &Apache::loncommon::compare_arrays($current{'resetemail'},\@resetemail);
               if (@diffs > 0) {
                   $changes{'reset'} = 1;
               }
           } elsif (!ref($domconfig{passwords}) eq 'HASH') {
               my @diffs = &Apache::loncommon::compare_arrays($staticdefaults{'resetemail'},\@resetemail);
               if (@diffs > 0) {
                   $changes{'reset'} = 1;
               }
           }
       }
       if ($env{'form.passwords_stdtext'} == 0) {
           $newvalues{'resetremove'} = 1;
           unless ($current{'resetremove'}) {
               $changes{'reset'} = 1;
           }
       } elsif ($current{'resetremove'}) {
           $changes{'reset'} = 1;
       }
       if ($env{'form.passwords_customfile.filename'} ne '') {
           my $servadm = $r->dir_config('lonAdmEMail');
           my ($configuserok,$author_ok,$switchserver) =
               &config_check($dom,$confname,$servadm);
           my $error;
           if ($configuserok eq 'ok') {
               if ($switchserver) {
                   $error = &mt("Upload of file containing domain-specific text is not permitted to this server: [_1]",$switchserver);
               } else {
                   if ($author_ok eq 'ok') {
                       my ($result,$customurl) =
                           &publishlogo($r,'upload','passwords_customfile',$dom,
                                        $confname,'customtext/resetpw','','',$customfn);
                       if ($result eq 'ok') {
                           $newvalues{'resetcustom'} = $customurl;
                           $changes{'reset'} = 1;
                       } else {
                           $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$customfn,$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].",$customfn,$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].",$customfn,$confname,$dom,$configuserok);
           }
           if ($error) {
               &Apache::lonnet::logthis($error);
               $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
           }
       } elsif ($current{'resetcustom'}) {
           if ($env{'form.passwords_custom_del'}) {
               $changes{'reset'} = 1;
           } else {
               $newvalues{'resetcustom'} = $current{'resetcustom'};
           }
       }
       $env{'form.intauth_cost'} =~ s/^\s+|\s+$//g;
       if (($env{'form.intauth_cost'} ne '') && ($env{'form.intauth_cost'} =~ /^\d+$/)) {
           $save_defaults{'intauth_cost'} = $env{'form.intauth_cost'};
           if ($save_defaults{'intauth_cost'} ne $curr_defaults{'intauth_cost'}) {
               $changes{'intauth'} = 1;
           }
       } else {
           $save_defaults{'intauth_cost'} = $curr_defaults{'intauth_cost'};
       }
       if ($env{'form.intauth_check'} =~ /^(0|1|2)$/) {
           $save_defaults{'intauth_check'} = $env{'form.intauth_check'};
           if ($save_defaults{'intauth_check'} ne $curr_defaults{'intauth_check'}) {
               $changes{'intauth'} = 1;
           }
       } else {
           $save_defaults{'intauth_check'} = $curr_defaults{'intauth_check'};
       }
       if ($env{'form.intauth_switch'} =~ /^(0|1|2)$/) {
           $save_defaults{'intauth_switch'} = $env{'form.intauth_switch'};
           if ($save_defaults{'intauth_switch'} ne $curr_defaults{'intauth_switch'}) {
               $changes{'intauth'} = 1;
           }
       } else {
           $save_defaults{'intauth_check'} = $curr_defaults{'intauth_check'};
       }
       foreach my $item ('cost','check','switch') {
           if ($save_defaults{'intauth_'.$item} ne $domdefaults{'intauth_'.$item}) {
               $domdefaults{'intauth_'.$item} = $save_defaults{'intauth_'.$item};
               $updatedefaults = 1;
           }
       }
       &password_rule_changes('passwords',\%newvalues,\%current,\%changes);
       my %crsownerchg = (
                           by => [],
                           for => [],
                         );
       foreach my $item ('by','for') {
           my @posstypes = &Apache::loncommon::get_env_multiple('form.passwords_crsowner_'.$item);
           foreach my $type (sort(@posstypes)) {
               if (grep(/^\Q$type\E$/,@oktypes)) {
                   push(@{$crsownerchg{$item}},$type);
               }
           }
       }
       $newvalues{'crsownerchg'} = \%crsownerchg;
       if (ref($current{'crsownerchg'}) eq 'HASH') {
           foreach my $item ('by','for') {
               if (ref($current{'crsownerchg'}{$item}) eq 'ARRAY') {
                   my @diffs = &Apache::loncommon::compare_arrays($current{'crsownerchg'}{$item},$crsownerchg{$item});
                   if (@diffs > 0) {
                       $changes{'crsownerchg'} = 1;
                       last;
                   }
               }
           }
       } elsif (!(ref($domconfig{passwords}) eq 'HASH')) {
           foreach my $item ('by','for') {
               if (@{$crsownerchg{$item}} > 0) {
                   $changes{'crsownerchg'} = 1;
                   last;
               }
           }
       }
   
       my %confighash = (
                           defaults  => \%save_defaults,
                           passwords => \%newvalues,
                        );
       &process_captcha('passwords',\%changes,$confighash{'passwords'},$domconfig{'passwords'});
   
       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 ('reset','intauth','rules','crsownerchg') {
                   if ($changes{$key}) {
                       unless ($key eq 'intauth') {
                           $updateconf = 1;
                       }
                       $resulttext .= '<li>'.$titles{$key}.':<ul>';
                       if ($key eq 'reset') {
                           if ($confighash{'passwords'}{'captcha'} eq 'original') {
                               $resulttext .= '<li>'.&mt('CAPTCHA validation set to use: original CAPTCHA').'</li>';
                           } elsif ($confighash{'passwords'}{'captcha'} eq 'recaptcha') {
                               $resulttext .= '<li>'.&mt('CAPTCHA validation set to use: reCAPTCHA').' '.
                                              &mt('version: [_1]',$confighash{'passwords'}{'recaptchaversion'}).'<br />';
                               if (ref($confighash{'passwords'}{'recaptchakeys'}) eq 'HASH') {
                                   $resulttext .= &mt('Public key: [_1]',$confighash{'passwords'}{'recaptchakeys'}{'public'}).'</br>'.
                                                  &mt('Private key: [_1]',$confighash{'passwords'}{'recaptchakeys'}{'private'}).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('No CAPTCHA validation').'</li>';
                           }
                           if ($confighash{'passwords'}{'resetlink'}) {
                               $resulttext .= '<li>'.&mt('Reset link expiration set to [quant,_1,hour]',$confighash{'passwords'}{'resetlink'}).'</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('No reset link expiration set.').' '.
                                                     &mt('Will default to 2 hours').'</li>';
                           }
                           if (ref($confighash{'passwords'}{'resetcase'}) eq 'ARRAY') {
                               if (@{$confighash{'passwords'}{'resetcase'}} == 0) {
                                   $resulttext .= '<li>'.&mt('User input for username and/or e-mail address not case sensitive for "Forgot Password" web form').'</li>';
                               } else {
                                   my $casesens;
                                   foreach my $type (@{$confighash{'passwords'}{'resetcase'}}) {
                                       if ($type eq 'default') {
                                           $casesens .= $othertitle.', ';
                                       } elsif ($usertypes->{$type} ne '') {
                                           $casesens .= $usertypes->{$type}.', ';
                                       }
                                   }
                                   $casesens =~ s/\Q, \E$//;
                                   $resulttext .= '<li>'.&mt('"Forgot Password" web form input for username and/or e-mail address is case-sensitive for: [_1]',$casesens).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('Case-sensitivity not set for "Forgot Password" web form').' '.&mt('Will default to case-sensitive for username and/or e-mail address for all').'</li>';
                           }
                           if ($confighash{'passwords'}{'resetprelink'} eq 'either') {
                               $resulttext .= '<li>'.&mt('Users can enter either a username or an e-mail address in "Forgot Password" web form').'</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('Users can enter both a username and an e-mail address in "Forgot Password" web form').'</li>';
                           }
                           if (ref($confighash{'passwords'}{'resetpostlink'}) eq 'HASH') {
                               my $output;
                               if (ref($types) eq 'ARRAY') {
                                   foreach my $type (@{$types}) {
                                       if (ref($confighash{'passwords'}{'resetpostlink'}{$type}) eq 'ARRAY') {
                                           if (@{$confighash{'passwords'}{'resetpostlink'}{$type}} == 0) {
                                               $output .= $usertypes->{$type}.' -- '.&mt('none');
                                           } else {
                                               $output .= $usertypes->{$type}.' -- '.
                                                          join(', ',map { $titles{$_}; } (@{$confighash{'passwords'}{'resetpostlink'}{$type}})).'; ';
                                           }
                                       }
                                   }
                               }
                               if (ref($confighash{'passwords'}{'resetpostlink'}{'default'}) eq 'ARRAY') {
                                   if (@{$confighash{'passwords'}{'resetpostlink'}{'default'}} == 0) {
                                       $output .= $othertitle.' -- '.&mt('none');
                                   } else {
                                       $output .= $othertitle.' -- '.
                                                  join(', ',map { $titles{$_}; } (@{$confighash{'passwords'}{'resetpostlink'}{'default'}}));
                                   }
                               }
                               if ($output) {
                                   $resulttext .= '<li>'.&mt('Information required for new password form (by user type) set to: [_1]',$output).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('Information required for new password form not set.').' '.&mt('Will default to requiring both the username and an e-mail address').'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('Information required for new password form not set.').' '.&mt('Will default to requiring both the username and an e-mail address').'</li>';
                           }
                           if (ref($confighash{'passwords'}{'resetemail'}) eq 'ARRAY') {
                               if (@{$confighash{'passwords'}{'resetemail'}} > 0) {
                                   $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$confighash{'passwords'}{'resetemail'}})).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('E-mail address(es) in LON-CAPA used for verification will include: [_1]',join(', ',map { $titles{$_}; } @{$staticdefaults{'resetemail'}})).'</li>';
                           }
                           if ($confighash{'passwords'}{'resetremove'}) {
                               $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" web form not shown').'</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" web form is shown').'</li>';
                           }
                           if ($confighash{'passwords'}{'resetcustom'}) {
                               my $customlink = &Apache::loncommon::modal_link($confighash{'passwords'}{'resetcustom'},
                                                                               &mt('custom text'),600,500,undef,undef,
                                                                               undef,undef,'background-color:#ffffff');
                               $resulttext .= '<li>'.&mt('Preamble to "Forgot Password" form includes: [_1]',$customlink).'</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('No custom text included in preamble to "Forgot Password" form').'</li>';
                           }
                       } elsif ($key eq 'intauth') {
                           foreach my $item ('cost','switch','check') {
                               my $value = $save_defaults{$key.'_'.$item};
                               if ($item eq 'switch') {
                                   my %optiondesc = &Apache::lonlocal::texthash (
                                                        0 => 'No',
                                                        1 => 'Yes',
                                                        2 => 'Yes, and copy existing passwd file to passwd.bak file',
                                                    );
                                   if ($value =~ /^(0|1|2)$/) {
                                       $value = $optiondesc{$value};
                                   } else {
                                       $value = &mt('none -- defaults to No');
                                   }
                               } elsif ($item eq 'check') {
                                   my %optiondesc = &Apache::lonlocal::texthash (
                                                        0 => 'No',
                                                        1 => 'Yes, allow login then update passwd file using default cost (if higher)',
                                                        2 => 'Yes, disallow login if stored cost is less than domain default',
                                                    );
                                   if ($value =~ /^(0|1|2)$/) {
                                       $value = $optiondesc{$value};
                                   } else {
                                       $value = &mt('none -- defaults to No');
                                   }
                               }
                               $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$titles{$key.'_'.$item},$value).'</li>';
                           }
                       } elsif ($key eq 'rules') {
                           foreach my $rule ('min','max','expire','numsaved') {
                               if ($confighash{'passwords'}{$rule} eq '') {
                                   if ($rule eq 'min') {
                                       $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule});
                                                      ' '.&mt('Default of [_1] will be used',
                                                              $Apache::lonnet::passwdmin).'</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('[_1] set to none',$titles{$rule}).'</li>';
                                   }
                               } else {
                                   $resulttext .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$confighash{'passwords'}{$rule}).'</li>';
                               }
                           }
                           if (ref($confighash{'passwords'}{'chars'}) eq 'ARRAY') {
                               if (@{$confighash{'passwords'}{'chars'}} > 0) {
                                   my %rulenames = &Apache::lonlocal::texthash(
                                                        uc => 'At least one upper case letter',
                                                        lc => 'At least one lower case letter',
                                                        num => 'At least one number',
                                                        spec => 'At least one non-alphanumeric',
                                                      );
                                   my $needed = '<ul><li>'.
                                                join('</li><li>',map {$rulenames{$_} } @{$confighash{'passwords'}{'chars'}}).
                                                '</li></ul>'; 
                                   $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>';
                               } else {
                                   $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>';
                           }
                       } elsif ($key eq 'crsownerchg') {
                           if (ref($confighash{'passwords'}{'crsownerchg'}) eq 'HASH') {
                               if ((@{$confighash{'passwords'}{'crsownerchg'}{'by'}} == 0) ||
                                   (@{$confighash{'passwords'}{'crsownerchg'}{'for'}} == 0)) {
                                   $resulttext .= '<li>'.&mt('Course owner may not change student passwords.').'</li>';
                               } else {
                                   my %crsownerstr;
                                   foreach my $item ('by','for') {
                                       if (ref($confighash{'passwords'}{'crsownerchg'}{$item}) eq 'ARRAY') {
                                           foreach my $type (@{$confighash{'passwords'}{'crsownerchg'}{$item}}) {
                                               if ($type eq 'default') {
                                                   $crsownerstr{$item} .= $othertitle.', ';
                                               } elsif ($usertypes->{$type} ne '') {
                                                   $crsownerstr{$item} .= $usertypes->{$type}.', ';
                                               }
                                           }
                                           $crsownerstr{$item} =~ s/\Q, \E$//;
                                       }
                                   }
                                   $resulttext .= '<li>'.&mt('Course owner (with status: [_1]) may change passwords for students (with status: [_2]).',
                                              $crsownerstr{'by'},$crsownerstr{'for'}).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('Course owner may not change student passwords.').'</li>';
                           }
                       }
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
           } else {
               $resulttext = &mt('No changes made to password settings');
           }
           my $cachetime = 24*60*60;
           if ($updatedefaults) {
               &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'domdefaults'} = 1;
               }
           }
           if ($updateconf) {
               &Apache::lonnet::do_cache_new('passwdconf',$dom,$confighash{'passwords'},$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'passwdconf'} = 1;
               }
           }
       } else {
           $resulttext = '<span class="LC_error">'.
               &mt('An error occurred: [_1]',$putresult).'</span>';
       }
       if ($errors) {
           $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul></p>';
       }
       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 'secrets') {
           @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 10927  sub modify_usercreation { Line 18735  sub modify_usercreation {
             if ($key eq 'cancreate') {              if ($key eq 'cancreate') {
                 if (ref($domconfig{'usercreation'}{$key}) eq 'HASH') {                  if (ref($domconfig{'usercreation'}{$key}) eq 'HASH') {
                     foreach my $item (keys(%{$domconfig{'usercreation'}{$key}})) {                      foreach my $item (keys(%{$domconfig{'usercreation'}{$key}})) {
                         if (($item eq 'selfcreate') || ($item eq 'statustocreate') ||                          if (($item eq 'requestcrs') || ($item eq 'course') || ($item eq 'author')) {
                             ($item eq 'captcha') || ($item eq 'recaptchakeys') ||  
                             ($item eq 'recaptchaversion')) {  
                             $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};  
                         } else {  
                             $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};                              $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};
                           } else {
                               $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};
                         }                          }
                     }                      }
                 }                  }
Line 11004  sub modify_usercreation { Line 18810  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 11135  sub modify_usercreation { Line 18941  sub modify_usercreation {
 }  }
   
 sub modify_selfcreation {  sub modify_selfcreation {
     my ($dom,%domconfig) = @_;      my ($dom,$lastactref,%domconfig) = @_;
     my ($resulttext,$warningmsg,%curr_usercreation,%curr_usermodify,%changes,%cancreate);      my ($resulttext,$warningmsg,%curr_usercreation,%curr_usermodify,%curr_inststatus,%changes,%cancreate);
     my (%save_usercreate,%save_usermodify);      my (%save_usercreate,%save_usermodify,%save_inststatus,@types,%usertypes);
     my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom);      my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
     if (ref($types) eq 'ARRAY') {      my ($othertitle,$usertypesref,$typesref) = &Apache::loncommon::sorted_inst_types($dom);
         $usertypes->{'default'} = $othertitle;      if (ref($typesref) eq 'ARRAY') {
         push(@{$types},'default');          @types = @{$typesref};
     }      }
       if (ref($usertypesref) eq 'HASH') {
           %usertypes = %{$usertypesref};
       }
       $usertypes{'default'} = $othertitle;
 #  #
 # Retrieve current domain configuration for self-creation of usernames from $domconfig{'usercreation'}.  # Retrieve current domain configuration for self-creation of usernames from $domconfig{'usercreation'}.
 #  #
Line 11152  sub modify_selfcreation { Line 18962  sub modify_selfcreation {
                 if (ref($domconfig{'usercreation'}{$key}) eq 'HASH') {                  if (ref($domconfig{'usercreation'}{$key}) eq 'HASH') {
                     foreach my $item (keys(%{$domconfig{'usercreation'}{$key}})) {                      foreach my $item (keys(%{$domconfig{'usercreation'}{$key}})) {
                         if (($item eq 'selfcreate') || ($item eq 'statustocreate') ||                          if (($item eq 'selfcreate') || ($item eq 'statustocreate') ||
                             ($item eq 'captcha') || ($item eq 'recaptchakeys') ||                               ($item eq 'captcha') || ($item eq 'recaptchakeys') ||
                             ($item eq 'recaptchaversion') ||                              ($item eq 'recaptchaversion') || ($item eq 'notify') ||
                             ($item eq 'emailusername') || ($item eq 'notify') ||                              ($item eq 'emailusername') || ($item eq 'shibenv') ||
                             ($item eq 'selfcreateprocessing') || ($item eq 'shibenv')) {                              ($item eq 'selfcreateprocessing') || ($item eq 'emailverified') ||
                               ($item eq 'emailoptions') || ($item eq 'emaildomain')) {
                             $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};                              $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};
                         } else {                          } else {
                             $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};                              $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item};
Line 11181  sub modify_selfcreation { Line 18992  sub modify_selfcreation {
             }              }
         }          }
     }      }
   #
   # Retrieve current domain configuration for institutional status types from $domconfig{'inststatus'}.
   #
       if (ref($domconfig{'inststatus'}) eq 'HASH') {
           foreach my $key (keys(%{$domconfig{'inststatus'}})) {
               if ($key eq 'inststatusguest') {
                   $curr_inststatus{$key} = $domconfig{'inststatus'}{$key};
               } else {
                   $save_inststatus{$key} = $domconfig{'inststatus'}{$key};
               }
           }
       }
   
     my @contexts = ('selfcreate');      my @contexts = ('selfcreate');
     @{$cancreate{'selfcreate'}} = ();      @{$cancreate{'selfcreate'}} = ();
     %{$cancreate{'emailusername'}} = ();      %{$cancreate{'emailusername'}} = ();
     @{$cancreate{'statustocreate'}} = ();      if (@types) {
           @{$cancreate{'statustocreate'}} = ();
       }
     %{$cancreate{'selfcreateprocessing'}} = ();      %{$cancreate{'selfcreateprocessing'}} = ();
     %{$cancreate{'shibenv'}} = ();      %{$cancreate{'shibenv'}} = ();
       %{$cancreate{'emailverified'}} = ();
       %{$cancreate{'emailoptions'}} = ();
       %{$cancreate{'emaildomain'}} = ();
     my %selfcreatetypes = (      my %selfcreatetypes = (
                              sso   => 'users authenticated by institutional single sign on',                               sso   => 'users authenticated by institutional single sign on',
                              login => 'users authenticated by institutional log-in',                               login => 'users authenticated by institutional log-in',
                              email => 'users who provide a valid e-mail address for use as username',                               email => 'users verified by e-mail',
                           );                            );
 #  #
 # 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 @statuses;      my (@statuses,%email_rule);
     if (ref($domconfig{'inststatus'}) eq 'HASH') {  
         if (ref($domconfig{'inststatus'}{'inststatusguest'}) eq 'ARRAY') {  
             @statuses = @{$domconfig{'inststatus'}{'inststatusguest'}};  
         }  
     }  
     push(@statuses,'default');  
   
     foreach my $item ('login','sso','email') {      foreach my $item ('login','sso','email') {
         if ($item eq 'email') {          if ($item eq 'email') {
             if ($env{'form.cancreate_email'}) {              if ($env{'form.cancreate_email'}) {
                 push(@{$cancreate{'selfcreate'}},'email');                  if (@types) {
                 push(@contexts,'selfcreateprocessing');                      my @poss_statuses = &Apache::loncommon::get_env_multiple('form.selfassign');
                 foreach my $type (@statuses) {                      foreach my $status (@poss_statuses) {
                     if ($type eq 'default') {                          if (grep(/^\Q$status\E$/,(@types,'default'))) {
                         $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess'};                              push(@statuses,$status);
                     } else {                           }
                         $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess_'.$type};                      }
                       $save_inststatus{'inststatusguest'} = \@statuses;
                   } else {
                       push(@statuses,'default');
                   }
                   if (@statuses) {
                       my %curr_rule;
                       if (ref($curr_usercreation{'email_rule'}) eq 'ARRAY') {
                           foreach my $type (@statuses) {
                               $curr_rule{$type} = $curr_usercreation{'email_rule'};
                           }
                       } elsif (ref($curr_usercreation{'email_rule'}) eq 'HASH') {
                           foreach my $type (@statuses) {
                               $curr_rule{$type} = $curr_usercreation{'email_rule'}{$type};
                           }
                       }
                       push(@{$cancreate{'selfcreate'}},'email');
                       push(@contexts,('selfcreateprocessing','emailverified','emailoptions'));
                       my %curremaildom;
                       if (ref($curr_usercreation{'cancreate'}{'emaildomain'}) eq 'HASH') {
                           %curremaildom = %{$curr_usercreation{'cancreate'}{'emaildomain'}};
                       }
                       foreach my $type (@statuses) {
                           if ($env{'form.cancreate_emailprocess_'.$type} =~ /^(?:approval|automatic)$/) {
                               $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess_'.$type};
                           }
                           if ($env{'form.cancreate_usernameoptions_'.$type} =~ /^(?:all|first|free)$/) {
                               $cancreate{'emailverified'}{$type} = $env{'form.cancreate_usernameoptions_'.$type};
                           }
                           if ($env{'form.cancreate_emailoptions_'.$type} =~ /^(any|inst|noninst|custom)$/) {
   #
   # Retrieve rules (if any) governing types of e-mail address which may be used to verify a username.
   #
                               my $chosen = $1;
                               if (($chosen eq 'inst') || ($chosen eq 'noninst')) {
                                   my $emaildom;
                                   if ($env{'form.cancreate_emaildomain_'.$chosen.'_'.$type} =~ /^\@[^\@]+$/) {
                                       $emaildom = $env{'form.cancreate_emaildomain_'.$chosen.'_'.$type}; 
                                       $cancreate{'emaildomain'}{$type}{$chosen} = $emaildom;
                                       if (ref($curremaildom{$type}) eq 'HASH') {
                                           if (exists($curremaildom{$type}{$chosen})) {
                                               if ($curremaildom{$type}{$chosen} ne $emaildom) {
                                                   push(@{$changes{'cancreate'}},'emaildomain');
                                               }
                                           } elsif ($emaildom ne '') {
                                               push(@{$changes{'cancreate'}},'emaildomain');
                                           }
                                       } elsif ($emaildom ne '') {
                                           push(@{$changes{'cancreate'}},'emaildomain');
                                       } 
                                   }
                                   $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type};
                               } elsif ($chosen eq 'custom') {
                                   my @possemail_rules = &Apache::loncommon::get_env_multiple('form.email_rule_'.$type);
                                   $email_rule{$type} = [];
                                   if (ref($emailrules) eq 'HASH') {
                                       foreach my $rule (@possemail_rules) {
                                           if (exists($emailrules->{$rule})) {
                                               push(@{$email_rule{$type}},$rule);
                                           }
                                       }
                                   }
                                   if (@{$email_rule{$type}}) {
                                       $cancreate{'emailoptions'}{$type} = 'custom';
                                       if (ref($curr_rule{$type}) eq 'ARRAY') {
                                           if (@{$curr_rule{$type}} > 0) {
                                               foreach my $rule (@{$curr_rule{$type}}) {
                                                   if (!grep(/^\Q$rule\E$/,@{$email_rule{$type}})) {
                                                       push(@{$changes{'email_rule'}},$type);
                                                   }
                                               }
                                           }
                                           foreach my $type (@{$email_rule{$type}}) {
                                               if (!grep(/^\Q$type\E$/,@{$curr_rule{$type}})) {
                                                   push(@{$changes{'email_rule'}},$type);
                                               }
                                           }
                                       } else {
                                           push(@{$changes{'email_rule'}},$type);
                                       }
                                   }
                               } else {
                                   $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type};
                               }
                           }
                       }
                       if (@types) {
                           if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') {
                               my @changed = &Apache::loncommon::compare_arrays(\@statuses,$curr_inststatus{'inststatusguest'});
                               if (@changed) {
                                   push(@{$changes{'inststatus'}},'inststatusguest');
                               }
                           } else {
                               push(@{$changes{'inststatus'}},'inststatusguest');
                           }
                       }
                   } else {
                       delete($env{'form.cancreate_email'});
                       if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') {
                           if (@{$curr_inststatus{'inststatusguest'}} > 0) {
                               push(@{$changes{'inststatus'}},'inststatusguest');
                           }
                       }
                   }
               } else {
                   $save_inststatus{'inststatusguest'} = [];
                   if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') {
                       if (@{$curr_inststatus{'inststatusguest'}} > 0) {
                           push(@{$changes{'inststatus'}},'inststatusguest');
                     }                      }
                 }                  }
             }              }
Line 11225  sub modify_selfcreation { Line 19155  sub modify_selfcreation {
             }              }
         }          }
     }      }
     my (@email_rule,%userinfo,%savecaptcha);      my (%userinfo,%savecaptcha);
     my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info();      my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info();
 #  #
 # Populate $cancreate{'emailusername'}{$type} hash ref with information fields (if new user will provide data  # Populate $cancreate{'emailusername'}{$type} hash ref with information fields (if new user will provide data
Line 11234  sub modify_selfcreation { Line 19164  sub modify_selfcreation {
   
     if ($env{'form.cancreate_email'}) {      if ($env{'form.cancreate_email'}) {
         push(@contexts,'emailusername');          push(@contexts,'emailusername');
         if (ref($types) eq 'ARRAY') {          if (@statuses) {
             foreach my $type (@{$types}) {              foreach my $type (@statuses) {
                 if (ref($infofields) eq 'ARRAY') {                  if (ref($infofields) eq 'ARRAY') {
                     foreach my $field (@{$infofields}) {                      foreach my $field (@{$infofields}) {
                         if ($env{'form.canmodify_emailusername_'.$type.'_'.$field} =~ /^(required|optional)$/) {                          if ($env{'form.canmodify_emailusername_'.$type.'_'.$field} =~ /^(required|optional)$/) {
Line 11247  sub modify_selfcreation { Line 19177  sub modify_selfcreation {
         }          }
 #  #
 # Populate $cancreate{'notify'} hash ref with names of Domain Coordinators who are to be notified of  # Populate $cancreate{'notify'} hash ref with names of Domain Coordinators who are to be notified of
 # queued requests for self-creation of account using e-mail address as username  # queued requests for self-creation of account verified by e-mail.
 #  #
   
         my @approvalnotify = &Apache::loncommon::get_env_multiple('form.selfcreationnotifyapproval');          my @approvalnotify = &Apache::loncommon::get_env_multiple('form.selfcreationnotifyapproval');
Line 11267  sub modify_selfcreation { Line 19197  sub modify_selfcreation {
             push(@{$changes{'cancreate'}},'notify');              push(@{$changes{'cancreate'}},'notify');
         }          }
   
 #  
 # Retrieve rules (if any) governing types of e-mail address which may be used as a username  
 #  
         @email_rule = &Apache::loncommon::get_env_multiple('form.email_rule');  
         &process_captcha('cancreate',\%changes,\%savecaptcha,$curr_usercreation{'cancreate'});          &process_captcha('cancreate',\%changes,\%savecaptcha,$curr_usercreation{'cancreate'});
         if (ref($curr_usercreation{'email_rule'}) eq 'ARRAY') {  
             if (@{$curr_usercreation{'email_rule'}} > 0) {  
                 foreach my $type (@{$curr_usercreation{'email_rule'}}) {  
                     if (!grep(/^\Q$type\E$/,@email_rule)) {  
                         push(@{$changes{'email_rule'}},$type);  
                     }  
                 }  
             }  
             if (@email_rule > 0) {  
                 foreach my $type (@email_rule) {  
                     if (!grep(/^\Q$type\E$/,@{$curr_usercreation{'email_rule'}})) {  
                         push(@{$changes{'email_rule'}},$type);  
                     }  
                 }  
             }  
         } elsif (@email_rule > 0) {  
             push(@{$changes{'email_rule'}},@email_rule);  
         }  
     }      }
 #    #  
 # Check if domain default is set appropriately, if self-creation of accounts is to be available for  # Check if domain default is set appropriately, if self-creation of accounts is to be available for
 # institutional log-in.  # institutional log-in.
 #  #
     if (grep(/^login$/,@{$cancreate{'selfcreate'}})) {      if (grep(/^login$/,@{$cancreate{'selfcreate'}})) {
         my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);  
         if (!((($domdefaults{'auth_def'} =~/^krb/) && ($domdefaults{'auth_arg_def'} ne '')) ||           if (!((($domdefaults{'auth_def'} =~/^krb/) && ($domdefaults{'auth_arg_def'} ne '')) || 
                ($domdefaults{'auth_def'} eq 'localauth'))) {                 ($domdefaults{'auth_def'} eq 'localauth'))) {
             $warningmsg = &mt('Although account creation has been set to be available for institutional logins, currently default authentication in this domain has not been set to support this.').' '.              $warningmsg = &mt('Although account creation has been set to be available for institutional logins, currently default authentication in this domain has not been set to support this.').' '.
Line 11315  sub modify_selfcreation { Line 19222  sub modify_selfcreation {
 # which the user may supply, if institutional data is unavailable.  # which the user may supply, if institutional data is unavailable.
 #  #
     if (($env{'form.cancreate_login'}) || ($env{'form.cancreate_sso'})) {      if (($env{'form.cancreate_login'}) || ($env{'form.cancreate_sso'})) {
         if (ref($types) eq 'ARRAY') {          if (@types) {
             if (@{$types} > 1) {              @{$cancreate{'statustocreate'}} = &Apache::loncommon::get_env_multiple('form.statustocreate');
                 @{$cancreate{'statustocreate'}} = &Apache::loncommon::get_env_multiple('form.statustocreate');              push(@contexts,'statustocreate');
                 push(@contexts,'statustocreate');              foreach my $type (@types) {
             } else {  
                 undef($cancreate{'statustocreate'});  
             }   
             foreach my $type (@{$types}) {  
                 my @modifiable =  &Apache::loncommon::get_env_multiple('form.canmodify_'.$type);                  my @modifiable =  &Apache::loncommon::get_env_multiple('form.canmodify_'.$type);
                 foreach my $field (@fields) {                  foreach my $field (@fields) {
                     if (grep(/^\Q$field\E$/,@modifiable)) {                      if (grep(/^\Q$field\E$/,@modifiable)) {
Line 11333  sub modify_selfcreation { Line 19236  sub modify_selfcreation {
                 }                  }
             }              }
             if (ref($curr_usermodify{'selfcreate'}) eq 'HASH') {              if (ref($curr_usermodify{'selfcreate'}) eq 'HASH') {
                 foreach my $type (@{$types}) {                  foreach my $type (@types) {
                     if (ref($curr_usermodify{'selfcreate'}{$type}) eq 'HASH') {                      if (ref($curr_usermodify{'selfcreate'}{$type}) eq 'HASH') {
                         foreach my $field (@fields) {                          foreach my $field (@fields) {
                             if ($save_usermodify{'selfcreate'}{$type}{$field} ne                              if ($save_usermodify{'selfcreate'}{$type}{$field} ne
Line 11345  sub modify_selfcreation { Line 19248  sub modify_selfcreation {
                     }                      }
                 }                  }
             } else {              } else {
                 foreach my $type (@{$types}) {                  foreach my $type (@types) {
                     push(@{$changes{'selfcreate'}},$type);                      push(@{$changes{'selfcreate'}},$type);
                 }                  }
             }              }
Line 11394  sub modify_selfcreation { Line 19297  sub modify_selfcreation {
             }              }
         } elsif (ref($curr_usercreation{'cancreate'}{$item}) eq 'HASH') {          } elsif (ref($curr_usercreation{'cancreate'}{$item}) eq 'HASH') {
             if (ref($cancreate{$item}) eq 'HASH') {              if (ref($cancreate{$item}) eq 'HASH') {
                 foreach my $curr (keys(%{$curr_usercreation{'cancreate'}{$item}})) {                  foreach my $type (keys(%{$curr_usercreation{'cancreate'}{$item}})) {
                     if (ref($curr_usercreation{'cancreate'}{$item}{$curr}) eq 'HASH') {                      if (ref($curr_usercreation{'cancreate'}{$item}{$type}) eq 'HASH') {
                         foreach my $field (keys(%{$curr_usercreation{'cancreate'}{$item}{$curr}})) {                          foreach my $field (keys(%{$curr_usercreation{'cancreate'}{$item}{$type}})) {
                             unless ($curr_usercreation{'cancreate'}{$item}{$curr}{$field} eq $cancreate{$item}{$curr}{$field}) {                              unless ($curr_usercreation{'cancreate'}{$item}{$type}{$field} eq $cancreate{$item}{$type}{$field}) {
                                 if (!grep(/^$item$/,@{$changes{'cancreate'}})) {                                  if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                     push(@{$changes{'cancreate'}},$item);                                      push(@{$changes{'cancreate'}},$item);
                                 }                                  }
                             }                              }
                         }                          }
                     } elsif ($item eq 'selfcreateprocessing') {                      } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) {
                         if ($cancreate{$item}{$curr} ne $curr_usercreation{'cancreate'}{$item}{$curr}) {                          if ($cancreate{$item}{$type} ne $curr_usercreation{'cancreate'}{$item}{$type}) {
                             if (!grep(/^$item$/,@{$changes{'cancreate'}})) {  
                                 push(@{$changes{'cancreate'}},$item);  
                             }  
                         }  
                     } else {  
                         if (!$cancreate{$item}{$curr}) {  
                             if (!grep(/^$item$/,@{$changes{'cancreate'}})) {                              if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                 push(@{$changes{'cancreate'}},$item);                                  push(@{$changes{'cancreate'}},$item);
                             }                              }
                         }                          }
                     }                      }
                 }                  }
                 foreach my $field (keys(%{$cancreate{$item}})) {                  foreach my $type (keys(%{$cancreate{$item}})) {
                     if (ref($cancreate{$item}{$field}) eq 'HASH') {                      if (ref($cancreate{$item}{$type}) eq 'HASH') {
                         foreach my $inner (keys(%{$cancreate{$item}{$field}})) {                          foreach my $field (keys(%{$cancreate{$item}{$type}})) {
                             if (ref($curr_usercreation{'cancreate'}{$item}{$field}) eq 'HASH') {                              if (ref($curr_usercreation{'cancreate'}{$item}{$type}) eq 'HASH') {
                                 unless ($curr_usercreation{'cancreate'}{$item}{$field}{$inner} eq $cancreate{$item}{$field}{$inner}) {                                  unless ($curr_usercreation{'cancreate'}{$item}{$type}{$field} eq $cancreate{$item}{$type}{$field}) {
                                     if (!grep(/^$item$/,@{$changes{'cancreate'}})) {                                      if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                         push(@{$changes{'cancreate'}},$item);                                          push(@{$changes{'cancreate'}},$item);
                                     }                                      }
Line 11432  sub modify_selfcreation { Line 19329  sub modify_selfcreation {
                                 }                                  }
                             }                              }
                         }                          }
                     } elsif ($item eq 'selfcreateprocessing') {                      } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) {
                         if ($cancreate{$item}{$field} ne $curr_usercreation{'cancreate'}{$item}{$field}) {                          if ($cancreate{$item}{$type} ne $curr_usercreation{'cancreate'}{$item}{$type}) {
                             if (!grep(/^$item$/,@{$changes{'cancreate'}})) {  
                                 push(@{$changes{'cancreate'}},$item);  
                             }  
                         }  
                     } else {  
                         if (!$curr_usercreation{'cancreate'}{$item}{$field}) {  
                             if (!grep(/^$item$/,@{$changes{'cancreate'}})) {                              if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                                 push(@{$changes{'cancreate'}},$item);                                  push(@{$changes{'cancreate'}},$item);
                             }                              }
Line 11454  sub modify_selfcreation { Line 19345  sub modify_selfcreation {
                         push(@{$changes{'cancreate'}},$item);                          push(@{$changes{'cancreate'}},$item);
                     }                      }
                 }                  }
             } elsif (ref($cancreate{$item}) eq 'HASH') {              }
                 if (!$cancreate{$item}{$curr_usercreation{'cancreate'}{$item}}) {          } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) {
                     if (!grep(/^$item$/,@{$changes{'cancreate'}})) {              if (ref($cancreate{$item}) eq 'HASH') {
                         push(@{$changes{'cancreate'}},$item);                  if (!grep(/^$item$/,@{$changes{'cancreate'}})) {
                     }                      push(@{$changes{'cancreate'}},$item);
                 }                  }
             }              }
         } elsif ($item eq 'emailusername') {          } elsif ($item eq 'emailusername') {
Line 11491  sub modify_selfcreation { Line 19382  sub modify_selfcreation {
     if (ref($cancreate{'selfcreateprocessing'}) eq 'HASH') {      if (ref($cancreate{'selfcreateprocessing'}) eq 'HASH') {
         $save_usercreate{'cancreate'}{'selfcreateprocessing'} = $cancreate{'selfcreateprocessing'};          $save_usercreate{'cancreate'}{'selfcreateprocessing'} = $cancreate{'selfcreateprocessing'};
     }      }
       if (ref($cancreate{'emailverified'}) eq 'HASH') {
           $save_usercreate{'cancreate'}{'emailverified'} = $cancreate{'emailverified'};
       }
       if (ref($cancreate{'emailoptions'}) eq 'HASH') {
           $save_usercreate{'cancreate'}{'emailoptions'} = $cancreate{'emailoptions'};
       }
       if (ref($cancreate{'emaildomain'}) eq 'HASH') {
           $save_usercreate{'cancreate'}{'emaildomain'} = $cancreate{'emaildomain'};
       }
     if (ref($cancreate{'statustocreate'}) eq 'ARRAY') {      if (ref($cancreate{'statustocreate'}) eq 'ARRAY') {
         $save_usercreate{'cancreate'}{'statustocreate'} = $cancreate{'statustocreate'};          $save_usercreate{'cancreate'}{'statustocreate'} = $cancreate{'statustocreate'};
     }      }
Line 11498  sub modify_selfcreation { Line 19398  sub modify_selfcreation {
         $save_usercreate{'cancreate'}{'shibenv'} = $cancreate{'shibenv'};          $save_usercreate{'cancreate'}{'shibenv'} = $cancreate{'shibenv'};
     }      }
     $save_usercreate{'cancreate'}{'emailusername'} = $cancreate{'emailusername'};      $save_usercreate{'cancreate'}{'emailusername'} = $cancreate{'emailusername'};
     $save_usercreate{'email_rule'} = \@email_rule;      $save_usercreate{'email_rule'} = \%email_rule;
   
     my %userconfig_hash = (      my %userconfig_hash = (
             usercreation     => \%save_usercreate,              usercreation     => \%save_usercreate,
             usermodification => \%save_usermodify,              usermodification => \%save_usermodify,
               inststatus       => \%save_inststatus,
     );      );
   
     my $putresult = &Apache::lonnet::put_dom('configuration',\%userconfig_hash,      my $putresult = &Apache::lonnet::put_dom('configuration',\%userconfig_hash,
                                              $dom);                                               $dom);
 #  #
 # Accumulate details of changes to domain cofiguration for self-creation of usernames in $resulttext  # Accumulate details of changes to domain configuration for self-creation of usernames in $resulttext
 #  #
     if ($putresult eq 'ok') {      if ($putresult eq 'ok') {
         if (keys(%changes) > 0) {          if (keys(%changes) > 0) {
Line 11515  sub modify_selfcreation { Line 19417  sub modify_selfcreation {
             if (ref($changes{'cancreate'}) eq 'ARRAY') {              if (ref($changes{'cancreate'}) eq 'ARRAY') {
                 my %lt = &selfcreation_types();                  my %lt = &selfcreation_types();
                 foreach my $type (@{$changes{'cancreate'}}) {                  foreach my $type (@{$changes{'cancreate'}}) {
                     my $chgtext;                      my $chgtext = '';
                     if ($type eq 'selfcreate') {                      if ($type eq 'selfcreate') {
                         if (@{$cancreate{$type}} == 0) {                          if (@{$cancreate{$type}} == 0) {
                             $chgtext .= &mt('Self creation of a new user account is not permitted.');                              $chgtext .= &mt('Self creation of a new user account is not permitted.');
Line 11530  sub modify_selfcreation { Line 19432  sub modify_selfcreation {
                                 if (grep(/^(login|sso)$/,@{$cancreate{$type}})) {                                  if (grep(/^(login|sso)$/,@{$cancreate{$type}})) {
                                     if (ref($cancreate{'statustocreate'}) eq 'ARRAY') {                                      if (ref($cancreate{'statustocreate'}) eq 'ARRAY') {
                                         if (@{$cancreate{'statustocreate'}} == 0) {                                          if (@{$cancreate{'statustocreate'}} == 0) {
                                             $chgtext .= '<br />'.                                              $chgtext .= '<span class="LC_warning">'.
                                                         '<span class="LC_warning">'.                                                          &mt("However, no institutional affiliations (including 'other') are currently permitted to create accounts via log-in or single sign-on.").
                                                         &mt("However, no institutional affiliations (including 'other') are currently permitted to create accounts.").                                                          '</span><br />';
                                                         '</span>';  
                                         }                                          }
                                     }                                      }
                                 }                                  }
                                   if (grep(/^email$/,@{$cancreate{$type}})) {
                                       if (!@statuses) {
                                           $chgtext .= '<span class="LC_warning">'.
                                                       &mt("However, e-mail verification is currently set to 'unavailable' for all user types (including 'other'), so self-creation of accounts is not possible for non-institutional log-in.").
                                                       '</span><br />';
   
                                       }
                                   }
                             }                              }
                         }                          }
                     } elsif ($type eq 'shibenv') {                      } elsif ($type eq 'shibenv') {
                         if (keys(%{$cancreate{$type}}) == 0) {                          if (keys(%{$cancreate{$type}}) == 0) {
                             $chgtext .= &mt('Shibboleth-autheticated user does not use environment variables to set user information');                               $chgtext .= &mt('Shibboleth-autheticated user does not use environment variables to set user information').'<br />'; 
                         } else {                          } else {
                             $chgtext .= &mt('Shibboleth-autheticated user information set from environment variables, as follows:').                              $chgtext .= &mt('Shibboleth-autheticated user information set from environment variables, as follows:').
                                         '<ul>';                                          '<ul>';
Line 11554  sub modify_selfcreation { Line 19463  sub modify_selfcreation {
                                 }                                  }
                             }                              }
                             $chgtext .= '</ul>';                              $chgtext .= '</ul>';
                         }                            }
                     } elsif ($type eq 'statustocreate') {                      } elsif ($type eq 'statustocreate') {
                         if ((ref($cancreate{'selfcreate'}) eq 'ARRAY') &&                          if ((ref($cancreate{'selfcreate'}) eq 'ARRAY') &&
                             (ref($cancreate{'statustocreate'}) eq 'ARRAY')) {                              (ref($cancreate{'statustocreate'}) eq 'ARRAY')) {
Line 11567  sub modify_selfcreation { Line 19476  sub modify_selfcreation {
                                                     &mt("However, no institutional affiliations (including 'other') are currently permitted to create accounts.").                                                      &mt("However, no institutional affiliations (including 'other') are currently permitted to create accounts.").
                                                     '</span>';                                                      '</span>';
                                     }                                      }
                                 } elsif (ref($usertypes) eq 'HASH') {                                  } elsif (keys(%usertypes) > 0) {
                                     if (grep(/^(login|sso)$/,@{$cancreate{'selfcreate'}})) {                                      if (grep(/^(login|sso)$/,@{$cancreate{'selfcreate'}})) {
                                         $chgtext .= &mt('Creation of a new account for an institutional user is restricted to the following institutional affiliation(s):');                                          $chgtext .= &mt('Creation of a new account for an institutional user is restricted to the following institutional affiliation(s):');
                                     } else {                                      } else {
Line 11578  sub modify_selfcreation { Line 19487  sub modify_selfcreation {
                                         if ($case eq 'default') {                                          if ($case eq 'default') {
                                             $chgtext .= '<li>'.$othertitle.'</li>';                                              $chgtext .= '<li>'.$othertitle.'</li>';
                                         } else {                                          } else {
                                             $chgtext .= '<li>'.$usertypes->{$case}.'</li>';                                              $chgtext .= '<li>'.$usertypes{$case}.'</li>';
                                         }                                          }
                                     }                                      }
                                     $chgtext .= '</ul>';                                      $chgtext .= '</ul>';
                                     if (!grep(/^(login|sso)$/,@{$cancreate{'selfcreate'}})) {                                      if (!grep(/^(login|sso)$/,@{$cancreate{'selfcreate'}})) {
                                         $chgtext .= '<br /><span class="LC_warning">'.                                          $chgtext .= '<span class="LC_warning">'.
                                                     &mt('However, users authenticated by institutional login/single sign on are not currently permitted to create accounts.').                                                      &mt('However, users authenticated by institutional login/single sign on are not currently permitted to create accounts.').
                                                     '</span>';                                                      '</span>';
                                     }                                      }
Line 11595  sub modify_selfcreation { Line 19504  sub modify_selfcreation {
                                     $chgtext .= &mt('Although institutional affiliations permitted to create accounts were changed, self creation of accounts is not currently permitted for any authentication types.');                                      $chgtext .= &mt('Although institutional affiliations permitted to create accounts were changed, self creation of accounts is not currently permitted for any authentication types.');
                                 }                                  }
                             }                              }
                               $chgtext .= '<br />';
                         }                          }
                     } elsif ($type eq 'selfcreateprocessing') {                      } elsif ($type eq 'selfcreateprocessing') {
                         my %choices = &Apache::lonlocal::texthash (                          my %choices = &Apache::lonlocal::texthash (
                                                                     automatic => 'Automatic approval',                                                                      automatic => 'Automatic approval',
                                                                     approval  => 'Queued for approval',                                                                      approval  => 'Queued for approval',
                                                                   );                                                                    );
                         if (@statuses > 1) {                          if (@types) {
                             $chgtext .= &mt('Processing of requests to create account with e-mail address as username set as follows:').                               if (@statuses) {
                                         '<ul>';                                  $chgtext .= &mt('Processing of requests to create account with e-mail verification set as follows:'). 
                            foreach my $type (@statuses) {                                              '<ul>';
                                if ($type eq 'default') {                                  foreach my $status (@statuses) {
                                    $chgtext .= '<li>'.$othertitle.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$type}}.'</li>';                                      if ($status eq 'default') {
                                } else {                                          $chgtext .= '<li>'.$othertitle.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'</li>';
                                    $chgtext .= '<li>'.$usertypes->{$type}.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$type}}.'</li>';                                      } else {
                                }                                          $chgtext .= '<li>'.$usertypes{$status}.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'</li>';
                            }                                      }
                            $chgtext .= '</ul>';                                  }
                                   $chgtext .= '</ul>';
                               }
                         } else {                          } else {
                            $chgtext .= &mt('Processing of requests to create account with e-mail address as username set to: "[_1]"',                              $chgtext .= &mt('Processing of requests to create account with e-mail verification set to: "[_1]"',
                                          $choices{$cancreate{'selfcreateprocessing'}{'default'}});                                              $choices{$cancreate{'selfcreateprocessing'}{'default'}});
                           }
                       } elsif ($type eq 'emailverified') {
                           my %options = &Apache::lonlocal::texthash (
                                                                       all   => 'Same as e-mail',
                                                                       first => 'Omit @domain',
                                                                       free  => 'Free to choose',
                                                                     );
                           if (@types) {
                               if (@statuses) {
                                   $chgtext .= &mt('For self-created accounts verified by e-mail address, username is set as follows:').
                                               '<ul>';
                                   foreach my $status (@statuses) {
                                       if ($status eq 'default') {
                                           $chgtext .= '<li>'.$othertitle.' -- '.$options{$cancreate{'emailverified'}{$status}}.'</li>';
                                       } else {
                                           $chgtext .= '<li>'.$usertypes{$status}.' -- '.$options{$cancreate{'emailverified'}{$status}}.'</li>';
                                       }
                                   }
                                   $chgtext .= '</ul>';
                               }
                           } else {
                               $chgtext .= &mt("For self-created accounts verified by e-mail address, user's username is: '[_1]'",
                                               $options{$cancreate{'emailverified'}{'default'}});
                           }
                       } elsif ($type eq 'emailoptions') {
                           my %options = &Apache::lonlocal::texthash (
                                                                       any     => 'Any e-mail',
                                                                       inst    => 'Institutional only',
                                                                       noninst => 'Non-institutional only',
                                                                       custom  => 'Custom restrictions',
                                                                     );
                           if (@types) {
                               if (@statuses) {
                                   $chgtext .= &mt('For self-created accounts verified by e-mail address, requirements for e-mail address are as follows:').
                                               '<ul>';
                                   foreach my $status (@statuses) {
                                       if ($type eq 'default') {
                                           $chgtext .= '<li>'.$othertitle.' -- '.$options{$cancreate{'emailoptions'}{$status}}.'</li>';
                                       } else {
                                           $chgtext .= '<li>'.$usertypes{$status}.' -- '.$options{$cancreate{'emailoptions'}{$status}}.'</li>';
                                       }
                                   }
                                   $chgtext .= '</ul>';
                               }
                           } else {
                               if ($cancreate{'emailoptions'}{'default'} eq 'any') {
                                   $chgtext .= &mt('For self-created accounts verified by e-mail address, any e-mail may be used');
                               } else {
                                   $chgtext .= &mt('For self-created accounts verified by e-mail address, e-mail restricted to: "[_1]"',
                                                   $options{$cancreate{'emailoptions'}{'default'}});
                               }
                           }
                       } elsif ($type eq 'emaildomain') {
                           my $output;
                           if (@statuses) {
                               foreach my $type (@statuses) {
                                   if (ref($cancreate{'emaildomain'}{$type}) eq 'HASH') {
                                       if ($cancreate{'emailoptions'}{$type} eq 'inst') {
                                           if ($type eq 'default') {
                                               if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
                                                   ($cancreate{'emaildomain'}{$type}{'inst'} eq '')) {
                                                   $output = '<li>'.$othertitle.' -- '.&mt('No restriction on e-mail domain').'</li>';
                                               } else {
                                                   $output = '<li>'.$othertitle.' -- '.&mt("User's e-mail address needs to end: [_1]",
                                                                                           $cancreate{'emaildomain'}{$type}{'inst'}).'</li>';
                                               }
                                           } else {
                                               if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
                                                   ($cancreate{'emaildomain'}{$type}{'inst'} eq '')) {
                                                   $output = '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>';
                                               } else {
                                                   $output = '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail address needs to end: [_1]",
                                                                                                 $cancreate{'emaildomain'}{$type}{'inst'}).'</li>'; 
                                               }
                                           }
                                       } elsif ($cancreate{'emailoptions'}{$type} eq 'noninst') {
                                           if ($type eq 'default') {
                                               if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
                                                   ($cancreate{'emaildomain'}{$type}{'noninst'} eq '')) {
                                                   $output = '<li>'.$othertitle.' -- '.&mt('No restriction on e-mail domain').'</li>';
                                               } else {
                                                   $output = '<li>'.$othertitle.' -- '.&mt("User's e-mail address must not end: [_1]",
                                                                                           $cancreate{'emaildomain'}{$type}{'noninst'}).'</li>';
                                               }
                                           } else {
                                               if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') ||
                                                   ($cancreate{'emaildomain'}{$type}{'noninst'} eq '')) {
                                                   $output = '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>';
                                               } else {
                                                   $output = '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail address must not end: [_1]",
                                                                                                   $cancreate{'emaildomain'}{$type}{'noninst'}).'</li>';   
                                               }
                                           }
                                       }
                                   }
                               }
                           }
                           if ($output ne '') {
                               $chgtext .= &mt('For self-created accounts verified by e-mail address:').
                                           '<ul>'.$output.'</ul>';
                         }                          }
                     } elsif ($type eq 'captcha') {                      } elsif ($type eq 'captcha') {
                         if ($savecaptcha{$type} eq 'notused') {                          if ($savecaptcha{$type} eq 'notused') {
Line 11651  sub modify_selfcreation { Line 19663  sub modify_selfcreation {
                         }                          }
                     } elsif ($type eq 'emailusername') {                      } elsif ($type eq 'emailusername') {
                         if (ref($cancreate{'emailusername'}) eq 'HASH') {                          if (ref($cancreate{'emailusername'}) eq 'HASH') {
                             if (ref($types) eq 'ARRAY') {                              if (@statuses) {
                                 foreach my $type (@{$types}) {                                  foreach my $type (@statuses) {
                                     if (ref($cancreate{'emailusername'}{$type}) eq 'HASH') {                                      if (ref($cancreate{'emailusername'}{$type}) eq 'HASH') {
                                         if (keys(%{$cancreate{'emailusername'}{$type}}) > 0) {                                          if (keys(%{$cancreate{'emailusername'}{$type}}) > 0) {
                                             $chgtext .= &mt('When self-creating account with e-mail as username, the following information will be provided by [_1]:',"'$usertypes->{$type}'").                                              $chgtext .= &mt('When self-creating account with e-mail verification, the following information will be provided by [_1]:',"'$usertypes{$type}'").
                                                     '<ul>';                                                      '<ul>';
                                             foreach my $field (@{$infofields}) {                                              foreach my $field (@{$infofields}) {
                                                 if ($cancreate{'emailusername'}{$type}{$field}) {                                                  if ($cancreate{'emailusername'}{$type}{$field}) {
Line 11664  sub modify_selfcreation { Line 19676  sub modify_selfcreation {
                                             }                                              }
                                             $chgtext .= '</ul>';                                              $chgtext .= '</ul>';
                                         } else {                                          } else {
                                             $chgtext .= &mt('When self creating account with e-mail as username, no information besides e-mail address will be provided by [_1].',"'$usertypes->{$type}'").'<br />';                                              $chgtext .= &mt('When self creating account with e-mail verification, no information besides e-mail address will be provided by [_1].',"'$usertypes{$type}'").'<br />';
                                         }                                          }
                                     } else {                                      } else {
                                         $chgtext .= &mt('When self creating account with e-mail as username, no information besides e-mail address will be provided by [_1].',"'$usertypes->{$type}'").'<br />';                                          $chgtext .= &mt('When self creating account with e-mail verification, no information besides e-mail address will be provided by [_1].',"'$usertypes{$type}'").'<br />';
                                     }                                      }
                                 }                                  }
                             }                              }
                         }                          }
                     } elsif ($type eq 'notify') {                      } elsif ($type eq 'notify') {
                         $chgtext = &mt('No Domain Coordinators will receive notification of username requests requiring approval.');                          my $numapprove = 0;
                         if (ref($changes{'cancreate'}) eq 'ARRAY') {                          if (ref($changes{'cancreate'}) eq 'ARRAY') {
                             if ((grep(/^notify$/,@{$changes{'cancreate'}})) && (ref($cancreate{'notify'}) eq 'HASH')) {                              if ((grep(/^notify$/,@{$changes{'cancreate'}})) && (ref($cancreate{'notify'}) eq 'HASH')) {
                                 if ($cancreate{'notify'}{'approval'}) {                                  if ($cancreate{'notify'}{'approval'}) {
                                     $chgtext = &mt('Notification of username requests requiring approval will be sent to: ').$cancreate{'notify'}{'approval'};                                      $chgtext .= &mt('Notification of username requests requiring approval will be sent to: ').$cancreate{'notify'}{'approval'};
                                       $numapprove ++;
                                 }                                  }
                             }                              }
                         }                          }
                           unless ($numapprove) {
                               $chgtext .= &mt('No Domain Coordinators will receive notification of username requests requiring approval.');
                           }
                     }                      }
                     if ($chgtext) {                      if ($chgtext) {
                         $resulttext .= '<li>'.$chgtext.'</li>';                          $resulttext .= '<li>'.$chgtext.'</li>';
                     }                      }
                 }                  }
             }              }
             if (ref($changes{'email_rule'}) eq 'ARRAY') {              if ((ref($changes{'email_rule'}) eq 'ARRAY') && (@{$changes{'email_rule'}} > 0)) {
                 my ($emailrules,$emailruleorder) =                  my ($emailrules,$emailruleorder) =
                     &Apache::lonnet::inst_userrules($dom,'email');                      &Apache::lonnet::inst_userrules($dom,'email');
                 my $chgtext = '<ul>';                  foreach my $type (@{$changes{'email_rule'}}) {
                 foreach my $type (@email_rule) {                      if (ref($email_rule{$type}) eq 'ARRAY') {
                     if (ref($emailrules->{$type}) eq 'HASH') {                          my $chgtext = '<ul>';
                         $chgtext .= '<li>'.$emailrules->{$type}{'name'}.'</li>';                          foreach my $rule (@{$email_rule{$type}}) {
                               if (ref($emailrules->{$rule}) eq 'HASH') {
                                   $chgtext .= '<li>'.$emailrules->{$rule}{'name'}.'</li>';
                               }
                           }
                           $chgtext .= '</ul>';
                           my $typename;
                           if (@types) {
                               if ($type eq 'default') {
                                   $typename = $othertitle;
                               } else {
                                   $typename = $usertypes{$type};
                               } 
                               $chgtext .= &mt('(Affiliation: [_1])',$typename);
                           }
                           if (@{$email_rule{$type}} > 0) {
                               $resulttext .= '<li>'.
                                              &mt('Accounts may not be created by users verified by e-mail, for e-mail addresses of the following types: ',
                                                  $usertypes{$type}).
                                              $chgtext.
                                              '</li>';
                           } else {
                               $resulttext .= '<li>'.
                                              &mt('There are now no restrictions on e-mail addresses which may be used for verification when a user requests an account.').
                                              '</li>'.
                                              &mt('(Affiliation: [_1])',$typename);
                           }
                     }                      }
                 }                  }
                 $chgtext .= '</ul>';              }
                 if (@email_rule > 0) {              if (ref($changes{'inststatus'}) eq 'ARRAY') {
                     $resulttext .= '<li>'.                  if (ref($save_inststatus{'inststatusguest'}) eq 'ARRAY') {
                                    &mt('Accounts may not be created by users self-enrolling with e-mail addresses of the following types: ').                      if (@{$save_inststatus{'inststatusguest'}} > 0) {
                                        $chgtext.                          my $chgtext = '<ul>';
                                    '</li>';                          foreach my $type (@{$save_inststatus{'inststatusguest'}}) {
                 } else {                              $chgtext .= '<li>'.$usertypes{$type}.'</li>';
                     $resulttext .= '<li>'.                          }
                                    &mt('There are now no restrictions on e-mail addresses which may be used as a username when self-enrolling.').                          $chgtext .= '</ul>';
                                    '</li>';                          $resulttext .= '<li>'.
                                          &mt('A user will self-report one of the following affiliations when requesting an account verified by e-mail: ').
                                             $chgtext.
                                          '</li>';
                       } else {
                           $resulttext .= '<li>'.
                                          &mt('No affiliations available for self-reporting when requesting an account verified by e-mail.').
                                          '</li>';
                       }
                 }                  }
             }              }
             if (ref($changes{'selfcreate'}) eq 'ARRAY') {              if (ref($changes{'selfcreate'}) eq 'ARRAY') {
Line 11713  sub modify_selfcreation { Line 19763  sub modify_selfcreation {
                 my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();                  my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();
                 foreach my $type (@{$changes{'selfcreate'}}) {                  foreach my $type (@{$changes{'selfcreate'}}) {
                     my $typename = $type;                      my $typename = $type;
                     if (ref($usertypes) eq 'HASH') {                      if (keys(%usertypes) > 0) {
                         if ($usertypes->{$type} ne '') {                          if ($usertypes{$type} ne '') {
                             $typename = $usertypes->{$type};                              $typename = $usertypes{$type};
                         }                          }
                     }                      }
                     my @modifiable;                      my @modifiable;
Line 11738  sub modify_selfcreation { Line 19788  sub modify_selfcreation {
                 $resulttext .= '</ul></li>';                  $resulttext .= '</ul></li>';
             }              }
             $resulttext .= '</ul>';              $resulttext .= '</ul>';
               my $cachetime = 24*60*60;
               $domdefaults{'inststatusguest'} = $save_inststatus{'inststatusguest'};
               &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'domdefaults'} = 1;
               }
         } else {          } else {
             $resulttext = &mt('No changes made to self-creation settings');              $resulttext = &mt('No changes made to self-creation settings');
         }          }
Line 11752  sub modify_selfcreation { Line 19808  sub modify_selfcreation {
 }  }
   
 sub process_captcha {  sub process_captcha {
     my ($container,$changes,$newsettings,$current) = @_;      my ($container,$changes,$newsettings,$currsettings) = @_;
     return unless ((ref($changes) eq 'HASH') && (ref($newsettings) eq 'HASH') || (ref($current) eq 'HASH'));      return unless ((ref($changes) eq 'HASH') && (ref($newsettings) eq 'HASH'));
     $newsettings->{'captcha'} = $env{'form.'.$container.'_captcha'};      $newsettings->{'captcha'} = $env{'form.'.$container.'_captcha'};
     unless ($newsettings->{'captcha'} eq 'recaptcha' || $newsettings->{'captcha'} eq 'notused') {      unless ($newsettings->{'captcha'} eq 'recaptcha' || $newsettings->{'captcha'} eq 'notused') {
         $newsettings->{'captcha'} = 'original';          $newsettings->{'captcha'} = 'original';
     }      }
     if ($current->{'captcha'} ne $newsettings->{'captcha'}) {      my %current;
       if (ref($currsettings) eq 'HASH') {
           %current = %{$currsettings};
       }
       if ($current{'captcha'} ne $newsettings->{'captcha'}) {
         if ($container eq 'cancreate') {          if ($container eq 'cancreate') {
             if (ref($changes->{'cancreate'}) eq 'ARRAY') {              if (ref($changes->{'cancreate'}) eq 'ARRAY') {
                 push(@{$changes->{'cancreate'}},'captcha');                  push(@{$changes->{'cancreate'}},'captcha');
             } elsif (!defined($changes->{'cancreate'})) {              } elsif (!defined($changes->{'cancreate'})) {
                 $changes->{'cancreate'} = ['captcha'];                  $changes->{'cancreate'} = ['captcha'];
             }              }
           } elsif ($container eq 'passwords') {
               $changes->{'reset'} = 1;
         } else {          } else {
             $changes->{'captcha'} = 1;              $changes->{'captcha'} = 1;
         }          }
Line 11786  sub process_captcha { Line 19848  sub process_captcha {
         }          }
         $newsettings->{'recaptchaversion'} = $newversion;          $newsettings->{'recaptchaversion'} = $newversion;
     }      }
     if (ref($current->{'recaptchakeys'}) eq 'HASH') {      if (ref($current{'recaptchakeys'}) eq 'HASH') {
         $currpub = $current->{'recaptchakeys'}{'public'};          $currpub = $current{'recaptchakeys'}{'public'};
         $currpriv = $current->{'recaptchakeys'}{'private'};          $currpriv = $current{'recaptchakeys'}{'private'};
         unless ($newsettings->{'captcha'} eq 'recaptcha') {          unless ($newsettings->{'captcha'} eq 'recaptcha') {
             $newsettings->{'recaptchakeys'} = {              $newsettings->{'recaptchakeys'} = {
                                                  public  => '',                                                   public  => '',
Line 11796  sub process_captcha { Line 19858  sub process_captcha {
                                               }                                                }
         }          }
     }      }
     if ($current->{'captcha'} eq 'recaptcha') {      if ($current{'captcha'} eq 'recaptcha') {
         $currversion = $current->{'recaptchaversion'};          $currversion = $current{'recaptchaversion'};
         if ($currversion ne '2') {          if ($currversion ne '2') {
             $currversion = 1;              $currversion = 1;
         }          }
Line 11809  sub process_captcha { Line 19871  sub process_captcha {
             } elsif (!defined($changes->{'cancreate'})) {              } elsif (!defined($changes->{'cancreate'})) {
                 $changes->{'cancreate'} = ['recaptchaversion'];                  $changes->{'cancreate'} = ['recaptchaversion'];
             }              }
           } elsif ($container eq 'passwords') {
               $changes->{'reset'} = 1;
         } else {          } else {
             $changes->{'recaptchaversion'} = 1;              $changes->{'recaptchaversion'} = 1;
         }          }
Line 11820  sub process_captcha { Line 19884  sub process_captcha {
             } elsif (!defined($changes->{'cancreate'})) {              } elsif (!defined($changes->{'cancreate'})) {
                 $changes->{'cancreate'} = ['recaptchakeys'];                  $changes->{'cancreate'} = ['recaptchakeys'];
             }              }
           } elsif ($container eq 'passwords') {
               $changes->{'reset'} = 1;
         } else {          } else {
             $changes->{'recaptchakeys'} = 1;              $changes->{'recaptchakeys'} = 1;
         }          }
Line 11935  sub modify_defaults { Line 20001  sub modify_defaults {
     my ($resulttext,$mailmsgtxt,%newvalues,%changes,@errors);      my ($resulttext,$mailmsgtxt,%newvalues,%changes,@errors);
     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','intauth_cost','intauth_check','intauth_switch');                   '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 11973  sub modify_defaults { Line 20039  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);
                 }                  }
             }              }
         } elsif ($item eq 'intauth_cost') {          }
             if ($newvalues{$item} ne '') {          if (grep(/^\Q$item\E$/,@errors)) {
                 if ($newvalues{$item} =~ /\D/) {              $newvalues{$item} = $domdefaults{$item};
                     push(@errors,$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 ($item eq 'intauth_check') {          } elsif ($domdefaults{$item} ne $newvalues{$item}) {
             if ($newvalues{$item} ne '') {              $changes{$item} = 1;
                 unless ($newvalues{$item} =~ /^(0|1|2)$/) {          }
                     push(@errors,$item);          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});
                           }
                       }
                 }                  }
             }              }
         } elsif ($item eq 'intauth_switch') {          }
             if ($newvalues{$item} ne '') {          $domdefaults{$item} = $newvalues{$item};
                 unless ($newvalues{$item} =~ /^(0|1|2)$/) {      }
                     push(@errors,$item);      my %staticdefaults = (
                              'intauth_cost'   => 10,
                              'intauth_check'  => 0,
                              'intauth_switch' => 0,
                            );
       foreach my $item ('intauth_cost','intauth_check','intauth_switch') {
           if (exists($domdefaults{$item})) {
               $newvalues{$item} = $domdefaults{$item};
           } else {
               $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 (grep(/^\Q$item\E$/,@errors)) {      }
             $newvalues{$item} = $domdefaults{$item};      if (ref($domdefaults{'unamemap_rule'}) eq 'ARRAY') {
         } elsif ($domdefaults{$item} ne $newvalues{$item}) {          if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') {
             $changes{$item} = 1;              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'};
         }          }
         $domdefaults{$item} = $newvalues{$item};  
     }      }
     my %defaults_hash = (      my %defaults_hash = (
                          defaults => \%newvalues,                           defaults => \%newvalues,
Line 12021  sub modify_defaults { Line 20158  sub modify_defaults {
     }      }
     my @todelete = &Apache::loncommon::get_env_multiple('form.inststatus_delete');      my @todelete = &Apache::loncommon::get_env_multiple('form.inststatus_delete');
     my @allpos;      my @allpos;
     my %guests;  
     my %alltypes;      my %alltypes;
     my ($currtitles,$currguests,$currorder);      my @inststatusguest;
       if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {
           foreach my $type (@{$currinststatus->{'inststatusguest'}}) {
               unless (grep(/^\Q$type\E$/,@todelete)) {
                   push(@inststatusguest,$type);
               }
           }
       }
       my ($currtitles,$currorder);
     if (ref($currinststatus) eq 'HASH') {      if (ref($currinststatus) eq 'HASH') {
         if (ref($currinststatus->{'inststatusorder'}) eq 'ARRAY') {          if (ref($currinststatus->{'inststatusorder'}) eq 'ARRAY') {
             foreach my $type (@{$currinststatus->{'inststatusorder'}}) {              foreach my $type (@{$currinststatus->{'inststatusorder'}}) {
Line 12038  sub modify_defaults { Line 20182  sub modify_defaults {
                     $allpos[$position] = $type;                      $allpos[$position] = $type;
                     $alltypes{$type} = $env{'form.inststatus_title_'.$type};                      $alltypes{$type} = $env{'form.inststatus_title_'.$type};
                     $alltypes{$type} =~ s/`//g;                      $alltypes{$type} =~ s/`//g;
                     if ($env{'form.inststatus_guest_'.$type}) {  
                         $guests{$type} = 1;  
                     }  
                 }                  }
             }              }
             if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {  
                 $currguests = join(',',@{$currinststatus->{'inststatusguest'}});  
             }  
             $currorder = join(',',@{$currinststatus->{'inststatusorder'}});              $currorder = join(',',@{$currinststatus->{'inststatusorder'}});
             $currtitles =~ s/,$//;              $currtitles =~ s/,$//;
         }          }
Line 12054  sub modify_defaults { Line 20192  sub modify_defaults {
         my $newtype = $env{'form.addinststatus'};          my $newtype = $env{'form.addinststatus'};
         $newtype =~ s/\W//g;          $newtype =~ s/\W//g;
         unless (exists($alltypes{$newtype})) {          unless (exists($alltypes{$newtype})) {
             if ($env{'form.addinststatus_guest'}) {  
                 $guests{$newtype} = 1;  
             }  
             $alltypes{$newtype} = $env{'form.addinststatus_title'};              $alltypes{$newtype} = $env{'form.addinststatus_title'};
             $alltypes{$newtype} =~ s/`//g;               $alltypes{$newtype} =~ s/`//g; 
             my $position = $env{'form.addinststatus_pos'};              my $position = $env{'form.addinststatus_pos'};
Line 12066  sub modify_defaults { Line 20201  sub modify_defaults {
             }              }
         }          }
     }      }
     my (@orderedstatus,@orderedguests);      my @orderedstatus;
     foreach my $type (@allpos) {      foreach my $type (@allpos) {
         unless (($type eq '') || (grep(/^\Q$type\E$/,@orderedstatus))) {          unless (($type eq '') || (grep(/^\Q$type\E$/,@orderedstatus))) {
             push(@orderedstatus,$type);              push(@orderedstatus,$type);
             if ($guests{$type}) {  
                 push(@orderedguests,$type);  
             }  
         }          }
     }      }
     foreach my $type (keys(%alltypes)) {      foreach my $type (keys(%alltypes)) {
Line 12083  sub modify_defaults { Line 20215  sub modify_defaults {
     $defaults_hash{'inststatus'} = {      $defaults_hash{'inststatus'} = {
                                      inststatustypes => \%alltypes,                                       inststatustypes => \%alltypes,
                                      inststatusorder => \@orderedstatus,                                       inststatusorder => \@orderedstatus,
                                      inststatusguest => \@orderedguests,                                       inststatusguest => \@inststatusguest,
                                    };                                     };
     if (ref($defaults_hash{'inststatus'}) eq 'HASH') {      if (ref($defaults_hash{'inststatus'}) eq 'HASH') {
         foreach my $item ('inststatustypes','inststatusorder','inststatusguest') {          foreach my $item ('inststatustypes','inststatusorder','inststatusguest') {
Line 12093  sub modify_defaults { Line 20225  sub modify_defaults {
     if ($currorder ne join(',',@orderedstatus)) {      if ($currorder ne join(',',@orderedstatus)) {
         $changes{'inststatus'}{'inststatusorder'} = 1;          $changes{'inststatus'}{'inststatusorder'} = 1;
     }      }
     if ($currguests ne join(',',@orderedguests)) {  
         $changes{'inststatus'}{'inststatusguest'} = 1;  
     }  
     my $newtitles;      my $newtitles;
     foreach my $item (@orderedstatus) {      foreach my $item (@orderedstatus) {
         $newtitles .= $alltypes{$item}.',';          $newtitles .= $alltypes{$item}.',';
Line 12114  sub modify_defaults { Line 20243  sub modify_defaults {
             foreach my $item (sort(keys(%changes))) {              foreach my $item (sort(keys(%changes))) {
                 if ($item eq 'inststatus') {                  if ($item eq 'inststatus') {
                     if (ref($changes{'inststatus'}) eq 'HASH') {                      if (ref($changes{'inststatus'}) eq 'HASH') {
                         if (($changes{'inststatus'}{'inststatustypes'}) || $changes{'inststatus'}{'inststatusorder'}) {                          if (@orderedstatus) {
                             $resulttext .= '<li>'.&mt('Institutional user status types set to:').' ';                              $resulttext .= '<li>'.&mt('Institutional user status types set to:').' ';
                             foreach my $type (@orderedstatus) {                               foreach my $type (@orderedstatus) { 
                                 $resulttext .= $alltypes{$type}.', ';                                  $resulttext .= $alltypes{$type}.', ';
                             }                              }
                             $resulttext =~ s/, $//;                              $resulttext =~ s/, $//;
                             $resulttext .= '</li>';                              $resulttext .= '</li>';
                           } else {
                               $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>'; 
                         }                          }
                         if ($changes{'inststatus'}{'inststatusguest'}) {                      }
                             $resulttext .= '<li>';                   } elsif ($item eq 'unamemap_rule') {
                             if (@orderedguests) {                      if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') {
                                 $resulttext .= &mt('Types assignable to "non-institutional" usernames set to:').' ';                          my @rulenames;
                                 foreach my $type (@orderedguests) {                          if (ref($unamemaprules) eq 'HASH') {
                                     $resulttext .= $alltypes{$type}.', ';                              foreach my $rule (@{$newvalues{'unamemap_rule'}}) {
                                   if (ref($unamemaprules->{$rule}) eq 'HASH') {
                                       push(@rulenames,$unamemaprules->{$rule}->{'name'});
                                 }                                  }
                                 $resulttext =~ s/, $//;  
                             } else {  
                                 $resulttext .= &mt('Types assignable to "non-institutional" usernames set to none.');  
                             }                              }
                             $resulttext .= '</li>';  
                         }                          }
                           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};
Line 12147  sub modify_defaults { Line 20285  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}};
                     } elsif ($item eq 'intauth_switch') {  
                         my %optiondesc = &Apache::lonlocal::texthash (  
                                             0 => 'No',  
                                             1 => 'Yes',  
                                             2 => 'Yes, and copy existing passwd file to passwd.bak file',  
                                          );  
                         if ($value =~ /^(0|1|2)$/) {  
                             $value = $optiondesc{$value};  
                         } else {  
                             $value = &mt('none -- defaults to No');  
                         }  
                     } elsif ($item eq 'intauth_check') {  
                         my %optiondesc = &Apache::lonlocal::texthash (  
                                              0 => 'No',  
                                              1 => 'Yes, allow login then update passwd file using default cost (if higher)',  
                                              2 => 'Yes, disallow login if stored cost is less than domain default',  
                                          );  
                         if ($value =~ /^(0|1|2)$/) {  
                             $value = $optiondesc{$value};  
                         } else {  
                             $value = &mt('none -- defaults to No');  
                         }  
                     }                      }
                     $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 12219  sub modify_scantron { Line 20349  sub modify_scantron {
     my $custom = 'custom.tab';      my $custom = 'custom.tab';
     my $default = 'default.tab';      my $default = 'default.tab';
     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);
     if ($env{'form.scantronformat.filename'} ne '') {      if ($env{'form.scantronformat.filename'} ne '') {
         my $error;          my $error;
Line 12254  sub modify_scantron { Line 20384  sub modify_scantron {
             if ($env{'form.scantronformat_del'}) {              if ($env{'form.scantronformat_del'}) {
                 $confhash{'scantron'}{'scantronformat'} = '';                  $confhash{'scantron'}{'scantronformat'} = '';
                 $changes{'scantronformat'} = 1;                  $changes{'scantronformat'} = 1;
               } else {
                   $confhash{'scantron'}{'scantronformat'} = $domconfig{'scantron'}{'scantronformat'};
               }
           }
       }
       my @options = ('hdr','pad','rem');
       my @fields = &scantroncsv_fields();
       my %titles = &scantronconfig_titles();
       my @formats = &Apache::loncommon::get_env_multiple('form.scantronconfig');
       my ($newdat,$currdat,%newcol,%currcol);
       if (grep(/^dat$/,@formats)) {
           $confhash{'scantron'}{config}{dat} = 1;
           $newdat = 1;
       } else {
           $newdat = 0;
       }
       if (grep(/^csv$/,@formats)) {
           my %bynum;
           foreach my $field (@fields) {
               if ($env{'form.scantronconfig_csv_'.$field} =~ /^(\d+)$/) {
                   my $posscol = $1;
                   if (($posscol < 20) && (!$bynum{$posscol})) {
                       $confhash{'scantron'}{config}{csv}{fields}{$field} = $posscol;
                       $bynum{$posscol} = $field;
                       $newcol{$field} = $posscol;
                   }
               }
           }
           if (keys(%newcol)) {
               foreach my $option (@options) {
                   if ($env{'form.scantroncsv_'.$option}) {
                       $confhash{'scantron'}{config}{csv}{options}{$option} = 1;
                   }
               }
           }
       }
       $currdat = 1;
       if (ref($domconfig{'scantron'}) eq 'HASH') {
           if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') {
               unless (exists($domconfig{'scantron'}{'config'}{'dat'})) {
                   $currdat = 0;
               }
               if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') {
                   if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                       %currcol = %{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}};
                   }
               }
           }
       }
       if ($currdat != $newdat) {
           $changes{'config'} = 1;
       } else {
           foreach my $field (@fields) {
               if ($currcol{$field} ne '') {
                   if ($currcol{$field} ne $newcol{$field}) {
                       $changes{'config'} = 1;
                       last;
                   }
               } elsif ($newcol{$field} ne '') {
                   $changes{'config'} = 1;
                   last;
             }              }
         }          }
     }      }
Line 12264  sub modify_scantron { Line 20455  sub modify_scantron {
             if (keys(%changes) > 0) {              if (keys(%changes) > 0) {
                 if (ref($confhash{'scantron'}) eq 'HASH') {                  if (ref($confhash{'scantron'}) eq 'HASH') {
                     $resulttext = &mt('Changes made:').'<ul>';                      $resulttext = &mt('Changes made:').'<ul>';
                     if ($confhash{'scantron'}{'scantronformat'} eq '') {                      if ($changes{'scantronformat'}) {
                         $resulttext .= '<li>'.&mt('[_1] bubblesheet format file removed; [_2] file will be used for courses in this domain.',$custom,$default).'</li>';                          if ($confhash{'scantron'}{'scantronformat'} eq '') {
                     } else {                              $resulttext .= '<li>'.&mt('[_1] bubblesheet format file removed; [_2] file will be used for courses in this domain.',$custom,$default).'</li>';
                         $resulttext .= '<li>'.&mt('Custom bubblesheet format file ([_1]) uploaded for use with courses in this domain.',$custom).'</li>';                          } else {
                               $resulttext .= '<li>'.&mt('Custom bubblesheet format file ([_1]) uploaded for use with courses in this domain.',$custom).'</li>';
                           }
                       }
                       if ($changes{'config'}) {
                           if (ref($confhash{'scantron'}{'config'}) eq 'HASH') {
                               if ($confhash{'scantron'}{'config'}{'dat'}) {
                                   $resulttext .= '<li>'.&mt('Bubblesheet data upload formats includes .dat format').'</li>';
                               }
                               if (ref($confhash{'scantron'}{'config'}{'csv'}) eq 'HASH') {
                                   if (ref($confhash{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') {
                                       if (keys(%{$confhash{'scantron'}{'config'}{'csv'}{'fields'}})) {
                                           $resulttext .= '<li>'.&mt('Bubblesheet data upload formats includes .csv format, with following fields/column numbers supported:').'<ul>';
                                           foreach my $field (@fields) {
                                               if ($confhash{'scantron'}{'config'}{'csv'}{'fields'}{$field} ne '') {
                                                   my $showcol = $confhash{'scantron'}{'config'}{'csv'}{'fields'}{$field} + 1;
                                                   $resulttext .= '<li>'.$titles{$field}.': '.$showcol.'</li>';
                                               }
                                           }
                                           $resulttext .= '</ul></li>';
                                           if (ref($confhash{'scantron'}{'config'}{'csv'}{'options'}) eq 'HASH') {
                                               if (keys(%{$confhash{'scantron'}{'config'}{'csv'}{'options'}})) {
                                                   $resulttext .= '<li>'.&mt('Bubblesheet data upload formats includes .csv format, with following options:').'<ul>';
                                                   foreach my $option (@options) {
                                                       if ($confhash{'scantron'}{'config'}{'csv'}{'options'}{$option} ne '') {
                                                           $resulttext .= '<li>'.$titles{$option}.'</li>';
                                                       }
                                                   }
                                                   $resulttext .= '</ul></li>';
                                               }
                                           }
                                       }
                                   }
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('No bubblesheet data upload formats set -- will default to assuming .dat format').'</li>';
                           }
                     }                      }
                     $resulttext .= '</ul>';                      $resulttext .= '</ul>';
                 } else {                  } else {
                     $resulttext = &mt('Changes made to bubblesheet format file.');                      $resulttext = &mt('Changes made to bubblesheet format file.');
                 }                  }
                 $resulttext .= '</ul>';  
                 &Apache::loncommon::devalidate_domconfig_cache($dom);                  &Apache::loncommon::devalidate_domconfig_cache($dom);
                 if (ref($lastactref) eq 'HASH') {                  if (ref($lastactref) eq 'HASH') {
                     $lastactref->{'domainconfig'} = 1;                      $lastactref->{'domainconfig'} = 1;
                 }                  }
             } else {              } else {
                 $resulttext = &mt('No changes made to bubblesheet format file');                  $resulttext = &mt('No changes made to bubblesheet format settings');
             }              }
         } else {          } else {
             $resulttext = '<span class="LC_error">'.              $resulttext = '<span class="LC_error">'.
Line 12289  sub modify_scantron { Line 20515  sub modify_scantron {
         $resulttext = &mt('No changes made to bubblesheet format file');           $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 12319  sub modify_coursecategories { Line 20545  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 12333  sub modify_coursecategories { Line 20568  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 12355  sub modify_coursecategories { Line 20594  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 12417  sub modify_coursecategories { Line 20659  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 12535  sub modify_coursecategories { Line 20791  sub modify_coursecategories {
                     }                      }
                     $resulttext .= '</ul></li>';                      $resulttext .= '</ul></li>';
                 }                  }
                   &Apache::lonnet::do_cache_new('cats',$dom,$cathash,3600);
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'cats'} = 1;
                   }
             }              }
             $resulttext .= '</ul>';              $resulttext .= '</ul>';
             if ($changes{'unauth'} || $changes{'auth'}) {              if ($changes{'unauth'} || $changes{'auth'}) {
Line 13014  sub modify_coursedefaults { Line 21274  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');                     'mysqltables_official','mysqltables_unofficial','mysqltables_community',
     my @types = ('official','unofficial','community','textbook');                     '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,
                            postsubmit           => 60,                             postsubmit           => 60,
                            mysqltables          => 172800,                             mysqltables          => 172800,
                          );                           );
       my %texoptions = (
                           MathJax  => 'MathJax',
                           mimetex  => &mt('Convert to Images'),
                           tth      => &mt('TeX to HTML'),
                        );
     $defaultshash{'coursedefaults'} = {};      $defaultshash{'coursedefaults'} = {};
   
     if (ref($domconfig{'coursedefaults'}) ne 'HASH') {      if (ref($domconfig{'coursedefaults'}) ne 'HASH') {
Line 13075  sub modify_coursedefaults { Line 21343  sub modify_coursedefaults {
                 $defaultshash{'coursedefaults'}{$setting}{$type} = $newdef;                  $defaultshash{'coursedefaults'}{$setting}{$type} = $newdef;
             }              }
             if ($currdef ne $newdef) {              if ($currdef ne $newdef) {
                 my $staticdef;  
                 if ($item eq 'anonsurvey_threshold') {                  if ($item eq 'anonsurvey_threshold') {
                     unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) {                      unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) {
                         $changes{$item} = 1;                          $changes{$item} = 1;
Line 13088  sub modify_coursedefaults { Line 21355  sub modify_coursedefaults {
                 }                  }
             }              }
         }          }
           my $texengine;
           if ($env{'form.texengine'} =~ /^(MathJax|mimetex|tth)$/) {
               $texengine = $env{'form.texengine'};
               my $currdef = $domconfig{'coursedefaults'}{'texengine'};
               if ($currdef eq '') {
                   unless ($texengine eq $Apache::lonnet::deftex) {
                       $changes{'texengine'} = 1;
                   }
               } elsif ($currdef ne $texengine) {
                   $changes{'texengine'} = 1;
               }
           }
           if ($texengine ne '') {
               $defaultshash{'coursedefaults'}{'texengine'} = $texengine;
           }
         my $currclone = $domconfig{'coursedefaults'}{'canclone'};          my $currclone = $domconfig{'coursedefaults'}{'canclone'};
         my @currclonecode;          my @currclonecode;
         if (ref($currclone) eq 'HASH') {          if (ref($currclone) eq 'HASH') {
Line 13206  sub modify_coursedefaults { Line 21488  sub modify_coursedefaults {
     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{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'}) ||
                 foreach my $item ('uselcmath','usejsme') {                  ($changes{'inline_chem'}) || ($changes{'ltiauth'})) {
                   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 13260  sub modify_coursedefaults { Line 21543  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 13272  sub modify_coursedefaults { Line 21561  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') {
                       if ($defaultshash{'coursedefaults'}{'texengine'} ne '') {
                           $resulttext .= '<li>'.&mt('Default method to display mathematics set to: "[_1]"',
                                                     $texoptions{$defaultshash{'coursedefaults'}{'texengine'}}).'</li>';
                       }
                 } elsif ($item eq 'anonsurvey_threshold') {                  } elsif ($item eq 'anonsurvey_threshold') {
                     $resulttext .= '<li>'.&mt('Responder count required for display of anonymous survey submissions set to [_1].',$defaultshash{'coursedefaults'}{'anonsurvey_threshold'}).'</li>';                      $resulttext .= '<li>'.&mt('Responder count required for display of anonymous survey submissions set to [_1].',$defaultshash{'coursedefaults'}{'anonsurvey_threshold'}).'</li>';
                 } elsif ($item eq 'uploadquota') {                  } elsif ($item eq 'uploadquota') {
Line 13280  sub modify_coursedefaults { Line 21580  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>';
Line 13293  sub modify_coursedefaults { Line 21593  sub modify_coursedefaults {
                                        '<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 13328  sub modify_coursedefaults { Line 21629  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 13363  sub modify_coursedefaults { Line 21666  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>';
                       }
                 }                  }
             }              }
             $resulttext .= '</ul>';              $resulttext .= '</ul>';
Line 13379  sub modify_coursedefaults { Line 21688  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 13596  sub modify_selfenrollment { Line 21905  sub modify_selfenrollment {
                         $resulttext .= '</ul></li>';                           $resulttext .= '</ul></li>'; 
                     }                      }
                 }                  }
                 if ((exists($changes{'admin'})) || (exists($changes{'default'}))) {              }
                     my $cachetime = 24*60*60;              if ((exists($changes{'admin'})) || (exists($changes{'default'}))) {
                     &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);                  my $cachetime = 24*60*60;
                     if (ref($lastactref) eq 'HASH') {                  &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
                         $lastactref->{'domdefaults'} = 1;                  if (ref($lastactref) eq 'HASH') {
                     }                      $lastactref->{'domdefaults'} = 1;
                 }                  }
             }              }
             $resulttext .= '</ul>';              $resulttext .= '</ul>';
Line 13615  sub modify_selfenrollment { Line 21924  sub modify_selfenrollment {
     return $resulttext;      return $resulttext;
 }  }
   
   sub modify_wafproxy {
       my ($dom,$action,$lastactref,%domconfig) = @_;
       my %servers = &Apache::lonnet::internet_dom_servers($dom);
       my (%othercontrol,%canset,%values,%curralias,%currsaml,%currvalue,@warnings,
           %wafproxy,%changes,%expirecache,%expiresaml);
       foreach my $server (sort(keys(%servers))) {
           my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server});
           if ($serverhome eq $server) {
               my $serverdom = &Apache::lonnet::host_domain($server);
               if ($serverdom eq $dom) {
                   $canset{$server} = 1;
               }
           }
       }
       if (ref($domconfig{'wafproxy'}) eq 'HASH') {
           %{$values{$dom}} = ();
           if (ref($domconfig{'wafproxy'}{'alias'}) eq 'HASH') {
               %curralias = %{$domconfig{'wafproxy'}{'alias'}};
           }
           if (ref($domconfig{'wafproxy'}{'saml'}) eq 'HASH') {
               %currsaml = %{$domconfig{'wafproxy'}{'saml'}};
           }
           foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
               $currvalue{$item} = $domconfig{'wafproxy'}{$item};
           }
       }
       my $output;
       if (keys(%canset)) {
           %{$wafproxy{'alias'}} = ();
           %{$wafproxy{'saml'}} = ();
           foreach my $key (sort(keys(%canset))) {
               if ($env{'form.wafproxy_'.$dom}) {
                   $wafproxy{'alias'}{$key} = $env{'form.wafproxy_alias_'.$key};
                   $wafproxy{'alias'}{$key} =~ s/^\s+|\s+$//g;
                   if ($wafproxy{'alias'}{$key} ne $curralias{$key}) {
                       $changes{'alias'} = 1;
                   }
                   if ($env{'form.wafproxy_alias_saml_'.$key}) {
                       $wafproxy{'saml'}{$key} = 1;
                   }
                   if ($wafproxy{'saml'}{$key} ne $currsaml{$key}) {
                       $changes{'saml'} = 1;
                   }
               } else {
                   $wafproxy{'alias'}{$key} = '';
                   $wafproxy{'saml'}{$key} = '';
                   if ($curralias{$key}) {
                       $changes{'alias'} = 1;
                   }
                   if ($currsaml{$key}) {
                       $changes{'saml'} = 1;
                   }
               }
               if ($wafproxy{'alias'}{$key} eq '') {
                   if ($curralias{$key}) {
                       $expirecache{$key} = 1;
                   }
                   delete($wafproxy{'alias'}{$key});
               }
               if ($wafproxy{'saml'}{$key} eq '') {
                   if ($currsaml{$key}) {
                       $expiresaml{$key} = 1;
                   }
                   delete($wafproxy{'saml'}{$key});
               }
           }
           unless (keys(%{$wafproxy{'alias'}})) {
               delete($wafproxy{'alias'});
           }
           unless (keys(%{$wafproxy{'saml'}})) {
               delete($wafproxy{'saml'});
           }
           # Localization for values in %warn occurs in &mt() calls separately.
           my %warn = (
                        trusted => 'trusted IP range(s)',
                        vpnint => 'internal IP range(s) for VPN sessions(s)',
                        vpnext => 'IP range(s) for backend WAF connections',
                      );
           foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
               my $possible = $env{'form.wafproxy_'.$item};
               $possible =~ s/^\s+|\s+$//g;
               if ($possible ne '') {
                   if ($item eq 'remoteip') {
                       if ($possible =~ /^[mhn]$/) {
                           $wafproxy{$item} = $possible;
                       }
                   } elsif ($item eq 'ipheader') {
                       if ($wafproxy{'remoteip'} eq 'h') {
                           $wafproxy{$item} = $possible;
                       }
                   } elsif ($item eq 'sslopt') {
                       if ($possible =~ /^0|1$/) {
                           $wafproxy{$item} = $possible;
                       }
                   } else {
                       my (@ok,$count);
                       if (($item eq 'vpnint') || ($item eq 'vpnext')) {
                           unless ($env{'form.wafproxy_vpnaccess'}) {
                               $possible = '';
                           }
                       } elsif ($item eq 'trusted') {
                           unless ($wafproxy{'remoteip'} eq 'h') {
                               $possible = '';
                           }
                       }
                       unless ($possible eq '') {
                           $possible =~ s/[\r\n]+/\s/g;
                           $possible =~ s/\s*-\s*/-/g;
                           $possible =~ s/\s+/,/g;
                           $possible =~ s/,+/,/g;
                       }
                       $count = 0;
                       if ($possible ne '') {
                           foreach my $poss (split(/\,/,$possible)) {
                               $count ++;
                               $poss = &validate_ip_pattern($poss);
                               if ($poss ne '') {
                                   push(@ok,$poss);
                               }
                           }
                           my $diff = $count - scalar(@ok);
                           if ($diff) {
                               push(@warnings,'<li>'.
                                    &mt('[quant,_1,IP] invalid and excluded from saved value for [_2]',
                                        $diff,$warn{$item}).
                                    '</li>');
                           }
                           if (@ok) {
                               my @cidr_list;
                               foreach my $item (@ok) {
                                   @cidr_list = &Net::CIDR::cidradd($item,@cidr_list);
                               }
                               $wafproxy{$item} = join(',',@cidr_list);
                           }
                       }
                   }
                   if ($wafproxy{$item} ne $currvalue{$item}) {
                       $changes{$item} = 1;
                   }
               } elsif ($currvalue{$item}) {
                   $changes{$item} = 1;
               } 
           }
       } else {
           if (keys(%curralias)) {
               $changes{'alias'} = 1;
           }
           if (keys(%currsaml)) {
               $changes{'saml'} = 1;
           } 
           if (keys(%currvalue)) {
               foreach my $key (keys(%currvalue)) {
                   $changes{$key} = 1;
               }
           }
       }
       if (keys(%changes)) {
           my %defaultshash = (
                                 wafproxy => \%wafproxy,
                              ); 
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               my $cachetime = 24*60*60;
               my (%domdefaults,$updatedomdefs);
               foreach my $item ('ipheader','trusted','vpnint','vpnext','sslopt') {
                   if ($changes{$item}) {
                       unless ($updatedomdefs) {
                           %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                           $updatedomdefs = 1;
                       }
                       if ($wafproxy{$item}) {
                           $domdefaults{'waf_'.$item} = $wafproxy{$item};
                       } elsif (exists($domdefaults{'waf_'.$item})) {
                           delete($domdefaults{'waf_'.$item});
                       } 
                   }
               }
               if ($updatedomdefs) {
                   &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'domdefaults'} = 1;
                   }
               }
               if ((exists($wafproxy{'alias'})) || (keys(%expirecache))) {
                   my %updates = %expirecache;
                   foreach my $key (keys(%expirecache)) {
                       &Apache::lonnet::devalidate_cache_new('proxyalias',$key);
                   }
                   if (ref($wafproxy{'alias'}) eq 'HASH') {
                       my $cachetime = 24*60*60;
                       foreach my $key (keys(%{$wafproxy{'alias'}})) {
                           $updates{$key} = 1;
                           &Apache::lonnet::do_cache_new('proxyalias',$key,$wafproxy{'alias'}{$key},
                                                         $cachetime);
                       }
                   }
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'proxyalias'} = \%updates;
                   }
               }
               if ((exists($wafproxy{'saml'})) || (keys(%expiresaml))) {
                   my %samlupdates = %expiresaml;
                   foreach my $key (keys(%expiresaml)) {
                       &Apache::lonnet::devalidate_cache_new('proxysaml',$key);
                   }
                   if (ref($wafproxy{'saml'}) eq 'HASH') {
                       my $cachetime = 24*60*60;
                       foreach my $key (keys(%{$wafproxy{'saml'}})) {
                           $samlupdates{$key} = 1;
                           &Apache::lonnet::do_cache_new('proxysaml',$key,$wafproxy{'saml'}{$key},
                                                         $cachetime);
                       }
                   }
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'proxysaml'} = \%samlupdates;
                   }
               }
               $output = &mt('Changes were made to Web Application Firewall/Reverse Proxy').'<ul>';
               foreach my $item ('alias','saml','remoteip','ipheader','trusted','vpnint','vpnext','sslopt') {
                   if ($changes{$item}) {
                       if ($item eq 'alias') {
                           my $numaliased = 0;
                           if (ref($wafproxy{'alias'}) eq 'HASH') {
                               my $shown;
                               if (keys(%{$wafproxy{'alias'}})) {
                                   foreach my $server (sort(keys(%{$wafproxy{'alias'}}))) {
                                       $shown .= '<li>'.&mt('[_1] aliased by [_2]',
                                                            &Apache::lonnet::hostname($server),
                                                            $wafproxy{'alias'}{$server}).'</li>';
                                       $numaliased ++;
                                   }
                                   if ($numaliased) {
                                       $output .= '<li>'.&mt('Aliases for hostnames set to: [_1]',
                                                             '<ul>'.$shown.'</ul>').'</li>';
                                   }
                               }
                           }
                           unless ($numaliased) {
                               $output .= '<li>'.&mt('Aliases deleted for hostnames').'</li>';
                           }
                       } elsif ($item eq 'saml') {
                           my $shown; 
                           if (ref($wafproxy{'saml'}) eq 'HASH') {
                               if (keys(%{$wafproxy{'saml'}})) {
                                   $shown = join(', ',sort(keys(%{$wafproxy{'saml'}})));
                               }
                           }
                           if ($shown) {
                               $output .= '<li>'.&mt('Alias used by SSO Auth for: [_1]',
                                                     $shown).'</li>';
                           } else {
                               $output .= '<li>'.&mt('No alias used for SSO Auth').'</li>';
                           }
                       } else {
                           if ($item eq 'remoteip') {
                               my %ip_methods = &remoteip_methods();
                               if ($wafproxy{$item} =~ /^[mh]$/) {
                                   $output .= '<li>'.&mt("Method for determining user's IP set to: [_1]",
                                                         $ip_methods{$wafproxy{$item}}).'</li>';
                               } else {
                                   if (($env{'form.wafproxy_'.$dom}) && (ref($wafproxy{'alias'}) eq 'HASH')) {
                                       $output .= '<li>'.&mt("No method in use to get user's real IP (will report IP used by WAF).").
                                                  '</li>';
                                   } else {
                                       $output .= '<li>'.&mt('WAF/Reverse Proxy not in use').'</li>';
                                   }
                               }
                           } elsif ($item eq 'ipheader') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('Request header with remote IP set to: [_1]',
                                                         $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('Request header with remote IP deleted').'</li>';
                               }
                           } elsif ($item eq 'trusted') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('Trusted IP range(s) set to: [_1]',
                                                         $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('Trusted IP range(s) deleted').'</li>';
                               }
                           } elsif ($item eq 'vpnint') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('Internal IP Range(s) for VPN sessions set to: [_1]',
                                                          $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('Internal IP Range(s) for VPN sessions deleted').'</li>';
                               }
                           } elsif ($item eq 'vpnext') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('IP Range(s) for backend WAF connections set to: [_1]',
                                                          $wafproxy{$item}).'</li>';
                               } else {
                                   $output .= '<li>'.&mt('IP Range(s) for backend WAF connections deleted').'</li>';
                               }
                           } elsif ($item eq 'sslopt') {
                               if ($wafproxy{$item}) {
                                   $output .= '<li>'.&mt('WAF/Reverse Proxy expected to forward requests to https on LON-CAPA node, regardless of original protocol in web browser (http or https).').'</li>';
                               } else {
                                   $output .= '<li>'.&mt('WAF/Reverse Proxy expected to preserve original protocol in web browser (either http or https) when forwarding to LON-CAPA node.').'</li>';
                               }
                           }
                       }
                   }
               }
           } else {
               $output = '<span class="LC_error">'.
                         &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } elsif (keys(%canset)) {
           $output = &mt('No changes made to Web Application Firewall/Reverse Proxy settings');
       }
       if (@warnings) {
           $output .= '<br />'.&mt('Warnings:').'<ul>'.
                          join("\n",@warnings).'</ul>';
       }
       return $output;
   }
   
   sub validate_ip_pattern {
       my ($pattern) = @_;
       if ($pattern =~ /^([^-]+)\-([^-]+)$/) {
           my ($start,$end) = ($1,$2);
           if ((&Net::CIDR::cidrvalidate($start)) && (&Net::CIDR::cidrvalidate($end))) {
               if (($start !~ m{/}) && ($end !~ m{/})) {
                   return $start.'-'.$end;
               }
           }
       } elsif ($pattern ne '') {
           $pattern = &Net::CIDR::cidrvalidate($pattern);
           if ($pattern ne '') {
               return $pattern;
           }
       }
       return;
   }
   
 sub modify_usersessions {  sub modify_usersessions {
     my ($dom,$lastactref,%domconfig) = @_;      my ($dom,$lastactref,%domconfig) = @_;
     my @hostingtypes = ('version','excludedomain','includedomain');      my @hostingtypes = ('version','excludedomain','includedomain');
Line 13626  sub modify_usersessions { Line 22273  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 13782  sub modify_usersessions { Line 22429  sub modify_usersessions {
         }          }
     }      }
     $defaultshash{'usersessions'}{'offloadnow'} = {};      $defaultshash{'usersessions'}{'offloadnow'} = {};
       $defaultshash{'usersessions'}{'offloadoth'} = {};
     my @offloadnow = &Apache::loncommon::get_env_multiple('form.offloadnow');      my @offloadnow = &Apache::loncommon::get_env_multiple('form.offloadnow');
     my @okoffload;      my @okoffload;
     if (@offloadnow) {      if (@offloadnow) {
Line 13798  sub modify_usersessions { Line 22446  sub modify_usersessions {
             }              }
         }          }
     }      }
       my @offloadoth = &Apache::loncommon::get_env_multiple('form.offloadoth');
       my @okoffloadoth;
       if (@offloadoth) {
           foreach my $server (@offloadoth) {
               if (&Apache::lonnet::hostname($server) ne '') {
                   unless (grep(/^\Q$server\E$/,@okoffloadoth)) {
                       push(@okoffloadoth,$server);
                   }
               }
           }
           if (@okoffloadoth) {
               foreach my $lonhost (@okoffloadoth) {
                   $defaultshash{'usersessions'}{'offloadoth'}{$lonhost} = 1;
               }
           }
       }
     if (ref($domconfig{'usersessions'}) eq 'HASH') {      if (ref($domconfig{'usersessions'}) eq 'HASH') {
         if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') {          if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') {
             if (ref($changes{'spares'}) eq 'HASH') {              if (ref($changes{'spares'}) eq 'HASH') {
Line 13808  sub modify_usersessions { Line 22472  sub modify_usersessions {
         } else {          } else {
             $savespares = 1;              $savespares = 1;
         }          }
         if (ref($domconfig{'usersessions'}{'offloadnow'}) eq 'HASH') {          foreach my $offload ('offloadnow','offloadoth') {
             foreach my $lonhost (keys(%{$domconfig{'usersessions'}{'offloadnow'}})) {              if (ref($domconfig{'usersessions'}{$offload}) eq 'HASH') {
                 unless ($defaultshash{'usersessions'}{'offloadnow'}{$lonhost}) {                  foreach my $lonhost (keys(%{$domconfig{'usersessions'}{$offload}})) {
                     $changes{'offloadnow'} = 1;                      unless ($defaultshash{'usersessions'}{$offload}{$lonhost}) {
                     last;                          $changes{$offload} = 1;
                 }  
             }  
             unless ($changes{'offloadnow'}) {  
                 foreach my $lonhost (keys(%{$defaultshash{'usersessions'}{'offloadnow'}})) {  
                     unless ($domconfig{'usersessions'}{'offloadnow'}{$lonhost}) {  
                         $changes{'offloadnow'} = 1;  
                         last;                          last;
                     }                      }
                 }                  }
             }                  unless ($changes{$offload}) {
         } elsif (@okoffload) {                      foreach my $lonhost (keys(%{$defaultshash{'usersessions'}{$offload}})) {
                           unless ($domconfig{'usersessions'}{$offload}{$lonhost}) {
                               $changes{$offload} = 1;
                               last;
                           }
                       }
                   }
               } else {
                   if (($offload eq 'offloadnow') && (@okoffload)) {
                        $changes{'offloadnow'} = 1;
                   }
                   if (($offload eq 'offloadoth') && (@okoffloadoth)) {
                       $changes{'offloadoth'} = 1;
                   }
               } 
           }
       } else {
           if (@okoffload) {
             $changes{'offloadnow'} = 1;              $changes{'offloadnow'} = 1;
         }          }
     } elsif (@okoffload) {          if (@okoffloadoth) {
         $changes{'offloadnow'} = 1;              $changes{'offloadoth'} = 1;
           }
     }      }
     my $nochgmsg = &mt('No changes made to settings for user session hosting/offloading.');      my $nochgmsg = &mt('No changes made to settings for user session hosting/offloading.');
     if ((keys(%changes) > 0) || ($savespares)) {      if ((keys(%changes) > 0) || ($savespares)) {
Line 13844  sub modify_usersessions { Line 22520  sub modify_usersessions {
                 if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {                  if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {
                     $domdefaults{'offloadnow'} = $defaultshash{'usersessions'}{'offloadnow'};                      $domdefaults{'offloadnow'} = $defaultshash{'usersessions'}{'offloadnow'};
                 }                  }
                   if (ref($defaultshash{'usersessions'}{'offloadoth'}) eq 'HASH') {
                       $domdefaults{'offloadoth'} = $defaultshash{'usersessions'}{'offloadoth'};
                   }
             }              }
             my $cachetime = 24*60*60;              my $cachetime = 24*60*60;
             &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);              &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
Line 13884  sub modify_usersessions { Line 22563  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 13899  sub modify_usersessions { Line 22582  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 13917  sub modify_usersessions { Line 22602  sub modify_usersessions {
                 if ($changes{'offloadnow'}) {                  if ($changes{'offloadnow'}) {
                     if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {                      if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') {
                         if (keys(%{$defaultshash{'usersessions'}{'offloadnow'}}) > 0) {                          if (keys(%{$defaultshash{'usersessions'}{'offloadnow'}}) > 0) {
                             $resulttext .= '<li>'.&mt('Switch active users on next access, for server(s):').'<ul>';                              $resulttext .= '<li>'.&mt('Switch any active user on next access, for server(s):').'<ul>';
                             foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadnow'}}))) {                              foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadnow'}}))) {
                                 $resulttext .= '<li>'.$lonhost.'</li>';                                  $resulttext .= '<li>'.$lonhost.'</li>';
                             }                              }
                             $resulttext .= '</ul>';                              $resulttext .= '</ul>';
                         } else {                          } else {
                             $resulttext .= '<li>'.&mt('No servers now set to switch active users on next access.');                              $resulttext .= '<li>'.&mt('No servers now set to switch any active user on next access.');
                           }
                       } else {
                           $resulttext .= '<li>'.&mt('No servers now set to switch any active user on next access.').'</li>';
                       }
                   }
                   if ($changes{'offloadoth'}) {
                       if (ref($defaultshash{'usersessions'}{'offloadoth'}) eq 'HASH') {
                           if (keys(%{$defaultshash{'usersessions'}{'offloadoth'}}) > 0) {
                               $resulttext .= '<li>'.&mt('Switch other institutions on next access, for server(s):').'<ul>';
                               foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadoth'}}))) {
                                   $resulttext .= '<li>'.$lonhost.'</li>';
                               }
                               $resulttext .= '</ul>';
                           } else {
                               $resulttext .= '<li>'.&mt('No servers now set to switch other institutions on next access.');
                           }
                       } else {
                           $resulttext .= '<li>'.&mt('No servers now set to switch other institutions on next access.').'</li>';
                       }
                   }
                   $resulttext .= '</ul>';
               } else {
                   $resulttext = $nochgmsg;
               }
           } else {
               $resulttext = '<span class="LC_error">'.
                             &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } else {
           $resulttext = $nochgmsg;
       }
       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 {                      } else {
                         $resulttext .= '<li>'.&mt('No servers now set to switch active users on next access.').'</li>';                          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>';                  $resulttext .= '</ul>';
Line 13954  sub modify_loadbalancing { Line 22974  sub modify_loadbalancing {
     my @sparestypes = ('primary','default');      my @sparestypes = ('primary','default');
     my %typetitles = &sparestype_titles();      my %typetitles = &sparestype_titles();
     my $resulttext;      my $resulttext;
     my (%currbalancer,%currtargets,%currrules,%existing);      my (%currbalancer,%currtargets,%currrules,%existing,%currcookies);
     if (ref($domconfig{'loadbalancing'}) eq 'HASH') {      if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
         %existing = %{$domconfig{'loadbalancing'}};          %existing = %{$domconfig{'loadbalancing'}};
     }      }
     &get_loadbalancers_config(\%servers,\%existing,\%currbalancer,      &get_loadbalancers_config(\%servers,\%existing,\%currbalancer,
                               \%currtargets,\%currrules);                                \%currtargets,\%currrules,\%currcookies);
     my ($saveloadbalancing,%defaultshash,%changes);      my ($saveloadbalancing,%defaultshash,%changes);
     my ($alltypes,$othertypes,$titles) =      my ($alltypes,$othertypes,$titles) =
         &loadbalancing_titles($dom,$intdom,$usertypes,$types);          &loadbalancing_titles($dom,$intdom,$usertypes,$types);
Line 14011  sub modify_loadbalancing { Line 23031  sub modify_loadbalancing {
             }              }
             $defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype} = \@offloadto;              $defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype} = \@offloadto;
         }          }
           if ($env{'form.loadbalancing_cookie_'.$i}) {
               $defaultshash{'loadbalancing'}{$balancer}{'cookie'} = 1;
               if (exists($currbalancer{$balancer})) { 
                   unless ($currcookies{$balancer}) {
                       $changes{'curr'}{$balancer}{'cookie'} = 1;
                   }
               }
           } elsif (exists($currbalancer{$balancer})) {
               if ($currcookies{$balancer}) {
                   $changes{'curr'}{$balancer}{'cookie'} = 1;
               }
           }
         if (ref($currtargets{$balancer}) eq 'HASH') {          if (ref($currtargets{$balancer}) eq 'HASH') {
             foreach my $sparetype (@sparestypes) {              foreach my $sparetype (@sparestypes) {
                 if (ref($currtargets{$balancer}{$sparetype}) eq 'ARRAY') {                  if (ref($currtargets{$balancer}{$sparetype}) eq 'ARRAY') {
Line 14164  sub modify_loadbalancing { Line 23196  sub modify_loadbalancing {
                                 }                                  }
                             }                              }
                         }                          }
                         if (keys(%toupdate)) {                          if ($changes{'curr'}{$balancer}{'cookie'}) {
                             my %thismachine;                              if ($currcookies{$balancer}) {
                             my $updatedhere;                                  $resulttext .= '<li>'.&mt('Load Balancer: [_1] -- cookie use disabled',
                             my $cachetime = 60*60*24;                                                            $balancer).'</li>';
                             map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();                              } else {
                             foreach my $lonhost (keys(%toupdate)) {                                  $resulttext .= '<li>'.&mt('Load Balancer: [_1] -- cookie use enabled',
                                 if ($thismachine{$lonhost}) {                                                            $balancer).'</li>';
                                     unless ($updatedhere) {                              }
                                         &Apache::lonnet::do_cache_new('loadbalancing',$dom,                          }
                                                                       $defaultshash{'loadbalancing'},                      }
                                                                       $cachetime);                  }
                                         $updatedhere = 1;                  if (keys(%toupdate)) {
                                     }                      my %thismachine;
                                 } else {                      my $updatedhere;
                                     my $cachekey = &escape('loadbalancing').':'.&escape($dom);                      my $cachetime = 60*60*24;
                                     &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]);                      map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();
                                 }                      foreach my $lonhost (keys(%toupdate)) {
                           if ($thismachine{$lonhost}) {
                               unless ($updatedhere) {
                                   &Apache::lonnet::do_cache_new('loadbalancing',$dom,
                                                                 $defaultshash{'loadbalancing'},
                                                                 $cachetime);
                                   $updatedhere = 1;
                             }                              }
                           } else {
                               my $cachekey = &escape('loadbalancing').':'.&escape($dom);
                               &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]);
                         }                          }
                     }                      }
                 }                  }
Line 14393  sub lonbalance_targets_js { Line 23434  sub lonbalance_targets_js {
     }      }
     push(@alltypes,'default','_LC_adv','_LC_author','_LC_internetdom','_LC_external');      push(@alltypes,'default','_LC_adv','_LC_author','_LC_internetdom','_LC_external');
     $allinsttypes = join("','",@alltypes);      $allinsttypes = join("','",@alltypes);
     my (%currbalancer,%currtargets,%currrules,%existing);      my (%currbalancer,%currtargets,%currrules,%existing,%currcookies);
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         %existing = %{$settings};          %existing = %{$settings};
     }      }
     &get_loadbalancers_config($servers,\%existing,\%currbalancer,      &get_loadbalancers_config($servers,\%existing,\%currbalancer,
                               \%currtargets,\%currrules);                                \%currtargets,\%currrules,\%currcookies);
     my $balancers = join("','",sort(keys(%currbalancer)));      my $balancers = join("','",sort(keys(%currbalancer)));
     return <<"END";      return <<"END";
   
Line 14678  function balancerChange(balnum,baltotal, Line 23719  function balancerChange(balnum,baltotal,
 END  END
 }  }
   
   
 sub new_spares_js {  sub new_spares_js {
     my @sparestypes = ('primary','default');      my @sparestypes = ('primary','default');
     my $types = join("','",@sparestypes);      my $types = join("','",@sparestypes);
Line 14751  function updateNewSpares(formname,lonhos Line 23793  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 14883  function toggleDisplay(domForm,caller) { Line 23925  function toggleDisplay(domForm,caller) {
         var optionsElement = domForm.coursecredits;          var optionsElement = domForm.coursecredits;
         var checkval = 1;          var checkval = 1;
         var dispval = 'block';          var dispval = 'block';
           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 14893  function toggleDisplay(domForm,caller) { Line 23936  function toggleDisplay(domForm,caller) {
             optionsElement = domForm.canclone;              optionsElement = domForm.canclone;
             checkval = 'instcode';              checkval = 'instcode';
         }          }
           if (selfcreateRegExp.test(caller)) {
               optionsElement = domForm.elements[caller];
               checkval = 'other';
               dispval = 'inline'
           }
         if (optionsElement.length) {          if (optionsElement.length) {
             var currval;              var currval;
             for (var i=0; i<optionsElement.length; i++) {              for (var i=0; i<optionsElement.length; i++) {
Line 14934  sub devalidate_remote_domconfs { Line 23982  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','ltitools','usersessions','directorysrch');      my @posscached = ('domainconfig','domdefaults','ltitools','usersessions',
                         'directorysrch','passwdconf','cats','proxyalias','proxysaml',
                         'ipaccess','trust');
       my %cache_by_lonhost;
       if (exists($cachekeys->{'samllanding'})) {
           if (ref($cachekeys->{'samllanding'}) eq 'HASH') {
               my %landing = %{$cachekeys->{'samllanding'}};
               my %domservers = &Apache::lonnet::get_servers($dom);
               if (keys(%domservers)) {
                   foreach my $server (keys(%domservers)) {
                       my @cached;
                       next if ($thismachine{$server});
                       if ($landing{$server}) {
                           push(@cached,&escape('samllanding').':'.&escape($server));
                       }
                       if (@cached) {
                           $cache_by_lonhost{$server} = \@cached;
                       }
                   }
               }
           }
       }
     if (keys(%servers)) {      if (keys(%servers)) {
         foreach my $server (keys(%servers)) {          foreach my $server (keys(%servers)) {
             next if ($thismachine{$server});              next if ($thismachine{$server});
             my @cached;              my @cached;
             foreach my $name (@posscached) {              foreach my $name (@posscached) {
                 if ($cachekeys->{$name}) {                  if ($cachekeys->{$name}) {
                     push(@cached,&escape($name).':'.&escape($dom));                      if (($name eq 'proxyalias') || ($name eq 'proxysaml')) {
                           if (ref($cachekeys->{$name}) eq 'HASH') {
                               foreach my $key (keys(%{$cachekeys->{$name}})) {
                                   push(@cached,&escape($name).':'.&escape($key));
                               }
                           }
                       } else {
                           push(@cached,&escape($name).':'.&escape($dom));
                       }
                 }                  }
             }              }
               if ((exists($cache_by_lonhost{$server})) &&
                   (ref($cache_by_lonhost{$server}) eq 'ARRAY')) {
                   push(@cached,@{$cache_by_lonhost{$server}});
               }
             if (@cached) {              if (@cached) {
                 &Apache::lonnet::remote_devalidate_cache($server,\@cached);                  &Apache::lonnet::remote_devalidate_cache($server,\@cached);
             }              }

Removed from v.1.160.6.84.2.8  
changed lines
  Added in v.1.416


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