--- loncom/interface/domainprefs.pm 2019/05/06 01:30:14 1.358 +++ loncom/interface/domainprefs.pm 2019/08/25 02:42:55 1.365 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.358 2019/05/06 01:30:14 raeburn Exp $ +# $Id: domainprefs.pm,v 1.365 2019/08/25 02:42:55 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -2905,7 +2905,7 @@ function toggleLTI(form,setting,item) { } } } - } else if ((setting == 'user') || (setting == 'crs') || (setting == 'passback')) { + } else if ((setting == 'user') || (setting == 'crs') || (setting == 'passback') || (setting == 'callback')) { var radioname = ''; var divid = ''; if (setting == 'user') { @@ -2914,6 +2914,9 @@ function toggleLTI(form,setting,item) { } else if (setting == 'crs') { radioname = 'lti_mapcrs_'+item; divid = 'lti_crsfield_'+item; + } else if (setting == 'callback') { + radioname = 'lti_callback_'+item; + divid = 'lti_callbackfield_'+item; } else { radioname = 'lti_passback_'+item; divid = 'lti_passback_'+item; @@ -2923,7 +2926,7 @@ function toggleLTI(form,setting,item) { var setvis = ''; for (var i=0; i{'callback'} ne '') { + $callback = $current->{'callback'}; + $checked{'callback'}{'Y'} = ' checked="checked"'; + $callbacksty = 'inline-block'; + } else { + $checked{'callback'}{'N'} = ' checked="checked"'; + } if ($current->{'topmenu'}) { $checked{'topmenu'}{'Y'} = ' checked="checked"'; } else { @@ -5079,6 +5090,7 @@ sub lti_options { } else { $checked{'makecrs'}{'N'} = ' checked="checked"'; $checked{'crssec'}{'N'} = ' checked="checked"'; + $checked{'callback'}{'N'} = ' checked="checked"'; $checked{'topmenu'}{'N'} = ' checked="checked"'; $checked{'inlinemenu'}{'Y'} = ' checked="checked"'; $checked{'menuitem'}{'grades'} = ' checked="checked"'; @@ -5107,6 +5119,7 @@ sub lti_options { my $onclickuser = ' onclick="toggleLTI(this.form,'."'user','$num'".');"'; my $onclickcrs = ' onclick="toggleLTI(this.form,'."'crs','$num'".');"'; my $onclicksec = ' onclick="toggleLTI(this.form,'."'sec','$num'".');"'; + my $onclickcallback = ' onclick="toggleLTI(this.form,'."'callback','$num'".');"'; my $onclicksecsrc = ' onclick="toggleLTI(this.form,'."'secsrc','$num'".')"'; my $onclicklcauth = ' onclick="toggleLTI(this.form,'."'lcauth','$num'".')"'; my $onclickmenu = ' onclick="toggleLTI(this.form,'."'lcmenu','$num'".');"'; @@ -5256,7 +5269,17 @@ sub lti_options { ''.(' 'x2). ''. + &mt('Outcomes Extension (1.0)').''. + '
'. + '
'.&mt('Callback on logout').': '. + ''.(' 'x2). + '
'. + '
'. + ''.&mt('Parameter').': '. + ''. + '
'. '
'.&mt('Course defaults (Course Coordinator can override)').''. '
'.$lt{'topmenu'}.': '. ''; } else { - my $checkedon; - my $checkedoff = ' checked="checked"'; + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my %ownerchg = ( + by => {}, + for => {}, + ); + my %ownertitles = &Apache::lonlocal::texthash ( + by => 'Course owner status(es) allowed', + for => 'Student status(es) allowed', + ); if (ref($settings) eq 'HASH') { - if ($settings->{crsownerchg}) { - $checkedon = $checkedoff; - $checkedoff = ''; + if (ref($settings->{crsownerchg}) eq 'HASH') { + if (ref($settings->{crsownerchg}{'by'}) eq 'ARRAY') { + map { $ownerchg{by}{$_} = 1; } (@{$settings->{crsownerchg}{'by'}}); + } + if (ref($settings->{crsownerchg}{'for'}) eq 'ARRAY') { + map { $ownerchg{for}{$_} = 1; } (@{$settings->{crsownerchg}{'for'}}); + } } } $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. ''. &mt('Requirements').'
    '. - '
  • '.&mt("Course 'type' is not a Community").'
  • '. + '
  • '.&mt("Course 'type' is not a Community or Placement Test").'
  • '. '
  • '.&mt('User is Course Coordinator and also course owner').'
  • '. '
  • '.&mt("Student's only active roles are student role(s) in course(s) owned by this user").'
  • '. + '
  • '.&mt('User, course, and student share same domain').'
  • '. '
'. ''. - ''. - '   '. - ''. - ''; - + ''; + foreach my $item ('by','for') { + $datatable .= '
'. + ''.$ownertitles{$item}.''; + if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) { + foreach my $type (@{$types}) { + my $checked; + if ($ownerchg{$item}{$type}) { + $checked = ' checked="checked"'; + } + $datatable .= ''. + '   '; + } + } + my $checked; + if ($ownerchg{$item}{'default'}) { + $checked = ' checked="checked"'; + } + $datatable .= '
'; + } + $datatable .= ''; } return $datatable; } @@ -9222,16 +9297,22 @@ ENDSCRIPT } sub passwords_javascript { - my $intauthcheck = &mt('Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.'); - my $intauthcost = &mt('Warning: bcrypt encryption cost for internal authentication must be an integer.'); - &js_escape(\$intauthcheck); - &js_escape(\$intauthcost); + my %intalert = &Apache::lonlocal::texthash ( + authcheck => 'Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.', + authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.', + passmin => 'Warning: minimum password length must be a positive integer greater than 6.', + passmax => 'Warning: maximum password length must be a positive integer (or blank).', + passexp => 'Warning: days before password expiration must be a positive integer (or blank).', + passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).', + ); + &js_escape(\%intalert); + my $defmin = $Apache::lonnet::passwdmin; my $intauthjs = <<"ENDSCRIPT"; function warnIntAuth(field) { if (field.name == 'intauth_check') { if (field.value == '2') { - alert('$intauthcheck'); + alert('$intalert{authcheck}'); } } if (field.name == 'intauth_cost') { @@ -9239,7 +9320,60 @@ function warnIntAuth(field) { if (field.value != '') { var regexdigit=/^\\d+\$/; if (!regexdigit.test(field.value)) { - alert('$intauthcost'); + alert('$intalert{authcost}'); + } + } + } + return; +} + +function warnIntPass(field) { + field.value.replace(/^\s+/,''); + field.value.replace(/\s+\$/,''); + var regexdigit=/^\\d+\$/; + if (field.name == 'passwords_min') { + if (field.value == '') { + alert('$intalert{passmin}'); + field.value = '$defmin'; + } else { + if (!regexdigit.test(field.value)) { + alert('$intalert{passmin}'); + field.value = '$defmin'; + } + var minval = parseFloat(field.value,10); + if (minval < $defmin) { + alert('$intalert{passmin}'); + field.value = '$defmin'; + } + } + } else { + if (field.value == '0') { + field.value = ''; + } + if (field.value != '') { + if (field.name == 'passwords_expire') { + var regexpposnum=/^\\d+(|\\.\\d*)\$/; + if (!regexpposnum.test(field.value)) { + alert('$intalert{passexp}'); + field.value = ''; + } else { + var expval = parseFloat(field.value); + if (expval == 0) { + alert('$intalert{passexp}'); + field.value = ''; + } + } + } else { + if (!regexdigit.test(field.value)) { + if (field.name == 'passwords_max') { + alert('$intalert{passmax}'); + } else { + if (field.name == 'passwords_numsaved') { + alert('$intalert{passnum}'); + } + } + } + field.value = ''; } } } @@ -10747,7 +10881,7 @@ sub check_configuser { my ($configuserok,%currroles); if ($uhome eq 'no_host') { srand( time() ^ ($$ + ($$ << 15)) ); # Seed rand. - my $configpass = &LONCAPA::Enrollment::create_password(); + my $configpass = &LONCAPA::Enrollment::create_password($dom); $configuserok = &Apache::lonnet::modifyuser($dom,$confname,'','internal', $configpass,'','','','','',undef,$servadm); @@ -12625,6 +12759,13 @@ sub modify_lti { } } } + if ($env{'form.lti_callback_'.$idx}) { + if ($env{'form.lti_callbackparam_'.$idx}) { + my $callback = $env{'form.lti_callbackparam_'.$idx}; + $callback =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'callback'} = $callback; + } + } foreach my $field ('passback','roster','topmenu','inlinemenu') { if ($env{'form.lti_'.$field.'_'.$idx}) { $confhash{$itemid}{$field} = 1; @@ -12650,7 +12791,7 @@ sub modify_lti { } } unless (($idx eq 'add') || ($changes{$itemid})) { - foreach my $field ('mapuser','mapcrs','makecrs','section','passback','roster','lcauth','lcauthparm','topmenu','inlinemenu') { + foreach my $field ('mapuser','mapcrs','makecrs','section','passback','roster','lcauth','lcauthparm','topmenu','inlinemenu','callback') { if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { $changes{$itemid} = 1; } @@ -12876,6 +13017,11 @@ sub modify_lti { } else { $resulttext .= '
  • '.&mt('No section assignment').'
  • '; } + if ($confhash{$itemid}{'callback'}) { + $resulttext .= '
  • '.&mt('Callback setting').': '.$confhash{$itemid}{'callback'}.'
  • '; + } else { + $resulttext .= '
  • '.&mt('No callback to logout LON-CAPA session when user logs out of Comsumer'); + } foreach my $item ('passback','roster','topmenu','inlinemenu') { $resulttext .= '
  • '.$lt{$item}.': '; if ($confhash{$itemid}{$item}) { @@ -14405,8 +14551,8 @@ sub modify_passwords { 'intauth_cost' => 10, 'intauth_check' => 0, 'intauth_switch' => 0, - 'min' => 7, ); + $staticdefaults{'min'} = $Apache::lonnet::passwdmin; foreach my $type (@oktypes) { $staticdefaults{'resetpostlink'}{$type} = ['email','username']; } @@ -14587,10 +14733,18 @@ sub modify_passwords { $env{'form.passwords_'.$rule} =~ s/^\s+|\s+$//g; my $ruleok; if ($rule eq 'expire') { - if ($env{'form.passwords_'.$rule} =~ /^\d+(|\.\d*)$/) { - $ruleok = 1; + if (($env{'form.passwords_'.$rule} =~ /^\d+(|\.\d*)$/) && + ($env{'form.passwords_'.$rule} ne '0')) { + $ruleok = 1; + } + } elsif ($rule eq 'min') { + if ($env{'form.passwords_'.$rule} =~ /^\d+$/) { + if ($env{'form.passwords_'.$rule} >= $Apache::lonnet::passwdmin) { + $ruleok = 1; + } } - } elsif ($env{'form.passwords_'.$rule} =~ /^\d+$/) { + } elsif (($env{'form.passwords_'.$rule} =~ /^\d+$/) && + ($env{'form.passwords_'.$rule} ne '0')) { $ruleok = 1; } if ($ruleok) { @@ -14628,13 +14782,36 @@ sub modify_passwords { } } } - if ($env{'form.passwords_crsowner'}) { - $newvalues{'crsownerchg'} = 1; - unless ($current{'crsownerchg'}) { - $changes{'crsownerchg'} = 1; + my %crsownerchg = ( + by => [], + for => [], + ); + foreach my $item ('by','for') { + my @posstypes = &Apache::loncommon::get_env_multiple('form.passwords_crsowner_'.$item); + foreach my $type (sort(@posstypes)) { + if (grep(/^\Q$type\E$/,@oktypes)) { + push(@{$crsownerchg{$item}},$type); + } + } + } + $newvalues{'crsownerchg'} = \%crsownerchg; + if (ref($current{'crsownerchg'}) eq 'HASH') { + foreach my $item ('by','for') { + if (ref($current{'crsownerchg'}{$item}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($current{'crsownerchg'}{$item},$crsownerchg{$item}); + if (@diffs > 0) { + $changes{'crsownerchg'} = 1; + last; + } + } + } + } elsif (!exists($domconfig{passwords})) { + foreach my $item ('by','for') { + if (@{$crsownerchg{$item}} > 0) { + $changes{'crsownerchg'} = 1; + last; + } } - } elsif ($current{'crsownerchg'}) { - $changes{'crsownerchg'} = 1; } my %confighash = ( @@ -14777,7 +14954,8 @@ sub modify_passwords { if ($confighash{'passwords'}{$rule} eq '') { if ($rule eq 'min') { $resulttext .= '
  • '.&mt('[_1] not set.',$titles{$rule}); - ' '.&mt('Default of 7 will be used').'
  • '; + ' '.&mt('Default of [_1] will be used', + $Apache::lonnet::passwdmin).''; } else { $resulttext .= '
  • '.&mt('[_1] set to none',$titles{$rule}).'
  • '; } @@ -14786,10 +14964,29 @@ sub modify_passwords { } } } elsif ($key eq 'crsownerchg') { - if ($confighash{'passwords'}{'crsownerchg'}) { - $resulttext .= '
  • '.&mt('Course owner may change student passwords.').'
  • '; + if (ref($confighash{'passwords'}{'crsownerchg'}) eq 'HASH') { + if ((@{$confighash{'passwords'}{'crsownerchg'}{'by'}} == 0) || + (@{$confighash{'passwords'}{'crsownerchg'}{'for'}} == 0)) { + $resulttext .= '
  • '.&mt('Course owner may not change student passwords.').'
  • '; + } else { + my %crsownerstr; + foreach my $item ('by','for') { + if (ref($confighash{'passwords'}{'crsownerchg'}{$item}) eq 'ARRAY') { + foreach my $type (@{$confighash{'passwords'}{'crsownerchg'}{$item}}) { + if ($type eq 'default') { + $crsownerstr{$item} .= $othertitle.', '; + } elsif ($usertypes->{$type} ne '') { + $crsownerstr{$item} .= $usertypes->{$type}.', '; + } + } + $crsownerstr{$item} =~ s/\Q, \E$//; + } + } + $resulttext .= '
  • '.&mt('Course owner (with status: [_1]) may change passwords for students (with status: [_2]).', + $crsownerstr{'by'},$crsownerstr{'for'}).'
  • '; + } } else { - $resulttext .= '
  • '.&mt('Course owner may not change student passwords.'); + $resulttext .= '
  • '.&mt('Course owner may not change student passwords.').'
  • '; } } $resulttext .= ''; @@ -15636,7 +15833,7 @@ sub modify_selfcreation { $chgtext .= &mt('For self-created accounts verified by e-mail address, username is set as follows:'). '
      '; foreach my $status (@statuses) { - if ($type eq 'default') { + if ($status eq 'default') { $chgtext .= '
    • '.$othertitle.' -- '.$options{$cancreate{'emailverified'}{$status}}.'
    • '; } else { $chgtext .= '
    • '.$usertypes{$status}.' -- '.$options{$cancreate{'emailverified'}{$status}}.'
    • '; @@ -16768,6 +16965,10 @@ sub modify_coursecategories { } $resulttext .= '
    '; } + &Apache::lonnet::do_cache_new('cats',$dom,$cathash,3600); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'cats'} = 1; + } } $resulttext .= ''; if ($changes{'unauth'} || $changes{'auth'}) { @@ -19548,7 +19749,7 @@ sub devalidate_remote_domconfs { my %thismachine; map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); my @posscached = ('domainconfig','domdefaults','ltitools','usersessions', - 'directorysrch','passwdconf'); + 'directorysrch','passwdconf','cats'); if (keys(%servers)) { foreach my $server (keys(%servers)) { next if ($thismachine{$server});