';
+ foreach my $item ('ipheader','trusted','vpnint','vpnext') {
+ my $showval = &mt('None');
+ if ($values{$domain}{$item}) {
+ $showval = $values{$domain}{$item};
+ }
+ $datatable .= '
'.
+ '
'.$lt{$item}.': '.$showval.'
';
+ }
+ $datatable .= '
';
+ }
+ }
+ }
+ $$rowtotal += $itemcount;
+ return $datatable;
+}
+
+sub wafproxy_titles {
+ return &Apache::lonlocal::texthash(
+ vpnint => 'Internal IP Range(s) for VPN sessions',
+ vpnext => 'IP Range for backend WAF connections',
+ trusted => 'Trusted IP range(s)',
+ ipheader => 'Custom request header',
+ );
+}
+
sub print_usersessions {
my ($position,$dom,$settings,$rowtotal) = @_;
my ($css_class,$datatable,$itemcount,%checked,%choices);
@@ -6421,13 +7322,18 @@ sub print_usersessions {
if ($position eq 'top') {
if (keys(%serverhomes) > 1) {
my %spareid = ¤t_offloads_to($dom,$settings,\%servers);
- my $curroffloadnow;
+ my ($curroffloadnow,$curroffloadoth);
if (ref($settings) eq 'HASH') {
if (ref($settings->{'offloadnow'}) eq 'HASH') {
$curroffloadnow = $settings->{'offloadnow'};
}
+ if (ref($settings->{'offloadoth'}) eq 'HASH') {
+ $curroffloadoth = $settings->{'offloadoth'};
+ }
}
- $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,$curroffloadnow,$rowtotal);
+ my $other_insts = scalar(keys(%by_location));
+ $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,
+ $other_insts,$curroffloadnow,$curroffloadoth,$rowtotal);
} else {
$datatable .= '
'.
&mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.').
@@ -6871,7 +7777,8 @@ sub current_offloads_to {
}
sub spares_row {
- my ($dom,$servers,$spareid,$serverhomes,$altids,$curroffloadnow,$rowtotal) = @_;
+ my ($dom,$servers,$spareid,$serverhomes,$altids,$other_insts,
+ $curroffloadnow,$curroffloadoth,$rowtotal) = @_;
my $css_class;
my $numinrow = 4;
my $itemcount = 1;
@@ -6891,12 +7798,17 @@ sub spares_row {
}
}
next unless (ref($spareid->{$server}) eq 'HASH');
- my $checkednow;
+ my ($checkednow,$checkedoth);
if (ref($curroffloadnow) eq 'HASH') {
if ($curroffloadnow->{$server}) {
$checkednow = ' checked="checked"';
}
}
+ if (ref($curroffloadoth) eq 'HASH') {
+ if ($curroffloadoth->{$server}) {
+ $checkedoth = ' checked="checked"';
+ }
+ }
$css_class = $itemcount%2 ? ' class="LC_odd_row"' : '';
$datatable .= '
@@ -6905,8 +7817,15 @@ sub spares_row {
,''.$server.'').' '.
''."\n".
''.
+ ' '.&mt('Switch any active user on next access').''.
+ "\n";
+ if ($other_insts) {
+ $datatable .= ' '.
+ ''."\n".
+ ''.
"\n";
+ }
my (%current,%canselect);
my @choices =
&possible_newspares($server,$spareid->{$server},$serverhomes,$altids);
@@ -7425,8 +8344,8 @@ sub contact_titles {
'updatesmail' => 'E-mail from nightly check of LON-CAPA module integrity/updates',
'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID',
'hostipmail' => 'E-mail from nightly check of hostname/IP network changes',
- 'errorthreshold' => 'Error/warning threshold for status e-mail',
- 'errorsysmail' => 'Error threshold for e-mail to core group',
+ 'errorthreshold' => 'Error count threshold for status e-mail to admin(s)',
+ 'errorsysmail' => 'Error count threshold for e-mail to developer group',
'errorweights' => 'Weights used to compute error count',
'errorexcluded' => 'Servers with unsent updates excluded from count',
);
@@ -9378,8 +10297,8 @@ function warnIntPass(field) {
alert('$intalert{passnum}');
}
}
+ field.value = '';
}
- field.value = '';
}
}
}
@@ -12410,7 +13329,7 @@ sub modify_ltitools {
}
}
if (!$numconfig) {
- $resulttext .= &mt('None');
+ $resulttext .= ' '.&mt('None');
}
$resulttext .= '';
foreach my $item ('passback','roster') {
@@ -12589,6 +13508,536 @@ sub get_ltitools_id {
return ($id,$error);
}
+sub modify_proctoring {
+ my ($r,$dom,$action,$lastactref,%domconfig) = @_;
+ my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
+ my (@allpos,%changes,%confhash,%encconfhash,$errors,$resulttext,%imgdeletions);
+ my $confname = $dom.'-domainconfig';
+ my $servadm = $r->dir_config('lonAdmEMail');
+ my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm);
+ my %providernames = &proctoring_providernames();
+ my $maxnum = scalar(keys(%providernames));
+
+ my (%requserfields,%optuserfields,%defaults,%extended,%crsconf,@courseroles,@ltiroles);
+ my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data();
+ if (ref($requref) eq 'HASH') {
+ %requserfields = %{$requref};
+ }
+ if (ref($opturef) eq 'HASH') {
+ %optuserfields = %{$opturef};
+ }
+ if (ref($defref) eq 'HASH') {
+ %defaults = %{$defref};
+ }
+ if (ref($extref) eq 'HASH') {
+ %extended = %{$extref};
+ }
+ if (ref($crsref) eq 'HASH') {
+ %crsconf = %{$crsref};
+ }
+ if (ref($rolesref) eq 'ARRAY') {
+ @courseroles = @{$rolesref};
+ }
+ if (ref($ltiref) eq 'ARRAY') {
+ @ltiroles = @{$ltiref};
+ }
+
+ if (ref($domconfig{$action}) eq 'HASH') {
+ my @todeleteimages = &Apache::loncommon::get_env_multiple('form.proctoring_image_del');
+ if (@todeleteimages) {
+ map { $imgdeletions{$_} = 1; } @todeleteimages;
+ }
+ }
+ my %customadds;
+ my @newcustom = &Apache::loncommon::get_env_multiple('form.proctoring_customadd');
+ if (@newcustom) {
+ map { $customadds{$_} = 1; } @newcustom;
+ }
+ foreach my $provider (sort(keys(%providernames))) {
+ $confhash{$provider} = {};
+ my $pos = $env{'form.proctoring_pos_'.$provider};
+ $pos =~ s/\D+//g;
+ $allpos[$pos] = $provider;
+ my (%current,%currentenc);
+ my $showroles = 0;
+ if (ref($domconfig{$action}) eq 'HASH') {
+ if (ref($domconfig{$action}{$provider}) eq 'HASH') {
+ %current = %{$domconfig{$action}{$provider}};
+ foreach my $item ('key','secret') {
+ $currentenc{$item} = $current{$item};
+ delete($current{$item});
+ }
+ }
+ }
+ if ($env{'form.proctoring_available_'.$provider}) {
+ $confhash{$provider}{'available'} = 1;
+ unless ($current{'available'}) {
+ $changes{$provider} = 1;
+ }
+ } else {
+ %{$confhash{$provider}} = %current;
+ %{$encconfhash{$provider}} = %currentenc;
+ $confhash{$provider}{'available'} = 0;
+ if ($current{'available'}) {
+ $changes{$provider} = 1;
+ }
+ }
+ if ($confhash{$provider}{'available'}) {
+ foreach my $field ('lifetime','version','sigmethod','url','key','secret') {
+ my $possval = $env{'form.proctoring_'.$provider.'_'.$field};
+ if ($field eq 'lifetime') {
+ if ($possval =~ /^\d+$/) {
+ $confhash{$provider}{$field} = $possval;
+ }
+ } elsif ($field eq 'version') {
+ if ($possval =~ /^\d+\.\d+$/) {
+ $confhash{$provider}{$field} = $possval;
+ }
+ } elsif ($field eq 'sigmethod') {
+ if ($possval =~ /^\QHMAC-SHA\E(1|256)$/) {
+ $confhash{$provider}{$field} = $possval;
+ }
+ } elsif ($field eq 'url') {
+ $confhash{$provider}{$field} = $possval;
+ } elsif (($field eq 'key') || ($field eq 'secret')) {
+ $encconfhash{$provider}{$field} = $possval;
+ unless ($currentenc{$field} eq $possval) {
+ $changes{$provider} = 1;
+ }
+ }
+ unless (($field eq 'key') || ($field eq 'secret')) {
+ unless ($current{$field} eq $confhash{$provider}{$field}) {
+ $changes{$provider} = 1;
+ }
+ }
+ }
+ if ($imgdeletions{$provider}) {
+ $changes{$provider} = 1;
+ } elsif ($env{'form.proctoring_image_'.$provider.'.filename'} ne '') {
+ my ($imageurl,$error) =
+ &process_proctoring_image($r,$dom,$confname,'proctoring_image_'.$provider,$provider,
+ $configuserok,$switchserver,$author_ok);
+ if ($imageurl) {
+ $confhash{$provider}{'image'} = $imageurl;
+ $changes{$provider} = 1;
+ }
+ if ($error) {
+ &Apache::lonnet::logthis($error);
+ $errors .= '
';
+ }
+ if ($errors) {
+ $resulttext .= &mt('The following errors occurred: ').'
'.
+ $errors.'
';
+ }
+ return $resulttext;
+}
+
+sub process_proctoring_image {
+ my ($r,$dom,$confname,$caller,$provider,$configuserok,$switchserver,$author_ok) = @_;
+ my $filename = $env{'form.'.$caller.'.filename'};
+ my ($error,$url);
+ my ($width,$height) = (21,21);
+ if ($configuserok eq 'ok') {
+ if ($switchserver) {
+ $error = &mt('Upload of Remote Proctoring Provider icon is not permitted to this server: [_1]',
+ $switchserver);
+ } elsif ($author_ok eq 'ok') {
+ my ($result,$imageurl,$madethumb) =
+ &publishlogo($r,'upload',$caller,$dom,$confname,
+ "proctoring/$provider/icon",$width,$height);
+ if ($result eq 'ok') {
+ if ($madethumb) {
+ my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$});
+ my $imagethumb = "$path/tn-".$imagefile;
+ $url = $imagethumb;
+ } else {
+ $url = $imageurl;
+ }
+ } else {
+ $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);
+ }
+ } else {
+ $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$author_ok);
+ }
+ } else {
+ $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$configuserok);
+ }
+ return ($url,$error);
+}
+
sub modify_lti {
my ($r,$dom,$action,$lastactref,%domconfig) = @_;
my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
@@ -13750,7 +15199,7 @@ sub modify_contacts {
$contacts_hash{'contacts'}{'lonstatus'}{$item} = \@excluded;
}
} elsif ($item eq 'weights') {
- foreach my $type ('E','W','N') {
+ foreach my $type ('E','W','N','U') {
$env{'form.error'.$item.'_'.$type} =~ s/^\s+|\s+$//g;
if ($env{'form.error'.$item.'_'.$type} =~ /^\d+$/) {
unless ($env{'form.error'.$item.'_'.$type} == $lonstatus_defs->{$type}) {
@@ -14763,6 +16212,8 @@ sub modify_passwords {
if ($staticdefaults{$rule} ne $newvalues{$rule}) {
$changes{'rules'} = 1;
}
+ } else {
+ $changes{'rules'} = 1;
}
} elsif (exists($current{$rule})) {
$changes{'rules'} = 1;
@@ -14972,6 +16423,24 @@ sub modify_passwords {
$resulttext .= '
'.&mt('[_1] set to [_2]',$titles{$rule},$confighash{'passwords'}{$rule}).'
';
}
}
+ if (ref($confighash{'passwords'}{'chars'}) eq 'ARRAY') {
+ if (@{$confighash{'passwords'}{'chars'}} > 0) {
+ my %rulenames = &Apache::lonlocal::texthash(
+ uc => 'At least one upper case letter',
+ lc => 'At least one lower case letter',
+ num => 'At least one number',
+ spec => 'At least one non-alphanumeric',
+ );
+ my $needed = '