--- loncom/interface/domainprefs.pm 2021/11/03 13:49:57 1.389 +++ loncom/interface/domainprefs.pm 2021/12/24 00:48:30 1.397 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.389 2021/11/03 13:49:57 raeburn Exp $ +# $Id: domainprefs.pm,v 1.397 2021/12/24 00:48:30 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -221,7 +221,7 @@ sub handler { 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', 'ltitools','ssl','trust','lti','privacy','passwords', - 'proctoring','wafproxy'],$dom); + 'proctoring','wafproxy','ipaccess'],$dom); my %encconfig = &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring'],$dom,undef,1); if (ref($domconfig{'ltitools'}) eq 'HASH') { @@ -260,8 +260,8 @@ sub handler { } } } - my @prefs_order = ('rolecolors','login','defaults','wafproxy','passwords','quotas', - 'autoenroll','autoupdate','autocreate','directorysrch', + my @prefs_order = ('rolecolors','login','ipaccess','defaults','wafproxy','passwords', + 'quotas','autoenroll','autoupdate','autocreate','directorysrch', 'contacts','privacy','usercreation','selfcreation', 'usermodification','scantron','requestcourses','requestauthor', 'coursecategories','serverstatuses','helpsettings','coursedefaults', @@ -624,6 +624,14 @@ sub handler { print => \&print_lti, modify => \&modify_lti, }, + 'ipaccess' => + {text => 'IP-based access control', + help => 'Domain_Configuration_IP_Access', + header => [{col1 => 'Setting', + col2 => 'Value'},], + print => \&print_ipaccess, + modify => \&modify_ipaccess, + }, ); if (keys(%servers) > 1) { $prefs{'login'} = { text => 'Log-in page options', @@ -679,6 +687,8 @@ $javascript_validations $coursebrowserjs END + } elsif (grep(/^ipaccess$/,@actions)) { + $js .= &Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}); } if (grep(/^selfcreation$/,@actions)) { $js .= &selfcreate_javascript(); @@ -825,6 +835,8 @@ sub process_changes { $output = &modify_passwords($r,$dom,$confname,$lastactref,%domconfig); } elsif ($action eq 'wafproxy') { $output = &modify_wafproxy($dom,$action,$lastactref,%domconfig); + } elsif ($action eq 'ipaccess') { + $output = &modify_ipaccess($dom,$lastactref,%domconfig); } return $output; } @@ -867,6 +879,8 @@ sub print_config_box { $output .= &autoupdate_javascript(); } elsif ($action eq 'login') { $output .= &saml_javascript(); + } elsif ($action eq 'ipaccess') { + $output .= &ipaccess_javascript($settings); } $output .= ' @@ -1235,7 +1249,7 @@ sub print_config_box { } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || ($action eq 'ltitools') || ($action eq 'lti') || - ($action eq 'proctoring')) { + ($action eq 'proctoring') || ($action eq 'ipaccess')) { $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } } @@ -1707,6 +1721,186 @@ sub login_file_options { ); } +sub print_ipaccess { + my ($dom,$settings,$rowtotal) = @_; + my $css_class; + my $itemcount = 0; + my $datatable; + my %ordered; + if (ref($settings) eq 'HASH') { + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + if ($num eq '') { + $num = scalar(keys(%{$settings})); + } + $ordered{$num} = $item; + } + } + } + my $maxnum = scalar(keys(%ordered)); + 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 $item = $ordered{$items[$i]}; + my ($name,$ipranges,%commblocks,%courses); + if (ref($settings->{$item}) eq 'HASH') { + $name = $settings->{$item}->{'name'}; + $ipranges = $settings->{$item}->{'ip'}; + if (ref($settings->{$item}->{'commblocks'}) eq 'HASH') { + %commblocks = %{$settings->{$item}->{'commblocks'}}; + } + if (ref($settings->{$item}->{'courses'}) eq 'HASH') { + %courses = %{$settings->{$item}->{'courses'}}; + } + } + my $chgstr = ' onchange="javascript:reorderIPaccess(this.form,'."'ipaccess_pos_".$item."'".');"'; + $datatable .= ''. + ''; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderIPaccess(this.form,'."'ipaccess_pos_add'".');"'; + $datatable .= ''."\n". + ''."\n". + ''."\n"; + $$rowtotal ++; + return $datatable; +} + +sub ipaccess_options { + my ($num,$itemcount,$dom,$name,$ipranges,$blocksref,$coursesref) = @_; + my (%currblocks,%currcourses,$output); + if (ref($blocksref) eq 'HASH') { + %currblocks = %{$blocksref}; + } + if (ref($coursesref) eq 'HASH') { + %currcourses = %{$coursesref}; + } + $output = '
'.&mt('Location(s)').''. + ''.&mt('Name').': '. + ''. + '
'. + '
'.&mt('IP Range(s)').''. + &mt('Format for each IP range').': '.&mt('A.B.C.D/N or A.B.C.D-E.F.G.H').'
'. + &mt('Range(s) will be stored as IP netblock(s) in CIDR notation (comma separated)').'
'. + '
'. + '
'.&mt('Functionality Blocked?').''. + &blocker_checkboxes($num,$blocksref).'
'. + '
'.&mt('Courses/Communities allowed').''. + '
' + .''.(' 'x2). + ''. + &ipaccess_options($i,$itemcount,$dom,$name,$ipranges,\%commblocks,\%courses). + '
'."\n". + ''."\n". + ' '."\n". + ''.&mt('Add').''. + &ipaccess_options('add',$itemcount,$dom). + '
'; + foreach my $cid (sort(keys(%currcourses))) { + my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1}); + $output .= ''; + } + $output .= '
'. + ''. + ' ('.$cid.')
'.&mt('Add').': '. + ''. + &Apache::loncommon::selectcourse_link('display','ipaccess_cnum_'.$num,'ipaccess_cdom_'.$num,'ipaccess_cdesc_'.$num,$dom,undef,'Course/Community'). + ''. + ''. + '
'."\n". + ''; + return $output; +} + +sub blocker_checkboxes { + my ($num,$blocks) = @_; + my ($typeorder,$types) = &commblocktype_text(); + my $numinrow = 6; + my $output = ''; + for (my $i=0; $i<@{$typeorder}; $i++) { + my $block = $typeorder->[$i]; + my $blockstatus; + if (ref($blocks) eq 'HASH') { + if ($blocks->{$block} eq 'on') { + $blockstatus = 'checked="checked"'; + } + } + my $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $output .= ''; + } + $output .= ''; + } + if ($i == scalar(@{$typeorder})-1) { + my $colsleft = $numinrow-$rem; + if ($colsleft > 1) { + $output .= ''; + } + $output .= '
'; + } else { + $output .= ''; + } + } else { + $output .= ''; + } + my $item = 'ipaccess_block_'.$num; + if ($blockstatus) { + $blockstatus = ' '.$blockstatus; + } + $output .= ''."\n". + '
'; + return $output; +} + +sub commblocktype_text { + my %types = &Apache::lonlocal::texthash( + 'com' => 'Messaging', + 'chat' => 'Chat Room', + 'boards' => 'Discussion', + 'port' => 'Portfolio', + 'groups' => 'Groups', + 'blogs' => 'Blogs', + 'about' => 'User Information', + 'printout' => 'Printouts', + 'passwd' => 'Change Password', + 'grades' => 'Gradebook', + 'search' => 'Course search', + 'wishlist' => 'Stored links', + 'annotate' => 'Annotations', + ); + my $typeorder = ['com','chat','boards','port','groups','blogs','about','wishlist','printout','grades','search','annotate','passwd']; + return ($typeorder,\%types); +} + sub print_rolecolors { my ($phase,$role,$dom,$confname,$settings,$rowtotal) = @_; my %choices = &color_font_choices(); @@ -3177,14 +3371,16 @@ sub lti_javascript { return $togglejs; } my (%ordered,$total,%jstext); - $total = 0; + $total = scalar(keys(%{$settings})); foreach my $item (keys(%{$settings})) { if (ref($settings->{$item}) eq 'HASH') { my $num = $settings->{$item}{'order'}; + if ($num eq '') { + $num = $total - 1; + } $ordered{$num} = $item; } } - $total = scalar(keys(%{$settings})); my @jsarray = (); foreach my $item (sort {$a <=> $b } (keys(%ordered))) { push(@jsarray,$ordered{$item}); @@ -3247,32 +3443,65 @@ sub lti_toggle_js { localauth => 'Local auth argument', krb => 'Kerberos domain', ); + my $crsincalert = &mt('"User\'s identity sent" needs to be set to "Yes" first,[_1] before setting "Course\'s identity sent" to "Yes"',"\n"); + &js_escape(\$crsincalert); return <<"ENDSCRIPT"; + +ENDSCRIPT +} + sub print_autoenroll { my ($dom,$settings,$rowtotal) = @_; my $autorun = &Apache::lonnet::auto_run(undef,$dom), @@ -3608,7 +3905,7 @@ sub print_autoupdate { ''.$choices{'run'}.''. ' '. + $updateoff.'value="0" />'.&mt('No').' '. ''. ''; @@ -5937,6 +6234,9 @@ sub print_lti { foreach my $item (keys(%{$settings})) { if (ref($settings->{$item}) eq 'HASH') { my $num = $settings->{$item}{'order'}; + if ($num eq '') { + $num = scalar(keys(%{$settings})); + } $ordered{$num} = $item; } } @@ -5949,13 +6249,14 @@ sub print_lti { for (my $i=0; $i<@items; $i++) { $css_class = $itemcount%2?' class="LC_odd_row"':''; my $item = $ordered{$items[$i]}; - my ($key,$secret,$lifetime,$consumer,$requser,$current); + my ($key,$secret,$lifetime,$consumer,$requser,$crsinc,$current); if (ref($settings->{$item}) eq 'HASH') { $key = $settings->{$item}->{'key'}; $secret = $settings->{$item}->{'secret'}; $lifetime = $settings->{$item}->{'lifetime'}; $consumer = $settings->{$item}->{'consumer'}; $requser = $settings->{$item}->{'requser'}; + $crsinc = $settings->{$item}->{'crsinc'}; $current = $settings->{$item}; } my $onclickrequser = ' onclick="toggleLTI(this.form,'."'requser','$i'".');"'; @@ -5967,6 +6268,15 @@ sub print_lti { $checkedrequser{'no'} = $checkedrequser{'yes'}; $checkedrequser{'yes'} = ''; } + my $onclickcrsinc = ' onclick="toggleLTI(this.form,'."'crsinc','$i'".');"'; + my %checkedcrsinc = ( + yes => ' checked="checked"', + no => '', + ); + if (!$crsinc) { + $checkedcrsinc{'no'} = $checkedcrsinc{'yes'}; + $checkedcrsinc{'yes'} = ''; + } my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_".$item."'".');"'; $datatable .= '' .''.&mt('Yes').' '."\n". ''."\n". '

'. + ''.$lt{'crsinc'}.':'. + ' '."\n". + ''."\n". + (' 'x4). ''.$lt{'key'}. ': '. (' 'x2). @@ -6036,6 +6350,10 @@ sub print_lti { ' '."\n". ''."\n". '

'. + ''.$lt{'crsinc'}.':'. + ' '."\n". + ''."\n". + (' 'x4). ''.$lt{'key'}.': '."\n". (' 'x2). ''.$lt{'secret'}.':'. @@ -6056,6 +6374,7 @@ sub lti_names { 'consumer' => 'Consumer', 'secret' => 'Secret', 'requser' => "User's identity sent", + 'crsinc' => "Course's identity sent", 'email' => 'Email address', 'sourcedid' => 'User ID', 'other' => 'Other', @@ -6072,7 +6391,8 @@ sub lti_options { my (%checked,%rolemaps,$crssecsrc,$userfield,$cidfield,$callback); $checked{'mapuser'}{'sourcedid'} = ' checked="checked"'; $checked{'mapcrs'}{'course_offering_sourcedid'} = ' checked="checked"'; - $checked{'makecrs'}{'N'} = ' checked="checked"'; + $checked{'storecrs'}{'Y'} = ' checked="checked"'; + $checked{'makecrs'}{'N'} = ' checked="checked"'; $checked{'mapcrstype'} = {}; $checked{'makeuser'} = {}; $checked{'selfenroll'} = {}; @@ -6090,6 +6410,7 @@ sub lti_options { my $callbacksty = 'none'; my $passbacksty = 'none'; my $optionsty = 'block'; + my $crssty = 'block'; my $lcauthparm; my $lcauthparmstyle = 'display:none'; my $lcauthparmtext; @@ -6100,6 +6421,9 @@ sub lti_options { if (ref($current) eq 'HASH') { if (!$current->{'requser'}) { $optionsty = 'none'; + $crssty = 'none'; + } elsif (!$current->{'crsinc'}) { + $crssty = 'none'; } if (($current->{'mapuser'} ne '') && ($current->{'mapuser'} ne 'lis_person_sourcedid')) { $checked{'mapuser'}{'sourcedid'} = ''; @@ -6126,6 +6450,10 @@ sub lti_options { $checked{'mapcrstype'}{$type} = ' checked="checked"'; } } + if (!$current->{'storecrs'}) { + $checked{'storecrs'}{'N'} = $checked{'storecrs'}{'Y'}; + $checked{'storecrs'}{'Y'} = ''; + } if ($current->{'makecrs'}) { $checked{'makecrs'}{'Y'} = ' checked="checked"'; } @@ -6232,7 +6560,17 @@ sub lti_options { my $onclicksecsrc = ' onclick="toggleLTI(this.form,'."'secsrc','$num'".')"'; my $onclicklcauth = ' onclick="toggleLTI(this.form,'."'lcauth','$num'".')"'; my $onclickmenu = ' onclick="toggleLTI(this.form,'."'lcmenu','$num'".');"'; - my $output = '
'.&mt('Mapping users').''. + my $output = '
'.&mt('Logout options').''. + '
'.&mt('Callback to logout LON-CAPA on log out from Consumer').': '. + ''.(' 'x2). + '
'. + '
'. + ''.&mt('Parameter').': '. + ''. + '
'. + '
'.&mt('Mapping users').''. '
'.&mt('LON-CAPA username').': '; foreach my $option ('sourcedid','email','other') { $output .= '
'. - '
'.&mt('Mapping course roles').''; - foreach my $ltirole (@lticourseroles) { - my ($selected,$selectnone); - if ($rolemaps{$ltirole} eq '') { - $selectnone = ' selected="selected"'; - } - $output .= ''; - } - $output .= '
'.$ltirole.'
'. - '
'. - '
'.&mt('Roles which may create user accounts').''; + '
'.&mt('Roles which may create user accounts').''; foreach my $ltirole (@ltiroles) { $output .= '  '; } $output .= '
'. - '
'.&mt('New user accounts created for LTI users').''. + '
'.&mt('New user accounts created for LTI users').''. ''. &modifiable_userdata_row('lti','instdata_'.$num,$current,$numinrow,$itemcount). '
'. @@ -6296,7 +6610,29 @@ sub lti_options { ''.$lcauthparmtext.''. ''. '
'. - '
'.&mt('Mapping courses').''. + '
'. + &mt('LON-CAPA menu items (Course Coordinator can override)').''. + '
'.$lt{'topmenu'}.': '. + ''.(' 'x2). + '
'. + '
'. + '
'.$lt{'inlinemenu'}.': '. + ''.(' 'x2). + '
'; + $output .='
'. + '
'. + ''.&mt('Menu items').': '; + foreach my $type ('fullname','coursetitle','role','logout','grades') { + $output .= ''. + (' 'x2); + } + $output .= '
'. + '
'.&mt('Mapping courses').''. '
'. &mt('Unique course identifier').': '; foreach my $option ('course_offering_sourcedid','context_id','other') { @@ -6313,21 +6649,51 @@ sub lti_options { $checked{'mapcrstype'}{$type}.' />'.$coursetypetitles{$type}.''. (' 'x2); } - $output .= '
'. - '
'.&mt('Creating courses').''. + $output .= '

'. + ''.&mt('Store mapping of course identifier to LON-CAPA CourseID').': '. + ''.(' 'x2). + ''. + '
'. + '
'.&mt('Mapping course roles').''; + foreach my $ltirole (@lticourseroles) { + my ($selected,$selectnone); + if ($rolemaps{$ltirole} eq '') { + $selectnone = ' selected="selected"'; + } + $output .= ''; + } + $output .= '
'.$ltirole.'
'. + '
'. + '
'.&mt('Creating courses').''. ''.&mt('Course created (if absent) on Instructor access').': '. ''.(' 'x2). ''. '
'. - '
'.&mt('Roles which may self-enroll').''; + '
'.&mt('Roles which may self-enroll').''; foreach my $lticrsrole (@lticourseroles) { $output .= '  '; } $output .= '
'. - '
'.&mt('Course options').''. + '
'.&mt('Course options').''. '
'.&mt('Assign users to sections').': '. ''.(' 'x2). @@ -6379,36 +6745,7 @@ sub lti_options { &mt('Outcomes Service (1.1)').''.(' 'x2). '
'. - '
'. - '
'.&mt('Callback on logout').': '. - ''.(' 'x2). - '
'. - '
'. - ''.&mt('Parameter').': '. - ''. - '
'. - '
'.&mt('Course defaults (Course Coordinator can override)').''. - '
'.$lt{'topmenu'}.': '. - ''.(' 'x2). - '
'. - '
'. - '
'.$lt{'inlinemenu'}.': '. - ''.(' 'x2). - '
'; - $output .='
'. - '
'. - ''.&mt('Menu items').': '; - foreach my $type ('fullname','coursetitle','role','logout','grades') { - $output .= ''. - (' 'x2); - } + '
'; $output .= '
'; # '
'.&mt('Assigning author roles').''; # @@ -7600,7 +7937,7 @@ sub print_wafproxy { if ($current) { $aliasrows .= $current; if ($forsaml) { - $aliasrows .= ' ('.&mt('also for Shibboleth').')'; + $aliasrows .= ' ('.&mt('also for SSO Auth').')'; } } else { $aliasrows .= &mt('None'); @@ -7628,7 +7965,7 @@ sub print_wafproxy { ''. (' 'x2).''. - &mt('Alias used for Shibboleth').':