Diff for /loncom/interface/domainprefs.pm between versions 1.160.6.118.2.27 and 1.438

version 1.160.6.118.2.27, 2025/03/15 01:32:13 version 1.438, 2024/04/14 17:12:27
Line 103  $datatable  - HTML containing form eleme Line 103  $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, textbook, and lti).  (official, unofficial, community, textbook, placement, and lti).  
 In each case the radio buttons 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).
Line 170  use Apache::courseprefs(); Line 170  use Apache::courseprefs();
 use LONCAPA qw(:DEFAULT :match);  use LONCAPA qw(:DEFAULT :match);
 use LONCAPA::Enrollment;  use LONCAPA::Enrollment;
 use LONCAPA::lonauthcgi();  use LONCAPA::lonauthcgi();
   use LONCAPA::SSL;
 use File::Copy;  use File::Copy;
 use Locale::Language;  use Locale::Language;
 use DateTime::TimeZone;  use DateTime::TimeZone;
Line 220  sub handler { Line 221  sub handler {
                 'serverstatuses','requestcourses','helpsettings',                  'serverstatuses','requestcourses','helpsettings',
                 'coursedefaults','usersessions','loadbalancing',                  'coursedefaults','usersessions','loadbalancing',
                 'requestauthor','selfenrollment','inststatus',                  'requestauthor','selfenrollment','inststatus',
                 'passwords','ltitools','toolsec','lti','ltisec',                  'ltitools','toolsec','ssl','trust','lti','ltisec',
                 'wafproxy','ipaccess','authordefaults'],$dom);                  'privacy','passwords','proctoring','wafproxy',
                   'ipaccess','authordefaults'],$dom);
     my %encconfig =      my %encconfig =
         &Apache::lonnet::get_dom('encconfig',['ltitools','lti','linkprot'],$dom,undef,1);          &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1);
     my ($checked_is_home,$is_home);      my ($checked_is_home,$is_home);
     if (ref($domconfig{'ltitools'}) eq 'HASH') {      if (ref($domconfig{'ltitools'}) eq 'HASH') {
         if (ref($encconfig{'ltitools'}) eq 'HASH') {          if (ref($encconfig{'ltitools'}) eq 'HASH') {
Line 286  sub handler { Line 288  sub handler {
             }              }
         }          }
     }      }
       if (ref($domconfig{'proctoring'}) eq 'HASH') {
           if (ref($encconfig{'proctoring'}) eq 'HASH') {
               foreach my $provider (keys(%{$domconfig{'proctoring'}})) {
                   if ((ref($domconfig{'proctoring'}{$provider}) eq 'HASH') &&
                       (ref($encconfig{'proctoring'}{$provider}) eq 'HASH')) {
                       foreach my $item ('key','secret') {
                           $domconfig{'proctoring'}{$provider}{$item} = $encconfig{'proctoring'}{$provider}{$item};
                       }
                   }
               }
           }
       }
     my @prefs_order = ('rolecolors','login','ipaccess','defaults','wafproxy','passwords',      my @prefs_order = ('rolecolors','login','ipaccess','defaults','wafproxy','passwords',
                        'quotas','autoenroll','autoupdate','autocreate','directorysrch',                         'quotas','autoenroll','autoupdate','autocreate','directorysrch',
                        'contacts','usercreation','selfcreation','usermodification',                         'contacts','privacy','usercreation','selfcreation',
                        'scantron','requestcourses','requestauthor','coursecategories',                         'usermodification','scantron','requestcourses','requestauthor',
                        'serverstatuses','helpsettings','coursedefaults',                         'coursecategories','serverstatuses','helpsettings','coursedefaults',
                        'authordefaults','ltitools','selfenrollment','usersessions','lti');                         'authordefaults','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 457  sub handler { Line 472  sub handler {
                     header => [{col1 => 'Target user has role',                      header => [{col1 => 'Target user has role',
                                 col2 => 'User information updatable in author context'},                                  col2 => 'User information updatable in author context'},
                                {col1 => 'Target user has role',                                 {col1 => 'Target user has role',
                                 col2 => 'User information updatable by co-author manager'},  
                                {col1 => 'Target user has role',  
                                 col2 => 'User information updatable in course context'}],                                  col2 => 'User information updatable in course context'}],
                     print => \&print_usermodification,                      print => \&print_usermodification,
                     modify => \&modify_usermodification,                      modify => \&modify_usermodification,
Line 554  sub handler { Line 567  sub handler {
                   print => \&print_selfenrollment,                    print => \&print_selfenrollment,
                   modify => \&modify_selfenrollment,                    modify => \&modify_selfenrollment,
                  },                   },
           'privacy' => 
                    {text   => 'Role assignments and user privacy',
                     help   => 'Domain_Configuration_User_Privacy',
                     header => [{col1 => 'Role assigned in different domain',
                                 col2 => 'Approval options'},
                                {col1 => 'Role assigned in different domain to user of type',
                                 col2 => 'User information available in that domain'},
                                {col1 => "Role assigned in user's domain",
                                 col2 => 'Information viewable by privileged user'},
                                {col1 => "Role assigned in user's domain",
                                 col2 => 'Information viewable by unprivileged user'}],
                     print => \&print_privacy,
                     modify => \&modify_privacy,
                    },
         'usersessions' =>          'usersessions' =>
                  {text  => 'User session hosting/offloading',                   {text  => 'User session hosting/offloading',
                   help  => 'Domain_Configuration_User_Sessions',                    help  => 'Domain_Configuration_User_Sessions',
Line 589  sub handler { Line 616  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' =>          'lti' =>
                  {text => 'LTI Link Protection and LTI Consumers',                   {text => 'LTI Link Protection and LTI Consumers',
                   help => 'Domain_Configuration_LTI_Provider',                    help => 'Domain_Configuration_LTI_Provider',
Line 812  sub process_changes { Line 885  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') {      } elsif ($action eq 'lti') {
         $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig);          $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig);
       } elsif ($action eq 'privacy') {
           $output = &modify_privacy($dom,$lastactref,%domconfig);
     } elsif ($action eq 'passwords') {      } elsif ($action eq 'passwords') {
         $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);          $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig);
     } elsif ($action eq 'wafproxy') {      } elsif ($action eq 'wafproxy') {
Line 857  sub print_config_box { Line 938  sub print_config_box {
     } elsif ($action eq 'lti') {      } elsif ($action eq 'lti') {
         $output .= &passwords_javascript('ltisecrets')."\n".          $output .= &passwords_javascript('ltisecrets')."\n".
                    &lti_javascript($dom,$settings);                     &lti_javascript($dom,$settings);
       } elsif ($action eq 'proctoring') {
           $output .= &proctoring_javascript($settings);
     } elsif ($action eq 'wafproxy') {      } elsif ($action eq 'wafproxy') {
         $output .= &wafproxy_javascript($dom);          $output .= &wafproxy_javascript($dom);
     } elsif ($action eq 'autoupdate') {      } elsif ($action eq 'autoupdate') {
Line 908  sub print_config_box { Line 991  sub print_config_box {
         $rowtotal ++;          $rowtotal ++;
         if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||          if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') ||
             ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||              ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') ||
             ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'directorysrch') ||              ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') ||
             ($action eq 'helpsettings') || ($action eq 'contacts') || ($action eq 'wafproxy') ||              ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') ||
               ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') ||
             ($action eq 'lti') || ($action eq 'ltitools') || ($action eq 'authordefaults')) {              ($action eq 'lti') || ($action eq 'ltitools') || ($action eq 'authordefaults')) {
             $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);              $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'passwords') {          } elsif ($action eq 'passwords') {
Line 944  sub print_config_box { Line 1028  sub print_config_box {
             $rowtotal ++;              $rowtotal ++;
         if (($action eq 'autoupdate') || ($action eq 'usercreation') ||          if (($action eq 'autoupdate') || ($action eq 'usercreation') ||
             ($action eq 'selfcreation') || ($action eq 'selfenrollment') ||              ($action eq 'selfcreation') || ($action eq 'selfenrollment') ||
             ($action eq 'usersessions') || ($action eq 'coursecategories') ||              ($action eq 'usersessions') || ($action eq 'coursecategories') || 
             ($action eq 'contacts') || ($action eq 'passwords') ||               ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'defaults') ||
             ($action eq 'defaults') || ($action eq 'lti') ||              ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti') ||
             ($action eq 'ltitools') || ($action eq 'usermodification')) {              ($action eq 'ltitools')) {
             if ($action eq 'coursecategories') {              if ($action eq 'coursecategories') {
                 $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal);                  $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal);
                 $colspan = ' colspan="2"';                  $colspan = ' colspan="2"';
               } elsif ($action eq 'trust') {
                   $output .= $item->{'print'}->('shared',$dom,$settings,\$rowtotal);
             } elsif ($action eq 'passwords') {              } elsif ($action eq 'passwords') {
                 $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal);                  $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal);
             } elsif ($action eq 'lti') {              } elsif ($action eq 'lti') {
Line 969  sub print_config_box { Line 1055  sub print_config_box {
             } else {              } else {
                 $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal);                  $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal);
             }              }
             my $hdridx = 2;              if ($action eq 'trust') {
             if ($action eq 'lti') {                  $output .= '
                 $hdridx = 3;              </table>
             }            </td>
             $output .= '           </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 {
                   my $hdridx = 2;
                   if ($action eq 'lti') {
                       $hdridx = 3;
                   }
                   $output .= '
            </table>             </table>
           </td>            </td>
          </tr>           </tr>
Line 984  sub print_config_box { Line 1099  sub print_config_box {
               <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td>                <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td>
               <td class="LC_right_item">'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td>                <td class="LC_right_item">'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td>
              </tr>'."\n";               </tr>'."\n";
             if ($action eq 'coursecategories') {                  if ($action eq 'coursecategories') {
                 $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal);                      $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal);
             } elsif (($action eq 'contacts') || ($action eq 'passwords')) {                  } elsif (($action eq 'contacts') || ($action eq 'privacy') || 
                 if ($action eq 'passwords') {                           ($action eq 'passwords') || ($action eq 'lti')) {
                     $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal);                      if ($action eq 'passwords') {
                 } else {                          $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal);
                     $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal);                      } else {
                 }                          $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal);
                 $hdridx ++;                      }
                 $output .= '                      $hdridx ++;
                       $output .= '
              </tr>               </tr>
             </table>              </table>
            </td>             </td>
Line 1004  sub print_config_box { Line 1120  sub print_config_box {
              <tr class="LC_info_row">               <tr class="LC_info_row">
               <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td>                <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td>
               <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td></tr>'."\n";                <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td></tr>'."\n";
                 if ($action eq 'passwords') {                      if ($action eq 'passwords') {
                     $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal);                          $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal);
                 } else {                      } else {
                     $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);                          $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
                 }                      }
                 $output .= '                      $output .= '
             </table>              </table>
           </td>            </td>
          </tr>           </tr>
          <tr>';           <tr>';
             } else {                  } else {
                 $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);                      $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
                   }
             }              }
             $rowtotal ++;              $rowtotal ++;
         } elsif (($action eq 'coursedefaults') || ($action eq 'authordefaults') ||          } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') ||
                  ($action eq 'directorysrch') || ($action eq 'helpsettings') ||                   ($action eq 'directorysrch') || ($action eq 'helpsettings') ||
                  ($action eq 'wafproxy')) {                   ($action eq 'wafproxy') || ($action eq 'authordefaults')) {
             $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);              $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'scantron') {          } elsif ($action eq 'scantron') {
             $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal);              $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal);
           } elsif ($action eq 'ssl') {
               $output .= $item->{'print'}->('connto',$dom,$settings,\$rowtotal).'
               </table>
             </td>
            </tr>
            <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col2'}).'</td></tr>'.
                              $item->{'print'}->('connfrom',$dom,$settings,\$rowtotal).'
               </table>
             </td>
            </tr>
            <tr>
              <td>
               <table class="LC_nested">
                <tr class="LC_info_row">
                 <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td>
                 <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td></tr>'.
                              $item->{'print'}->('bottom',$dom,$settings,\$rowtotal);
         } elsif ($action eq 'login') {          } elsif ($action eq 'login') {
             if ($numheaders == 5) {              if ($numheaders == 5) {
                 $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'                  $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).'
Line 1114  sub print_config_box { Line 1253  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 1130  sub print_config_box { Line 1269  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 1160  sub print_config_box { Line 1299  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 1192  sub print_config_box { Line 1331  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 1200  sub print_config_box { Line 1339  sub print_config_box {
         if ($action eq 'quotas') {          if ($action eq 'quotas') {
             $output .= &print_quotas($dom,$settings,\$rowtotal,$action);              $output .= &print_quotas($dom,$settings,\$rowtotal,$action);
         } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') ||           } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || 
                  ($action eq 'serverstatuses') || ($action eq 'loadbalancing') ||                   ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || 
                  ($action eq 'ipaccess')) {                   ($action eq 'proctoring') || ($action eq 'ipaccess')) {
             $output .= $item->{'print'}->($dom,$settings,\$rowtotal);              $output .= $item->{'print'}->($dom,$settings,\$rowtotal);
         }          }
     }      }
Line 1226  sub print_login { Line 1365  sub print_login {
         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 1507  sub print_login { Line 1646  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 1880  sub print_rolecolors { Line 2019  sub print_rolecolors {
     my %choices = &color_font_choices();      my %choices = &color_font_choices();
     my @bgs = ('pgbg','tabbg','sidebg');      my @bgs = ('pgbg','tabbg','sidebg');
     my @links = ('link','alink','vlink');      my @links = ('link','alink','vlink');
     my @images = ();      my @images = ('img');
     my %alt_text = &Apache::lonlocal::texthash(img => "Banner for $role role");      my %alt_text = &Apache::lonlocal::texthash(img => "Banner for $role role");
     my %designhash = &Apache::loncommon::get_domainconf($dom);      my %designhash = &Apache::loncommon::get_domainconf($dom);
     my %defaultdesign = %Apache::loncommon::defaultdesign;      my %defaultdesign = %Apache::loncommon::defaultdesign;
Line 1888  sub print_rolecolors { Line 2027  sub print_rolecolors {
     my %defaults = &role_defaults($role,\@bgs,\@links,\@images);      my %defaults = &role_defaults($role,\@bgs,\@links,\@images);
     if (ref($settings) eq 'HASH') {      if (ref($settings) eq 'HASH') {
         if (ref($settings->{$role}) eq 'HASH') {          if (ref($settings->{$role}) eq 'HASH') {
               if ($settings->{$role}->{'img'} ne '') {
                   $designs{'img'} = $settings->{$role}->{'img'};
                   $is_custom{'img'} = 1;
               }
             if ($settings->{$role}->{'font'} ne '') {              if ($settings->{$role}->{'font'} ne '') {
                 $designs{'font'} = $settings->{$role}->{'font'};                  $designs{'font'} = $settings->{$role}->{'font'};
                 $is_custom{'font'} = 1;                  $is_custom{'font'} = 1;
Line 1910  sub print_rolecolors { Line 2053  sub print_rolecolors {
             }              }
         }          }
     } else {      } else {
           if ($designhash{$dom.'.'.$role.'.img'} ne '') {
               $designs{img} = $designhash{$dom.'.'.$role.'.img'};
               $is_custom{'img'} = 1;
           }
         if ($designhash{$dom.'.'.$role.'.fontmenu'} ne '') {          if ($designhash{$dom.'.'.$role.'.fontmenu'} ne '') {
             $designs{fontmenu} = $designhash{$dom.'.'.$role.'.fontmenu'};              $designs{fontmenu} = $designhash{$dom.'.'.$role.'.fontmenu'};
             $is_custom{'fontmenu'} = 1;               $is_custom{'fontmenu'} = 1; 
Line 1959  sub role_defaults { Line 2106  sub role_defaults {
         }          }
     } else {      } else {
         %defaults = (          %defaults = (
                          img => $defaultdesign{$role.'.img'},
                        font => $defaultdesign{$role.'.font'},                         font => $defaultdesign{$role.'.font'},
                        fontmenu => $defaultdesign{$role.'.fontmenu'},                         fontmenu => $defaultdesign{$role.'.fontmenu'},
                     );                      );
Line 2147  sub display_color_options { Line 2295  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 2175  sub display_color_options { Line 2323  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 2236  sub login_text_colors { Line 2384  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 2269  sub image_changes { Line 2417  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 2288  sub print_quotas { Line 2436  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','lti');          @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 2741  sub print_studentcode { Line 2889  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','lti');      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 3232  function toggleWAF() { Line 3380  function toggleWAF() {
 ENDSCRIPT  ENDSCRIPT
 }  }
   
   sub proctoring_javascript {
       my ($settings) = @_;
       my (%ordered,$total,%jstext);
       $total = 0;
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   $ordered{$num} = $item;
               }
           }
           $total = scalar(keys(%{$settings}));
       } else {
           %ordered = (
                          0 => 'proctorio',
                          1 => 'examity',
                      );
           $total = 2; 
       }
       my @jsarray = ();
       foreach my $item (sort {$a <=> $b } (keys(%ordered))) {
           push(@jsarray,$ordered{$item});
       }
       my $jstext = '    var proctors = Array('."'".join("','",@jsarray)."'".');'."\n";
       return <<"ENDSCRIPT";
   <script type="text/javascript">
   // <![CDATA[
   function reorderProctoring(form,item) {
       var changedVal;
   $jstext
       var maxh = $total;
       var current = new Array;
       var changedVal = form.elements[item].options[form.elements[item].selectedIndex].value;
       for (var i=0; i<proctors.length; i++) {
           var elementName = 'proctoring_pos_'+proctors[i];
           if (elementName != item) {
               if (form.elements[elementName]) {
                   var currVal = form.elements[elementName].options[form.elements[elementName].selectedIndex].value;
                   current[currVal] = elementName;
               }
           }
       }
       var oldVal;
       for (var j=0; j<maxh; j++) {
           if (current[j] == undefined) {
               oldVal = j;
           }
       }
       if (oldVal < changedVal) {
           for (var k=oldVal+1; k<=changedVal ; k++) {
              var elementName = current[k];
              form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex - 1;
           }
       } else {
           for (var k=changedVal; k<oldVal; k++) {
               var elementName = current[k];
               form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex + 1;
           }
       }
       return;
   }
   
   function toggleProctoring(form,item) {
       var fieldsets = document.getElementsByClassName('proctoring_'+item);
       if (fieldsets.length) {
           var radioname = 'proctoring_available_'+item;
           var num = form.elements[radioname].length;
           if (num) {
               var setvis = '';
               for (var i=0; i<num; i++) {
                   if (form.elements[radioname][i].checked) {
                       if (form.elements[radioname][i].value == '1') {
                          setvis = 1;
                          break;
                       }
                   }
               }
               for (var j=0; j<fieldsets.length; j++) {
                   if (setvis) {
                       fieldsets[j].style.display = 'block';
                   } else {
                       fieldsets[j].style.display = 'none';
                   }
               }
           }
       }
       return;
   }
   
   // ]]>
   </script>
   
   ENDSCRIPT
   }
   
   
 sub lti_javascript {  sub lti_javascript {
     my ($dom,$settings) = @_;      my ($dom,$settings) = @_;
     my $togglejs = &lti_toggle_js($dom);      my $togglejs = &lti_toggle_js($dom);
Line 4765  sub print_helpsettings { Line 5009  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 4804  sub print_helpsettings { Line 5048  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 5085  sub radiobutton_prefs { Line 5329  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 5161  sub print_ltitools { Line 5405  sub print_ltitools {
     return $datatable;      return $datatable;
 }  }
   
   sub ltitools_names {
       my %lt = &Apache::lonlocal::texthash(
                                             'title'          => 'Title',
                                             'version'        => 'Version',
                                             'msgtype'        => 'Message Type',
                                             'sigmethod'      => 'Signature Method',
                                             'url'            => 'URL',
                                             'key'            => 'Key',
                                             'lifetime'       => 'Nonce lifetime (s)',
                                             'secret'         => 'Secret',
                                             'icon'           => 'Icon',
                                             'user'           => 'User',
                                             'fullname'       => 'Full Name',
                                             'firstname'      => 'First Name',
                                             'lastname'       => 'Last Name',
                                             'email'          => 'E-mail',
                                             'roles'          => 'Role',
                                             'window'         => 'Window',
                                             'tab'            => 'Tab',
                                             'iframe'         => 'iFrame',
                                             'height'         => 'Height',
                                             'width'          => 'Width',
                                             'linktext'       => 'Default Link Text',
                                             'explanation'    => 'Default Explanation',
                                             'passback'       => 'Tool can return grades:',
                                             'roster'         => 'Tool can retrieve roster:',
                                             'crstarget'      => 'Display target',
                                             'crslabel'       => 'Course label',
                                             'crstitle'       => 'Course title',
                                             'crslinktext'    => 'Link Text',
                                             'crsexplanation' => 'Explanation',
                                             'crsappend'      => 'Provider URL',
                                           );
       return %lt;
   }
   
 sub secrets_form {  sub secrets_form {
     my ($dom,$context,$encrypt,$privkeys,$rowtotal) = @_;      my ($dom,$context,$encrypt,$privkeys,$rowtotal) = @_;
     my @ids=&Apache::lonnet::current_machine_ids();      my @ids=&Apache::lonnet::current_machine_ids();
Line 5278  sub secrets_form { Line 5558  sub secrets_form {
     return $output;      return $output;
 }  }
   
   sub print_proctoring {
       my ($dom,$settings,$rowtotal) = @_;
       my $itemcount = 1;
       my (%ordered,%providernames,%current,%currentdef);
       my $confname = $dom.'-domainconfig';
       my $switchserver = &check_switchserver($dom,$confname);
       if (ref($settings) eq 'HASH') {
           foreach my $item (keys(%{$settings})) {
               if (ref($settings->{$item}) eq 'HASH') {
                   my $num = $settings->{$item}{'order'};
                   $ordered{$num} = $item;
               }
           }
       } else {
           %ordered = (
                        1 => 'proctorio',
                        2 => 'examity',
                      );
       }
       %providernames = &proctoring_providernames();
       my $maxnum = scalar(keys(%ordered));
       my (%requserfields,%optuserfields,%defaults,%extended,%crsconf,@courseroles,@ltiroles);
       my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data();
       if (ref($requref) eq 'HASH') {
           %requserfields = %{$requref};
       }
       if (ref($opturef) eq 'HASH') {
           %optuserfields = %{$opturef};
       }
       if (ref($defref) eq 'HASH') {
           %defaults = %{$defref};
       }
       if (ref($extref) eq 'HASH') {
           %extended = %{$extref};
       }
       if (ref($crsref) eq 'HASH') {
           %crsconf = %{$crsref};
       }
       if (ref($rolesref) eq 'ARRAY') {
           @courseroles = @{$rolesref};
       }
       if (ref($ltiref) eq 'ARRAY') {
           @ltiroles = @{$ltiref};
       }
       my $datatable;
       my $css_class;
       if (keys(%ordered)) {
           my @items = sort { $a <=> $b } keys(%ordered);
           for (my $i=0; $i<@items; $i++) {
               $css_class = $itemcount%2?' class="LC_odd_row"':'';
               my $provider = $ordered{$items[$i]};
               my $optionsty = 'none';
               my ($available,$version,$lifetime,$imgsrc,$userincdom,$showroles,
                   %checkedfields,%rolemaps,%inuse,%crsconfig,%current);
               if (ref($settings) eq 'HASH') {
                   if (ref($settings->{$provider}) eq 'HASH') {
                       %current = %{$settings->{$provider}};
                       if ($current{'available'}) {
                           $optionsty = 'block';
                           $available = 1;
                       }
                       if ($current{'lifetime'} =~ /^\d+$/) {
                           $lifetime = $current{'lifetime'};
                       }
                       if ($current{'version'} =~ /^\d+\.\d+$/) {
                           $version = $current{'version'};
                       }
                       if ($current{'image'} ne '') {
                           $imgsrc = '<img src="'.$current{'image'}.'" alt="'.&mt('Proctoring service icon').'" />';
                       }
                       if (ref($current{'fields'}) eq 'ARRAY') {
                           map { $checkedfields{$_} = 1; } @{$current{'fields'}};
                       }
                       $userincdom = $current{'incdom'};
                       if (ref($current{'roles'}) eq 'HASH') {
                           %rolemaps = %{$current{'roles'}};
                           $checkedfields{'roles'} = 1;
                       }
                       if (ref($current{'defaults'}) eq 'ARRAY') {
                           foreach my $val (@{$current{'defaults'}}) {
                               if (grep(/^\Q$val\E$/,@{$defaults{$provider}})) {
                                   $inuse{$val} = 1;
                               } else {
                                   foreach my $poss (keys(%{$extended{$provider}})) {
                                       if (ref($extended{$provider}{$poss}) eq 'ARRAY') {
                                           if (grep(/^\Q$val\E$/,@{$extended{$provider}{$poss}})) {
                                               $inuse{$poss} = $val;
                                               last;
                                           }
                                       }
                                   }
                               }
                           }
                       } elsif (ref($current{'defaults'}) eq 'HASH') {
                           foreach my $key (keys(%{$current{'defaults'}})) {
                               my $currval = $current{'defaults'}{$key}; 
                               if (grep(/^\Q$key\E$/,@{$defaults{$provider}})) {
                                   $inuse{$key} = 1;
                               } else {
                                   my $match;
                                   foreach my $poss (keys(%{$extended{$provider}})) {
                                       if (ref($extended{$provider}{$poss}) eq 'ARRAY') {
                                           if (grep(/^\Q$key\E$/,@{$extended{$provider}{$poss}})) {
                                               $inuse{$poss} = $key;
                                               last;
                                           }
                                       } elsif (ref($extended{$provider}{$poss}) eq 'HASH') {
                                           foreach my $inner (sort(keys(%{$extended{$provider}{$poss}}))) {
                                               if (ref($extended{$provider}{$poss}{$inner}) eq 'ARRAY') {
                                                   if (grep(/^\Q$currval\E$/,@{$extended{$provider}{$poss}{$inner}})) {
                                                       $currentdef{$inner} = $currval;
                                                       $match = 1;
                                                       last;
                                                   }
                                               } elsif ($inner eq $key) {
                                                   $currentdef{$key} = $currval;
                                                   $match = 1;
                                                   last;
                                               }
                                           }
                                       }
                                       last if ($match);
                                   }
                               }
                           }
                       }
                       if (ref($current{'crsconf'}) eq 'ARRAY') {
                           map { $crsconfig{$_} = 1; } @{$current{'crsconf'}};
                       }
                   }
               }
               my %lt = &proctoring_titles($provider);
               my %fieldtitles = &proctoring_fieldtitles($provider);
               my $onclickavailable = ' onclick="toggleProctoring(this.form,'."'$provider'".');"';
               my %checkedavailable = (
                                      yes => '',
                                      no  => ' checked="checked"',
                                    );
               if ($available) {
                   $checkedavailable{'yes'} = $checkedavailable{'no'};
                   $checkedavailable{'no'} = '';
               }
               my $chgstr = ' onchange="javascript:reorderProctoring(this.form,'."'proctoring_pos_".$provider."'".');"';
               $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                            .'<select name="proctoring_pos_'.$provider.'"'.$chgstr.'>';
               for (my $k=0; $k<$maxnum; $k++) {
                   my $vpos = $k+1;
                   my $selstr;
                   if ($k == $i) {
                       $selstr = ' selected="selected" ';
                   }
                   $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>';
               }
               if ($version eq '') {
                   if ($provider eq 'proctorio') {
                       $version = '1.0';
                   } elsif ($provider eq 'examity') {
                       $version = '1.1';
                   }
               }
               if ($lifetime eq '') {
                   $lifetime = '300';
               }
               $datatable .=
                   '</select>'.('&nbsp;'x2).'<b>'.$providernames{$provider}.'</b></span><br />'.
                   '<span class="LC_nobreak">'.$lt{'avai'}.'&nbsp;'.
                   '<label><input type="radio" name="proctoring_available_'.$provider.'" value="1"'.$onclickavailable.$checkedavailable{yes}.' />'.&mt('Yes').'</label>&nbsp;'."\n".
                   '<label><input type="radio" name="proctoring_available_'.$provider.'" value="0"'.$onclickavailable.$checkedavailable{no}.' />'.&mt('No').'</label></span>'."\n".
                   '</td>'.
                   '<td colspan="2">'.
                   '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'base'}.'</legend>'.
                   '<span class="LC_nobreak">'.$lt{'version'}.':<select name="proctoring_'.$provider.'_version">'.
                   '<option value="'.$version.'" selected="selected">'.$version.'</option></select></span> '."\n".
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'sigmethod'}.':<select name="proctoring_'.$provider.'_sigmethod">'.
                   '<option value="HMAC-SHA1" selected="selected">HMAC-SHA1</option>'.
                   '<option value="HMAC-SHA256">HMAC-SHA256</option></select></span>'.
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="5" name="proctoring_'.$provider.'_lifetime" value="'.$lifetime.'" /></span> '."\n".
                   '<br />'.
                   '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="proctoring_'.$provider.'_url" value="'.$current{'url'}.'" /></span> '."\n".
                   '<br />'.
                   '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="proctoring_'.$provider.'_key" value="'.$current{'key'}.'" /></span> '."\n".
                   ('&nbsp;'x2).
                   '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="proctoring_'.$provider.'_secret" value="'.$current{'secret'}.'" />'.
                   '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.proctoring_'.$provider.'_secret.type='."'text'".' } else { this.form.proctoring_'.$provider.'_secret.type='."'password'".' }" />'.$lt{'visible'}.'</label></span><br />'."\n";
               $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.':&nbsp;';
               if ($imgsrc) {
                   $datatable .= $imgsrc.
                                 '<label><input type="checkbox" name="proctoring_image_del"'.
                                 ' value="'.$provider.'" />'.&mt('Delete?').'</label></span> '.
                                 '<span class="LC_nobreak">&nbsp;'.&mt('Replace:');
               }
               $datatable .= '&nbsp;';
               if ($switchserver) {
                   $datatable .= &mt('Upload to library server: [_1]',$switchserver);
               } else {
                   $datatable .= '<input type="file" name="proctoring_image_'.$provider.'" value="" />';
               }
               unless ($imgsrc) {
                   $datatable .= '<br />('.&mt('if larger than 21x21 pixels, image will be scaled').')';
               }
               $datatable .= '</fieldset>'."\n";
               if (ref($requserfields{$provider}) eq 'ARRAY') {
                   if (@{$requserfields{$provider}} > 0) {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'requ'}.'</legend>';
                       foreach my $field (@{$requserfields{$provider}}) {
                           $datatable .= '<span class="LC_nobreak">'.
                                         '<label><input type="checkbox" name="proctoring_reqd_'.$provider.'" value="'.$field.'" checked="checked" disabled="disabled" />'.
                                         $lt{$field}.'</label>';
                           if ($field eq 'user') {
                               my $seluserdom = '';
                               my $unseluserdom = ' selected="selected"';
                               if ($userincdom) {
                                   $seluserdom = $unseluserdom;
                                   $unseluserdom = '';
                               }
                               $datatable .= ':&nbsp;'.
                                   '<select name="proctoring_userincdom_'.$provider.'">'.
                                   '<option value="0"'.$unseluserdom.'>'.$lt{'username'}.'</option>'.
                                   '<option value="1"'.$seluserdom.'>'.$lt{'uname:dom'}.'</option>'.
                                   '</select>&nbsp;';
                           } else {
                               $datatable .= '&nbsp;';
                               if ($field eq 'roles') {
                                   $showroles = 1; 
                               } 
                           }
                           $datatable .= '</span> ';
                       }
                   }
                   $datatable .= '</fieldset>'."\n";
               }
               if (ref($optuserfields{$provider}) eq 'ARRAY') {
                   if (@{$optuserfields{$provider}} > 0) {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'optu'}.'</legend>'; 
                       foreach my $field (@{$optuserfields{$provider}}) {
                           my $checked;
                           if ($checkedfields{$field}) {
                               $checked = ' checked="checked"';
                           }
                           $datatable .= '<span class="LC_nobreak">'.
                                         '<label><input type="checkbox" name="proctoring_optional_'.$provider.'" value="'.$field.'"'.$checked.' />'.$lt{$field}.'</label></span>&nbsp; ';
                       }
                       $datatable .= '</fieldset>'."\n";
                   }
               }
               if (ref($defaults{$provider}) eq 'ARRAY') {
                   if (@{$defaults{$provider}}) {
                       my (%options,@selectboxes);
                       if (ref($extended{$provider}) eq 'HASH') {
                           %options = %{$extended{$provider}};
                       }
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'"><legend>'.$lt{'defa'}.'</legend>';
                       my ($rem,$numinrow,$dropdowns);
                       if ($provider eq 'proctorio') { 
                           $datatable .= '<table>';
                           $numinrow = 4;
                       }
                       my $i = 0;
                       foreach my $field (@{$defaults{$provider}}) {
                           my $checked;
                           if ($inuse{$field}) {
                               $checked = ' checked="checked"';
                           }
                           if ($provider eq 'examity') {
                               if ($field eq 'display') {
                                   $datatable .= '<span class="LC_nobreak">'.&mt('Display target:');
                                   foreach my $option ('iframe','tab','window') {
                                       my $checkdisp;
                                       if ($currentdef{'target'} eq $option) {
                                           $checkdisp = ' checked="checked"';
                                       }
                                       $datatable .= '<label><input type="radio" name="proctoring_target_'.$provider.'" value="'.$option.'"'.$checkdisp.' />'.
                                       $fieldtitles{$option}.'</label>'.('&nbsp;'x2);
                                   }
                                   $datatable .= ('&nbsp;'x4);
                                   foreach my $dimen ('width','height') {
                                       $datatable .= '<label>'.$fieldtitles{$dimen}.'&nbsp;'.
                                                     '<input type="text" name="proctoring_'.$dimen.'_'.$provider.'" size="5" '.
                                                     'value="'.$currentdef{$dimen}.'" /></label>'.
                                                     ('&nbsp;'x2);
                                   }
                                   $datatable .= '</span><br />'.
                                                 '<div class="LC_left_float">'.$fieldtitles{'linktext'}.'<br />'.
                                                 '<input type="text" name="proctoring_linktext_'.$provider.'" '.
                                                 'size="25" value="'.$currentdef{'linktext'}.'" /></div>'.
                                                 '<div class="LC_left_float">'.$fieldtitles{'explanation'}.'<br />'.
                                                 '<textarea name="proctoring_explanation_'.$provider.'" rows="5" cols="40">'.
                                                 $currentdef{'explanation'}.
                                                 '</textarea></div><div style=""></div><br />';
                               }
                           } else {
                               if ((exists($options{$field})) && (ref($options{$field}) eq 'ARRAY')) {
                                   my ($output,$selnone);
                                   unless ($checked) {
                                       $selnone = ' selected="selected"';
                                   }
                                   $output .= '<span class="LC_nobreak">'.$fieldtitles{$field}.': '.
                                              '<select name="proctoring_defaults_'.$field.'_'.$provider.'">'.
                                              '<option value=""'.$selnone.'>'.&mt('Not in use').'</option>'; 
                                   foreach my $option (@{$options{$field}}) {
                                       my $sel; 
                                       if ($inuse{$field} eq $option) {
                                           $sel = ' selected="selected"';
                                       }
                                       $output .= '<option value="'.$option.'"'.$sel.'>'.$fieldtitles{$option}.'</option>';
                                   }
                                   $output .= '</select></span>';
                                   push(@selectboxes,$output);
                               } else {
                                   $rem = $i%($numinrow);
                                   if ($rem == 0) {
                                       if ($i > 0) {
                                           $datatable .= '</tr>';
                                       }
                                       $datatable .= '<tr>';
                                   }
                                   $datatable .= '<td class="LC_left_item">'.
                                                 '<span class="LC_nobreak">'.
                                                 '<label><input type="checkbox" name="proctoring_defaults_'.$provider.'" value="'.$field.'"'.$checked.' />'.
                                                 $fieldtitles{$field}.'</label></span></td>';
                                   $i++;
                               }
                           }
                       }
                       if ($provider eq 'proctorio') {
                           if ($numinrow) {
                               $rem = $i%$numinrow;
                           }
                           my $colsleft = $numinrow - $rem;
                           if ($colsleft > 1) {
                               $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">';
                           } else {
                               $datatable .= '<td class="LC_left_item">';
                           }
                           $datatable .= '&nbsp;'.
                                         '</td></tr></table>';
                           if (@selectboxes) {
                               $datatable .= '<hr /><table>';
                               $numinrow = 2;
                               for (my $i=0; $i<@selectboxes; $i++) {
                                   $rem = $i%($numinrow);
                                   if ($rem == 0) {
                                       if ($i > 0) {
                                           $datatable .= '</tr>';
                                       }
                                       $datatable .= '<tr>';
                                   }
                                   $datatable .= '<td class="LC_left_item">'.
                                                 $selectboxes[$i].'</td>';
                               }
                               if ($numinrow) {
                                   $rem = $i%$numinrow;
                               }
                               $colsleft = $numinrow - $rem;
                               if ($colsleft > 1) {
                                   $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">';
                               } else {
                                   $datatable .= '<td class="LC_left_item">';
                               }
                               $datatable .= '&nbsp;'.
                                             '</td></tr></table>';
                           }
                       }
                       $datatable .= '</fieldset>';
                   }
                   if (ref($crsconf{$provider}) eq 'ARRAY') {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
                                     '<legend>'.&mt('Configurable in course').'</legend>';
                       my ($rem,$numinrow);
                       if ($provider eq 'proctorio') {
                           $datatable .= '<table>';
                           $numinrow = 4;
                       }
                       my $i = 0;
                       foreach my $item (@{$crsconf{$provider}}) {
                           my $name;
                           if ($provider eq 'examity') {
                               $name = $lt{'crs'.$item};
                           } elsif ($provider eq 'proctorio') {
                               $name = $fieldtitles{$item};
                               $rem = $i%($numinrow);
                               if ($rem == 0) {
                                   if ($i > 0) {
                                       $datatable .= '</tr>';
                                   }
                                   $datatable .= '<tr>';
                               }
                               $datatable .= '<td class="LC_left_item>';
                           }
                           my $checked;
                           if ($crsconfig{$item}) {
                               $checked = ' checked="checked"';
                           }
                           $datatable .= '<span class="LC_nobreak"><label>'.
                                         '<input type="checkbox" name="proctoring_crsconf_'.$provider.'" value="'.$item.'"'.$checked.' />'.
                                         $name.'</label></span>';
                           if ($provider eq 'examity') {
                               $datatable .= '&nbsp; ';
                           }
                           $datatable .= "\n";
                           $i++;
                       }
                       if ($provider eq 'proctorio') {
                           if ($numinrow) {
                               $rem = $i%$numinrow;
                           }
                           my $colsleft = $numinrow - $rem;
                           if ($colsleft > 1) {
                               $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">';
                           } else {
                               $datatable .= '<td class="LC_left_item">';
                           }
                           $datatable .= '&nbsp;'.
                                         '</td></tr></table>';
                       }
                       $datatable .= '</fieldset>';
                   }
                   if ($showroles) {
                       $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
                                     '<legend>'.&mt('Role mapping').'</legend><table><tr>';
                       foreach my $role (@courseroles) {
                           my ($selected,$selectnone);
                           if (!$rolemaps{$role}) {
                               $selectnone = ' selected="selected"';
                           }
                           $datatable .= '<td style="text-align: center">'.
                                         &Apache::lonnet::plaintext($role,'Course').'<br />'.
                                         '<select name="proctoring_roles_'.$role.'_'.$provider.'">'.
                                         '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';
                           foreach my $ltirole (@ltiroles) {
                               unless ($selectnone) {
                                   if ($rolemaps{$role} eq $ltirole) {
                                       $selected = ' selected="selected"';
                                   } else {
                                       $selected = '';
                                   }
                               }
                               $datatable .= '<option value="'.$ltirole.'"'.$selected.'>'.$ltirole.'</option>';
                           }
                           $datatable .= '</select></td>';
                       }
                       $datatable .= '</tr></table></fieldset>'.
                                     '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
                                     '<legend>'.&mt('Custom items sent on launch').'</legend>'.
                                     '<table><tr><th>'.&mt('Action').'</th><th>'.&mt('Name').'</th><th>'.&mt('Value').'</th></tr>'.
                                     '<tr><td></td><td>lms</td>'.
                                     '<td><input type="text" name="proctoring_customval_lms_'.$provider.'"'.
                                     ' value="Loncapa" disabled="disabled"/></td></tr>';
                       if ((ref($settings) eq 'HASH') && (ref($settings->{$provider}) eq 'HASH') &&
                           (ref($settings->{$provider}->{'custom'}) eq 'HASH')) {
                           my %custom = %{$settings->{$provider}->{'custom'}};
                           if (keys(%custom) > 0) {
                               foreach my $key (sort(keys(%custom))) {
                                   next if ($key eq 'lms');
                                   $datatable .= '<tr><td><span class="LC_nobreak">'.
                                                 '<label><input type="checkbox" name="proctoring_customdel_'.$provider.'" value="'.
                                                 $key.'" />'.&mt('Delete').'</label></span></td><td>'.$key.'</td>'.
                                                 '<td><input type="text" name="proctoring_customval_'.$key.'_'.$provider.'"'.
                                                 ' value="'.$custom{$key}.'" /></td></tr>';
                               }
                           }
                       }
                       $datatable .= '<tr><td><span class="LC_nobreak">'.
                                     '<label><input type="checkbox" name="proctoring_customadd" value="'.$provider.'" />'.
                                     &mt('Add more').'</label></span></td><td><input type="text" name="proctoring_custom_name_'.$provider.'" />'.
                                     '</td><td><input type="text" name="proctoring_custom_value_'.$provider.'" /></td></tr>'.
                                     '</table></fieldset></td></tr>'."\n";
                   }
                   $datatable .= '</td></tr>';
               }
               $itemcount ++;
           }
       }
       return $datatable;
   }
   
   sub proctoring_data {
       my $requserfields = {
                         proctorio => ['user'],
                         examity   => ['roles','user'],
                        };
       my $optuserfields = {
                           proctorio => ['fullname'],
                           examity   => ['fullname','firstname','lastname','email'],
                        };
       my $defaults = {
                     proctorio => ['recordvideo','recordaudio','recordscreen','recordwebtraffic',
                                   'recordroomstart','verifyvideo','verifyaudio','verifydesktop',
                                   'verifyid','verifysignature','fullscreen','clipboard','tabslinks',
                                   'closetabs','onescreen','print','downloads','cache','rightclick',
                                   'reentry','calculator','whiteboard'],
                     examity   => ['display'],
                   };
       my $extended = { 
                     proctorio => {
                                    verifyid => ['verifyidauto','verifyidlive'],
                                    fullscreen => ['fullscreenlenient','fullscreenmoderate','fullscreensever'],
                                    tabslinks => ['notabs','linksonly'],
                                    reentry => ['noreentry','agentreentry'],
                                    calculator => ['calculatorbasic','calculatorsci'],
                                  },
                     examity => {
                                  display => {
                                              target      => ['iframe','tab','window'],
                                              width       => '',
                                              height      => '',
                                              linktext    => '',
                                              explanation => '',
                                             },
                                },
                   };
       my $crsconf = {
                    proctorio => ['recordvideo','recordaudio','recordscreen','recordwebtraffic',
                                  'recordroomstart','verifyvideo','verifyaudio','verifydesktop',
                                  'verifyid','verifysignature','fullscreen','clipboard','tabslinks',
                                  'closetabs','onescreen','print','downloads','cache','rightclick',
                                  'reentry','calculator','whiteboard'],
                    examity => ['label','title','target','linktext','explanation','append'],
                  };
       my $courseroles = ['cc','in','ta','ep','st'];
       my $ltiroles = ['Instructor','ContentDeveloper','TeachingAssistant','Learner'];
       return ($requserfields,$optuserfields,$defaults,$extended,$crsconf,$courseroles,$ltiroles);
   }
   
   sub proctoring_titles {
       my ($item) = @_;
       my (%common_lt,%custom_lt);
       %common_lt = &Apache::lonlocal::texthash (
                        'avai'      => 'Available?',
                        'base'      => 'Basic Settings',
                        'requ'      => 'User data required to be sent on launch',
                        'optu'      => 'User data optionally sent on launch',
                        'udsl'      => 'User data sent on launch',
                        'defa'      => 'Defaults for items configurable in course',
                        'sigmethod' => 'Signature Method',
                        'key'       => 'Key',
                        'lifetime'  => 'Nonce lifetime (s)',
                        'secret'    => 'Secret',
                        'icon'      => 'Icon',
                        'fullname'  => 'Full Name',
                        'visible'   => 'Visible input',
                        'username'  => 'username',
                        'user'      => 'User',
                    );
       if ($item eq 'proctorio') {
           %custom_lt = &Apache::lonlocal::texthash (
                            'version'        => 'OAuth version',
                            'url'            => 'API URL',
                            'uname:dom'      => 'username-domain',
           );
       } elsif ($item eq 'examity') {
           %custom_lt = &Apache::lonlocal::texthash (
                            'version'        => 'LTI Version',
                            'url'            => 'URL',
                            'uname:dom'      => 'username:domain',
                            'msgtype'        => 'Message Type',
                            'firstname'      => 'First Name',
                            'lastname'       => 'Last Name',
                            'email'          => 'E-mail',
                            'roles'          => 'Role',
                            'crstarget'      => 'Display target',
                            'crslabel'       => 'Course label',
                            'crstitle'       => 'Course title', 
                            'crslinktext'    => 'Link Text',
                            'crsexplanation' => 'Explanation',
                            'crsappend'      => 'Provider URL',
           );
       }
       my %lt = (%common_lt,%custom_lt);
       return %lt;
   }
   
   sub proctoring_fieldtitles {
       my ($item) = @_;
       if ($item eq 'proctorio') {
           return &Apache::lonlocal::texthash (
                     'recordvideo' => 'Record video',
                     'recordaudio' => 'Record audio',
                     'recordscreen' => 'Record screen',
                     'recordwebtraffic' => 'Record web traffic',
                     'recordroomstart' => 'Record room scan',
                     'verifyvideo' => 'Verify webcam',
                     'verifyaudio' => 'Verify microphone',
                     'verifydesktop' => 'Verify desktop recording',
                     'verifyid' => 'Photo ID verification',
                     'verifysignature' => 'Require signature',
                     'fullscreen' => 'Fullscreen',
                     'clipboard' => 'Disable copy/paste',
                     'tabslinks' => 'New tabs/windows',
                     'closetabs' => 'Close other tabs',
                     'onescreen' => 'Limit to single screen',
                     'print' => 'Disable Printing',
                     'downloads' => 'Disable Downloads',
                     'cache' => 'Empty cache after exam',
                     'rightclick' => 'Disable right click',
                     'reentry' => 'Re-entry to exam',
                     'calculator' => 'Onscreen calculator',
                     'whiteboard' => 'Onscreen whiteboard',
                     'verifyidauto' => 'Automated verification',
                     'verifyidlive' => 'Live agent verification',
                     'fullscreenlenient' => 'Forced, but can navigate away for up to 30s',
                     'fullscreenmoderate' => 'Forced, but can navigate away for up to 15s',
                     'fullscreensever' => 'Forced, navigation away ends exam',
                     'notabs' => 'Disaallowed',
                     'linksonly' => 'Allowed from links in exam',
                     'noreentry' => 'Disallowed',
                     'agentreentry' => 'Agent required for re-entry',
                     'calculatorbasic' => 'Basic',
                     'calculatorsci' => 'Scientific',
           );
       } elsif ($item eq 'examity') {
           return &Apache::lonlocal::texthash (
                   'target'         => 'Display target',   
                   'window'         => 'Window',
                   'tab'            => 'Tab',
                   'iframe'         => 'iFrame',
                   'height'         => 'Height (pixels)',
                   'width'          => 'Width (pixels)',
                   'linktext'       => 'Default Link Text',
                   'explanation'    => 'Default Explanation',
                   'append'         => 'Provider URL',
           );
       }
   }
   
   sub proctoring_providernames {
       return (
                proctorio => 'Proctorio',
                examity   => 'Examity',
              );
   }
   
 sub print_lti {  sub print_lti {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my $itemcount = 1;      my $itemcount = 1;
Line 5699  sub lti_options { Line 6613  sub lti_options {
         $checked{'menuitem'}{'grades'} = ' checked="checked"';          $checked{'menuitem'}{'grades'} = ' checked="checked"';
         $menusty = 'inline-block';          $menusty = 'inline-block';
     }      }
     my @coursetypes = ('official','unofficial','community','textbook','lti');      my @coursetypes = ('official','unofficial','community','textbook','placement','lti');
     my %coursetypetitles = &Apache::lonlocal::texthash (      my %coursetypetitles = &Apache::lonlocal::texthash (
                                official   => 'Official',                                 official   => 'Official',
                                unofficial => 'Unofficial',                                 unofficial => 'Unofficial',
                                community  => 'Community',                                 community  => 'Community',
                                textbook   => 'Textbook',                                 textbook   => 'Textbook',
                                  placement  => 'Placement Test',
                                lti        => 'LTI Provider',                                 lti        => 'LTI Provider',
     );      );
     my @authtypes = ('internal','krb4','krb5','localauth');      my @authtypes = ('internal','krb4','krb5','localauth');
Line 5986  sub print_coursedefaults { Line 6901  sub print_coursedefaults {
     my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles);      my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles);
     my $itemcount = 1;      my $itemcount = 1;
     my %choices =  &Apache::lonlocal::texthash (      my %choices =  &Apache::lonlocal::texthash (
           canuse_pdfforms      => 'Course/Community users can create/upload PDF forms',
         uploadquota          => 'Default quota for files uploaded directly to course/community using Course Editor (MB)',          uploadquota          => 'Default quota for files uploaded directly to course/community using Course Editor (MB)',
         coursequota          => 'Default cumulative quota for all group portfolio spaces in course (MB)',          coursequota          => 'Default cumulative quota for all group portfolio spaces in course (MB)',
         anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys',          anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys',
Line 6000  sub print_coursedefaults { Line 6916  sub print_coursedefaults {
         ltiauth              => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication',          ltiauth              => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication',
         domexttool           => 'External Tools defined in the domain may be used in courses/communities (by type)',          domexttool           => 'External Tools defined in the domain may be used in courses/communities (by type)',
         exttool              => 'External Tools can be defined and configured in courses/communities (by type)',          exttool              => 'External Tools can be defined and configured in courses/communities (by type)',
           crsauthor            => 'Standard LON-CAPA problems can be created within a course/community (by type)',
         crseditors           => 'Available editors for web pages and/or problems created in a course/community',          crseditors           => 'Available editors for web pages and/or problems created in a course/community',
     );      );
     my %staticdefaults = (      my %staticdefaults = (
Line 6010  sub print_coursedefaults { Line 6927  sub print_coursedefaults {
                            mysqltables          => 172800,                             mysqltables          => 172800,
                            domexttool           => 1,                             domexttool           => 1,
                            exttool              => 0,                             exttool              => 0,
                            crseditors           => ['edit','xml'],                             crsauthor            => 1,
                              crseditors           => ['edit','xml'],  
                          );                           );
     if ($position eq 'top') {      if ($position eq 'top') {
         %defaultchecked = (          %defaultchecked = (
                               'canuse_pdfforms' => 'off',
                             'uselcmath'       => 'on',                              'uselcmath'       => 'on',
                             'usejsme'         => 'on',                              'usejsme'         => 'on',
                             'inline_chem'     => 'on',                              'inline_chem'     => 'on',
                             'canclone'        => 'none',                              'canclone'        => 'none',
                           );                            );
         @toggles = ('uselcmath','usejsme','inline_chem');          @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem');
         my $deftex = $Apache::lonnet::deftex;          my $deftex = $Apache::lonnet::deftex;
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ($settings->{'texengine'}) {              if ($settings->{'texengine'}) {
Line 6052  sub print_coursedefaults { Line 6971  sub print_coursedefaults {
         $datatable = $mathdisp.$datatable;          $datatable = $mathdisp.$datatable;
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $css_class = $itemcount%2?' class="LC_odd_row"':'';
         $datatable .=          $datatable .=
             '<tr'.$css_class.'><td valign="top">'.              '<tr'.$css_class.'><td style="vertical-align: top">'.
             '<span class="LC_nobreak">'.$choices{'canclone'}.              '<span class="LC_nobreak">'.$choices{'canclone'}.
             '</span></td><td class="LC_left_item">';              '</span></td><td class="LC_left_item">';
         my $currcanclone = 'none';          my $currcanclone = 'none';
Line 6130  sub print_coursedefaults { Line 7049  sub print_coursedefaults {
         my $ltiauth = 0;          my $ltiauth = 0;
         my %domexttool;          my %domexttool;
         my %exttool;          my %exttool;
           my %crsauthor;
         my %crseditors;          my %crseditors;
         my @types = ('official','unofficial','community','textbook');          my @types = ('official','unofficial','community','textbook','placement');
         if (ref($settings) eq 'HASH') {          if (ref($settings) eq 'HASH') {
             if ($settings->{'ltiauth'}) {              if ($settings->{'ltiauth'}) {
                 $ltiauth = 1;                  $ltiauth = 1;
Line 6156  sub print_coursedefaults { Line 7076  sub print_coursedefaults {
                     }                      }
                 }                  }
             }              }
               if (ref($settings->{'crsauthor'}) eq 'HASH') {
                   foreach my $type (@types) {
                       if ($settings->{'crsauthor'}->{$type}) {
                           $crsauthor{$type} = ' checked="checked"';
                       }
                   }
               } else {
                   foreach my $type (@types) {
                       if ($staticdefaults{'crsauthor'}) {
                           $crsauthor{$type} = ' checked="checked"';
                       }
                   }
               }
             if (ref($settings->{'crseditors'}) eq 'ARRAY') {              if (ref($settings->{'crseditors'}) eq 'ARRAY') {
                 foreach my $editor (@{$settings->{'crseditors'}}) {                  foreach my $editor (@{$settings->{'crseditors'}}) {
                     $crseditors{$editor} = ' checked="checked"';                      $crseditors{$editor} = ' checked="checked"';
Line 6224  sub print_coursedefaults { Line 7157  sub print_coursedefaults {
                 if ($staticdefaults{'domexttool'}) {                  if ($staticdefaults{'domexttool'}) {
                     $domexttool{$type} = ' checked="checked"';                      $domexttool{$type} = ' checked="checked"';
                 }                  }
                   if ($staticdefaults{'crsauthor'}) {
                       $crsauthor{$type} = ' checked="checked"';
                   }
             }              }
             foreach my $editor (@{$staticdefaults{'crseditors'}}) {              foreach my $editor (@{$staticdefaults{'crseditors'}}) {
                 $crseditors{$editor} = ' checked="checked"';                  $crseditors{$editor} = ' checked="checked"';
Line 6255  sub print_coursedefaults { Line 7191  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 6286  sub print_coursedefaults { Line 7222  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 6310  sub print_coursedefaults { Line 7246  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 6328  sub print_coursedefaults { Line 7264  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>';
         }          }
Line 6378  sub print_coursedefaults { Line 7314  sub print_coursedefaults {
         $itemcount ++;          $itemcount ++;
         $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';          $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
         $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.          $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                         $choices{'crsauthor'}.
                         '</span></td>'.
                         '<td style="text-align: right" class="LC_right_item">'.
                         '<table><tr>';
           foreach my $type (@types) {
               $datatable .= '<td style="text-align: left">'.
                             '<span class="LC_nobreak">'.
                             '<label><input type="checkbox" name="crsauthor"'.
                             ' value="'.$type.'"'.$crsauthor{$type}.' />'.
                             &mt($type).'</label></span></td>'."\n";
           }
           $datatable .= '</tr></table></td></tr>'."\n";
           $itemcount ++;
           $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
           $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.
                       $choices{'crseditors'}.                        $choices{'crseditors'}.
                       '</span></td>'.                        '</span></td>'.
                       '<td style="text-align: right" class="LC_right_item">'.                        '<td style="text-align: right" class="LC_right_item">'.
Line 6448  sub print_authordefaults { Line 7399  sub print_authordefaults {
                       &selectbox('sourceavail',$currrights{'sourceavail'},'',                        &selectbox('sourceavail',$currrights{'sourceavail'},'',
                                  \&Apache::loncommon::source_copyrightdescription,                                   \&Apache::loncommon::source_copyrightdescription,
                                  (&Apache::loncommon::source_copyrightids)).                                   (&Apache::loncommon::source_copyrightids)).
                       '</td></tr>'."\n";                        '</td></tr>'."\n";        
     } else {      } else {
         $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';          $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
         my $curreditors;          my $curreditors;
Line 6572  sub print_authordefaults { Line 7523  sub print_authordefaults {
         }          }
         $datatable .= '</td></tr>';          $datatable .= '</td></tr>';
         $itemcount ++;          $itemcount ++;
         my %defchecked = (  
                                 'archive' => 'off',  
                              );  
         my @toggles = ('archive');  
         (my $archive,$itemcount) = &radiobutton_prefs($settings,['archive'],  
                                                       {'archive' => 'off'},  
                                                       \%titles,$itemcount);  
         $datatable .= $archive."\n";  
         $itemcount ++;  
     }      }
     $$rowtotal += $itemcount;      $$rowtotal += $itemcount;
     return $datatable;      return $datatable;
Line 6604  sub authordefaults_titles { Line 7546  sub authordefaults_titles {
                none => 'No override set',                 none => 'No override set',
                overon => 'Override -- webDAV on',                 overon => 'Override -- webDAV on',
                overoff => 'Override -- webDAV off',                 overoff => 'Override -- webDAV off',
                archive => 'Authors can download tar.gz file of Authoring Space',  
     );      );
 }  }
   
Line 6629  sub print_selfenrollment { Line 7570  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 6850  sub print_validation_rows { Line 7791  sub print_validation_rows {
     return $datatable;      return $datatable;
 }  }
   
   sub print_privacy {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my ($datatable,$css_class,$numinrow,@items,%names,$othertitle,$usertypes,$types);
       my $itemcount = 0;
       if ($position eq 'top') {
           $numinrow = 2;
       } else {
           @items = ('domain','author','course','community');
           %names = &Apache::lonlocal::texthash (
                        domain => 'Assigned domain role(s)',
                        author => 'Assigned co-author role(s)',
                        course => 'Assigned course role(s)',
                        community => 'Assigned community role(s)',
                    );
           $numinrow = 4;
           ($othertitle,$usertypes,$types) =
               &Apache::loncommon::sorted_inst_types($dom);
       }
       if (($position eq 'top') || ($position eq 'middle')) {
           my (%by_ip,%by_location,@intdoms,@instdoms);
           &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
           if ($position eq 'top') {
               my %curr;
               my @options = ('none','user','domain','auto');
               my %titles = &Apache::lonlocal::texthash (
                   none   => 'Not allowed',
                   user   => 'User authorizes',
                   domain => 'DC authorizes',
                   auto   => 'Unrestricted',
                   instdom => 'Other domain shares institution/provider',
                   extdom => 'Other domain has different institution/provider',
                   notify => 'Notify when role needs authorization',
               );
               my %names = &Apache::lonlocal::texthash (
                   domain => 'Domain role',
                   author => 'Co-author role',
                   course => 'Course role',
                   community => 'Community role',
               );
               my $primary_id = &Apache::lonnet::domain($dom,'primary');
               my $intdom = &Apache::lonnet::internet_dom($primary_id);
               foreach my $domtype ('instdom','extdom') {
                   my (%checked,$skip);
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   $datatable .= '<tr'.$css_class.'><td>'.$titles{$domtype}.'</td>'.
                                 '<td class="LC_left_item">';
                   if ($domtype eq 'instdom') {
                       unless (@instdoms > 1) {
                           $datatable .= &mt('Nothing to set, as no domains besides [_1] are hosted by [_2]',$dom,$intdom);
                           $skip = 1;
                       }
                   } elsif ($domtype eq 'extdom') {
                       if (keys(%by_location) == 0) {
                           $datatable .= &mt('Nothing to set, as no other hosts besides [_1]',$intdom);
                           $skip = 1;
                       }
                   }
                   unless ($skip) {
                       foreach my $roletype ('domain','author','course','community') {
                           $checked{'auto'} = ' checked="checked"';
                           if (ref($settings) eq 'HASH') {
                               if (ref($settings->{approval}) eq 'HASH') {
                                   if (ref($settings->{approval}->{$domtype}) eq 'HASH') {
                                       if ($settings->{approval}->{$domtype}->{$roletype}=~ /^(none|user|domain)$/) {
                                           $checked{$1} = ' checked="checked"';
                                           $checked{'auto'} = '';
                                       }
                                   }
                               }
                           }
                           $datatable .= '<fieldset><legend>'.$names{$roletype}.'</legend>';
                           foreach my $option (@options) {
                               $datatable .= '<span class="LC_nobreak"><label>'.
                                             '<input type="radio" name="privacy_approval_'.$domtype.'_'.$roletype.'" '.
                                             'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}.
                                             '</label></span>&nbsp; ';
                           }
                           $datatable .= '</fieldset>';
                       }
                   }
                   $datatable .= '</td></tr>';
                   $itemcount ++;
               }
               $css_class = $itemcount%2?' class="LC_odd_row"':'';
               $datatable .= '<tr'.$css_class.'><td>'.$titles{'notify'}.'</td>'.
                             '<td class="LC_left_item">';
               if ((@instdoms > 1) || (keys(%by_location) > 0)) {
                   my %curr;
                   if (ref($settings) eq 'HASH') {
                       if ($settings->{'notify'} ne '') {
                           map {$curr{$_}=1;} split(/,/,$settings->{'notify'});
                       }
                   }
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   my ($numdc,$table,$rows) = &active_dc_picker($dom,$numinrow,'checkbox',
                                                                'privacy_notify',%curr);
                   if ($numdc > 0) {
                       $datatable .= $table;
                   } else {
                       $datatable .= &mt('There are no active Domain Coordinators');
                   }
               } else {
                   $datatable .= &mt('Nothing to set here, as there are no other domains');
               }
               $datatable .='</td></tr>';
           } elsif ($position eq 'middle') {
               if ((@instdoms > 1) || (keys(%by_location) > 0)) {
                   if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
                       foreach my $item (@{$types}) {
                           $datatable .= &modifiable_userdata_row('privacy','othdom_'.$item,$settings,
                                                                  $numinrow,$itemcount,'','','','','',
                                                                  '',$usertypes->{$item});
                           $itemcount ++;
                       }
                   }
                   $datatable .= &modifiable_userdata_row('privacy','othdom_default',$settings,
                                                          $numinrow,$itemcount,'','','','','',
                                                          '',$othertitle);
                   $itemcount ++;
               } else {
                   my (@insttypes,%insttitles);
                   if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) {
                       @insttypes = @{$types};
                       %insttitles = %{$usertypes};
                   }
                   foreach my $item (@insttypes,'default') {
                       my $title;
                       if ($item eq 'default') {
                           $title = $othertitle;
                       } else {
                           $title = $insttitles{$item};
                       }
                       $css_class = $itemcount%2?' class="LC_odd_row"':'';
                       $datatable .= '<tr'.$css_class.'>'.
                                     '<td class="LC_left_item">'.$title.'</td>'.
                                     '<td class="LC_left_item">'.
                                     &mt('Nothing to set here, as there are no other domains').
                                     '</td></tr>';
                       $itemcount ++;
                   }
               }
           }
       } else {
           my $prefix;
           if ($position eq 'lower') {
               $prefix = 'priv';
           } else {
               $prefix = 'unpriv';
           }
           foreach my $item (@items) {
               $datatable .= &modifiable_userdata_row('privacy',$prefix.'_'.$item,$settings,
                                                      $numinrow,$itemcount,'','','','','',
                                                      '',$names{$item});
               $itemcount ++;
           }
       }
       if (ref($rowtotal)) {
           $$rowtotal += $itemcount;
       }
       return $datatable;
   }
   
 sub print_passwords {  sub print_passwords {
     my ($position,$dom,$confname,$settings,$rowtotal) = @_;      my ($position,$dom,$confname,$settings,$rowtotal) = @_;
     my ($datatable,$css_class);      my ($datatable,$css_class);
Line 6871  sub print_passwords { Line 7974  sub print_passwords {
         min            => 'Minimum password length',          min            => 'Minimum password length',
         max            => 'Maximum password length',          max            => 'Maximum password length',
         chars          => 'Required characters',          chars          => 'Required characters',
           expire         => 'Password expiration (days)',
         numsaved       => 'Number of previous passwords to save and disallow reuse',          numsaved       => 'Number of previous passwords to save and disallow reuse',
     );      );
     if ($position eq 'top') {      if ($position eq 'top') {
Line 7160  sub print_passwords { Line 8264  sub print_passwords {
         $datatable .= '<tr '.$css_class.'>'.          $datatable .= '<tr '.$css_class.'>'.
                       '<td>'.                        '<td>'.
                       &mt('Requirements').'<ul>'.                        &mt('Requirements').'<ul>'.
                       '<li>'.&mt("Course 'type' is not a Community").'</li>'.                        '<li>'.&mt("Course 'type' is not a Community or Placement Test").'</li>'.
                       '<li>'.&mt('User is Course Coordinator and also course owner').'</li>'.                        '<li>'.&mt('User is Course Coordinator and also course owner').'</li>'.
                       '<li>'.&mt("Student's only active roles are student role(s) in course(s) owned by this user").'</li>'.                        '<li>'.&mt("Student's only active roles are student role(s) in course(s) owned by this user").'</li>'.
                       '<li>'.&mt('User, course, and student share same domain').'</li>'.                        '<li>'.&mt('User, course, and student share same domain').'</li>'.
Line 7197  sub print_passwords { Line 8301  sub print_passwords {
   
 sub password_rules {  sub password_rules {
     my ($prefix,$itemcountref,$settings) = @_;      my ($prefix,$itemcountref,$settings) = @_;
     my ($min,$max,%chars,$numsaved,$numinrow);      my ($min,$max,%chars,$expire,$numsaved,$numinrow);
     my %titles;      my %titles;
     if ($prefix eq 'passwords') {      if ($prefix eq 'passwords') {
         %titles = &Apache::lonlocal::texthash (          %titles = &Apache::lonlocal::texthash (
Line 7229  sub password_rules { Line 8333  sub password_rules {
             map { $chars{$_} = 1; } (@{$settings->{chars}});              map { $chars{$_} = 1; } (@{$settings->{chars}});
         }          }
         if ($prefix eq 'passwords') {          if ($prefix eq 'passwords') {
               if ($settings->{expire}) {
                   $expire = $settings->{expire};
               }
             if ($settings->{numsaved}) {              if ($settings->{numsaved}) {
                 $numsaved = $settings->{numsaved};                  $numsaved = $settings->{numsaved};
             }              }
Line 7290  sub password_rules { Line 8397  sub password_rules {
     $datatable .='</table></td></tr>';      $datatable .='</table></td></tr>';
     $itemcount ++;      $itemcount ++;
     if ($prefix eq 'passwords') {      if ($prefix eq 'passwords') {
           $titles{'expire'} = &mt('Password expiration (days)');
         $titles{'numsaved'} = &mt('Number of previous passwords to save and disallow reuse');          $titles{'numsaved'} = &mt('Number of previous passwords to save and disallow reuse');
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $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>'.          $datatable .= '<tr'.$css_class.'><td>'.$titles{'numsaved'}.'</td>'.
                       '<td class="LC_left_item"><span class="LC_nobreak">'.                        '<td class="LC_left_item"><span class="LC_nobreak">'.
                       '<input type="text" name="'.$prefix.'_numsaved" value="'.$numsaved.'" size="3" '.                        '<input type="text" name="'.$prefix.'_numsaved" value="'.$numsaved.'" size="3" '.
Line 7378  sub print_wafproxy { Line 8494  sub print_wafproxy {
                             '<td class="LC_left_item" style="vertical-align: baseline;">'.                              '<td class="LC_left_item" style="vertical-align: baseline;">'.
                             &mt('Hostname').': '.                              &mt('Hostname').': '.
                             '<span class="LC_nobreak LC_cusr_emph">'.                              '<span class="LC_nobreak LC_cusr_emph">'.
                             &Apache::lonnet::hostname($server).'</span></td><td>&nbsp;</td>';                              &Apache::lonnet::hostname($server).
                               '</span></td><td>&nbsp;</td>';
             if ($othercontrol{$server}) {              if ($othercontrol{$server}) {
                 $dom_in_effect = $othercontrol{$server};                  $dom_in_effect = $othercontrol{$server};
                 my ($current,$forsaml);                  my ($current,$forsaml);
Line 7616  sub remoteip_methods { Line 8733  sub remoteip_methods {
   
 sub print_usersessions {  sub print_usersessions {
     my ($position,$dom,$settings,$rowtotal) = @_;      my ($position,$dom,$settings,$rowtotal) = @_;
     my ($css_class,$datatable,%checked,%choices);      my ($css_class,$datatable,$itemcount,%checked,%choices);
     my (%by_ip,%by_location,@intdoms);      my (%by_ip,%by_location,@intdoms,@instdoms);
     &build_location_hashes(\@intdoms,\%by_ip,\%by_location);      &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
   
     my @alldoms = &Apache::lonnet::all_domains();      my @alldoms = &Apache::lonnet::all_domains();
     my %serverhomes = %Apache::lonnet::serverhomeIDs;      my %serverhomes = %Apache::lonnet::serverhomeIDs;
     my %servers = &Apache::lonnet::internet_dom_servers($dom);      my %servers = &Apache::lonnet::internet_dom_servers($dom);
     my %altids = &id_for_thisdom(%servers);      my %altids = &id_for_thisdom(%servers);
     my $itemcount = 1;  
     if ($position eq 'top') {      if ($position eq 'top') {
         if (keys(%serverhomes) > 1) {          if (keys(%serverhomes) > 1) {
             my %spareid = &current_offloads_to($dom,$settings,\%servers);              my %spareid = &current_offloads_to($dom,$settings,\%servers);
Line 7646  sub print_usersessions { Line 8762  sub print_usersessions {
                           '</td></tr>';                            '</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') {
                           '</td></tr>';              $prefix = 'remote';
               @types = ('version','excludedomain','includedomain');
         } else {          } else {
             my %lt = &usersession_titles();              $prefix = 'hosted';
             my $numinrow = 5;              @types = ('excludedomain','includedomain');
             my $prefix;          }
             my @types;          ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles);
             if ($position eq 'bottom') {      }
                 $prefix = 'remote';      $$rowtotal += $itemcount;
                 @types = ('version','excludedomain','includedomain');      return $datatable;
             } else {  }
                 $prefix = 'hosted';  
                 @types = ('excludedomain','includedomain');  sub rules_by_location {
             }      my ($settings,$prefix,$by_location,$by_ip,$types,$titles) = @_; 
             my (%current,%checkedon,%checkedoff);      my ($datatable,$itemcount,$css_class);
             my @lcversions = &Apache::lonnet::all_loncaparevs();      if (keys(%{$by_location}) == 0) {
             my @locations = sort(keys(%by_location));          $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
             foreach my $type (@types) {          $datatable = '<tr'.$css_class.'><td colspan="2">'.
                 $checkedon{$type} = '';                       &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.').
                 $checkedoff{$type} = ' checked="checked"';                       '</td></tr>';
             }          $itemcount = 1;
             if (ref($settings) eq 'HASH') {      } else {
                 if (ref($settings->{$prefix}) eq 'HASH') {          $itemcount = 0;
                     foreach my $key (keys(%{$settings->{$prefix}})) {          my $numinrow = 5;
                         $current{$key} = $settings->{$prefix}{$key};          my (%current,%checkedon,%checkedoff);
                         if ($key eq 'version') {          my @locations = sort(keys(%{$by_location}));
                             if ($current{$key} ne '') {          foreach my $type (@{$types}) {
                                 $checkedon{$key} = ' checked="checked"';              $checkedon{$type} = '';
                                 $checkedoff{$key} = '';              $checkedoff{$type} = ' checked="checked"';
                             }          }
                         } elsif (ref($current{$key}) eq 'ARRAY') {          if (ref($settings) eq 'HASH') {
               if (ref($settings->{$prefix}) eq 'HASH') {
                   foreach my $key (keys(%{$settings->{$prefix}})) {
                       $current{$key} = $settings->{$prefix}{$key};
                       if ($key eq 'version') {
                           if ($current{$key} ne '') {
                             $checkedon{$key} = ' checked="checked"';                              $checkedon{$key} = ' checked="checked"';
                             $checkedoff{$key} = '';                              $checkedoff{$key} = '';
                         }                          }
                       } elsif (ref($current{$key}) eq 'ARRAY') {
                           $checkedon{$key} = ' checked="checked"';
                           $checkedoff{$key} = '';
                     }                      }
                 }                  }
             }              }
             foreach my $type (@types) {          }
                 next if ($type ne 'version' && !@locations);          foreach my $type (@{$types}) {
                 $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';              next if ($type ne 'version' && !@locations);
                 $datatable .= '<tr'.$css_class.'>              $css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
                                <td><span class="LC_nobreak">'.$lt{$type}.'</span><br />              $datatable .= '<tr'.$css_class.'>
                                <span class="LC_nobreak">&nbsp;                             <td><span class="LC_nobreak">'.$titles->{$type}.'</span><br />
                                <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label>&nbsp;                             <span class="LC_nobreak">&nbsp;
                                <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>';                             <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label>&nbsp;
                 if ($type eq 'version') {                             <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>';
                     my $selector = '<select name="'.$prefix.'_version">';              if ($type eq 'version') {
                     foreach my $version (@lcversions) {                  my @lcversions = &Apache::lonnet::all_loncaparevs();
                         my $selected = '';                  my $selector = '<select name="'.$prefix.'_version">';
                         if ($current{'version'} eq $version) {                  foreach my $version (@lcversions) {
                             $selected = ' selected="selected"';                      my $selected = '';
                         }                      if ($current{'version'} eq $version) {
                         $selector .= ' <option value="'.$version.'"'.                          $selected = ' selected="selected"';
                                      $selected.'>'.$version.'</option>';                      }
                     }                      $selector .= ' <option value="'.$version.'"'.
                     $selector .= '</select> ';                                   $selected.'>'.$version.'</option>';
                     $datatable .= &mt('remote server must be version: [_1] or later',$selector);                  }
                 } else {                  $selector .= '</select> ';
                     $datatable.= '<div><input type="button" value="'.&mt('check all').'" '.                  $datatable .= &mt('remote server must be version: [_1] or later',$selector);
                                  'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.              } else {
                                  ' />'.('&nbsp;'x2).                  $datatable.= '<div><input type="button" value="'.&mt('check all').'" '.
                                  '<input type="button" value="'.&mt('uncheck all').'" '.                               'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'.
                                  'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.                               ' />'.('&nbsp;'x2).
                                  "\n".                               '<input type="button" value="'.&mt('uncheck all').'" '.
                                  '</div><div><table>';                               'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'.
                     my $rem;                               "\n".
                     for (my $i=0; $i<@locations; $i++) {                               '</div><div><table>';
                         my ($showloc,$value,$checkedtype);                  my $rem;
                         if (ref($by_location{$locations[$i]}) eq 'ARRAY') {                  for (my $i=0; $i<@locations; $i++) {
                             my $ip = $by_location{$locations[$i]}->[0];                      my ($showloc,$value,$checkedtype);
                             if (ref($by_ip{$ip}) eq 'ARRAY') {                      if (ref($by_location->{$locations[$i]}) eq 'ARRAY') {
                                  $value = join(':',@{$by_ip{$ip}});                          my $ip = $by_location->{$locations[$i]}->[0];
                                 $showloc = join(', ',@{$by_ip{$ip}});                          if (ref($by_ip->{$ip}) eq 'ARRAY') {
                                 if (ref($current{$type}) eq 'ARRAY') {                              $value = join(':',@{$by_ip->{$ip}});
                                     foreach my $loc (@{$by_ip{$ip}}) {                                $showloc = join(', ',@{$by_ip->{$ip}});
                                         if (grep(/^\Q$loc\E$/,@{$current{$type}})) {                              if (ref($current{$type}) eq 'ARRAY') {
                                             $checkedtype = ' checked="checked"';                                  foreach my $loc (@{$by_ip->{$ip}}) {
                                             last;                                      if (grep(/^\Q$loc\E$/,@{$current{$type}})) {
                                         }                                          $checkedtype = ' checked="checked"';
                                           last;
                                     }                                      }
                                 }                                  }
                             }                              }
                         }                          }
                         $rem = $i%($numinrow);  
                         if ($rem == 0) {  
                             if ($i > 0) {  
                                 $datatable .= '</tr>';  
                             }  
                             $datatable .= '<tr>';  
                         }  
                         $datatable .= '<td class="LC_left_item">'.  
                                       '<span class="LC_nobreak"><label>'.  
                                       '<input type="checkbox" name="'.$prefix.'_'.$type.  
                                       '" value="'.$value.'"'.$checkedtype.' />'.$showloc.  
                                       '</label></span></td>';  
                     }                      }
                     $rem = @locations%($numinrow);                      $rem = $i%($numinrow);
                     my $colsleft = $numinrow - $rem;                      if ($rem == 0) {
                     if ($colsleft > 1 ) {                          if ($i > 0) {
                         $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.                              $datatable .= '</tr>';
                                       '&nbsp;</td>';                          }
                     } elsif ($colsleft == 1) {                          $datatable .= '<tr>';
                         $datatable .= '<td class="LC_left_item">&nbsp;</td>';                      }
                       $datatable .= '<td class="LC_left_item">'.
                                     '<span class="LC_nobreak"><label>'.
                                     '<input type="checkbox" name="'.$prefix.'_'.$type.
                                     '" value="'.$value.'"'.$checkedtype.' />'.$showloc.
                                     '</label></span></td>';
                   }
                   $rem = @locations%($numinrow);
                   my $colsleft = $numinrow - $rem;
                   if ($colsleft > 1 ) {
                       $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'.
                                     '&nbsp;</td>';
                   } elsif ($colsleft == 1) {
                       $datatable .= '<td class="LC_left_item">&nbsp;</td>';
                   }
                   $datatable .= '</tr></table>';
               }
               $datatable .= '</td></tr>';
               $itemcount ++;
           }
       }
       return ($datatable,$itemcount);
   }
   
   sub print_ssl {
       my ($position,$dom,$settings,$rowtotal) = @_;
       my ($css_class,$datatable);
       my $itemcount = 1;
       if ($position eq 'top') {
           my $primary_id = &Apache::lonnet::domain($dom,'primary');
           my $intdom = &Apache::lonnet::internet_dom($primary_id);
           my $same_institution;
           if ($intdom ne '') {
               my $internet_names = &Apache::lonnet::get_internet_names($Apache::lonnet::perlvar{'lonHostID'});
               if (ref($internet_names) eq 'ARRAY') {
                   if (grep(/^\Q$intdom\E$/,@{$internet_names})) {
                       $same_institution = 1;
                   }
               }
           }
           $css_class = $itemcount%2?' class="LC_odd_row"':'';
           $datatable = '<tr'.$css_class.'><td colspan="2">';
           if ($same_institution) {
               my %domservers = &Apache::lonnet::get_servers($dom);
               $datatable .= &LONCAPA::SSL::print_certstatus(\%domservers,'web','domprefs');
           } else {
               $datatable .= &mt("You need to be logged into one of your own domain's servers to display information about the status of LON-CAPA SSL certificates.");
           }
           $datatable .= '</td></tr>';
           $itemcount ++;
       } else {
           my %titles = &ssl_titles();
           my (%by_ip,%by_location,@intdoms,@instdoms);
           &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
           my @alldoms = &Apache::lonnet::all_domains();
           my %serverhomes = %Apache::lonnet::serverhomeIDs;
           my @domservers = &Apache::lonnet::get_servers($dom);
           my %servers = &Apache::lonnet::internet_dom_servers($dom);
           my %altids = &id_for_thisdom(%servers);
           if (($position eq 'connto') || ($position eq 'connfrom')) {
               my $legacy;
               unless (ref($settings) eq 'HASH') {
                   my $name;
                   if ($position eq 'connto') {
                       $name = 'loncAllowInsecure';
                   } else {
                       $name = 'londAllowInsecure';
                   }
                   my $primarylibserv = &Apache::lonnet::domain($dom,'primary');
                   my @ids=&Apache::lonnet::current_machine_ids();
                   if (($primarylibserv ne '') && (!grep(/^\Q$primarylibserv\E$/,@ids))) {
                       my %what = (
                                      $name => 1,
                                  );
                       my ($result,$returnhash) =
                           &Apache::lonnet::get_remote_globals($primarylibserv,\%what);
                       if ($result eq 'ok') {
                           if (ref($returnhash) eq 'HASH') {
                               $legacy = $returnhash->{$name};
                           }
                       }
                   } else {
                       $legacy = $Apache::lonnet::perlvar{$name};
                   }
               }
               foreach my $type ('dom','intdom','other') {
                   my %checked;
                   $css_class = $itemcount%2?' class="LC_odd_row"':'';
                   $datatable .= '<tr'.$css_class.'><td>'.$titles{$type}.'</td>'.
                                 '<td class="LC_right_item">';
                   my $skip; 
                   if ($type eq 'dom') {
                       unless (keys(%servers) > 1) {
                           $datatable .= &mt('Nothing to set here, as there are no other servers/VMs');    
                           $skip = 1;
                       }
                   }
                   if ($type eq 'intdom') {
                       unless (@instdoms > 1) {
                           $datatable .= &mt('Nothing to set here, as there are no other domains for this institution');
                           $skip = 1;
                       } 
                   } elsif ($type eq 'other') {
                       if (keys(%by_location) == 0) {
                           $datatable .= &mt('Nothing to set here, as there are no other institutions');
                           $skip = 1;
                       }
                   }
                   unless ($skip) {
                       $checked{'yes'} = ' checked="checked"'; 
                       if (ref($settings) eq 'HASH') {
                           if (ref($settings->{$position}) eq 'HASH') {
                               if ($settings->{$position}->{$type} =~ /^(no|req)$/) {
                                   $checked{$1} = $checked{'yes'};
                                   delete($checked{'yes'}); 
                               }
                           }
                       } else {
                           if ($legacy == 0) {
                               $checked{'req'} = $checked{'yes'};
                               delete($checked{'yes'});    
                           }
                       }
                       foreach my $option ('no','yes','req') {
                           $datatable .= '<span class="LC_nobreak"><label>'.
                                         '<input type="radio" name="'.$position.'_'.$type.'" '.
                                         'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}.
                                         '</label></span>'.('&nbsp;'x2);
                     }                      }
                     $datatable .= '</tr></table>';  
                 }                  }
                 $datatable .= '</td></tr>';                  $datatable .= '</td></tr>';
                   $itemcount ++; 
               }
           } else {
               my $prefix = 'replication';
               my @types = ('certreq','nocertreq');
               if (keys(%by_location) == 0) {
                   $datatable .= '<tr'.$css_class.'><td>'.
                                 &mt('Nothing to set here, as there are no other institutions').
                                 '</td></tr>';
                 $itemcount ++;                  $itemcount ++;
               } else {
                   ($datatable,$itemcount) = 
                       &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles);
             }              }
         }          }
     }      }
Line 7763  sub print_usersessions { Line 9015  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 7783  sub build_location_hashes { Line 9085  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 8103  sub print_loadbalancing { Line 9411  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 8136  sub print_loadbalancing { Line 9444  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 8368  sub loadbalance_rule_row { Line 9676  sub loadbalance_rule_row {
         $space = '<div style="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 8509  sub tool_titles { Line 9817  sub tool_titles {
                      unofficial => 'Unofficial courses',                       unofficial => 'Unofficial courses',
                      community  => 'Communities',                       community  => 'Communities',
                      textbook   => 'Textbook courses',                       textbook   => 'Textbook courses',
                        placement  => 'Placement tests',
                  );                   );
     return %titles;      return %titles;
 }  }
Line 8519  sub courserequest_titles { Line 9828  sub courserequest_titles {
                                    unofficial => 'Unofficial',                                     unofficial => 'Unofficial',
                                    community  => 'Communities',                                     community  => 'Communities',
                                    textbook   => 'Textbook',                                     textbook   => 'Textbook',
                                      placement  => 'Placement tests',
                                    lti        => 'LTI Provider',                                     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 8610  sub print_usercreation { Line 9920  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 9053  sub noninst_users { Line 10363  sub noninst_users {
     }      }
     $output .= '</tr><tr>';      $output .= '</tr><tr>';
     foreach my $item ('approve','email','username') {      foreach my $item ('approve','email','username') {
         $output .= '<td valign="top">';          $output .= '<td style="vertical-align: top">';
         my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom);          my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom);
         if ($item eq 'approve') {          if ($item eq 'approve') {
             %choices = &Apache::lonlocal::texthash (              %choices = &Apache::lonlocal::texthash (
Line 9404  sub print_usermodification { Line 10714  sub print_usermodification {
             $$rowtotal ++;              $$rowtotal ++;
             $rowcount ++;              $rowcount ++;
         }          }
     } elsif ($position eq 'middle') {  
         $rowcount = 0;  
         $context = 'coauthor';  
         foreach my $role ('ca','aa') {  
             $datatable .= &modifiable_userdata_row($context,$role,$settings,  
                                                    $numinrow,$rowcount);  
             $$rowtotal ++;  
             $rowcount ++;  
         }  
     } elsif ($position eq 'bottom') {      } elsif ($position eq 'bottom') {
         $context = 'course';          $context = 'course';
         $rowcount = 0;          $rowcount = 0;
Line 9533  sub print_defaults { Line 10834  sub print_defaults {
                     $datatable .= '</select>&nbsp;'.&mt('Internal ID:').'&nbsp;<b>'.$item.'</b>&nbsp;'.                      $datatable .= '</select>&nbsp;'.&mt('Internal ID:').'&nbsp;<b>'.$item.'</b>&nbsp;'.
                                   '<input type="checkbox" name="inststatus_delete" value="'.$item.'" />'.                                    '<input type="checkbox" name="inststatus_delete" value="'.$item.'" />'.
                                   &mt('delete').'</span></td>'.                                    &mt('delete').'</span></td>'.
                                   '<td class="LC_left_item"><span class="LC_nobreak">'.&mt('Name displayed').':'.                                    '<td class="LC_left_item" colspan="2"><span class="LC_nobreak">'.&mt('Name displayed').':'.
                                   '<input type="text" size="20" name="inststatus_title_'.$item.'" value="'.$title.'" />'.                                    '<input type="text" size="20" name="inststatus_title_'.$item.'" value="'.$title.'" />'.
                                   '</span></td></tr>';                                    '</span></td></tr>';
                 }                  }
Line 9552  sub print_defaults { Line 10853  sub print_defaults {
                 $datatable .= '</select>&nbsp;'.&mt('Internal ID:').                  $datatable .= '</select>&nbsp;'.&mt('Internal ID:').
                               '<input type="text" size="10" name="addinststatus" value="" />'.                                '<input type="text" size="10" name="addinststatus" value="" />'.
                               '&nbsp;'.&mt('(new)').                                '&nbsp;'.&mt('(new)').
                               '</span></td><td class="LC_left_item"><span class="LC_nobreak">'.                                '</span></td><td class="LC_left_item" colspan="2"><span class="LC_nobreak">'.
                               &mt('Name displayed').':'.                                &mt('Name displayed').':'.
                               '<input type="text" size="20" name="addinststatus_title" value="" /></span></td>'.                                '<input type="text" size="20" name="addinststatus_title" value="" /></span></td>'.
                               '</tr>'."\n";                                '</tr>'."\n";
Line 9787  sub print_scantronformat { Line 11088  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 10025  sub print_coursecategories { Line 11326  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 10043  sub print_coursecategories { Line 11348  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 10083  sub print_coursecategories { Line 11399  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 10109  sub print_coursecategories { Line 11439  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 10135  sub print_coursecategories { Line 11468  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 10158  sub print_coursecategories { Line 11491  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 10190  sub print_coursecategories { Line 11523  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 10279  sub print_serverstatuses { Line 11612  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 {
Line 10385  sub passwords_javascript { Line 11718  sub passwords_javascript {
             authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.',              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.',              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).',              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).',              passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).',
         );          );
     } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {      } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {
Line 10446  function warnInt$prefix(field) { Line 11780  function warnInt$prefix(field) {
             field.value = '';              field.value = '';
         }          }
         if (field.value != '') {          if (field.value != '') {
             if (!regexdigit.test(field.value)) {              if (field.name == '${prefix}_expire') {
                 if (field.name == '${prefix}_max') {                  var regexpposnum=/^\\d+(|\\.\\d*)\$/; 
                     alert('$intalert{passmax}');                  if (!regexpposnum.test(field.value)) {
                       alert('$intalert{passexp}');
                       field.value = '';
                 } else {                  } else {
                     if (field.name == '${prefix}_numsaved') {                      var expval = parseFloat(field.value);
                         alert('$intalert{passnum}');                      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 = '';
                 }                  }
                 field.value = '';  
             }              }
         }          }
     }      }
Line 10489  sub coursecategories_javascript { Line 11837  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 10561  function categoryCheck(form) { Line 11911  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 10577  sub initialize_categories { Line 11931  sub initialize_categories {
     my %default_names = &Apache::lonlocal::texthash (      my %default_names = &Apache::lonlocal::texthash (
                       instcode    => 'Official courses (with institutional codes)',                        instcode    => 'Official courses (with institutional codes)',
                       communities => 'Communities',                        communities => 'Communities',
                         placement   => 'Placement Tests',
                         );                          );
     my $select0 = ' selected="selected"';      my %selnum = (
     my $select1 = '';                     instcode    => '0',
     foreach my $default ('instcode','communities') {                     communities => '1',
                      placement   => '2',
                    );
       my %selected;
       foreach my $default ('instcode','communities','placement') {
         $css_class = $itemcount%2?' class="LC_odd_row"':'';          $css_class = $itemcount%2?' class="LC_odd_row"':'';
         $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','0'".');"';          $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','0'".');"';
         if ($default eq 'communities') {          map { $selected{$selnum{$_}} = '' } keys(%selnum);
             $select1 = $select0;          $selected{$selnum{$default}} = ' selected="selected"';
             $select0 = '';  
         }  
         $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'          $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'
                      .'<select name="'.$default.'_pos">'                       .'<select name="'.$default.'_pos"'.$chgstr.'>'
                      .'<option value="0"'.$select0.'>1</option>'                       .'<option value="0"'.$selected{'0'}.'>1</option>'
                      .'<option value="1"'.$select1.'>2</option>'                       .'<option value="1"'.$selected{'1'}.'>2</option>'
                      .'<option value="2">3</option></select>&nbsp;'                       .'<option value="2"'.$selected{'2'}.'>3</option>'
                        .'<option value="3">4</option></select>&nbsp;'
                      .$default_names{$default}                       .$default_names{$default}
                      .'</span></td><td><span class="LC_nobreak">'                       .'</span></td><td><span class="LC_nobreak">'
                      .'<label><input type="radio" name="'.$default.'" value="1" checked="checked" />'                       .'<label><input type="radio" name="'.$default.'" value="1" checked="checked" />'
Line 10606  sub initialize_categories { Line 11964  sub initialize_categories {
                   .'<select name="addcategory_pos"'.$chgstr.'>'                    .'<select name="addcategory_pos"'.$chgstr.'>'
                   .'<option value="0">1</option>'                    .'<option value="0">1</option>'
                   .'<option value="1">2</option>'                    .'<option value="1">2</option>'
                   .'<option value="2" selected="selected">3</option></select>&nbsp;'                    .'<option value="2">3</option>'
                     .'<option value="3" selected="selected">4</option></select>&nbsp;'
                   .&mt('Add category').'</span></td><td><span class="LC_nobreak">'.&mt('Name:')                    .&mt('Add category').'</span></td><td><span class="LC_nobreak">'.&mt('Name:')
                   .'&nbsp;<input type="text" size="20" name="addcategory_name" value="" /></span>'                    .'&nbsp;<input type="text" size="20" name="addcategory_name" value="" /></span>'
                   .'</td></tr>';                    .'</td></tr>';
Line 10720  sub modifiable_userdata_row { Line 12079  sub modifiable_userdata_row {
         }          }
     } elsif ($context eq 'lti') {      } elsif ($context eq 'lti') {
         $rolename = &mt('Institutional data used (if available)');          $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 10768  sub modifiable_userdata_row { Line 12129  sub modifiable_userdata_row {
             if (ref($settings) eq 'HASH') {              if (ref($settings) eq 'HASH') {
                 $hashref = $settings->{'instdata'};                  $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') {          } elsif (ref($settings->{$context}) eq 'HASH') {
             if (ref($settings->{$context}->{$role}) eq 'HASH') {              if (ref($settings->{$context}->{$role}) eq 'HASH') {
                 $hashref = $settings->{'lti_instdata'};                  $hashref = $settings->{'lti_instdata'};
Line 10805  sub modifiable_userdata_row { Line 12173  sub modifiable_userdata_row {
         unless ($role eq 'emailusername') {          unless ($role eq 'emailusername') {
             if (exists($checks{$fields[$i]})) {              if (exists($checks{$fields[$i]})) {
                 $check = $checks{$fields[$i]};                  $check = $checks{$fields[$i]};
               } 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') {              } elsif ($context ne 'lti') {
                 if ($role eq 'st') {                  if ($role eq 'st') {
                     if (ref($settings) ne 'HASH') {                      if (ref($settings) ne 'HASH') {
Line 10833  sub modifiable_userdata_row { Line 12219  sub modifiable_userdata_row {
         } else {          } else {
             if ($context eq 'lti') {              if ($context eq 'lti') {
                 $prefix = 'lti';                  $prefix = 'lti';
             } elsif ($context eq 'coauthor') {              } elsif ($context eq 'privacy') {
                 $prefix = 'cacanmodify';                  $prefix = 'privacy';
             }              }
             $output .= '<label>'.              $output .= '<label>'.
                        '<input type="checkbox" name="'.$prefix.'_'.$role.'" '.                         '<input type="checkbox" name="'.$prefix.'_'.$role.'" '.
Line 11725  sub check_exempt_addresses { Line 13111  sub check_exempt_addresses {
 sub color_font_choices {  sub color_font_choices {
     my %choices =      my %choices =
         &Apache::lonlocal::texthash (          &Apache::lonlocal::texthash (
               img => "Header",
             bgs => "Background colors",              bgs => "Background colors",
             links => "Link colors",              links => "Link colors",
             images => "Images",              images => "Images",
Line 11779  sub modify_ipaccess { Line 13166  sub modify_ipaccess {
     foreach my $idx (@items) {      foreach my $idx (@items) {
         my $itemid = $itemids{$idx};          my $itemid = $itemids{$idx};
         next unless ($itemid);          next unless ($itemid);
         my ($position,%current);          my %current;
         if ($idx eq 'add') {          unless ($idx eq 'add') {
             $position = $env{'form.ipaccess_pos_add'};  
         } else {  
             $position = $env{'form.ipaccess_pos_'.$itemid};  
             if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {              if (ref($domconfig{'ipaccess'}{$itemid}) eq 'HASH') {
                 %current = %{$domconfig{'ipaccess'}{$itemid}};                  %current = %{$domconfig{'ipaccess'}{$itemid}};
             }              }
         }          }
           my $position = $env{'form.ipaccess_pos_'.$itemid};
         $position =~ s/\D+//g;          $position =~ s/\D+//g;
         if ($position ne '') {          if ($position ne '') {
             $allpos[$position] = $itemid;              $allpos[$position] = $itemid;
Line 12040  sub modify_authordefaults { Line 13425  sub modify_authordefaults {
                            'nocodemirror' => 'off',                             'nocodemirror' => 'off',
                            'daxecollapse' => 'off',                             'daxecollapse' => 'off',
                            'domcoordacc'  => 'on',                             'domcoordacc'  => 'on',
                            'editors'      => ['edit','xml'],                             'editors'      => ['edit','xml'].
                            'authorquota'  => 500,                             'authorquota'  => 500,
                            'webdav'       => 0,                             'webdav'       => 0,
                            'archive'      => 'off',  
                          );                           );
     my %titles = &authordefaults_titles();      my %titles = &authordefaults_titles();
     foreach my $item ('nocodemirror','daxecollapse','domcoordacc','archive') {      foreach my $item ('nocodemirror','daxecollapse','domcoordacc') {
         if ($env{'form.'.$item} =~ /^(0|1)$/) {          if ($env{'form.'.$item} =~ /^(0|1)$/) {
             $confhash{$item} = $env{'form.'.$item};              $confhash{$item} = $env{'form.'.$item};
         }          }
Line 12090  sub modify_authordefaults { Line 13474  sub modify_authordefaults {
         $save_quotas{'webdav'}{'_LC_adv'} = $env{'form.webdav_LC_adv'};          $save_quotas{'webdav'}{'_LC_adv'} = $env{'form.webdav_LC_adv'};
     }      }
     if (ref($domconfig{'authordefaults'}) eq 'HASH') {      if (ref($domconfig{'authordefaults'}) eq 'HASH') {
         foreach my $item ('nocodemirror','daxecollapse','domcoordacc','copyright','sourceavail','archive') {          foreach my $item ('nocodemirror','daxecollapse','domcoordacc','copyright','sourceavail') {
             if ($domconfig{'authordefaults'}{$item} ne $confhash{$item}) {              if ($domconfig{'authordefaults'}{$item} ne $confhash{$item}) {
                 $changes{$item} = 1;                  $changes{$item} = 1;
              }               }
Line 12112  sub modify_authordefaults { Line 13496  sub modify_authordefaults {
         }          }
     } else {      } else {
         my @offon = ('off','on');          my @offon = ('off','on');
         foreach my $item ('nocodemirror','daxecollapse','domcoordacc','archive') {          foreach my $item ('nocodemirror','daxecollapse','domcoordacc') {
             if ($offon[$confhash{$item}] ne $staticdefaults{$item}) {              if ($offon[$confhash{$item}] ne $staticdefaults{$item}) {
                 $changes{$item} = 1;                  $changes{$item} = 1; 
             }              }
         }          }
         foreach my $item ('copyright','sourceavail') {          foreach my $item ('copyright','sourceavail') {
Line 12122  sub modify_authordefaults { Line 13506  sub modify_authordefaults {
                 $changes{$item} = 1;                  $changes{$item} = 1;
             }              }
         }          }
         my @diffs =  
             &Apache::loncommon::compare_arrays($confhash{'editors'},  
                                                $staticdefaults{'editors'});  
         unless (@diffs == 0) {  
             $changes{'editors'} = 1;  
         }  
     }      }
     foreach my $key ('authorquota','webdav') {      foreach my $key ('authorquota','webdav') {
         if (ref($curr_quotas{$key}) eq 'HASH') {          if (ref($curr_quotas{$key}) eq 'HASH') {
Line 12209  sub modify_authordefaults { Line 13587  sub modify_authordefaults {
                 $resulttext .= '</ul></li>';                  $resulttext .= '</ul></li>';
             }              }
             my $domcoordoverride;              my $domcoordoverride;
             foreach my $key ('editors','authorquota','webdav','webdav_LC_adv','archive') {              foreach my $key ('editors','authorquota','webdav','webdav_LC_adv') {
                 if (exists($changes{$key})) {                  if (exists($changes{$key})) {
                     my $shown;                      my $shown;
                     unless ($domcoordoverride) {                      unless ($domcoordoverride) {
Line 12241  sub modify_authordefaults { Line 13619  sub modify_authordefaults {
                         } else {                          } else {
                             $shown = $titles{'none'};                              $shown = $titles{'none'};
                         }                          }
                     } elsif ($key eq 'archive') {  
                         $domdefaults{$key} = $confhash{$key};  
                         $shown = ($confhash{$key} ? &mt('Yes') : &mt('No'));  
                     }                      }
                     $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{$key},$shown).'</li>';                      $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{$key},$shown).'</li>';
                 }                     }
             }              }
             if ($domcoordoverride) {              if ($domcoordoverride) {
                 $resulttext .= '</ul></li>';                  $resulttext .= '</ul></li>';
             }              }
             $resulttext .= '</ul>';  
             my $cachetime = 24*60*60;              my $cachetime = 24*60*60;
             &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);              &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
             if (ref($lastactref) eq 'HASH') {              if (ref($lastactref) eq 'HASH') {
Line 12320  sub modify_colors { Line 13694  sub modify_colors {
             @images = ('img','logo','domlogo','login');              @images = ('img','logo','domlogo','login');
             @bgs = ('pgbg','mainbg','sidebg');              @bgs = ('pgbg','mainbg','sidebg');
         } else {          } else {
             @images = ();              @images = ('img');
             @bgs = ('pgbg','tabbg','sidebg');              @bgs = ('pgbg','tabbg','sidebg');
         }          }
         my %defaults = &role_defaults($role,\@bgs,\@links,\@images,\@logintext);          my %defaults = &role_defaults($role,\@bgs,\@links,\@images,\@logintext);
         $env{'form.'.$role.'_font'} = lc($env{'form.'.$role.'_font'});          unless ($env{'form.'.$role.'_font'} eq $defaults{'font'}) {
         if ($env{'form.'.$role.'_font'} =~ /^\w+/) {  
             $env{'form.'.$role.'_font'} = '#'.$env{'form.'.$role.'_font'};  
         }  
         unless ($env{'form.'.$role.'_font'} eq lc($defaults{'font'})) {  
             $confhash->{$role}{'font'} = $env{'form.'.$role.'_font'};              $confhash->{$role}{'font'} = $env{'form.'.$role.'_font'};
         }          }
         if ($role eq 'login') {          if ($role eq 'login') {
Line 12346  sub modify_colors { Line 13716  sub modify_colors {
             if ($env{'form.'.$role.'_fontmenu'} =~ /^\w+/) {              if ($env{'form.'.$role.'_fontmenu'} =~ /^\w+/) {
                 $env{'form.'.$role.'_fontmenu'} = '#'.$env{'form.'.$role.'_fontmenu'};                  $env{'form.'.$role.'_fontmenu'} = '#'.$env{'form.'.$role.'_fontmenu'};
             }              }
             unless ($env{'form.'.$role.'_fontmenu'} eq lc($defaults{'fontmenu'})) {              unless($env{'form.'.$role.'_fontmenu'} eq lc($defaults{'fontmenu'})) {
                 $confhash->{$role}{'fontmenu'} = $env{'form.'.$role.'_fontmenu'};                  $confhash->{$role}{'fontmenu'} = $env{'form.'.$role.'_fontmenu'};
             }              }
         }          }
Line 12655  sub display_colorchgs { Line 14025  sub display_colorchgs {
                         } 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 12826  sub modify_quotas { Line 14196  sub modify_quotas {
         $context = $action;          $context = $action;
     }      }
     if ($context eq 'requestcourses') {      if ($context eq 'requestcourses') {
         @usertools = ('official','unofficial','community','textbook','lti');          @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 12873  sub modify_quotas { Line 14243  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','lti');          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 13459  sub modify_quotas { Line 14829  sub modify_quotas {
                             $resulttext .= '<li>'.&mt('Validated course requests identified as processed by: [_1]',                              $resulttext .= '<li>'.&mt('Validated course requests identified as processed by: [_1]',
                                                      '<b>'.$changes{'validation'}{'dc'}.'</b>').'</li>';                                                       '<b>'.$changes{'validation'}{'dc'}.'</b>').'</li>';
                         }                          }
                         $resulttext .= '</ul></li>';  
                     }                      }
                 }                  }
             }              }
Line 13897  sub lti_security_results { Line 15266  sub lti_security_results {
     return $output;      return $output;
 }  }
   
   sub modify_proctoring {
       my ($r,$dom,$action,$lastactref,%domconfig) = @_;
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my (@allpos,%changes,%confhash,%encconfhash,$errors,$resulttext,%imgdeletions);
       my $confname = $dom.'-domainconfig';
       my $servadm = $r->dir_config('lonAdmEMail');
       my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
       my %providernames = &proctoring_providernames();
       my $maxnum = scalar(keys(%providernames));
   
       my (%requserfields,%optuserfields,%defaults,%extended,%crsconf,@courseroles,@ltiroles);
       my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data();
       if (ref($requref) eq 'HASH') {
           %requserfields = %{$requref};
       }
       if (ref($opturef) eq 'HASH') {
           %optuserfields = %{$opturef};
       }
       if (ref($defref) eq 'HASH') {
           %defaults = %{$defref};
       }
       if (ref($extref) eq 'HASH') {
           %extended = %{$extref};
       }
       if (ref($crsref) eq 'HASH') {
           %crsconf = %{$crsref};
       }
       if (ref($rolesref) eq 'ARRAY') {
           @courseroles = @{$rolesref};
       }
       if (ref($ltiref) eq 'ARRAY') {
           @ltiroles = @{$ltiref};
       }
   
       if (ref($domconfig{$action}) eq 'HASH') {
           my @todeleteimages = &Apache::loncommon::get_env_multiple('form.proctoring_image_del');
           if (@todeleteimages) {
               map { $imgdeletions{$_} = 1; } @todeleteimages;
           }
       }
       my %customadds;
       my @newcustom = &Apache::loncommon::get_env_multiple('form.proctoring_customadd');
       if (@newcustom) {
           map { $customadds{$_} = 1; } @newcustom;
       }
       foreach my $provider (sort(keys(%providernames))) {
           $confhash{$provider} = {};
           my $pos = $env{'form.proctoring_pos_'.$provider};
           $pos =~ s/\D+//g;
           $allpos[$pos] = $provider;
           my (%current,%currentenc);
           my $showroles = 0;
           if (ref($domconfig{$action}) eq 'HASH') {
               if (ref($domconfig{$action}{$provider}) eq 'HASH') {
                   %current = %{$domconfig{$action}{$provider}};
                   foreach my $item ('key','secret') { 
                       $currentenc{$item} = $current{$item};
                       delete($current{$item});
                   }
               }
           }
           if ($env{'form.proctoring_available_'.$provider}) {
               $confhash{$provider}{'available'} = 1;
               unless ($current{'available'}) {
                   $changes{$provider} = 1;
               }
           } else {
               %{$confhash{$provider}} = %current;
               %{$encconfhash{$provider}} = %currentenc;
               $confhash{$provider}{'available'} = 0;
               if ($current{'available'}) {
                   $changes{$provider} = 1;
               }
           }
           if ($confhash{$provider}{'available'}) {
               foreach my $field ('lifetime','version','sigmethod','url','key','secret') {
                   my $possval = $env{'form.proctoring_'.$provider.'_'.$field};
                   if ($field eq 'lifetime') {
                       if ($possval =~ /^\d+$/) {
                           $confhash{$provider}{$field} = $possval;
                       }
                   } elsif ($field eq 'version') {
                       if ($possval =~ /^\d+\.\d+$/) {
                           $confhash{$provider}{$field} = $possval;
                       }
                   } elsif ($field eq 'sigmethod') {
                       if ($possval =~ /^\QHMAC-SHA\E(1|256)$/) {
                           $confhash{$provider}{$field} = $possval;
                       }
                   } elsif ($field eq 'url') {
                       $confhash{$provider}{$field} = $possval;
                   } elsif (($field eq 'key') || ($field eq 'secret')) {
                       $encconfhash{$provider}{$field} = $possval;
                       unless ($currentenc{$field} eq $possval) {
                           $changes{$provider} = 1;
                       }
                   }
                   unless (($field eq 'key') || ($field eq 'secret')) {
                       unless ($current{$field} eq $confhash{$provider}{$field}) {
                           $changes{$provider} = 1;
                       }
                   }
               }
               if ($imgdeletions{$provider}) {
                   $changes{$provider} = 1;
               } elsif ($env{'form.proctoring_image_'.$provider.'.filename'} ne '') {
                   my ($imageurl,$error) =
                       &process_proctoring_image($r,$dom,$confname,'proctoring_image_'.$provider,$provider,
                                                 $configuserok,$switchserver,$author_ok);
                   if ($imageurl) {
                       $confhash{$provider}{'image'} = $imageurl;
                       $changes{$provider} = 1; 
                   }
                   if ($error) {
                       &Apache::lonnet::logthis($error);
                       $errors .= '<li><span class="LC_error">'.$error.'</span></li>';
                   }
               } elsif (exists($current{'image'})) {
                   $confhash{$provider}{'image'} = $current{'image'};
               }
               if (ref($requserfields{$provider}) eq 'ARRAY') {
                   if (@{$requserfields{$provider}} > 0) {
                       if (grep(/^user$/,@{$requserfields{$provider}})) {
                           if ($env{'form.proctoring_userincdom_'.$provider}) {
                               $confhash{$provider}{'incdom'} = 1;
                           }
                           unless ($current{'incdom'} eq $confhash{$provider}{'incdom'}) {
                               $changes{$provider} = 1;
                           }
                       }
                       if (grep(/^roles$/,@{$requserfields{$provider}})) {
                           $showroles = 1;
                       }
                   }
               }
               $confhash{$provider}{'fields'} = [];
               if (ref($optuserfields{$provider}) eq 'ARRAY') {
                   if (@{$optuserfields{$provider}} > 0) {
                       my @optfields = &Apache::loncommon::get_env_multiple('form.proctoring_optional_'.$provider);
                       foreach my $field (@{$optuserfields{$provider}}) {
                           if (grep(/^\Q$field\E$/,@optfields)) {
                               push(@{$confhash{$provider}{'fields'}},$field);
                           }
                       }
                   }
                   if (ref($current{'fields'}) eq 'ARRAY') {
                       unless ($changes{$provider}) {
                           my @new = sort(@{$confhash{$provider}{'fields'}});
                           my @old = sort(@{$current{'fields'}}); 
                           my @diffs = &Apache::loncommon::compare_arrays(\@new,\@old);
                           if (@diffs) {
                               $changes{$provider} = 1;
                           }
                       }
                   } elsif (@{$confhash{$provider}{'fields'}}) {
                       $changes{$provider} = 1;
                   }
               }
               if (ref($defaults{$provider}) eq 'ARRAY') {  
                   if (@{$defaults{$provider}} > 0) {
                       my %options;
                       if (ref($extended{$provider}) eq 'HASH') {
                           %options = %{$extended{$provider}};
                       }
                       my @checked = &Apache::loncommon::get_env_multiple('form.proctoring_defaults_'.$provider);
                       foreach my $field (@{$defaults{$provider}}) {
                           if ((exists($options{$field})) && (ref($options{$field}) eq 'ARRAY')) {
                               my $poss = $env{'form.proctoring_defaults_'.$field.'_'.$provider};
                               if (grep(/^\Q$poss\E$/,@{$options{$field}})) {
                                   push(@{$confhash{$provider}{'defaults'}},$poss); 
                               }
                           } elsif ((exists($options{$field})) && (ref($options{$field}) eq 'HASH')) {
                               foreach my $inner (keys(%{$options{$field}})) {
                                   if (ref($options{$field}{$inner}) eq 'ARRAY') {
                                       my $poss = $env{'form.proctoring_'.$inner.'_'.$provider};
                                       if (grep(/^\Q$poss\E$/,@{$options{$field}{$inner}})) {
                                           $confhash{$provider}{'defaults'}{$inner} = $poss;
                                       }
                                   } else {
                                       $confhash{$provider}{'defaults'}{$inner} = $env{'form.proctoring_'.$inner.'_'.$provider};
                                   }
                               }
                           } else {
                               if (grep(/^\Q$field\E$/,@checked)) {
                                   push(@{$confhash{$provider}{'defaults'}},$field);
                               }
                           }
                       }
                       if (ref($confhash{$provider}{'defaults'}) eq 'ARRAY') {
                           if (ref($current{'defaults'}) eq 'ARRAY') {
                               unless ($changes{$provider}) {
                                   my @new = sort(@{$confhash{$provider}{'defaults'}});
                                   my @old = sort(@{$current{'defaults'}});
                                   my @diffs = &Apache::loncommon::compare_arrays(\@new,\@old);
                                   if (@diffs) {
                                       $changes{$provider} = 1; 
                                   }
                               }
                           } elsif (ref($current{'defaults'}) eq 'ARRAY') {
                               if (@{$current{'defaults'}}) {
                                   $changes{$provider} = 1;
                               }
                           }
                       } elsif (ref($confhash{$provider}{'defaults'}) eq 'HASH') {
                           if (ref($current{'defaults'}) eq 'HASH') {
                               unless ($changes{$provider}) {
                                   foreach my $key (keys(%{$confhash{$provider}{'defaults'}})) {
                                       unless ($confhash{$provider}{'defaults'}{$key} eq $current{'defaults'}{$key}) {
                                           $changes{$provider} = 1;
                                           last;
                                       }
                                   }
                               }
                               unless ($changes{$provider}) {
                                   foreach my $key (keys(%{$current{'defaults'}})) {
                                       unless ($current{'defaults'}{$key} eq $confhash{$provider}{'defaults'}{$key}) {
                                           $changes{$provider} = 1;
                                           last;
                                       }
                                   }
                               }
                           } elsif (keys(%{$confhash{$provider}{'defaults'}})) {
                               $changes{$provider} = 1;
                           }
                       }
                   }
               }
               if (ref($crsconf{$provider}) eq 'ARRAY') {
                   if (@{$crsconf{$provider}} > 0) {
                       $confhash{$provider}{'crsconf'} = [];
                       my @checked = &Apache::loncommon::get_env_multiple('form.proctoring_crsconf_'.$provider);
                       foreach my $crsfield (@{$crsconf{$provider}}) {
                           if (grep(/^\Q$crsfield\E$/,@checked)) {
                               push(@{$confhash{$provider}{'crsconf'}},$crsfield);
                           }
                       }
                       if (ref($current{'crsconf'}) eq 'ARRAY') {
                           unless ($changes{$provider}) {  
                               my @new = sort(@{$confhash{$provider}{'crsconf'}});
                               my @old = sort(@{$current{'crsconf'}});
                               my @diffs = &Apache::loncommon::compare_arrays(\@new,\@old);
                               if (@diffs) {
                                   $changes{$provider} = 1;
                               }
                           }
                       } elsif (@{$confhash{$provider}{'crsconf'}}) {
                           $changes{$provider} = 1;
                       }
                   }
               }
               if ($showroles) {
                   $confhash{$provider}{'roles'} = {};
                   foreach my $role (@courseroles) {
                       my $poss = $env{'form.proctoring_roles_'.$role.'_'.$provider};
                       if (grep(/^\Q$poss\E$/,@ltiroles)) {
                           $confhash{$provider}{'roles'}{$role} = $poss;
                       }
                   }
                   unless ($changes{$provider}) {
                       if (ref($current{'roles'}) eq 'HASH') {
                           foreach my $role (keys(%{$current{'roles'}})) {
                               unless ($current{'roles'}{$role} eq $confhash{$provider}{'roles'}{$role}) {
                                   $changes{$provider} = 1;
                                   last
                               }
                           }
                           unless ($changes{$provider}) {
                               foreach my $role (keys(%{$confhash{$provider}{'roles'}})) {
                                   unless ($confhash{$provider}{'roles'}{$role} eq $current{'roles'}{$role}) {
                                       $changes{$provider} = 1;
                                       last;
                                   }
                               }
                           }
                       } elsif (keys(%{$confhash{$provider}{'roles'}})) {
                           $changes{$provider} = 1;
                       }
                   }
               }
               if (ref($current{'custom'}) eq 'HASH') {
                   my @customdels = &Apache::loncommon::get_env_multiple('form.proctoring_customdel_'.$provider);
                   foreach my $key (keys(%{$current{'custom'}})) {
                       if (grep(/^\Q$key\E$/,@customdels)) {
                           $changes{$provider} = 1;
                       } else {
                           $confhash{$provider}{'custom'}{$key} = $env{'form.proctoring_customval_'.$key.'_'.$provider};
                           if ($confhash{$provider}{'custom'}{$key} ne $current{'custom'}{$key}) {
                               $changes{$provider} = 1;
                           }
                       }
                   }
               }
               if ($customadds{$provider}) {
                   my $name = $env{'form.proctoring_custom_name_'.$provider};
                   $name =~ s/(`)/'/g;
                   $name =~ s/^\s+//;
                   $name =~ s/\s+$//;
                   my $value = $env{'form.proctoring_custom_value_'.$provider};
                   $value =~ s/(`)/'/g;
                   $value =~ s/^\s+//;
                   $value =~ s/\s+$//;
                   if ($name ne '') {
                       $confhash{$provider}{'custom'}{$name} = $value;
                       $changes{$provider} = 1;
                   }
               }
           }
       }
       if (@allpos > 0) {
           my $idx = 0;
           foreach my $provider (@allpos) {
               if ($provider ne '') {
                   $confhash{$provider}{'order'} = $idx;
                   unless ($changes{$provider}) {
                       if (ref($domconfig{$action}) eq 'HASH') {
                           if (ref($domconfig{$action}{$provider}) eq 'HASH') {
                               if ($domconfig{$action}{$provider}{'order'} ne $idx) {
                                   $changes{$provider} = 1;
                               }
                           }
                       }
                   }
                   $idx ++;
               }
           }
       }
       my %proc_hash = (
                             $action => { %confhash }
                          );
       my $putresult = &Apache::lonnet::put_dom('configuration',\%proc_hash,
                                                $dom);
       if ($putresult eq 'ok') {
           my %proc_enchash = (
                                $action => { %encconfhash }
                            );
           &Apache::lonnet::put_dom('encconfig',\%proc_enchash,$dom,undef,1);
           if (keys(%changes) > 0) {
               my $cachetime = 24*60*60;
               my %procall = %confhash;
               foreach my $provider (keys(%procall)) {
                   if (ref($encconfhash{$provider}) eq 'HASH') {
                       foreach my $key ('key','secret') {
                           $procall{$provider}{$key} = $encconfhash{$provider}{$key};
                       }
                   }
               }
               &Apache::lonnet::do_cache_new('proctoring',$dom,\%procall,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'proctoring'} = 1;
               }
               $resulttext = &mt('Configuration for Provider(s) with changes:').'<ul>';
               my %bynum;
               foreach my $provider (sort(keys(%changes))) {
                   my $position = $confhash{$provider}{'order'};
                   $bynum{$position} = $provider;
               }
               foreach my $pos (sort { $a <=> $b } keys(%bynum)) {
                   my $provider = $bynum{$pos};
                   my %lt = &proctoring_titles($provider);
                   my %fieldtitles = &proctoring_fieldtitles($provider);
                   if (!$confhash{$provider}{'available'}) {
                       $resulttext .= '<li>'.&mt('Proctoring integration unavailable for: [_1]','<b>'.$providernames{$provider}.'</b>').'</li>';
                   } else {
                       $resulttext .= '<li>'.&mt('Proctoring integration available for: [_1]','<b>'.$providernames{$provider}.'</b>');
                       if ($confhash{$provider}{'image'}) {
                           $resulttext .= '&nbsp;'.
                                          '<img src="'.$confhash{$provider}{'image'}.'"'.
                                          ' alt="'.&mt('Proctoring icon').'" />';
                       }
                       $resulttext .= '<ul>';
                       my $position = $pos + 1;
                       $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>';
                       foreach my $key ('version','sigmethod','url','lifetime') {
                           if ($confhash{$provider}{$key} ne '') {
                               $resulttext .= '<li>'.$lt{$key}.':&nbsp;'.$confhash{$provider}{$key}.'</li>';
                           }
                       }
                       if ($encconfhash{$provider}{'key'} ne '') {
                           $resulttext .= '<li>'.$lt{'key'}.':&nbsp;'.$encconfhash{$provider}{'key'}.'</li>';
                       }
                       if ($encconfhash{$provider}{'secret'} ne '') {
                           $resulttext .= '<li>'.$lt{'secret'}.':&nbsp;';
                           my $num = length($encconfhash{$provider}{'secret'});
                           $resulttext .= ('*'x$num).'</li>';
                       }
                       my (@fields,$showroles);
                       if (ref($requserfields{$provider}) eq 'ARRAY') {
                           push(@fields,@{$requserfields{$provider}});
                       }
                       if (ref($confhash{$provider}{'fields'}) eq 'ARRAY') {
                           push(@fields,@{$confhash{$provider}{'fields'}});
                       } elsif (ref($confhash{$provider}{'fields'}) eq 'HASH') {
                           push(@fields,(keys(%{$confhash{$provider}{'fields'}})));
                       }
                       if (@fields) {
                           if (grep(/^roles$/,@fields)) {
                               $showroles = 1;
                           }
                           $resulttext .= '<li>'.$lt{'udsl'}.':&nbsp;"'.
                                          join('", "', map { $lt{$_}; } @fields).'"</li>';
                       }
                       if (ref($requserfields{$provider}) eq 'ARRAY') {
                           if (grep(/^user$/,@{$requserfields{$provider}})) {
                               if ($confhash{$provider}{'incdom'}) {
                                   $resulttext .= '<li>'.&mt('[_1] sent as [_2]',$lt{'user'},$lt{'uname:dom'}).'</li>';
                               } else { 
                                   $resulttext .= '<li>'.&mt('[_1] sent as [_2]',$lt{'user'},$lt{'username'}).'</li>';
                               }
                           }
                       }
                       if (ref($confhash{$provider}{'defaults'}) eq 'ARRAY') {
                           if (@{$confhash{$provider}{'defaults'}} > 0) {
                               $resulttext .= '<li>'.$lt{'defa'};
                               foreach my $field (@{$confhash{$provider}{'defaults'}}) {
                                   $resulttext .= ' "'.$fieldtitles{$field}.'",';
                               }
                               $resulttext =~ s/,$//;
                               $resulttext .= '</li>';
                           }
                       } elsif (ref($confhash{$provider}{'defaults'}) eq 'HASH') {
                           if (keys(%{$confhash{$provider}{'defaults'}})) {
                               $resulttext .= '<li>'.$lt{'defa'}.':&nbsp;<ul>';
                               foreach my $key (sort(keys(%{$confhash{$provider}{'defaults'}}))) {
                                   if ($confhash{$provider}{'defaults'}{$key} ne '') {
                                       $resulttext .= '<li>'.$fieldtitles{$key}.' = '.$confhash{$provider}{'defaults'}{$key}.'</li>';
                                   }
                               }
                               $resulttext .= '</ul></li>';
                           }
                       }
                       if (ref($crsconf{$provider}) eq 'ARRAY') {
                           if (@{$crsconf{$provider}} > 0) {
                               $resulttext .= '<li>'.&mt('Configurable in course:');
                               my $numconfig = 0;
                               if (ref($confhash{$provider}{'crsconf'}) eq 'ARRAY') {
                                   if (@{$confhash{$provider}{'crsconf'}} > 0) {
                                       foreach my $field (@{$confhash{$provider}{'crsconf'}}) {
                                           $numconfig ++;
                                           if ($provider eq 'examity') {
                                               $resulttext .= ' "'.$lt{'crs'.$field}.'",';
                                           } else {
                                               $resulttext .= ' "'.$fieldtitles{$field}.'",';
                                           }
                                       }
                                       $resulttext =~ s/,$//;
                                   }
                               }
                               if (!$numconfig) {
                                   $resulttext .= '&nbsp;'.&mt('None');
                               }
                               $resulttext .= '</li>';
                           }
                       }
                       if ($showroles) {
                           if (ref($confhash{$provider}{'roles'}) eq 'HASH') {
                               my $rolemaps;
                               foreach my $role (@courseroles) {
                                   if ($confhash{$provider}{'roles'}{$role}) {
                                       $rolemaps .= ('&nbsp;'x2).&Apache::lonnet::plaintext($role,'Course').'='.
                                                    $confhash{$provider}{'roles'}{$role}.',';
                                   }
                               }
                               if ($rolemaps) {
                                   $rolemaps =~ s/,$//;
                                   $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';
                               }
                           }
                       }
                       if (ref($confhash{$provider}{'custom'}) eq 'HASH') {
                           my $customlist;
                           if (keys(%{$confhash{$provider}{'custom'}})) {
                               foreach my $key (sort(keys(%{$confhash{$provider}{'custom'}}))) {
                                   $customlist .= $key.'='.$confhash{$provider}{'custom'}{$key}.', ';
                               }
                               $customlist =~ s/,$//;
                           }
                           if ($customlist) {
                               $resulttext .= '<li>'.&mt('Custom items').': '.$customlist.'</li>';
                           }
                       } 
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
           } else {
               $resulttext = &mt('No changes made.');
           }
       } else {
           $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
       }
       if ($errors) {
           $resulttext .= &mt('The following errors occurred: ').'<ul>'.
                          $errors.'</ul>';
       }
       return $resulttext;
   }
   
   sub process_proctoring_image {
       my ($r,$dom,$confname,$caller,$provider,$configuserok,$switchserver,$author_ok) = @_;
       my $filename = $env{'form.'.$caller.'.filename'};
       my ($error,$url);
       my ($width,$height) = (21,21);
       if ($configuserok eq 'ok') {
           if ($switchserver) {
               $error = &mt('Upload of Remote Proctoring Provider icon is not permitted to this server: [_1]',
                            $switchserver);
           } elsif ($author_ok eq 'ok') {
               my $modified = [];
               my ($result,$imageurl,$madethumb) =
                   &Apache::lonconfigsettings::publishlogo($r,'upload',$caller,$dom,$confname,
                                                           "proctoring/$provider/icon",$width,$height,
                                                           '',$modified);
               if ($result eq 'ok') {
                   if ($madethumb) {
                       my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$});
                       my $imagethumb = "$path/tn-".$imagefile;
                       $url = $imagethumb;
                   } else {
                       $url = $imageurl;
                   }
                   &update_modify_urls($r,$modified);
               } else {
                   $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);
               }
           } else {
               $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3].  Error was: [_4].",$filename,$confname,$dom,$author_ok);
           }
       } else {
           $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3].  Error was: [_4].",$filename,$confname,$dom,$configuserok);
       }
       return ($url,$error);
   }
   
 sub modify_lti {  sub modify_lti {
     my ($r,$dom,$action,$lastactref,%domconfig) = @_;      my ($r,$dom,$action,$lastactref,%domconfig) = @_;
     my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);      my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
Line 14188  sub modify_lti { Line 16090  sub modify_lti {
                     ($env{'form.lti_mapcrs_'.$idx} eq 'context_id'))  {                      ($env{'form.lti_mapcrs_'.$idx} eq 'context_id'))  {
                     $confhash{$itemid}{'mapcrs'} = $env{'form.lti_mapcrs_'.$idx};                      $confhash{$itemid}{'mapcrs'} = $env{'form.lti_mapcrs_'.$idx};
                 } elsif ($env{'form.lti_mapcrs_'.$idx} eq 'other') {                  } elsif ($env{'form.lti_mapcrs_'.$idx} eq 'other') {
                     my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx};                      my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx}; 
                     $mapcrs =~ s/(`)/'/g;                      $mapcrs =~ s/(`)/'/g;
                     $mapcrs =~ s/^\s+|\s+$//g;                      $mapcrs =~ s/^\s+|\s+$//g;
                     $confhash{$itemid}{'mapcrs'} = $mapcrs;                      $confhash{$itemid}{'mapcrs'} = $mapcrs;
Line 14203  sub modify_lti { Line 16105  sub modify_lti {
                 $confhash{$itemid}{'mapcrstype'} = \@crstypes;                  $confhash{$itemid}{'mapcrstype'} = \@crstypes;
                 if ($env{'form.lti_storecrs_'.$idx}) {                  if ($env{'form.lti_storecrs_'.$idx}) {
                     $confhash{$itemid}{'storecrs'} = 1;                      $confhash{$itemid}{'storecrs'} = 1;
                 }                  }       
                 if ($env{'form.lti_makecrs_'.$idx}) {                  if ($env{'form.lti_makecrs_'.$idx}) {
                     $confhash{$itemid}{'makecrs'} = 1;                      $confhash{$itemid}{'makecrs'} = 1;
                 }                  }
Line 14283  sub modify_lti { Line 16185  sub modify_lti {
                         if (ref($currlti{'maproles'}) eq 'HASH') {                          if (ref($currlti{'maproles'}) eq 'HASH') {
                             if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {                              if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') {
                                 foreach my $ltirole (keys(%{$currlti{'maproles'}})) {                                  foreach my $ltirole (keys(%{$currlti{'maproles'}})) {
                                     if ($currlti{'maproles'}{$ltirole} ne                                      if ($currlti{'maproles'}{$ltirole} ne 
                                         $confhash{$itemid}{'maproles'}{$ltirole}) {                                          $confhash{$itemid}{'maproles'}{$ltirole}) {
                                         $changes{$itemid} = 1;                                          $changes{$itemid} = 1;
                                         last;                                          last;
Line 14291  sub modify_lti { Line 16193  sub modify_lti {
                                 }                                  }
                                 unless ($changes{$itemid}) {                                  unless ($changes{$itemid}) {
                                     foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) {                                      foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) {
                                         if ($confhash{$itemid}{'maproles'}{$ltirole} ne                                          if ($confhash{$itemid}{'maproles'}{$ltirole} ne 
                                             $currlti{'maproles'}{$ltirole}) {                                              $currlti{'maproles'}{$ltirole}) {
                                             $changes{$itemid} = 1;                                              $changes{$itemid} = 1;
                                             last;                                              last;
Line 14503  sub modify_lti { Line 16405  sub modify_lti {
                             $resulttext .= '<li>'.&mt('LON-CAPA username').': '.$shownmapuser.'</li>';                              $resulttext .= '<li>'.&mt('LON-CAPA username').': '.$shownmapuser.'</li>';
                         }                          }
                         if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') {                          if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') {
                             if (@{$confhash{$itemid}{'makeuser'}} > 0) {                              if (@{$confhash{$itemid}{'makeuser'}} > 0) { 
                                 $resulttext .= '<li>'.&mt('Following roles may create user accounts: [_1]',                                  $resulttext .= '<li>'.&mt('Following roles may create user accounts: [_1]',
                                                           join(', ',@{$confhash{$itemid}{'makeuser'}})).'<br />';                                                            join(', ',@{$confhash{$itemid}{'makeuser'}})).'<br />';
                                 if ($confhash{$itemid}{'lcauth'} eq 'lti') {                                  if ($confhash{$itemid}{'lcauth'} eq 'lti') {
Line 14598  sub modify_lti { Line 16500  sub modify_lti {
                             if ($confhash{$itemid}{'section'}) {                              if ($confhash{$itemid}{'section'}) {
                                 if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') {                                  if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') {
                                     $resulttext .= '<li>'.&mt('User section from standard field:').                                      $resulttext .= '<li>'.&mt('User section from standard field:').
                                                          ' (course_section_sourcedid)'.'</li>';                                                           ' (course_section_sourcedid)'.'</li>';  
                                 } else {                                  } else {
                                     $resulttext .= '<li>'.&mt('User section from:').' '.                                      $resulttext .= '<li>'.&mt('User section from:').' '.
                                                           $confhash{$itemid}{'section'}.'</li>';                                                            $confhash{$itemid}{'section'}.'</li>';
Line 14625  sub modify_lti { Line 16527  sub modify_lti {
                             if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') {                              if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') {
                                 if (@{$confhash{$itemid}{'lcmenu'}} > 0) {                                  if (@{$confhash{$itemid}{'lcmenu'}} > 0) {
                                     $resulttext .= '<li>'.&mt('Menu items:').' '.                                      $resulttext .= '<li>'.&mt('Menu items:').' '.
                                                    join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>';                                                     join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>'; 
                                 } else {                                  } else {
                                     $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>';                                      $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>'; 
                                 }                                  }
                             }                              }
                         }                          }
Line 14663  sub get_priv_creds { Line 16565  sub get_priv_creds {
     my ($needenc,$cipher,$privnum);      my ($needenc,$cipher,$privnum);
     my %domdefs = &Apache::lonnet::get_domain_defaults($dom);      my %domdefs = &Apache::lonnet::get_domain_defaults($dom);
     if (($encchg) && (ref($encrypt) eq 'HASH')) {      if (($encchg) && (ref($encrypt) eq 'HASH')) {
         $needenc = $encrypt->{'consumers'}          $needenc = $encrypt->{'consumers'} 
     } else {      } else {
         $needenc = $domdefs{'ltienc_consumers'};          $needenc = $domdefs{'ltienc_consumers'};
     }      }
Line 15937  sub modify_contacts { Line 17839  sub modify_contacts {
         }          }
     } else {      } else {
         $resulttext = '<span class="LC_error">'.          $resulttext = '<span class="LC_error">'.
               &mt('An error occurred: [_1].',$putresult).'</span>';
       }
       return $resulttext;
   }
   
   sub modify_privacy {
       my ($dom,$lastactref,%domconfig) = @_;
       my ($resulttext,%current,%changes);
       if (ref($domconfig{'privacy'}) eq 'HASH') {
           %current = %{$domconfig{'privacy'}};
       }
       my @fields = ('lastname','firstname','middlename','generation','permanentemail','id');
       my @items = ('domain','author','course','community');
       my %names = &Apache::lonlocal::texthash (
                      domain => 'Assigned domain role(s)',
                      author => 'Assigned co-author role(s)',
                      course => 'Assigned course role(s)',
                      community => 'Assigned community role(s)',
                   );
       my %roles = &Apache::lonlocal::texthash (
                      domain => 'Domain role',
                      author => 'Co-author role',
                      course => 'Course role',
                      community => 'Community role',
                   );
       my %titles = &Apache::lonlocal::texthash (
                     approval => 'Approval for role in different domain',
                     othdom   => 'User information available in other domain',
                     priv     => 'Information viewable by privileged user in same domain',
                     unpriv   => 'Information viewable by unprivileged user in same domain',
                     instdom  => 'Other domain shares institution/provider',
                     extdom   => 'Other domain has different institution/provider',
                     none     => 'Not allowed',
                     user     => 'User authorizes',
                     domain   => 'Domain Coordinator authorizes',
                     auto     => 'Unrestricted',
                     notify   => 'Notify when role needs authorization',
       );
       my %fieldnames = &Apache::lonlocal::texthash (
                           id => 'Student/Employee ID',
                           permanentemail => 'E-mail address',
                           lastname => 'Last Name',
                           firstname => 'First Name',
                           middlename => 'Middle Name',
                           generation => 'Generation',
       );
       my ($othertitle,$usertypes,$types) =
           &Apache::loncommon::sorted_inst_types($dom);
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
   
       my %privacyhash = (
                          'approval' => {
                                          instdom => {},
                                          extdom  => {},
                                        },
                          'othdom'   => {},
                          'priv'     => {},
                          'unpriv'   => {},
                         );
       foreach my $item (@items) {
           if (@instdoms > 1) {
               if ($env{'form.privacy_approval_instdom_'.$item} =~ /^(none|user|domain|auto)$/) {
                   $privacyhash{'approval'}{'instdom'}{$item} = $env{'form.privacy_approval_instdom_'.$item};
               }
               if (ref($current{'approval'}) eq 'HASH') {
                   if (ref($current{'approval'}{'instdom'}) eq 'HASH') {
                       unless ($privacyhash{'approval'}{'instdom'}{$item} eq $current{'approval'}{'instdom'}{$item}) {
                           $changes{'approval'} = 1;
                       }
                   }
               } elsif ($privacyhash{'approval'}{'instdom'}{$item} ne 'auto') {
                   $changes{'approval'} = 1;
               }
           }
           if (keys(%by_location) > 0) {
               if ($env{'form.privacy_approval_extdom_'.$item} =~ /^(none|user|domain|auto)$/) {
                   $privacyhash{'approval'}{'extdom'}{$item} = $env{'form.privacy_approval_extdom_'.$item};
               }
               if (ref($current{'approval'}) eq 'HASH') {
                   if (ref($current{'approval'}{'extdom'}) eq 'HASH') {
                       unless ($privacyhash{'approval'}{'extdom'}{$item} eq $current{'approval'}{'extdom'}{$item}) {
                           $changes{'approval'} = 1;
                       }
                   }
               } elsif ($privacyhash{'approval'}{'extdom'}{$item} ne 'auto') {
                   $changes{'approval'} = 1;
               }
           }
           foreach my $status ('priv','unpriv') {
               my @possibles = sort(&Apache::loncommon::get_env_multiple('form.privacy_'.$status.'_'.$item));
               my @newvalues;
               foreach my $field (@possibles) {
                   if (grep(/^\Q$field\E$/,@fields)) {
                       $privacyhash{$status}{$item}{$field} = 1;
                       push(@newvalues,$field);
                   }
               }
               @newvalues = sort(@newvalues);
               if (ref($current{$status}) eq 'HASH') {
                   if (ref($current{$status}{$item}) eq 'HASH') {
                       my @currvalues = sort(keys(%{$current{$status}{$item}}));
                       my @diffs = &Apache::loncommon::compare_arrays(\@currvalues,\@newvalues);
                       if (@diffs > 0) {
                           $changes{$status} = 1;
                       }
                   }
               } else {
                   my @stdfields;
                   foreach my $field (@fields) {
                       if ($field eq 'id') {
                           next if ($status eq 'unpriv');
                           next if (($status eq 'priv') && ($item eq 'community'));
                       }
                       push(@stdfields,$field);
                   }
                   my @diffs = &Apache::loncommon::compare_arrays(\@stdfields,\@newvalues);
                   if (@diffs > 0) {
                       $changes{$status} = 1;
                   }
               }
           }
       }
       if ((@instdoms > 1) || (keys(%by_location) > 0)) {
           my @statuses;
           if (ref($types) eq 'ARRAY') {
               @statuses = @{$types};
           }
           foreach my $type (@statuses,'default') {
               my @possfields = &Apache::loncommon::get_env_multiple('form.privacy_othdom_'.$type);
               my @newvalues;
               foreach my $field (sort(@possfields)) {
                   if (grep(/^\Q$field\E$/,@fields)) {
                       $privacyhash{'othdom'}{$type}{$field} = 1;
                       push(@newvalues,$field);
                   }
               }
               @newvalues = sort(@newvalues);
               if (ref($current{'othdom'}) eq 'HASH') {
                   if (ref($current{'othdom'}{$type}) eq 'HASH') {
                       my @currvalues = sort(keys(%{$current{'othdom'}{$type}}));
                       my @diffs = &Apache::loncommon::compare_arrays(\@currvalues,\@newvalues);
                       if (@diffs > 0) {
                           $changes{'othdom'} = 1;
                       }
                   }
               } else {
                   my @stdfields = ('lastname','firstname','middlename','generation','permanentemail');
                   my @diffs = &Apache::loncommon::compare_arrays(\@stdfields,\@newvalues);
                   if (@diffs > 0) {
                       $changes{'othdom'} = 1;
                   }
               }
           }
           my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']);
           my %notify;
           foreach my $possdc (&Apache::loncommon::get_env_multiple('form.privacy_notify')) {
               if (exists($domcoords{$possdc})) {
                   $notify{$possdc} = 1;
               }
           }
           my $notify = join(',',sort(keys(%notify)));
           if ($current{'notify'} ne $notify) {
               $changes{'notify'} = 1;
           }
           $privacyhash{'notify'} = $notify;
       }
       my %confighash = (
                           privacy => \%privacyhash,
                        );
       my $putresult = &Apache::lonnet::put_dom('configuration',\%confighash,$dom);
       if ($putresult eq 'ok') {
           if (keys(%changes) > 0) {
               $resulttext = &mt('Changes made: ').'<ul>';
               foreach my $key ('approval','notify','othdom','priv','unpriv') {
                   if ($changes{$key}) {
                       $resulttext .= '<li>'.$titles{$key}.':<ul>';
                       if ($key eq 'approval') {
                           if (keys(%{$privacyhash{$key}{instdom}})) {
                               $resulttext .= '<li>'.$titles{'instdom'}.'<ul>';
                               foreach my $item (@items) {
                                   $resulttext .=  '<li>'.$roles{$item}.': '.$titles{$privacyhash{$key}{instdom}{$item}}.'</li>';
                               }
                               $resulttext .= '</ul></li>';
                           }
                           if (keys(%{$privacyhash{$key}{extdom}})) {
                               $resulttext .= '<li>'.$titles{'extdom'}.'<ul>';
                               foreach my $item (@items) {
                                   $resulttext .=  '<li>'.$roles{$item}.': '.$titles{$privacyhash{$key}{extdom}{$item}}.'</li>';
                               }
                               $resulttext .= '</ul></li>';
                           }
                       } elsif ($key eq 'notify') {
                           if ($privacyhash{$key}) {
                               foreach my $dc (split(/,/,$privacyhash{$key})) {
                                   my ($dcname,$dcdom) = split(/:/,$dc);
                                   $resulttext .= '<li>'.&Apache::loncommon::plainname($dcname,$dcdom).'</li>';
                               }
                           } else {
                               $resulttext .= '<li>'.&mt('No DCs to notify').'</li>';
                           }
                       } elsif ($key eq 'othdom') {
                           my @statuses;
                           if (ref($types) eq 'ARRAY') {
                               @statuses = @{$types};
                           }
                           if (ref($privacyhash{$key}) eq 'HASH') {
                               foreach my $status (@statuses,'default') {
                                   if ($status eq 'default') {
                                       $resulttext .= '<li>'.$othertitle.': ';
                                   } elsif (ref($usertypes) eq 'HASH') {
                                       $resulttext .= '<li>'.$usertypes->{$status}.': ';
                                   } else {
                                       next;
                                   }
                                   if (ref($privacyhash{$key}{$status}) eq 'HASH') {
                                       if (keys(%{$privacyhash{$key}{$status}})) {
                                           $resulttext .= join(', ', map { $fieldnames{$_}; } (sort(keys(%{$privacyhash{$key}{$status}}))));
                                       } else {
                                           $resulttext .= &mt('none');
                                       }
                                   }
                                   $resulttext .= '</li>';
                               }
                           }
                       } else {
                           foreach my $item (@items) {
                               if (ref($privacyhash{$key}{$item}) eq 'HASH') {
                                   $resulttext .= '<li>'.$names{$item}.': ';
                                   if (keys(%{$privacyhash{$key}{$item}})) {
                                       $resulttext .= join(', ', map { $fieldnames{$_}; } (sort(keys(%{$privacyhash{$key}{$item}}))));
                                   } else {
                                       $resulttext .= &mt('none');
                                   }
                                   $resulttext .= '</li>';
                               }
                           }
                       }
                       $resulttext .= '</ul></li>';
                   }
               }
               $resulttext .= '</ul>';
               if ($changes{'approval'}) {
                   my %domdefaults = &Apache::lonnet::get_domain_defaults($dom);
                   delete($domdefaults{'userapprovals'});
                   if (ref($privacyhash{'approval'}) eq 'HASH') {
                       foreach my $domtype ('instdom','extdom') {
                           if (ref($privacyhash{'approval'}{$domtype}) eq 'HASH') {
                               foreach my $roletype ('domain','author','course','community') {
                                   if ($privacyhash{'approval'}{$domtype}{$roletype} eq 'user') {
                                       $domdefaults{'userapprovals'} = 1;
                                       last;
                                   }
                               }
                           }
                           last if ($domdefaults{'userapprovals'});               
                       }
                   }
                   my $cachetime = 24*60*60;
                   &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
                   if (ref($lastactref) eq 'HASH') {
                       $lastactref->{'domdefaults'} = 1;
                   }
               }
           } else {
               $resulttext = &mt('No changes made to user information settings');
           }
       } else {
           $resulttext = '<span class="LC_error">'.
             &mt('An error occurred: [_1]',$putresult).'</span>';              &mt('An error occurred: [_1]',$putresult).'</span>';
     }      }
     return $resulttext;      return $resulttext;
Line 15967  sub modify_passwords { Line 18138  sub modify_passwords {
         min            => 'Minimum password length',          min            => 'Minimum password length',
         max            => 'Maximum password length',          max            => 'Maximum password length',
         chars          => 'Required characters',          chars          => 'Required characters',
           expire         => 'Password expiration (days)',
         numsaved       => 'Number of previous passwords to save',          numsaved       => 'Number of previous passwords to save',
         reset          => 'Resetting Forgotten Password',          reset          => 'Resetting Forgotten Password',
         intauth        => 'Encryption of Stored Passwords (Internal Auth)',          intauth        => 'Encryption of Stored Passwords (Internal Auth)',
Line 16350  sub modify_passwords { Line 18522  sub modify_passwords {
                             $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$titles{$key.'_'.$item},$value).'</li>';                              $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$titles{$key.'_'.$item},$value).'</li>';
                         }                          }
                     } elsif ($key eq 'rules') {                      } elsif ($key eq 'rules') {
                         foreach my $rule ('min','max','numsaved') {                          foreach my $rule ('min','max','expire','numsaved') {
                             if ($confighash{'passwords'}{$rule} eq '') {                              if ($confighash{'passwords'}{$rule} eq '') {
                                 if ($rule eq 'min') {                                  if ($rule eq 'min') {
                                     $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule});                                      $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule});
Line 16445  sub password_rule_changes { Line 18617  sub password_rule_changes {
                    (ref($changes) eq 'HASH'));                     (ref($changes) eq 'HASH'));
     my (@rules,%staticdefaults);      my (@rules,%staticdefaults);
     if ($prefix eq 'passwords') {      if ($prefix eq 'passwords') {
         @rules = ('min','max','numsaved');          @rules = ('min','max','expire','numsaved');
     } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {      } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) {
         @rules = ('min','max');          @rules = ('min','max');
     }      }
Line 16453  sub password_rule_changes { Line 18625  sub password_rule_changes {
     foreach my $rule (@rules) {      foreach my $rule (@rules) {
         $env{'form.'.$prefix.'_'.$rule} =~ s/^\s+|\s+$//g;          $env{'form.'.$prefix.'_'.$rule} =~ s/^\s+|\s+$//g;
         my $ruleok;          my $ruleok;
         if ($rule eq 'min') {          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} =~ /^\d+$/) {
                 if ($env{'form.'.$prefix.'_'.$rule} >= $staticdefaults{$rule}) {                  if ($env{'form.'.$prefix.'_'.$rule} >= $staticdefaults{$rule}) {
                     $ruleok = 1;                      $ruleok = 1;
Line 17682  sub modify_usermodification { Line 19859  sub modify_usermodification {
             }              }
         }          }
     }      }
     my @contexts = ('author','coauthor','course');      my @contexts = ('author','course');
     my %context_title = (      my %context_title = (
                            author => 'In author context',                             author => 'In author context',
                            coauthor => 'As co-author manager',   
                            course => 'In course context',                             course => 'In course context',
                         );                          );
     my @fields = ('lastname','firstname','middlename','generation',      my @fields = ('lastname','firstname','middlename','generation',
                   'permanentemail','id');                    'permanentemail','id');
     my %roles = (      my %roles = (
                   author => ['ca','aa'],                    author => ['ca','aa'],
                   coauthor => ['ca','aa'],  
                   course => ['st','ep','ta','in','cr'],                    course => ['st','ep','ta','in','cr'],
                 );                  );
     my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();      my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles();
     foreach my $context (@contexts) {      foreach my $context (@contexts) {
         my $prefix = 'canmodify';  
         if ($context eq 'coauthor') {  
             $prefix = 'cacanmodify';  
         }  
         foreach my $role (@{$roles{$context}}) {          foreach my $role (@{$roles{$context}}) {
             my @modifiable =  &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$role);              my @modifiable =  &Apache::loncommon::get_env_multiple('form.canmodify_'.$role);
             foreach my $item (@fields) {              foreach my $item (@fields) {
                 if (grep(/^\Q$item\E$/,@modifiable)) {                  if (grep(/^\Q$item\E$/,@modifiable)) {
                     $modifyhash{$context}{$role}{$item} = 1;                      $modifyhash{$context}{$role}{$item} = 1;
Line 17943  sub modify_defaults { Line 20114  sub modify_defaults {
     my @allpos;      my @allpos;
     my %alltypes;      my %alltypes;
     my @inststatusguest;      my @inststatusguest;
     if (ref($currinststatus) eq 'HASH') {      if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {
         if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') {          foreach my $type (@{$currinststatus->{'inststatusguest'}}) {
             foreach my $type (@{$currinststatus->{'inststatusguest'}}) {              unless (grep(/^\Q$type\E$/,@todelete)) {
                 unless (grep(/^\Q$type\E$/,@todelete)) {                  push(@inststatusguest,$type);
                     push(@inststatusguest,$type);  
                 }  
             }              }
         }          }
     }      }
Line 18300  sub modify_scantron { Line 20469  sub modify_scantron {
                 &mt('An error occurred: [_1]',$putresult).'</span>';                  &mt('An error occurred: [_1]',$putresult).'</span>';
         }          }
     } else {      } else {
         $resulttext = &mt('No changes made to bubblesheet format settings');          $resulttext = &mt('No changes made to bubblesheet format file'); 
     }      }
     if ($errors) {      if ($errors) {
         $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.          $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'.
Line 18333  sub modify_coursecategories { Line 20502  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 18347  sub modify_coursecategories { Line 20525  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 18369  sub modify_coursecategories { Line 20551  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 18431  sub modify_coursecategories { Line 20616  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 19032  sub modify_coursedefaults { Line 21231  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',                             'inline_chem'     => 'on',
                            'ltiauth'         => 'off',                             'ltiauth'         => 'off',
                          );                           );
     my @toggles = ('uselcmath','usejsme','inline_chem','ltiauth');      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','coursequota_official',                     'uploadquota_community','uploadquota_textbook','uploadquota_placement',
                    'coursequota_unofficial','coursequota_community','coursequota_textbook',                     'coursequota_official','coursequota_unofficial','coursequota_community',
                    'mysqltables_official','mysqltables_unofficial','mysqltables_community',                     'coursequota_textbook','coursequota_placement','mysqltables_official',
                    'mysqltables_textbook');                     'mysqltables_unofficial','mysqltables_community','mysqltables_textbook',
     my @types = ('official','unofficial','community','textbook');                     'mysqltables_placement');
       my @types = ('official','unofficial','community','textbook','placement');
     my %staticdefaults = (      my %staticdefaults = (
                            anonsurvey_threshold => 10,                             anonsurvey_threshold => 10,
                            uploadquota          => 500,                             uploadquota          => 500,
Line 19051  sub modify_coursedefaults { Line 21252  sub modify_coursedefaults {
                            postsubmit           => 60,                             postsubmit           => 60,
                            mysqltables          => 172800,                             mysqltables          => 172800,
                            domexttool           => 1,                             domexttool           => 1,
                              crsauthor            => 1,
                            crseditors           => ['edit','xml'],                             crseditors           => ['edit','xml'],
                          );                           );
     my %texoptions = (      my %texoptions = (
Line 19247  sub modify_coursedefaults { Line 21449  sub modify_coursedefaults {
                 $changes{'postsubmit'} = 1;                  $changes{'postsubmit'} = 1;
             }              }
         }          }
         my (%newdomexttool,%newexttool,%olddomexttool,%oldexttool,%posscrseditors);          my (%newdomexttool,%newexttool,%newcrsauthor,%olddomexttool,%oldexttool,%oldcrsauthor,
               %posscrseditors);
         map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool');          map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool');
         map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool');          map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool');
           map { $newcrsauthor{$_} = 1; } &Apache::loncommon::get_env_multiple('form.crsauthor');
         map { $posscrseditors{$_} = 1; } &Apache::loncommon::get_env_multiple('form.crseditors');          map { $posscrseditors{$_} = 1; } &Apache::loncommon::get_env_multiple('form.crseditors');
         if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') {          if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') {
             %olddomexttool = %{$domconfig{'coursedefaults'}{'domexttool'}};              %olddomexttool = %{$domconfig{'coursedefaults'}{'domexttool'}};
Line 19273  sub modify_coursedefaults { Line 21477  sub modify_coursedefaults {
                }                 }
             }              }
         }          }
           if (ref($domconfig{'coursedefaults'}{'crsauthor'}) eq 'HASH') {
               %oldcrsauthor = %{$domconfig{'coursedefaults'}{'crsauthor'}};
           } else {
               foreach my $type (@types) {
                  if ($staticdefaults{'crsauthor'}) {
                      $oldcrsauthor{$type} = 1;
                  } else {
                      $oldcrsauthor{$type} = 0;
                  }
               }
           }
         my @newcrseditors = ();          my @newcrseditors = ();
         foreach my $editor (@editors) {          foreach my $editor (@editors) {
             if ($posscrseditors{$editor}) {              if ($posscrseditors{$editor}) {
Line 19284  sub modify_coursedefaults { Line 21499  sub modify_coursedefaults {
                 &Apache::loncommon::compare_arrays($domconfig{'coursedefaults'}{'crseditors'},                  &Apache::loncommon::compare_arrays($domconfig{'coursedefaults'}{'crseditors'},
                                                    \@newcrseditors);                                                     \@newcrseditors);
             if (@diffs) {              if (@diffs) {
                 $changes{'crseditors'} = 1;                  $changes{'crseditors'} = 1; 
             }              }
         } else {          } else {
             my @diffs =              my @diffs =
Line 19292  sub modify_coursedefaults { Line 21507  sub modify_coursedefaults {
                                                    \@newcrseditors);                                                     \@newcrseditors);
             unless (@diffs == 0) {              unless (@diffs == 0) {
                 $changes{'crseditors'} = 1;                  $changes{'crseditors'} = 1;
             }              }       
         }          }
         foreach my $type (@types) {          foreach my $type (@types) {
             unless ($newdomexttool{$type}) {              unless ($newdomexttool{$type}) {
Line 19301  sub modify_coursedefaults { Line 21516  sub modify_coursedefaults {
             unless ($newexttool{$type}) {              unless ($newexttool{$type}) {
                 $newexttool{$type} = 0;                  $newexttool{$type} = 0;
             }              }
               unless ($newcrsauthor{$type}) {
                   $newcrsauthor{$type} = 0;
               }
             if ($newdomexttool{$type} != $olddomexttool{$type}) {              if ($newdomexttool{$type} != $olddomexttool{$type}) {
                 $changes{'domexttool'} = 1;                  $changes{'domexttool'} = 1;
             }              }
             if ($newexttool{$type} != $oldexttool{$type}) {              if ($newexttool{$type} != $oldexttool{$type}) {
                 $changes{'exttool'} = 1;                  $changes{'exttool'} = 1;
             }              }
               if ($newcrsauthor{$type} != $oldcrsauthor{$type}) {
                   $changes{'crsauthor'} = 1;
               }
         }          }
         $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool;          $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool;
         $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool;          $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool;
           $defaultshash{'coursedefaults'}{'crsauthor'} = \%newcrsauthor;
         $defaultshash{'coursedefaults'}{'crseditors'} = \@newcrseditors;          $defaultshash{'coursedefaults'}{'crseditors'} = \@newcrseditors;
     }      }
     my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,      my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
Line 19317  sub modify_coursedefaults { Line 21539  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{'texengine'}) ||                  ($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'}) ||
                 ($changes{'inline_chem'}) || ($changes{'ltiauth'}) || ($changes{'domexttool'}) ||                  ($changes{'inline_chem'}) || ($changes{'ltiauth'}) || ($changes{'domexttool'}) ||
                 ($changes{'exttool'}) || ($changes{'coursequota'})) {                  ($changes{'exttool'}) || ($changes{'coursequota'}) || ($changes{'crsauthor'})) {
                 foreach my $item ('uselcmath','usejsme','inline_chem','texengine','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 19386  sub modify_coursedefaults { Line 21609  sub modify_coursedefaults {
                         }                          }
                     }                      }
                 }                  }
                   if ($changes{'crsauthor'}) {
                       if (ref($defaultshash{'coursedefaults'}{'crsauthor'}) eq 'HASH') {
                           foreach my $type (@types) {
                               $domdefaults{$type.'crsauthor'}=$defaultshash{'coursedefaults'}{'crsauthor'}{$type};
                           }
                       }
                   }
                 if ($changes{'crseditors'}) {                  if ($changes{'crseditors'}) {
                     if (ref($defaultshash{'coursedefaults'}{'crseditors'}) eq 'ARRAY') {                      if (ref($defaultshash{'coursedefaults'}{'crseditors'}) eq 'ARRAY') {
                         $domdefaults{'crseditors'}=join(',',@{$defaultshash{'coursedefaults'}{'crseditors'}});                          $domdefaults{'crseditors'}=join(',',@{$defaultshash{'coursedefaults'}{'crseditors'}});
Line 19399  sub modify_coursedefaults { Line 21629  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 19430  sub modify_coursedefaults { Line 21666  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 19442  sub modify_coursedefaults { Line 21679  sub modify_coursedefaults {
                                        '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'official'}.'</b>').'</li>'.                                         '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'official'}.'</b>').'</li>'.
                                        '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'unofficial'}.'</b>').'</li>'.                                         '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'unofficial'}.'</b>').'</li>'.
                                        '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'textbook'}.'</b>').'</li>'.                                         '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'textbook'}.'</b>').'</li>'.
                                          '<li>'.&mt('Placement tests: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'placement'}.'</b>').'</li>'.
                                        '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'community'}.'</b>').'</li>'.                                         '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'community'}.'</b>').'</li>'.
                                        '</ul>'.                                         '</ul>'.
                                        '</li>';                                         '</li>';
Line 19454  sub modify_coursedefaults { Line 21692  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 19489  sub modify_coursedefaults { Line 21728  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 19530  sub modify_coursedefaults { Line 21771  sub modify_coursedefaults {
                     } else {                      } else {
                         $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL will require re-authentication').'</li>';                          $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL will require re-authentication').'</li>';
                     }                      }
                 } elsif ($item eq 'domexttool') {                  } elsif (($item eq 'domexttool') || ($item eq 'exttool') || ($item eq 'crsauthor')) {
                     my @noyes = (&mt('no'),&mt('yes'));                      my @noyes = (&mt('no'),&mt('yes'));
                     if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') {                      my %status = (
                         $resulttext .= '<li>'.&mt('External Tools defined in the domain may be used as follows:').'<ul>'.                                     domexttool => {
                                        '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'official'}].'</b>').'</li>'.                                                    ishash => &mt('External Tools defined in the domain may be used as follows:'), 
                                        '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'unofficial'}].'</b>').'</li>'.                                                    default => &mt('External Tools defined in the domain may be used in all course types, by default'),
                                        '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'textbook'}].'</b>').'</li>'.                                                   },
                                        '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'placement'}].'</b>').'</li>'.                                     exttool => {
                                        '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'community'}].'</b>').'</li>'.                                                    ishash => &mt('External Tools can be defined and configured in course containers as follows:'),
                                        '</ul>'.                                                    default => &mt('External Tools can not be defined in any course types, by default'),
                                        '</li>';                                                },
                     } else {                                     crsauthor => {
                         $resulttext .= '<li>'.&mt('External Tools defined in the domain may be used in all course types, by default').'</li>';                                                    ishash => &mt('Standard Problems can be created within course containers as follows:'),
                     }                                                    default => &mt('Standard Problems can be created within any course type, by default'),
                 } elsif ($item eq 'exttool') {                                                  },
                     my @noyes = (&mt('no'),&mt('yes'));                                   );
                     if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') {                                     
                         $resulttext .= '<li>'.&mt('External Tools can be defined and configured in course containers as follows:').'<ul>'.                      if (ref($defaultshash{'coursedefaults'}{$item}) eq 'HASH') {
                                        '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'official'}].'</b>').'</li>'.                          $resulttext .= '<li>'.$status{$item}{'ishash'}.'<ul>'.
                                        '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'unofficial'}].'</b>').'</li>'.                                         '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{$item}{'official'}].'</b>').'</li>'.
                                        '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'textbook'}].'</b>').'</li>'.                                         '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{$item}{'unofficial'}].'</b>').'</li>'.
                                        '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'placement'}].'</b>').'</li>'.                                         '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{$item}{'textbook'}].'</b>').'</li>'.
                                        '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'community'}].'</b>').'</li>'.                                         '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{$item}{'placement'}].'</b>').'</li>'.
                                          '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{$item}{'community'}].'</b>').'</li>'.
                                        '</ul>'.                                         '</ul>'.
                                        '</li>';                                         '</li>';
                     } else {                      } else {
                         $resulttext .= '<li>'.&mt('External Tools can not be defined in any course types, by default').'</li>';                          $resulttext .= '<li>'.$status{$item}{'default'}.'</li>';
                     }                      }
                 } elsif ($item eq 'crseditors') {                  } elsif ($item eq 'crseditors') {
                     if (ref($defaultshash{'coursedefaults'}{$item}) eq 'ARRAY') {                      if (ref($defaultshash{'coursedefaults'}{$item}) eq 'ARRAY') {
Line 19584  sub modify_coursedefaults { Line 21826  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 20170  sub modify_usersessions { Line 22412  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 20460  sub modify_usersessions { Line 22702  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 20475  sub modify_usersessions { Line 22721  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>';
                                         }                                          }
Line 20534  sub modify_usersessions { Line 22782  sub modify_usersessions {
     return $resulttext;      return $resulttext;
 }  }
   
   sub modify_ssl {
       my ($dom,$lastactref,%domconfig) = @_;
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
       my @locations = sort(keys(%by_location));
       my %servers = &Apache::lonnet::internet_dom_servers($dom);
       my (%defaultshash,%changes);
       my $action = 'ssl';
       my @prefixes = ('connto','connfrom','replication');
       foreach my $prefix (@prefixes) {
           $defaultshash{$action}{$prefix} = {};
       }
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my $resulttext;
       my %iphost = &Apache::lonnet::get_iphost();
       my @reptypes = ('certreq','nocertreq');
       my @connecttypes = ('dom','intdom','other');
       my %types = (
                     connto      => \@connecttypes,
                     connfrom    => \@connecttypes,
                     replication => \@reptypes,
                   );
       foreach my $prefix (sort(keys(%types))) {
           foreach my $type (@{$types{$prefix}}) {
               if (($prefix eq 'connto') || ($prefix eq 'connfrom')) {
                   my $value = 'yes';
                   if ($env{'form.'.$prefix.'_'.$type} =~ /^(no|req)$/) {
                       $value = $env{'form.'.$prefix.'_'.$type};
                   }
                   if (ref($domconfig{$action}) eq 'HASH') {
                       if (ref($domconfig{$action}{$prefix}) eq 'HASH') {
                           if ($domconfig{$action}{$prefix}{$type} ne '') {
                               if ($value ne $domconfig{$action}{$prefix}{$type}) {
                                   $changes{$prefix}{$type} = 1;
                               }
                               $defaultshash{$action}{$prefix}{$type} = $value;
                           } else {
                               $defaultshash{$action}{$prefix}{$type} = $value;
                               $changes{$prefix}{$type} = 1;
                           }
                       } else {
                           $defaultshash{$action}{$prefix}{$type} = $value;
                           $changes{$prefix}{$type} = 1;
                       }
                   } else {
                       $defaultshash{$action}{$prefix}{$type} = $value;
                       $changes{$prefix}{$type} = 1;
                   }
                   if (($type eq 'dom') && (keys(%servers) == 1)) {
                       delete($changes{$prefix}{$type});
                   } elsif (($type eq 'intdom') && (@instdoms == 1)) {
                       delete($changes{$prefix}{$type});
                   } elsif (($type eq 'other') && (keys(%by_location) == 0)) { 
                       delete($changes{$prefix}{$type});
                   }
               } elsif ($prefix eq 'replication') {
                   if (@locations > 0) {
                       my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'};
                       my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type);
                       my @okvals;
                       foreach my $val (@vals) {
                           if ($val =~ /:/) {
                               my @items = split(/:/,$val);
                               foreach my $item (@items) {
                                   if (ref($by_location{$item}) eq 'ARRAY') {
                                       push(@okvals,$item);
                                   }
                               }
                           } else {
                               if (ref($by_location{$val}) eq 'ARRAY') {
                                   push(@okvals,$val);
                               }
                           }
                       }
                       @okvals = sort(@okvals);
                       if (ref($domconfig{$action}) eq 'HASH') {
                           if (ref($domconfig{$action}{$prefix}) eq 'HASH') {
                               if (ref($domconfig{$action}{$prefix}{$type}) eq 'ARRAY') {
                                   if ($inuse == 0) {
                                       $changes{$prefix}{$type} = 1;
                                   } else {
                                       $defaultshash{$action}{$prefix}{$type} = \@okvals;
                                       my @changed = &Apache::loncommon::compare_arrays($domconfig{$action}{$prefix}{$type},$defaultshash{$action}{$prefix}{$type});
                                       if (@changed > 0) {
                                           $changes{$prefix}{$type} = 1;
                                       }
                                   }
                               } else {
                                   if ($inuse == 1) {
                                       $defaultshash{$action}{$prefix}{$type} = \@okvals;
                                       $changes{$prefix}{$type} = 1;
                                   }
                               }
                           } else {
                               if ($inuse == 1) {
                                   $defaultshash{$action}{$prefix}{$type} = \@okvals;
                                   $changes{$prefix}{$type} = 1;
                               }
                           }
                       } else {
                           if ($inuse == 1) {
                               $defaultshash{$action}{$prefix}{$type} = \@okvals;
                               $changes{$prefix}{$type} = 1;
                           }
                       }
                   }
               }
           }
       }
       if (keys(%changes)) {
           foreach my $prefix (keys(%changes)) {
               if (ref($changes{$prefix}) eq 'HASH') {
                   if (scalar(keys(%{$changes{$prefix}})) == 0) {
                       delete($changes{$prefix});
                   }
               } else {
                   delete($changes{$prefix});
               }
           }
       }
       my $nochgmsg = &mt('No changes made to LON-CAPA SSL settings');
       if (keys(%changes) > 0) {
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               if (ref($defaultshash{$action}) eq 'HASH') {
                   if (ref($defaultshash{$action}{'replication'}) eq 'HASH') {
                       $domdefaults{'replication'} = $defaultshash{$action}{'replication'};
                   }
                   if (ref($defaultshash{$action}{'connto'}) eq 'HASH') {
                       $domdefaults{'connto'} = $defaultshash{$action}{'connto'};
                   }
                   if (ref($defaultshash{$action}{'connfrom'}) eq 'HASH') {
                       $domdefaults{'connfrom'} = $defaultshash{$action}{'connfrom'};
                   }
               }
               my $cachetime = 24*60*60;
               &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'domdefaults'} = 1;
               }
               if (keys(%changes) > 0) {
                   my %titles = &ssl_titles();
                   $resulttext = &mt('Changes made:').'<ul>';
                   foreach my $prefix (@prefixes) {
                       if (ref($changes{$prefix}) eq 'HASH') {
                           $resulttext .= '<li>'.$titles{$prefix}.'<ul>';
                           foreach my $type (@{$types{$prefix}}) {
                               if (defined($changes{$prefix}{$type})) {
                                   my ($newvalue,$notinuse);
                                   if (ref($defaultshash{$action}) eq 'HASH') {
                                       if (ref($defaultshash{$action}{$prefix})) {
                                           if (($prefix eq 'connto') || ($prefix eq 'connfrom')) {
                                               $newvalue = $titles{$defaultshash{$action}{$prefix}{$type}};
                                           } else {
                                               if (ref($defaultshash{$action}{$prefix}{$type}) eq 'ARRAY') {
                                                   if (@{$defaultshash{$action}{$prefix}{$type}} > 0) {
                                                       $newvalue = join(', ',@{$defaultshash{$action}{$prefix}{$type}});
                                                   }
                                               } else {
                                                   $notinuse = 1;
                                               }
                                           }
                                       }
                                       if ($notinuse) {
                                           $resulttext .= '<li>'.&mt('[_1] set to: not in use',$titles{$type}).'</li>';
                                       } elsif ($newvalue eq '') {
                                           $resulttext .= '<li>'.&mt('[_1] set to: none',$titles{$type}).'</li>';
                                       } else {
                                           $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$titles{$type},$newvalue).'</li>';
                                       }
                                   }
                               }
                           }
                           $resulttext .= '</ul>';
                       }
                   }
               } else {
                   $resulttext = $nochgmsg;
               }
           } else {
               $resulttext = '<span class="LC_error">'.
                             &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } else {
           $resulttext = $nochgmsg;
       }
       return $resulttext;
   }
   
   sub modify_trust {
       my ($dom,$lastactref,%domconfig) = @_;
       my (%by_ip,%by_location,@intdoms,@instdoms);
       &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms);
       my @locations = sort(keys(%by_location));
       my @prefixes = qw(content shared enroll othcoau coaurem domroles catalog reqcrs msg);
       my @types = ('exc','inc');
       my (%defaultshash,%changes);
       foreach my $prefix (@prefixes) {
           $defaultshash{'trust'}{$prefix} = {};
       }
       my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
       my $resulttext;
       foreach my $prefix (@prefixes) {
           foreach my $type (@types) {
               my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'};
               my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type);
               my @okvals;
               foreach my $val (@vals) {
                   if ($val =~ /:/) {
                       my @items = split(/:/,$val);
                       foreach my $item (@items) {
                           if (ref($by_location{$item}) eq 'ARRAY') {
                               push(@okvals,$item);
                           }
                       }
                   } else {
                       if (ref($by_location{$val}) eq 'ARRAY') {
                           push(@okvals,$val);
                       }
                   }
               }
               @okvals = sort(@okvals);
               if (ref($domconfig{'trust'}) eq 'HASH') {
                   if (ref($domconfig{'trust'}{$prefix}) eq 'HASH') {
                       if (ref($domconfig{'trust'}{$prefix}{$type}) eq 'ARRAY') {
                           if ($inuse == 0) {
                               $changes{$prefix}{$type} = 1;
                           } else {
                               $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                               my @changed = &Apache::loncommon::compare_arrays($domconfig{'trust'}{$prefix}{$type},$defaultshash{'trust'}{$prefix}{$type});
                               if (@changed > 0) {
                                   $changes{$prefix}{$type} = 1;
                               }
                           }
                       } else {
                           if ($inuse == 1) {
                               $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                               $changes{$prefix}{$type} = 1;
                           }
                       }
                   } else {
                       if ($inuse == 1) {
                           $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                           $changes{$prefix}{$type} = 1;
                       }
                   }
               } else {
                   if ($inuse == 1) {
                       $defaultshash{'trust'}{$prefix}{$type} = \@okvals;
                       $changes{$prefix}{$type} = 1;
                   }
               }
           }
       }
       my $nochgmsg = &mt('No changes made to trust settings.');
       if (keys(%changes) > 0) {
           my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash,
                                                    $dom);
           if ($putresult eq 'ok') {
               if (ref($defaultshash{'trust'}) eq 'HASH') {
                   foreach my $prefix (@prefixes) {
                       if (ref($defaultshash{'trust'}{$prefix}) eq 'HASH') {
                           $domdefaults{'trust'.$prefix} = $defaultshash{'trust'}{$prefix};
                       }
                   }
               }
               my $cachetime = 24*60*60;
               &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime);
               &Apache::lonnet::do_cache_new('trust',$dom,$defaultshash{'trust'},3600);
               if (ref($lastactref) eq 'HASH') {
                   $lastactref->{'domdefaults'} = 1;
                   $lastactref->{'trust'} = 1;
               }
               if (keys(%changes) > 0) {
                   my %lt = &trust_titles();
                   $resulttext = &mt('Changes made:').'<ul>';
                   foreach my $prefix (@prefixes) {
                       if (ref($changes{$prefix}) eq 'HASH') {
                           $resulttext .= '<li>'.$lt{$prefix}.'<ul>';
                           foreach my $type (@types) {
                               if (defined($changes{$prefix}{$type})) {
                                   my ($newvalue,$notinuse);
                                   if (ref($defaultshash{'trust'}) eq 'HASH') {
                                       if (ref($defaultshash{'trust'}{$prefix})) {
                                           if (ref($defaultshash{'trust'}{$prefix}{$type}) eq 'ARRAY') {
                                               if (@{$defaultshash{'trust'}{$prefix}{$type}} > 0) {
                                                   $newvalue = join(', ',@{$defaultshash{'trust'}{$prefix}{$type}});
                                               }
                                           } else {
                                               $notinuse = 1;
                                           }
                                       }
                                   }
                                   if ($notinuse) {
                                       $resulttext .= '<li>'.&mt('[_1] set to: not in use',$lt{$type}).'</li>';
                                   } elsif ($newvalue eq '') {
                                       $resulttext .= '<li>'.&mt('[_1] set to: none',$lt{$type}).'</li>';
                                   } else {
                                       $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'</li>';
                                   }
                               }
                           }
                           $resulttext .= '</ul>';
                       }
                   }
                   $resulttext .= '</ul>';
               } else {
                   $resulttext = $nochgmsg;
               }
           } else {
               $resulttext = '<span class="LC_error">'.
                             &mt('An error occurred: [_1]',$putresult).'</span>';
           }
       } else {
           $resulttext = $nochgmsg;
       }
       return $resulttext;
   }
   
 sub modify_loadbalancing {  sub modify_loadbalancing {
     my ($dom,%domconfig) = @_;      my ($dom,%domconfig) = @_;
     my $primary_id = &Apache::lonnet::domain($dom,'primary');      my $primary_id = &Apache::lonnet::domain($dom,'primary');
Line 21554  sub devalidate_remote_domconfs { Line 24122  sub devalidate_remote_domconfs {
     map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();      map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids();
     my @posscached = ('domainconfig','domdefaults','ltitools','usersessions',      my @posscached = ('domainconfig','domdefaults','ltitools','usersessions',
                       'directorysrch','passwdconf','cats','proxyalias','proxysaml',                        'directorysrch','passwdconf','cats','proxyalias','proxysaml',
                       'ipaccess');                        'ipaccess','trust');
     my %cache_by_lonhost;      my %cache_by_lonhost;
     if (exists($cachekeys->{'samllanding'})) {      if (exists($cachekeys->{'samllanding'})) {
         if (ref($cachekeys->{'samllanding'}) eq 'HASH') {          if (ref($cachekeys->{'samllanding'}) eq 'HASH') {

Removed from v.1.160.6.118.2.27  
changed lines
  Added in v.1.438


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