[LON-CAPA-cvs] cvs: loncom /interface domainprefs.pm lonconfigsettings.pm
raeburn
raeburn at source.lon-capa.org
Wed Sep 16 20:35:04 EDT 2020
raeburn Thu Sep 17 00:35:04 2020 EDT
Modified files:
/loncom/interface domainprefs.pm lonconfigsettings.pm
Log:
- Bug 6935
-------------- next part --------------
Index: loncom/interface/domainprefs.pm
diff -u loncom/interface/domainprefs.pm:1.371 loncom/interface/domainprefs.pm:1.372
--- loncom/interface/domainprefs.pm:1.371 Sun Mar 15 23:04:15 2020
+++ loncom/interface/domainprefs.pm Thu Sep 17 00:35:04 2020
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: domainprefs.pm,v 1.371 2020/03/15 23:04:15 raeburn Exp $
+# $Id: domainprefs.pm,v 1.372 2020/09/17 00:35:04 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -219,9 +219,10 @@
'serverstatuses','requestcourses','helpsettings',
'coursedefaults','usersessions','loadbalancing',
'requestauthor','selfenrollment','inststatus',
- 'ltitools','ssl','trust','lti','privacy','passwords'],$dom);
+ 'ltitools','ssl','trust','lti','privacy','passwords',
+ 'proctoring'],$dom);
my %encconfig =
- &Apache::lonnet::get_dom('encconfig',['ltitools','lti'],$dom);
+ &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring'],$dom);
if (ref($domconfig{'ltitools'}) eq 'HASH') {
if (ref($encconfig{'ltitools'}) eq 'HASH') {
foreach my $id (keys(%{$domconfig{'ltitools'}})) {
@@ -246,12 +247,25 @@
}
}
}
+ 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','defaults','passwords','quotas','autoenroll',
'autoupdate','autocreate','directorysrch','contacts','privacy',
'usercreation','selfcreation','usermodification','scantron',
'requestcourses','requestauthor','coursecategories',
'serverstatuses','helpsettings','coursedefaults',
- 'ltitools','selfenrollment','usersessions','ssl','trust','lti');
+ 'ltitools','proctoring','selfenrollment','usersessions','ssl',
+ 'trust','lti');
my %existing;
if (ref($domconfig{'loadbalancing'}) eq 'HASH') {
%existing = %{$domconfig{'loadbalancing'}};
@@ -541,6 +555,14 @@
print => \&print_ltitools,
modify => \&modify_ltitools,
},
+ 'proctoring' =>
+ {text => 'Remote Proctoring Integration',
+ help => 'Domain_Configuration_Proctoring',
+ header => [{col1 => 'Name',
+ col2 => 'Configuration'}],
+ print => \&print_proctoring,
+ modify => \&modify_proctoring,
+ },
'ssl' =>
{text => 'LON-CAPA Network (SSL)',
help => 'Domain_Configuration_Network_SSL',
@@ -771,6 +793,8 @@
$output = &modify_loadbalancing($dom,%domconfig);
} elsif ($action eq 'ltitools') {
$output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig);
+ } elsif ($action eq 'proctoring') {
+ $output = &modify_proctoring($r,$dom,$action,$lastactref,%domconfig);
} elsif ($action eq 'ssl') {
$output = &modify_ssl($dom,$lastactref,%domconfig);
} elsif ($action eq 'trust') {
@@ -815,6 +839,8 @@
$output .= <itools_javascript($settings);
} elsif ($action eq 'lti') {
$output .= <i_javascript($settings);
+ } elsif ($action eq 'proctoring') {
+ $output .= &proctoring_javascript($settings);
}
$output .=
'<table class="LC_nested_outer">
@@ -1162,7 +1188,8 @@
$output .= &print_quotas($dom,$settings,\$rowtotal,$action);
} elsif (($action eq 'autoenroll') || ($action eq 'autocreate') ||
($action eq 'serverstatuses') || ($action eq 'loadbalancing') ||
- ($action eq 'ltitools') || ($action eq 'lti')) {
+ ($action eq 'ltitools') || ($action eq 'lti') ||
+ ($action eq 'proctoring')) {
$output .= $item->{'print'}->($dom,$settings,\$rowtotal);
}
}
@@ -2803,6 +2830,102 @@
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("','", at jsarray)."'".');'."\n";
+ return <<"ENDSCRIPT";
+<script type="text/javascript">
+// <![CDATA[
+function reorderProctoring(form,item) {
+ var changedVal;
+$jstext
+ var maxh = $total;
+ var current = new Array;
+ var changedVal = form.elements[item].options[form.elements[item].selectedIndex].value;
+ for (var i=0; i<proctors.length; i++) {
+ var elementName = 'proctoring_pos_'+proctors[i];
+ if (elementName != item) {
+ if (form.elements[elementName]) {
+ var currVal = form.elements[elementName].options[form.elements[elementName].selectedIndex].value;
+ current[currVal] = elementName;
+ }
+ }
+ }
+ var oldVal;
+ for (var j=0; j<maxh; j++) {
+ if (current[j] == undefined) {
+ oldVal = j;
+ }
+ }
+ if (oldVal < changedVal) {
+ for (var k=oldVal+1; k<=changedVal ; k++) {
+ var elementName = current[k];
+ form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex - 1;
+ }
+ } else {
+ for (var k=changedVal; k<oldVal; k++) {
+ var elementName = current[k];
+ form.elements[elementName].selectedIndex = form.elements[elementName].selectedIndex + 1;
+ }
+ }
+ return;
+}
+
+function toggleProctoring(form,item) {
+ var fieldsets = document.getElementsByClassName('proctoring_'+item);
+ if (fieldsets.length) {
+ var radioname = 'proctoring_available_'+item;
+ var num = form.elements[radioname].length;
+ if (num) {
+ var setvis = '';
+ for (var i=0; i<num; i++) {
+ if (form.elements[radioname][i].checked) {
+ if (form.elements[radioname][i].value == '1') {
+ setvis = 1;
+ break;
+ }
+ }
+ }
+ for (var j=0; j<fieldsets.length; j++) {
+ if (setvis) {
+ fieldsets[j].style.display = 'block';
+ } else {
+ fieldsets[j].style.display = 'none';
+ }
+ }
+ }
+ }
+ return;
+}
+
+// ]]>
+</script>
+
+ENDSCRIPT
+}
+
+
sub lti_javascript {
my ($settings) = @_;
my $togglejs = <i_toggle_js();
@@ -4620,7 +4743,7 @@
}
$datatable .= '<label>'.
'<input type="checkbox" name="ltitools_courseconfig_'.$i.'" value="'.$item.'"'.$checked.' />'.
- $lt{'crs'.$item}.'</label>'.(' ' x2)."\n";
+ $lt{'crs'.$item}.'</label> '."\n";
}
$datatable .= '</span></fieldset>'.
'<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'.
@@ -4822,6 +4945,640 @@
return %lt;
}
+sub print_proctoring {
+ my ($dom,$settings,$rowtotal) = @_;
+ my $itemcount = 1;
+ my (%ordered,%providernames,%current,%currentdef);
+ my $confname = $dom.'-domainconfig';
+ my $switchserver = &check_switchserver($dom,$confname);
+ if (ref($settings) eq 'HASH') {
+ foreach my $item (keys(%{$settings})) {
+ if (ref($settings->{$item}) eq 'HASH') {
+ my $num = $settings->{$item}{'order'};
+ $ordered{$num} = $item;
+ }
+ }
+ } else {
+ %ordered = (
+ 1 => 'proctorio',
+ 2 => 'examity',
+ );
+ }
+ %providernames = &proctoring_providernames();
+ my $maxnum = scalar(keys(%ordered));
+ my (%requserfields,%optuserfields,%defaults,%extended,%crsconf, at courseroles, at 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>'.(' 'x2).'<b>'.$providernames{$provider}.'</b></span><br />'.
+ '<span class="LC_nobreak">'.$lt{'avai'}.' '.
+ '<label><input type="radio" name="proctoring_available_'.$provider.'" value="1"'.$onclickavailable.$checkedavailable{yes}.' />'.&mt('Yes').'</label> '."\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".
+ (' '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>'.
+ (' '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".
+ (' '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'}.': ';
+ if ($imgsrc) {
+ $datatable .= $imgsrc.
+ '<label><input type="checkbox" name="proctoring_image_del"'.
+ ' value="'.$provider.'" />'.&mt('Delete?').'</label></span> '.
+ '<span class="LC_nobreak"> '.&mt('Replace:');
+ }
+ $datatable .= ' ';
+ 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 .= ': '.
+ '<select name="proctoring_userincdom_'.$provider.'">'.
+ '<option value="0"'.$unseluserdom.'>'.$lt{'username'}.'</option>'.
+ '<option value="1"'.$seluserdom.'>'.$lt{'uname:dom'}.'</option>'.
+ '</select> ';
+ } else {
+ $datatable .= ' ';
+ 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> ';
+ }
+ $datatable .= '</fieldset>'."\n";
+ }
+ }
+ if (ref($defaults{$provider}) eq 'ARRAY') {
+ if (@{$defaults{$provider}}) {
+ my (%options, at 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>'.(' 'x2);
+ }
+ $datatable .= (' 'x4);
+ foreach my $dimen ('width','height') {
+ $datatable .= '<label>'.$fieldtitles{$dimen}.' '.
+ '<input type="text" name="proctoring_'.$dimen.'_'.$provider.'" size="5" '.
+ 'value="'.$currentdef{$dimen}.'" /></label>'.
+ (' '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 .= ' '.
+ '</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 .= ' '.
+ '</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 .= ' ';
+ }
+ $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 .= ' '.
+ '</td></tr></table>';
+ }
+ $datatable .= '</fieldset>';
+ }
+ if ($showroles) {
+ $datatable .= '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
+ '<legend>'.&mt('Role mapping').'</legend><table><tr>';
+ foreach my $role (@courseroles) {
+ my ($selected,$selectnone);
+ if (!$rolemaps{$role}) {
+ $selectnone = ' selected="selected"';
+ }
+ $datatable .= '<td style="text-align: center">'.
+ &Apache::lonnet::plaintext($role,'Course').'<br />'.
+ '<select name="proctoring_roles_'.$role.'_'.$provider.'">'.
+ '<option value=""'.$selectnone.'>'.&mt('Select').'</option>';
+ foreach my $ltirole (@ltiroles) {
+ unless ($selectnone) {
+ if ($rolemaps{$role} eq $ltirole) {
+ $selected = ' selected="selected"';
+ } else {
+ $selected = '';
+ }
+ }
+ $datatable .= '<option value="'.$ltirole.'"'.$selected.'>'.$ltirole.'</option>';
+ }
+ $datatable .= '</select></td>';
+ }
+ $datatable .= '</tr></table></fieldset>'.
+ '<fieldset class="proctoring_'.$provider.'" style="display:'.$optionsty.'">'.
+ '<legend>'.&mt('Custom items sent on launch').'</legend>'.
+ '<table><tr><th>'.&mt('Action').'</th><th>'.&mt('Name').'</th><th>'.&mt('Value').'</th></tr>'.
+ '<tr><td></td><td>lms</td>'.
+ '<td><input type="text" name="proctoring_customval_lms_'.$provider.'"'.
+ ' value="Loncapa" disabled="disabled"/></td></tr>';
+ if ((ref($settings) eq 'HASH') && (ref($settings->{$provider}) eq 'HASH') &&
+ (ref($settings->{$provider}->{'custom'}) eq 'HASH')) {
+ my %custom = %{$settings->{$provider}->{'custom'}};
+ if (keys(%custom) > 0) {
+ foreach my $key (sort(keys(%custom))) {
+ next if ($key eq 'lms');
+ $datatable .= '<tr><td><span class="LC_nobreak">'.
+ '<label><input type="checkbox" name="proctoring_customdel_'.$provider.'" value="'.
+ $key.'" />'.&mt('Delete').'</label></span></td><td>'.$key.'</td>'.
+ '<td><input type="text" name="proctoring_customval_'.$key.'_'.$provider.'"'.
+ ' value="'.$custom{$key}.'" /></td></tr>';
+ }
+ }
+ }
+ $datatable .= '<tr><td><span class="LC_nobreak">'.
+ '<label><input type="checkbox" name="proctoring_customadd" value="'.$provider.'" />'.
+ &mt('Add more').'</label></span></td><td><input type="text" name="proctoring_custom_name_'.$provider.'" />'.
+ '</td><td><input type="text" name="proctoring_custom_value_'.$provider.'" /></td></tr>'.
+ '</table></fieldset></td></tr>'."\n";
+ }
+ $datatable .= '</td></tr>';
+ }
+ $itemcount ++;
+ }
+ }
+ return $datatable;
+}
+
+sub proctoring_data {
+ my $requserfields = {
+ proctorio => ['user'],
+ examity => ['roles','user'],
+ };
+ my $optuserfields = {
+ proctorio => ['fullname'],
+ examity => ['fullname','firstname','lastname','email'],
+ };
+ my $defaults = {
+ proctorio => ['recordvideo','recordaudio','recordscreen','recordwebtraffic',
+ 'recordroomstart','verifyvideo','verifyaudio','verifydesktop',
+ 'verifyid','verifysignature','fullscreen','clipboard','tabslinks',
+ 'closetabs','onescreen','print','downloads','cache','rightclick',
+ 'reentry','calculator','whiteboard'],
+ examity => ['display'],
+ };
+ my $extended = {
+ proctorio => {
+ verifyid => ['verifyidauto','verifyidlive'],
+ fullscreen => ['fullscreenlenient','fullscreenmoderate','fullscreensever'],
+ tabslinks => ['notabs','linksonly'],
+ reentry => ['noreentry','agentreentry'],
+ calculator => ['calculatorbasic','calculatorsci'],
+ },
+ examity => {
+ display => {
+ target => ['iframe','tab','window'],
+ width => '',
+ height => '',
+ linktext => '',
+ explanation => '',
+ },
+ },
+ };
+ my $crsconf = {
+ proctorio => ['recordvideo','recordaudio','recordscreen','recordwebtraffic',
+ 'recordroomstart','verifyvideo','verifyaudio','verifydesktop',
+ 'verifyid','verifysignature','fullscreen','clipboard','tabslinks',
+ 'closetabs','onescreen','print','downloads','cache','rightclick',
+ 'reentry','calculator','whiteboard'],
+ examity => ['label','title','target','linktext','explanation','append'],
+ };
+ my $courseroles = ['cc','in','ta','ep','st'];
+ my $ltiroles = ['Instructor','ContentDeveloper','TeachingAssistant','Learner'];
+ return ($requserfields,$optuserfields,$defaults,$extended,$crsconf,$courseroles,$ltiroles);
+}
+
+sub proctoring_titles {
+ my ($item) = @_;
+ my (%common_lt,%custom_lt);
+ %common_lt = &Apache::lonlocal::texthash (
+ 'avai' => 'Available?',
+ 'base' => 'Basic Settings',
+ 'requ' => 'User data required to be sent on launch',
+ 'optu' => 'User data optionally sent on launch',
+ 'udsl' => 'User data sent on launch',
+ 'defa' => 'Defaults for items configurable in course',
+ 'sigmethod' => 'Signature Method',
+ 'key' => 'Key',
+ 'lifetime' => 'Nonce lifetime (s)',
+ 'secret' => 'Secret',
+ 'icon' => 'Icon',
+ 'fullname' => 'Full Name',
+ 'visible' => 'Visible input',
+ 'username' => 'username',
+ 'user' => 'User',
+ );
+ if ($item eq 'proctorio') {
+ %custom_lt = &Apache::lonlocal::texthash (
+ 'version' => 'OAuth version',
+ 'url' => 'API URL',
+ 'uname:dom' => 'username-domain',
+ );
+ } elsif ($item eq 'examity') {
+ %custom_lt = &Apache::lonlocal::texthash (
+ 'version' => 'LTI Version',
+ 'url' => 'URL',
+ 'uname:dom' => 'username:domain',
+ 'msgtype' => 'Message Type',
+ 'firstname' => 'First Name',
+ 'lastname' => 'Last Name',
+ 'email' => 'E-mail',
+ 'roles' => 'Role',
+ 'crstarget' => 'Display target',
+ 'crslabel' => 'Course label',
+ 'crstitle' => 'Course title',
+ 'crslinktext' => 'Link Text',
+ 'crsexplanation' => 'Explanation',
+ 'crsappend' => 'Provider URL',
+ );
+ }
+ my %lt = (%common_lt,%custom_lt);
+ return %lt;
+}
+
+sub proctoring_fieldtitles {
+ my ($item) = @_;
+ if ($item eq 'proctorio') {
+ return &Apache::lonlocal::texthash (
+ 'recordvideo' => 'Record video',
+ 'recordaudio' => 'Record audio',
+ 'recordscreen' => 'Record screen',
+ 'recordwebtraffic' => 'Record web traffic',
+ 'recordroomstart' => 'Record room scan',
+ 'verifyvideo' => 'Verify webcam',
+ 'verifyaudio' => 'Verify microphone',
+ 'verifydesktop' => 'Verify desktop recording',
+ 'verifyid' => 'Photo ID verification',
+ 'verifysignature' => 'Require signature',
+ 'fullscreen' => 'Fullscreen',
+ 'clipboard' => 'Disable copy/paste',
+ 'tabslinks' => 'New tabs/windows',
+ 'closetabs' => 'Close other tabs',
+ 'onescreen' => 'Limit to single screen',
+ 'print' => 'Disable Printing',
+ 'downloads' => 'Disable Downloads',
+ 'cache' => 'Empty cache after exam',
+ 'rightclick' => 'Disable right click',
+ 'reentry' => 'Re-entry to exam',
+ 'calculator' => 'Onscreen calculator',
+ 'whiteboard' => 'Onscreen whiteboard',
+ 'verifyidauto' => 'Automated verification',
+ 'verifyidlive' => 'Live agent verification',
+ 'fullscreenlenient' => 'Forced, but can navigate away for up to 30s',
+ 'fullscreenmoderate' => 'Forced, but can navigate away for up to 15s',
+ 'fullscreensever' => 'Forced, navigation away ends exam',
+ 'notabs' => 'Disaallowed',
+ 'linksonly' => 'Allowed from links in exam',
+ 'noreentry' => 'Disallowed',
+ 'agentreentry' => 'Agent required for re-entry',
+ 'calculatorbasic' => 'Basic',
+ 'calculatorsci' => 'Scientific',
+ );
+ } elsif ($item eq 'examity') {
+ return &Apache::lonlocal::texthash (
+ 'target' => 'Display target',
+ 'window' => 'Window',
+ 'tab' => 'Tab',
+ 'iframe' => 'iFrame',
+ 'height' => 'Height (pixels)',
+ 'width' => 'Width (pixels)',
+ 'linktext' => 'Default Link Text',
+ 'explanation' => 'Default Explanation',
+ 'append' => 'Provider URL',
+ );
+ }
+}
+
+sub proctoring_providernames {
+ return (
+ proctorio => 'Proctorio',
+ examity => 'Examity',
+ );
+}
+
sub print_lti {
my ($dom,$settings,$rowtotal) = @_;
my $itemcount = 1;
@@ -5137,7 +5894,7 @@
$output .= '</span></div>'.
'<div class="LC_floatleft" style="display:'.$userfieldsty.';" id="lti_userfield_'.$num.'">'.
'<input type="text" name="lti_customuser_'.$num.'" '.
- 'value="'.$userfield.'" /></div></fieldset>'.
+ 'value="'.$userfield.'" /></div></fieldset>'.
'<fieldset class="ltioption_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Mapping course roles').'</legend><table><tr>';
foreach my $ltirole (@lticourseroles) {
my ($selected,$selectnone);
@@ -12427,7 +13184,7 @@
}
}
if (!$numconfig) {
- $resulttext .= &mt('None');
+ $resulttext .= ' '.&mt('None');
}
$resulttext .= '</li>';
foreach my $item ('passback','roster') {
@@ -12606,6 +13363,536 @@
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, at courseroles, at 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$/, at 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$/, at 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$/, at 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$/, at 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$/, at 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);
+ 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 .= ' '.
+ '<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}.': '.$confhash{$provider}{$key}.'</li>';
+ }
+ }
+ if ($encconfhash{$provider}{'key'} ne '') {
+ $resulttext .= '<li>'.$lt{'key'}.': '.$encconfhash{$provider}{'key'}.'</li>';
+ }
+ if ($encconfhash{$provider}{'secret'} ne '') {
+ $resulttext .= '<li>'.$lt{'secret'}.': ';
+ 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$/, at fields)) {
+ $showroles = 1;
+ }
+ $resulttext .= '<li>'.$lt{'udsl'}.': "'.
+ 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'}.': <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 .= ' '.&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 .= (' 'x2).&Apache::lonnet::plaintext($role,'Course').'='.
+ $confhash{$provider}{'roles'}{$role}.',';
+ }
+ }
+ if ($rolemaps) {
+ $rolemaps =~ s/,$//;
+ $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>';
+ }
+ }
+ }
+ if (ref($confhash{$provider}{'custom'}) eq 'HASH') {
+ my $customlist;
+ if (keys(%{$confhash{$provider}{'custom'}})) {
+ foreach my $key (sort(keys(%{$confhash{$provider}{'custom'}}))) {
+ $customlist .= $key.'='.$confhash{$provider}{'custom'}{$key}.', ';
+ }
+ $customlist =~ s/,$//;
+ }
+ if ($customlist) {
+ $resulttext .= '<li>'.&mt('Custom items').': '.$customlist.'</li>';
+ }
+ }
+ $resulttext .= '</ul></li>';
+ }
+ }
+ $resulttext .= '</ul>';
+ } else {
+ $resulttext = &mt('No changes made.');
+ }
+ } else {
+ $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>';
+ }
+ if ($errors) {
+ $resulttext .= &mt('The following errors occurred: ').'<ul>'.
+ $errors.'</ul>';
+ }
+ return $resulttext;
+}
+
+sub process_proctoring_image {
+ my ($r,$dom,$confname,$caller,$provider,$configuserok,$switchserver,$author_ok) = @_;
+ my $filename = $env{'form.'.$caller.'.filename'};
+ my ($error,$url);
+ my ($width,$height) = (21,21);
+ if ($configuserok eq 'ok') {
+ if ($switchserver) {
+ $error = &mt('Upload of Remote Proctoring Provider icon is not permitted to this server: [_1]',
+ $switchserver);
+ } elsif ($author_ok eq 'ok') {
+ my ($result,$imageurl,$madethumb) =
+ &publishlogo($r,'upload',$caller,$dom,$confname,
+ "proctoring/$provider/icon",$width,$height);
+ if ($result eq 'ok') {
+ if ($madethumb) {
+ my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$});
+ my $imagethumb = "$path/tn-".$imagefile;
+ $url = $imagethumb;
+ } else {
+ $url = $imageurl;
+ }
+ } else {
+ $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result);
+ }
+ } else {
+ $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$author_ok);
+ }
+ } else {
+ $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$configuserok);
+ }
+ return ($url,$error);
+}
+
sub modify_lti {
my ($r,$dom,$action,$lastactref,%domconfig) = @_;
my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1);
@@ -19589,6 +20876,7 @@
END
}
+
sub new_spares_js {
my @sparestypes = ('primary','default');
my $types = join("','", at sparestypes);
@@ -19662,7 +20950,7 @@
function checkNewSpares(lonhost,type) {
var newSpare = document.getElementById('newspare_'+type+'_'+lonhost);
var chosen = newSpare.options[newSpare.selectedIndex].value;
- if (chosen != '') {
+ if (chosen != '') {
var othertype;
var othernewSpare;
if (type == 'primary') {
@@ -19796,7 +21084,7 @@
var dispval = 'block';
var selfcreateRegExp = /^cancreate_emailverified/;
if (caller == 'emailoptions') {
- optionsElement = domForm.cancreate_email;
+ optionsElement = domForm.cancreate_email;
}
if (caller == 'studentsubmission') {
optionsElement = domForm.postsubmit;
Index: loncom/interface/lonconfigsettings.pm
diff -u loncom/interface/lonconfigsettings.pm:1.46 loncom/interface/lonconfigsettings.pm:1.47
--- loncom/interface/lonconfigsettings.pm:1.46 Thu Jul 18 18:28:52 2019
+++ loncom/interface/lonconfigsettings.pm Thu Sep 17 00:35:04 2020
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set domain-wide configuration settings
#
-# $Id: lonconfigsettings.pm,v 1.46 2019/07/18 18:28:52 raeburn Exp $
+# $Id: lonconfigsettings.pm,v 1.47 2020/09/17 00:35:04 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -253,6 +253,10 @@
}
}
}
+ if (grep(/^proctoring$/, at actions)) {
+ $onload .= "toggleProctoring(document.display,'proctorio');".
+ "toggleProctoring(document.display,'examity');";
+ }
if (grep(/^scantron$/, at actions)) {
$onload .= "toggleScantron('document.display');";
}
More information about the LON-CAPA-cvs
mailing list