--- loncom/interface/domainprefs.pm 2022/01/19 17:01:34 1.160.6.120 +++ loncom/interface/domainprefs.pm 2022/11/17 21:43:38 1.418 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.160.6.120 2022/01/19 17:01:34 raeburn Exp $ +# $Id: domainprefs.pm,v 1.418 2022/11/17 21:43:38 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -27,7 +27,7 @@ # # ############################################################### -############################################################## +############################################################### =pod @@ -104,8 +104,8 @@ $datatable - HTML containing form eleme In the case of course requests, radio buttons are displayed for each institutional affiliate type (and also default, and _LC_adv) for each of the course types -(official, unofficial, community, and textbook). In each case the radio buttons -allow the selection of one of four values: +(official, unofficial, community, textbook, placement, and lti). +In each case the radio buttons allow the selection of one of four values: 0, approval, validate, autolimit=N (where N is blank, or a positive integer). which have the following effects: @@ -170,10 +170,12 @@ use Apache::loncoursequeueadmin(); use LONCAPA qw(:DEFAULT :match); use LONCAPA::Enrollment; use LONCAPA::lonauthcgi(); +use LONCAPA::SSL; use File::Copy; use Locale::Language; use DateTime::TimeZone; use DateTime::Locale; +use Time::HiRes qw( sleep ); use Net::CIDR; my $registered_cleanup; @@ -218,13 +220,70 @@ sub handler { 'serverstatuses','requestcourses','helpsettings', 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', - 'passwords','wafproxy','ipaccess'],$dom); + 'ltitools','ssl','trust','lti','ltisec','privacy','passwords', + 'proctoring','wafproxy','ipaccess'],$dom); + my %encconfig = + &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1); + if (ref($domconfig{'ltitools'}) eq 'HASH') { + if (ref($encconfig{'ltitools'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'ltitools'}})) { + if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') && + (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item}; + } + } + } + } + } + if (ref($domconfig{'lti'}) eq 'HASH') { + if (ref($encconfig{'lti'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'lti'}})) { + if ((ref($domconfig{'lti'}{$id}) eq 'HASH') && + (ref($encconfig{'lti'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item}; + } + } + } + } + } + if (ref($domconfig{'ltisec'}) eq 'HASH') { + if (ref($domconfig{'ltisec'}{'linkprot'}) eq 'HASH') { + if (ref($encconfig{'linkprot'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'ltisec'}{'linkprot'}})) { + unless ($id =~ /^\d+$/) { + delete($domconfig{'ltisec'}{'linkprot'}{$id}); + } + if ((ref($domconfig{'ltisec'}{'linkprot'}{$id}) eq 'HASH') && + (ref($encconfig{'linkprot'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'ltisec'}{'linkprot'}{$id}{$item} = $encconfig{'linkprot'}{$id}{$item}; + } + } + } + } + } + } + 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','ipaccess','defaults','wafproxy','passwords', 'quotas','autoenroll','autoupdate','autocreate','directorysrch', - 'contacts','usercreation','selfcreation','usermodification', - 'scantron','requestcourses','requestauthor','coursecategories', - 'serverstatuses','helpsettings','coursedefaults', - 'selfenrollment','usersessions'); + 'contacts','privacy','usercreation','selfcreation', + 'usermodification','scantron','requestcourses','requestauthor', + 'coursecategories','serverstatuses','helpsettings','coursedefaults', + 'ltitools','proctoring','selfenrollment','usersessions','ssl', + 'trust','lti'); my %existing; if (ref($domconfig{'loadbalancing'}) eq 'HASH') { %existing = %{$domconfig{'loadbalancing'}}; @@ -268,7 +327,9 @@ sub handler { header => [{col1 => 'Setting', col2 => 'Value'}, {col1 => 'Institutional user types', - col2 => 'Name displayed'}], + col2 => 'Name displayed'}, + {col1 => 'Mapping for missing usernames via standard log-in', + col2 => 'Rules in use'}], print => \&print_defaults, modify => \&modify_defaults, }, @@ -298,7 +359,7 @@ sub handler { modify => \&modify_passwords, }, 'quotas' => - { text => 'Blogs, personal web pages, webDAV/quotas, portfolios', + { text => 'Blogs, personal pages/timezones, webDAV/quotas, portfolio', help => 'Domain_Configuration_Quotas', header => [{col1 => 'User affiliation', col2 => 'Available tools', @@ -483,6 +544,20 @@ sub handler { print => \&print_selfenrollment, modify => \&modify_selfenrollment, }, + 'privacy' => + {text => 'Availability of User Information', + help => 'Domain_Configuration_User_Privacy', + 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, + }, 'usersessions' => {text => 'User session hosting/offloading', help => 'Domain_Configuration_User_Sessions', @@ -506,7 +581,75 @@ sub handler { print => \&print_loadbalancing, modify => \&modify_loadbalancing, }, - 'ipaccess' => + 'ltitools' => + {text => 'External Tools (LTI)', + help => 'Domain_Configuration_LTI_Tools', + header => [{col1 => 'Setting', + col2 => 'Value',}], + 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', + header => [{col1 => 'Server', + col2 => 'Certificate Status'}, + {col1 => 'Connections to other servers', + col2 => 'Rules'}, + {col1 => 'Connections from other servers', + col2 => 'Rules'}, + {col1 => "Replicating domain's published content", + col2 => 'Rules'}], + print => \&print_ssl, + modify => \&modify_ssl, + }, + 'trust' => + {text => 'Trust Settings', + help => 'Domain_Configuration_Trust', + header => [{col1 => "Access to this domain's content by others", + col2 => 'Rules'}, + {col1 => "Access to other domain's content by this domain", + col2 => 'Rules'}, + {col1 => "Enrollment in this domain's courses by others", + col2 => 'Rules',}, + {col1 => "Co-author roles in this domain for others", + col2 => 'Rules',}, + {col1 => "Co-author roles for this domain's users elsewhere", + col2 => 'Rules',}, + {col1 => "Domain roles in this domain assignable to others", + col2 => 'Rules'}, + {col1 => "Course catalog for this domain displayed elsewhere", + col2 => 'Rules'}, + {col1 => "Requests for creation of courses in this domain by others", + col2 => 'Rules'}, + {col1 => "Users in other domains can send messages to this domain", + col2 => 'Rules'},], + print => \&print_trust, + modify => \&modify_trust, + }, + 'lti' => + {text => 'LTI Link Protection and LTI Consumers', + help => 'Domain_Configuration_LTI_Provider', + header => [{col1 => 'Encryption of shared secrets', + col2 => 'Settings'}, + {col1 => 'Rules for shared secrets', + col2 => 'Settings'}, + {col1 => 'Link Protectors', + col2 => 'Settings'}, + {col1 => 'Consumers', + col2 => 'Settings'},], + print => \&print_lti, + modify => \&modify_lti, + }, + 'ipaccess' => {text => 'IP-based access control', help => 'Domain_Configuration_IP_Access', header => [{col1 => 'Setting', @@ -521,7 +664,7 @@ sub handler { header => [{col1 => 'Log-in Service', col2 => 'Server Setting',}, {col1 => 'Log-in Page Items', - col2 => ''}, + col2 => 'Settings'}, {col1 => 'Log-in Help', col2 => 'Value'}, {col1 => 'Custom HTML in document head', @@ -701,6 +844,18 @@ sub process_changes { $output = &modify_usersessions($dom,$lastactref,%domconfig); } elsif ($action eq 'loadbalancing') { $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') { + $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); } elsif ($action eq 'wafproxy') { @@ -720,7 +875,7 @@ sub print_config_box { } elsif ($action eq 'defaults') { $output = &defaults_javascript($settings); } elsif ($action eq 'passwords') { - $output = &passwords_javascript(); + $output = &passwords_javascript($action); } elsif ($action eq 'helpsettings') { my (%privs,%levelscurrent); my %full=(); @@ -735,8 +890,15 @@ sub print_config_box { &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent); my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); $output = - &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full, + &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full, \@templateroles); + } elsif ($action eq 'ltitools') { + $output .= <itools_javascript($settings); + } elsif ($action eq 'lti') { + $output .= &passwords_javascript('secrets')."\n". + <i_javascript($dom,$settings); + } elsif ($action eq 'proctoring') { + $output .= &proctoring_javascript($settings); } elsif ($action eq 'wafproxy') { $output .= &wafproxy_javascript($dom); } elsif ($action eq 'autoupdate') { @@ -763,7 +925,7 @@ sub print_config_box { if ($numheaders > 1) { my $colspan = ''; my $rightcolspan = ''; - my $leftnobr = ''; + my $leftnobr = ''; if (($action eq 'rolecolors') || ($action eq 'defaults') || ($action eq 'directorysrch') || (($action eq 'login') && ($numheaders < 5))) { @@ -786,8 +948,9 @@ sub print_config_box { $rowtotal ++; if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') || ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') || - ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'directorysrch') || - ($action eq 'helpsettings') || ($action eq 'contacts') || ($action eq 'wafproxy')) { + ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') || + ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') || + ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') || ($action eq 'lti')) { $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal); @@ -821,17 +984,49 @@ sub print_config_box { $rowtotal ++; if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') || ($action eq 'selfenrollment') || - ($action eq 'usersessions') || ($action eq 'coursecategories') || - ($action eq 'contacts') || ($action eq 'passwords')) { + ($action eq 'usersessions') || ($action eq 'coursecategories') || + ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'defaults') || + ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti')) { if ($action eq 'coursecategories') { $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal); $colspan = ' colspan="2"'; + } elsif ($action eq 'trust') { + $output .= $item->{'print'}->('shared',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal); } else { $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal); } - $output .= ' + if ($action eq 'trust') { + $output .= ' + </table> + </td> + </tr>'; + my @trusthdrs = qw(2 3 4 5 6 7); + my @prefixes = qw(enroll othcoau coaurem domroles catalog reqcrs); + for (my $i=0; $i<@trusthdrs; $i++) { + $output .= ' + <tr> + <td> + <table class="LC_nested"> + <tr class="LC_info_row"> + <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[$trusthdrs[$i]]->{'col1'}).'</td> + <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[$trusthdrs[$i]]->{'col2'}).'</td></tr>'. + $item->{'print'}->($prefixes[$i],$dom,$settings,\$rowtotal).' + </table> + </td> + </tr>'; + } + $output .= ' + <tr> + <td> + <table class="LC_nested"> + <tr class="LC_info_row"> + <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[8]->{'col1'}).'</td> + <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[8]->{'col2'}).'</td></tr>'. + $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } else { + $output .= ' </table> </td> </tr> @@ -842,15 +1037,16 @@ sub print_config_box { <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td> <td class="LC_right_item">'.&mt($item->{'header'}->[2]->{'col2'}).'</td> </tr>'."\n"; - if ($action eq 'coursecategories') { - $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal); - } elsif (($action eq 'contacts') || ($action eq 'passwords')) { - if ($action eq 'passwords') { - $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal); - } else { - $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal); - } - $output .= ' + if ($action eq 'coursecategories') { + $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal); + } elsif (($action eq 'contacts') || ($action eq 'privacy') || + ($action eq 'passwords') || ($action eq 'lti')) { + if ($action eq 'passwords') { + $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal); + } else { + $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal); + } + $output .= ' </tr> </table> </td> @@ -861,26 +1057,49 @@ sub print_config_box { <tr class="LC_info_row"> <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td> <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td></tr>'."\n"; - if ($action eq 'passwords') { - $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal); - } else { - $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); - } - $output .= ' + if ($action eq 'passwords') { + $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal); + } else { + $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } + $output .= ' </table> </td> </tr> <tr>'; - } else { - $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } else { + $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } } $rowtotal ++; } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') || - ($action eq 'defaults') || ($action eq 'directorysrch') || - ($action eq 'helpsettings') || ($action eq 'wafproxy')) { + ($action eq 'directorysrch') || ($action eq 'helpsettings') || + ($action eq 'wafproxy')) { $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } elsif ($action eq 'scantron') { $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal); + } elsif ($action eq 'ssl') { + $output .= $item->{'print'}->('connto',$dom,$settings,\$rowtotal).' + </table> + </td> + </tr> + <tr> + <td> + <table class="LC_nested"> + <tr class="LC_info_row"> + <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col1'}).'</td> + <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[2]->{'col2'}).'</td></tr>'. + $item->{'print'}->('connfrom',$dom,$settings,\$rowtotal).' + </table> + </td> + </tr> + <tr> + <td> + <table class="LC_nested"> + <tr class="LC_info_row"> + <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col1'}).'</td> + <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[3]->{'col2'}).'</td></tr>'. + $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } elsif ($action eq 'login') { if ($numheaders == 5) { $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).' @@ -971,8 +1190,8 @@ sub print_config_box { <td> <table class="LC_nested"> <tr class="LC_info_row"> - <td class="LC_left_item"'.$colspan.' valign="top">'.&mt($item->{'header'}->[4]->{'col1'}).'</td> - <td class="LC_right_item" valign="top">'.&mt($item->{'header'}->[4]->{'col2'}).'</td> + <td class="LC_left_item"'.$colspan.' style="vertical-align: top">'.&mt($item->{'header'}->[4]->{'col1'}).'</td> + <td class="LC_right_item" style="vertical-align: top">'.&mt($item->{'header'}->[4]->{'col2'}).'</td> </tr>'. &print_validation_rows('requestcourses',$dom,$settings,\$rowtotal); } elsif ($action eq 'requestauthor') { @@ -987,9 +1206,9 @@ sub print_config_box { <td> <table class="LC_nested"> <tr class="LC_info_row"> - <td class="LC_left_item"'.$colspan.' valign="top">'. + <td class="LC_left_item"'.$colspan.' style="vertical-align: top">'. &mt($item->{'header'}->[2]->{'col1'}).'</td> - <td class="LC_right_item" valign="top">'. + <td class="LC_right_item" style="vertical-align: top">'. &mt($item->{'header'}->[2]->{'col2'}).'</td> </tr>'. &print_rolecolors($phase,'author',$dom,$confname,$settings,\$rowtotal).' @@ -1017,30 +1236,30 @@ sub print_config_box { <td class="LC_left_item" colspan="2">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>'; } elsif ($action eq 'serverstatuses') { $output .= ' - <td class="LC_left_item" valign="top">'.&mt($item->{'header'}->[0]->{'col1'}). + <td class="LC_left_item" style="vertical-align: top">'.&mt($item->{'header'}->[0]->{'col1'}). '<br />('.&mt('Automatic access for Dom. Coords.').')</td>'; } else { $output .= ' - <td class="LC_left_item" valign="top">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>'; + <td class="LC_left_item" style="vertical-align: top">'.&mt($item->{'header'}->[0]->{'col1'}).'</td>'; } if (defined($item->{'header'}->[0]->{'col3'})) { - $output .= '<td class="LC_left_item" valign="top">'. + $output .= '<td class="LC_left_item" style="vertical-align: top">'. &mt($item->{'header'}->[0]->{'col2'}); if ($action eq 'serverstatuses') { $output .= '<br />(<tt>'.&mt('user1:domain1,user2:domain2 etc.').'</tt>)'; } } else { - $output .= '<td class="LC_right_item" valign="top">'. + $output .= '<td class="LC_right_item" style="vertical-align: top">'. &mt($item->{'header'}->[0]->{'col2'}); } $output .= '</td>'; if ($item->{'header'}->[0]->{'col3'}) { if (defined($item->{'header'}->[0]->{'col4'})) { - $output .= '<td class="LC_left_item" valign="top">'. + $output .= '<td class="LC_left_item" style="vertical-align: top">'. &mt($item->{'header'}->[0]->{'col3'}); } else { - $output .= '<td class="LC_right_item" valign="top">'. + $output .= '<td class="LC_right_item" style="vertical-align: top">'. &mt($item->{'header'}->[0]->{'col3'}); } if ($action eq 'serverstatuses') { @@ -1049,7 +1268,7 @@ sub print_config_box { $output .= '</td>'; } if ($item->{'header'}->[0]->{'col4'}) { - $output .= '<td class="LC_right_item" valign="top">'. + $output .= '<td class="LC_right_item" style="vertical-align: top">'. &mt($item->{'header'}->[0]->{'col4'}); } $output .= '</tr>'; @@ -1057,7 +1276,8 @@ sub print_config_box { if ($action eq 'quotas') { $output .= &print_quotas($dom,$settings,\$rowtotal,$action); } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || - ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || + ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || + ($action eq 'ltitools') || ($action eq 'proctoring') || ($action eq 'ipaccess')) { $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } @@ -1078,13 +1298,12 @@ sub print_login { %lt = &login_file_options(); $switchserver = &check_switchserver($dom,$confname); } - if ($caller eq 'service') { my %servers = &Apache::lonnet::internet_dom_servers($dom); my $choice = $choices{'disallowlogin'}; $css_class = ' class="LC_odd_row"'; $datatable .= '<tr'.$css_class.'><td>'.$choice.'</td>'. - '<td align="right"><table><tr><th>'.$choices{'hostid'}.'</th>'. + '<td style="text-align: right"><table><tr><th>'.$choices{'hostid'}.'</th>'. '<th>'.$choices{'server'}.'</th>'. '<th>'.$choices{'serverpath'}.'</th>'. '<th>'.$choices{'custompath'}.'</th>'. @@ -1365,7 +1584,7 @@ sub print_login { my $choice = $choices{'headtag'}; $css_class = ' class="LC_odd_row"'; $datatable .= '<tr'.$css_class.'><td colspan="2">'.$choice.'</td>'. - '<td align="left"><table><tr><th>'.$choices{'hostid'}.'</th>'. + '<td style="text-align: left"><table><tr><th>'.$choices{'hostid'}.'</th>'. '<th>'.$choices{'current'}.'</th>'. '<th>'.$choices{'action'}.'</th>'. '<th>'.$choices{'exempt'}.'</th></tr>'."\n"; @@ -1409,13 +1628,13 @@ sub print_login { '<table><tr><th>'.$choices{'hostid'}.'</th>'. '<th>'.$choices{'samllanding'}.'</th>'. '<th>'.$choices{'samloptions'}.'</th></tr>'."\n"; - my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso,%styleon,%styleoff); + my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso,%styleon,%styleoff); foreach my $lonhost (keys(%domservers)) { $samlurl{$lonhost} = '/adm/sso'; $styleon{$lonhost} = 'display:none'; $styleoff{$lonhost} = ''; } - if (ref($settings->{'saml'}) eq 'HASH') { + if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) { foreach my $lonhost (keys(%{$settings->{'saml'}})) { if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') { $saml{$lonhost} = 1; @@ -1424,6 +1643,7 @@ sub print_login { $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'}; $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'}; $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'}; + $samlwindow{$lonhost} = $settings->{'saml'}{$lonhost}{'window'}; $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'}; $styleon{$lonhost} = ''; $styleoff{$lonhost} = 'display:none'; @@ -1441,6 +1661,12 @@ sub print_login { $samlon = $samloff; $samloff = ' '; } + my $samlwinon = ''; + my $samlwinoff = ' checked="checked"'; + if ($samlwindow{$lonhost}) { + $samlwinon = $samlwinoff; + $samlwinoff = ''; + } my $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'.$domservers{$lonhost}.'</span></td>'. '<td><span class="LC_nobreak"><label><input type="radio" name="saml_'.$lonhost.'"'.$samloff. @@ -1450,12 +1676,10 @@ sub print_login { 'onclick="toggleSamlOptions(this.form,'."'$lonhost'".');" value="1" />'. &mt('Yes').'</label></span></td>'. '<td id="samloptionson_'.$lonhost.'" style="'.$styleon{$lonhost}.'" width="100%">'. - '<table><tr><th colspan="5" align="center">'.&mt('SSO').'</th><th align="center">'. - '<span class="LC_nobreak">'.&mt('Non-SSO').'</span></th></tr>'. + '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th></tr>'. '<tr><th>'.&mt('Text').'</th><th>'.&mt('Image').'</th>'. - '<th>'.&mt('Alt Text').'</th><th>'.&mt('URL').'</th>'. - '<th>'.&mt('Tool Tip').'</th><th>'.&mt('Text').'</th></tr>'. - '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="8" value="'. + '<th>'.&mt('Alt Text').'</th></tr>'. + '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="20" value="'. $samltext{$lonhost}.'" /></td><td>'; if ($samlimg{$lonhost}) { $datatable .= '<img src="'.$samlimg{$lonhost}.'" /><br />'. @@ -1472,13 +1696,21 @@ sub print_login { $datatable .= '<input type="file" name="saml_img_'.$lonhost.'" />'; } $datatable .= '</td>'. - '<td><input type="text" name="saml_alt_'.$lonhost.'" size="20" '. - 'value="'.$samlalt{$lonhost}.'" /></td>'. - '<td><input type="text" name="saml_url_'.$lonhost.'" size="8" '. + '<td><input type="text" name="saml_alt_'.$lonhost.'" size="25" '. + 'value="'.$samlalt{$lonhost}.'" /></td></tr></table><br />'. + '<table width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th><th align="center">'. + '<span class="LC_nobreak">'.&mt('Non-SSO').'</span></th></tr>'. + '<tr><th>'.&mt('URL').'</th><th>'.&mt('Tool Tip').'</th>'. + '<th>'.&mt('Pop-up if iframe').'</th><th>'.&mt('Text').'</th></tr>'. + '<tr'.$css_class.'>'. + '<td><input type="text" name="saml_url_'.$lonhost.'" size="30" '. 'value="'.$samlurl{$lonhost}.'" /></td>'. - '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="15">'. + '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="20">'. $samltitle{$lonhost}.'</textarea></td>'. - '<td><input type="text" name="saml_notsso_'.$lonhost.'" size="8" '. + '<td><label><input type="radio" name="saml_window_'.$lonhost.'" value=""'.$samlwinoff.'>'. + &mt('No').'</label>'.(' 'x2).'<label><input type="radio" '. + 'name="saml_window_'.$lonhost.'" value="1"'.$samlwinon.'>'.&mt('Yes').'</label></td>'. + '<td><input type="text" name="saml_notsso_'.$lonhost.'" size="12" '. 'value="'.$samlnotsso{$lonhost}.'" /></td></tr>'. '</table></td>'. '<td id="samloptionsoff_'.$lonhost.'" style="'.$styleoff{$lonhost}.'" width="100%"> </td></tr>'; @@ -1877,7 +2109,7 @@ sub display_color_options { $logincolors = &login_text_colors($img,$role,$logintext,$phase,$choices, $designs,$defaults); - } else + } else { if ($img ne 'domlogo') { $datatable.= &logo_display_options($img,$defaults,$designs); } @@ -2001,7 +2233,7 @@ sub display_color_options { '<table border="0"><tr>'; foreach my $item (@{$bgs}) { - $datatable .= '<td align="center">'.$choices->{$item}; + $datatable .= '<td style="text-align: center">'.$choices->{$item}; my $color = $designs->{'bgs'}{$item} ? $designs->{'bgs'}{$item} : $defaults->{'bgs'}{$item}; if ($designs->{'bgs'}{$item}) { $datatable .= ' '; @@ -2029,7 +2261,7 @@ sub display_color_options { '<table border="0"><tr>'; foreach my $item (@{$links}) { my $color = $designs->{'links'}{$item} ? $designs->{'links'}{$item} : $defaults->{'links'}{$item}; - $datatable .= '<td align="center">'.$choices->{$item}."\n"; + $datatable .= '<td style="text-align: center">'.$choices->{$item}."\n"; if ($designs->{'links'}{$item}) { $datatable.=' '; } @@ -2090,7 +2322,7 @@ sub login_text_colors { my ($img,$role,$logintext,$phase,$choices,$designs,$defaults) = @_; my $color_menu = '<table border="0"><tr>'; foreach my $item (@{$logintext}) { - $color_menu .= '<td align="center">'.$choices->{$item}; + $color_menu .= '<td style="text-align: center">'.$choices->{$item}; my $color = $designs->{'logintext'}{$item} ? $designs->{'logintext'}{$item} : $defaults->{'logintext'}{$item}; $color_menu .= '<br /><input type="text" class="colorchooser" size="8" name="'.$role.'_'.$item.'" value="'.$color. '" onblur = "javascript:colchg_span('."'css_".$role.'_'.$item."'".',this);" /></td>'; @@ -2123,7 +2355,7 @@ sub image_changes { $role.'_del_'.$img.'" value="1" />'.&mt('Delete?'). '</label> '.&mt('Replace:').'</span><br />'; } else { - $output .= '<td valign="middle">'.$logincolors.&mt('Upload:').'<br />'; + $output .= '<td class="LC_middle">'.$logincolors.&mt('Upload:').'<br />'; } } return $output; @@ -2142,7 +2374,7 @@ sub print_quotas { my $typecount = 0; my ($css_class,%titles); if ($context eq 'requestcourses') { - @usertools = ('official','unofficial','community','textbook'); + @usertools = ('official','unofficial','community','textbook','placement','lti'); @options =('norequest','approval','validate','autolimit'); %validations = &Apache::lonnet::auto_courserequest_checks($dom); %titles = &courserequest_titles(); @@ -2151,7 +2383,7 @@ sub print_quotas { @options = ('norequest','approval','automatic'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','webdav','portfolio'); + @usertools = ('aboutme','blog','webdav','portfolio','timezone'); %titles = &tool_titles(); } if (ref($types) eq 'ARRAY') { @@ -2255,9 +2487,12 @@ sub print_quotas { } } else { my $checked = 'checked="checked" '; + if ($item eq 'timezone') { + $checked = ''; + } if (ref($settings) eq 'HASH') { if (ref($settings->{$item}) eq 'HASH') { - if ($settings->{$item}->{$type} == 0) { + if (!$settings->{$item}->{$type}) { $checked = ''; } elsif ($settings->{$item}->{$type} == 1) { $checked = 'checked="checked" '; @@ -2606,7 +2841,7 @@ sub print_studentcode { my ($settings,$rowtotal) = @_; my $rownum = 0; my ($output,%current); - my @crstypes = ('official','unofficial','community','textbook'); + my @crstypes = ('official','unofficial','community','textbook','placement','lti'); if (ref($settings) eq 'HASH') { if (ref($settings->{'uniquecode'}) eq 'HASH') { foreach my $type (@crstypes) { @@ -2856,6 +3091,132 @@ $jstext{'templates'}; ENDSCRIPT } +sub ltitools_javascript { + my ($settings) = @_; + my $togglejs = <itools_toggle_js(); + unless (ref($settings) eq 'HASH') { + return $togglejs; + } + my (%ordered,$total,%jstext); + $total = 0; + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + $total = scalar(keys(%{$settings})); + my @jsarray = (); + foreach my $item (sort {$a <=> $b } (keys(%ordered))) { + push(@jsarray,$ordered{$item}); + } + my $jstext = ' var ltitools = Array('."'".join("','",@jsarray)."'".');'."\n"; + return <<"ENDSCRIPT"; +<script type="text/javascript"> +// <![CDATA[ +function reorderLTITools(form,item) { + var changedVal; +$jstext + var newpos = 'ltitools_add_pos'; + var maxh = 1 + $total; + var current = new Array; + var newitemVal = form.elements[newpos].options[form.elements[newpos].selectedIndex].value; + if (item == newpos) { + changedVal = newitemVal; + } else { + changedVal = form.elements[item].options[form.elements[item].selectedIndex].value; + current[newitemVal] = newpos; + } + for (var i=0; i<ltitools.length; i++) { + var elementName = 'ltitools_'+ltitools[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; +} + +// ]]> +</script> + +$togglejs + +ENDSCRIPT +} + +sub ltitools_toggle_js { + return <<"ENDSCRIPT"; +<script type="text/javascript"> +// <![CDATA[ + +function toggleLTITools(form,setting,item) { + var radioname = ''; + var divid = ''; + if ((setting == 'passback') || (setting == 'roster')) { + radioname = 'ltitools_'+setting+'_'+item; + divid = 'ltitools_'+setting+'time_'+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') { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'inline-block'; + } + setvis = 1; + } + break; + } + } + } + if (!setvis) { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'none'; + } + } + } + if (setting == 'user') { + divid = 'ltitools_'+setting+'_div_'+item; + var checkid = 'ltitools_'+setting+'_field_'+item; + if (document.getElementById(divid)) { + if (document.getElementById(checkid)) { + if (document.getElementById(checkid).checked) { + document.getElementById(divid).style.display = 'inline-block'; + } else { + document.getElementById(divid).style.display = 'none'; + } + } + } + } + return; +} +// ]]> +</script> + +ENDSCRIPT +} + sub wafproxy_javascript { my ($dom) = @_; return <<"ENDSCRIPT"; @@ -2971,6 +3332,519 @@ function toggleWAF() { 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("','",@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 ($dom,$settings) = @_; + my $togglejs = <i_toggle_js($dom); + unless (ref($settings) eq 'HASH') { + return $togglejs; + } + my (%ordered,$total,%jstext); + $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; + } + } + my @jsarray = (); + foreach my $item (sort {$a <=> $b } (keys(%ordered))) { + push(@jsarray,$ordered{$item}); + } + my $jstext = ' var lti = Array('."'".join("','",@jsarray)."'".');'."\n"; + my $linkprot_js = &Apache::courseprefs::linkprot_javascript(); + return <<"ENDSCRIPT"; +<script type="text/javascript"> +// <![CDATA[ +function reorderLTI(form,item) { + var changedVal; +$jstext + var newpos = 'lti_pos_add'; + var maxh = 1 + $total; + var current = new Array; + var newitemVal = form.elements[newpos].options[form.elements[newpos].selectedIndex].value; + if (item == newpos) { + changedVal = newitemVal; + } else { + changedVal = form.elements[item].options[form.elements[item].selectedIndex].value; + current[newitemVal] = newpos; + } + for (var i=0; i<lti.length; i++) { + var elementName = 'lti_pos_'+lti[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; +} + +$linkprot_js + +// ]]> +</script> + +$togglejs + +ENDSCRIPT +} + +sub lti_toggle_js { + my ($dom) = @_; + my %lcauthparmtext = &Apache::lonlocal::texthash ( + 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); + my %servers = &Apache::lonnet::get_servers($dom,'library'); + my $primary = &Apache::lonnet::domain($dom,'primary'); + my $course_servers = "'".join("','",keys(%servers))."'"; + + return <<"ENDSCRIPT"; +<script type="text/javascript"> +// <![CDATA[ + +function toggleLTI(form,setting,item) { + if ((setting == 'requser') || (setting == 'crsinc')) { + var usrfieldsets = document.getElementsByClassName('ltioption_usr_'+item); + var setvis = ''; + var radioname = 'lti_requser_'+item; + var num = form.elements[radioname].length; + if (num) { + for (var i=0; i<num; i++) { + if (form.elements[radioname][i].checked) { + if (form.elements[radioname][i].value == '1') { + setvis = 1; + break; + } + } + } + } + if (usrfieldsets.length) { + for (var j=0; j<usrfieldsets.length; j++) { + if (setvis) { + usrfieldsets[j].style.display = 'block'; + } else { + usrfieldsets[j].style.display = 'none'; + } + } + } + var crsfieldsets = document.getElementsByClassName('ltioption_crs_'+item); + if (crsfieldsets.length) { + radioname = 'lti_crsinc_'+item; + var num = form.elements[radioname].length; + if (num) { + var crsvis = ''; + for (var i=0; i<num; i++) { + if (form.elements[radioname][i].checked) { + if (form.elements[radioname][i].value == '1') { + if (setvis == '') { + if (setting == 'crsinc'){ + alert("$crsincalert"); + form.elements[radioname][0].checked = true; + } + } else { + crsvis = 1; + } + break; + } + } + } + setvis = crsvis; + } + for (var j=0; j<crsfieldsets.length; j++) { + if (setvis) { + crsfieldsets[j].style.display = 'block'; + } else { + crsfieldsets[j].style.display = 'none'; + } + } + } + } else if ((setting == 'user') || (setting == 'crs') || (setting == 'passback') || (setting == 'callback')) { + var radioname = ''; + var divid = ''; + if (setting == 'user') { + radioname = 'lti_mapuser_'+item; + divid = 'lti_userfield_'+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; + } + var num = form.elements[radioname].length; + if (num) { + var setvis = ''; + for (var i=0; i<num; i++) { + if (form.elements[radioname][i].checked) { + if ((setting == 'passback') || (setting == 'callback')) { + if (form.elements[radioname][i].value == '1') { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'inline-block'; + } + setvis = 1; + break; + } + } else { + if (form.elements[radioname][i].value == 'other') { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'inline-block'; + } + setvis = 1; + break; + } + } + } + } + if (!setvis) { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'none'; + } + } + } + } else if ((setting == 'sec') || (setting == 'secsrc')) { + var numsec = form.elements['lti_crssec_'+item].length; + if (numsec) { + var setvis = ''; + for (var i=0; i<numsec; i++) { + if (form.elements['lti_crssec_'+item][i].checked) { + if (form.elements['lti_crssec_'+item][i].value == '1') { + if (document.getElementById('lti_crssecfield_'+item)) { + document.getElementById('lti_crssecfield_'+item).style.display = 'inline-block'; + setvis = 1; + var numsrcsec = form.elements['lti_crssecsrc_'+item].length; + if (numsrcsec) { + var setsrcvis = ''; + for (var j=0; j<numsrcsec; j++) { + if (form.elements['lti_crssecsrc_'+item][j].checked) { + if (form.elements['lti_crssecsrc_'+item][j].value == 'other') { + if (document.getElementById('lti_secsrcfield_'+item)) { + document.getElementById('lti_secsrcfield_'+item).style.display = 'inline-block'; + setsrcvis = 1; + } + } + } + } + if (!setsrcvis) { + if (document.getElementById('lti_secsrcfield_'+item)) { + document.getElementById('lti_secsrcfield_'+item).style.display = 'none'; + } + } + } + } + } + } + } + if (!setvis) { + if (document.getElementById('lti_crssecfield_'+item)) { + document.getElementById('lti_crssecfield_'+item).style.display = 'none'; + } + if (document.getElementById('lti_secsrcfield_'+item)) { + document.getElementById('lti_secsrcfield_'+item).style.display = 'none'; + } + } + } + } else if (setting == 'lcauth') { + var numauth = form.elements['lti_lcauth_'+item].length; + if (numauth) { + for (var i=0; i<numauth; i++) { + if (form.elements['lti_lcauth_'+item][i].checked) { + if (document.getElementById('lti_'+setting+'_parmrow_'+item)) { + if ((form.elements['lti_'+setting+'_'+item][i].value == 'internal') || (form.elements['lti_'+setting+'_'+item][i].value == 'lti')) { + document.getElementById('lti_'+setting+'_parmrow_'+item).style.display = 'none'; + } else { + document.getElementById('lti_'+setting+'_parmrow_'+item).style.display = 'table-row'; + if (document.getElementById('lti_'+setting+'_parmtext_'+item)) { + if (form.elements['lti_'+setting+'_'+item][i].value == 'localauth') { + document.getElementById('lti_'+setting+'_parmtext_'+item).innerHTML = "$lcauthparmtext{'localauth'}"; + } else { + document.getElementById('lti_'+setting+'_parmtext_'+item).innerHTML = "$lcauthparmtext{'krb'}"; + } + } + } + } + } + } + } + } else if (setting == 'lcmenu') { + var menus = new Array('lti_topmenu_'+item,'lti_inlinemenu_'+item); + var divid = 'lti_menufield_'+item; + var setvis = ''; + for (var i=0; i<menus.length; i++) { + var radioname = menus[i]; + var num = form.elements[radioname].length; + if (num) { + for (var j=0; j<num; j++) { + if (form.elements[radioname][j].checked) { + if (form.elements[radioname][j].value == '1') { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'inline-block'; + } + setvis = 1; + break; + } + } + } + } + if (setvis == 1) { + break; + } + } + if (!setvis) { + if (document.getElementById(divid)) { + document.getElementById(divid).style.display = 'none'; + } + } + } + return; +} + +function toggleLTIEncKey(form) { + var shownhosts = new Array(); + var hiddenhosts = new Array(); + var forcourse = new Array($course_servers); + var fromdomain = '$primary'; + var crsradio = form.elements['ltisec_crslinkprot']; + if (crsradio.length) { + for (var i=0; i<crsradio.length; i++) { + if (crsradio[i].checked) { + if (crsradio[i].value == 1) { + if (forcourse.length > 0) { + for (var j=0; j<forcourse.length; j++) { + if (!shownhosts.includes(forcourse[j])) { + shownhosts.push(forcourse[j]); + } + } + } + } else { + if (forcourse.length > 0) { + for (var j=0; j<forcourse.length; j++) { + if (!hiddenhosts.includes(forcourse[j])) { + hiddenhosts.push(forcourse[j]); + } + } + } + } + } + } + } + var domradio = form.elements['ltisec_domlinkprot']; + if (domradio.length) { + for (var i=0; i<domradio.length; i++) { + if (domradio[i].checked) { + if (domradio[i].value == 1) { + if (!shownhosts.includes(fromdomain)) { + shownhosts.push(fromdomain); + } + } else { + if (!hiddenhosts.includes(fromdomain)) { + hiddenhosts.push(fromdomain); + } + } + } + } + } + var consumersradio = form.elements['ltisec_consumers']; + if (consumersradio.length) { + for (var i=0; i<consumersradio.length; i++) { + if (consumersradio[i].checked) { + if (consumersradio[i].value == 1) { + if (!shownhosts.includes(fromdomain)) { + shownhosts.push(fromdomain); + } + } else { + if (!hiddenhosts.includes(fromdomain)) { + hiddenhosts.push(fromdomain); + } + } + } + } + } + if (shownhosts.length > 0) { + for (var i=0; i<shownhosts.length; i++) { + if (document.getElementById('ltisec_info_'+shownhosts[i])) { + document.getElementById('ltisec_info_'+shownhosts[i]).style.display = 'block'; + } + } + if (document.getElementById('ltisec_noprivkey')) { + document.getElementById('ltisec_noprivkey').style.display = 'none'; + } + } else { + if (document.getElementById('ltisec_noprivkey')) { + document.getElementById('ltisec_noprivkey').style.display = 'inline-block'; + } + } + if (hiddenhosts.length > 0) { + for (var i=0; i<hiddenhosts.length; i++) { + if (!shownhosts.includes(hiddenhosts[i])) { + if (document.getElementById('ltisec_info_'+hiddenhosts[i])) { + document.getElementById('ltisec_info_'+hiddenhosts[i]).style.display = 'none'; + } + } + } + } + return; +} + +function togglePrivKey(form,hostid) { + var radioname = ''; + var currdivid = ''; + var newdivid = ''; + if ((document.getElementById('ltisec_divcurrprivkey_'+hostid)) && + (document.getElementById('ltisec_divchgprivkey_'+hostid))) { + currdivid = document.getElementById('ltisec_divcurrprivkey_'+hostid); + newdivid = document.getElementById('ltisec_divchgprivkey_'+hostid); + radioname = form.elements['ltisec_changeprivkey_'+hostid]; + if (radioname) { + if (radioname.length > 0) { + var setvis; + for (var i=0; i<radioname.length; i++) { + if (radioname[i].checked == true) { + if (radioname[i].value == 1) { + newdivid.style.display = 'inline-block'; + currdivid.style.display = 'none'; + setvis = 1; + } + break; + } + } + if (!setvis) { + newdivid.style.display = 'none'; + currdivid.style.display = 'inline-block'; + } + } + } + } +} + +// ]]> +</script> + +ENDSCRIPT +} + sub autoupdate_javascript { return <<"ENDSCRIPT"; <script type="text/javascript"> @@ -3045,16 +3919,16 @@ sub saml_javascript { return <<"ENDSCRIPT"; <script type="text/javascript"> // <![CDATA[ -function toggleSamlOptions(form,hostid) { +function toggleSamlOptions(form,hostid) { var radioname = 'saml_'+hostid; var tablecellon = 'samloptionson_'+hostid; var tablecelloff = 'samloptionsoff_'+hostid; var num = form.elements[radioname].length; if (num) { - var setvis = ''; + var setvis = ''; for (var i=0; i<num; i++) { if (form.elements[radioname][i].checked) { - if (form.elements[radioname][i].value == '1') { + if (form.elements[radioname][i].value == '1') { if (document.getElementById(tablecellon)) { document.getElementById(tablecellon).style.display=''; } @@ -3281,8 +4155,8 @@ sub print_autoupdate { $updateoff = ' '; } } - $enable = '<tr class="LC_odd_row">'. - '<td>'.&mt($choices{'run'}).'</td>'. + $enable = '<tr class="LC_odd_row">'. + '<td>'.$choices{'run'}.'</td>'. '<td class="LC_left_item"><span class="LC_nobreak"><label>'. '<input type="radio" name="autoupdate_run"'. $updateoff.'value="0" />'.&mt('No').'</label> '. @@ -3822,7 +4696,7 @@ sub print_contacts { map {$excluded{$_} = 1; } @{$lonstatus{'excluded'}}; } } - foreach my $item ('errorthreshold','errorsysmail') { + foreach my $item ('errorthreshold','errorsysmail') { $css_class = $rownum%2?' class="LC_odd_row"':''; $datatable .= '<tr'.$css_class.'>'. '<td class="LC_left_item"><span class="LC_nobreak">'. @@ -3904,7 +4778,7 @@ sub print_contacts { $includeloc{'override_'.$key} = ''; $includestr{'override_'.$key} = ''; if ($settings->{'overrides'}{$key}{'include'} ne '') { - ($includeloc{'override_'.$key},$includestr{'override_'.$key}) = + ($includeloc{'override_'.$key},$includestr{'override_'.$key}) = split(/:/,$settings->{'overrides'}{$key}{'include'},2); $includestr{'override_'.$key} = &unescape($includestr{'override_'.$key}); } @@ -3916,7 +4790,7 @@ sub print_contacts { my $optionsprefix = 'LC_options_helpdesk_'; my $onclicktypes = "toggleHelpdeskRow(this.form,'overrides','$customclass','$optionsprefix');"; - + $datatable .= &insttypes_row($settings,$types,$usertypes,$dom, $numinrow,$othertitle,'overrides', \$rownum,$onclicktypes,$customclass); @@ -3983,7 +4857,7 @@ sub overridden_helpdesk { } my $title; if (ref($short_titles) eq 'HASH') { - $title = $short_titles->{$item}; + $title = $short_titles->{$item}; } $output .= '<label>'. '<input type="checkbox" name="override_'.$type.'"'.$check. @@ -4062,6 +4936,7 @@ function toggleHelpdeskRow(form,checkbox return; } + // ]]> </script> @@ -4133,7 +5008,6 @@ sub print_helpsettings { push(@jsarray,('notinc','notexc')); } my $hiddenstr = join("','",@jsarray); - $datatable .= &helpsettings_javascript(\@roles_by_num,$maxnum,$hiddenstr,$formname); my $context = 'domprefs'; my $crstype = 'Course'; my $prefix = 'helproles_'; @@ -4167,7 +5041,7 @@ sub print_helpsettings { my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); $css_class = $itemcount%2?' class="LC_odd_row"':''; my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$num."_pos'".');"'; - $datatable .= '<tr '.$css_class.'><td valign="top"><b>'.$role.'</b><br />'. + $datatable .= '<tr '.$css_class.'><td style="vertical-align: top"><b>'.$role.'</b><br />'. '<select name="helproles_'.$num.'_pos"'.$chgstr.'>'; for (my $k=0; $k<=$maxnum; $k++) { my $vpos = $k+1; @@ -4206,7 +5080,7 @@ sub print_helpsettings { &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent); my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$count."_pos'".');"'; - $datatable .= '<tr '.$css_class.'><td valign="top"><span class="LC_nobreak"><label>'. + $datatable .= '<tr '.$css_class.'><td style="vertical-align: top"><span class="LC_nobreak"><label>'. '<input type="hidden" name="helproles_maxnum" value="'.$maxnum.'" />'."\n". '<select name="helproles_'.$count.'_pos"'.$chgstr.'>'; for (my $k=0; $k<$maxnum+1; $k++) { @@ -4487,7 +5361,7 @@ sub radiobutton_prefs { foreach my $item (@{$toggles}) { $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= - '<tr'.$css_class.'><td valign="top">'. + '<tr'.$css_class.'><td style="vertical-align: top">'. '<span class="LC_nobreak">'.$choices->{$item}. '</span></td>'; if ($align eq 'left') { @@ -4504,10 +5378,10 @@ sub radiobutton_prefs { $checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes').'</label>'; } else { $datatable .= - '<label><input type="radio" name="'. - $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes'). - '</label> <label><input type="radio" name="'.$item.'" '. - $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>'; + '<label><input type="radio" name="'. + $item.'" '.$checkedon{$item}.' value="1"'.$onclick.' />'.&mt('Yes'). + '</label> <label><input type="radio" name="'.$item.'" '. + $checkedoff{$item}.' value="0"'.$onclick.' />'.&mt('No').'</label>'; } $datatable .= '</span>'.$additional.'</td></tr>'; $itemcount ++; @@ -4515,11 +5389,1803 @@ sub radiobutton_prefs { return ($datatable,$itemcount); } +sub print_ltitools { + my ($dom,$settings,$rowtotal) = @_; + my $rownum = 0; + my $css_class; + my $itemcount = 1; + my $maxnum = 0; + my %ordered; + if (ref($settings) eq 'HASH') { + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + } + my $confname = $dom.'-domainconfig'; + my $switchserver = &check_switchserver($dom,$confname); + my $maxnum = scalar(keys(%ordered)); + my $datatable; + my %lt = <itools_names(); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); + my @fields = ('fullname','firstname','lastname','email','roles','user'); + 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 ($title,$key,$secret,$url,$lifetime,$imgsrc,%sigsel); + if (ref($settings->{$item}) eq 'HASH') { + $title = $settings->{$item}->{'title'}; + $url = $settings->{$item}->{'url'}; + $key = $settings->{$item}->{'key'}; + $secret = $settings->{$item}->{'secret'}; + $lifetime = $settings->{$item}->{'lifetime'}; + my $image = $settings->{$item}->{'image'}; + if ($image ne '') { + $imgsrc = '<img src="'.$image.'" alt="'.&mt('Tool Provider icon').'" />'; + } + if ($settings->{$item}->{'sigmethod'} eq 'HMAC-256') { + $sigsel{'HMAC-256'} = ' selected="selected"'; + } else { + $sigsel{'HMAC-SHA1'} = ' selected="selected"'; + } + } + my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_".$item."'".');"'; + $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">' + .'<select name="ltitools_'.$item.'"'.$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>'; + } + $datatable .= '</select>'.(' 'x2). + '<label><input type="checkbox" name="ltitools_del" value="'.$item.'" />'. + &mt('Delete?').'</label></span></td>'. + '<td colspan="2">'. + '<fieldset><legend>'.&mt('Required settings').'</legend>'. + '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="20" name="ltitools_title_'.$i.'" value="'.$title.'" /></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_version_'.$i.'">'. + '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_msgtype_'.$i.'">'. + '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'sigmethod'}.':<select name="ltitools_sigmethod_'.$i.'">'. + '<option value="HMAC-SHA1"'.$sigsel{'HMAC-SHA1'}.'>HMAC-SHA1</option>'. + '<option value="HMAC-SHA256"'.$sigsel{'HMAC-SHA256'}.'>HMAC-SHA256</option></select></span>'. + '<br /><br />'. + '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="ltitools_url_'.$i.'"'. + ' value="'.$url.'" /></span>'. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'key'}.':'. + '<input type="text" size="25" name="ltitools_key_'.$i.'" value="'.$key.'" /></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'lifetime'}.':'. + '<input type="text" size="5" name="ltitools_lifetime_'.$i.'" value="'.$lifetime.'" /></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'secret'}.':'. + '<input type="password" size="20" name="ltitools_secret_'.$i.'" value="'.$secret.'" />'. + '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_secret_'.$i.'.type='."'text'".' } else { this.form.ltitools_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'. + '<input type="hidden" name="ltitools_id_'.$i.'" value="'.$item.'" /></span>'. + '</fieldset>'. + '<fieldset><legend>'.&mt('Optional settings').'</legend>'. + '<span class="LC_nobreak">'.&mt('Display target:'); + my %currdisp; + if (ref($settings->{$item}->{'display'}) eq 'HASH') { + if ($settings->{$item}->{'display'}->{'target'} eq 'window') { + $currdisp{'window'} = ' checked="checked"'; + } elsif ($settings->{$item}->{'display'}->{'target'} eq 'tab') { + $currdisp{'tab'} = ' checked="checked"'; + } else { + $currdisp{'iframe'} = ' checked="checked"'; + } + if ($settings->{$item}->{'display'}->{'width'} =~ /^(\d+)$/) { + $currdisp{'width'} = $1; + } + if ($settings->{$item}->{'display'}->{'height'} =~ /^(\d+)$/) { + $currdisp{'height'} = $1; + } + $currdisp{'linktext'} = $settings->{$item}->{'display'}->{'linktext'}; + $currdisp{'explanation'} = $settings->{$item}->{'display'}->{'explanation'}; + } else { + $currdisp{'iframe'} = ' checked="checked"'; + } + foreach my $disp ('iframe','tab','window') { + $datatable .= '<label><input type="radio" name="ltitools_target_'.$i.'" value="'.$disp.'"'.$currdisp{$disp}.' />'. + $lt{$disp}.'</label>'.(' 'x2); + } + $datatable .= (' 'x4); + foreach my $dimen ('width','height') { + $datatable .= '<label>'.$lt{$dimen}.' '. + '<input type="text" name="ltitools_'.$dimen.'_'.$i.'" size="5" value="'.$currdisp{$dimen}.'" /></label>'. + (' 'x2); + } + $datatable .= '</span><br />'. + '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'. + '<input type="text" name="ltitools_linktext_'.$i.'" size="25" value="'.$currdisp{'linktext'}.'" /></div>'. + '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'. + '<textarea name="ltitools_explanation_'.$i.'" rows="5" cols="40">'.$currdisp{'explanation'}. + '</textarea></div><div style=""></div><br />'; + my %units = ( + 'passback' => 'days', + 'roster' => 'seconds', + ); + foreach my $extra ('passback','roster') { + my $validsty = 'none'; + my $currvalid; + my $checkedon = ''; + my $checkedoff = ' checked="checked"'; + if ($settings->{$item}->{$extra}) { + $checkedon = $checkedoff; + $checkedoff = ''; + $validsty = 'inline-block'; + if ($settings->{$item}->{$extra.'valid'} =~ /^\d+\.?\d*$/) { + $currvalid = $settings->{$item}->{$extra.'valid'}; + } + } + my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','$i'".');"'; + $datatable .= '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{$extra}.' '. + '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="0"'.$checkedoff.$onclick.' />'. + &mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="ltitools_'.$extra.'_'.$i.'" value="1"'.$checkedon.$onclick.' />'. + &mt('Yes').'</label></span></div>'. + '<div class="LC_floatleft" style="display:'.$validsty.';" id="ltitools_'.$extra.'time_'.$i.'">'. + '<span class="LC_nobreak">'. + &mt("at least [_1] $units{$extra} after launch", + '<input type="text" name="ltitools_'.$extra.'valid_'.$i.'" value="'.$currvalid.'" />'). + '</span></div><div style="padding:0;clear:both;margin:0;border:0"></div>'; + } + $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.': '; + if ($imgsrc) { + $datatable .= $imgsrc. + '<label><input type="checkbox" name="ltitools_image_del"'. + ' value="'.$item.'" />'.&mt('Delete?').'</label></span> '. + '<span class="LC_nobreak"> '.&mt('Replace:').' '; + } else { + $datatable .= '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; + } + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= '<input type="file" name="ltitools_image_'.$i.'" value="" />'; + } + $datatable .= '</span></fieldset>'; + my (%checkedfields,%rolemaps,$userincdom); + if (ref($settings->{$item}) eq 'HASH') { + if (ref($settings->{$item}->{'fields'}) eq 'HASH') { + %checkedfields = %{$settings->{$item}->{'fields'}}; + } + $userincdom = $settings->{$item}->{'incdom'}; + if (ref($settings->{$item}->{'roles'}) eq 'HASH') { + %rolemaps = %{$settings->{$item}->{'roles'}}; + $checkedfields{'roles'} = 1; + } + } + $datatable .= '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'. + '<span class="LC_nobreak">'; + my $userfieldstyle = 'display:none;'; + my $seluserdom = ''; + my $unseluserdom = ' selected="selected"'; + foreach my $field (@fields) { + my ($checked,$onclick,$id,$spacer); + if ($checkedfields{$field}) { + $checked = ' checked="checked"'; + } + if ($field eq 'user') { + $id = ' id="ltitools_user_field_'.$i.'"'; + $onclick = ' onclick="toggleLTITools(this.form,'."'$field','$i'".')"'; + if ($checked) { + $userfieldstyle = 'display:inline-block'; + if ($userincdom) { + $seluserdom = $unseluserdom; + $unseluserdom = ''; + } + } + } else { + $spacer = (' ' x2); + } + $datatable .= '<label>'. + '<input type="checkbox" name="ltitools_fields_'.$i.'" value="'.$field.'"'.$id.$checked.$onclick.' />'. + $lt{$field}.'</label>'.$spacer; + } + $datatable .= '</span>'; + $datatable .= '<div style="'.$userfieldstyle.'" id="ltitools_user_div_'.$i.'">'. + '<span class="LC_nobreak"> : '. + '<select name="ltitools_userincdom_'.$i.'">'. + '<option value="">'.&mt('Select').'</option>'. + '<option value="0"'.$unseluserdom.'>'.&mt('username').'</option>'. + '<option value="1"'.$seluserdom.'>'.&mt('username:domain').'</option>'. + '</select></span></div>'; + $datatable .= '</fieldset>'. + '<fieldset><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="ltitools_roles_'.$role.'_'.$i.'">'. + '<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>'; + my %courseconfig; + if (ref($settings->{$item}) eq 'HASH') { + if (ref($settings->{$item}->{'crsconf'}) eq 'HASH') { + %courseconfig = %{$settings->{$item}->{'crsconf'}}; + } + } + $datatable .= '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">'; + foreach my $item ('label','title','target','linktext','explanation','append') { + my $checked; + if ($courseconfig{$item}) { + $checked = ' checked="checked"'; + } + $datatable .= '<label>'. + '<input type="checkbox" name="ltitools_courseconfig_'.$i.'" value="'.$item.'"'.$checked.' />'. + $lt{'crs'.$item}.'</label> '."\n"; + } + $datatable .= '</span></fieldset>'. + '<fieldset><legend>'.&mt('Custom items sent on launch').'</legend>'. + '<table><tr><th>'.&mt('Action').'</th><th>'.&mt('Name').'</th><th>'.&mt('Value').'</th></tr>'; + if (ref($settings->{$item}->{'custom'}) eq 'HASH') { + my %custom = %{$settings->{$item}->{'custom'}}; + if (keys(%custom) > 0) { + foreach my $key (sort(keys(%custom))) { + $datatable .= '<tr><td><span class="LC_nobreak">'. + '<label><input type="checkbox" name="ltitools_customdel_'.$i.'" value="'. + $key.'" />'.&mt('Delete').'</label></span></td><td>'.$key.'</td>'. + '<td><input type="text" name="ltitools_customval_'.$key.'_'.$i.'"'. + ' value="'.$custom{$key}.'" /></td></tr>'; + } + } + } + $datatable .= '<tr><td><span class="LC_nobreak">'. + '<label><input type="checkbox" name="ltitools_customadd" value="'.$i.'" />'. + &mt('Add').'</label></span></td><td><input type="text" name="ltitools_custom_name_'.$i.'" />'. + '</td><td><input type="text" name="ltitools_custom_value_'.$i.'" /></td></tr>'; + $datatable .= '</table></fieldset></td></tr>'."\n"; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_add_pos'".');"'; + $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n". + '<input type="hidden" name="ltitools_maxnum" value="'.$maxnum.'" />'."\n". + '<select name="ltitools_add_pos"'.$chgstr.'>'; + for (my $k=0; $k<$maxnum+1; $k++) { + my $vpos = $k+1; + my $selstr; + if ($k == $maxnum) { + $selstr = ' selected="selected" '; + } + $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>'; + } + $datatable .= '</select> '."\n". + '<input type="checkbox" name="ltitools_add" value="1" />'.&mt('Add').'</span></td>'."\n". + '<td colspan="2">'. + '<fieldset><legend>'.&mt('Required settings').'</legend>'. + '<span class="LC_nobreak">'.$lt{'title'}.':<input type="text" size="20" name="ltitools_add_title" value="" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'version'}.':<select name="ltitools_add_version">'. + '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'msgtype'}.':<select name="ltitools_add_msgtype">'. + '<option value="basic-lti-launch-request" selected="selected">Launch</option></select></span> '. + '<span class="LC_nobreak">'.$lt{'sigmethod'}.':<select name="ltitools_add_sigmethod">'. + '<option value="HMAC-SHA1" selected="selected">HMAC-SHA1</option>'. + '<option value="HMAC-SHA256">HMAC-SHA256</option></select></span>'. + '<br />'. + '<span class="LC_nobreak">'.$lt{'url'}.':<input type="text" size="40" name="ltitools_add_url" value="" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="ltitools_add_key" value="" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="5" name="ltitools_add_lifetime" value="300" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="ltitools_add_secret" value="" />'. + '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltitools_add_secret.type='."'text'".' } else { this.form.ltitools_add_secret.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n". + '</fieldset>'. + '<fieldset><legend>'.&mt('Optional settings').'</legend>'. + '<span class="LC_nobreak">'.&mt('Display target:'); + my %defaultdisp; + $defaultdisp{'iframe'} = ' checked="checked"'; + foreach my $disp ('iframe','tab','window') { + $datatable .= '<label><input type="radio" name="ltitools_add_target" value="'.$disp.'"'.$defaultdisp{$disp}.' />'. + $lt{$disp}.'</label>'.(' 'x2); + } + $datatable .= (' 'x4); + foreach my $dimen ('width','height') { + $datatable .= '<label>'.$lt{$dimen}.' '. + '<input type="text" name="ltitools_add_'.$dimen.'" size="5" /></label>'. + (' 'x2); + } + $datatable .= '</span><br />'. + '<div class="LC_left_float">'.$lt{'linktext'}.'<br />'. + '<input type="text" name="ltitools_add_linktext" size="5" /></div>'. + '<div class="LC_left_float">'.$lt{'explanation'}.'<br />'. + '<textarea name="ltitools_add_explanation" rows="5" cols="40"></textarea>'. + '</div><div style=""></div><br />'; + my %units = ( + 'passback' => 'days', + 'roster' => 'seconds', + ); + my %defaulttimes = ( + 'passback' => '7', + 'roster' => '300', + ); + foreach my $extra ('passback','roster') { + my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','add'".');"'; + $datatable .= '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{$extra}.' '. + '<label><input type="radio" name="ltitools_'.$extra.'_add" value="0" checked="checked"'.$onclick.' />'. + &mt('No').'</label></span>'.(' 'x2).'<span class="LC_nobreak">'. + '<label><input type="radio" name="ltitools_'.$extra.'_add" value="1"'.$onclick.' />'. + &mt('Yes').'</label></span></div>'. + '<div class="LC_floatleft" style="display:none;" id="ltitools_'.$extra.'time_add">'. + '<span class="LC_nobreak">'. + &mt("at least [_1] $units{$extra} after launch", + '<input type="text" name="ltitools_'.$extra.'valid_add" value="'.$defaulttimes{$extra}.'" />'). + '</span></div><div style="padding:0;clear:both;margin:0;border:0"></div>'; + } + $datatable .= '<span class="LC_nobreak">'.$lt{'icon'}.': '. + '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= '<input type="file" name="ltitools_add_image" value="" />'; + } + $datatable .= '</span></fieldset>'. + '<fieldset><legend>'.&mt('User data sent on launch').'</legend>'. + '<span class="LC_nobreak">'; + foreach my $field (@fields) { + my ($id,$onclick,$spacer); + if ($field eq 'user') { + $id = ' id="ltitools_user_field_add"'; + $onclick = ' onclick="toggleLTITools(this.form,'."'$field','add'".')"'; + } else { + $spacer = (' ' x2); + } + $datatable .= '<label>'. + '<input type="checkbox" name="ltitools_add_fields" value="'.$field.'"'.$id.$onclick.' />'. + $lt{$field}.'</label>'.$spacer; + } + $datatable .= '</span>'. + '<div style="display:none;" id="ltitools_user_div_add">'. + '<span class="LC_nobreak"> : '. + '<select name="ltitools_userincdom_add">'. + '<option value="" selected="selected">'.&mt('Select').'</option>'. + '<option value="0">'.&mt('username').'</option>'. + '<option value="1">'.&mt('username:domain').'</option>'. + '</select></span></div></fieldset>'; + $datatable .= '<fieldset><legend>'.&mt('Role mapping').'</legend><table><tr>'; + foreach my $role (@courseroles) { + my ($checked,$checkednone); + $datatable .= '<td style="text-align: center">'. + &Apache::lonnet::plaintext($role,'Course').'<br />'. + '<select name="ltitools_add_roles_'.$role.'">'. + '<option value="" selected="selected">'.&mt('Select').'</option>'; + foreach my $ltirole (@ltiroles) { + $datatable .= '<option value="'.$ltirole.'">'.$ltirole.'</option>'; + } + $datatable .= '</select></td>'; + } + $datatable .= '</tr></table></fieldset>'. + '<fieldset><legend>'.&mt('Configurable in course').'</legend><span class="LC_nobreak">'; + foreach my $item ('label','title','target','linktext','explanation','append') { + $datatable .= '<label>'. + '<input type="checkbox" name="ltitools_courseconfig" value="'.$item.'" checked="checked" />'. + $lt{'crs'.$item}.'</label>'.(' ' x2)."\n"; + } + $datatable .= '</span></fieldset>'. + '<fieldset><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><span class="LC_nobreak">'. + '<label><input type="checkbox" name="ltitools_add_custom" value="1" />'. + &mt('Add').'</label></span></td><td><input type="text" name="ltitools_add_custom_name" />'. + '</td><td><input type="text" name="ltitools_add_custom_value" /></td></tr>'. + '</table></fieldset>'."\n". + '</td>'."\n". + '</tr>'."\n"; + $itemcount ++; + return $datatable; +} + +sub ltitools_names { + my %lt = &Apache::lonlocal::texthash( + 'title' => 'Title', + 'version' => 'Version', + 'msgtype' => 'Message Type', + 'sigmethod' => 'Signature Method', + 'url' => 'URL', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'secret' => 'Secret', + 'icon' => 'Icon', + 'user' => 'User', + 'fullname' => 'Full Name', + 'firstname' => 'First Name', + 'lastname' => 'Last Name', + 'email' => 'E-mail', + 'roles' => 'Role', + 'window' => 'Window', + 'tab' => 'Tab', + 'iframe' => 'iFrame', + 'height' => 'Height', + 'width' => 'Width', + 'linktext' => 'Default Link Text', + 'explanation' => 'Default Explanation', + 'passback' => 'Tool can return grades:', + 'roster' => 'Tool can retrieve roster:', + 'crstarget' => 'Display target', + 'crslabel' => 'Course label', + 'crstitle' => 'Course title', + 'crslinktext' => 'Link Text', + 'crsexplanation' => 'Explanation', + 'crsappend' => 'Provider URL', + ); + 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,@courseroles,@ltiroles); + my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data(); + if (ref($requref) eq 'HASH') { + %requserfields = %{$requref}; + } + if (ref($opturef) eq 'HASH') { + %optuserfields = %{$opturef}; + } + if (ref($defref) eq 'HASH') { + %defaults = %{$defref}; + } + if (ref($extref) eq 'HASH') { + %extended = %{$extref}; + } + if (ref($crsref) eq 'HASH') { + %crsconf = %{$crsref}; + } + if (ref($rolesref) eq 'ARRAY') { + @courseroles = @{$rolesref}; + } + if (ref($ltiref) eq 'ARRAY') { + @ltiroles = @{$ltiref}; + } + 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,@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 ($position,$dom,$settings,$rowtotal) = @_; + my $itemcount = 1; + my ($datatable,$css_class); + my (%rules,%encrypt,%privkeys,%linkprot); + if (ref($settings) eq 'HASH') { + if ($position eq 'top') { + if (exists($settings->{'encrypt'})) { + if (ref($settings->{'encrypt'}) eq 'HASH') { + foreach my $key (keys(%{$settings->{'encrypt'}})) { + if ($key eq 'consumers') { + $encrypt{'ltisec_'.$key} = $settings->{'encrypt'}{$key}; + } else { + $encrypt{'ltisec_'.$key.'linkprot'} = $settings->{'encrypt'}{$key}; + } + } + } + } + if (exists($settings->{'private'})) { + if (ref($settings->{'private'}) eq 'HASH') { + if (ref($settings->{'private'}) eq 'HASH') { + if (ref($settings->{'private'}{'keys'}) eq 'ARRAY') { + map { $privkeys{$_} = 1; } (@{$settings->{'private'}{'keys'}}); + } + } + } + } + } elsif ($position eq 'middle') { + if (exists($settings->{'rules'})) { + if (ref($settings->{'rules'}) eq 'HASH') { + %rules = %{$settings->{'rules'}}; + } + } + } elsif ($position eq 'lower') { + if (exists($settings->{'linkprot'})) { + if (ref($settings->{'linkprot'}) eq 'HASH') { + %linkprot = %{$settings->{'linkprot'}}; + if ($linkprot{'lock'}) { + delete($linkprot{'lock'}); + } + } + } + } else { + foreach my $key ('encrypt','private','rules','linkprot') { + if (exists($settings->{$key})) { + delete($settings->{$key}); + } + } + } + } + if ($position eq 'top') { + my @ids=&Apache::lonnet::current_machine_ids(); + my %servers = &Apache::lonnet::get_servers($dom,'library'); + my $primary = &Apache::lonnet::domain($dom,'primary'); + my ($extra,$numshown); + foreach my $hostid (sort(keys(%servers))) { + my ($showextra,$divsty,$switch); + if ($hostid eq $primary) { + if (($encrypt{'ltisec_consumers'}) || ($encrypt{'ltisec_domlinkprot'})) { + $showextra = 1; + } + } + if ($encrypt{'ltisec_crslinkprot'}) { + $showextra = 1; + } + unless (grep(/^\Q$hostid\E$/,@ids)) { + $switch = 1; + } + if ($showextra) { + $numshown ++; + $divsty = 'display:inline-block'; + } else { + $divsty = 'display:none'; + } + $extra .= '<fieldset id="ltisec_info_'.$hostid.'" style="'.$divsty.'">'. + '<legend>'.$hostid.'</legend>'; + if ($switch) { + my $switchserver = '<a href="/adm/switchserver?otherserver='.$hostid.'&role='. + &HTML::Entities::encode($env{'request.role'},'\'<>"&'). + '&destinationurl=/adm/domainprefs">'.&mt('Switch Server').'</a>'; + if (exists($privkeys{$hostid})) { + $extra .= '<div id="ltisec_divcurrprivkey_'.$hostid.'" style="display:inline-block" />'. + '<span class="LC_nobreak">'. + &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'. + '<span class="LC_nobreak">'.&mt('Change?'). + '<label><input type="radio" value="0" name="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" checked="checked" />'.&mt('No').'</label>'. + (' 'x2). + '<label><input type="radio" value="1" name="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" />'.&mt('Yes'). + '</label> </span><div id="ltisec_divchgprivkey_'.$hostid.'" style="display:none" />'. + '<span class="LC_nobreak"> - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver). + '</span></div>'; + } else { + $extra .= '<span class="LC_nobreak">'. + &mt('Key required').' - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver). + '</span>'."\n"; + } + } elsif (exists($privkeys{$hostid})) { + $extra .= '<div id="ltisec_divcurrprivkey_'.$hostid.'" style="display:inline-block" /><span class="LC_nobreak">'. + &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'. + '<span class="LC_nobreak">'.&mt('Change?'). + '<label><input type="radio" value="0" name="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" checked="checked" />'.&mt('No').'</label>'. + (' 'x2). + '<label><input type="radio" value="1" name="ltisec_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$hostid'".');" />'.&mt('Yes'). + '</label> </span><div id="ltisec_divchgprivkey_'.$hostid.'" style="display:none" />'. + '<span class="LC_nobreak">'.&mt('New Key').':'. + '<input type="password" size="20" name="ltisec_privkey_'.$hostid.'" value="" autocomplete="new-password" />'. + '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltisec_privkey_'.$hostid.'.type='."'text'".' } else { this.form.ltisec_privkey_'.$hostid.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'. + '</span></div>'; + } else { + $extra .= '<span class="LC_nobreak">'.&mt('Encryption Key').':'. + '<input type="password" size="20" name="ltisec_privkey_'.$hostid.'" value="" autocomplete="new-password" />'. + '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.ltisec_privkey_'.$hostid.'.type='."'text'".' } else { this.form.ltisec_privkey_'.$hostid.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'; + } + $extra .= '</fieldset>'; + } + my %choices = &Apache::lonlocal::texthash ( + ltisec_crslinkprot => 'Encrypt stored link protection secrets defined in courses', + ltisec_domlinkprot => 'Encrypt stored link protection secrets defined in domain', + ltisec_consumers => 'Encrypt stored consumer secrets defined in domain', + ); + my @toggles = qw(ltisec_crslinkprot ltisec_domlinkprot ltisec_consumers); + my %defaultchecked = ( + 'ltisec_crslinkprot' => 'off', + 'ltisec_domlinkprot' => 'off', + 'ltisec_consumers' => 'off', + ); + my ($onclick,$itemcount); + $onclick = 'javascript:toggleLTIEncKey(this.form);'; + ($datatable,$itemcount) = &radiobutton_prefs(\%encrypt,\@toggles,\%defaultchecked, + \%choices,$itemcount,$onclick,'','left','no'); + + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $noprivkeysty = 'display:inline-block'; + if ($numshown) { + $noprivkeysty = 'display:none'; + } + $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.&mt('Encryption Key(s)').'</td>'. + '<td><div id="ltisec_noprivkey" style="'.$noprivkeysty.'" >'. + '<span class="LC_nobreak">'.&mt('Not in use').'</span></div>'. + $extra. + '</td></tr>'; + $itemcount ++; + $$rowtotal += $itemcount; + } elsif ($position eq 'middle') { + $datatable = &password_rules('secrets',\$itemcount,\%rules); + $$rowtotal += $itemcount; + } elsif ($position eq 'lower') { + $datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain'); + } else { + my $maxnum = 0; + 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; + } + } + } + $maxnum = scalar(keys(%ordered)); + my %lt = <i_names(); + 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 ($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'".');"'; + my %checkedrequser = ( + yes => ' checked="checked"', + no => '', + ); + if (!$requser) { + $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 .= '<tr '.$css_class.'><td><span class="LC_nobreak">' + .'<select name="lti_pos_'.$item.'"'.$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>'; + } + $datatable .= '</select>'.(' 'x2). + '<label><input type="checkbox" name="lti_del" value="'.$item.'" />'. + &mt('Delete?').'</label></span></td>'. + '<td colspan="2">'. + '<fieldset><legend>'.&mt('Required settings').'</legend>'. + '<span class="LC_nobreak">'.$lt{'consumer'}. + ':<input type="text" size="15" name="lti_consumer_'.$i.'" value="'.$consumer.'" /></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'version'}.':<select name="lti_version_'.$i.'">'. + '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" name="lti_lifetime_'.$i.'"'. + 'value="'.$lifetime.'" size="3" /></span>'. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'requser'}.':'. + '<label><input type="radio" name="lti_requser_'.$i.'" value="1"'.$onclickrequser.$checkedrequser{yes}.' />'.&mt('Yes').'</label> '."\n". + '<label><input type="radio" name="lti_requser_'.$i.'" value="0"'.$onclickrequser.$checkedrequser{no}.' />'.&mt('No').'</label></span>'."\n". + '<br /><br />'. + '<span class="LC_nobreak">'.$lt{'crsinc'}.':'. + '<label><input type="radio" name="lti_crsinc_'.$i.'" value="1"'.$onclickcrsinc.$checkedcrsinc{yes}.' />'.&mt('Yes').'</label> '."\n". + '<label><input type="radio" name="lti_crsinc_'.$i.'" value="0"'.$onclickcrsinc.$checkedcrsinc{no}.' />'.&mt('No').'</label></span>'."\n". + (' 'x4). + '<span class="LC_nobreak">'.$lt{'key'}. + ':<input type="text" size="25" name="lti_key_'.$i.'" value="'.$key.'" /></span> '. + (' 'x2). + '<span class="LC_nobreak">'.$lt{'secret'}.':'. + '<input type="password" size="20" name="lti_secret_'.$i.'" value="'.$secret.'" />'. + '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.lti_secret_'.$i.'.type='."'text'".' } else { this.form.lti_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'. + '<input type="hidden" name="lti_id_'.$i.'" value="'.$item.'" /></span>'. + '</fieldset>'.<i_options($i,$current,$itemcount,%lt).'</td></tr>'; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_add'".');"'; + $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n". + '<input type="hidden" name="lti_maxnum" value="'.$maxnum.'" />'."\n". + '<select name="lti_pos_add"'.$chgstr.'>'; + for (my $k=0; $k<$maxnum+1; $k++) { + my $vpos = $k+1; + my $selstr; + if ($k == $maxnum) { + $selstr = ' selected="selected" '; + } + $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>'; + } + $datatable .= '</select> '."\n". + '<input type="checkbox" name="lti_add" value="1" />'.&mt('Add').'</span></td>'."\n". + '<td colspan="2">'. + '<fieldset><legend>'.&mt('Required settings').'</legend>'. + '<span class="LC_nobreak">'.$lt{'consumer'}. + ':<input type="text" size="15" name="lti_consumer_add" value="" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'version'}.':<select name="lti_version_add">'. + '<option value="LTI-1p0" selected="selected">1.1</option></select></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'lifetime'}.':<input type="text" size="3" name="lti_lifetime_add" value="300" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'requser'}.':'. + '<label><input type="radio" name="lti_requser_add" value="1" onclick="toggleLTI(this.form,'."'requser','add'".');" checked="checked" />'.&mt('Yes').'</label> '."\n". + '<label><input type="radio" name="lti_requser_add" value="0" onclick="toggleLTI(this.form,'."'requser','add'".');" />'.&mt('No').'</label></span>'."\n". + '<br /><br />'. + '<span class="LC_nobreak">'.$lt{'crsinc'}.':'. + '<label><input type="radio" name="lti_crsinc_add" value="1" onclick="toggleLTI(this.form,'."'crsinc','add'".');" checked="checked" />'.&mt('Yes').'</label> '."\n". + '<label><input type="radio" name="lti_crsinc_add" value="0" onclick="toggleLTI(this.form,'."'crsinc','add'".');" />'.&mt('No').'</label></span>'."\n". + (' 'x4). + '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="lti_key_add" value="" /></span> '."\n". + (' 'x2). + '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="lti_secret_add" value="" />'. + '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.lti_secret_add.type='."'text'".' } else { this.form.lti_secret_add.type='."'password'".' }" />'.&mt('Visible input').'</label></span> '."\n". + '</fieldset>'.<i_options('add',undef,$itemcount,%lt). + '</td>'."\n". + '</tr>'."\n"; + $itemcount ++; + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub lti_names { + my %lt = &Apache::lonlocal::texthash( + 'version' => 'LTI Version', + 'url' => 'URL', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'consumer' => 'Consumer', + 'secret' => 'Secret', + 'requser' => "User's identity sent", + 'crsinc' => "Course's identity sent", + 'email' => 'Email address', + 'sourcedid' => 'User ID', + 'other' => 'Other', + 'passback' => 'Can return grades to Consumer:', + 'roster' => 'Can retrieve roster from Consumer:', + 'topmenu' => 'Display LON-CAPA page header', + 'inlinemenu'=> 'Display LON-CAPA inline menu', + ); + return %lt; +} + +sub lti_options { + my ($num,$current,$itemcount,%lt) = @_; + my (%checked,%rolemaps,$crssecsrc,$userfield,$cidfield,$callback); + $checked{'mapuser'}{'sourcedid'} = ' checked="checked"'; + $checked{'mapcrs'}{'course_offering_sourcedid'} = ' checked="checked"'; + $checked{'storecrs'}{'Y'} = ' checked="checked"'; + $checked{'makecrs'}{'N'} = ' checked="checked"'; + $checked{'mapcrstype'} = {}; + $checked{'makeuser'} = {}; + $checked{'selfenroll'} = {}; + $checked{'crssec'} = {}; + $checked{'crssecsrc'} = {}; + $checked{'lcauth'} = {}; + $checked{'menuitem'} = {}; + if ($num eq 'add') { + $checked{'lcauth'}{'lti'} = ' checked="checked"'; + } + my $userfieldsty = 'none'; + my $crsfieldsty = 'none'; + my $crssecfieldsty = 'none'; + my $secsrcfieldsty = 'none'; + my $callbacksty = 'none'; + my $passbacksty = 'none'; + my $optionsty = 'block'; + my $crssty = 'block'; + my $lcauthparm; + my $lcauthparmstyle = 'display:none'; + my $lcauthparmtext; + my $menusty; + my $numinrow = 4; + my %menutitles = <imenu_titles(); + + 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'} = ''; + if ($current->{'mapuser'} eq 'lis_person_contact_email_primary') { + $checked{'mapuser'}{'email'} = ' checked="checked"'; + } else { + $checked{'mapuser'}{'other'} = ' checked="checked"'; + $userfield = $current->{'mapuser'}; + $userfieldsty = 'inline-block'; + } + } + if (($current->{'mapcrs'} ne '') && ($current->{'mapcrs'} ne 'course_offering_sourcedid')) { + $checked{'mapcrs'}{'course_offering_sourcedid'} = ''; + if ($current->{'mapcrs'} eq 'context_id') { + $checked{'mapcrs'}{'context_id'} = ' checked="checked"'; + } else { + $checked{'mapcrs'}{'other'} = ' checked="checked"'; + $cidfield = $current->{'mapcrs'}; + $crsfieldsty = 'inline-block'; + } + } + if (ref($current->{'mapcrstype'}) eq 'ARRAY') { + foreach my $type (@{$current->{'mapcrstype'}}) { + $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"'; + } + if (ref($current->{'makeuser'}) eq 'ARRAY') { + foreach my $role (@{$current->{'makeuser'}}) { + $checked{'makeuser'}{$role} = ' checked="checked"'; + } + } + if ($current->{'lcauth'} =~ /^(internal|localauth|krb4|krb5|lti)$/) { + $checked{'lcauth'}{$1} = ' checked="checked"'; + unless (($current->{'lcauth'} eq 'lti') || ($current->{'lcauth'} eq 'internal')) { + $lcauthparm = $current->{'lcauthparm'}; + $lcauthparmstyle = 'display:table-row'; + if ($current->{'lcauth'} eq 'localauth') { + $lcauthparmtext = &mt('Local auth argument'); + } else { + $lcauthparmtext = &mt('Kerberos domain'); + } + } + } + if (ref($current->{'selfenroll'}) eq 'ARRAY') { + foreach my $role (@{$current->{'selfenroll'}}) { + $checked{'selfenroll'}{$role} = ' checked="checked"'; + } + } + if (ref($current->{'maproles'}) eq 'HASH') { + %rolemaps = %{$current->{'maproles'}}; + } + if ($current->{'section'} ne '') { + $checked{'crssec'}{'Y'} = ' checked="checked"'; + $crssecfieldsty = 'inline-block'; + if ($current->{'section'} eq 'course_section_sourcedid') { + $checked{'crssecsrc'}{'sourcedid'} = ' checked="checked"'; + } else { + $checked{'crssecsrc'}{'other'} = ' checked="checked"'; + $crssecsrc = $current->{'section'}; + $secsrcfieldsty = 'inline-block'; + } + } else { + $checked{'crssec'}{'N'} = ' checked="checked"'; + } + if ($current->{'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 { + $checked{'topmenu'}{'N'} = ' checked="checked"'; + } + if ($current->{'inlinemenu'}) { + $checked{'inlinemenu'}{'Y'} = ' checked="checked"'; + } else { + $checked{'inlinemenu'}{'N'} = ' checked="checked"'; + } + if (($current->{'topmenu'}) || ($current->{'inlinemenu'})) { + $menusty = 'inline-block'; + if (ref($current->{'lcmenu'}) eq 'ARRAY') { + foreach my $item (@{$current->{'lcmenu'}}) { + if (exists($menutitles{$item})) { + $checked{'menuitem'}{$item} = ' checked="checked"'; + } + } + } + } else { + $menusty = 'none'; + } + } 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"'; + $menusty = 'inline-block'; + } + my @coursetypes = ('official','unofficial','community','textbook','placement','lti'); + my %coursetypetitles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Community', + textbook => 'Textbook', + placement => 'Placement Test', + lti => 'LTI Provider', + ); + my @authtypes = ('internal','krb4','krb5','localauth'); + my %shortauth = ( + internal => 'int', + krb4 => 'krb4', + krb5 => 'krb5', + localauth => 'loc' + ); + my %authnames = &authtype_names(); + my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); + my @lticourseroles = qw(Learner Instructor TeachingAssistant Mentor); + my @courseroles = ('cc','in','ta','ep','st'); + 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'".');"'; + my $output = '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Logout options').'</legend>'. + '<div class="LC_floatleft"><span class="LC_nobreak">'.&mt('Callback to logout LON-CAPA on log out from Consumer').': '. + '<label><input type="radio" name="lti_callback_'.$num.'" value="0"'. + $checked{'callback'}{'N'}.$onclickcallback.' />'.&mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_callback_'.$num.'" value="1"'. + $checked{'callback'}{'Y'}.$onclickcallback.' />'.&mt('Yes').'</label></span></div>'. + '<div class="LC_floatleft" style="display:'.$callbacksty.';" id="lti_callbackfield_'.$num.'">'. + '<span class="LC_nobreak">'.&mt('Parameter').': '. + '<input type="text" name="lti_callbackparam_'.$num.'" value="'.$callback.'" /></span>'. + '</div><div style="padding:0;clear:both;margin:0;border:0"></div></fieldset>'. + '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Mapping users').'</legend>'. + '<div class="LC_floatleft"><span class="LC_nobreak">'.&mt('LON-CAPA username').': '; + foreach my $option ('sourcedid','email','other') { + $output .= '<label><input type="radio" name="lti_mapuser_'.$num.'" value="'.$option.'"'. + $checked{'mapuser'}{$option}.$onclickuser.' />'.$lt{$option}.'</label>'. + ($option eq 'other' ? '' : (' 'x2) ); + } + $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>'. + '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('Roles which may create user accounts').'</legend>'; + foreach my $ltirole (@ltiroles) { + $output .= '<span class="LC_nobreak"><label><input type="checkbox" name="lti_makeuser_'.$num.'" value="'.$ltirole.'"'. + $checked{'makeuser'}{$ltirole}.' />'.$ltirole.'</label> </span> '; + } + $output .= '</fieldset>'. + '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'.&mt('New user accounts created for LTI users').'</legend>'. + '<table>'. + &modifiable_userdata_row('lti','instdata_'.$num,$current,$numinrow,$itemcount). + '</table>'. + '<table class="LC_nested"><tr><td class="LC_left_item">LON-CAPA Authentication</td>'. + '<td class="LC_left_item">'; + foreach my $auth ('lti',@authtypes) { + my $authtext; + if ($auth eq 'lti') { + $authtext = &mt('None'); + } else { + $authtext = $authnames{$shortauth{$auth}}; + } + $output .= '<span class="LC_nobreak"><label><input type="radio" name="lti_lcauth_'.$num. + '" value="'.$auth.'"'.$checked{'lcauth'}{$auth}.$onclicklcauth.' />'. + $authtext.'</label></span> '; + } + $output .= '</td></tr>'. + '<tr id="lti_lcauth_parmrow_'.$num.'" style="'.$lcauthparmstyle.'">'. + '<td class="LC_right_item" colspan="2"><span class="LC_nobreak">'. + '<span id="lti_lcauth_parmtext_'.$num.'">'.$lcauthparmtext.'</span>'. + '<input type="text" name="lti_lcauthparm_'.$num.'" value="" /></span></td></tr>'. + '</table></fieldset>'. + '<fieldset class="ltioption_usr_'.$num.'" style="display:'.$optionsty.'"><legend>'. + &mt('LON-CAPA menu items (Course Coordinator can override)').'</legend>'. + '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{'topmenu'}.': '. + '<label><input type="radio" name="lti_topmenu_'.$num.'" value="0"'. + $checked{'topmenu'}{'N'}.$onclickmenu.' />'.&mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_topmenu_'.$num.'" value="1"'. + $checked{'topmenu'}{'Y'}.$onclickmenu.' />'.&mt('Yes').'</label></span></div>'. + '<div style="padding:0;clear:both;margin:0;border:0"></div>'. + '<div class="LC_floatleft"><span class="LC_nobreak">'.$lt{'inlinemenu'}.': '. + '<label><input type="radio" name="lti_inlinemenu_'.$num.'" value="0"'. + $checked{'inlinemenu'}{'N'}.$onclickmenu.' />'.&mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_inlinemenu_'.$num.'" value="1"'. + $checked{'inlinemenu'}{'Y'}.$onclickmenu.' />'.&mt('Yes').'</label></span></div>'; + $output .='<div style="padding:0;clear:both;margin:0;border:0"></div>'. + '<div class="LC_floatleft" style="display:'.$menusty.';" id="lti_menufield_'.$num.'">'. + '<span class="LC_nobreak">'.&mt('Menu items').': '; + foreach my $type ('fullname','coursetitle','role','logout','grades') { + $output .= '<label><input type="checkbox" name="lti_menuitem_'.$num.'" value="'.$type.'"'. + $checked{'menuitem'}{$type}.' />'.$menutitles{$type}.'</label>'. + (' 'x2); + } + $output .= '</span></div></fieldset>'. + '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Mapping courses').'</legend>'. + '<div class="LC_floatleft"><span class="LC_nobreak">'. + &mt('Unique course identifier').': '; + foreach my $option ('course_offering_sourcedid','context_id','other') { + $output .= '<label><input type="radio" name="lti_mapcrs_'.$num.'" value="'.$option.'"'. + $checked{'mapcrs'}{$option}.$onclickcrs.' />'.$option.'</label>'. + ($option eq 'other' ? '' : (' 'x2) ); + } + $output .= '</span></div><div class="LC_floatleft" style="display:'.$crsfieldsty.';" id="lti_crsfield_'.$num.'">'. + '<input type="text" name="lti_mapcrsfield_'.$num.'" value="'.$cidfield.'" />'. + '</div><div style="padding:0;clear:both;margin:0;border:0"></div>'. + '<span class="LC_nobreak">'.&mt('LON-CAPA course type(s)').': '; + foreach my $type (@coursetypes) { + $output .= '<label><input type="checkbox" name="lti_mapcrstype_'.$num.'" value="'.$type.'"'. + $checked{'mapcrstype'}{$type}.' />'.$coursetypetitles{$type}.'</label>'. + (' 'x2); + } + $output .= '</span><br /><br />'. + '<span class="LC_nobreak">'.&mt('Store mapping of course identifier to LON-CAPA CourseID').': '. + '<label><input type="radio" name="lti_storecrs_'.$num.'" value="0"'. + $checked{'storecrs'}{'N'}.' />'.&mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_storecrs_'.$num.'" value="1"'. + $checked{'storecrs'}{'Y'}.' />'.&mt('Yes').'</label></span>'. + '</fieldset>'. + '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Mapping course roles').'</legend><table><tr>'; + foreach my $ltirole (@lticourseroles) { + my ($selected,$selectnone); + if ($rolemaps{$ltirole} eq '') { + $selectnone = ' selected="selected"'; + } + $output .= '<td style="text-align: center">'.$ltirole.'<br />'. + '<select name="lti_maprole_'.$ltirole.'_'.$num.'">'. + '<option value=""'.$selectnone.'>'.&mt('Select').'</option>'; + foreach my $role (@courseroles) { + unless ($selectnone) { + if ($rolemaps{$ltirole} eq $role) { + $selected = ' selected="selected"'; + } else { + $selected = ''; + } + } + $output .= '<option value="'.$role.'"'.$selected.'>'. + &Apache::lonnet::plaintext($role,'Course'). + '</option>'; + } + $output .= '</select></td>'; + } + $output .= '</tr></table></fieldset>'. + '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Creating courses').'</legend>'. + '<span class="LC_nobreak">'.&mt('Course created (if absent) on Instructor access').': '. + '<label><input type="radio" name="lti_makecrs_'.$num.'" value="0"'. + $checked{'makecrs'}{'N'}.' />'.&mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_makecrs_'.$num.'" value="1"'. + $checked{'makecrs'}{'Y'}.' />'.&mt('Yes').'</label></span>'. + '</fieldset>'. + '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Roles which may self-enroll').'</legend>'; + foreach my $lticrsrole (@lticourseroles) { + $output .= '<span class="LC_nobreak"><label><input type="checkbox" name="lti_selfenroll_'.$num.'" value="'.$lticrsrole.'"'. + $checked{'selfenroll'}{$lticrsrole}.' />'.$lticrsrole.'</label> </span> '; + } + $output .= '</fieldset>'. + '<fieldset class="ltioption_crs_'.$num.'" style="display:'.$crssty.'"><legend>'.&mt('Course options').'</legend>'. + '<div class="LC_floatleft"><span class="LC_nobreak">'.&mt('Assign users to sections').': '. + '<label><input type="radio" name="lti_crssec_'.$num.'" value="0"'. + $checked{'crssec'}{'N'}.$onclicksec.' />'.&mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_crssec_'.$num.'" value="1"'. + $checked{'crssec'}{'Y'}.$onclicksec.' />'.&mt('Yes').'</label></span></div>'. + '<div class="LC_floatleft" style="display:'.$crssecfieldsty.';" id="lti_crssecfield_'.$num.'">'. + '<span class="LC_nobreak">'.&mt('From').':<label>'. + '<input type="radio" name="lti_crssecsrc_'.$num.'" value="course_section_sourcedid"'. + $checked{'crssecsrc'}{'sourcedid'}.$onclicksecsrc.' />'. + &mt('Standard field').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_crssecsrc_'.$num.'" value="other"'. + $checked{'crssecsrc'}{'other'}.$onclicksecsrc.' />'.&mt('Other'). + '</label></span></div><div class="LC_floatleft" style="display:'.$secsrcfieldsty.';" id="lti_secsrcfield_'.$num.'">'. + '<input type="text" name="lti_customsection_'.$num.'" value="'.$crssecsrc.'" />'. + '</div><div style="padding:0;clear:both;margin:0;border:0"></div>'; + my ($pb1p1chk,$pb1p0chk,$onclickpb); + foreach my $extra ('roster','passback') { + my $checkedon = ''; + my $checkedoff = ' checked="checked"'; + if ($extra eq 'passback') { + $pb1p1chk = ' checked="checked"'; + $pb1p0chk = ''; + $onclickpb = ' onclick="toggleLTI(this.form,'."'passback','$num'".');"'; + } else { + $onclickpb = ''; + } + if (ref($current) eq 'HASH') { + if (($current->{$extra})) { + $checkedon = $checkedoff; + $checkedoff = ''; + if ($extra eq 'passback') { + $passbacksty = 'inline-block'; + } + if ($current->{'passbackformat'} eq '1.0') { + $pb1p0chk = ' checked="checked"'; + $pb1p1chk = ''; + } + } + } + $output .= $lt{$extra}.' '. + '<label><input type="radio" name="lti_'.$extra.'_'.$num.'" value="0"'.$checkedoff.$onclickpb.' />'. + &mt('No').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_'.$extra.'_'.$num.'" value="1"'.$checkedon.$onclickpb.' />'. + &mt('Yes').'</label><br />'; + } + $output .= '<div class="LC_floatleft" style="display:'.$passbacksty.';" id="lti_passback_'.$num.'">'. + '<span class="LC_nobreak">'.&mt('Grade format'). + '<label><input type="radio" name="lti_passbackformat_'.$num.'" value="1.1"'.$pb1p1chk.' />'. + &mt('Outcomes Service (1.1)').'</label>'.(' 'x2). + '<label><input type="radio" name="lti_passbackformat_'.$num.'" value="1.0"'.$pb1p0chk.'/>'. + &mt('Outcomes Extension (1.0)').'</label></span></div>'. + '<div style="padding:0;clear:both;margin:0;border:0"></div></fieldset>'; + $output .= '</span></div></fieldset>'; +# '<fieldset><legend>'.&mt('Assigning author roles').'</legend>'; +# +# $output .= '</fieldset>'. +# '<fieldset><legend>'.&mt('Assigning domain roles').'</legend>'; + return $output; +} + +sub ltimenu_titles { + return &Apache::lonlocal::texthash( + fullname => 'Full name', + coursetitle => 'Course title', + role => 'Role', + logout => 'Logout', + grades => 'Grades', + ); +} + +sub check_switchserver { + my ($home) = @_; + my $switchserver; + if ($home ne '') { + my $allowed; + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } } + if (!$allowed) { + $switchserver='<a href="/adm/switchserver?otherserver='.$home.'&role='. + &HTML::Entities::encode($env{'request.role'},'\'<>"&'). + '&destinationurl=/adm/domainprefs">'.&mt('Switch Server').'</a>'; + } + } + return $switchserver; +} + sub print_coursedefaults { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles); my $itemcount = 1; my %choices = &Apache::lonlocal::texthash ( + canuse_pdfforms => 'Course/Community users can create/upload PDF forms', uploadquota => 'Default quota for files uploaded directly to course/community using Course Editor (MB)', anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys', coursecredits => 'Credits can be specified for courses', @@ -4530,6 +7196,7 @@ sub print_coursedefaults { postsubmit => 'Disable submit button/keypress following student submission', canclone => "People who may clone a course (besides course's owner and coordinators)", mysqltables => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver', + ltiauth => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication', ); my %staticdefaults = ( anonsurvey_threshold => 10, @@ -4539,12 +7206,13 @@ sub print_coursedefaults { ); if ($position eq 'top') { %defaultchecked = ( + 'canuse_pdfforms' => 'off', 'uselcmath' => 'on', 'usejsme' => 'on', 'inline_chem' => 'on', 'canclone' => 'none', ); - @toggles = ('uselcmath','usejsme','inline_chem'); + @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem'); my $deftex = $Apache::lonnet::deftex; if (ref($settings) eq 'HASH') { if ($settings->{'texengine'}) { @@ -4577,7 +7245,7 @@ sub print_coursedefaults { $datatable = $mathdisp.$datatable; $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= - '<tr'.$css_class.'><td valign="top">'. + '<tr'.$css_class.'><td style="vertical-align: top">'. '<span class="LC_nobreak">'.$choices{'canclone'}. '</span></td><td class="LC_left_item">'; my $currcanclone = 'none'; @@ -4621,7 +7289,7 @@ sub print_coursedefaults { if ($checked) { $show = 'block'; } - $additional = '<div id="cloneinstcode" style="display:'.$show.'" />'. + $additional = '<div id="cloneinstcode" style="display:'.$show.';" />'. &mt('Institutional codes for new and cloned course have identical:'). '<br />'; foreach my $item (@code_order) { @@ -4651,8 +7319,12 @@ sub print_coursedefaults { my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql); my $currusecredits = 0; my $postsubmitclient = 1; - my @types = ('official','unofficial','community','textbook'); + my $ltiauth = 0; + my @types = ('official','unofficial','community','textbook','placement'); if (ref($settings) eq 'HASH') { + if ($settings->{'ltiauth'}) { + $ltiauth = 1; + } $currdefresponder = $settings->{'anonsurvey_threshold'}; if (ref($settings->{'uploadquota'}) eq 'HASH') { foreach my $type (keys(%{$settings->{'uploadquota'}})) { @@ -4729,10 +7401,10 @@ sub print_coursedefaults { $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. $choices{'uploadquota'}. '</span></td>'. - '<td align="right" class="LC_right_item">'. + '<td style="text-align: right" class="LC_right_item">'. '<table><tr>'; foreach my $type (@types) { - $datatable .= '<td align="center">'.&mt($type).'<br />'. + $datatable .= '<td style="text-align: center">'.&mt($type).'<br />'. '<input type="text" name="uploadquota_'.$type.'"'. ' value="'.$curruploadquota{$type}.'" size="5" /></td>'; } @@ -4747,7 +7419,7 @@ sub print_coursedefaults { '<i>'.&mt('Default credits').'</i><br /><table><tr>'; foreach my $type (@types) { next if ($type eq 'community'); - $additional .= '<td align="center">'.&mt($type).'<br />'. + $additional .= '<td style="text-align: center">'.&mt($type).'<br />'. '<input type="text" name="'.$type.'_credits"'. ' value="'.$defcredits{$type}.'" size="3" /></td>'; } @@ -4771,7 +7443,7 @@ sub print_coursedefaults { '<i>'.&mt('Enter 0 to remain disabled until page reload.').'</i><br />'. '<table><tr>'; foreach my $type (@types) { - $additional .= '<td align="center">'.&mt($type).'<br />'. + $additional .= '<td style="text-align: center">'.&mt($type).'<br />'. '<input type="text" name="'.$type.'_timeout" value="'. $deftimeout{$type}.'" size="5" /></td>'; } @@ -4789,16 +7461,25 @@ sub print_coursedefaults { $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. $choices{'mysqltables'}. '</span></td>'. - '<td align="right" class="LC_right_item">'. + '<td style="text-align: right" class="LC_right_item">'. '<table><tr>'; foreach my $type (@types) { - $datatable .= '<td align="center">'.&mt($type).'<br />'. + $datatable .= '<td style="text-align: center">'.&mt($type).'<br />'. '<input type="text" name="mysqltables_'.$type.'"'. ' value="'.$currmysql{$type}.'" size="8" /></td>'; } $datatable .= '</tr></table></td></tr>'."\n"; $itemcount ++; - + %defaultchecked = ('ltiauth' => 'off'); + @toggles = ('ltiauth'); + $current = { + 'ltiauth' => $ltiauth, + }; + ($table,$itemcount) = + &radiobutton_prefs($current,\@toggles,\%defaultchecked, + \%choices,$itemcount,undef,undef,'left'); + $datatable .= $table; + $itemcount ++; } $$rowtotal += $itemcount; return $datatable; @@ -4808,7 +7489,7 @@ sub print_selfenrollment { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable); my $itemcount = 1; - my @types = ('official','unofficial','community','textbook'); + my @types = ('official','unofficial','community','textbook','placement'); if (($position eq 'top') || ($position eq 'middle')) { my ($rowsref,$titlesref) = &Apache::lonuserutils::get_selfenroll_titles(); my %descs = &Apache::lonuserutils::selfenroll_default_descs(); @@ -5029,6 +7710,168 @@ sub print_validation_rows { return $datatable; } +sub print_privacy { + my ($position,$dom,$settings,$rowtotal) = @_; + my ($datatable,$css_class,$numinrow,@items,%names,$othertitle,$usertypes,$types); + my $itemcount = 0; + if ($position eq 'top') { + $numinrow = 2; + } else { + @items = ('domain','author','course','community'); + %names = &Apache::lonlocal::texthash ( + domain => 'Assigned domain role(s)', + author => 'Assigned co-author role(s)', + course => 'Assigned course role(s)', + community => 'Assigned community role(s)', + ); + $numinrow = 4; + ($othertitle,$usertypes,$types) = + &Apache::loncommon::sorted_inst_types($dom); + } + if (($position eq 'top') || ($position eq 'middle')) { + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + if ($position eq 'top') { + my %curr; + my @options = ('none','user','domain','auto'); + my %titles = &Apache::lonlocal::texthash ( + none => 'Not allowed', + user => 'User authorizes', + domain => 'DC authorizes', + auto => 'Unrestricted', + instdom => 'Other domain shares institution/provider', + extdom => 'Other domain has different institution/provider', + notify => 'Notify when role needs authorization', + ); + my %names = &Apache::lonlocal::texthash ( + domain => 'Domain role', + author => 'Co-author role', + course => 'Course role', + community => 'Community role', + ); + my $primary_id = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($primary_id); + foreach my $domtype ('instdom','extdom') { + my (%checked,$skip); + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{$domtype}.'</td>'. + '<td class="LC_left_item">'; + 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 .= '<fieldset><legend>'.$names{$roletype}.'</legend>'; + foreach my $option (@options) { + $datatable .= '<span class="LC_nobreak"><label>'. + '<input type="radio" name="privacy_approval_'.$domtype.'_'.$roletype.'" '. + 'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}. + '</label></span> '; + } + $datatable .= '</fieldset>'; + } + } + $datatable .= '</td></tr>'; + $itemcount ++; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{'notify'}.'</td>'. + '<td class="LC_left_item">'; + if ((@instdoms > 1) || (keys(%by_location) > 0)) { + my %curr; + if (ref($settings) eq 'HASH') { + if ($settings->{'notify'} ne '') { + map {$curr{$_}=1;} split(/,/,$settings->{'notify'}); + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my ($numdc,$table,$rows) = &active_dc_picker($dom,$numinrow,'checkbox', + 'privacy_notify',%curr); + if ($numdc > 0) { + $datatable .= $table; + } else { + $datatable .= &mt('There are no active Domain Coordinators'); + } + } else { + $datatable .= &mt('Nothing to set here, as there are no other domains'); + } + $datatable .='</td></tr>'; + } 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 .= '<tr'.$css_class.'>'. + '<td class="LC_left_item">'.$title.'</td>'. + '<td class="LC_left_item">'. + &mt('Nothing to set here, as there are no other domains'). + '</td></tr>'; + $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); @@ -5050,6 +7893,7 @@ sub print_passwords { min => 'Minimum password length', max => 'Maximum password length', chars => 'Required characters', + expire => 'Password expiration (days)', numsaved => 'Number of previous passwords to save and disallow reuse', ); if ($position eq 'top') { @@ -5314,84 +8158,7 @@ sub print_passwords { $itemcount ++; } } elsif ($position eq 'lower') { - my ($min,$max,%chars,$numsaved); - $min = $Apache::lonnet::passwdmin; - if (ref($settings) eq 'HASH') { - if ($settings->{min}) { - $min = $settings->{min}; - } - if ($settings->{max}) { - $max = $settings->{max}; - } - if (ref($settings->{chars}) eq 'ARRAY') { - map { $chars{$_} = 1; } (@{$settings->{chars}}); - } - if ($settings->{numsaved}) { - $numsaved = $settings->{numsaved}; - } - } - my %rulenames = &Apache::lonlocal::texthash( - uc => 'At least one upper case letter', - lc => 'At least one lower case letter', - num => 'At least one number', - spec => 'At least one non-alphanumeric', - ); - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr'.$css_class.'><td>'.$titles{'min'}.'</td>'. - '<td class="LC_left_item"><span class="LC_nobreak">'. - '<input type="text" name="passwords_min" value="'.$min.'" size="3" '. - 'onblur="javascript:warnIntPass(this);" />'. - '<span class="LC_fontsize_small"> '.&mt('(Enter an integer: 7 or larger)').'</span>'. - '</span></td></tr>'; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr'.$css_class.'><td>'.$titles{'max'}.'</td>'. - '<td class="LC_left_item"><span class="LC_nobreak">'. - '<input type="text" name="passwords_max" value="'.$max.'" size="3" '. - 'onblur="javascript:warnIntPass(this);" />'. - '<span class="LC_fontsize_small"> '.&mt('(Leave blank for no maximum)').'</span>'. - '</span></td></tr>'; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr'.$css_class.'><td>'.$titles{'chars'}.'<br />'. - '<span class="LC_nobreak LC_fontsize_small">'.&mt('(Leave unchecked if not required)'). - '</span></td>'; - my $numinrow = 2; - my @possrules = ('uc','lc','num','spec'); - $datatable .= '<td class="LC_left_item"><table>'; - for (my $i=0; $i<@possrules; $i++) { - my ($rem,$checked); - if ($chars{$possrules[$i]}) { - $checked = ' checked="checked"'; - } - $rem = $i%($numinrow); - if ($rem == 0) { - if ($i > 0) { - $datatable .= '</tr>'; - } - $datatable .= '<tr>'; - } - $datatable .= '<td><span class="LC_nobreak"><label>'. - '<input type="checkbox" name="passwords_chars" value="'.$possrules[$i].'"'.$checked.' />'. - $rulenames{$possrules[$i]}.'</label></span></td>'; - } - my $rem = @possrules%($numinrow); - my $colsleft = $numinrow - $rem; - if ($colsleft > 1 ) { - $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'. - ' </td>'; - } elsif ($colsleft == 1) { - $datatable .= '<td class="LC_left_item"> </td>'; - } - $datatable .='</table></td></tr>'; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr'.$css_class.'><td>'.$titles{'numsaved'}.'</td>'. - '<td class="LC_left_item"><span class="LC_nobreak">'. - '<input type="text" name="passwords_numsaved" value="'.$numsaved.'" size="3" '. - 'onblur="javascript:warnIntPass(this);" />'. - '<span class="LC_fontsize_small"> '.&mt('(Leave blank to not save previous passwords)').'</span>'. - '</span></td></tr>'; + $datatable .= &password_rules('passwords',\$itemcount,$settings); } else { my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my %ownerchg = ( @@ -5416,7 +8183,7 @@ sub print_passwords { $datatable .= '<tr '.$css_class.'>'. '<td>'. &mt('Requirements').'<ul>'. - '<li>'.&mt("Course 'type' is not a Community").'</li>'. + '<li>'.&mt("Course 'type' is not a Community or Placement Test").'</li>'. '<li>'.&mt('User is Course Coordinator and also course owner').'</li>'. '<li>'.&mt("Student's only active roles are student role(s) in course(s) owned by this user").'</li>'. '<li>'.&mt('User, course, and student share same domain').'</li>'. @@ -5451,6 +8218,129 @@ sub print_passwords { return $datatable; } +sub password_rules { + my ($prefix,$itemcountref,$settings) = @_; + my ($min,$max,%chars,$expire,$numsaved,$numinrow); + my %titles; + if ($prefix eq 'passwords') { + %titles = &Apache::lonlocal::texthash ( + min => 'Minimum password length', + max => 'Maximum password length', + chars => 'Required characters', + ); + } elsif ($prefix eq 'secrets') { + %titles = &Apache::lonlocal::texthash ( + min => 'Minimum secret length', + max => 'Maximum secret length', + chars => 'Required characters', + ); + } + $min = $Apache::lonnet::passwdmin; + my $datatable; + my $itemcount; + if (ref($itemcountref)) { + $itemcount = $$itemcountref; + } + if (ref($settings) eq 'HASH') { + if ($settings->{min}) { + $min = $settings->{min}; + } + if ($settings->{max}) { + $max = $settings->{max}; + } + if (ref($settings->{chars}) eq 'ARRAY') { + map { $chars{$_} = 1; } (@{$settings->{chars}}); + } + if ($prefix eq 'passwords') { + if ($settings->{expire}) { + $expire = $settings->{expire}; + } + if ($settings->{numsaved}) { + $numsaved = $settings->{numsaved}; + } + } + } + my %rulenames = &Apache::lonlocal::texthash( + uc => 'At least one upper case letter', + lc => 'At least one lower case letter', + num => 'At least one number', + spec => 'At least one non-alphanumeric', + ); + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{'min'}.'</td>'. + '<td class="LC_left_item"><span class="LC_nobreak">'. + '<input type="text" name="'.$prefix.'_min" value="'.$min.'" size="3" '. + 'onblur="javascript:warnInt'.$prefix.'(this);" />'. + '<span class="LC_fontsize_small"> '.&mt('(Enter an integer: 7 or larger)').'</span>'. + '</span></td></tr>'; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{'max'}.'</td>'. + '<td class="LC_left_item"><span class="LC_nobreak">'. + '<input type="text" name="'.$prefix.'_max" value="'.$max.'" size="3" '. + 'onblur="javascript:warnInt'.$prefix.'(this);" />'. + '<span class="LC_fontsize_small"> '.&mt('(Leave blank for no maximum)').'</span>'. + '</span></td></tr>'; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{'chars'}.'<br />'. + '<span class="LC_nobreak LC_fontsize_small">'.&mt('(Leave unchecked if not required)'). + '</span></td>'; + my $numinrow = 2; + my @possrules = ('uc','lc','num','spec'); + $datatable .= '<td class="LC_left_item"><table>'; + for (my $i=0; $i<@possrules; $i++) { + my ($rem,$checked); + if ($chars{$possrules[$i]}) { + $checked = ' checked="checked"'; + } + $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $datatable .= '</tr>'; + } + $datatable .= '<tr>'; + } + $datatable .= '<td><span class="LC_nobreak"><label>'. + '<input type="checkbox" name="'.$prefix.'_chars" value="'.$possrules[$i].'"'.$checked.' />'. + $rulenames{$possrules[$i]}.'</label></span></td>'; + } + my $rem = @possrules%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'. + ' </td>'; + } elsif ($colsleft == 1) { + $datatable .= '<td class="LC_left_item"> </td>'; + } + $datatable .='</table></td></tr>'; + $itemcount ++; + if ($prefix eq 'passwords') { + $titles{'expire'} = &mt('Password expiration (days)'); + $titles{'numsaved'} = &mt('Number of previous passwords to save and disallow reuse'); + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{'expire'}.'</td>'. + '<td class="LC_left_item"><span class="LC_nobreak">'. + '<input type="text" name="'.$prefix.'_expire" value="'.$expire.'" size="4" '. + 'onblur="javascript:warnInt'.$prefix.'(this);" />'. + '<span class="LC_fontsize_small"> '.&mt('(Leave blank for no expiration)').'</span>'. + '</span></td></tr>'; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{'numsaved'}.'</td>'. + '<td class="LC_left_item"><span class="LC_nobreak">'. + '<input type="text" name="'.$prefix.'_numsaved" value="'.$numsaved.'" size="3" '. + 'onblur="javascript:warnInt'.$prefix.'(this);" />'. + '<span class="LC_fontsize_small"> '.&mt('(Leave blank to not save previous passwords)').'</span>'. + '</span></td></tr>'; + $itemcount ++; + } + if (ref($itemcountref)) { + $$itemcountref += $itemcount; + } + return $datatable; +} + sub print_wafproxy { my ($position,$dom,$settings,$rowtotal) = @_; my $css_class; @@ -5504,7 +8394,7 @@ sub print_wafproxy { my %config = &Apache::lonnet::get_dom('configuration',['wafproxy'],$domain); if (ref($config{'wafproxy'}) eq 'HASH') { $aliases{$domain} = $config{'wafproxy'}{'alias'}; - if (exists($config{'wafproxy'}{'saml'})) { + if (exists($config{'wafproxy'}{'saml'})) { $saml{$domain} = $config{'wafproxy'}{'saml'}; } foreach my $item ('remoteip','ipheader','trusted','vpnint','vpnext') { @@ -5569,10 +8459,10 @@ sub print_wafproxy { (' 'x2).'<span class="LC_nobreak">'. &mt('Alias used for SSO Auth').': <label>'. '<input type="radio" value="0"'.$samloff.' name="wafproxy_alias_saml_'.$server.'" />'. - &mt('No').'</label> <label>'. + &mt('No').'</label> <label>'. '<input type="radio" value="1"'.$samlon.' name="wafproxy_alias_saml_'.$server.'" />'. &mt('Yes').'</label></span>'. - '</td>'; + '</td>'; } $aliasrows .= '</tr>'; $aliasinfo{$dom_in_effect} .= $aliasrows; @@ -5760,15 +8650,14 @@ sub remoteip_methods { sub print_usersessions { my ($position,$dom,$settings,$rowtotal) = @_; - my ($css_class,$datatable,%checked,%choices); - my (%by_ip,%by_location,@intdoms); - &build_location_hashes(\@intdoms,\%by_ip,\%by_location); + my ($css_class,$datatable,$itemcount,%checked,%choices); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); my @alldoms = &Apache::lonnet::all_domains(); my %serverhomes = %Apache::lonnet::serverhomeIDs; my %servers = &Apache::lonnet::internet_dom_servers($dom); my %altids = &id_for_thisdom(%servers); - my $itemcount = 1; if ($position eq 'top') { if (keys(%serverhomes) > 1) { my %spareid = ¤t_offloads_to($dom,$settings,\%servers); @@ -5786,118 +8675,256 @@ sub print_usersessions { $other_insts,$curroffloadnow,$curroffloadoth,$rowtotal); } else { $datatable .= '<tr'.$css_class.'><td colspan="2">'. - &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.'); + &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.'). + '</td></tr>'; } } else { - if (keys(%by_location) == 0) { - $datatable .= '<tr'.$css_class.'><td colspan="2">'. - &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.'); + my %titles = &usersession_titles(); + my ($prefix,@types); + if ($position eq 'bottom') { + $prefix = 'remote'; + @types = ('version','excludedomain','includedomain'); } else { - my %lt = &usersession_titles(); - my $numinrow = 5; - my $prefix; - my @types; - if ($position eq 'bottom') { - $prefix = 'remote'; - @types = ('version','excludedomain','includedomain'); - } else { - $prefix = 'hosted'; - @types = ('excludedomain','includedomain'); - } - my (%current,%checkedon,%checkedoff); - my @lcversions = &Apache::lonnet::all_loncaparevs(); - my @locations = sort(keys(%by_location)); - foreach my $type (@types) { - $checkedon{$type} = ''; - $checkedoff{$type} = ' checked="checked"'; - } - if (ref($settings) eq 'HASH') { - if (ref($settings->{$prefix}) eq 'HASH') { - foreach my $key (keys(%{$settings->{$prefix}})) { - $current{$key} = $settings->{$prefix}{$key}; - if ($key eq 'version') { - if ($current{$key} ne '') { - $checkedon{$key} = ' checked="checked"'; - $checkedoff{$key} = ''; - } - } elsif (ref($current{$key}) eq 'ARRAY') { + $prefix = 'hosted'; + @types = ('excludedomain','includedomain'); + } + ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles); + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub rules_by_location { + my ($settings,$prefix,$by_location,$by_ip,$types,$titles) = @_; + my ($datatable,$itemcount,$css_class); + if (keys(%{$by_location}) == 0) { + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable = '<tr'.$css_class.'><td colspan="2">'. + &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.'). + '</td></tr>'; + $itemcount = 1; + } else { + $itemcount = 0; + my $numinrow = 5; + my (%current,%checkedon,%checkedoff); + my @locations = sort(keys(%{$by_location})); + foreach my $type (@{$types}) { + $checkedon{$type} = ''; + $checkedoff{$type} = ' checked="checked"'; + } + if (ref($settings) eq 'HASH') { + if (ref($settings->{$prefix}) eq 'HASH') { + foreach my $key (keys(%{$settings->{$prefix}})) { + $current{$key} = $settings->{$prefix}{$key}; + if ($key eq 'version') { + if ($current{$key} ne '') { $checkedon{$key} = ' checked="checked"'; $checkedoff{$key} = ''; } + } elsif (ref($current{$key}) eq 'ARRAY') { + $checkedon{$key} = ' checked="checked"'; + $checkedoff{$key} = ''; } } } - foreach my $type (@types) { - next if ($type ne 'version' && !@locations); - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= '<tr'.$css_class.'> - <td><span class="LC_nobreak">'.$lt{$type}.'</span><br /> - <span class="LC_nobreak"> - <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label> - <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>'; - if ($type eq 'version') { - my $selector = '<select name="'.$prefix.'_version">'; - foreach my $version (@lcversions) { - my $selected = ''; - if ($current{'version'} eq $version) { - $selected = ' selected="selected"'; - } - $selector .= ' <option value="'.$version.'"'. - $selected.'>'.$version.'</option>'; + } + foreach my $type (@{$types}) { + next if ($type ne 'version' && !@locations); + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= '<tr'.$css_class.'> + <td><span class="LC_nobreak">'.$titles->{$type}.'</span><br /> + <span class="LC_nobreak"> + <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedoff{$type}.' value="0" />'.&mt('Not in use').'</label> + <label><input type="radio" name="'.$prefix.'_'.$type.'_inuse" '.$checkedon{$type}.' value="1" />'.&mt('In use').'</label></span></td><td>'; + if ($type eq 'version') { + my @lcversions = &Apache::lonnet::all_loncaparevs(); + my $selector = '<select name="'.$prefix.'_version">'; + foreach my $version (@lcversions) { + my $selected = ''; + if ($current{'version'} eq $version) { + $selected = ' selected="selected"'; } - $selector .= '</select> '; - $datatable .= &mt('remote server must be version: [_1] or later',$selector); - } else { - $datatable.= '<div><input type="button" value="'.&mt('check all').'" '. - 'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'. - ' />'.(' 'x2). - '<input type="button" value="'.&mt('uncheck all').'" '. - 'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'. - "\n". - '</div><div><table>'; - my $rem; - for (my $i=0; $i<@locations; $i++) { - my ($showloc,$value,$checkedtype); - if (ref($by_location{$locations[$i]}) eq 'ARRAY') { - my $ip = $by_location{$locations[$i]}->[0]; - if (ref($by_ip{$ip}) eq 'ARRAY') { - $value = join(':',@{$by_ip{$ip}}); - $showloc = join(', ',@{$by_ip{$ip}}); - if (ref($current{$type}) eq 'ARRAY') { - foreach my $loc (@{$by_ip{$ip}}) { - if (grep(/^\Q$loc\E$/,@{$current{$type}})) { - $checkedtype = ' checked="checked"'; - last; - } + $selector .= ' <option value="'.$version.'"'. + $selected.'>'.$version.'</option>'; + } + $selector .= '</select> '; + $datatable .= &mt('remote server must be version: [_1] or later',$selector); + } else { + $datatable.= '<div><input type="button" value="'.&mt('check all').'" '. + 'onclick="javascript:checkAll(document.display.'.$prefix.'_'.$type.')"'. + ' />'.(' 'x2). + '<input type="button" value="'.&mt('uncheck all').'" '. + 'onclick="javascript:uncheckAll(document.display.'.$prefix.'_'.$type.')" />'. + "\n". + '</div><div><table>'; + my $rem; + for (my $i=0; $i<@locations; $i++) { + my ($showloc,$value,$checkedtype); + if (ref($by_location->{$locations[$i]}) eq 'ARRAY') { + my $ip = $by_location->{$locations[$i]}->[0]; + if (ref($by_ip->{$ip}) eq 'ARRAY') { + $value = join(':',@{$by_ip->{$ip}}); + $showloc = join(', ',@{$by_ip->{$ip}}); + if (ref($current{$type}) eq 'ARRAY') { + foreach my $loc (@{$by_ip->{$ip}}) { + if (grep(/^\Q$loc\E$/,@{$current{$type}})) { + $checkedtype = ' checked="checked"'; + last; } } } } - $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="'.$prefix.'_'.$type. - '" value="'.$value.'"'.$checkedtype.' />'.$showloc. - '</label></span></td>'; } - $rem = @locations%($numinrow); - my $colsleft = $numinrow - $rem; - if ($colsleft > 1 ) { - $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'. - ' </td>'; - } elsif ($colsleft == 1) { - $datatable .= '<td class="LC_left_item"> </td>'; + $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="'.$prefix.'_'.$type. + '" value="'.$value.'"'.$checkedtype.' />'.$showloc. + '</label></span></td>'; + } + $rem = @locations%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $datatable .= '<td colspan="'.$colsleft.'" class="LC_left_item">'. + ' </td>'; + } elsif ($colsleft == 1) { + $datatable .= '<td class="LC_left_item"> </td>'; + } + $datatable .= '</tr></table>'; + } + $datatable .= '</td></tr>'; + $itemcount ++; + } + } + return ($datatable,$itemcount); +} + +sub print_ssl { + my ($position,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable); + my $itemcount = 1; + if ($position eq 'top') { + my $primary_id = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($primary_id); + my $same_institution; + if ($intdom ne '') { + my $internet_names = &Apache::lonnet::get_internet_names($Apache::lonnet::perlvar{'lonHostID'}); + if (ref($internet_names) eq 'ARRAY') { + if (grep(/^\Q$intdom\E$/,@{$internet_names})) { + $same_institution = 1; + } + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable = '<tr'.$css_class.'><td colspan="2">'; + if ($same_institution) { + my %domservers = &Apache::lonnet::get_servers($dom); + $datatable .= &LONCAPA::SSL::print_certstatus(\%domservers,'web','domprefs'); + } else { + $datatable .= &mt("You need to be logged into one of your own domain's servers to display information about the status of LON-CAPA SSL certificates."); + } + $datatable .= '</td></tr>'; + $itemcount ++; + } else { + my %titles = &ssl_titles(); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @alldoms = &Apache::lonnet::all_domains(); + my %serverhomes = %Apache::lonnet::serverhomeIDs; + my @domservers = &Apache::lonnet::get_servers($dom); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %altids = &id_for_thisdom(%servers); + if (($position eq 'connto') || ($position eq 'connfrom')) { + my $legacy; + unless (ref($settings) eq 'HASH') { + my $name; + if ($position eq 'connto') { + $name = 'loncAllowInsecure'; + } else { + $name = 'londAllowInsecure'; + } + my $primarylibserv = &Apache::lonnet::domain($dom,'primary'); + my @ids=&Apache::lonnet::current_machine_ids(); + if (($primarylibserv ne '') && (!grep(/^\Q$primarylibserv\E$/,@ids))) { + my %what = ( + $name => 1, + ); + my ($result,$returnhash) = + &Apache::lonnet::get_remote_globals($primarylibserv,\%what); + if ($result eq 'ok') { + if (ref($returnhash) eq 'HASH') { + $legacy = $returnhash->{$name}; + } + } + } else { + $legacy = $Apache::lonnet::perlvar{$name}; + } + } + foreach my $type ('dom','intdom','other') { + my %checked; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= '<tr'.$css_class.'><td>'.$titles{$type}.'</td>'. + '<td class="LC_right_item">'; + my $skip; + if ($type eq 'dom') { + unless (keys(%servers) > 1) { + $datatable .= &mt('Nothing to set here, as there are no other servers/VMs'); + $skip = 1; + } + } + if ($type eq 'intdom') { + unless (@instdoms > 1) { + $datatable .= &mt('Nothing to set here, as there are no other domains for this institution'); + $skip = 1; + } + } elsif ($type eq 'other') { + if (keys(%by_location) == 0) { + $datatable .= &mt('Nothing to set here, as there are no other institutions'); + $skip = 1; + } + } + unless ($skip) { + $checked{'yes'} = ' checked="checked"'; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$position}) eq 'HASH') { + if ($settings->{$position}->{$type} =~ /^(no|req)$/) { + $checked{$1} = $checked{'yes'}; + delete($checked{'yes'}); + } + } + } else { + if ($legacy == 0) { + $checked{'req'} = $checked{'yes'}; + delete($checked{'yes'}); + } + } + foreach my $option ('no','yes','req') { + $datatable .= '<span class="LC_nobreak"><label>'. + '<input type="radio" name="'.$position.'_'.$type.'" '. + 'value="'.$option.'"'.$checked{$option}.' />'.$titles{$option}. + '</label></span>'.(' 'x2); } - $datatable .= '</tr></table>'; } $datatable .= '</td></tr>'; + $itemcount ++; + } + } else { + my $prefix = 'replication'; + my @types = ('certreq','nocertreq'); + if (keys(%by_location) == 0) { + $datatable .= '<tr'.$css_class.'><td>'. + &mt('Nothing to set here, as there are no other institutions'). + '</td></tr>'; $itemcount ++; + } else { + ($datatable,$itemcount) = + &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles); } } } @@ -5905,10 +8932,60 @@ sub print_usersessions { return $datatable; } +sub ssl_titles { + return &Apache::lonlocal::texthash ( + dom => 'LON-CAPA servers/VMs from same domain', + intdom => 'LON-CAPA servers/VMs from same "internet" domain', + other => 'External LON-CAPA servers/VMs', + connto => 'Connections to other servers', + connfrom => 'Connections from other servers', + replication => 'Replicating content to other institutions', + certreq => 'Client certificate required, but specific domains exempt', + nocertreq => 'No client certificate required, except for specific domains', + no => 'SSL not used', + yes => 'SSL Optional (used if available)', + req => 'SSL Required', + ); +} + +sub print_trust { + my ($prefix,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable,%checked,%choices); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my $itemcount = 1; + my %titles = &trust_titles(); + my @types = ('exc','inc'); + if ($prefix eq 'top') { + $prefix = 'content'; + } elsif ($prefix eq 'bottom') { + $prefix = 'msg'; + } + ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles); + $$rowtotal += $itemcount; + return $datatable; +} + +sub trust_titles { + return &Apache::lonlocal::texthash( + content => "Access to this domain's content by others", + shared => "Access to other domain's content by this domain", + enroll => "Enrollment in this domain's courses by others", + othcoau => "Co-author roles in this domain for others", + coaurem => "Co-author roles for this domain's users elsewhere", + domroles => "Domain roles in this domain assignable to others", + catalog => "Course Catalog for this domain displayed elsewhere", + reqcrs => "Requests for creation of courses in this domain by others", + msg => "Users in other domains can send messages to this domain", + exc => "Allow all, but exclude specific domains", + inc => "Deny all, but include specific domains", + ); +} + sub build_location_hashes { - my ($intdoms,$by_ip,$by_location) = @_; + my ($intdoms,$by_ip,$by_location,$instdoms) = @_; return unless((ref($intdoms) eq 'ARRAY') && (ref($by_ip) eq 'HASH') && - (ref($by_location) eq 'HASH')); + (ref($by_location) eq 'HASH') && (ref($instdoms) eq 'ARRAY')); my %iphost = &Apache::lonnet::get_iphost(); my $primary_id = &Apache::lonnet::domain($env{'request.role.domain'},'primary'); my $primary_ip = &Apache::lonnet::get_host_ip($primary_id); @@ -5925,7 +9002,13 @@ sub build_location_hashes { foreach my $id (@{$iphost{$ip}}) { my $location = &Apache::lonnet::internet_dom($id); if ($location) { - next if (grep(/^\Q$location\E$/,@{$intdoms})); + if (grep(/^\Q$location\E$/,@{$intdoms})) { + my $dom = &Apache::lonnet::host_domain($id); + unless (grep(/^\Q$dom\E/,@{$instdoms})) { + push(@{$instdoms},$dom); + } + next; + } if (ref($by_ip->{$ip}) eq 'ARRAY') { unless(grep(/^\Q$location\E$/,@{$by_ip->{$ip}})) { push(@{$by_ip->{$ip}},$location); @@ -6245,7 +9328,7 @@ sub print_loadbalancing { my $disabled_div_style = 'display: block'; my $homedom_div_style = 'display: none'; $datatable .= '<tr class="'.$css_class[$cssidx].'">'. - '<td rowspan="'.$rownum.'" valign="top">'. + '<td rowspan="'.$rownum.'" style="vertical-align: top">'. '<p>'; if ($lonhost eq '') { $datatable .= '<span class="LC_nobreak">'; @@ -6278,7 +9361,7 @@ sub print_loadbalancing { $homedom_div_style = 'display: block'; } } - $datatable .= '</p></td><td rowspan="'.$rownum.'" valign="top">'. + $datatable .= '</p></td><td rowspan="'.$rownum.'" style="vertical-align: top">'. '<div id="loadbalancing_disabled_'.$balnum.'" style="'. $disabled_div_style.'">'.$disabledtext.'</div>'."\n". '<div id="loadbalancing_targets_'.$balnum.'" style="'.$targets_div_style.'">'.&mt('Offloads to:').'<br />'; @@ -6289,7 +9372,7 @@ sub print_loadbalancing { no => ' checked="checked"', ); my %balcookiechecked = ( - no => ' checked="checked"', + no => ' checked="checked"', ); foreach my $sparetype (@sparestypes) { my $targettable; @@ -6507,10 +9590,10 @@ sub loadbalance_rule_row { } my $space; if ($islast && $num == 1) { - $space = '<div display="inline-block"> </div>'; + $space = '<div style="display:inline-block;"> </div>'; } my $output = - '<tr class="'.$css_class.'" id="balanceruletr_'.$balnum.'_'.$num.'"><td valign="top">'.$space. + '<tr class="'.$css_class.'" id="balanceruletr_'.$balnum.'_'.$num.'"><td style="vertical-align: top">'.$space. '<div id="balanceruletitle_'.$balnum.'_'.$type.'" style="'.$style.'">'.$title.'</div></td>'."\n". '<td valaign="top">'.$space. '<div id="balancerule_'.$balnum.'_'.$type.'" style="'.$style.'">'."\n"; @@ -6646,10 +9729,12 @@ sub tool_titles { blog => 'Blog', webdav => 'WebDAV', portfolio => 'Portfolio', + timezone => 'Can set time zone', official => 'Official courses (with institutional codes)', unofficial => 'Unofficial courses', community => 'Communities', textbook => 'Textbook courses', + placement => 'Placement tests', ); return %titles; } @@ -6660,8 +9745,10 @@ sub courserequest_titles { unofficial => 'Unofficial', community => 'Communities', textbook => 'Textbook', + placement => 'Placement tests', + lti => 'LTI Provider', norequest => 'Not allowed', - approval => 'Approval by Dom. Coord.', + approval => 'Approval by DC', validate => 'With validation', autolimit => 'Numerical limit', unlimited => '(blank for unlimited)', @@ -6750,7 +9837,7 @@ sub print_usercreation { } $datatable .= '<tr'.$css_class.'>'. '<td><span class="LC_nobreak">'.$lt{$item}. - '</span></td><td align="right">'; + '</span></td><td style="text-align: right">'; my @options = ('any'); if (ref($rules) eq 'HASH') { if (keys(%{$rules}) > 0) { @@ -6773,7 +9860,7 @@ sub print_usercreation { } } else { my @contexts = ('author','course','domain'); - my @authtypes = ('int','krb4','krb5','loc'); + my @authtypes = ('int','krb4','krb5','loc','lti'); my %checked; if (ref($settings) eq 'HASH') { if (ref($settings->{'authtypes'}) eq 'HASH') { @@ -6872,7 +9959,7 @@ sub print_selfcreation { ($datatable,$itemcount) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked, \%choices,$itemcount,$onclick); $$rowtotal += $itemcount; - + if (ref($usertypes) eq 'HASH') { if (keys(%{$usertypes}) > 0) { $datatable .= &insttypes_row($createsettings,$types,$usertypes, @@ -7009,7 +10096,7 @@ sub print_selfcreation { my $currstyle = 'display:none'; if (grep(/^\Q$status\E$/,@ordered)) { $currstyle = $rowstyle; - $hidden = 0; + $hidden = 0; } $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain, $emailrules,$emailruleorder,$settings,$status,$rowid, @@ -7036,8 +10123,8 @@ sub print_selfcreation { foreach my $status (@posstypes) { my $rowid = $classprefix.$status; my $datarowstyle = 'display:none'; - if (grep(/^\Q$status\E$/,@ordered)) { - $datarowstyle = $rowstyle; + if (grep(/^\Q$status\E$/,@ordered)) { + $datarowstyle = $rowstyle; } $datatable .= &modifiable_userdata_row('cancreate','emailusername_'.$status,$settings, $numinrow,$$rowtotal,\%usertypeshash,$infofields, @@ -7139,7 +10226,7 @@ function toggleEmailOptions(form,radio,p document.getElementById(altprefix+'_inst_'+status).style.display = 'none'; document.getElementById(altprefix+'_noninst_'+status).style.display = 'none'; if (curr == 'custom') { - if (prefix) { + if (prefix) { document.getElementById(prefix+'_'+status).style.display = 'inline'; } } else if (curr == 'inst') { @@ -7162,10 +10249,10 @@ ENDSCRIPT sub noninst_users { my ($processing,$emailverified,$emailoptions,$emaildomain,$emailrules, - $emailruleorder,$settings,$type,$rowid,$typetitle,$css_class,$rowstyle,$intdom) = @_; + $emailruleorder,$settings,$type,$rowid,$typetitle,$css_class,$rowstyle,$intdom) = @_; my $class = 'LC_left_item'; if ($css_class) { - $css_class = ' class="'.$css_class.'"'; + $css_class = ' class="'.$css_class.'"'; } if ($rowid) { $rowid = ' id="'.$rowid.'"'; @@ -7180,10 +10267,10 @@ sub noninst_users { $description = &mt('Requests for: [_1] (status self-reported)',$typetitle); } $output = '<tr'.$css_class.$rowid.$rowstyle.'>'. - "<td>$description</td>\n". + "<td>$description</td>\n". '<td class="'.$class.'" colspan="2">'. '<table><tr>'; - my %headers = &Apache::lonlocal::texthash( + my %headers = &Apache::lonlocal::texthash( approve => 'Processing', email => 'E-mail', username => 'Username', @@ -7193,7 +10280,7 @@ sub noninst_users { } $output .= '</tr><tr>'; foreach my $item ('approve','email','username') { - $output .= '<td valign="top">'; + $output .= '<td style="vertical-align: top">'; my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom); if ($item eq 'approve') { %choices = &Apache::lonlocal::texthash ( @@ -7308,7 +10395,7 @@ sub noninst_users { my $value; if (ref($emaildomain) eq 'HASH') { if (ref($emaildomain->{$type}) eq 'HASH') { - $value = $emaildomain->{$type}->{$option}; + $value = $emaildomain->{$type}->{$option}; } } if ($value eq '') { @@ -7335,7 +10422,7 @@ sub noninst_users { sub captcha_choice { my ($context,$settings,$itemcount,$customcss,$rowstyle) = @_; my ($keyentry,$currpub,$currpriv,%checked,$rowname,$pubtext,$privtext, - $vertext,$currver); + $vertext,$currver); my %lt = &captcha_phrases(); $keyentry = 'hidden'; my $colspan=2; @@ -7424,7 +10511,7 @@ sub user_formats_row { 'username' => 'new usernames', 'id' => 'IDs', ); - unless ($type eq 'email') { + unless (($type eq 'email') || ($type eq 'unamemap')) { my $css_class = $rowcount%2?' class="LC_odd_row"':''; $output = '<tr '.$css_class.'>'. '<td><span class="LC_nobreak">'. @@ -7479,9 +10566,9 @@ sub user_formats_row { } elsif ($colsleft == 1) { $output .= '<td class="LC_left_item"> </td>'; } - $output .= '</tr></table>'; - unless ($type eq 'email') { - $output .= '</td></tr>'; + $output .= '</tr>'; + unless (($type eq 'email') || ($type eq 'unamemap')) { + $output .= '</table></td></tr>'; } return $output; } @@ -7517,6 +10604,7 @@ sub authtype_names { krb4 => 'Kerberos 4', krb5 => 'Kerberos 5', loc => 'Local', + lti => 'LTI', ); return %lt; } @@ -7585,12 +10673,13 @@ sub print_defaults { '<td><span class="LC_nobreak">'.$titles->{$item}. '</span></td><td class="LC_right_item" colspan="3">'; if ($item eq 'auth_def') { - my @authtypes = ('internal','krb4','krb5','localauth'); + my @authtypes = ('internal','krb4','krb5','localauth','lti'); my %shortauth = ( internal => 'int', krb4 => 'krb4', krb5 => 'krb5', - localauth => 'loc' + localauth => 'loc', + lti => 'lti', ); my %authnames = &authtype_names(); foreach my $auth (@authtypes) { @@ -7611,18 +10700,34 @@ sub print_defaults { } elsif ($item eq 'lang_def') { my $includeempty = 1; $datatable .= &Apache::loncommon::select_language($item,$defaults{$item},$includeempty); - } else { - my $size; - if ($item eq 'portal_def') { - $size = ' size="25"'; - } + } elsif ($item eq 'portal_def') { $datatable .= '<input type="text" name="'.$item.'" value="'. - $defaults{$item}.'"'.$size.' />'; + $defaults{$item}.'" size="25" onkeyup="portalExtras(this);" />'; + my $portalsty = 'none'; + if ($defaults{$item}) { + $portalsty = 'block'; + } + foreach my $field ('email','web') { + my $checkedoff = ' checked="checked"'; + my $checkedon; + if ($defaults{$item.'_'.$field}) { + $checkedon = $checkedoff; + $checkedoff = ''; + } + $datatable .= '<div id="'.$item.'_'.$field.'_div" style="display:'.$portalsty.'">'. + '<span class="LC_nobreak">'.$titles->{$field}.' '. + '<label><input type="radio" name="'.$item.'_'.$field.'" value="1"'.$checkedon.'/>'.&mt('Yes').'</label>'. + (' 'x2). + '<label><input type="radio" name="'.$item.'_'.$field.'" value="0"'.$checkedoff.'/>'.&mt('No').'</label>'. + '</div>'; + } + } else { + $datatable .= '<input type="text" name="'.$item.'" value="'.$defaults{$item}.'" />'; } $datatable .= '</td></tr>'; $rownum ++; } - } else { + } elsif ($position eq 'middle') { my %defaults; if (ref($settings) eq 'HASH') { if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) { @@ -7646,7 +10751,7 @@ sub print_defaults { $datatable .= '</select> '.&mt('Internal ID:').' <b>'.$item.'</b> '. '<input type="checkbox" name="inststatus_delete" value="'.$item.'" />'. &mt('delete').'</span></td>'. - '<td class="LC_left_item"><span class="LC_nobreak">'.&mt('Name displayed').':'. + '<td class="LC_left_item" colspan="2"><span class="LC_nobreak">'.&mt('Name displayed').':'. '<input type="text" size="20" name="inststatus_title_'.$item.'" value="'.$title.'" />'. '</span></td></tr>'; } @@ -7665,13 +10770,29 @@ sub print_defaults { $datatable .= '</select> '.&mt('Internal ID:'). '<input type="text" size="10" name="addinststatus" value="" />'. ' '.&mt('(new)'). - '</span></td><td class="LC_left_item"><span class="LC_nobreak">'. + '</span></td><td class="LC_left_item" colspan="2"><span class="LC_nobreak">'. &mt('Name displayed').':'. '<input type="text" size="20" name="addinststatus_title" value="" /></span></td>'. '</tr>'."\n"; $rownum ++; } } + } else { + my ($unamemaprules,$ruleorder) = + &Apache::lonnet::inst_userrules($dom,'unamemap'); + $css_class = $rownum%2?' class="LC_odd_row"':''; + if ((ref($unamemaprules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) { + my $numinrow = 2; + $datatable .= '<tr'.$css_class.'><td>'.&mt('Available conversions').'</td><td><table>'. + &user_formats_row('unamemap',$settings,$unamemaprules, + $ruleorder,$numinrow). + '</table></td></tr>'; + } + if ($datatable eq '') { + $datatable .= '<tr'.$css_class.'><td colspan="2">'. + &mt('No rules set for domain in customized localenroll.pm'). + '</td></tr>'; + } } $$rowtotal += $rownum; return $datatable; @@ -7697,6 +10818,8 @@ sub defaults_titles { 'timezone_def' => 'Default timezone', 'datelocale_def' => 'Default locale for dates', 'portal_def' => 'Portal/Default URL', + 'email' => 'Email links use portal URL', + 'web' => 'Public web links use portal URL', 'intauth_cost' => 'Encryption cost for bcrypt (positive integer)', 'intauth_check' => 'Check bcrypt cost if authenticated', 'intauth_switch' => 'Existing crypt-based switched to bcrypt on authentication', @@ -7882,7 +11005,7 @@ sub print_scantronformat { } $datatable .= '</span></td>'; if (keys(%error) == 0) { - $datatable .= '<td valign="bottom">'; + $datatable .= '<td style="vertical-align: bottom">'; if (!$switchserver) { $datatable .= &mt('Upload:').'<br />'; } @@ -8117,6 +11240,10 @@ sub print_coursecategories { my $toggle_catscomm_dom = ' checked="checked" '; my $can_catcomm_comm = ' '; my $can_catcomm_dom = ' checked="checked" '; + my $toggle_catsplace_place = ' '; + my $toggle_catsplace_dom = ' checked="checked" '; + my $can_catplace_place = ' '; + my $can_catplace_dom = ' checked="checked" '; if (ref($settings) eq 'HASH') { if ($settings->{'togglecats'} eq 'crs') { @@ -8135,17 +11262,28 @@ sub print_coursecategories { $can_catcomm_comm = $can_catcomm_dom; $can_catcomm_dom = ' '; } + if ($settings->{'togglecatsplace'} eq 'place') { + $toggle_catsplace_place = $toggle_catsplace_dom; + $toggle_catsplace_dom = ' '; + } + if ($settings->{'categorizeplace'} eq 'place') { + $can_catplace_place = $can_catplace_dom; + $can_catplace_dom = ' '; + } } my %title = &Apache::lonlocal::texthash ( - togglecats => 'Show/Hide a course in catalog', - togglecatscomm => 'Show/Hide a community in catalog', - categorize => 'Assign a category to a course', - categorizecomm => 'Assign a category to a community', + togglecats => 'Show/Hide a course in catalog', + togglecatscomm => 'Show/Hide a community in catalog', + togglecatsplace => 'Show/Hide a placement test in catalog', + categorize => 'Assign a category to a course', + categorizecomm => 'Assign a category to a community', + categorizeplace => 'Assign a category to a placement test', ); my %level = &Apache::lonlocal::texthash ( - dom => 'Set in Domain', - crs => 'Set in Course', - comm => 'Set in Community', + dom => 'Set in Domain', + crs => 'Set in Course', + comm => 'Set in Community', + place => 'Set in Placement Test', ); $datatable = '<tr class="LC_odd_row">'. '<td>'.$title{'togglecats'}.'</td>'. @@ -8175,8 +11313,22 @@ sub print_coursecategories { $can_catcomm_dom.' value="dom" />'.$level{'dom'}.'</label> '. '<label><input type="radio" name="categorizecomm"'. $can_catcomm_comm.'value="comm" />'.$level{'comm'}.'</label></span></td>'. + '</tr><tr class="LC_odd_row">'. + '<td>'.$title{'togglecatsplace'}.'</td>'. + '<td class="LC_right_item"><span class="LC_nobreak"><label>'. + '<input type="radio" name="togglecatsplace"'. + $toggle_catsplace_dom.' value="dom" />'.$level{'dom'}.'</label> '. + '<label><input type="radio" name="togglecatscomm"'. + $toggle_catsplace_place.' value="comm" />'.$level{'place'}.'</label></span></td>'. + '</tr><tr>'. + '<td>'.$title{'categorizeplace'}.'</td>'. + '<td class="LC_right_item"><span class="LC_nobreak">'. + '<label><input type="radio" name="categorizeplace"'. + $can_catplace_dom.' value="dom" />'.$level{'dom'}.'</label> '. + '<label><input type="radio" name="categorizeplace"'. + $can_catplace_place.'value="place" />'.$level{'place'}.'</label></span></td>'. '</tr>'; - $$rowtotal += 4; + $$rowtotal += 6; } else { my $css_class; my $itemcount = 1; @@ -8201,12 +11353,15 @@ sub print_coursecategories { my %default_names = ( instcode => &mt('Official courses'), communities => &mt('Communities'), + placement => &mt('Placement Tests'), ); if ((!grep(/^instcode$/,@{$cats[0]})) || ($cathash->{'instcode::0'} eq '') || (!grep(/^communities$/,@{$cats[0]})) || - ($cathash->{'communities::0'} eq '')) { + ($cathash->{'communities::0'} eq '') || + (!grep(/^placement$/,@{$cats[0]})) || + ($cathash->{'placement::0'} eq '')) { $maxnum ++; } my $lastidx; @@ -8227,7 +11382,7 @@ sub print_coursecategories { $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>'; } $datatable .= '</select></span></td><td>'; - if ($parent eq 'instcode' || $parent eq 'communities') { + if ($parent eq 'instcode' || $parent eq 'communities' || $parent eq 'placement') { $datatable .= '<span class="LC_nobreak">' .$default_names{$parent}.'</span>'; if ($parent eq 'instcode') { @@ -8250,7 +11405,7 @@ sub print_coursecategories { $datatable .= '<label><input type="radio" name="' .$parent.'" value="0" />' .&mt('Do not display').'</label></span>'; - if ($parent eq 'communities') { + if (($parent eq 'communities') || ($parent eq 'placement')) { $datatable .= '</td></tr></table>'; } $datatable .= '</td>'; @@ -8282,7 +11437,7 @@ sub print_coursecategories { .'<input type="text" size="20" name="addcategory_name" value="" /></td>' .'</tr>'."\n"; $itemcount ++; - foreach my $default ('instcode','communities') { + foreach my $default ('instcode','communities','placement') { if ((!grep(/^\Q$default\E$/,@{$cats[0]})) || ($cathash->{$default.'::0'} eq '')) { $css_class = $itemcount%2?' class="LC_odd_row"':''; my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','$lastidx'".');"'; @@ -8371,14 +11526,31 @@ sub print_serverstatuses { sub serverstatus_pages { return ('userstatus','lonstatus','loncron','server-status','codeversions', - 'checksums','clusterstatus','metadata_keywords','metadata_harvest', - 'takeoffline','takeonline','showenv','toggledebug','ping','domconf', - 'uniquecodes','diskusage','coursecatalog'); + 'checksums','clusterstatus','certstatus','metadata_keywords', + 'metadata_harvest','takeoffline','takeonline','showenv','toggledebug', + 'ping','domconf','uniquecodes','diskusage','coursecatalog'); } sub defaults_javascript { my ($settings) = @_; return unless (ref($settings) eq 'HASH'); + my $portal_js = <<"ENDPORTAL"; + +function portalExtras(caller) { + var x = caller.value; + var y = new Array('email','web'); + for (var i=0; i<y.length; i++) { + if (document.getElementById('portal_def_'+y[i]+'_div')) { + var z = document.getElementById('portal_def_'+y[i]+'_div'); + if (x.length > 0) { + z.style.display = 'block'; + } else { + z.style.display = 'none'; + } + } + } +} +ENDPORTAL if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) { my $maxnum = scalar(@{$settings->{'inststatusorder'}}); if ($maxnum eq '') { @@ -8432,25 +11604,47 @@ $jstext return; } +$portal_js + +// ]]> +</script> + +ENDSCRIPT + } else { +return <<"ENDSCRIPT"; +<script type="text/javascript"> +// <![CDATA[ +$portal_js // ]]> </script> ENDSCRIPT } + return; } sub passwords_javascript { - 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).', - ); + my ($prefix) = @_; + my %intalert; + if ($prefix eq 'passwords') { + %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).', + ); + } elsif ($prefix eq 'secrets') { + %intalert = &Apache::lonlocal::texthash ( + passmin => 'Warning: minimum secret length must be a positive integer greater than 6.', + passmax => 'Warning: maximum secret length must be a positive integer (or blank).', + ); + } &js_escape(\%intalert); my $defmin = $Apache::lonnet::passwdmin; - my $intauthjs = <<"ENDSCRIPT"; + my $intauthjs; + if ($prefix eq 'passwords') { $intauthjs = <<"ENDSCRIPT"; function warnIntAuth(field) { if (field.name == 'intauth_check') { @@ -8470,11 +11664,17 @@ function warnIntAuth(field) { return; } -function warnIntPass(field) { +ENDSCRIPT + + } + + $intauthjs .= <<"ENDSCRIPT"; + +function warnInt$prefix(field) { field.value.replace(/^\s+/,''); field.value.replace(/\s+\$/,''); var regexdigit=/^\\d+\$/; - if (field.name == 'passwords_min') { + if (field.name == '${prefix}_min') { if (field.value == '') { alert('$intalert{passmin}'); field.value = '$defmin'; @@ -8494,8 +11694,8 @@ function warnIntPass(field) { field.value = ''; } if (field.value != '') { - if (field.name == 'passwords_expire') { - var regexpposnum=/^\\d+(|\\.\\d*)\$/; + if (field.name == '${prefix}_expire') { + var regexpposnum=/^\\d+(|\\.\\d*)\$/; if (!regexpposnum.test(field.value)) { alert('$intalert{passexp}'); field.value = ''; @@ -8508,10 +11708,10 @@ function warnIntPass(field) { } } else { if (!regexdigit.test(field.value)) { - if (field.name == 'passwords_max') { + if (field.name == '${prefix}_max') { alert('$intalert{passmax}'); } else { - if (field.name == 'passwords_numsaved') { + if (field.name == '${prefix}_numsaved') { alert('$intalert{passnum}'); } } @@ -8551,9 +11751,11 @@ sub coursecategories_javascript { } my $instcode_reserved = &mt('The name: [_1] is a reserved category.','"instcode"'); my $communities_reserved = &mt('The name: [_1] is a reserved category.','"communities"'); + my $placement_reserved = &mt('The name: [_1] is a reserved category.','"placement"'); my $choose_again = "\n".&mt('Please use a different name for the new top level category.'); &js_escape(\$instcode_reserved); &js_escape(\$communities_reserved); + &js_escape(\$placement_reserved); &js_escape(\$choose_again); $output = <<"ENDSCRIPT"; <script type="text/javascript"> @@ -8623,6 +11825,10 @@ function categoryCheck(form) { alert('$communities_reserved\\n$choose_again'); return false; } + if (form.elements['addcategory_name'].value == 'placement') { + alert('$placement_reserved\\n$choose_again'); + return false; + } return true; } @@ -8639,21 +11845,25 @@ sub initialize_categories { my %default_names = &Apache::lonlocal::texthash ( instcode => 'Official courses (with institutional codes)', communities => 'Communities', + placement => 'Placement Tests', ); - my $select0 = ' selected="selected"'; - my $select1 = ''; - foreach my $default ('instcode','communities') { + my %selnum = ( + instcode => '0', + communities => '1', + placement => '2', + ); + my %selected; + foreach my $default ('instcode','communities','placement') { $css_class = $itemcount%2?' class="LC_odd_row"':''; $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','0'".');"'; - if ($default eq 'communities') { - $select1 = $select0; - $select0 = ''; - } + map { $selected{$selnum{$_}} = '' } keys(%selnum); + $selected{$selnum{$default}} = ' selected="selected"'; $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">' - .'<select name="'.$default.'_pos">' - .'<option value="0"'.$select0.'>1</option>' - .'<option value="1"'.$select1.'>2</option>' - .'<option value="2">3</option></select> ' + .'<select name="'.$default.'_pos"'.$chgstr.'>' + .'<option value="0"'.$selected{'0'}.'>1</option>' + .'<option value="1"'.$selected{'1'}.'>2</option>' + .'<option value="2"'.$selected{'2'}.'>3</option>' + .'<option value="3">4</option></select> ' .$default_names{$default} .'</span></td><td><span class="LC_nobreak">' .'<label><input type="radio" name="'.$default.'" value="1" checked="checked" />' @@ -8668,7 +11878,8 @@ sub initialize_categories { .'<select name="addcategory_pos"'.$chgstr.'>' .'<option value="0">1</option>' .'<option value="1">2</option>' - .'<option value="2" selected="selected">3</option></select> ' + .'<option value="2">3</option>' + .'<option value="3" selected="selected">4</option></select> ' .&mt('Add category').'</span></td><td><span class="LC_nobreak">'.&mt('Name:') .' <input type="text" size="20" name="addcategory_name" value="" /></span>' .'</td></tr>'; @@ -8759,7 +11970,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') { @@ -8780,6 +11991,10 @@ sub modifiable_userdata_row { } else { $rolename = $role; } + } 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'); @@ -8817,42 +12032,50 @@ sub modifiable_userdata_row { if ($rowid) { $rowid = ' id="'.$rowid.'"'; } - $output = '<tr '.$css_class.$rowid.'>'. '<td><span class="LC_nobreak">'.$rolename.'</span></td>'. '<td class="LC_left_item" colspan="2"><table>'; my $rem; my %checks; + my %current; if (ref($settings) eq 'HASH') { - if (ref($settings->{$context}) eq 'HASH') { + my $hashref; + if ($context eq 'lti') { + 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') { - my $hashref = $settings->{$context}->{$role}; - if ($role eq 'emailusername') { - if ($statustype) { - if (ref($settings->{$context}->{$role}->{$statustype}) eq 'HASH') { - $hashref = $settings->{$context}->{$role}->{$statustype}; - if (ref($hashref) eq 'HASH') { - foreach my $field (@fields) { - if ($hashref->{$field}) { - $checks{$field} = $hashref->{$field}; - } - } - } - } + $hashref = $settings->{'lti_instdata'}; + } + if ($role eq 'emailusername') { + if ($statustype) { + if (ref($settings->{$context}->{$role}->{$statustype}) eq 'HASH') { + $hashref = $settings->{$context}->{$role}->{$statustype}; } - } else { - if (ref($hashref) eq 'HASH') { - foreach my $field (@fields) { - if ($hashref->{$field}) { - $checks{$field} = ' checked="checked" '; - } - } + } + } + } + if (ref($hashref) eq 'HASH') { + foreach my $field (@fields) { + if ($hashref->{$field}) { + if ($role eq 'emailusername') { + $checks{$field} = $hashref->{$field}; + } else { + $checks{$field} = ' checked="checked" '; } } } } } - + my $total = scalar(@fields); for (my $i=0; $i<$total; $i++) { $rem = $i%($numinrow); @@ -8866,7 +12089,25 @@ sub modifiable_userdata_row { unless ($role eq 'emailusername') { if (exists($checks{$fields[$i]})) { $check = $checks{$fields[$i]}; - } else { + } 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') { $check = ' checked="checked" '; @@ -8876,6 +12117,7 @@ sub modifiable_userdata_row { } $output .= '<td class="LC_left_item">'. '<span class="LC_nobreak">'; + my $prefix = 'canmodify'; if ($role eq 'emailusername') { unless ($checks{$fields[$i]} =~ /^(required|optional)$/) { $checks{$fields[$i]} = 'omit'; @@ -8886,13 +12128,18 @@ sub modifiable_userdata_row { $checked='checked="checked" '; } $output .= '<label>'. - '<input type="radio" name="canmodify_'.$item.'_'.$fields[$i].'" value="'.$option.'" '.$checked.'/>'. + '<input type="radio" name="'.$prefix.'_'.$item.'_'.$fields[$i].'" value="'.$option.'" '.$checked.'/>'. &mt($option).'</label>'.(' ' x2); } $output .= '<i>'.$fieldtitles{$fields[$i]}.'</i>'; } else { + if ($context eq 'lti') { + $prefix = 'lti'; + } elsif ($context eq 'privacy') { + $prefix = 'privacy'; + } $output .= '<label>'. - '<input type="checkbox" name="canmodify_'.$role.'" '. + '<input type="checkbox" name="'.$prefix.'_'.$role.'" '. 'value="'.$fields[$i].'"'.$check.'/>'.$fieldtitles{$fields[$i]}. '</label>'; } @@ -8979,7 +12226,7 @@ sub insttypes_row { $output .= '<td class="LC_left_item">'. '<span class="LC_nobreak"><label>'. '<input type="checkbox" name="'.$context.'" '. - 'value="'.$types->[$i].'"'.$check.$onclick.'/>'. + 'value="'.$types->[$i].'"'.$check.$onclick.' />'. $usertypes->{$types->[$i]}.'</label></span></td>'; } } @@ -8992,7 +12239,7 @@ sub insttypes_row { } else { $output .= '<td class="LC_left_item">'; } - $output .= ' '; + $output .= ' '; } else { if ($rem == 0) { $output .= '<tr>'; @@ -9095,7 +12342,7 @@ sub modify_login { my ($r,$dom,$confname,$lastactref,%domconfig) = @_; my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl, %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon, - %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso); + %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso); %title = ( coursecatalog => 'Display course catalog', adminmail => 'Display administrator E-mail address', helpdesk => 'Display "Contact Helpdesk" link', @@ -9119,6 +12366,7 @@ sub modify_login { $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'}; $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'}; $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'}; + $samlwindow{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'window'}; $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'}; } } @@ -9376,10 +12624,13 @@ sub modify_login { if ($env{'form.saml_img_'.$lonhost.'.filename'}) { push(@newsamlimgs,$lonhost); } - foreach my $item ('text','alt','url','title','notsso') { + foreach my $item ('text','alt','url','title','window','notsso') { $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g; } if ($saml{$lonhost}) { + if ($env{'form.saml_window_'.$lonhost} ne '1') { + $env{'form.saml_window_'.$lonhost} = ''; + } if (grep(/^\Q$lonhost\E$/,@delsamlimg)) { #FIXME Need to obsolete published image delete($currsaml{$lonhost}{'img'}); @@ -9397,25 +12648,28 @@ sub modify_login { if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) { $changes{'saml'}{$lonhost} = 1; } + if ($env{'form.saml_window_'.$lonhost} ne $samlwindow{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } if ($env{'form.saml_notsso_'.$lonhost} ne $samlnotsso{$lonhost}) { $changes{'saml'}{$lonhost} = 1; } } else { $changes{'saml'}{$lonhost} = 1; } - foreach my $item ('text','alt','url','title','notsso') { + foreach my $item ('text','alt','url','title','window','notsso') { $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost}; } } else { - if ($saml{$lonhost}) { + if ($saml{$lonhost}) { $changes{'saml'}{$lonhost} = 1; delete($currsaml{$lonhost}); } } } foreach my $posshost (keys(%currsaml)) { - unless (exists($domservers{$posshost})) { - delete($currsaml{$posshost}); + unless (exists($domservers{$posshost})) { + delete($currsaml{$posshost}); } } %{$loginhash{'login'}{'saml'}} = %currsaml; @@ -9604,19 +12858,22 @@ sub modify_login { alt => 'Alt text for button image', url => 'SSO URL', title => 'Tooltip for SSO link', + window => 'Pop-up window if iframe', notsso => 'Text for non-SSO log-in', ); foreach my $lonhost (sort(keys(%{$changes{$item}}))) { if (ref($currsaml{$lonhost}) eq 'HASH') { $resulttext .= '<li>'.&mt("$title{$item} in use for [_1]","<b>$lonhost</b>"). '<ul>'; - foreach my $key ('text','img','alt','url','title','notsso') { + foreach my $key ('text','img','alt','url','title','window','notsso') { if ($currsaml{$lonhost}{$key} eq '') { $resulttext .= '<li>'.&mt("$notlt{$key} not in use").'</li>'; } else { my $value = "'$currsaml{$lonhost}{$key}'"; if ($key eq 'img') { $value = '<img src="'.$currsaml{$lonhost}{$key}.'" />'; + } elsif ($key eq 'window') { + $value = 'On'; } $resulttext .= '<li>'.&mt("$notlt{$key} set to: [_1]", $value).'</li>'; @@ -9792,7 +13049,7 @@ sub modify_ipaccess { $possrange =~ s/,+/,/g; if ($possrange ne '') { my (@ok,$count); - $count = 0; + $count = 0; foreach my $poss (split(/\,/,$possrange)) { $count ++; $poss = &validate_ip_pattern($poss); @@ -9825,9 +13082,9 @@ sub modify_ipaccess { } } $confhash{$itemid}{'commblocks'} = {}; - + my %commblocks; - map { $commblocks{$_} = 1; } &Apache::loncommon::get_env_multiple('form.ipaccess_block_'.$idx); + map { $commblocks{$_} = 1; } &Apache::loncommon::get_env_multiple('form.ipaccess_block_'.$idx); foreach my $type (@{$typeorder}) { if ($commblocks{$type}) { $confhash{$itemid}{'commblocks'}{$type} = 'on'; @@ -9859,7 +13116,7 @@ sub modify_ipaccess { } $env{'form.ipaccess_cnum_'.$idx} =~ s/^\s+|\s+$//g; $env{'form.ipaccess_cdom_'.$idx} =~ s/^\s+|\s+$//g; - if (($env{'form.ipaccess_cnum_'.$idx} =~ /^$match_courseid$/) && + if (($env{'form.ipaccess_cnum_'.$idx} =~ /^$match_courseid$/) && ($env{'form.ipaccess_cdom_'.$idx} =~ /^$match_domain$/)) { if (&Apache::lonnet::homeserver($env{'form.ipaccess_cnum_'.$idx}, $env{'form.ipaccess_cdom_'.$idx}) eq 'no_host') { @@ -9939,7 +13196,7 @@ sub modify_ipaccess { if (keys(%{$confhash{$itemid}{'courses'}})) { my @courses; foreach my $cid (sort(keys(%{$confhash{$itemid}{'courses'}}))) { - my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1}); + my %courseinfo = &Apache::lonnet::coursedescription($cid,{'one_time' => 1}); push(@courses,$courseinfo{'description'}.' ('.$cid.')'); } $resulttext .= '<li>'.&mt('Courses/Communities allowed').':<ul><li>'. @@ -10114,7 +13371,7 @@ sub modify_colors { $domconfig->{$role} = {}; } foreach my $img (@images) { - if ($role eq 'login') { + if ($role eq 'login') { if (($img eq 'img') || ($img eq 'logo')) { if (defined($env{'form.login_showlogo_'.$img})) { $confhash->{$role}{'showlogo'}{$img} = 1; @@ -10388,7 +13645,7 @@ sub display_colorchgs { } else { my $newitem = $confhash->{$role}{$item}; if ($key eq 'images') { - $newitem = '<img src="'.$confhash->{$role}{$item}.'" alt="'.$choices{$item}.'" valign="bottom" />'; + $newitem = '<img src="'.$confhash->{$role}{$item}.'" alt="'.$choices{$item}.'" style="vertical-align: bottom" />'; } $resulttext .= '<li>'.&mt("$choices{$item} set to [_1]",$newitem).'</li>'; } @@ -10470,7 +13727,7 @@ sub check_authorstatus { sub publishlogo { my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight,$savefileas) = @_; - my ($output,$fname,$logourl); + my ($output,$fname,$logourl,$madethumb); if ($action eq 'upload') { $fname=$env{'form.'.$formname.'.filename'}; chop($env{'form.'.$formname}); @@ -10599,6 +13856,7 @@ $env{'user.name'}.':'.$env{'user.domain' $r->set_handlers('PerlCleanupHandler' => [\¬ifysubscribed,@{$handlers}]); $registered_cleanup=1; } + $madethumb = 1; } else { print $logfile "\nUnable to write ".$copyfile. ':'.$!."\n"; @@ -10611,7 +13869,7 @@ $env{'user.name'}.':'.$env{'user.domain' $output = $versionresult; } } - return ($output,$logourl); + return ($output,$logourl,$madethumb); } sub logo_versioning { @@ -10765,7 +14023,7 @@ sub modify_quotas { $context = $action; } if ($context eq 'requestcourses') { - @usertools = ('official','unofficial','community','textbook'); + @usertools = ('official','unofficial','community','textbook','placement','lti'); @options =('norequest','approval','validate','autolimit'); %validations = &Apache::lonnet::auto_courserequest_checks($dom); %titles = &courserequest_titles(); @@ -10780,7 +14038,7 @@ sub modify_quotas { @usertools = ('author'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','webdav','portfolio'); + @usertools = ('aboutme','blog','webdav','portfolio','timezone'); %titles = &tool_titles(); } my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); @@ -10814,7 +14072,7 @@ sub modify_quotas { my @approvalnotify = &Apache::loncommon::get_env_multiple('form.'.$context.'notifyapproval'); @approvalnotify = sort(@approvalnotify); $confhash{'notify'}{'approval'} = join(',',@approvalnotify); - my @crstypes = ('official','unofficial','community','textbook'); + my @crstypes = ('official','unofficial','community','textbook','placement','lti'); my @hasuniquecode = &Apache::loncommon::get_env_multiple('form.uniquecode'); foreach my $type (@hasuniquecode) { if (grep(/^\Q$type\E$/,@crstypes)) { @@ -10936,7 +14194,7 @@ sub modify_quotas { &Apache::lonnet::logthis($error); $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; } - } + } } elsif ($domconfig{$action}{$type}{$key}{'image'}) { $confhash{$type}{$key}{'image'} = $domconfig{$action}{$type}{$key}{'image'}; @@ -11488,6 +14746,2009 @@ sub process_textbook_image { return ($url,$error); } +sub modify_ltitools { + my ($r,$dom,$action,$lastactref,%domconfig) = @_; + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my $confname = $dom.'-domainconfig'; + my $servadm = $r->dir_config('lonAdmEMail'); + my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + my (%posslti,%possfield); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); + map { $posslti{$_} = 1; } @ltiroles; + my @allfields = ('fullname','firstname','lastname','email','user','roles'); + map { $possfield{$_} = 1; } @allfields; + my %lt = <itools_names(); + if ($env{'form.ltitools_add'}) { + my $title = $env{'form.ltitools_add_title'}; + $title =~ s/(`)/'/g; + ($newid,my $error) = &get_ltitools_id($dom,$title); + if ($newid) { + my $position = $env{'form.ltitools_add_pos'}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos[$position] = $newid; + } + $changes{$newid} = 1; + foreach my $item ('title','url','key','secret','lifetime') { + $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g; + if ($item eq 'lifetime') { + $env{'form.ltitools_add_'.$item} =~ s/[^\d.]//g; + } + if ($env{'form.ltitools_add_'.$item}) { + if (($item eq 'key') || ($item eq 'secret')) { + $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item}; + } else { + $confhash{$newid}{$item} = $env{'form.ltitools_add_'.$item}; + } + } + } + if ($env{'form.ltitools_add_version'} eq 'LTI-1p0') { + $confhash{$newid}{'version'} = $env{'form.ltitools_add_version'}; + } + if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') { + $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'}; + } + if ($env{'form.ltitools_add_sigmethod'} eq 'HMAC-SHA256') { + $confhash{$newid}{'sigmethod'} = $env{'form.ltitools_add_sigmethod'}; + } else { + $confhash{$newid}{'sigmethod'} = 'HMAC-SHA1'; + } + foreach my $item ('width','height','linktext','explanation') { + $env{'form.ltitools_add_'.$item} =~ s/^\s+//; + $env{'form.ltitools_add_'.$item} =~ s/\s+$//; + if (($item eq 'width') || ($item eq 'height')) { + if ($env{'form.ltitools_add_'.$item} =~ /^\d+$/) { + $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; + } + } else { + if ($env{'form.ltitools_add_'.$item} ne '') { + $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; + } + } + } + if ($env{'form.ltitools_add_target'} eq 'window') { + $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'}; + } elsif ($env{'form.ltitools_add_target'} eq 'tab') { + $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'}; + } else { + $confhash{$newid}{'display'}{'target'} = 'iframe'; + } + foreach my $item ('passback','roster') { + if ($env{'form.ltitools_'.$item.'_add'}) { + $confhash{$newid}{$item} = 1; + if ($env{'form.ltitools_'.$item.'valid_add'} ne '') { + my $lifetime = $env{'form.ltitools_'.$item.'valid_add'}; + $lifetime =~ s/^\s+|\s+$//g; + if ($lifetime =~ /^\d+\.?\d*$/) { + $confhash{$newid}{$item.'valid'} = $lifetime; + } + } + } + } + if ($env{'form.ltitools_add_image.filename'} ne '') { + my ($imageurl,$error) = + &process_ltitools_image($r,$dom,$confname,'ltitools_add_image',$newid, + $configuserok,$switchserver,$author_ok); + if ($imageurl) { + $confhash{$newid}{'image'} = $imageurl; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; + } + } + my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_add_fields'); + foreach my $field (@fields) { + if ($possfield{$field}) { + if ($field eq 'roles') { + foreach my $role (@courseroles) { + my $choice = $env{'form.ltitools_add_roles_'.$role}; + if (($choice ne '') && ($posslti{$choice})) { + $confhash{$newid}{'roles'}{$role} = $choice; + if ($role eq 'cc') { + $confhash{$newid}{'roles'}{'co'} = $choice; + } + } + } + } else { + $confhash{$newid}{'fields'}{$field} = 1; + } + } + } + if (ref($confhash{$newid}{'fields'}) eq 'HASH') { + if ($confhash{$newid}{'fields'}{'user'}) { + if ($env{'form.ltitools_userincdom_add'}) { + $confhash{$newid}{'incdom'} = 1; + } + } + } + my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig'); + foreach my $item (@courseconfig) { + $confhash{$newid}{'crsconf'}{$item} = 1; + } + if ($env{'form.ltitools_add_custom'}) { + my $name = $env{'form.ltitools_add_custom_name'}; + my $value = $env{'form.ltitools_add_custom_value'}; + $value =~ s/(`)/'/g; + $name =~ s/(`)/'/g; + $confhash{$newid}{'custom'}{$name} = $value; + } + } else { + my $error = &mt('Failed to acquire unique ID for new external tool'); + $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; + } + } + if (ref($domconfig{$action}) eq 'HASH') { + my %deletions; + my @todelete = &Apache::loncommon::get_env_multiple('form.ltitools_del'); + if (@todelete) { + map { $deletions{$_} = 1; } @todelete; + } + my %customadds; + my @newcustom = &Apache::loncommon::get_env_multiple('form.ltitools_customadd'); + if (@newcustom) { + map { $customadds{$_} = 1; } @newcustom; + } + my %imgdeletions; + my @todeleteimages = &Apache::loncommon::get_env_multiple('form.ltitools_image_del'); + if (@todeleteimages) { + map { $imgdeletions{$_} = 1; } @todeleteimages; + } + my $maxnum = $env{'form.ltitools_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.ltitools_id_'.$i}; + $itemid =~ s/\D+//g; + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($deletions{$itemid}) { + if ($domconfig{$action}{$itemid}{'image'}) { + #FIXME need to obsolete item in RES space + } + $changes{$itemid} = $domconfig{$action}{$itemid}{'title'}; + next; + } else { + my $newpos = $env{'form.ltitools_'.$itemid}; + $newpos =~ s/\D+//g; + foreach my $item ('title','url','lifetime') { + $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; + if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + foreach my $item ('key','secret') { + $encconfig{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; + if ($domconfig{$action}{$itemid}{$item} ne $encconfig{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + if ($env{'form.ltitools_version_'.$i} eq 'LTI-1p0') { + $confhash{$itemid}{'version'} = $env{'form.ltitools_version_'.$i}; + } + if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') { + $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i}; + } + if ($env{'form.ltitools_sigmethod_'.$i} eq 'HMAC-SHA256') { + $confhash{$itemid}{'sigmethod'} = $env{'form.ltitools_sigmethod_'.$i}; + } else { + $confhash{$itemid}{'sigmethod'} = 'HMAC-SHA1'; + } + if ($domconfig{$action}{$itemid}{'sigmethod'} eq '') { + if ($confhash{$itemid}{'sigmethod'} ne 'HMAC-SHA1') { + $changes{$itemid} = 1; + } + } elsif ($domconfig{$action}{$itemid}{'sigmethod'} ne $confhash{$itemid}{'sigmethod'}) { + $changes{$itemid} = 1; + } + foreach my $size ('width','height') { + $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//; + $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//; + if ($env{'form.ltitools_'.$size.'_'.$i} =~ /^\d+$/) { + $confhash{$itemid}{'display'}{$size} = $env{'form.ltitools_'.$size.'_'.$i}; + if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$size} ne $confhash{$itemid}{'display'}{$size}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$size} ne '') { + $changes{$itemid} = 1; + } + } + } + foreach my $item ('linktext','explanation') { + $env{'form.ltitools_'.$item.'_'.$i} =~ s/^\s+//; + $env{'form.ltitools_'.$item.'_'.$i} =~ s/\s+$//; + if ($env{'form.ltitools_'.$item.'_'.$i} ne '') { + $confhash{$itemid}{'display'}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; + if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$item} ne $confhash{$itemid}{'display'}{$item}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$item} ne '') { + $changes{$itemid} = 1; + } + } + } + if ($env{'form.ltitools_target_'.$i} eq 'window') { + $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i}; + } elsif ($env{'form.ltitools_target_'.$i} eq 'tab') { + $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i}; + } else { + $confhash{$itemid}{'display'}{'target'} = 'iframe'; + } + if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{'target'} ne $confhash{$itemid}{'display'}{'target'}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + foreach my $extra ('passback','roster') { + if ($env{'form.ltitools_'.$extra.'_'.$i}) { + $confhash{$itemid}{$extra} = 1; + if ($env{'form.ltitools_'.$extra.'valid_'.$i} ne '') { + my $lifetime = $env{'form.ltitools_'.$extra.'valid_'.$i}; + $lifetime =~ s/^\s+|\s+$//g; + if ($lifetime =~ /^\d+\.?\d*$/) { + $confhash{$itemid}{$extra.'valid'} = $lifetime; + } + } + } + if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) { + $changes{$itemid} = 1; + } + if ($domconfig{$action}{$itemid}{$extra.'valid'} ne $confhash{$itemid}{$extra.'valid'}) { + $changes{$itemid} = 1; + } + } + my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i); + foreach my $item ('label','title','target','linktext','explanation','append') { + if (grep(/^\Q$item\E$/,@courseconfig)) { + $confhash{$itemid}{'crsconf'}{$item} = 1; + if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'crsconf'}{$item} ne $confhash{$itemid}{'crsconf'}{$item}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } + } + my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_fields_'.$i); + foreach my $field (@fields) { + if ($possfield{$field}) { + if ($field eq 'roles') { + foreach my $role (@courseroles) { + my $choice = $env{'form.ltitools_roles_'.$role.'_'.$i}; + if (($choice ne '') && ($posslti{$choice})) { + $confhash{$itemid}{'roles'}{$role} = $choice; + if ($role eq 'cc') { + $confhash{$itemid}{'roles'}{'co'} = $choice; + } + } + if (ref($domconfig{$action}{$itemid}{'roles'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'roles'}{$role} ne $confhash{$itemid}{'roles'}{$role}) { + $changes{$itemid} = 1; + } + } elsif ($confhash{$itemid}{'roles'}{$role}) { + $changes{$itemid} = 1; + } + } + } else { + $confhash{$itemid}{'fields'}{$field} = 1; + if (ref($domconfig{$action}{$itemid}{'fields'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'fields'}{$field} ne $confhash{$itemid}{'fields'}{$field}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } + } + } + if (ref($confhash{$itemid}{'fields'}) eq 'HASH') { + if ($confhash{$itemid}{'fields'}{'user'}) { + if ($env{'form.ltitools_userincdom_'.$i}) { + $confhash{$itemid}{'incdom'} = 1; + } + if ($domconfig{$action}{$itemid}{'incdom'} ne $confhash{$itemid}{'incdom'}) { + $changes{$itemid} = 1; + } + } + } + $allpos[$newpos] = $itemid; + } + if ($imgdeletions{$itemid}) { + $changes{$itemid} = 1; + #FIXME need to obsolete item in RES space + } elsif ($env{'form.ltitools_image_'.$i.'.filename'}) { + my ($imgurl,$error) = &process_ltitools_image($r,$dom,$confname,'ltitools_image_'.$i, + $itemid,$configuserok,$switchserver, + $author_ok); + if ($imgurl) { + $confhash{$itemid}{'image'} = $imgurl; + $changes{$itemid} = 1; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; + } + } elsif ($domconfig{$action}{$itemid}{'image'}) { + $confhash{$itemid}{'image'} = + $domconfig{$action}{$itemid}{'image'}; + } + if ($customadds{$i}) { + my $name = $env{'form.ltitools_custom_name_'.$i}; + $name =~ s/(`)/'/g; + $name =~ s/^\s+//; + $name =~ s/\s+$//; + my $value = $env{'form.ltitools_custom_value_'.$i}; + $value =~ s/(`)/'/g; + $value =~ s/^\s+//; + $value =~ s/\s+$//; + if ($name ne '') { + $confhash{$itemid}{'custom'}{$name} = $value; + $changes{$itemid} = 1; + } + } + my %customdels; + my @customdeletions = &Apache::loncommon::get_env_multiple('form.ltitools_customdel_'.$i); + if (@customdeletions) { + $changes{$itemid} = 1; + } + map { $customdels{$_} = 1; } @customdeletions; + if (ref($domconfig{$action}{$itemid}{'custom'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{$action}{$itemid}{'custom'}})) { + unless ($customdels{$key}) { + if ($env{'form.ltitools_customval_'.$key.'_'.$i} ne '') { + $confhash{$itemid}{'custom'}{$key} = $env{'form.ltitools_customval_'.$key.'_'.$i}; + } + if ($domconfig{$action}{$itemid}{'custom'}{$key} ne $env{'form.ltitools_customval_'.$key.'_'.$i}) { + $changes{$itemid} = 1; + } + } + } + } + unless ($changes{$itemid}) { + foreach my $key (keys(%{$domconfig{$action}{$itemid}})) { + if (ref($domconfig{$action}{$itemid}{$key}) eq 'HASH') { + if (ref($confhash{$itemid}{$key}) eq 'HASH') { + foreach my $innerkey (keys(%{$domconfig{$action}{$itemid}{$key}})) { + unless (exists($confhash{$itemid}{$key}{$innerkey})) { + $changes{$itemid} = 1; + last; + } + } + } elsif (keys(%{$domconfig{$action}{$itemid}{$key}}) > 0) { + $changes{$itemid} = 1; + } + } + last if ($changes{$itemid}); + } + } + } + } + } + if (@allpos > 0) { + my $idx = 0; + foreach my $itemid (@allpos) { + if ($itemid ne '') { + $confhash{$itemid}{'order'} = $idx; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'order'} ne $idx) { + $changes{$itemid} = 1; + } + } + } + $idx ++; + } + } + } + my %ltitoolshash = ( + $action => { %confhash } + ); + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash, + $dom); + if ($putresult eq 'ok') { + my %ltienchash = ( + $action => { %encconfig } + ); + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); + if (keys(%changes) > 0) { + my $cachetime = 24*60*60; + my %ltiall = %confhash; + foreach my $id (keys(%ltiall)) { + if (ref($encconfig{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $ltiall{$id}{$item} = $encconfig{$id}{$item}; + } + } + } + &Apache::lonnet::do_cache_new('ltitools',$dom,\%ltiall,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'ltitools'} = 1; + } + $resulttext = &mt('Changes made:').'<ul>'; + my %bynum; + foreach my $itemid (sort(keys(%changes))) { + my $position = $confhash{$itemid}{'order'}; + $bynum{$position} = $itemid; + } + foreach my $pos (sort { $a <=> $b } keys(%bynum)) { + my $itemid = $bynum{$pos}; + if (ref($confhash{$itemid}) ne 'HASH') { + $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>'; + } else { + $resulttext .= '<li><b>'.$confhash{$itemid}{'title'}.'</b>'; + if ($confhash{$itemid}{'image'}) { + $resulttext .= ' '. + '<img src="'.$confhash{$itemid}{'image'}.'"'. + ' alt="'.&mt('Tool Provider icon').'" />'; + } + $resulttext .= '</li><ul>'; + my $position = $pos + 1; + $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>'; + foreach my $item ('version','msgtype','sigmethod','url','lifetime') { + if ($confhash{$itemid}{$item} ne '') { + $resulttext .= '<li>'.$lt{$item}.': '.$confhash{$itemid}{$item}.'</li>'; + } + } + if ($encconfig{$itemid}{'key'} ne '') { + $resulttext .= '<li>'.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'</li>'; + } + if ($encconfig{$itemid}{'secret'} ne '') { + $resulttext .= '<li>'.$lt{'secret'}.': '; + my $num = length($encconfig{$itemid}{'secret'}); + $resulttext .= ('*'x$num).'</li>'; + } + $resulttext .= '<li>'.&mt('Configurable in course:'); + my @possconfig = ('label','title','target','linktext','explanation','append'); + my $numconfig = 0; + if (ref($confhash{$itemid}{'crsconf'}) eq 'HASH') { + foreach my $item (@possconfig) { + if ($confhash{$itemid}{'crsconf'}{$item}) { + $numconfig ++; + $resulttext .= ' "'.$lt{'crs'.$item}.'"'; + } + } + } + if (!$numconfig) { + $resulttext .= ' '.&mt('None'); + } + $resulttext .= '</li>'; + foreach my $item ('passback','roster') { + $resulttext .= '<li>'.$lt{$item}.' '; + if ($confhash{$itemid}{$item}) { + $resulttext .= &mt('Yes'); + if ($confhash{$itemid}{$item.'valid'}) { + if ($item eq 'passback') { + $resulttext .= ' '.&mt('valid for at least [quant,_1,day] after launch', + $confhash{$itemid}{$item.'valid'}); + } else { + $resulttext .= ' '.&mt('valid for at least [quant,_1,second] after launch', + $confhash{$itemid}{$item.'valid'}); + } + } + } else { + $resulttext .= &mt('No'); + } + $resulttext .= '</li>'; + } + if (ref($confhash{$itemid}{'display'}) eq 'HASH') { + my $displaylist; + if ($confhash{$itemid}{'display'}{'target'}) { + $displaylist = &mt('Display target').': '. + $confhash{$itemid}{'display'}{'target'}.','; + } + foreach my $size ('width','height') { + if ($confhash{$itemid}{'display'}{$size}) { + $displaylist .= (' 'x2).$lt{$size}.': '. + $confhash{$itemid}{'display'}{$size}.','; + } + } + if ($displaylist) { + $displaylist =~ s/,$//; + $resulttext .= '<li>'.$displaylist.'</li>'; + } + foreach my $item ('linktext','explanation') { + if ($confhash{$itemid}{'display'}{$item}) { + $resulttext .= '<li>'.$lt{$item}.': '.$confhash{$itemid}{'display'}{$item}.'</li>'; + } + } + } + if (ref($confhash{$itemid}{'fields'}) eq 'HASH') { + my $fieldlist; + foreach my $field (@allfields) { + if ($confhash{$itemid}{'fields'}{$field}) { + $fieldlist .= (' 'x2).$lt{$field}.','; + } + } + if ($fieldlist) { + $fieldlist =~ s/,$//; + if ($confhash{$itemid}{'fields'}{'user'}) { + if ($confhash{$itemid}{'incdom'}) { + $fieldlist .= ' ('.&mt('username:domain').')'; + } else { + $fieldlist .= ' ('.&mt('username').')'; + } + } + $resulttext .= '<li>'.&mt('Data sent').':'.$fieldlist.'</li>'; + } + } + if (ref($confhash{$itemid}{'roles'}) eq 'HASH') { + my $rolemaps; + foreach my $role (@courseroles) { + if ($confhash{$itemid}{'roles'}{$role}) { + $rolemaps .= (' 'x2).&Apache::lonnet::plaintext($role,'Course').'='. + $confhash{$itemid}{'roles'}{$role}.','; + } + } + if ($rolemaps) { + $rolemaps =~ s/,$//; + $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>'; + } + } + if (ref($confhash{$itemid}{'custom'}) eq 'HASH') { + my $customlist; + if (keys(%{$confhash{$itemid}{'custom'}})) { + foreach my $key (sort(keys(%{$confhash{$itemid}{'custom'}}))) { + $customlist .= $key.':'.$confhash{$itemid}{'custom'}{$key}.(' 'x2); + } + } + 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_ltitools_image { + my ($r,$dom,$confname,$caller,$itemid,$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 Tool Provider (LTI) icon is not permitted to this server: [_1]', + $switchserver); + } elsif ($author_ok eq 'ok') { + my ($result,$imageurl,$madethumb) = + &publishlogo($r,'upload',$caller,$dom,$confname, + "ltitools/$itemid/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 get_ltitools_id { + my ($cdom,$title) = @_; + # get lock on ltitools db + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom); + my ($id,$error); + + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom); + } + if ($gotlock eq 'ok') { + my %currids = &Apache::lonnet::dump_dom('ltitools',$cdom); + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } + } else { + $id = 1; + } + if ($id) { + unless (&Apache::lonnet::newput_dom('ltitools',{ $id => $title },$cdom) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome = &Apache::lonnet::del_dom('ltitools',['lock'],$cdom); + } else { + $error = 'nolock'; + } + return ($id,$error); +} + +sub modify_proctoring { + my ($r,$dom,$action,$lastactref,%domconfig) = @_; + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my (@allpos,%changes,%confhash,%encconfhash,$errors,$resulttext,%imgdeletions); + my $confname = $dom.'-domainconfig'; + my $servadm = $r->dir_config('lonAdmEMail'); + my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + my %providernames = &proctoring_providernames(); + my $maxnum = scalar(keys(%providernames)); + + my (%requserfields,%optuserfields,%defaults,%extended,%crsconf,@courseroles,@ltiroles); + my ($requref,$opturef,$defref,$extref,$crsref,$rolesref,$ltiref) = &proctoring_data(); + if (ref($requref) eq 'HASH') { + %requserfields = %{$requref}; + } + if (ref($opturef) eq 'HASH') { + %optuserfields = %{$opturef}; + } + if (ref($defref) eq 'HASH') { + %defaults = %{$defref}; + } + if (ref($extref) eq 'HASH') { + %extended = %{$extref}; + } + if (ref($crsref) eq 'HASH') { + %crsconf = %{$crsref}; + } + if (ref($rolesref) eq 'ARRAY') { + @courseroles = @{$rolesref}; + } + if (ref($ltiref) eq 'ARRAY') { + @ltiroles = @{$ltiref}; + } + + if (ref($domconfig{$action}) eq 'HASH') { + my @todeleteimages = &Apache::loncommon::get_env_multiple('form.proctoring_image_del'); + if (@todeleteimages) { + map { $imgdeletions{$_} = 1; } @todeleteimages; + } + } + my %customadds; + my @newcustom = &Apache::loncommon::get_env_multiple('form.proctoring_customadd'); + if (@newcustom) { + map { $customadds{$_} = 1; } @newcustom; + } + foreach my $provider (sort(keys(%providernames))) { + $confhash{$provider} = {}; + my $pos = $env{'form.proctoring_pos_'.$provider}; + $pos =~ s/\D+//g; + $allpos[$pos] = $provider; + my (%current,%currentenc); + my $showroles = 0; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$provider}) eq 'HASH') { + %current = %{$domconfig{$action}{$provider}}; + foreach my $item ('key','secret') { + $currentenc{$item} = $current{$item}; + delete($current{$item}); + } + } + } + if ($env{'form.proctoring_available_'.$provider}) { + $confhash{$provider}{'available'} = 1; + unless ($current{'available'}) { + $changes{$provider} = 1; + } + } else { + %{$confhash{$provider}} = %current; + %{$encconfhash{$provider}} = %currentenc; + $confhash{$provider}{'available'} = 0; + if ($current{'available'}) { + $changes{$provider} = 1; + } + } + if ($confhash{$provider}{'available'}) { + foreach my $field ('lifetime','version','sigmethod','url','key','secret') { + my $possval = $env{'form.proctoring_'.$provider.'_'.$field}; + if ($field eq 'lifetime') { + if ($possval =~ /^\d+$/) { + $confhash{$provider}{$field} = $possval; + } + } elsif ($field eq 'version') { + if ($possval =~ /^\d+\.\d+$/) { + $confhash{$provider}{$field} = $possval; + } + } elsif ($field eq 'sigmethod') { + if ($possval =~ /^\QHMAC-SHA\E(1|256)$/) { + $confhash{$provider}{$field} = $possval; + } + } elsif ($field eq 'url') { + $confhash{$provider}{$field} = $possval; + } elsif (($field eq 'key') || ($field eq 'secret')) { + $encconfhash{$provider}{$field} = $possval; + unless ($currentenc{$field} eq $possval) { + $changes{$provider} = 1; + } + } + unless (($field eq 'key') || ($field eq 'secret')) { + unless ($current{$field} eq $confhash{$provider}{$field}) { + $changes{$provider} = 1; + } + } + } + if ($imgdeletions{$provider}) { + $changes{$provider} = 1; + } elsif ($env{'form.proctoring_image_'.$provider.'.filename'} ne '') { + my ($imageurl,$error) = + &process_proctoring_image($r,$dom,$confname,'proctoring_image_'.$provider,$provider, + $configuserok,$switchserver,$author_ok); + if ($imageurl) { + $confhash{$provider}{'image'} = $imageurl; + $changes{$provider} = 1; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '<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$/,@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$/,@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$/,@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$/,@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$/,@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,undef,1); + 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$/,@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); + my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my (%posslti,%posslticrs,%posscrstype); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); + my @lticourseroles = qw(Instructor TeachingAssistant Mentor Learner); + my @coursetypes = ('official','unofficial','community','textbook','placement','lti'); + my %coursetypetitles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Community', + textbook => 'Textbook', + placement => 'Placement Test', + lti => 'LTI Provider', + ); + my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); + my %lt = <i_names(); + map { $posslti{$_} = 1; } @ltiroles; + map { $posslticrs{$_} = 1; } @lticourseroles; + map { $posscrstype{$_} = 1; } @coursetypes; + + my %menutitles = <imenu_titles(); + + my (%currltisec,%secchanges,%newltisec,%newltienc,%keyset,%newkeyset); + $newltisec{'private'}{'keys'} = []; + $newltisec{'encrypt'} = {}; + $newltisec{'rules'} = {}; + $newltisec{'linkprot'} = {}; + if (ref($domconfig{'ltisec'}) eq 'HASH') { + %currltisec = %{$domconfig{'ltisec'}}; + if (ref($currltisec{'linkprot'}) eq 'HASH') { + foreach my $id (keys(%{$currltisec{'linkprot'}})) { + unless ($id =~ /^\d+$/) { + delete($currltisec{'linkprot'}{$id}); + } + } + } + if (ref($currltisec{'private'}) eq 'HASH') { + if (ref($currltisec{'private'}{'keys'}) eq 'ARRAY') { + $newltisec{'private'}{'keys'} = $currltisec{'private'}{'keys'}; + map { $keyset{$_} = 1; } @{$currltisec{'private'}{'keys'}}; + } + } + } + foreach my $item ('crs','dom','consumers') { + my $formelement; + if ($item eq 'consumers') { + $formelement = 'form.ltisec_'.$item; + } else { + $formelement = 'form.ltisec_'.$item.'linkprot'; + } + if ($env{$formelement}) { + $newltisec{'encrypt'}{$item} = 1; + if (ref($currltisec{'encrypt'}) eq 'HASH') { + unless ($currltisec{'encrypt'}{$item}) { + $secchanges{'encrypt'} = 1; + } + } else { + $secchanges{'encrypt'} = 1; + } + } elsif (ref($currltisec{'encrypt'}) eq 'HASH') { + if ($currltisec{'encrypt'}{$item}) { + $secchanges{'encrypt'} = 1; + } + } + } + unless (exists($currltisec{'rules'})) { + $currltisec{'rules'} = {}; + } + &password_rule_changes('secrets',$newltisec{'rules'},$currltisec{'rules'},\%secchanges); + + my @ids=&Apache::lonnet::current_machine_ids(); + my %servers = &Apache::lonnet::get_servers($dom,'library'); + + foreach my $hostid (keys(%servers)) { + if (($hostid ne '') && (grep(/^\Q$hostid\E$/,@ids))) { + my $newkey; + my $keyitem = 'form.ltisec_privkey_'.$hostid; + if (exists($env{$keyitem})) { + $env{$keyitem} =~ s/(`)/'/g; + if ($keyset{$hostid}) { + if ($env{'form.ltisec_changeprivkey_'.$hostid}) { + if ($env{$keyitem} ne '') { + $secchanges{'private'} = 1; + $newkeyset{$hostid} = $env{$keyitem}; + } + } + } elsif ($env{$keyitem} ne '') { + unless (grep(/^\Q$hostid\E$/,@{$newltisec{'private'}{'keys'}})) { + push(@{$newltisec{'private'}{'keys'}},$hostid); + } + $secchanges{'private'} = 1; + $newkeyset{$hostid} = $env{$keyitem}; + } + } + } + } + + my (%linkprotchg,$linkprotoutput,$is_home); + my $proterror = &Apache::courseprefs::process_linkprot($dom,'',$currltisec{'linkprot'}, + \%linkprotchg,'domain'); + my $home = &Apache::lonnet::domain($dom,'primary'); + unless (($home eq 'no_host') || ($home eq '')) { + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $id (@ids) { if ($id eq $home) { $is_home=1; } } + } + + if (keys(%linkprotchg)) { + $secchanges{'linkprot'} = 1; + my %oldlinkprot; + if (ref($currltisec{'linkprot'}) eq 'HASH') { + %oldlinkprot = %{$currltisec{'linkprot'}}; + } + foreach my $id (keys(%linkprotchg)) { + if (ref($linkprotchg{$id}) eq 'HASH') { + foreach my $inner (keys(%{$linkprotchg{$id}})) { + if (($inner eq 'secret') || ($inner eq 'key')) { + if ($is_home) { + $newltienc{$id}{$inner} = $linkprotchg{$id}{$inner}; + } + } + } + } else { + $newltisec{'linkprot'}{$id} = $linkprotchg{$id}; + } + } + $linkprotoutput = &Apache::courseprefs::store_linkprot($dom,'','domain',\%linkprotchg,\%oldlinkprot); + if (keys(%linkprotchg)) { + %{$newltisec{'linkprot'}} = %linkprotchg; + } + } + if (ref($currltisec{'linkprot'}) eq 'HASH') { + foreach my $id (%{$currltisec{'linkprot'}}) { + next if ($id !~ /^\d+$/); + unless (exists($linkprotchg{$id})) { + if (ref($currltisec{'linkprot'}{$id}) eq 'HASH') { + foreach my $inner (keys(%{$currltisec{'linkprot'}{$id}})) { + if (($inner eq 'secret') || ($inner eq 'key')) { + if ($is_home) { + $newltienc{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner}; + } + } else { + $newltisec{'linkprot'}{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner}; + } + } + } else { + $newltisec{'linkprot'}{$id} = $currltisec{'linkprot'}{$id}; + } + } + } + } + if ($proterror) { + $errors .= '<li>'.$proterror.'</li>'; + } + my (@items,%deletions,%itemids); + if ($env{'form.lti_add'}) { + my $consumer = $env{'form.lti_consumer_add'}; + $consumer =~ s/(`)/'/g; + ($newid,my $error) = &get_lti_id($dom,$consumer); + if ($newid) { + $itemids{'add'} = $newid; + push(@items,'add'); + $changes{$newid} = 1; + } else { + my $error = &mt('Failed to acquire unique ID for new LTI configuration'); + $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; + } + } + if (ref($domconfig{$action}) eq 'HASH') { + my @todelete = &Apache::loncommon::get_env_multiple('form.lti_del'); + if (@todelete) { + map { $deletions{$_} = 1; } @todelete; + } + my $maxnum = $env{'form.lti_maxnum'}; + for (my $i=0; $i<$maxnum; $i++) { + my $itemid = $env{'form.lti_id_'.$i}; + $itemid =~ s/\D+//g; + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($deletions{$itemid}) { + $changes{$itemid} = $domconfig{$action}{$itemid}{'consumer'}; + } else { + push(@items,$i); + $itemids{$i} = $itemid; + } + } + } + } + foreach my $idx (@items) { + my $itemid = $itemids{$idx}; + next unless ($itemid); + my $position = $env{'form.lti_pos_'.$itemid}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos[$position] = $itemid; + } + foreach my $item ('consumer','key','secret','lifetime','requser','crsinc') { + my $formitem = 'form.lti_'.$item.'_'.$idx; + $env{$formitem} =~ s/(`)/'/g; + if ($item eq 'lifetime') { + $env{$formitem} =~ s/[^\d.]//g; + } + if ($env{$formitem} ne '') { + if (($item eq 'key') || ($item eq 'secret')) { + $encconfig{$itemid}{$item} = $env{$formitem}; + } else { + $confhash{$itemid}{$item} = $env{$formitem}; + unless (($idx eq 'add') || ($changes{$itemid})) { + if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + } + } + } + if ($env{'form.lti_version_'.$idx} eq 'LTI-1p0') { + $confhash{$itemid}{'version'} = $env{'form.lti_version_'.$idx}; + } + if ($confhash{$itemid}{'requser'}) { + if ($env{'form.lti_mapuser_'.$idx} eq 'sourcedid') { + $confhash{$itemid}{'mapuser'} = 'lis_person_sourcedid'; + } elsif ($env{'form.lti_mapuser_'.$idx} eq 'email') { + $confhash{$itemid}{'mapuser'} = 'lis_person_contact_email_primary'; + } elsif ($env{'form.lti_mapuser_'.$idx} eq 'other') { + my $mapuser = $env{'form.lti_customuser_'.$idx}; + $mapuser =~ s/(`)/'/g; + $mapuser =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapuser'} = $mapuser; + } + my @possmakeuser = &Apache::loncommon::get_env_multiple('form.lti_makeuser_'.$idx); + my @makeuser; + foreach my $ltirole (sort(@possmakeuser)) { + if ($posslti{$ltirole}) { + push(@makeuser,$ltirole); + } + } + $confhash{$itemid}{'makeuser'} = \@makeuser; + if (@makeuser) { + my $lcauth = $env{'form.lti_lcauth_'.$idx}; + if ($lcauth =~ /^(internal|krb4|krb5|localauth)$/) { + $confhash{$itemid}{'lcauth'} = $lcauth; + if ($lcauth ne 'internal') { + my $lcauthparm = $env{'form.lti_lcauthparm_'.$idx}; + $lcauthparm =~ s/^(\s+|\s+)$//g; + $lcauthparm =~ s/`//g; + if ($lcauthparm ne '') { + $confhash{$itemid}{'lcauthparm'} = $lcauthparm; + } + } + } else { + $confhash{$itemid}{'lcauth'} = 'lti'; + } + } + my @possinstdata = &Apache::loncommon::get_env_multiple('form.lti_instdata_'.$idx); + if (@possinstdata) { + foreach my $field (@possinstdata) { + if (exists($fieldtitles{$field})) { + push(@{$confhash{$itemid}{'instdata'}}); + } + } + } + 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 ('topmenu','inlinemenu') { + if ($env{'form.lti_'.$field.'_'.$idx}) { + $confhash{$itemid}{$field} = 1; + } + } + if ($env{'form.lti_topmenu_'.$idx} || $env{'form.lti_inlinemenu_'.$idx}) { + $confhash{$itemid}{lcmenu} = []; + my @possmenu = &Apache::loncommon::get_env_multiple('form.lti_menuitem_'.$idx); + foreach my $field (@possmenu) { + if (exists($menutitles{$field})) { + if ($field eq 'grades') { + next unless ($env{'form.lti_inlinemenu_'.$idx}); + } + push(@{$confhash{$itemid}{lcmenu}},$field); + } + } + } + if ($confhash{$itemid}{'crsinc'}) { + if (($env{'form.lti_mapcrs_'.$idx} eq 'course_offering_sourcedid') || + ($env{'form.lti_mapcrs_'.$idx} eq 'context_id')) { + $confhash{$itemid}{'mapcrs'} = $env{'form.lti_mapcrs_'.$idx}; + } elsif ($env{'form.lti_mapcrs_'.$idx} eq 'other') { + my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx}; + $mapcrs =~ s/(`)/'/g; + $mapcrs =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapcrs'} = $mapcrs; + } + my @posstypes = &Apache::loncommon::get_env_multiple('form.lti_mapcrstype_'.$idx); + my @crstypes; + foreach my $type (sort(@posstypes)) { + if ($posscrstype{$type}) { + push(@crstypes,$type); + } + } + $confhash{$itemid}{'mapcrstype'} = \@crstypes; + if ($env{'form.lti_storecrs_'.$idx}) { + $confhash{$itemid}{'storecrs'} = 1; + } + if ($env{'form.lti_makecrs_'.$idx}) { + $confhash{$itemid}{'makecrs'} = 1; + } + foreach my $ltirole (@lticourseroles) { + my $possrole = $env{'form.lti_maprole_'.$ltirole.'_'.$idx}; + if (grep(/^\Q$possrole\E$/,@courseroles)) { + $confhash{$itemid}{'maproles'}{$ltirole} = $possrole; + } + } + my @possenroll = &Apache::loncommon::get_env_multiple('form.lti_selfenroll_'.$idx); + my @selfenroll; + foreach my $type (sort(@possenroll)) { + if ($posslticrs{$type}) { + push(@selfenroll,$type); + } + } + $confhash{$itemid}{'selfenroll'} = \@selfenroll; + if ($env{'form.lti_crssec_'.$idx}) { + if ($env{'form.lti_crssecsrc_'.$idx} eq 'course_section_sourcedid') { + $confhash{$itemid}{'section'} = $env{'form.lti_crssecsrc_'.$idx}; + } elsif ($env{'form.lti_crssecsrc_'.$idx} eq 'other') { + my $section = $env{'form.lti_customsection_'.$idx}; + $section =~ s/(`)/'/g; + $section =~ s/^\s+|\s+$//g; + if ($section ne '') { + $confhash{$itemid}{'section'} = $section; + } + } + } + foreach my $field ('passback','roster') { + if ($env{'form.lti_'.$field.'_'.$idx}) { + $confhash{$itemid}{$field} = 1; + } + } + if ($env{'form.lti_passback_'.$idx}) { + if ($env{'form.lti_passbackformat_'.$idx} eq '1.0') { + $confhash{$itemid}{'passbackformat'} = '1.0'; + } else { + $confhash{$itemid}{'passbackformat'} = '1.1'; + } + } + } + unless (($idx eq 'add') || ($changes{$itemid})) { + if ($confhash{$itemid}{'crsinc'}) { + foreach my $field ('mapcrs','storecrs','makecrs','section','passback','roster') { + if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { + $changes{$itemid} = 1; + } + } + unless ($changes{$itemid}) { + if ($domconfig{$action}{$itemid}{'passback'} eq $confhash{$itemid}{'passback'}) { + if ($domconfig{$action}{$itemid}{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) { + $changes{$itemid} = 1; + } + } + } + foreach my $field ('mapcrstype','selfenroll') { + unless ($changes{$itemid}) { + if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { + if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, + $confhash{$itemid}{$field}); + if (@diffs) { + $changes{$itemid} = 1; + } + } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + if (@{$confhash{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } + } + } + unless ($changes{$itemid}) { + if (ref($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') { + if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) { + if ($domconfig{$action}{$itemid}{'maproles'}{$ltirole} ne + $confhash{$itemid}{'maproles'}{$ltirole}) { + $changes{$itemid} = 1; + last; + } + } + unless ($changes{$itemid}) { + foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) { + if ($confhash{$itemid}{'maproles'}{$ltirole} ne + $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) { + $changes{$itemid} = 1; + last; + } + } + } + } elsif (keys(%{$domconfig{$action}{$itemid}{'maproles'}}) > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + unless ($changes{$itemid}) { + if (keys(%{$confhash{$itemid}{'maproles'}}) > 0) { + $changes{$itemid} = 1; + } + } + } + } + } + unless ($changes{$itemid}) { + foreach my $field ('mapuser','lcauth','lcauthparm','topmenu','inlinemenu','callback') { + if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { + $changes{$itemid} = 1; + } + } + unless ($changes{$itemid}) { + foreach my $field ('makeuser','lcmenu') { + if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { + if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, + $confhash{$itemid}{$field}); + if (@diffs) { + $changes{$itemid} = 1; + } + } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + if (@{$confhash{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } + } + } + } + } + } + } + if (@allpos > 0) { + my $idx = 0; + foreach my $itemid (@allpos) { + if ($itemid ne '') { + $confhash{$itemid}{'order'} = $idx; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'order'} ne $idx) { + $changes{$itemid} = 1; + } + } + } + $idx ++; + } + } + } + my %ltihash = ( + $action => { %confhash } + ); + my %ltienchash = ( + $action => { %encconfig } + ); + if (keys(%secchanges)) { + $ltihash{'ltisec'} = \%newltisec; + if ($secchanges{'linkprot'}) { + if ($is_home) { + $ltienchash{'linkprot'} = \%newltienc; + } + } + } + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash,$dom); + if ($putresult eq 'ok') { + my %keystore; + if (keys(%secchanges)) { + if ($secchanges{'private'}) { + my $who = &escape($env{'user.name'}.':'.$env{'user.domain'}); + foreach my $hostid (keys(%newkeyset)) { + my $storehash = { + key => $newkeyset{$hostid}, + who => $env{'user.name'}.':'.$env{'user.domain'}, + }; + $keystore{$hostid} = &Apache::lonnet::store_dom($storehash,'lti','private', + $dom,$hostid); + } + } + if (ref($lastactref) eq 'HASH') { + if (($secchanges{'encrypt'}) || ($secchanges{'private'})) { + $lastactref->{'domdefaults'} = 1; + } + } + } + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); + if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) { + return &mt('No changes made.'); + } + $resulttext = &mt('Changes made:').'<ul>'; + if (keys(%secchanges) > 0) { + foreach my $item (keys(%secchanges)) { + if ($item eq 'encrypt') { + my %encrypted = ( + crs => { + on => &mt('Encryption of stored link protection secrets defined in courses enabled'), + off => &mt('Encryption of stored link protection secrets defined in courses disabled'), + }, + dom => { + on => &mt('Encryption of stored link protection secrets defined in domain enabled'), + off => &mt('Encryption of stored link protection secrets defined in domain disabled'), + }, + consumers => { + on => &mt('Encryption of stored consumer secrets defined in domain enabled'), + off => &mt('Encryption of stored consumer secrets defined in domain disabled'), + }, + ); + foreach my $type ('crs','dom','consumers') { + my $shown = $encrypted{$type}{'off'}; + if (ref($newltisec{$item}) eq 'HASH') { + if ($newltisec{$item}{$type}) { + $shown = $encrypted{$type}{'on'}; + } + } + $resulttext .= '<li>'.$shown.'</li>'; + } + } elsif ($item eq 'rules') { + my %titles = &Apache::lonlocal::texthash( + min => 'Minimum password length', + max => 'Maximum password length', + chars => 'Required characters', + ); + foreach my $rule ('min','max') { + if ($newltisec{rules}{$rule} eq '') { + if ($rule eq 'min') { + $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule}); + ' '.&mt('Default of [_1] will be used', + $Apache::lonnet::passwdmin).'</li>'; + } else { + $resulttext .= '<li>'.&mt('[_1] set to none',$titles{$rule}).'</li>'; + } + } else { + $resulttext .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$newltisec{rules}{$rule}).'</li>'; + } + } + if (ref($newltisec{'rules'}{'chars'}) eq 'ARRAY') { + if (@{$newltisec{'rules'}{'chars'}} > 0) { + my %rulenames = &Apache::lonlocal::texthash( + uc => 'At least one upper case letter', + lc => 'At least one lower case letter', + num => 'At least one number', + spec => 'At least one non-alphanumeric', + ); + my $needed = '<ul><li>'. + join('</li><li>',map {$rulenames{$_} } @{$newltisec{'rules'}{'chars'}}). + '</li></ul>'; + $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>'; + } else { + $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>'; + } + } else { + $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>'; + } + } elsif ($item eq 'private') { + if (keys(%newkeyset)) { + foreach my $hostid (sort(keys(%newkeyset))) { + if ($keystore{$hostid} eq 'ok') { + $resulttext .= '<li>'.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'</li>'; + } + } + } + } elsif ($item eq 'linkprot') { + $resulttext .= $linkprotoutput; + } + } + } + if (keys(%changes) > 0) { + my $cachetime = 24*60*60; + my %ltiall = %confhash; + foreach my $id (keys(%ltiall)) { + if (ref($encconfig{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $ltiall{$id}{$item} = $encconfig{$id}{$item}; + } + } + } + &Apache::lonnet::do_cache_new('lti',$dom,\%ltiall,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'lti'} = 1; + } + my %bynum; + foreach my $itemid (sort(keys(%changes))) { + my $position = $confhash{$itemid}{'order'}; + $bynum{$position} = $itemid; + } + foreach my $pos (sort { $a <=> $b } keys(%bynum)) { + my $itemid = $bynum{$pos}; + if (ref($confhash{$itemid}) ne 'HASH') { + $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>'; + } else { + $resulttext .= '<li><b>'.$confhash{$itemid}{'consumer'}.'</b><ul>'; + my $position = $pos + 1; + $resulttext .= '<li>'.&mt('Order: [_1]',$position).'</li>'; + foreach my $item ('version','lifetime') { + if ($confhash{$itemid}{$item} ne '') { + $resulttext .= '<li>'.$lt{$item}.': '.$confhash{$itemid}{$item}.'</li>'; + } + } + if ($encconfig{$itemid}{'key'} ne '') { + $resulttext .= '<li>'.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'</li>'; + } + if ($encconfig{$itemid}{'secret'} ne '') { + $resulttext .= '<li>'.$lt{'secret'}.': '; + my $num = length($encconfig{$itemid}{'secret'}); + $resulttext .= ('*'x$num).'</li>'; + } + if ($confhash{$itemid}{'requser'}) { + if ($confhash{$itemid}{'callback'}) { + $resulttext .= '<li>'.&mt('Callback setting').': '.$confhash{$itemid}{'callback'}.'</li>'; + } else { + $resulttext .= '<li>'.&mt('Callback to logout LON-CAPA on log out from Consumer').'</li>'; + } + if ($confhash{$itemid}{'mapuser'}) { + my $shownmapuser; + if ($confhash{$itemid}{'mapuser'} eq 'lis_person_sourcedid') { + $shownmapuser = $lt{'sourcedid'}.' (lis_person_sourcedid)'; + } elsif ($confhash{$itemid}{'mapuser'} eq 'lis_person_contact_email_primary') { + $shownmapuser = $lt{'email'}.' (lis_person_contact_email_primary)'; + } else { + $shownmapuser = &mt('Other').' ('.$confhash{$itemid}{'mapuser'}.')'; + } + $resulttext .= '<li>'.&mt('LON-CAPA username').': '.$shownmapuser.'</li>'; + } + if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'makeuser'}} > 0) { + $resulttext .= '<li>'.&mt('Following roles may create user accounts: [_1]', + join(', ',@{$confhash{$itemid}{'makeuser'}})).'<br />'; + if ($confhash{$itemid}{'lcauth'} eq 'lti') { + $resulttext .= &mt('New users will only be able to authenticate via LTI').'</li>'; + } else { + $resulttext .= &mt('New users will be assigned LON-CAPA authentication: [_1]', + $confhash{$itemid}{'lcauth'}); + if ($confhash{$itemid}{'lcauth'} eq 'internal') { + $resulttext .= '; '.&mt('a randomly generated password will be created'); + } elsif ($confhash{$itemid}{'lcauth'} eq 'localauth') { + if ($confhash{$itemid}{'lcauthparm'} ne '') { + $resulttext .= ' '.&mt('with argument: [_1]',$confhash{$itemid}{'lcauthparm'}); + } + } else { + $resulttext .= '; '.&mt('Kerberos domain: [_1]',$confhash{$itemid}{'lcauthparm'}); + } + } + $resulttext .= '</li>'; + } else { + $resulttext .= '<li>'.&mt('User account creation not permitted.').'</li>'; + } + } + if (ref($confhash{$itemid}{'instdata'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'instdata'}} > 0) { + $resulttext .= '<li>'.&mt('Institutional data will be used when creating a new user for: [_1]', + join(', ',map { $fieldtitles{$_}; } @{$confhash{$itemid}{'instdata'}})).'</li>'; + } else { + $resulttext .= '<li>'.&mt('No institutional data used when creating a new user.').'</li>'; + } + } + foreach my $item ('topmenu','inlinemenu') { + $resulttext .= '<li>'.$lt{$item}.': '; + if ($confhash{$itemid}{$item}) { + $resulttext .= &mt('Yes'); + } else { + $resulttext .= &mt('No'); + } + $resulttext .= '</li>'; + } + if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'lcmenu'}} > 0) { + $resulttext .= '<li>'.&mt('Menu items:').' '. + join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>'; + } else { + $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>'; + } + } + if ($confhash{$itemid}{'crsinc'}) { + if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + my $rolemaps; + foreach my $role (@ltiroles) { + if ($confhash{$itemid}{'maproles'}{$role}) { + $rolemaps .= (' 'x2).$role.'='. + &Apache::lonnet::plaintext($confhash{$itemid}{'maproles'}{$role}, + 'Course').','; + } + } + if ($rolemaps) { + $rolemaps =~ s/,$//; + $resulttext .= '<li>'.&mt('Role mapping:').$rolemaps.'</li>'; + } + } + if ($confhash{$itemid}{'mapcrs'}) { + $resulttext .= '<li>'.&mt('Unique course identifier').': '.$confhash{$itemid}{'mapcrs'}.'</li>'; + } + if (ref($confhash{$itemid}{'mapcrstype'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'mapcrstype'}} > 0) { + $resulttext .= '<li>'.&mt('Mapping for the following LON-CAPA course types: [_1]', + join(', ',map { $coursetypetitles{$_}; } @coursetypes)). + '</li>'; + } else { + $resulttext .= '<li>'.&mt('No mapping to LON-CAPA courses').'</li>'; + } + } + if ($confhash{$itemid}{'storecrs'}) { + $resulttext .= '<li>'.&mt('Store mapping of course identifier to LON-CAPA CourseID').': '.$confhash{$itemid}{'storecrs'}.'</li>'; + } + if ($confhash{$itemid}{'makecrs'}) { + $resulttext .= '<li>'.&mt('Instructor may create course (if absent).').'</li>'; + } else { + $resulttext .= '<li>'.&mt('Instructor may not create course (if absent).').'</li>'; + } + if (ref($confhash{$itemid}{'selfenroll'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'selfenroll'}} > 0) { + $resulttext .= '<li>'.&mt('Self-enrollment for following roles: [_1]', + join(', ',@{$confhash{$itemid}{'selfenroll'}})). + '</li>'; + } else { + $resulttext .= '<li>'.&mt('Self-enrollment not permitted').'</li>'; + } + } + if ($confhash{$itemid}{'section'}) { + if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') { + $resulttext .= '<li>'.&mt('User section from standard field:'). + ' (course_section_sourcedid)'.'</li>'; + } else { + $resulttext .= '<li>'.&mt('User section from:').' '. + $confhash{$itemid}{'section'}.'</li>'; + } + } else { + $resulttext .= '<li>'.&mt('No section assignment').'</li>'; + } + foreach my $item ('passback','roster','topmenu','inlinemenu') { + $resulttext .= '<li>'.$lt{$item}.': '; + if ($confhash{$itemid}{$item}) { + $resulttext .= &mt('Yes'); + if ($item eq 'passback') { + if ($confhash{$itemid}{'passbackformat'} eq '1.0') { + $resulttext .= ' ('.&mt('Outcomes Extension (1.0)').')'; + } elsif ($confhash{$itemid}{'passbackformat'} eq '1.1') { + $resulttext .= ' ('.&mt('Outcomes Service (1.1)').')'; + } + } + } else { + $resulttext .= &mt('No'); + } + $resulttext .= '</li>'; + } + if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'lcmenu'}} > 0) { + $resulttext .= '<li>'.&mt('Menu items:').' '. + join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>'; + } else { + $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>'; + } + } + } + } + $resulttext .= '</ul></li>'; + } + } + } + $resulttext .= '</ul>'; + } 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 get_lti_id { + my ($domain,$consumer) = @_; + # get lock on lti db + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + my ($id,$error); + + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + } + if ($gotlock eq 'ok') { + my %currids = &Apache::lonnet::dump_dom('lti',$domain); + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } + } else { + $id = 1; + } + if ($id) { + unless (&Apache::lonnet::newput_dom('lti',{ $id => $consumer },$domain) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome = &Apache::lonnet::del_dom('lti',['lock'],$domain); + } else { + $error = 'nolock'; + } + return ($id,$error); +} + sub modify_autoenroll { my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,%changes); @@ -12247,7 +17508,7 @@ sub modify_contacts { $contacts_hash{'contacts'}{'overrides'}{$type}{'include'} = $includeloc{$type}.':'.&escape($includestr{$type}); $newsetting{'override_'.$type}{'include'} = $contacts_hash{'contacts'}{'overrides'}{$type}{'include'}; } - } + } } } if (keys(%currsetting) > 0) { @@ -12305,12 +17566,12 @@ sub modify_contacts { } } if (@statuses) { - if (ref($currsetting{'overrides'}) eq 'HASH') { + if (ref($currsetting{'overrides'}) eq 'HASH') { foreach my $key (keys(%{$currsetting{'overrides'}})) { if (ref($currsetting{'overrides'}{$key}) eq 'HASH') { if (ref($newsetting{'override_'.$key}) eq 'HASH') { foreach my $item (@contacts,'bcc','others','include') { - if ($currsetting{'overrides'}{$key}{$item} ne $newsetting{'override_'.$key}{$item}) { + if ($currsetting{'overrides'}{$key}{$item} ne $newsetting{'override_'.$key}{$item}) { push(@{$changes{'overrides'}},$key); last; } @@ -12327,7 +17588,7 @@ sub modify_contacts { } } else { foreach my $key (@overrides) { - push(@{$changes{'overrides'}},$key); + push(@{$changes{'overrides'}},$key); } } } @@ -12509,7 +17770,7 @@ sub modify_contacts { $resulttext .= $bcctext.': <span class="LC_cusr_emph">'.$bcc{$type}.'</span>'; } elsif (!@text) { $resulttext .= &mt('No one'); - } + } if ($includestr{$type} ne '') { if ($includeloc{$type} eq 'b') { $resulttext .= '<br />'.&mt('Text automatically added to e-mail body:').' '.$includestr{$type}; @@ -12533,14 +17794,14 @@ sub modify_contacts { if (ref($newsetting{'override_'.$type}) eq 'HASH') { my @text; foreach my $item (@contacts) { - if ($newsetting{'override_'.$type}{$item}) { + if ($newsetting{'override_'.$type}{$item}) { push(@text,$short_titles->{$item}); } } if ($newsetting{'override_'.$type}{'others'} ne '') { push(@text,$newsetting{'override_'.$type}{'others'}); } - + if (@text) { $resulttext .= &mt('Helpdesk e-mail sent to: [_1]', '<span class="LC_cusr_emph">'.join(', ',@text).'</span>'); @@ -12702,6 +17963,252 @@ sub modify_contacts { return $resulttext; } +sub modify_privacy { + my ($dom,%domconfig) = @_; + my ($resulttext,%current,%changes); + if (ref($domconfig{'privacy'}) eq 'HASH') { + %current = %{$domconfig{'privacy'}}; + } + my @fields = ('lastname','firstname','middlename','generation','permanentemail','id'); + my @items = ('domain','author','course','community'); + my %names = &Apache::lonlocal::texthash ( + domain => 'Assigned domain role(s)', + author => 'Assigned co-author role(s)', + course => 'Assigned course role(s)', + community => 'Assigned community role(s)', + ); + my %roles = &Apache::lonlocal::texthash ( + domain => 'Domain role', + author => 'Co-author role', + course => 'Course role', + community => 'Community role', + ); + my %titles = &Apache::lonlocal::texthash ( + approval => 'Approval for role in different domain', + othdom => 'User information available in other domain', + priv => 'Information viewable by privileged user in same domain', + unpriv => 'Information viewable by unprivileged user in same domain', + instdom => 'Other domain shares institution/provider', + extdom => 'Other domain has different institution/provider', + none => 'Not allowed', + user => 'User authorizes', + domain => 'Domain Coordinator authorizes', + auto => 'Unrestricted', + notify => 'Notify when role needs authorization', + ); + my %fieldnames = &Apache::lonlocal::texthash ( + id => 'Student/Employee ID', + permanentemail => 'E-mail address', + lastname => 'Last Name', + firstname => 'First Name', + middlename => 'Middle Name', + generation => 'Generation', + ); + my ($othertitle,$usertypes,$types) = + &Apache::loncommon::sorted_inst_types($dom); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + + my %privacyhash = ( + 'approval' => { + instdom => {}, + extdom => {}, + }, + 'othdom' => {}, + 'priv' => {}, + 'unpriv' => {}, + ); + foreach my $item (@items) { + if (@instdoms > 1) { + if ($env{'form.privacy_approval_instdom_'.$item} =~ /^(none|user|domain|auto)$/) { + $privacyhash{'approval'}{'instdom'}{$item} = $env{'form.privacy_approval_instdom_'.$item}; + } + if (ref($current{'approval'}) eq 'HASH') { + if (ref($current{'approval'}{'instdom'}) eq 'HASH') { + unless ($privacyhash{'approval'}{'instdom'}{$item} eq $current{'approval'}{'instdom'}{$item}) { + $changes{'approval'} = 1; + } + } + } elsif ($privacyhash{'approval'}{'instdom'}{$item} ne 'auto') { + $changes{'approval'} = 1; + } + } + if (keys(%by_location) > 0) { + if ($env{'form.privacy_approval_extdom_'.$item} =~ /^(none|user|domain|auto)$/) { + $privacyhash{'approval'}{'extdom'}{$item} = $env{'form.privacy_approval_extdom_'.$item}; + } + if (ref($current{'approval'}) eq 'HASH') { + if (ref($current{'approval'}{'extdom'}) eq 'HASH') { + unless ($privacyhash{'approval'}{'extdom'}{$item} eq $current{'approval'}{'extdom'}{$item}) { + $changes{'approval'} = 1; + } + } + } elsif ($privacyhash{'approval'}{'extdom'}{$item} ne 'auto') { + $changes{'approval'} = 1; + } + } + foreach my $status ('priv','unpriv') { + my @possibles = sort(&Apache::loncommon::get_env_multiple('form.privacy_'.$status.'_'.$item)); + my @newvalues; + foreach my $field (@possibles) { + if (grep(/^\Q$field\E$/,@fields)) { + $privacyhash{$status}{$item}{$field} = 1; + push(@newvalues,$field); + } + } + @newvalues = sort(@newvalues); + if (ref($current{$status}) eq 'HASH') { + if (ref($current{$status}{$item}) eq 'HASH') { + my @currvalues = sort(keys(%{$current{$status}{$item}})); + my @diffs = &Apache::loncommon::compare_arrays(\@currvalues,\@newvalues); + if (@diffs > 0) { + $changes{$status} = 1; + } + } + } else { + my @stdfields; + foreach my $field (@fields) { + if ($field eq 'id') { + next if ($status eq 'unpriv'); + next if (($status eq 'priv') && ($item eq 'community')); + } + push(@stdfields,$field); + } + my @diffs = &Apache::loncommon::compare_arrays(\@stdfields,\@newvalues); + if (@diffs > 0) { + $changes{$status} = 1; + } + } + } + } + if ((@instdoms > 1) || (keys(%by_location) > 0)) { + my @statuses; + if (ref($types) eq 'ARRAY') { + @statuses = @{$types}; + } + foreach my $type (@statuses,'default') { + my @possfields = &Apache::loncommon::get_env_multiple('form.privacy_othdom_'.$type); + my @newvalues; + foreach my $field (sort(@possfields)) { + if (grep(/^\Q$field\E$/,@fields)) { + $privacyhash{'othdom'}{$type}{$field} = 1; + push(@newvalues,$field); + } + } + @newvalues = sort(@newvalues); + if (ref($current{'othdom'}) eq 'HASH') { + if (ref($current{'othdom'}{$type}) eq 'HASH') { + my @currvalues = sort(keys(%{$current{'othdom'}{$type}})); + my @diffs = &Apache::loncommon::compare_arrays(\@currvalues,\@newvalues); + if (@diffs > 0) { + $changes{'othdom'} = 1; + } + } + } else { + my @stdfields = ('lastname','firstname','middlename','generation','permanentemail'); + my @diffs = &Apache::loncommon::compare_arrays(\@stdfields,\@newvalues); + if (@diffs > 0) { + $changes{'othdom'} = 1; + } + } + } + my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']); + my %notify; + foreach my $possdc (&Apache::loncommon::get_env_multiple('form.privacy_notify')) { + if (exists($domcoords{$possdc})) { + $notify{$possdc} = 1; + } + } + my $notify = join(',',sort(keys(%notify))); + if ($current{'notify'} ne $notify) { + $changes{'notify'} = 1; + } + $privacyhash{'notify'} = $notify; + } + my %confighash = ( + privacy => \%privacyhash, + ); + my $putresult = &Apache::lonnet::put_dom('configuration',\%confighash,$dom); + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + $resulttext = &mt('Changes made: ').'<ul>'; + foreach my $key ('approval','notify','othdom','priv','unpriv') { + if ($changes{$key}) { + $resulttext .= '<li>'.$titles{$key}.':<ul>'; + if ($key eq 'approval') { + if (keys(%{$privacyhash{$key}{instdom}})) { + $resulttext .= '<li>'.$titles{'instdom'}.'<ul>'; + foreach my $item (@items) { + $resulttext .= '<li>'.$roles{$item}.': '.$titles{$privacyhash{$key}{instdom}{$item}}.'</li>'; + } + $resulttext .= '</ul></li>'; + } + if (keys(%{$privacyhash{$key}{extdom}})) { + $resulttext .= '<li>'.$titles{'extdom'}.'<ul>'; + foreach my $item (@items) { + $resulttext .= '<li>'.$roles{$item}.': '.$titles{$privacyhash{$key}{extdom}{$item}}.'</li>'; + } + $resulttext .= '</ul></li>'; + } + } elsif ($key eq 'notify') { + if ($privacyhash{$key}) { + foreach my $dc (split(/,/,$privacyhash{$key})) { + my ($dcname,$dcdom) = split(/:/,$dc); + $resulttext .= '<li>'.&Apache::loncommon::plainname($dcname,$dcdom).'</li>'; + } + } else { + $resulttext .= '<li>'.&mt('No DCs to notify').'</li>'; + } + } elsif ($key eq 'othdom') { + my @statuses; + if (ref($types) eq 'ARRAY') { + @statuses = @{$types}; + } + if (ref($privacyhash{$key}) eq 'HASH') { + foreach my $status (@statuses,'default') { + if ($status eq 'default') { + $resulttext .= '<li>'.$othertitle.': '; + } elsif (ref($usertypes) eq 'HASH') { + $resulttext .= '<li>'.$usertypes->{$status}.': '; + } else { + next; + } + if (ref($privacyhash{$key}{$status}) eq 'HASH') { + if (keys(%{$privacyhash{$key}{$status}})) { + $resulttext .= join(', ', map { $fieldnames{$_}; } (sort(keys(%{$privacyhash{$key}{$status}})))); + } else { + $resulttext .= &mt('none'); + } + } + $resulttext .= '</li>'; + } + } + } else { + foreach my $item (@items) { + if (ref($privacyhash{$key}{$item}) eq 'HASH') { + $resulttext .= '<li>'.$names{$item}.': '; + if (keys(%{$privacyhash{$key}{$item}})) { + $resulttext .= join(', ', map { $fieldnames{$_}; } (sort(keys(%{$privacyhash{$key}{$item}})))); + } else { + $resulttext .= &mt('none'); + } + $resulttext .= '</li>'; + } + } + } + $resulttext .= '</ul></li>'; + } + } + } else { + $resulttext = &mt('No changes made to user information settings'); + } + } else { + $resulttext = '<span class="LC_error">'. + &mt('An error occurred: [_1]',$putresult).'</span>'; + } + return $resulttext; +} + sub modify_passwords { my ($r,$dom,$confname,$lastactref,%domconfig) = @_; my ($resulttext,%current,%changes,%newvalues,@oktypes,$errors, @@ -12727,6 +18234,7 @@ sub modify_passwords { min => 'Minimum password length', max => 'Maximum password length', chars => 'Required characters', + expire => 'Password expiration (days)', numsaved => 'Number of previous passwords to save', reset => 'Resetting Forgotten Password', intauth => 'Encryption of Stored Passwords (Internal Auth)', @@ -12869,7 +18377,6 @@ sub modify_passwords { } if ($env{'form.passwords_customfile.filename'} ne '') { my $servadm = $r->dir_config('lonAdmEMail'); - my $servadm = $r->dir_config('lonAdmEMail'); my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); my $error; @@ -12936,56 +18443,7 @@ sub modify_passwords { $updatedefaults = 1; } } - foreach my $rule ('min','max','numsaved') { - $env{'form.passwords_'.$rule} =~ s/^\s+|\s+$//g; - my $ruleok; - if ($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+$/) && - ($env{'form.passwords_'.$rule} ne '0')) { - $ruleok = 1; - } - if ($ruleok) { - $newvalues{$rule} = $env{'form.passwords_'.$rule}; - if (exists($current{$rule})) { - if ($newvalues{$rule} ne $current{$rule}) { - $changes{'rules'} = 1; - } - } elsif ($rule eq 'min') { - if ($staticdefaults{$rule} ne $newvalues{$rule}) { - $changes{'rules'} = 1; - } - } else { - $changes{'rules'} = 1; - } - } elsif (exists($current{$rule})) { - $changes{'rules'} = 1; - } - } - my @posschars = &Apache::loncommon::get_env_multiple('form.passwords_chars'); - my @chars; - foreach my $item (sort(@posschars)) { - if ($item =~ /^(uc|lc|num|spec)$/) { - push(@chars,$item); - } - } - $newvalues{'chars'} = \@chars; - unless ($changes{'rules'}) { - if (ref($current{'chars'}) eq 'ARRAY') { - my @diffs = &Apache::loncommon::compare_arrays($current{'chars'},\@chars); - if (@diffs > 0) { - $changes{'rules'} = 1; - } - } else { - if (@chars > 0) { - $changes{'rules'} = 1; - } - } - } + &password_rule_changes('passwords',\%newvalues,\%current,\%changes); my %crsownerchg = ( by => [], for => [], @@ -13157,7 +18615,7 @@ sub modify_passwords { $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$titles{$key.'_'.$item},$value).'</li>'; } } elsif ($key eq 'rules') { - foreach my $rule ('min','max','numsaved') { + foreach my $rule ('min','max','expire','numsaved') { if ($confighash{'passwords'}{$rule} eq '') { if ($rule eq 'min') { $resulttext .= '<li>'.&mt('[_1] not set.',$titles{$rule}); @@ -13180,7 +18638,7 @@ sub modify_passwords { ); my $needed = '<ul><li>'. join('</li><li>',map {$rulenames{$_} } @{$confighash{'passwords'}{'chars'}}). - '</li></ul>'; + '</li></ul>'; $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>'; } else { $resulttext .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>'; @@ -13245,6 +18703,76 @@ sub modify_passwords { return $resulttext; } +sub password_rule_changes { + my ($prefix,$newvalues,$current,$changes) = @_; + return unless ((ref($newvalues) eq 'HASH') && + (ref($current) eq 'HASH') && + (ref($changes) eq 'HASH')); + my (@rules,%staticdefaults); + if ($prefix eq 'passwords') { + @rules = ('min','max','expire','numsaved'); + } elsif ($prefix eq 'secrets') { + @rules = ('min','max'); + } + $staticdefaults{'min'} = $Apache::lonnet::passwdmin; + foreach my $rule (@rules) { + $env{'form.'.$prefix.'_'.$rule} =~ s/^\s+|\s+$//g; + my $ruleok; + if ($rule eq 'expire') { + if (($env{'form.'.$prefix.'_'.$rule} =~ /^\d+(|\.\d*)$/) && + ($env{'form.'.$prefix.'_'.$rule} ne '0')) { + $ruleok = 1; + } + } elsif ($rule eq 'min') { + if ($env{'form.'.$prefix.'_'.$rule} =~ /^\d+$/) { + if ($env{'form.'.$prefix.'_'.$rule} >= $staticdefaults{$rule}) { + $ruleok = 1; + } + } + } elsif (($env{'form.'.$prefix.'_'.$rule} =~ /^\d+$/) && + ($env{'form.'.$prefix.'_'.$rule} ne '0')) { + $ruleok = 1; + } + if ($ruleok) { + $newvalues->{$rule} = $env{'form.'.$prefix.'_'.$rule}; + if (exists($current->{$rule})) { + if ($newvalues->{$rule} ne $current->{$rule}) { + $changes->{'rules'} = 1; + } + } elsif ($rule eq 'min') { + if ($staticdefaults{$rule} ne $newvalues->{$rule}) { + $changes->{'rules'} = 1; + } + } else { + $changes->{'rules'} = 1; + } + } elsif (exists($current->{$rule})) { + $changes->{'rules'} = 1; + } + } + my @posschars = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_chars'); + my @chars; + foreach my $item (sort(@posschars)) { + if ($item =~ /^(uc|lc|num|spec)$/) { + push(@chars,$item); + } + } + $newvalues->{'chars'} = \@chars; + unless ($changes->{'rules'}) { + if (ref($current->{'chars'}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($current->{'chars'},\@chars); + if (@diffs > 0) { + $changes->{'rules'} = 1; + } + } else { + if (@chars > 0) { + $changes->{'rules'} = 1; + } + } + } + return; +} + sub modify_usercreation { my ($dom,%domconfig) = @_; my ($resulttext,%curr_usercreation,%changes,%authallowed,%cancreate,%save_usercreate); @@ -13329,7 +18857,7 @@ sub modify_usercreation { } my @authen_contexts = ('author','course','domain'); - my @authtypes = ('int','krb4','krb5','loc'); + my @authtypes = ('int','krb4','krb5','loc','lti'); my %authhash; foreach my $item (@authen_contexts) { my @authallowed = &Apache::loncommon::get_env_multiple('form.'.$item.'_auth'); @@ -13544,7 +19072,6 @@ sub modify_selfcreation { # Populate $cancreate{'selfcreate'} array reference with types of user, for which self-creation of user accounts # is permitted. # - my ($emailrules,$emailruleorder) = &Apache::lonnet::inst_userrules($dom,'email'); my (@statuses,%email_rule); @@ -13594,7 +19121,7 @@ sub modify_selfcreation { if (($chosen eq 'inst') || ($chosen eq 'noninst')) { my $emaildom; if ($env{'form.cancreate_emaildomain_'.$chosen.'_'.$type} =~ /^\@[^\@]+$/) { - $emaildom = $env{'form.cancreate_emaildomain_'.$chosen.'_'.$type}; + $emaildom = $env{'form.cancreate_emaildomain_'.$chosen.'_'.$type}; $cancreate{'emaildomain'}{$type}{$chosen} = $emaildom; if (ref($curremaildom{$type}) eq 'HASH') { if (exists($curremaildom{$type}{$chosen})) { @@ -13606,7 +19133,7 @@ sub modify_selfcreation { } } elsif ($emaildom ne '') { push(@{$changes{'cancreate'}},'emaildomain'); - } + } } $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type}; } elsif ($chosen eq 'custom') { @@ -14033,7 +19560,7 @@ sub modify_selfcreation { ); if (@types) { if (@statuses) { - $chgtext .= &mt('Processing of requests to create account with e-mail verification set as follows:'). + $chgtext .= &mt('Processing of requests to create account with e-mail verification set as follows:'). '<ul>'; foreach my $status (@statuses) { if ($status eq 'default') { @@ -14119,7 +19646,7 @@ sub modify_selfcreation { $output = '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>'; } else { $output = '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail address needs to end: [_1]", - $cancreate{'emaildomain'}{$type}{'inst'}).'</li>'; + $cancreate{'emaildomain'}{$type}{'inst'}).'</li>'; } } } elsif ($cancreate{'emailoptions'}{$type} eq 'noninst') { @@ -14137,7 +19664,7 @@ sub modify_selfcreation { $output = '<li>'.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'</li>'; } else { $output = '<li>'.$usertypes{$type}.' -- '.&mt("User's e-mail address must not end: [_1]", - $cancreate{'emaildomain'}{$type}{'noninst'}).'</li>'; + $cancreate{'emaildomain'}{$type}{'noninst'}).'</li>'; } } } @@ -14241,7 +19768,7 @@ sub modify_selfcreation { $typename = $othertitle; } else { $typename = $usertypes{$type}; - } + } $chgtext .= &mt('(Affiliation: [_1])',$typename); } if (@{$email_rule{$type}} > 0) { @@ -14522,7 +20049,7 @@ sub modify_defaults { my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); my @items = ('auth_def','auth_arg_def','lang_def','timezone_def','datelocale_def', 'portal_def'); - my @authtypes = ('internal','krb4','krb5','localauth'); + my @authtypes = ('internal','krb4','krb5','localauth','lti'); foreach my $item (@items) { $newvalues{$item} = $env{'form.'.$item}; if ($item eq 'auth_def') { @@ -14559,16 +20086,58 @@ sub modify_defaults { } } elsif ($item eq 'portal_def') { if ($newvalues{$item} ne '') { - unless ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) { + if ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) { + foreach my $field ('email','web') { + if ($env{'form.'.$item.'_'.$field}) { + $newvalues{$item.'_'.$field} = $env{'form.'.$item.'_'.$field}; + } + } + } else { push(@errors,$item); } } } if (grep(/^\Q$item\E$/,@errors)) { $newvalues{$item} = $domdefaults{$item}; + if ($item eq 'portal_def') { + if ($domdefaults{$item}) { + foreach my $field ('email','web') { + if (exists($domdefaults{$item.'_'.$field})) { + $newvalues{$item.'_'.$field} = $domdefaults{$item.'_'.$field}; + } + } + } + } } elsif ($domdefaults{$item} ne $newvalues{$item}) { $changes{$item} = 1; } + if ($item eq 'portal_def') { + unless (grep(/^\Q$item\E$/,@errors)) { + if ($newvalues{$item} eq '') { + foreach my $field ('email','web') { + if (exists($domdefaults{$item.'_'.$field})) { + delete($domdefaults{$item.'_'.$field}); + } + } + } else { + unless ($changes{$item}) { + foreach my $field ('email','web') { + if ($domdefaults{$item.'_'.$field} ne $newvalues{$item.'_'.$field}) { + $changes{$item} = 1; + last; + } + } + } + foreach my $field ('email','web') { + if ($newvalues{$item.'_'.$field}) { + $domdefaults{$item.'_'.$field} = $newvalues{$item.'_'.$field}; + } elsif (exists($domdefaults{$item.'_'.$field})) { + delete($domdefaults{$item.'_'.$field}); + } + } + } + } + } $domdefaults{$item} = $newvalues{$item}; } my %staticdefaults = ( @@ -14583,6 +20152,41 @@ sub modify_defaults { $newvalues{$item} = $staticdefaults{$item}; } } + my ($unamemaprules,$ruleorder); + my @possunamemaprules = &Apache::loncommon::get_env_multiple('form.unamemap_rule'); + if (@possunamemaprules) { + ($unamemaprules,$ruleorder) = + &Apache::lonnet::inst_userrules($dom,'unamemap'); + if ((ref($unamemaprules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) { + if (@{$ruleorder} > 0) { + my %possrules; + map { $possrules{$_} = 1; } @possunamemaprules; + foreach my $rule (@{$ruleorder}) { + if ($possrules{$rule}) { + push(@{$newvalues{'unamemap_rule'}},$rule); + } + } + } + } + } + if (ref($domdefaults{'unamemap_rule'}) eq 'ARRAY') { + if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') { + my @rulediffs = &Apache::loncommon::compare_arrays($domdefaults{'unamemap_rule'}, + $newvalues{'unamemap_rule'}); + if (@rulediffs) { + $changes{'unamemap_rule'} = 1; + $domdefaults{'unamemap_rule'} = $newvalues{'unamemap_rule'}; + } + } elsif (@{$domdefaults{'unamemap_rule'}} > 0) { + $changes{'unamemap_rule'} = 1; + delete($domdefaults{'unamemap_rule'}); + } + } elsif (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') { + if (@{$newvalues{'unamemap_rule'}} > 0) { + $changes{'unamemap_rule'} = 1; + $domdefaults{'unamemap_rule'} = $newvalues{'unamemap_rule'}; + } + } my %defaults_hash = ( defaults => \%newvalues, ); @@ -14603,12 +20207,10 @@ sub modify_defaults { my @allpos; my %alltypes; my @inststatusguest; - if (ref($currinststatus) eq 'HASH') { - if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') { - foreach my $type (@{$currinststatus->{'inststatusguest'}}) { - unless (grep(/^\Q$type\E$/,@todelete)) { - push(@inststatusguest,$type); - } + if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') { + foreach my $type (@{$currinststatus->{'inststatusguest'}}) { + unless (grep(/^\Q$type\E$/,@todelete)) { + push(@inststatusguest,$type); } } } @@ -14696,8 +20298,28 @@ sub modify_defaults { $resulttext =~ s/, $//; $resulttext .= '</li>'; } else { - $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>'; + $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>'; + } + } + } elsif ($item eq 'unamemap_rule') { + if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') { + my @rulenames; + if (ref($unamemaprules) eq 'HASH') { + foreach my $rule (@{$newvalues{'unamemap_rule'}}) { + if (ref($unamemaprules->{$rule}) eq 'HASH') { + push(@rulenames,$unamemaprules->{$rule}->{'name'}); + } + } + } + if (@rulenames) { + $resulttext .= '<li>'.&mt('Mapping for missing usernames includes: [_1]', + '<ul><li>'.join('</li><li>',@rulenames).'</li></ul>'). + '</li>'; + } else { + $resulttext .= '<li>'.&mt('No mapping for missing usernames via standard log-in').'</li>'; } + } else { + $resulttext .= '<li>'.&mt('Mapping for missing usernames via standard log-in deleted').'</li>'; } } else { my $value = $env{'form.'.$item}; @@ -14710,11 +20332,25 @@ sub modify_defaults { krb4 => 'krb4', krb5 => 'krb5', localauth => 'loc', + lti => 'lti', ); $value = $authnames{$shortauth{$value}}; } $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$title->{$item},$value).'</li>'; - $mailmsgtext .= "$title->{$item} set to $value\n"; + $mailmsgtext .= "$title->{$item} set to $value\n"; + if ($item eq 'portal_def') { + if ($env{'form.'.$item} ne '') { + foreach my $field ('email','web') { + $value = $env{'form.'.$item.'_'.$field}; + if ($value) { + $value = &mt('Yes'); + } else { + $value = &mt('No'); + } + $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$title->{$field},$value).'</li>'; + } + } + } } } $resulttext .= '</ul>'; @@ -14923,11 +20559,11 @@ sub modify_scantron { &mt('An error occurred: [_1]',$putresult).'</span>'; } } else { - $resulttext = &mt('No changes made to bubblesheet format settings'); + $resulttext = &mt('No changes made to bubblesheet format file'); } if ($errors) { - $resulttext .= &mt('The following errors occurred: ').'<ul>'. - $errors.'</ul>'; + $resulttext .= '<p>'.&mt('The following errors occurred: ').'<ul>'. + $errors.'</ul></p>'; } return $resulttext; } @@ -14956,6 +20592,15 @@ sub modify_coursecategories { if ($domconfig{'coursecategories'}{'categorizecomm'} ne $env{'form.categorizecomm'}) { $changes{'categorizecomm'} = 1; $domconfig{'coursecategories'}{'categorizecomm'} = $env{'form.categorizecomm'}; + + } + if ($domconfig{'coursecategories'}{'togglecatsplace'} ne $env{'form.togglecatsplace'}) { + $changes{'togglecatsplace'} = 1; + $domconfig{'coursecategories'}{'togglecatsplace'} = $env{'form.togglecatsplace'}; + } + if ($domconfig{'coursecategories'}{'categorizeplace'} ne $env{'form.categorizeplace'}) { + $changes{'categorizeplace'} = 1; + $domconfig{'coursecategories'}{'categorizeplace'} = $env{'form.categorizeplace'}; } foreach my $item (@catitems) { if (grep(/^\Q$env{'form.coursecat_'.$item}\E$/,@cattypes)) { @@ -14970,11 +20615,15 @@ sub modify_coursecategories { $changes{'categorize'} = 1; $changes{'togglecatscomm'} = 1; $changes{'categorizecomm'} = 1; + $changes{'togglecatsplace'} = 1; + $changes{'categorizeplace'} = 1; $domconfig{'coursecategories'} = { togglecats => $env{'form.togglecats'}, categorize => $env{'form.categorize'}, togglecatscomm => $env{'form.togglecatscomm'}, categorizecomm => $env{'form.categorizecomm'}, + togglecatsplace => $env{'form.togglecatsplace'}, + categorizeplace => $env{'form.categorizeplace'}, }; foreach my $item (@catitems) { if ($env{'form.coursecat_'.$item} ne 'std') { @@ -14992,6 +20641,9 @@ sub modify_coursecategories { if (($domconfig{'coursecategories'}{'cats'}{'communities::0'} ne '') && ($env{'form.communities'} == 0)) { push(@deletecategory,'communities::0'); } + if (($domconfig{'coursecategories'}{'cats'}{'placement::0'} ne '') && ($env{'form.placement'} == 0)) { + push(@deletecategory,'placement::0'); + } } my (@predelcats,@predeltrails,%predelallitems,%sort_by_deltrail); if (ref($cathash) eq 'HASH') { @@ -15054,9 +20706,23 @@ sub modify_coursecategories { $adds{$newitem} = 1; } } + if ($env{'form.placement'} eq '1') { + if (ref($cathash) eq 'HASH') { + my $newitem = 'placement::0'; + if ($cathash->{$newitem} eq '') { + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.placement_pos'}; + $adds{$newitem} = 1; + } + } else { + my $newitem = 'placement::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.placement_pos'}; + $adds{$newitem} = 1; + } + } if ($env{'form.addcategory_name'} ne '') { if (($env{'form.addcategory_name'} ne 'instcode') && - ($env{'form.addcategory_name'} ne 'communities')) { + ($env{'form.addcategory_name'} ne 'communities') && + ($env{'form.addcategory_name'} ne 'placement')) { my $newitem = &escape($env{'form.addcategory_name'}).'::0'; $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos'}; $adds{$newitem} = 1; @@ -15655,15 +21321,18 @@ sub modify_coursedefaults { my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,$errors,%changes,%defaultshash); my %defaultchecked = ( + 'canuse_pdfforms' => 'off', 'uselcmath' => 'on', 'usejsme' => 'on', 'inline_chem' => 'on', + 'ltiauth' => 'off', ); - my @toggles = ('uselcmath','usejsme','inline_chem'); + my @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem','ltiauth'); my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial', - 'uploadquota_community','uploadquota_textbook','mysqltables_official', - 'mysqltables_unofficial','mysqltables_community','mysqltables_textbook'); - my @types = ('official','unofficial','community','textbook'); + 'uploadquota_community','uploadquota_textbook','uploadquota_placement', + 'mysqltables_official','mysqltables_unofficial','mysqltables_community', + 'mysqltables_textbook','mysqltables_placement'); + my @types = ('official','unofficial','community','textbook','placement'); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, @@ -15866,11 +21535,11 @@ sub modify_coursedefaults { if ($putresult eq 'ok') { if (keys(%changes) > 0) { my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); - if (($changes{'uploadquota'}) || ($changes{'postsubmit'}) || + if (($changes{'canuse_pdfforms'}) || ($changes{'uploadquota'}) || ($changes{'postsubmit'}) || ($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) || ($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'}) || - ($changes{'inline_chem'})) { - foreach my $item ('uselcmath','usejsme','inline_chem','texengine') { + ($changes{'inline_chem'}) || ($changes{'ltiauth'})) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine','ltiauth') { if ($changes{$item}) { $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; } @@ -15921,7 +21590,13 @@ sub modify_coursedefaults { } $resulttext = &mt('Changes made:').'<ul>'; foreach my $item (sort(keys(%changes))) { - if ($item eq 'uselcmath') { + if ($item eq 'canuse_pdfforms') { + if ($env{'form.'.$item} eq '1') { + $resulttext .= '<li>'.&mt("Course/Community users can create/upload PDF forms set to 'on'").'</li>'; + } else { + $resulttext .= '<li>'.&mt('Course/Community users can create/upload PDF forms set to "off"').'</li>'; + } + } elsif ($item eq 'uselcmath') { if ($env{'form.'.$item} eq '1') { $resulttext .= '<li>'.&mt('Math preview uses LON-CAPA previewer (javascript), if supported by browser.').'</li>'; } else { @@ -15952,7 +21627,7 @@ sub modify_coursedefaults { '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'official'}.'</b>').'</li>'. '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'unofficial'}.'</b>').'</li>'. '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'textbook'}.'</b>').'</li>'. - + '<li>'.&mt('Placement tests: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'placement'}.'</b>').'</li>'. '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'uploadquota'}{'community'}.'</b>').'</li>'. '</ul>'. '</li>'; @@ -15965,6 +21640,7 @@ sub modify_coursedefaults { '<li>'.&mt('Official courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'official'}.'</b>').'</li>'. '<li>'.&mt('Unofficial courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'unofficial'}.'</b>').'</li>'. '<li>'.&mt('Textbook courses: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'textbook'}.'</b>').'</li>'. + '<li>'.&mt('Placement tests: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'placement'}.'</b>').'</li>'. '<li>'.&mt('Communities: [_1] s','<b>'.$defaultshash{'coursedefaults'}{'mysqltables'}{'community'}.'</b>').'</li>'. '</ul>'. '</li>'; @@ -16000,6 +21676,8 @@ sub modify_coursedefaults { $resulttext .= &mt('Unofficial courses'); } elsif ($type eq 'textbook') { $resulttext .= &mt('Textbook courses'); + } elsif ($type eq 'placement') { + $resulttext .= &mt('Placement tests'); } $resulttext .= ' -- '.$display.'</li>'; } @@ -16035,6 +21713,12 @@ sub modify_coursedefaults { } else { $resulttext .= '<li>'.&mt('By default, only course owner and coordinators may clone a course.').'</li>'; } + } elsif ($item eq 'ltiauth') { + if ($env{'form.'.$item} eq '1') { + $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL need not require re-authentication').'</li>'; + } else { + $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL will require re-authentication').'</li>'; + } } } $resulttext .= '</ul>'; @@ -16051,7 +21735,7 @@ sub modify_coursedefaults { sub modify_selfenrollment { my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,$errors,%changes,%selfenrollhash,%ordered); - my @types = ('official','unofficial','community','textbook'); + my @types = ('official','unofficial','community','textbook','placement'); my %titles = &tool_titles(); my %descs = &Apache::lonuserutils::selfenroll_default_descs(); ($ordered{'admin'},my $titlesref) = &Apache::lonuserutils::get_selfenroll_titles(); @@ -16428,7 +22112,7 @@ sub modify_wafproxy { } } elsif ($currvalue{$item}) { $changes{$item} = 1; - } + } } } else { if (keys(%curralias)) { @@ -16436,7 +22120,7 @@ sub modify_wafproxy { } if (keys(%currsaml)) { $changes{'saml'} = 1; - } + } if (keys(%currvalue)) { foreach my $key (keys(%currvalue)) { $changes{$key} = 1; @@ -16446,7 +22130,7 @@ sub modify_wafproxy { if (keys(%changes)) { my %defaultshash = ( wafproxy => \%wafproxy, - ); + ); my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); if ($putresult eq 'ok') { @@ -16462,7 +22146,7 @@ sub modify_wafproxy { $domdefaults{'waf_'.$item} = $wafproxy{$item}; } elsif (exists($domdefaults{'waf_'.$item})) { delete($domdefaults{'waf_'.$item}); - } + } } } if ($updatedomdefs) { @@ -16529,7 +22213,7 @@ sub modify_wafproxy { $output .= '<li>'.&mt('Aliases deleted for hostnames').'</li>'; } } elsif ($item eq 'saml') { - my $shown; + my $shown; if (ref($wafproxy{'saml'}) eq 'HASH') { if (keys(%{$wafproxy{'saml'}})) { $shown = join(', ',sort(keys(%{$wafproxy{'saml'}}))); @@ -16636,8 +22320,8 @@ sub modify_usersessions { ); my @prefixes = ('remote','hosted','spares'); my @lcversions = &Apache::lonnet::all_loncaparevs(); - my (%by_ip,%by_location,@intdoms); - &build_location_hashes(\@intdoms,\%by_ip,\%by_location); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); my @locations = sort(keys(%by_location)); my (%defaultshash,%changes); foreach my $prefix (@prefixes) { @@ -16858,7 +22542,7 @@ sub modify_usersessions { if (($offload eq 'offloadoth') && (@okoffloadoth)) { $changes{'offloadoth'} = 1; } - } + } } } else { if (@okoffload) { @@ -16926,14 +22610,18 @@ sub modify_usersessions { } else { foreach my $type (@{$types{$prefix}}) { if (defined($changes{$prefix}{$type})) { - my $newvalue; + my ($newvalue,$notinuse); if (ref($defaultshash{'usersessions'}) eq 'HASH') { if (ref($defaultshash{'usersessions'}{$prefix})) { if ($type eq 'version') { $newvalue = $defaultshash{'usersessions'}{$prefix}{$type}; - } elsif (ref($defaultshash{'usersessions'}{$prefix}{$type}) eq 'ARRAY') { - if (@{$defaultshash{'usersessions'}{$prefix}{$type}} > 0) { - $newvalue = join(', ',@{$defaultshash{'usersessions'}{$prefix}{$type}}); + } else { + if (ref($defaultshash{'usersessions'}{$prefix}{$type}) eq 'ARRAY') { + if (@{$defaultshash{'usersessions'}{$prefix}{$type}} > 0) { + $newvalue = join(', ',@{$defaultshash{'usersessions'}{$prefix}{$type}}); + } + } else { + $notinuse = 1; } } } @@ -16941,12 +22629,14 @@ sub modify_usersessions { if ($newvalue eq '') { if ($type eq 'version') { $resulttext .= '<li>'.&mt('[_1] set to: off',$lt{$type}).'</li>'; + } elsif ($notinuse) { + $resulttext .= '<li>'.&mt('[_1] set to: not in use',$lt{$type}).'</li>'; } else { $resulttext .= '<li>'.&mt('[_1] set to: none',$lt{$type}).'</li>'; } } else { if ($type eq 'version') { - $newvalue .= ' '.&mt('(or later)'); + $newvalue .= ' '.&mt('(or later)'); } $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'</li>'; } @@ -17000,6 +22690,326 @@ sub modify_usersessions { return $resulttext; } +sub modify_ssl { + my ($dom,$lastactref,%domconfig) = @_; + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @locations = sort(keys(%by_location)); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my (%defaultshash,%changes); + my $action = 'ssl'; + my @prefixes = ('connto','connfrom','replication'); + foreach my $prefix (@prefixes) { + $defaultshash{$action}{$prefix} = {}; + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my $resulttext; + my %iphost = &Apache::lonnet::get_iphost(); + my @reptypes = ('certreq','nocertreq'); + my @connecttypes = ('dom','intdom','other'); + my %types = ( + connto => \@connecttypes, + connfrom => \@connecttypes, + replication => \@reptypes, + ); + foreach my $prefix (sort(keys(%types))) { + foreach my $type (@{$types{$prefix}}) { + if (($prefix eq 'connto') || ($prefix eq 'connfrom')) { + my $value = 'yes'; + if ($env{'form.'.$prefix.'_'.$type} =~ /^(no|req)$/) { + $value = $env{'form.'.$prefix.'_'.$type}; + } + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$prefix}) eq 'HASH') { + if ($domconfig{$action}{$prefix}{$type} ne '') { + if ($value ne $domconfig{$action}{$prefix}{$type}) { + $changes{$prefix}{$type} = 1; + } + $defaultshash{$action}{$prefix}{$type} = $value; + } else { + $defaultshash{$action}{$prefix}{$type} = $value; + $changes{$prefix}{$type} = 1; + } + } else { + $defaultshash{$action}{$prefix}{$type} = $value; + $changes{$prefix}{$type} = 1; + } + } else { + $defaultshash{$action}{$prefix}{$type} = $value; + $changes{$prefix}{$type} = 1; + } + if (($type eq 'dom') && (keys(%servers) == 1)) { + delete($changes{$prefix}{$type}); + } elsif (($type eq 'intdom') && (@instdoms == 1)) { + delete($changes{$prefix}{$type}); + } elsif (($type eq 'other') && (keys(%by_location) == 0)) { + delete($changes{$prefix}{$type}); + } + } elsif ($prefix eq 'replication') { + if (@locations > 0) { + my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'}; + my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type); + my @okvals; + foreach my $val (@vals) { + if ($val =~ /:/) { + my @items = split(/:/,$val); + foreach my $item (@items) { + if (ref($by_location{$item}) eq 'ARRAY') { + push(@okvals,$item); + } + } + } else { + if (ref($by_location{$val}) eq 'ARRAY') { + push(@okvals,$val); + } + } + } + @okvals = sort(@okvals); + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$prefix}) eq 'HASH') { + if (ref($domconfig{$action}{$prefix}{$type}) eq 'ARRAY') { + if ($inuse == 0) { + $changes{$prefix}{$type} = 1; + } else { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + my @changed = &Apache::loncommon::compare_arrays($domconfig{$action}{$prefix}{$type},$defaultshash{$action}{$prefix}{$type}); + if (@changed > 0) { + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } + } + } + } + if (keys(%changes)) { + foreach my $prefix (keys(%changes)) { + if (ref($changes{$prefix}) eq 'HASH') { + if (scalar(keys(%{$changes{$prefix}})) == 0) { + delete($changes{$prefix}); + } + } else { + delete($changes{$prefix}); + } + } + } + my $nochgmsg = &mt('No changes made to LON-CAPA SSL settings'); + if (keys(%changes) > 0) { + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + if (ref($defaultshash{$action}) eq 'HASH') { + if (ref($defaultshash{$action}{'replication'}) eq 'HASH') { + $domdefaults{'replication'} = $defaultshash{$action}{'replication'}; + } + if (ref($defaultshash{$action}{'connto'}) eq 'HASH') { + $domdefaults{'connto'} = $defaultshash{$action}{'connto'}; + } + if (ref($defaultshash{$action}{'connfrom'}) eq 'HASH') { + $domdefaults{'connfrom'} = $defaultshash{$action}{'connfrom'}; + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + if (keys(%changes) > 0) { + my %titles = &ssl_titles(); + $resulttext = &mt('Changes made:').'<ul>'; + foreach my $prefix (@prefixes) { + if (ref($changes{$prefix}) eq 'HASH') { + $resulttext .= '<li>'.$titles{$prefix}.'<ul>'; + foreach my $type (@{$types{$prefix}}) { + if (defined($changes{$prefix}{$type})) { + my ($newvalue,$notinuse); + if (ref($defaultshash{$action}) eq 'HASH') { + if (ref($defaultshash{$action}{$prefix})) { + if (($prefix eq 'connto') || ($prefix eq 'connfrom')) { + $newvalue = $titles{$defaultshash{$action}{$prefix}{$type}}; + } else { + if (ref($defaultshash{$action}{$prefix}{$type}) eq 'ARRAY') { + if (@{$defaultshash{$action}{$prefix}{$type}} > 0) { + $newvalue = join(', ',@{$defaultshash{$action}{$prefix}{$type}}); + } + } else { + $notinuse = 1; + } + } + } + if ($notinuse) { + $resulttext .= '<li>'.&mt('[_1] set to: not in use',$titles{$type}).'</li>'; + } elsif ($newvalue eq '') { + $resulttext .= '<li>'.&mt('[_1] set to: none',$titles{$type}).'</li>'; + } else { + $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$titles{$type},$newvalue).'</li>'; + } + } + } + } + $resulttext .= '</ul>'; + } + } + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = '<span class="LC_error">'. + &mt('An error occurred: [_1]',$putresult).'</span>'; + } + } else { + $resulttext = $nochgmsg; + } + return $resulttext; +} + +sub modify_trust { + my ($dom,$lastactref,%domconfig) = @_; + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @locations = sort(keys(%by_location)); + my @prefixes = qw(content shared enroll othcoau coaurem domroles catalog reqcrs msg); + my @types = ('exc','inc'); + my (%defaultshash,%changes); + foreach my $prefix (@prefixes) { + $defaultshash{'trust'}{$prefix} = {}; + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my $resulttext; + foreach my $prefix (@prefixes) { + foreach my $type (@types) { + my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'}; + my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type); + my @okvals; + foreach my $val (@vals) { + if ($val =~ /:/) { + my @items = split(/:/,$val); + foreach my $item (@items) { + if (ref($by_location{$item}) eq 'ARRAY') { + push(@okvals,$item); + } + } + } else { + if (ref($by_location{$val}) eq 'ARRAY') { + push(@okvals,$val); + } + } + } + @okvals = sort(@okvals); + if (ref($domconfig{'trust'}) eq 'HASH') { + if (ref($domconfig{'trust'}{$prefix}) eq 'HASH') { + if (ref($domconfig{'trust'}{$prefix}{$type}) eq 'ARRAY') { + if ($inuse == 0) { + $changes{$prefix}{$type} = 1; + } else { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + my @changed = &Apache::loncommon::compare_arrays($domconfig{'trust'}{$prefix}{$type},$defaultshash{'trust'}{$prefix}{$type}); + if (@changed > 0) { + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } + } + my $nochgmsg = &mt('No changes made to trust settings.'); + if (keys(%changes) > 0) { + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + if (ref($defaultshash{'trust'}) eq 'HASH') { + foreach my $prefix (@prefixes) { + if (ref($defaultshash{'trust'}{$prefix}) eq 'HASH') { + $domdefaults{'trust'.$prefix} = $defaultshash{'trust'}{$prefix}; + } + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + &Apache::lonnet::do_cache_new('trust',$dom,$defaultshash{'trust'},3600); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + $lastactref->{'trust'} = 1; + } + if (keys(%changes) > 0) { + my %lt = &trust_titles(); + $resulttext = &mt('Changes made:').'<ul>'; + foreach my $prefix (@prefixes) { + if (ref($changes{$prefix}) eq 'HASH') { + $resulttext .= '<li>'.$lt{$prefix}.'<ul>'; + foreach my $type (@types) { + if (defined($changes{$prefix}{$type})) { + my ($newvalue,$notinuse); + if (ref($defaultshash{'trust'}) eq 'HASH') { + if (ref($defaultshash{'trust'}{$prefix})) { + if (ref($defaultshash{'trust'}{$prefix}{$type}) eq 'ARRAY') { + if (@{$defaultshash{'trust'}{$prefix}{$type}} > 0) { + $newvalue = join(', ',@{$defaultshash{'trust'}{$prefix}{$type}}); + } + } else { + $notinuse = 1; + } + } + } + if ($notinuse) { + $resulttext .= '<li>'.&mt('[_1] set to: not in use',$lt{$type}).'</li>'; + } elsif ($newvalue eq '') { + $resulttext .= '<li>'.&mt('[_1] set to: none',$lt{$type}).'</li>'; + } else { + $resulttext .= '<li>'.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'</li>'; + } + } + } + $resulttext .= '</ul>'; + } + } + $resulttext .= '</ul>'; + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = '<span class="LC_error">'. + &mt('An error occurred: [_1]',$putresult).'</span>'; + } + } else { + $resulttext = $nochgmsg; + } + return $resulttext; +} + sub modify_loadbalancing { my ($dom,%domconfig) = @_; my $primary_id = &Apache::lonnet::domain($dom,'primary'); @@ -17070,7 +23080,7 @@ sub modify_loadbalancing { } if ($env{'form.loadbalancing_cookie_'.$i}) { $defaultshash{'loadbalancing'}{$balancer}{'cookie'} = 1; - if (exists($currbalancer{$balancer})) { + if (exists($currbalancer{$balancer})) { unless ($currcookies{$balancer}) { $changes{'curr'}{$balancer}{'cookie'} = 1; } @@ -17756,6 +23766,7 @@ function balancerChange(balnum,baltotal, END } + sub new_spares_js { my @sparestypes = ('primary','default'); my $types = join("','",@sparestypes); @@ -17829,7 +23840,7 @@ function updateNewSpares(formname,lonhos 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') { @@ -17963,7 +23974,7 @@ function toggleDisplay(domForm,caller) { var dispval = 'block'; var selfcreateRegExp = /^cancreate_emailverified/; if (caller == 'emailoptions') { - optionsElement = domForm.cancreate_email; + optionsElement = domForm.cancreate_email; } if (caller == 'studentsubmission') { optionsElement = domForm.postsubmit; @@ -18018,9 +24029,9 @@ sub devalidate_remote_domconfs { my %servers = &Apache::lonnet::internet_dom_servers($dom); my %thismachine; map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); - my @posscached = ('domainconfig','domdefaults','usersessions', + my @posscached = ('domainconfig','domdefaults','ltitools','usersessions', 'directorysrch','passwdconf','cats','proxyalias','proxysaml', - 'ipaccess'); + 'ipaccess','trust'); my %cache_by_lonhost; if (exists($cachekeys->{'samllanding'})) { if (ref($cachekeys->{'samllanding'}) eq 'HASH') {