--- loncom/interface/domainprefs.pm 2019/04/26 20:22:18 1.356 +++ loncom/interface/domainprefs.pm 2020/02/05 23:46:01 1.369 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.356 2019/04/26 20:22:18 raeburn Exp $ +# $Id: domainprefs.pm,v 1.369 2020/02/05 23:46:01 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -219,7 +219,7 @@ sub handler { 'serverstatuses','requestcourses','helpsettings', 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', - 'ltitools','ssl','trust','lti','passwords'],$dom); + 'ltitools','ssl','trust','lti','privacy','passwords'],$dom); my %encconfig = &Apache::lonnet::get_dom('encconfig',['ltitools','lti'],$dom); if (ref($domconfig{'ltitools'}) eq 'HASH') { @@ -247,7 +247,7 @@ sub handler { } } my @prefs_order = ('rolecolors','login','defaults','passwords','quotas','autoenroll', - 'autoupdate','autocreate','directorysrch','contacts', + 'autoupdate','autocreate','directorysrch','contacts','privacy', 'usercreation','selfcreation','usermodification','scantron', 'requestcourses','requestauthor','coursecategories', 'serverstatuses','helpsettings','coursedefaults', @@ -497,10 +497,16 @@ sub handler { modify => \&modify_selfenrollment, }, 'privacy' => - {text => 'User Privacy', + {text => 'Availability of User Information', help => 'Domain_Configuration_User_Privacy', - header => [{col1 => 'Setting', - col2 => 'Value',}], + 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, }, @@ -771,6 +777,8 @@ sub process_changes { $output = &modify_trust($dom,$lastactref,%domconfig); } elsif ($action eq 'lti') { $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig); + } elsif ($action eq 'privacy') { + $output = &modify_privacy($dom,%domconfig); } elsif ($action eq 'passwords') { $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig); } @@ -823,6 +831,7 @@ sub print_config_box { if ($numheaders > 1) { my $colspan = ''; my $rightcolspan = ''; + my $leftnobr = ''; if (($action eq 'rolecolors') || ($action eq 'defaults') || ($action eq 'directorysrch') || (($action eq 'login') && ($numheaders < 4))) { @@ -831,12 +840,15 @@ sub print_config_box { if ($action eq 'usersessions') { $rightcolspan = ' colspan="3"'; } + if ($action eq 'passwords') { + $leftnobr = ' LC_nobreak'; + } $output .= ' - + '; $rowtotal ++; @@ -844,7 +856,7 @@ sub print_config_box { ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') || ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') || - ($action eq 'contacts')) { + ($action eq 'contacts') || ($action eq 'privacy')) { $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal); @@ -879,7 +891,9 @@ sub print_config_box { if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'coursecategories') || - ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'passwords')) { + ($action eq 'trust') || ($action eq 'contacts') || + ($action eq 'privacy') || ($action eq 'passwords')) { + my $leftnobr = ' LC_nobreak'; if ($action eq 'coursecategories') { $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal); $colspan = ' colspan="2"'; @@ -932,7 +946,7 @@ sub print_config_box { '."\n"; if ($action eq 'coursecategories') { $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal); - } elsif (($action eq 'contacts') || ($action eq 'passwords')) { + } elsif (($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'passwords')) { if ($action eq 'passwords') { $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal); } else { @@ -947,7 +961,7 @@ sub print_config_box {
'.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col1'}).' '.&mt($item->{'header'}->[0]->{'col2'}).'
- + '."\n"; if ($action eq 'passwords') { $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal); @@ -2896,7 +2910,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') { @@ -2905,6 +2919,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; @@ -2914,7 +2931,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 { @@ -5070,6 +5095,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"'; @@ -5098,6 +5124,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'".');"'; @@ -5247,7 +5274,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'}.': '. '
'. + ''; + $itemcount ++; + } + } elsif ($position eq 'middle') { + if ((@instdoms > 1) || (keys(%by_location) > 0)) { + if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) { + foreach my $item (@{$types}) { + $datatable .= &modifiable_userdata_row('privacy','othdom_'.$item,$settings, + $numinrow,$itemcount,'','','','','', + '',$usertypes->{$item}); + $itemcount ++; + } + } + $datatable .= &modifiable_userdata_row('privacy','othdom_default',$settings, + $numinrow,$itemcount,'','','','','', + '',$othertitle); + $itemcount ++; + } else { + my (@insttypes,%insttitles); + if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) { + @insttypes = @{$types}; + %insttitles = %{$usertypes}; + } + foreach my $item (@insttypes,'default') { + my $title; + if ($item eq 'default') { + $title = $othertitle; + } else { + $title = $insttitles{$item}; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''. + ''; + $itemcount ++; + } + } + } + } else { + my $prefix; + if ($position eq 'lower') { + $prefix = 'priv'; + } else { + $prefix = 'unpriv'; + } + foreach my $item (@items) { + $datatable .= &modifiable_userdata_row('privacy',$prefix.'_'.$item,$settings, + $numinrow,$itemcount,'','','','','', + '',$names{$item}); + $itemcount ++; + } + } + if (ref($rowtotal)) { + $$rowtotal += $itemcount; + } + return $datatable; +} + sub print_passwords { my ($position,$dom,$confname,$settings,$rowtotal) = @_; my ($datatable,$css_class); @@ -5887,7 +6061,7 @@ sub print_passwords { $datatable .= ''. - '   '; + '   '; } } my $checkedcase; @@ -5991,7 +6165,7 @@ sub print_passwords { &mt('(If you use the same account ... reset a password from this page.)').'

'. &mt('Include custom text:'); if ($customurl) { - my $link = &Apache::loncommon::modal_link($customurl,&mt('Custom text file'),600,500, + my $link = &Apache::loncommon::modal_link($customurl,&mt('custom text'),600,500, undef,undef,undef,undef,'background-color:#ffffff'); $datatable .= ' '.$link. ''. ''; $itemcount ++; $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. ''; $itemcount ++; @@ -6162,40 +6337,73 @@ sub print_passwords { $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. ''; $itemcount ++; $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. ''; } 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 .= ''. ''. - ''; - + ''; } return $datatable; } @@ -9095,16 +9303,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') { @@ -9112,7 +9326,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 = parseInt(field.value,10); + if (minval < $defmin) { + alert('$intalert{passmin}'); + field.value = '$defmin'; + } + } + } else { + if (field.value == '0') { + field.value = ''; + } + if (field.value != '') { + if (field.name == '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 = ''; } } } @@ -9366,7 +9633,7 @@ sub build_category_rows { sub modifiable_userdata_row { my ($context,$item,$settings,$numinrow,$rowcount,$usertypes,$fieldsref,$titlesref, - $rowid,$customcss,$rowstyle) = @_; + $rowid,$customcss,$rowstyle,$itemdesc) = @_; my ($role,$rolename,$statustype); $role = $item; if ($context eq 'cancreate') { @@ -9389,6 +9656,8 @@ sub modifiable_userdata_row { } } elsif ($context eq 'lti') { $rolename = &mt('Institutional data used (if available)'); + } elsif ($context eq 'privacy') { + $rolename = $itemdesc; } else { if ($role eq 'cr') { $rolename = &mt('Custom role'); @@ -9438,6 +9707,13 @@ sub modifiable_userdata_row { if (ref($settings) eq 'HASH') { $hashref = $settings->{'instdata'}; } + } elsif ($context eq 'privacy') { + my ($key,$inner) = split(/_/,$role); + if (ref($settings) eq 'HASH') { + if (ref($settings->{$key}) eq 'HASH') { + $hashref = $settings->{$key}->{$inner}; + } + } } elsif (ref($settings->{$context}) eq 'HASH') { if (ref($settings->{$context}->{$role}) eq 'HASH') { $hashref = $settings->{'lti_instdata'}; @@ -9476,6 +9752,24 @@ sub modifiable_userdata_row { unless ($role eq 'emailusername') { if (exists($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') { if ($role eq 'st') { if (ref($settings) ne 'HASH') { @@ -9504,6 +9798,8 @@ sub modifiable_userdata_row { } else { if ($context eq 'lti') { $prefix = 'lti'; + } elsif ($context eq 'privacy') { + $prefix = 'privacy'; } $output .= '
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col1'}).' '.&mt($item->{'header'}->[3]->{'col2'}).'
'.$titles{$domtype}.''; + 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 .= '
'.$names{$roletype}.''; + foreach my $option (@options) { + $datatable .= '  '; + } + $datatable .= '
'; + } + } + $datatable .= '
'.$title.''. + &mt('Nothing to set here, as there are no other domains'). + '
'.$titles{'min'}.''. - ''. - ' '.&mt('(Leave blank for no minimum)').''. + ''. + ' '.&mt('(Enter an integer: 7 or larger)').''. '
'.$titles{'max'}.''. - ''. + ''. ' '.&mt('(Leave blank for no maximum)').''. '
'.$titles{'expire'}.''. - ''. + ''. ' '.&mt('(Leave blank for no expiration)').''. '
'.$titles{'numsaved'}.''. - ''. + ''. ' '.&mt('(Leave blank to not save previous passwords)').''. '
'. &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 .= '