--- loncom/interface/domainprefs.pm 2024/08/26 10:52:47 1.160.6.118.2.24 +++ loncom/interface/domainprefs.pm 2022/02/18 13:39:22 1.408 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.160.6.118.2.24 2024/08/26 10:52:47 raeburn Exp $ +# $Id: domainprefs.pm,v 1.408 2022/02/18 13:39:22 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -95,7 +95,8 @@ about default quota sizes for portfolio institutional affiliation in the domain (e.g., Faculty, Staff, Student etc.), but is now also used to manage availability of user tools: i.e., blogs, aboutme page, and portfolios, and the course request tool, -used by course owners to request creation of a course. +used by course owners to request creation of a course, and to display/store +default quota sizes for Authoring Spaces. Outputs: 1 @@ -103,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, textbook, and lti). -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: @@ -166,17 +167,16 @@ use Apache::lonmsg(); use Apache::lonconfigsettings; use Apache::lonuserutils(); use Apache::loncoursequeueadmin(); -use Apache::courseprefs(); 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; -use Crypt::CBC; my $registered_cleanup; my $modified_urls; @@ -220,27 +220,17 @@ sub handler { 'serverstatuses','requestcourses','helpsettings', 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', - 'passwords','ltitools','toolsec','lti','ltisec', - 'wafproxy','ipaccess','authordefaults'],$dom); + 'ltitools','ssl','trust','lti','ltisec','privacy','passwords', + 'proctoring','wafproxy','ipaccess'],$dom); my %encconfig = - &Apache::lonnet::get_dom('encconfig',['ltitools','lti','linkprot'],$dom,undef,1); - my ($checked_is_home,$is_home); + &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1); if (ref($domconfig{'ltitools'}) eq 'HASH') { if (ref($encconfig{'ltitools'}) eq 'HASH') { - my $home = &Apache::lonnet::domain($dom,'primary'); - unless (($home eq 'no_host') || ($home eq '')) { - my @ids=&Apache::lonnet::current_machine_ids(); - if (grep(/^\Q$home\E$/,@ids)) { - $is_home = 1; - } - } - $checked_is_home = 1; foreach my $id (keys(%{$domconfig{'ltitools'}})) { if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') && (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) { - $domconfig{'ltitools'}{$id}{'key'} = $encconfig{'ltitools'}{$id}{'key'}; - if (($is_home) && ($phase eq 'process')) { - $domconfig{'ltitools'}{$id}{'secret'} = $encconfig{'ltitools'}{$id}{'secret'}; + foreach my $item ('key','secret') { + $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item}; } } } @@ -248,22 +238,11 @@ sub handler { } if (ref($domconfig{'lti'}) eq 'HASH') { if (ref($encconfig{'lti'}) eq 'HASH') { - unless ($checked_is_home) { - my $home = &Apache::lonnet::domain($dom,'primary'); - unless (($home eq 'no_host') || ($home eq '')) { - my @ids=&Apache::lonnet::current_machine_ids(); - if (grep(/^\Q$home\E$/,@ids)) { - $is_home = 1; - } - } - $checked_is_home = 1; - } foreach my $id (keys(%{$domconfig{'lti'}})) { if ((ref($domconfig{'lti'}{$id}) eq 'HASH') && (ref($encconfig{'lti'}{$id}) eq 'HASH')) { - $domconfig{'lti'}{$id}{'key'} = $encconfig{'lti'}{$id}{'key'}; - if (($is_home) && ($phase eq 'process')) { - $domconfig{'lti'}{$id}{'secret'} = $encconfig{'lti'}{$id}{'secret'}; + foreach my $item ('key','secret') { + $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item}; } } } @@ -286,12 +265,25 @@ sub handler { } } } + 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', - 'authordefaults','ltitools','selfenrollment','usersessions','lti'); + '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'}}; @@ -335,9 +327,7 @@ sub handler { header => [{col1 => 'Setting', col2 => 'Value'}, {col1 => 'Institutional user types', - col2 => 'Name displayed'}, - {col1 => 'Mapping for missing usernames via standard log-in', - col2 => 'Rules in use'}], + col2 => 'Name displayed'}], print => \&print_defaults, modify => \&modify_defaults, }, @@ -367,11 +357,11 @@ sub handler { modify => \&modify_passwords, }, 'quotas' => - { text => 'Blogs, personal pages/timezones, portfolio/quotas', + { text => 'Blogs, personal web pages, webDAV/quotas, portfolios', help => 'Domain_Configuration_Quotas', header => [{col1 => 'User affiliation', col2 => 'Available tools', - col3 => 'Portfolio quota (MB)',}], + col3 => 'Quotas, MB; (Authoring requires role)',}], print => \&print_quotas, modify => \&modify_quotas, }, @@ -552,6 +542,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', @@ -575,27 +579,67 @@ sub handler { print => \&print_loadbalancing, modify => \&modify_loadbalancing, }, - 'ltitools' => + 'ltitools' => {text => 'External Tools (LTI)', help => 'Domain_Configuration_LTI_Tools', - header => [{col1 => 'Encryption of shared secrets', - col2 => 'Settings'}, - {col1 => 'Rules for shared secrets', - col2 => 'Settings'}, - {col1 => 'Providers', - col2 => 'Settings',}], + header => [{col1 => 'Setting', + col2 => 'Value',}], print => \&print_ltitools, modify => \&modify_ltitools, }, - 'lti' => + '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', + {col1 => 'Rules for shared secrets', col2 => 'Settings'}, - {col1 => 'Link Protectors in Courses', - col2 => 'Values'}, {col1 => 'Link Protectors', col2 => 'Settings'}, {col1 => 'Consumers', @@ -603,7 +647,7 @@ sub handler { print => \&print_lti, modify => \&modify_lti, }, - 'ipaccess' => + 'ipaccess' => {text => 'IP-based access control', help => 'Domain_Configuration_IP_Access', header => [{col1 => 'Setting', @@ -611,16 +655,6 @@ sub handler { print => \&print_ipaccess, modify => \&modify_ipaccess, }, - 'authordefaults' => - {text => 'Authoring Space defaults', - help => 'Domain_Configuration_Author_Defaults', - header => [{col1 => 'Defaults which can be overridden by Author', - col2 => 'Settings',}, - {col1 => 'Defaults which can be overridden by a Dom. Coord.', - col2 => 'Settings',},], - print => \&print_authordefaults, - modify => \&modify_authordefaults, - }, ); if (keys(%servers) > 1) { $prefs{'login'} = { text => 'Log-in page options', @@ -810,16 +844,22 @@ sub process_changes { $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') { $output = &modify_wafproxy($dom,$action,$lastactref,%domconfig); } elsif ($action eq 'ipaccess') { $output = &modify_ipaccess($dom,$lastactref,%domconfig); - } elsif ($action eq 'authordefaults') { - $output = &modify_authordefaults($dom,$lastactref,%domconfig); } return $output; } @@ -848,13 +888,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 .= &Apache::lonconfigsettings::ltitools_javascript($settings); + $output .= <itools_javascript($settings); } elsif ($action eq 'lti') { - $output .= &passwords_javascript('ltisecrets')."\n". + $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') { @@ -865,8 +907,6 @@ sub print_config_box { $output .= &saml_javascript(); } elsif ($action eq 'ipaccess') { $output .= &ipaccess_javascript($settings); - } elsif ($action eq 'authordefaults') { - $output .= &authordefaults_javascript(); } $output .= '<table class="LC_nested_outer"> @@ -906,9 +946,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 'lti') || ($action eq 'ltitools') || ($action eq 'authordefaults')) { + ($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); @@ -942,36 +982,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 'defaults') || ($action eq 'lti') || - ($action eq 'ltitools')) { + ($action eq 'usersessions') || ($action eq 'coursecategories') || + ($action eq 'trust') || ($action eq 'contacts') || + ($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); - } elsif ($action eq 'lti') { - $output .= $item->{'print'}->('upper',$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">'.&mt($item->{'header'}->[2]->{'col2'}).'</td> - </tr>'."\n". - $item->{'print'}->('middle',$dom,$settings,\$rowtotal); } else { $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal); } - my $hdridx = 2; - if ($action eq 'lti') { - $hdridx = 3; - } - $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> @@ -979,19 +1032,19 @@ sub print_config_box { <td> <table class="LC_nested"> <tr class="LC_info_row"> - <td class="LC_left_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td> - <td class="LC_right_item">'.&mt($item->{'header'}->[$hdridx]->{'col2'}).'</td> + <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); - } - $hdridx ++; - $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> @@ -1000,28 +1053,51 @@ sub print_config_box { <td> <table class="LC_nested"> <tr class="LC_info_row"> - <td class="LC_left_item'.$leftnobr.'"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'col1'}).'</td> - <td class="LC_right_item"'.$colspan.'>'.&mt($item->{'header'}->[$hdridx]->{'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 .= ' + <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 .= ' </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 'directorysrch') || ($action eq 'helpsettings') || - ($action eq 'wafproxy') || ($action eq 'authordefaults')) { + ($action eq 'defaults') || ($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).' @@ -1112,8 +1188,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') { @@ -1128,9 +1204,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).' @@ -1158,30 +1234,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') { @@ -1190,7 +1266,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>'; @@ -1198,7 +1274,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); } @@ -1224,7 +1301,7 @@ sub print_login { 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>'. @@ -1505,7 +1582,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"; @@ -1549,13 +1626,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,%samlwindow,%samlnotsso,%styleon,%styleoff); + my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso,%styleon,%styleoff); foreach my $lonhost (keys(%domservers)) { $samlurl{$lonhost} = '/adm/sso'; $styleon{$lonhost} = 'display:none'; $styleoff{$lonhost} = ''; } - if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) { + if (ref($settings->{'saml'}) eq 'HASH') { foreach my $lonhost (keys(%{$settings->{'saml'}})) { if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') { $saml{$lonhost} = 1; @@ -1564,7 +1641,6 @@ 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'; @@ -1582,12 +1658,6 @@ 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. @@ -1597,10 +1667,12 @@ 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 width="100%"><tr><th colspan="3" align="center">'.&mt('SSO').'</th></tr>'. + '<table><tr><th colspan="5" align="center">'.&mt('SSO').'</th><th align="center">'. + '<span class="LC_nobreak">'.&mt('Non-SSO').'</span></th></tr>'. '<tr><th>'.&mt('Text').'</th><th>'.&mt('Image').'</th>'. - '<th>'.&mt('Alt Text').'</th></tr>'. - '<tr'.$css_class.'><td><input type="text" name="saml_text_'.$lonhost.'" size="20" value="'. + '<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="'. $samltext{$lonhost}.'" /></td><td>'; if ($samlimg{$lonhost}) { $datatable .= '<img src="'.$samlimg{$lonhost}.'" /><br />'. @@ -1617,21 +1689,13 @@ sub print_login { $datatable .= '<input type="file" name="saml_img_'.$lonhost.'" />'; } $datatable .= '</td>'. - '<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" '. + '<td><input type="text" name="saml_alt_'.$lonhost.'" size="20" '. + 'value="'.$samlalt{$lonhost}.'" /></td>'. + '<td><input type="text" name="saml_url_'.$lonhost.'" size="8" '. 'value="'.$samlurl{$lonhost}.'" /></td>'. - '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="20">'. + '<td><textarea name="saml_title_'.$lonhost.'" rows="3" cols="15">'. $samltitle{$lonhost}.'</textarea></td>'. - '<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" '. + '<td><input type="text" name="saml_notsso_'.$lonhost.'" size="8" '. 'value="'.$samlnotsso{$lonhost}.'" /></td></tr>'. '</table></td>'. '<td id="samloptionsoff_'.$lonhost.'" style="'.$styleoff{$lonhost}.'" width="100%"> </td></tr>'; @@ -2154,7 +2218,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 .= ' '; @@ -2182,7 +2246,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.=' '; } @@ -2243,7 +2307,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>'; @@ -2276,7 +2340,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; @@ -2290,12 +2354,12 @@ sub print_quotas { } else { $context = $action; } - my ($datatable,$defaultquota,@usertools,@options,%validations); + my ($datatable,$defaultquota,$authorquota,@usertools,@options,%validations); my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my $typecount = 0; my ($css_class,%titles); if ($context eq 'requestcourses') { - @usertools = ('official','unofficial','community','textbook','lti'); + @usertools = ('official','unofficial','community','textbook','placement','lti'); @options =('norequest','approval','validate','autolimit'); %validations = &Apache::lonnet::auto_courserequest_checks($dom); %titles = &courserequest_titles(); @@ -2304,12 +2368,12 @@ sub print_quotas { @options = ('norequest','approval','automatic'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','portfolio','portaccess','timezone'); + @usertools = ('aboutme','blog','webdav','portfolio'); %titles = &tool_titles(); } if (ref($types) eq 'ARRAY') { foreach my $type (@{$types}) { - my $currdefquota; + my ($currdefquota,$currauthorquota); unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { if (ref($settings) eq 'HASH') { @@ -2318,6 +2382,9 @@ sub print_quotas { } else { $currdefquota = $settings->{$type}; } + if (ref($settings->{authorquota}) eq 'HASH') { + $currauthorquota = $settings->{authorquota}->{$type}; + } } } if (defined($usertypes->{$type})) { @@ -2405,12 +2472,9 @@ 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}) { + if ($settings->{$item}->{$type} == 0) { $checked = ''; } elsif ($settings->{$item}->{$type} == 1) { $checked = 'checked="checked" '; @@ -2435,9 +2499,13 @@ sub print_quotas { ($context eq 'requestauthor')) { $datatable .= '<td class="LC_right_item">'. - '<span class="LC_nobreak">'. + '<span class="LC_nobreak">'.&mt('Portfolio').': '. '<input type="text" name="quota_'.$type. '" value="'.$currdefquota. + '" size="5" /></span>'.(' ' x 2). + '<span class="LC_nobreak">'.&mt('Authoring').': '. + '<input type="text" name="authorquota_'.$type. + '" value="'.$currauthorquota. '" size="5" /></span></td>'; } $datatable .= '</tr>'; @@ -2446,12 +2514,16 @@ sub print_quotas { } unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { $defaultquota = '20'; + $authorquota = '500'; if (ref($settings) eq 'HASH') { if (ref($settings->{'defaultquota'}) eq 'HASH') { $defaultquota = $settings->{'defaultquota'}->{'default'}; } elsif (defined($settings->{'default'})) { $defaultquota = $settings->{'default'}; } + if (ref($settings->{'authorquota'}) eq 'HASH') { + $authorquota = $settings->{'authorquota'}->{'default'}; + } } } $typecount ++; @@ -2563,9 +2635,12 @@ sub print_quotas { $datatable .= '</td>'; unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { $datatable .= '<td class="LC_right_item">'. - '<span class="LC_nobreak">'. + '<span class="LC_nobreak">'.&mt('Portfolio').': '. '<input type="text" name="defaultquota" value="'. - $defaultquota.'" size="5" /></span></td>'; + $defaultquota.'" size="5" /></span>'.(' ' x2). + '<span class="LC_nobreak">'.&mt('Authoring').': '. + '<input type="text" name="authorquota" value="'. + $authorquota.'" size="5" /></span></td>'; } $datatable .= '</tr>'; $typecount ++; @@ -2748,7 +2823,7 @@ sub print_studentcode { my ($settings,$rowtotal) = @_; my $rownum = 0; my ($output,%current); - my @crstypes = ('official','unofficial','community','textbook','lti'); + my @crstypes = ('official','unofficial','community','textbook','placement','lti'); if (ref($settings) eq 'HASH') { if (ref($settings->{'uniquecode'}) eq 'HASH') { foreach my $type (@crstypes) { @@ -3239,20 +3314,107 @@ function toggleWAF() { ENDSCRIPT } -sub lti_javascript { - my ($dom,$settings) = @_; - my $togglejs = <i_toggle_js($dom); - my $linkprot_js = &Apache::courseprefs::linkprot_javascript(); - unless (ref($settings) eq 'HASH') { - return $togglejs.' +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; +} -'.$linkprot_js.' +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})); @@ -3270,6 +3432,7 @@ sub lti_javascript { 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[ @@ -3336,6 +3499,7 @@ sub lti_toggle_js { 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[ @@ -3434,7 +3598,7 @@ function toggleLTI(form,setting,item) { break; } } - } + } } if (!setvis) { if (document.getElementById(divid)) { @@ -3511,7 +3675,7 @@ function toggleLTI(form,setting,item) { var divid = 'lti_menufield_'+item; var setvis = ''; for (var i=0; i<menus.length; i++) { - var radioname = menus[i]; + var radioname = menus[i]; var num = form.elements[radioname].length; if (num) { for (var j=0; j<num; j++) { @@ -3539,6 +3703,124 @@ function toggleLTI(form,setting,item) { 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> @@ -3619,16 +3901,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=''; } @@ -3725,39 +4007,6 @@ $jstext ENDSCRIPT } -sub authordefaults_javascript { - my %alert = &Apache::lonlocal::texthash ( - reqd => 'Warning: at least one editor needs to be available.', - rest => 'Unchecking this editor disallowed while others unchecked.', - ); - &js_escape(\%alert); - return <<"ENDSCRIPT"; -<script type="text/javascript"> -// <![CDATA[ - -function checkEditors(form,checkbox,current) { - if (form.elements[checkbox].length != undefined) { - var count = 0; - for (var i=0; i<form.elements[checkbox].length; i++) { - if (form.elements[checkbox][i].checked) { - count ++; - } - } - if (count == 0) { - if (current.type =='radio') { - current.checked = true; - alert('$alert{reqd}\\n$alert{rest}'); - } - } - } - return; -} -// ]]> -</script> - -ENDSCRIPT -} - sub print_autoenroll { my ($dom,$settings,$rowtotal) = @_; my $autorun = &Apache::lonnet::auto_run(undef,$dom), @@ -4429,7 +4678,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">'. @@ -4511,7 +4760,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}); } @@ -4523,6 +4772,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); @@ -4589,7 +4839,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. @@ -4668,6 +4918,7 @@ function toggleHelpdeskRow(form,checkbox return; } + // ]]> </script> @@ -4772,7 +5023,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; @@ -4811,7 +5062,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++) { @@ -5092,7 +5343,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') { @@ -5121,50 +5372,421 @@ sub radiobutton_prefs { } sub print_ltitools { - my ($position,$dom,$settings,$rowtotal) = @_; - my (%rules,%encrypt,%privkeys,%linkprot); + my ($dom,$settings,$rowtotal) = @_; + my $rownum = 0; + my $css_class; + my $itemcount = 1; + my $maxnum = 0; + my %ordered; 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'}})) { - $encrypt{'toolsec_'.$key} = $settings->{'encrypt'}{$key}; + 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; } - 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'}}); + $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>'; } - } elsif ($position eq 'middle') { - if (exists($settings->{'rules'})) { - if (ref($settings->{'rules'}) eq 'HASH') { - %rules = %{$settings->{'rules'}}; + $datatable .= '</tr></table></fieldset>'; + my %courseconfig; + if (ref($settings->{$item}) eq 'HASH') { + if (ref($settings->{$item}->{'crsconf'}) eq 'HASH') { + %courseconfig = %{$settings->{$item}->{'crsconf'}}; } } - } else { - foreach my $key ('encrypt','private','rules') { - if (exists($settings->{$key})) { - delete($settings->{$key}); + $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 ++; } } - my $datatable; - my $itemcount = 1; - if ($position eq 'top') { - $datatable = &secrets_form($dom,'toolsec',\%encrypt,\%privkeys,$rowtotal); - } elsif ($position eq 'middle') { - $datatable = &password_rules('toolsecrets',\$itemcount,\%rules); - $$rowtotal += $itemcount; - } else { - $datatable = &Apache::courseprefs::print_ltitools($dom,'',$settings,\$rowtotal,'','','domain'); + $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; } @@ -5178,7 +5800,7 @@ sub ltitools_names { 'key' => 'Key', 'lifetime' => 'Nonce lifetime (s)', 'secret' => 'Secret', - 'icon' => 'Icon', + 'icon' => 'Icon', 'user' => 'User', 'fullname' => 'Full Name', 'firstname' => 'First Name', @@ -5196,7 +5818,7 @@ sub ltitools_names { 'roster' => 'Tool can retrieve roster:', 'crstarget' => 'Display target', 'crslabel' => 'Course label', - 'crstitle' => 'Course title', + 'crstitle' => 'Course title', 'crslinktext' => 'Link Text', 'crsexplanation' => 'Explanation', 'crsappend' => 'Provider URL', @@ -5204,128 +5826,645 @@ sub ltitools_names { return %lt; } -sub secrets_form { - my ($dom,$context,$encrypt,$privkeys,$rowtotal) = @_; - my @ids=&Apache::lonnet::current_machine_ids(); - my %servers = &Apache::lonnet::get_servers($dom,'library'); - my $primary = &Apache::lonnet::domain($dom,'primary'); - my ($css_class,$extra,$numshown,$itemcount,$output); - $itemcount = 0; - foreach my $hostid (sort(keys(%servers))) { - my ($showextra,$divsty,$switch); - if ($hostid eq $primary) { - if ($context eq 'ltisec') { - if (($encrypt->{'ltisec_consumers'}) || ($encrypt->{'ltisec_domlinkprot'})) { - $showextra = 1; +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'}}; + } } - if ($encrypt->{'ltisec_crslinkprot'}) { - $showextra = 1; + } + 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" '; } - } else { - if (($encrypt->{'toolsec_crs'}) || ($encrypt->{'toolsec_dom'})) { - $showextra = 1; + $datatable .= '<option value="'.$k.'"'.$selstr.'>'.$vpos.'</option>'; + } + if ($version eq '') { + if ($provider eq 'proctorio') { + $version = '1.0'; + } elsif ($provider eq 'examity') { + $version = '1.1'; } } - unless (grep(/^\Q$hostid\E$/,@ids)) { - $switch = 1; + if ($lifetime eq '') { + $lifetime = '300'; } - if ($showextra) { - $numshown ++; - $divsty = 'display:inline-block'; + $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 { - $divsty = 'display:none'; + $datatable .= '<input type="file" name="proctoring_image_'.$provider.'" value="" />'; } - $extra .= '<fieldset id="'.$context.'_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="'.$context.'_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="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" checked="checked" />'.&mt('No').'</label>'. - (' 'x2). - '<label><input type="radio" value="1" name="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" />'.&mt('Yes'). - '</label> </span><div id="'.$context.'_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"; + 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> '; + } } - } elsif (exists($privkeys->{$hostid})) { - $extra .= '<div id="'.$context.'_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="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" checked="checked" />'.&mt('No').'</label>'. - (' 'x2). - '<label><input type="radio" value="1" name="'.$context.'_changeprivkey_'.$hostid.'" onclick="javascript:togglePrivKey(this.form,'."'$context','$hostid'".');" />'.&mt('Yes'). - '</label> </span><div id="'.$context.'_divchgprivkey_'.$hostid.'" style="display:none" />'. - '<span class="LC_nobreak">'.&mt('New Key').':'. - '<input type="password" size="20" name="'.$context.'_privkey_'.$hostid.'" value="" autocomplete="new-password" />'. - '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.'.$context.'_privkey_'.$hostid.'.type='."'text'".' } else { this.form.'.$context.'_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="'.$context.'_privkey_'.$hostid.'" value="" autocomplete="new-password" />'. - '<label><input type="checkbox" name="visible" onclick="if (this.checked) { this.form.'.$context.'_privkey_'.$hostid.'.type='."'text'".' } else { this.form.'.$context.'_privkey_'.$hostid.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'; + $datatable .= '</fieldset>'."\n"; } - $extra .= '</fieldset>'; + 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 ++; } } - my (%choices,@toggles,%defaultchecked); - if ($context eq 'ltisec') { - %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', - ); - @toggles = qw(ltisec_crslinkprot ltisec_domlinkprot ltisec_consumers); - %defaultchecked = ( - 'ltisec_crslinkprot' => 'off', - 'ltisec_domlinkprot' => 'off', - 'ltisec_consumers' => 'off', - ); - } else { - %choices = &Apache::lonlocal::texthash ( - toolsec_crs => 'Encrypt stored external tool secrets defined in courses', - toolsec_dom => 'Encrypt stored external tool secrets defined in domain', - ); - @toggles = qw(toolsec_crs toolsec_dom); - %defaultchecked = ( - 'toolsec_crs' => 'off', - 'toolsec_dom' => 'off', - ); + 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 ($onclick,$itemcount); - $onclick = 'javascript:toggleLTIEncKey(this.form,'."'$context'".');'; - ($output,$itemcount) = &radiobutton_prefs($encrypt,\@toggles,\%defaultchecked, - \%choices,$itemcount,$onclick,'','left','no'); + my %lt = (%common_lt,%custom_lt); + return %lt; +} - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $noprivkeysty = 'display:inline-block'; - if ($numshown) { - $noprivkeysty = 'display:none'; - } - $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.&mt('Encryption Key(s)').'</td>'. - '<td><div id="'.$context.'_noprivkey" style="'.$noprivkeysty.'" >'. - '<span class="LC_nobreak">'.&mt('Not in use').'</span></div>'. - $extra. - '</td></tr>'; - $itemcount ++; - $$rowtotal += $itemcount; - return $output; +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,%suggestions); + my (%rules,%encrypt,%privkeys,%linkprot); if (ref($settings) eq 'HASH') { if ($position eq 'top') { if (exists($settings->{'encrypt'})) { @@ -5348,18 +6487,12 @@ sub print_lti { } } } - } elsif ($position eq 'upper') { + } elsif ($position eq 'middle') { if (exists($settings->{'rules'})) { if (ref($settings->{'rules'}) eq 'HASH') { %rules = %{$settings->{'rules'}}; } } - } elsif ($position eq 'middle') { - if (exists($settings->{'suggested'})) { - if (ref($settings->{'suggested'}) eq 'HASH') { - %suggestions = %{$settings->{'suggested'}}; - } - } } elsif ($position eq 'lower') { if (exists($settings->{'linkprot'})) { if (ref($settings->{'linkprot'}) eq 'HASH') { @@ -5370,7 +6503,7 @@ sub print_lti { } } } else { - foreach my $key ('encrypt','private','rules','linkprot','suggestions') { + foreach my $key ('encrypt','private','rules','linkprot') { if (exists($settings->{$key})) { delete($settings->{$key}); } @@ -5378,19 +6511,104 @@ sub print_lti { } } if ($position eq 'top') { - $datatable = &secrets_form($dom,'ltisec',\%encrypt,\%privkeys,$rowtotal); - } elsif ($position eq 'upper') { - $datatable = &password_rules('ltisecrets',\$itemcount,\%rules); + 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="off" />'. + '<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="off" />'. + '<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 = &linkprot_suggestions(\%suggestions,\$itemcount); + $datatable = &password_rules('secrets',\$itemcount,\%rules); $$rowtotal += $itemcount; } elsif ($position eq 'lower') { - $datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain'); + $datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain'); } else { - my ($switchserver,$switchmessage); - $switchserver = &check_switchserver($dom); - $switchmessage = &mt("submit from domain's primary library server: [_1].",$switchserver); my $maxnum = 0; my %ordered; if (ref($settings) eq 'HASH') { @@ -5411,10 +6629,10 @@ sub print_lti { for (my $i=0; $i<@items; $i++) { $css_class = $itemcount%2?' class="LC_odd_row"':''; my $item = $ordered{$items[$i]}; - my ($key,$secret,$usable,$lifetime,$consumer,$requser,$crsinc,$current); + my ($key,$secret,$lifetime,$consumer,$requser,$crsinc,$current); if (ref($settings->{$item}) eq 'HASH') { $key = $settings->{$item}->{'key'}; - $usable = $settings->{$item}->{'usable'}; + $secret = $settings->{$item}->{'secret'}; $lifetime = $settings->{$item}->{'lifetime'}; $consumer = $settings->{$item}->{'consumer'}; $requser = $settings->{$item}->{'requser'}; @@ -5462,56 +6680,8 @@ sub print_lti { '<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><br /><br />'; - if ($key ne '') { - $datatable .= '<span class="LC_nobreak">'.$lt{'key'}; - if ($switchserver) { - $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']'; - } else { - $datatable .= ':<input type="text" size="25" name="lti_key_'.$i.'" value="'.$key.'" autocomplete="off" />'; - } - $datatable .= '</span> '.(' 'x2); - } elsif (!$switchserver) { - $datatable .= '<span class="LC_nobreak">'.$lt{'key'}.':'. - '<input type="text" size="25" name="lti_key_'.$i.'" value="'.$key.'" autocomplete="off" />'. - '</span> '.(' 'x2); - } - if ($switchserver) { - if ($usable ne '') { - $datatable .= '<div id="lti_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'. - $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'. - '<span class="LC_nobreak">'.&mt('Change secret?'). - '<label><input type="radio" value="0" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" checked="checked" />'.&mt('No').'</label>'. - (' 'x2). - '<label><input type="radio" value="1" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" />'.&mt('Yes').'</label>'.(' 'x2). - '</span><div id="lti_divchgsecret_'.$i.'" style="display:none" />'. - '<span class="LC_nobreak"> - '.$switchmessage.'</span>'. - '</div>'; - } elsif ($key eq '') { - $datatable .= '<span class="LC_nobreak">'.&mt('Key and Secret are required').' - '.$switchmessage.'</span>'."\n"; - } else { - $datatable .= '<span class="LC_nobreak">'.&mt('Secret required').' - '.$switchmessage.'</span>'."\n"; - } - } else { - if ($usable ne '') { - $datatable .= '<div id="lti_divcurrsecret_'.$i.'" style="display:inline-block" /><span class="LC_nobreak">'. - $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'</span></div>'. - '<span class="LC_nobreak">'.&mt('Change?'). - '<label><input type="radio" value="0" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" checked="checked" />'.&mt('No').'</label>'. - (' 'x2). - '<label><input type="radio" value="1" name="lti_changesecret_'.$i.'" onclick="javascript:toggleChgSecret(this.form,'."'$i','secret','lti'".');" />'.&mt('Yes'). - '</label> </span><div id="lti_divchgsecret_'.$i.'" style="display:none" />'. - '<span class="LC_nobreak">'.&mt('New Secret').':'. - '<input type="password" size="20" name="lti_secret_'.$i.'" value="" autocomplete="new-password" />'. - '<label><input type="checkbox" name="lti_visible_'.$i.'" id="lti_visible_'.$i.'" onclick="if (this.checked) { this.form.lti_secret_'.$i.'.type='."'text'".' } else { this.form.lti_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label></span></div>'; - } else { - $datatable .= - '<span class="LC_nobreak">'.$lt{'secret'}.':'. - '<input type="password" size="20" name="lti_secret_'.$i.'" value="" autocomplete="new-password" />'. - '<label><input type="checkbox" name="lti_visible_'.$i.'" id="lti_visible_'.$i.'" onclick="if (this.checked) { this.form.lti_secret_'.$i.'.type='."'text'".' } else { this.form.lti_secret_'.$i.'.type='."'password'".' }" />'.&mt('Visible input').'</label>'; - } - } - $datatable .= '<br /><br />'. + '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". @@ -5520,6 +6690,12 @@ sub print_lti { '<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 ++; @@ -5548,16 +6724,8 @@ sub print_lti { '<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><br /><br />'."\n"; - if ($switchserver) { - $datatable .= '<span class="LC_nobreak">'.&mt('Key and Secret are required').' - '.$switchmessage.'</span>'."\n"; - } else { - $datatable .= '<span class="LC_nobreak">'.$lt{'key'}.':<input type="text" size="25" name="lti_key_add" value="" autocomplete="off" /></span> '."\n". - (' 'x2). - '<span class="LC_nobreak">'.$lt{'secret'}.':<input type="password" size="20" name="lti_secret_add" value="" autocomplete="new-password" />'. - '<label><input type="checkbox" name="lti_add_visible" id="lti_add_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"; - } - $datatable .= '<br /><br />'. + '<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". @@ -5565,6 +6733,11 @@ sub print_lti { '<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"; @@ -5637,7 +6810,7 @@ sub lti_options { 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"'; + $checked{'mapuser'}{'email'} = ' checked="checked"'; } else { $checked{'mapuser'}{'other'} = ' checked="checked"'; $userfield = $current->{'mapuser'}; @@ -5647,7 +6820,7 @@ sub lti_options { 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"'; + $checked{'mapcrs'}{'context_id'} = ' checked="checked"'; } else { $checked{'mapcrs'}{'other'} = ' checked="checked"'; $cidfield = $current->{'mapcrs'}; @@ -5675,7 +6848,7 @@ sub lti_options { $checked{'lcauth'}{$1} = ' checked="checked"'; unless (($current->{'lcauth'} eq 'lti') || ($current->{'lcauth'} eq 'internal')) { $lcauthparm = $current->{'lcauthparm'}; - $lcauthparmstyle = 'display:table-row'; + $lcauthparmstyle = 'display:table-row'; if ($current->{'lcauth'} eq 'localauth') { $lcauthparmtext = &mt('Local auth argument'); } else { @@ -5692,7 +6865,7 @@ sub lti_options { %rolemaps = %{$current->{'maproles'}}; } if ($current->{'section'} ne '') { - $checked{'crssec'}{'Y'} = ' checked="checked"'; + $checked{'crssec'}{'Y'} = ' checked="checked"'; $crssecfieldsty = 'inline-block'; if ($current->{'section'} eq 'course_section_sourcedid') { $checked{'crssecsrc'}{'sourcedid'} = ' checked="checked"'; @@ -5738,16 +6911,17 @@ sub lti_options { $checked{'crssec'}{'N'} = ' checked="checked"'; $checked{'callback'}{'N'} = ' checked="checked"'; $checked{'topmenu'}{'N'} = ' checked="checked"'; - $checked{'inlinemenu'}{'Y'} = ' checked="checked"'; + $checked{'inlinemenu'}{'Y'} = ' checked="checked"'; $checked{'menuitem'}{'grades'} = ' checked="checked"'; - $menusty = 'inline-block'; + $menusty = 'inline-block'; } - my @coursetypes = ('official','unofficial','community','textbook','lti'); + 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'); @@ -5792,7 +6966,7 @@ sub lti_options { '<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> '; + $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>'. @@ -5924,9 +7098,9 @@ sub lti_options { if ($extra eq 'passback') { $pb1p1chk = ' checked="checked"'; $pb1p0chk = ''; - $onclickpb = ' onclick="toggleLTI(this.form,'."'passback','$num'".');"'; + $onclickpb = ' onclick="toggleLTI(this.form,'."'passback','$num'".');"'; } else { - $onclickpb = ''; + $onclickpb = ''; } if (ref($current) eq 'HASH') { if (($current->{$extra})) { @@ -5972,56 +7146,20 @@ sub ltimenu_titles { ); } -sub linkprot_suggestions { - my ($suggested,$itemcount) = @_; - my $count = 0; - my $next = 1; - my %lt = &Apache::lonlocal::texthash( - 'name' => 'Suggested Launcher', - 'info' => 'Recommendations', - ); - my ($datatable,$css_class,$dest); - if (ref($suggested) eq 'HASH') { - my @current = sort { $a <=> $b } keys(%{$suggested}); - $next += $current[-1]; - for (my $i=0; $i<@current; $i++) { - my $num = $current[$i]; - my %values; - if (ref($suggested->{$num}) eq 'HASH') { - %values = %{$suggested->{$num}}; - } else { - next; - } - $css_class = $$itemcount%2?' class="LC_odd_row"':''; - $datatable .= - '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n". - '<label><input type="checkbox" name="linkprot_suggested_del" value="'.$i.'" />'."\n". - &mt('Delete?').'</label></span></td><td>'."\n". - '<div class="LC_floatleft"><fieldset><legend>'.$lt{'name'}.'</legend>'."\n". - '<input type="text" size="15" name="linkprot_suggested_name_'.$i.'" value="'.$values{'name'}.'" autocomplete="off" />'."\n". - '</fieldset></div>'. - '<div class="LC_floatleft"><fieldset><legend>'.$lt{'info'}.'</legend>'."\n". - '<textarea cols="55" rows="5" name="linkprot_suggested_info_'.$i.'">'.$values{'info'}.'</textarea>'. - '</fieldset></div>'. - '<div style="padding:0;clear:both;margin:0;border:0"></div>'."\n". - '<input type="hidden" name="linkprot_suggested_id_'.$i.'" value="'.$num.'" /></td></tr>'."\n"; - $$itemcount ++; +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>'; } } - $css_class = $$itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr '.$css_class.'><td><span class="LC_nobreak">'."\n". - '<input type="hidden" name="linkprot_suggested_maxnum" value="'.$next.'" />'."\n". - '<input type="checkbox" name="linkprot_suggested_add" value="1" />'.&mt('Add').'</span></td>'."\n". - '<td>'."\n". - '<div class="LC_floatleft"><fieldset><legend>'.$lt{'name'}.'</legend>'."\n". - '<input type="text" size="15" name="linkprot_suggested_name_add" value="" autocomplete="off" />'."\n". - '</fieldset></div>'. - '<div class="LC_floatleft"><fieldset><legend>'.$lt{'info'}.'</legend>'."\n". - '<textarea cols="55" rows="5" name="linkprot_suggested_info_add"></textarea>'. - '</fieldset></div>'. - '<div style="padding:0;clear:both;margin:0;border:0"></div>'."\n". - '</td></tr>'."\n"; - return $datatable; + return $switchserver; } sub print_coursedefaults { @@ -6029,8 +7167,8 @@ sub print_coursedefaults { 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)', - coursequota => 'Default cumulative quota for all group portfolio spaces in course (MB)', anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys', coursecredits => 'Credits can be specified for courses', uselcmath => 'Math preview uses LON-CAPA previewer (javascript) in place of DragMath (Java)', @@ -6041,28 +7179,22 @@ sub print_coursedefaults { 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', - domexttool => 'External Tools defined in the domain may be used in courses/communities (by type)', - exttool => 'External Tools can be defined and configured in courses/communities (by type)', - crseditors => 'Available editors for web pages and/or problems created in a course/community', ); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, - coursequota => 20, postsubmit => 60, mysqltables => 172800, - domexttool => 1, - exttool => 0, - crseditors => ['edit','xml'], ); 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'}) { @@ -6095,7 +7227,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'; @@ -6166,59 +7298,21 @@ sub print_coursedefaults { $itemcount ++; } else { $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - my ($currdefresponder,%defcredits,%curruploadquota,%currcoursequota, - %deftimeout,%currmysql); + my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql); my $currusecredits = 0; my $postsubmitclient = 1; my $ltiauth = 0; - my %domexttool; - my %exttool; - my %crseditors; - my @types = ('official','unofficial','community','textbook'); + my @types = ('official','unofficial','community','textbook','placement'); if (ref($settings) eq 'HASH') { if ($settings->{'ltiauth'}) { $ltiauth = 1; } - if (ref($settings->{'domexttool'}) eq 'HASH') { - foreach my $type (@types) { - if ($settings->{'domexttool'}->{$type}) { - $domexttool{$type} = ' checked="checked"'; - } - } - } else { - foreach my $type (@types) { - if ($staticdefaults{'domexttool'}) { - $domexttool{$type} = ' checked="checked"'; - } - } - } - if (ref($settings->{'exttool'}) eq 'HASH') { - foreach my $type (@types) { - if ($settings->{'exttool'}->{$type}) { - $exttool{$type} = ' checked="checked"'; - } - } - } - if (ref($settings->{'crseditors'}) eq 'ARRAY') { - foreach my $editor (@{$settings->{'crseditors'}}) { - $crseditors{$editor} = ' checked="checked"'; - } - } else { - foreach my $editor (@{$staticdefaults{'crseditors'}}) { - $crseditors{$editor} = ' checked="checked"'; - } - } $currdefresponder = $settings->{'anonsurvey_threshold'}; if (ref($settings->{'uploadquota'}) eq 'HASH') { foreach my $type (keys(%{$settings->{'uploadquota'}})) { $curruploadquota{$type} = $settings->{'uploadquota'}{$type}; } } - if (ref($settings->{'coursequota'}) eq 'HASH') { - foreach my $type (keys(%{$settings->{'coursequota'}})) { - $currcoursequota{$type} = $settings->{'coursequota'}{$type}; - } - } if (ref($settings->{'coursecredits'}) eq 'HASH') { foreach my $type (@types) { next if ($type eq 'community'); @@ -6264,12 +7358,6 @@ sub print_coursedefaults { } else { foreach my $type (@types) { $deftimeout{$type} = $staticdefaults{'postsubmit'}; - if ($staticdefaults{'domexttool'}) { - $domexttool{$type} = ' checked="checked"'; - } - } - foreach my $editor (@{$staticdefaults{'crseditors'}}) { - $crseditors{$editor} = ' checked="checked"'; } } if (!$currdefresponder) { @@ -6281,9 +7369,6 @@ sub print_coursedefaults { if ($curruploadquota{$type} eq '') { $curruploadquota{$type} = $staticdefaults{'uploadquota'}; } - if ($currcoursequota{$type} eq '') { - $currcoursequota{$type} = $staticdefaults{'coursequota'}; - } } $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. @@ -6298,25 +7383,12 @@ sub print_coursedefaults { $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. $choices{'uploadquota'}. '</span></td>'. - '<td align="right" class="LC_right_item">'. - '<table><tr>'; - foreach my $type (@types) { - $datatable .= '<td align="center">'.&mt($type).'<br />'. - '<input type="text" name="uploadquota_'.$type.'"'. - ' value="'.$curruploadquota{$type}.'" size="5" /></td>'; - } - $datatable .= '</tr></table></td></tr>'."\n"; - $itemcount ++; - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. - $choices{'coursequota'}. - '</span></td>'. '<td style="text-align: right" class="LC_right_item">'. '<table><tr>'; foreach my $type (@types) { $datatable .= '<td style="text-align: center">'.&mt($type).'<br />'. - '<input type="text" name="coursequota_'.$type.'"'. - ' value="'.$currcoursequota{$type}.'" size="5" /></td>'; + '<input type="text" name="uploadquota_'.$type.'"'. + ' value="'.$curruploadquota{$type}.'" size="5" /></td>'; } $datatable .= '</tr></table></td></tr>'."\n"; $itemcount ++; @@ -6329,7 +7401,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>'; } @@ -6353,7 +7425,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>'; } @@ -6371,10 +7443,10 @@ 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>'; } @@ -6389,290 +7461,17 @@ sub print_coursedefaults { &radiobutton_prefs($current,\@toggles,\%defaultchecked, \%choices,$itemcount,undef,undef,'left'); $datatable .= $table; - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. - $choices{'domexttool'}. - '</span></td>'. - '<td style="text-align: right" class="LC_right_item">'. - '<table><tr>'; - foreach my $type (@types) { - $datatable .= '<td style="text-align: left">'. - '<span class="LC_nobreak">'. - '<label><input type="checkbox" name="domexttool"'. - ' value="'.$type.'"'.$domexttool{$type}.' />'. - &mt($type).'</label></span></td>'."\n"; - } - $datatable .= '</tr></table></td></tr>'."\n"; - $itemcount ++; - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. - $choices{'exttool'}. - '</span></td>'. - '<td style="text-align: right" class="LC_right_item">'. - '<table><tr>'; - foreach my $type (@types) { - $datatable .= '<td style="text-align: left">'. - '<span class="LC_nobreak">'. - '<label><input type="checkbox" name="exttool"'. - ' value="'.$type.'"'.$exttool{$type}.' />'. - &mt($type).'</label></span></td>'."\n"; - } - $datatable .= '</tr></table></td></tr>'."\n"; - $itemcount ++; - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - $datatable .= '<tr'.$css_class.'><td><span class="LC_nobreak">'. - $choices{'crseditors'}. - '</span></td>'. - '<td style="text-align: right" class="LC_right_item">'. - '<table><tr>'; - my @editors = ('edit','xml','daxe'); - my %editornames = &crseditor_titles(); - foreach my $editor (@editors) { - $datatable .= '<td style="text-align: left">'. - '<span class="LC_nobreak">'. - '<label><input type="checkbox" name="crseditors"'. - ' value="'.$editor.'"'.$crseditors{$editor}.' />'. - $editornames{$editor}.'</label></span></td>'."\n"; - } - $datatable .= '</tr></table></td></tr>'."\n"; - } - $$rowtotal += $itemcount; - return $datatable; -} - -sub crseditor_titles { - return &Apache::lonlocal::texthash( - edit => 'Standard editor (Edit)', - xml => 'Text editor (EditXML)', - daxe => 'Daxe editor (Daxe)', - ); -} - -sub print_authordefaults { - my ($position,$dom,$settings,$rowtotal) = @_; - my ($css_class,$datatable,%checkedon,%checkedoff); - my $itemcount = 1; - my %titles = &authordefaults_titles(); - if ($position eq 'top') { - my %defaultchecked = ( - 'nocodemirror' => 'off', - 'daxecollapse' => 'off', - 'domcoordacc' => 'on', - ); - my @toggles = ('nocodemirror','daxecollapse','domcoordacc'); - ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked, - \%titles,$itemcount); - my %staticdefaults = ( - 'copyright' => 'default', - 'sourceavail' => 'closed', - ); - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my %currrights; - foreach my $item ('copyright','sourceavail') { - $currrights{$item} = $staticdefaults{$item}; - if (ref($settings) eq 'HASH') { - if (exists($settings->{$item})) { - $currrights{$item} = $settings->{$item}; - } - } - } - $datatable .= '<tr'.$css_class.'><td style="vertical-align: top">'. - '<span class="LC_nobreak">'.$titles{'copyright'}. - '</span></td><td class="LC_right_item">'. - &selectbox('copyright',$currrights{'copyright'},'', - \&Apache::loncommon::copyrightdescription, - (grep !/^priv|custom$/,(&Apache::loncommon::copyrightids))). - '</td></tr>'."\n"; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr'.$css_class.'><td style="vertical-align: top">'. - '<span class="LC_nobreak">'.$titles{'sourceavail'}. - '</span></td><td class="LC_right_item">'. - &selectbox('sourceavail',$currrights{'sourceavail'},'', - \&Apache::loncommon::source_copyrightdescription, - (&Apache::loncommon::source_copyrightids)). - '</td></tr>'."\n"; - } else { - $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - my $curreditors; - my %staticdefaults = ( - editors => ['edit','xml'], - authorquota => 500, - webdav => 0, - ); - my $curreditors = $staticdefaults{'editors'}; - if ((ref($settings) eq 'HASH') && - (ref($settings->{'editors'}) eq 'ARRAY')) { - $curreditors = $settings->{'editors'}; - } else { - $curreditors = $staticdefaults{'editors'}; - } - my @editors = ('edit','xml','daxe'); - $datatable = '<tr'.$css_class.'>'."\n". - '<td>'.$titles{'editors'}.'</td>'."\n". - '<td class="LC_left_item">'."\n". - '<span class="LC_nobreak">'; - foreach my $editor (@editors) { - my $checked; - if (grep(/^\Q$editor\E$/,@{$curreditors})) { - $checked = ' checked="checked"'; - } - $datatable .= '<label>'. - '<input type="checkbox" name="author_editors" '. - $checked.' value="'.$editor.'" '. - 'onclick="javascript:checkEditors(this.form,'."'author_editors'".',this);" />'. - $titles{$editor}.'</label> '; - } - $datatable .= '</span>'."\n".'</td>'."\n".'</tr>'."\n"; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); - my @insttypes; - if (ref($types) eq 'ARRAY') { - @insttypes = @{$types}; - } - my $typecount = 0; - my %domconf = &Apache::lonnet::get_dom('configuration',['quotas'],$dom); - my @items = ('webdav','authorquota'); - my %quotas; - if (ref($domconf{'quotas'}) eq 'HASH') { - %quotas = %{$domconf{'quotas'}}; - foreach my $item (@items) { - if (ref($quotas{$item}) eq 'HASH') { - foreach my $type (@insttypes,'default') { - if ($item eq 'authorquota') { - if ($quotas{$item}{$type} !~ /^\d+$/) { - $quotas{$item}{$type} = $staticdefaults{$item}; - } - } elsif ($item eq 'webdav') { - if ($quotas{$item}{$type} !~ /^(0|1)$/) { - $quotas{$item}{$type} = $staticdefaults{$item}; - } - } - } - } else { - foreach my $type (@insttypes,'default') { - $quotas{$item}{$type} = $staticdefaults{$item}; - } - } - } - } else { - foreach my $item (@items) { - foreach my $type (@insttypes,'default') { - $quotas{$item}{$type} = $staticdefaults{$item}; - } - } - } - if (ref($usertypes) eq 'HASH') { - my $numinrow = 4; - my $onclick = ''; - $datatable .= &insttypes_row(\%quotas,$types,$usertypes,$dom, - $numinrow,$othertitle,'authorquota', - \$itemcount,$onclick); - $itemcount ++; - $datatable .= &insttypes_row(\%quotas,$types,$usertypes,$dom, - $numinrow,$othertitle,'webdav', - \$itemcount); - $itemcount ++; - } - my $checkedno = ' checked="checked"'; - my ($checkedon,$checkedoff); - if (ref($quotas{'webdav'}) eq 'HASH') { - if ($quotas{'webdav'}{'_LC_adv'} =~ /^0|1$/) { - if ($quotas{'webdav'}{'_LC_adv'}) { - $checkedon = $checkedno; - } else { - $checkedoff = $checkedno; - } - undef($checkedno); - } - } - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= '<tr'.$css_class.'>'. - '<td>'.$titles{'webdav_LC_adv'}.'<br />'. - $titles{'webdav_LC_adv_over'}. - '</td>'. - '<td class="LC_left_item">'; - foreach my $option ('none','off','on') { - my ($text,$val,$checked); - if ($option eq 'none') { - $text = $titles{'none'}; - $val = ''; - $checked = $checkedno; - } elsif ($option eq 'off') { - $text = $titles{'overoff'}; - $val = 0; - $checked = $checkedoff; - } elsif ($option eq 'on') { - $text = $titles{'overon'}; - $val = 1; - $checked = $checkedon; - } - $datatable .= '<span class="LC_nobreak"><label>'. - '<input type="radio" name="webdav_LC_adv"'. - ' value="'.$val.'"'.$checked.' />'. - $text.'</label></span> '; - } - $datatable .= '</td></tr>'; - $itemcount ++; - my %defchecked = ( - 'archive' => 'off', - ); - my @toggles = ('archive'); - (my $archive,$itemcount) = &radiobutton_prefs($settings,['archive'], - {'archive' => 'off'}, - \%titles,$itemcount); - $datatable .= $archive."\n"; $itemcount ++; } $$rowtotal += $itemcount; return $datatable; } -sub authordefaults_titles { - return &Apache::lonlocal::texthash( - copyright => 'Copyright/Distribution', - sourceavail => ' Source Available', - editors => 'Available Editors', - webdav => 'WebDAV', - authorquota => 'Authoring Space quotas (MB)', - nocodemirror => 'Deactivate CodeMirror for EditXML editor', - daxecollapse => 'Daxe editor: LON-CAPA standard menus start collapsed', - domcoordacc => 'Dom. Coords. can enter Authoring Spaces in domain', - edit => 'Standard editor (Edit)', - xml => 'Text editor (EditXML)', - daxe => 'Daxe editor (Daxe)', - webdav_LC_adv => 'WebDAV access for LON-CAPA "advanced" users', - webdav_LC_adv_over => '(overrides access based on affiliation, if set)', - none => 'No override set', - overon => 'Override -- webDAV on', - overoff => 'Override -- webDAV off', - archive => 'Authors can download tar.gz file of Authoring Space', - ); -} - -sub selectbox { - my ($name,$value,$readonly,$functionref,@idlist)=@_; - my $selout = '<select name="'.$name.'">'; - foreach my $id (@idlist) { - $selout.='<option value="'.$id.'"'; - if ($id eq $value) { - $selout.=' selected="selected"'; - } - if ($readonly) { - $selout .= ' disabled="disabled"'; - } - $selout.='>'.&{$functionref}($id).'</option>'; - } - $selout.='</select>'; - return $selout; -} - 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(); @@ -6893,6 +7692,143 @@ 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; + unless ($position eq 'top') { + @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', + ); + $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', + ); + 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 ++; + } + } 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); @@ -6914,6 +7850,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') { @@ -7203,7 +8140,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>'. @@ -7240,7 +8177,7 @@ sub print_passwords { sub password_rules { my ($prefix,$itemcountref,$settings) = @_; - my ($min,$max,%chars,$numsaved,$numinrow); + my ($min,$max,%chars,$expire,$numsaved,$numinrow); my %titles; if ($prefix eq 'passwords') { %titles = &Apache::lonlocal::texthash ( @@ -7248,7 +8185,7 @@ sub password_rules { max => 'Maximum password length', chars => 'Required characters', ); - } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { + } elsif ($prefix eq 'secrets') { %titles = &Apache::lonlocal::texthash ( min => 'Minimum secret length', max => 'Maximum secret length', @@ -7271,7 +8208,10 @@ sub password_rules { if (ref($settings->{chars}) eq 'ARRAY') { map { $chars{$_} = 1; } (@{$settings->{chars}}); } - if ($prefix eq 'passwords') { + if ($prefix eq 'passwords') { + if ($settings->{expire}) { + $expire = $settings->{expire}; + } if ($settings->{numsaved}) { $numsaved = $settings->{numsaved}; } @@ -7333,8 +8273,17 @@ sub password_rules { $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" '. @@ -7402,7 +8351,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') { @@ -7419,9 +8368,8 @@ sub print_wafproxy { my $dom_in_effect; my $aliasrows = '<tr>'. '<td class="LC_left_item" style="vertical-align: baseline;">'. - &mt('Hostname').': '. - '<span class="LC_nobreak LC_cusr_emph">'. - &Apache::lonnet::hostname($server).'</span></td><td> </td>'; + &mt('Hostname').': '. + '<i>'.&Apache::lonnet::hostname($server).'</i></td><td> </td>'; if ($othercontrol{$server}) { $dom_in_effect = $othercontrol{$server}; my ($current,$forsaml); @@ -7468,10 +8416,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; @@ -7659,15 +8607,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); @@ -7689,116 +8636,252 @@ sub print_usersessions { '</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.'). - '</td></tr>'; + 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); } } } @@ -7806,10 +8889,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); @@ -7826,7 +8959,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); @@ -8146,7 +9285,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">'; @@ -8179,7 +9318,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 />'; @@ -8190,7 +9329,7 @@ sub print_loadbalancing { no => ' checked="checked"', ); my %balcookiechecked = ( - no => ' checked="checked"', + no => ' checked="checked"', ); foreach my $sparetype (@sparestypes) { my $targettable; @@ -8411,7 +9550,7 @@ sub loadbalance_rule_row { $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"; @@ -8545,13 +9684,13 @@ sub tool_titles { my %titles = &Apache::lonlocal::texthash ( aboutme => 'Personal web page', blog => 'Blog', + webdav => 'WebDAV', portfolio => 'Portfolio', - portaccess => 'Share portfolio files', - timezone => 'Can set time zone', official => 'Official courses (with institutional codes)', unofficial => 'Unofficial courses', community => 'Communities', textbook => 'Textbook courses', + placement => 'Placement tests', ); return %titles; } @@ -8562,9 +9701,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)', @@ -8653,7 +9793,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) { @@ -8775,7 +9915,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, @@ -8912,7 +10052,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, @@ -8939,8 +10079,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, @@ -9042,7 +10182,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') { @@ -9065,10 +10205,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.'"'; @@ -9083,10 +10223,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', @@ -9096,7 +10236,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 ( @@ -9211,7 +10351,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 '') { @@ -9327,7 +10467,7 @@ sub user_formats_row { 'username' => 'new usernames', 'id' => 'IDs', ); - unless (($type eq 'email') || ($type eq 'unamemap')) { + unless ($type eq 'email') { my $css_class = $rowcount%2?' class="LC_odd_row"':''; $output = '<tr '.$css_class.'>'. '<td><span class="LC_nobreak">'. @@ -9382,9 +10522,9 @@ sub user_formats_row { } elsif ($colsleft == 1) { $output .= '<td class="LC_left_item"> </td>'; } - $output .= '</tr>'; - unless (($type eq 'email') || ($type eq 'unamemap')) { - $output .= '</table></td></tr>'; + $output .= '</tr></table>'; + unless ($type eq 'email') { + $output .= '</td></tr>'; } return $output; } @@ -9516,34 +10656,18 @@ sub print_defaults { } elsif ($item eq 'lang_def') { my $includeempty = 1; $datatable .= &Apache::loncommon::select_language($item,$defaults{$item},$includeempty); - } elsif ($item eq 'portal_def') { - $datatable .= '<input type="text" name="'.$item.'" value="'. - $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}.'" />'; + my $size; + if ($item eq 'portal_def') { + $size = ' size="25"'; + } + $datatable .= '<input type="text" name="'.$item.'" value="'. + $defaults{$item}.'"'.$size.' />'; } $datatable .= '</td></tr>'; $rownum ++; } - } elsif ($position eq 'middle') { + } else { my %defaults; if (ref($settings) eq 'HASH') { if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) { @@ -9567,7 +10691,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>'; } @@ -9586,29 +10710,13 @@ 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; @@ -9634,8 +10742,6 @@ 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', @@ -9821,7 +10927,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 />'; } @@ -9868,13 +10974,10 @@ sub legacy_scantronformat { my ($url,$error); my @statinfo = &Apache::lonnet::stat_file($newurl); if ((!@statinfo) || ($statinfo[0] eq 'no_such_dir')) { - my $modified = []; (my $result,$url) = - &Apache::lonconfigsettings::publishlogo($r,'copy',$legacyfile,$dom,$confname, - 'scantron','','',$newfile,$modified); - if ($result eq 'ok') { - &update_modify_urls($r,$modified); - } else { + &publishlogo($r,'copy',$legacyfile,$dom,$confname,'scantron', + '','',$newfile); + if ($result ne 'ok') { $error = &mt("An error occurred publishing the [_1] bubblesheet format file in RES space. Error was: [_2].",$newfile,$result); } } @@ -10059,6 +11162,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') { @@ -10077,17 +11184,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>'. @@ -10117,8 +11235,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; @@ -10143,12 +11275,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; @@ -10169,7 +11304,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') { @@ -10192,7 +11327,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>'; @@ -10224,7 +11359,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'".');"'; @@ -10313,31 +11448,14 @@ 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 '') { @@ -10391,17 +11509,6 @@ $jstext return; } -$portal_js - -// ]]> -</script> - -ENDSCRIPT - } else { -return <<"ENDSCRIPT"; -<script type="text/javascript"> -// <![CDATA[ -$portal_js // ]]> </script> @@ -10419,9 +11526,10 @@ sub passwords_javascript { 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 'ltisecrets') || ($prefix eq 'toolsecrets')) { + } 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).', @@ -10429,7 +11537,7 @@ sub passwords_javascript { } &js_escape(\%intalert); my $defmin = $Apache::lonnet::passwdmin; - my $intauthjs; + my $intauthjs; if ($prefix eq 'passwords') { $intauthjs = <<"ENDSCRIPT"; function warnIntAuth(field) { @@ -10480,15 +11588,29 @@ function warnInt$prefix(field) { field.value = ''; } if (field.value != '') { - if (!regexdigit.test(field.value)) { - if (field.name == '${prefix}_max') { - alert('$intalert{passmax}'); + if (field.name == '${prefix}_expire') { + var regexpposnum=/^\\d+(|\\.\\d*)\$/; + if (!regexpposnum.test(field.value)) { + alert('$intalert{passexp}'); + field.value = ''; } else { - if (field.name == '${prefix}_numsaved') { - alert('$intalert{passnum}'); + var expval = parseFloat(field.value); + if (expval == 0) { + alert('$intalert{passexp}'); + field.value = ''; } } - field.value = ''; + } else { + if (!regexdigit.test(field.value)) { + if (field.name == '${prefix}_max') { + alert('$intalert{passmax}'); + } else { + if (field.name == '${prefix}_numsaved') { + alert('$intalert{passnum}'); + } + } + field.value = ''; + } } } } @@ -10523,9 +11645,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"> @@ -10595,6 +11719,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; } @@ -10611,21 +11739,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" />' @@ -10640,7 +11772,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>'; @@ -10754,6 +11887,8 @@ sub modifiable_userdata_row { } } elsif ($context eq 'lti') { $rolename = &mt('Institutional data used (if available)'); + } elsif ($context eq 'privacy') { + $rolename = $itemdesc; } else { if ($role eq 'cr') { $rolename = &mt('Custom role'); @@ -10796,12 +11931,20 @@ sub modifiable_userdata_row { '<td class="LC_left_item" colspan="2"><table>'; my $rem; my %checks; + my %current; if (ref($settings) 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') { $hashref = $settings->{'lti_instdata'}; @@ -10814,7 +11957,7 @@ sub modifiable_userdata_row { } } } - if (ref($hashref) eq 'HASH') { + if (ref($hashref) eq 'HASH') { foreach my $field (@fields) { if ($hashref->{$field}) { if ($role eq 'emailusername') { @@ -10826,6 +11969,7 @@ sub modifiable_userdata_row { } } } + my $total = scalar(@fields); for (my $i=0; $i<$total; $i++) { $rem = $i%($numinrow); @@ -10839,6 +11983,24 @@ sub modifiable_userdata_row { unless ($role eq 'emailusername') { if (exists($checks{$fields[$i]})) { $check = $checks{$fields[$i]}; + } elsif ($context eq 'privacy') { + if ($role =~ /^priv_(domain|course)$/) { + if (ref($settings) ne 'HASH') { + $check = ' checked="checked" '; + } + } elsif ($role =~ /^priv_(author|community)$/) { + if (ref($settings) ne 'HASH') { + unless ($fields[$i] eq 'id') { + $check = ' checked="checked" '; + } + } + } elsif ($role =~ /^(unpriv|othdom)_/) { + if (ref($settings) ne 'HASH') { + if (($fields[$i] eq 'lastname') || ($fields[$i] eq 'firstname')) { + $check = ' checked="checked" '; + } + } + } } elsif ($context ne 'lti') { if ($role eq 'st') { if (ref($settings) ne 'HASH') { @@ -10867,6 +12029,8 @@ sub modifiable_userdata_row { } else { if ($context eq 'lti') { $prefix = 'lti'; + } elsif ($context eq 'privacy') { + $prefix = 'privacy'; } $output .= '<label>'. '<input type="checkbox" name="'.$prefix.'_'.$role.'" '. @@ -10894,19 +12058,15 @@ sub insttypes_row { my ($settings,$types,$usertypes,$dom,$numinrow,$othertitle,$context,$rowtotal,$onclick, $customcss,$rowstyle) = @_; my %lt = &Apache::lonlocal::texthash ( - cansearch => 'Users allowed to search', + cansearch => 'Users allowed to search', statustocreate => 'Institutional affiliation(s) able to create own account (login/SSO)', - lockablenames => 'User preference to lock name', - selfassign => 'Self-reportable affiliations', - overrides => "Override domain's helpdesk settings based on requester's affiliation", - webdav => 'WebDAV access available', - authorquota => 'Authoring Space quota (MB)', + lockablenames => 'User preference to lock name', + selfassign => 'Self-reportable affiliations', + overrides => "Override domain's helpdesk settings based on requester's affiliation", ); - my ($showdom,$defaultquota); + my $showdom; if ($context eq 'cansearch') { $showdom = ' ('.$dom.')'; - } elsif ($context eq 'authorquota') { - $defaultquota = 500; } my $class = 'LC_left_item'; if ($context eq 'statustocreate') { @@ -10943,44 +12103,25 @@ sub insttypes_row { } $output .= '<tr>'; } - if ($context eq 'authorquota') { - my $currquota; - if ($settings->{$context}->{$types->[$i]} =~ /^\d+$/) { - $currquota = $settings->{$context}->{$types->[$i]}; - } else { - $currquota = $defaultquota; - } - $output .= '<td class="LC_left_item">'."\n". - '<label><span class="LC_nobreak">'."\n". - $usertypes->{$types->[$i]}.'</span><br />'."\n". - '<input type="text" name="'.$context.'_'.$types->[$i].'" '. - 'value="'.$currquota.'" size="5"'.$onclick.'/>'."\n". - '</label></td>'; - } else { - my $check = ' '; - if (ref($settings) eq 'HASH') { - if (ref($settings->{$context}) eq 'ARRAY') { - if (grep(/^\Q$types->[$i]\E$/,@{$settings->{$context}})) { - $check = ' checked="checked" '; - } - } elsif (ref($settings->{$context}) eq 'HASH') { - if (ref($settings->{$context}->{$types->[$i]}) eq 'HASH') { - $check = ' checked="checked" '; - } elsif ($context eq 'webdav') { - if ($settings->{$context}->{$types->[$i]}) { - $check = ' checked="checked" '; - } - } - } elsif ($context eq 'statustocreate') { + my $check = ' '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$context}) eq 'ARRAY') { + if (grep(/^\Q$types->[$i]\E$/,@{$settings->{$context}})) { + $check = ' checked="checked" '; + } + } elsif (ref($settings->{$context}) eq 'HASH') { + if (ref($settings->{$context}->{$types->[$i]}) eq 'HASH') { $check = ' checked="checked" '; } + } elsif ($context eq 'statustocreate') { + $check = ' checked="checked" '; } - $output .= '<td class="LC_left_item">'. - '<span class="LC_nobreak"><label>'. - '<input type="checkbox" name="'.$context.'" '. - 'value="'.$types->[$i].'"'.$check.$onclick.'/>'. - $usertypes->{$types->[$i]}.'</label></span></td>'; } + $output .= '<td class="LC_left_item">'. + '<span class="LC_nobreak"><label>'. + '<input type="checkbox" name="'.$context.'" '. + 'value="'.$types->[$i].'"'.$check.$onclick.' />'. + $usertypes->{$types->[$i]}.'</label></span></td>'; } } $rem = @{$types}%($numinrow); @@ -10992,7 +12133,7 @@ sub insttypes_row { } else { $output .= '<td class="LC_left_item">'; } - $output .= ' '; + $output .= ' '; } else { if ($rem == 0) { $output .= '<tr>'; @@ -11002,41 +12143,20 @@ sub insttypes_row { } else { $output .= '<td class="LC_left_item">'; } - if ($context eq 'authorquota') { - my $currquota = 500; - if ((ref($settings) eq 'HASH') && (ref($settings->{$context}) eq 'HASH')) { - if ($settings->{$context}{'default'} =~ /^\d+$/) { - $currquota = $settings->{$context}{'default'}; - } - } - $output .= '<label><span class="LC_nobreak">'.$othertitle.'</span><br />'."\n". - '<input type="text" name="'.$context.'_default" '. - 'value="'.$currquota.'" size="5"'.$onclick.'/>'."\n". - '</label>'; - } else { - my $defcheck = ' '; - if (ref($settings) eq 'HASH') { - if (ref($settings->{$context}) eq 'ARRAY') { - if (grep(/^default$/,@{$settings->{$context}})) { - $defcheck = ' checked="checked" '; - } - } elsif (ref($settings->{$context}) eq 'HASH') { - if (ref($settings->{$context}->{'default'}) eq 'HASH') { - $defcheck = ' checked="checked" '; - } elsif ($context eq 'webdav') { - if ($settings->{$context}->{'default'}) { - $defcheck = ' checked="checked" '; - } - } - } elsif ($context eq 'statustocreate') { + my $defcheck = ' '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$context}) eq 'ARRAY') { + if (grep(/^default$/,@{$settings->{$context}})) { $defcheck = ' checked="checked" '; } + } elsif ($context eq 'statustocreate') { + $defcheck = ' checked="checked" '; } - $output .= '<span class="LC_nobreak"><label>'. - '<input type="checkbox" name="'.$context.'" '. - 'value="default"'.$defcheck.$onclick.'/>'. - $othertitle.'</label></span>'; } + $output .= '<span class="LC_nobreak"><label>'. + '<input type="checkbox" name="'.$context.'" '. + 'value="default"'.$defcheck.$onclick.' />'. + $othertitle.'</label></span>'; } $output .= '</td></tr></table></td></tr>'; return $output; @@ -11116,7 +12236,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,%samlwindow,%samlnotsso); + %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso); %title = ( coursecatalog => 'Display course catalog', adminmail => 'Display administrator E-mail address', helpdesk => 'Display "Contact Helpdesk" link', @@ -11140,7 +12260,6 @@ 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'}; } } @@ -11283,16 +12402,13 @@ sub modify_login { if ($addedfile ne '') { push(@allnew,$addedfile); } - my $modified = []; foreach my $lang (@allnew) { my $formelem = 'loginhelpurl_'.$lang; if ($lang eq $env{'form.loginhelpurl_add_lang'}) { $formelem = 'loginhelpurl_add_file'; } - (my $result,$newurl{$lang}) = - &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname, - "help/$lang",'','',$newfile{$lang}, - $modified); + (my $result,$newurl{$lang}) = &publishlogo($r,'upload',$formelem,$dom,$confname, + "help/$lang",'','',$newfile{$lang}); if ($result eq 'ok') { $loginhash{'login'}{'helpurl'}{$lang} = $newurl{$lang}; $changes{'helpurl'}{$lang} = 1; @@ -11305,7 +12421,6 @@ sub modify_login { } } } - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of custom log-in help file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2]. Error was: [_3].",$confname,$dom,$author_ok); } @@ -11363,14 +12478,11 @@ sub modify_login { if ($switchserver) { $error = &mt("Upload of custom markup is not permitted to this server: [_1]",$switchserver); } elsif ($author_ok eq 'ok') { - my $modified = []; foreach my $lonhost (@newhosts) { my $formelem = 'loginheadtag_'.$lonhost; - (my $result,$newheadtagurls{$lonhost}) = - &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname, - "login/headtag/$lonhost",'','', - $env{'form.loginheadtag_'.$lonhost.'.filename'}, - $modified); + (my $result,$newheadtagurls{$lonhost}) = &publishlogo($r,'upload',$formelem,$dom,$confname, + "login/headtag/$lonhost",'','', + $env{'form.loginheadtag_'.$lonhost.'.filename'}); if ($result eq 'ok') { $loginhash{'login'}{'headtag'}{$lonhost}{'url'} = $newheadtagurls{$lonhost}; $changes{'headtag'}{$lonhost} = 1; @@ -11387,7 +12499,6 @@ sub modify_login { } } } - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of custom markup file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2]. Error was: [_3].",$confname,$dom,$author_ok); } @@ -11406,13 +12517,10 @@ sub modify_login { if ($env{'form.saml_img_'.$lonhost.'.filename'}) { push(@newsamlimgs,$lonhost); } - foreach my $item ('text','alt','url','title','window','notsso') { + foreach my $item ('text','alt','url','title','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'}); @@ -11430,28 +12538,25 @@ 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','window','notsso') { + foreach my $item ('text','alt','url','title','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; @@ -11462,14 +12567,11 @@ sub modify_login { if ($switchserver) { $error = &mt("Upload of SSO Button Image is not permitted to this server: [_1].",$switchserver); } elsif ($author_ok eq 'ok') { - my $modified = []; foreach my $lonhost (@newsamlimgs) { my $formelem = 'saml_img_'.$lonhost; - my ($result,$imgurl) = - &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname, - "login/saml/$lonhost",'','', - $env{'form.saml_img_'.$lonhost.'.filename'}, - $modified); + my ($result,$imgurl) = &publishlogo($r,'upload',$formelem,$dom,$confname, + "login/saml/$lonhost",'','', + $env{'form.saml_img_'.$lonhost.'.filename'}); if ($result eq 'ok') { $currsaml{$lonhost}{'img'} = $imgurl; $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl; @@ -11480,7 +12582,6 @@ sub modify_login { $errors .= '<li><span class="LC_error">'.$puberror.'</span></li>'; } } - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of SSO button image file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2]. Error was: [_3].",$confname,$dom,$author_ok); } @@ -11644,22 +12745,19 @@ 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','window','notsso') { + foreach my $key ('text','img','alt','url','title','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>'; @@ -11835,7 +12933,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); @@ -11868,9 +12966,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'; @@ -11902,7 +13000,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') { @@ -11982,7 +13080,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>'. @@ -12050,251 +13148,6 @@ sub get_ipaccess_id { return ($id,$error); } -sub modify_authordefaults { - my ($dom,$lastactref,%domconfig) = @_; -# -# Retrieve current domain configuration for webDAV and Authoring Space quotas from $domconfig{'quotas'}. -# - my (%curr_quotas,%save_quotas,%confhash,%changes,%newvalues); - if (ref($domconfig{'quotas'}) eq 'HASH') { - foreach my $key (keys(%{$domconfig{'quotas'}})) { - if ($key =~ /^webdav|authorquota$/) { - $curr_quotas{$key} = $domconfig{'quotas'}{$key}; - } else { - $save_quotas{$key} = $domconfig{'quotas'}{$key}; - } - } - } - my %staticdefaults = ( - 'copyright' => 'default', - 'sourceavail' => 'closed', - 'nocodemirror' => 'off', - 'daxecollapse' => 'off', - 'domcoordacc' => 'on', - 'editors' => ['edit','xml'], - 'authorquota' => 500, - 'webdav' => 0, - 'archive' => 'off', - ); - my %titles = &authordefaults_titles(); - foreach my $item ('nocodemirror','daxecollapse','domcoordacc','archive') { - if ($env{'form.'.$item} =~ /^(0|1)$/) { - $confhash{$item} = $env{'form.'.$item}; - } - } - if ($env{'form.copyright'} =~ /^(default|domain|public)$/) { - $confhash{'copyright'} = $1; - } - if ($env{'form.sourceavail'} =~ /^(closed|open)$/) { - $confhash{'sourceavail'} = $1; - } - my @posseditors = &Apache::loncommon::get_env_multiple('form.author_editors'); - my @okeditors = ('edit','xml','daxe'); - my @editors; - foreach my $item (@posseditors) { - if (grep(/^\Q$item\E$/,@okeditors)) { - push(@editors,$item); - } - } - $confhash{'editors'} = \@editors; - - my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); - my @insttypes; - if (ref($types) eq 'ARRAY') { - @insttypes = @{$types}; - } - my @webdavon = &Apache::loncommon::get_env_multiple('form.webdav'); - my %webdav; - map { $webdav{$_} = 1; } @webdavon; - foreach my $type (@insttypes,'default') { - my $possquota = $env{'form.authorquota_'.$type}; - if ($possquota =~ /^\d+$/) { - $save_quotas{'authorquota'}{$type} = $possquota; - } - if ($webdav{$type}) { - $save_quotas{'webdav'}{$type} = 1; - } else { - $save_quotas{'webdav'}{$type} = 0; - } - } - if ($env{'form.webdav_LC_adv'} =~ /^(0|1)$/) { - $save_quotas{'webdav'}{'_LC_adv'} = $env{'form.webdav_LC_adv'}; - } - if (ref($domconfig{'authordefaults'}) eq 'HASH') { - foreach my $item ('nocodemirror','daxecollapse','domcoordacc','copyright','sourceavail','archive') { - if ($domconfig{'authordefaults'}{$item} ne $confhash{$item}) { - $changes{$item} = 1; - } - } - if (ref($domconfig{'authordefaults'}{'editors'}) eq 'ARRAY') { - my @diffs = - &Apache::loncommon::compare_arrays($confhash{'editors'}, - $domconfig{'authordefaults'}{'editors'}); - unless (@diffs == 0) { - $changes{'editors'} = 1; - } - } else { - my @diffs = - &Apache::loncommon::compare_arrays($confhash{'editors'}, - $staticdefaults{'editors'}); - unless (@diffs == 0) { - $changes{'editors'} = 1; - } - } - } else { - my @offon = ('off','on'); - foreach my $item ('nocodemirror','daxecollapse','domcoordacc','archive') { - if ($offon[$confhash{$item}] ne $staticdefaults{$item}) { - $changes{$item} = 1; - } - } - foreach my $item ('copyright','sourceavail') { - if ($confhash{$item} ne $staticdefaults{$item}) { - $changes{$item} = 1; - } - } - my @diffs = - &Apache::loncommon::compare_arrays($confhash{'editors'}, - $staticdefaults{'editors'}); - unless (@diffs == 0) { - $changes{'editors'} = 1; - } - } - foreach my $key ('authorquota','webdav') { - if (ref($curr_quotas{$key}) eq 'HASH') { - foreach my $type (@insttypes,'default') { - if (exists($save_quotas{$key}{$type})) { - if ($save_quotas{$key}{$type} ne $curr_quotas{$key}{$type}) { - $changes{$key}{$type} = 1; - } - } elsif (exists($curr_quotas{$key}{$type})) { - $save_quotas{$key}{$type} = $curr_quotas{$key}{$type}; - } else { - $save_quotas{$key}{$type} = $staticdefaults{$key}; - } - } - } else { - foreach my $type (@insttypes,'default') { - if (exists($save_quotas{$key}{$type})) { - unless ($save_quotas{$key}{$type} eq $staticdefaults{$key}) { - $changes{$key}{$type} = 1; - } - } else { - $save_quotas{$key}{$type} = $staticdefaults{$key}; - } - } - } - } - if (ref($curr_quotas{'webdav'}) eq 'HASH') { - if (exists($save_quotas{'webdav'}{'_LC_adv'})) { - if ($save_quotas{'webdav'}{'_LC_adv'} ne $curr_quotas{'webdav'}{'_LC_adv'}) { - $changes{'webdav_LC_adv'} = 1; - } - } elsif (exists($curr_quotas{'webdav'}{'_LC_adv'})) { - $changes{'webdav_LC_adv'} = 1; - } - } elsif (exists($save_quotas{'webdav'}{'_LC_adv'})) { - $changes{'webdav_LC_adv'} = 1; - } - my %confighash = ( - quotas => \%save_quotas, - authordefaults => \%confhash, - ); - my $putresult = &Apache::lonnet::put_dom('configuration',\%confighash, - $dom); - my $resulttext; - if ($putresult eq 'ok') { - if (keys(%changes)) { - my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); - if ((exists($changes{'authorquota'})) || (exists($changes{'webdav'})) || - ($changes{'webdav_LC_adv'})) { - if ((exists($changes{'authorquota'})) && (ref($save_quotas{'authorquota'}) eq 'HASH')) { - $domdefaults{'authorquota'} = $save_quotas{'authorquota'}; - } - if (((exists($changes{'webdav'})) || ($changes{'webdav_LC_adv'})) && - (ref($save_quotas{'webdav'}) eq 'HASH')) { - $domdefaults{'webdav'} = $save_quotas{'webdav'}; - } - } - $resulttext = &mt('Changes made:').'<ul>'; - my $authoroverride; - foreach my $key ('nocodemirror','daxecollapse','domcoordacc','copyright','sourceavail') { - if (exists($changes{$key})) { - $domdefaults{$key} = $confhash{$key}; - my $shown; - unless ($authoroverride) { - $resulttext .= '<li>'.&mt('Defaults which can be overridden by Author').'<ul>'; - $authoroverride = 1; - } - if (($key eq 'nocodemirror') || ($key eq 'daxecollapse') || ($key eq 'domcoordacc')) { - $shown = ($confhash{$key} ? &mt('Yes') : &mt('No')); - } elsif ($key eq 'copyright') { - $shown = &Apache::loncommon::copyrightdescription($confhash{$key}); - } elsif ($key eq 'sourceavail') { - $shown = &Apache::loncommon::source_copyrightdescription($confhash{$key}); - } - $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{$key},$shown).'</li>'; - } - } - if ($authoroverride) { - $resulttext .= '</ul></li>'; - } - my $domcoordoverride; - foreach my $key ('editors','authorquota','webdav','webdav_LC_adv','archive') { - if (exists($changes{$key})) { - my $shown; - unless ($domcoordoverride) { - $resulttext .= '<li>'.&mt('Defaults which can be overridden by a Domain Coodinator').'<ul>'; - $domcoordoverride = 1; - } - if ($key eq 'editors') { - if (ref($confhash{'editors'}) eq 'ARRAY') { - $domdefaults{'editors'} = join(',',@{$confhash{'editors'}}); - if (@{$confhash{'editors'}}) { - $shown = join(', ', map { $titles{$_} } @{$confhash{'editors'}}); - } else { - $shown = &mt('None'); - } - } - } elsif ($key eq 'authorquota') { - foreach my $type (@insttypes) { - $shown .= $usertypes->{$type}.' -- '.$save_quotas{$key}{$type}.', '; - } - $shown .= $othertitle.' -- '.$save_quotas{$key}{'default'}; - } elsif ($key eq 'webdav') { - foreach my $type (@insttypes) { - $shown .= $usertypes->{$type}.' -- '. ($save_quotas{$key}{$type} ? &mt('Yes') : &mt('No')).', '; - } - $shown .= $othertitle.' -- '. ($save_quotas{$key}{'default'} ? &mt('Yes') : &mt('No')); - } elsif ($key eq 'webdav_LC_adv') { - if (exists($save_quotas{'webdav'}{'_LC_adv'})) { - $shown = ($save_quotas{'webdav'}{'_LC_adv'} ? $titles{'overon'} : $titles{'overoff'}); - } else { - $shown = $titles{'none'}; - } - } elsif ($key eq 'archive') { - $domdefaults{$key} = $confhash{$key}; - $shown = ($confhash{$key} ? &mt('Yes') : &mt('No')); - } - $resulttext .= '<li>'.&mt('[_1] set to: [_2]',$titles{$key},$shown).'</li>'; - } - } - if ($domcoordoverride) { - $resulttext .= '</ul></li>'; - } - $resulttext .= '</ul>'; - my $cachetime = 24*60*60; - &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); - if (ref($lastactref) eq 'HASH') { - $lastactref->{'domdefaults'} = 1; - } - } else { - $resulttext = &mt('No changes made to Authoring Space defaults'); - } - } - return $resulttext; -} - sub modify_rolecolors { my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_; my ($resulttext,%rolehash); @@ -12430,15 +13283,12 @@ sub modify_colors { $error = &mt("Upload of [_1] image for $role page(s) is not permitted to this server: [_2]",$choices{$img},$switchserver); } else { if ($author_ok eq 'ok') { - my $modified = []; my ($result,$logourl) = - &Apache::lonconfigsettings::publishlogo($r,'upload',$role.'_'.$img, - $dom,$confname,$img,$width,$height, - '',$modified); + &publishlogo($r,'upload',$role.'_'.$img, + $dom,$confname,$img,$width,$height); if ($result eq 'ok') { $confhash->{$role}{$img} = $logourl; $changes{$role}{'images'}{$img} = 1; - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of [_1] image for $role page(s) failed because an error occurred publishing the file in RES space. Error was: [_2].",$choices{img},$result); } @@ -12460,15 +13310,12 @@ sub modify_colors { # is confname an author? if ($switchserver eq '') { if ($author_ok eq 'ok') { - my $modified = []; my ($result,$logourl) = - &Apache::lonconfigsettings::publishlogo($r,'copy',$domconfig->{$role}{$img}, - $dom,$confname,$img,$width,$height, - '',$modified); + &publishlogo($r,'copy',$domconfig->{$role}{$img}, + $dom,$confname,$img,$width,$height); if ($result eq 'ok') { $confhash->{$role}{$img} = $logourl; $changes{$role}{'images'}{$img} = 1; - &update_modify_urls($r,$modified); } } } @@ -12682,7 +13529,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>'; } @@ -12762,16 +13609,229 @@ sub check_authorstatus { return $author_ok; } -sub update_modify_urls { - my ($r,$modified) = @_; - if ((ref($modified) eq 'ARRAY') && (@{$modified})) { - push(@{$modified_urls},$modified); - unless ($registered_cleanup) { - my $handlers = $r->get_handlers('PerlCleanupHandler'); - $r->set_handlers('PerlCleanupHandler' => [\¬ifysubscribed,@{$handlers}]); - $registered_cleanup=1; +sub publishlogo { + my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight,$savefileas) = @_; + my ($output,$fname,$logourl,$madethumb); + if ($action eq 'upload') { + $fname=$env{'form.'.$formname.'.filename'}; + chop($env{'form.'.$formname}); + } else { + ($fname) = ($formname =~ /([^\/]+)$/); + } + if ($savefileas ne '') { + $fname = $savefileas; + } + $fname=&Apache::lonnet::clean_filename($fname); +# See if there is anything left + unless ($fname) { return ('error: no uploaded file'); } + $fname="$subdir/$fname"; + my $docroot=$r->dir_config('lonDocRoot'); + my $filepath="$docroot/priv"; + my $relpath = "$dom/$confname"; + my ($fnamepath,$file,$fetchthumb); + $file=$fname; + if ($fname=~m|/|) { + ($fnamepath,$file) = ($fname =~ m|^(.*)/([^/]+)$|); + } + my @parts=split(/\//,"$filepath/$relpath/$fnamepath"); + my $count; + for ($count=5;$count<=$#parts;$count++) { + $filepath.="/$parts[$count]"; + if ((-e $filepath)!=1) { + mkdir($filepath,02770); + } + } + # Check for bad extension and disallow upload + if ($file=~/\.(\w+)$/ && + (&Apache::loncommon::fileembstyle($1) eq 'hdn')) { + $output = + &mt('Invalid file extension ([_1]) - reserved for internal use.',$1); + } elsif ($file=~/\.(\w+)$/ && + !defined(&Apache::loncommon::fileembstyle($1))) { + $output = &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1); + } elsif ($file=~/\.(\d+)\.(\w+)$/) { + $output = &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2); + } elsif (-d "$filepath/$file") { + $output = &mt('Filename is a directory name - rename the file and re-upload'); + } else { + my $source = $filepath.'/'.$file; + my $logfile; + if (!open($logfile,">>",$source.'.log')) { + return (&mt('No write permission to Authoring Space')); + } + print $logfile +"\n================= Publish ".localtime()." ================\n". +$env{'user.name'}.':'.$env{'user.domain'}."\n"; +# Save the file + if (!open(FH,">",$source)) { + &Apache::lonnet::logthis('Failed to create '.$source); + return (&mt('Failed to create file')); + } + if ($action eq 'upload') { + if (!print FH ($env{'form.'.$formname})) { + &Apache::lonnet::logthis('Failed to write to '.$source); + return (&mt('Failed to write file')); + } + } else { + my $original = &Apache::lonnet::filelocation('',$formname); + if(!copy($original,$source)) { + &Apache::lonnet::logthis('Failed to copy '.$original.' to '.$source); + return (&mt('Failed to write file')); + } + } + close(FH); + chmod(0660, $source); # Permissions to rw-rw---. + + my $targetdir=$docroot.'/res/'.$dom.'/'.$confname .'/'.$fnamepath; + my $copyfile=$targetdir.'/'.$file; + + my @parts=split(/\//,$targetdir); + my $path="/$parts[1]/$parts[2]/$parts[3]/$parts[4]"; + for (my $count=5;$count<=$#parts;$count++) { + $path.="/$parts[$count]"; + if (!-e $path) { + print $logfile "\nCreating directory ".$path; + mkdir($path,02770); + } + } + my $versionresult; + if (-e $copyfile) { + $versionresult = &logo_versioning($targetdir,$file,$logfile); + } else { + $versionresult = 'ok'; + } + if ($versionresult eq 'ok') { + if (copy($source,$copyfile)) { + print $logfile "\nCopied original source to ".$copyfile."\n"; + $output = 'ok'; + $logourl = '/res/'.$dom.'/'.$confname.'/'.$fname; + push(@{$modified_urls},[$copyfile,$source]); + my $metaoutput = + &write_metadata($dom,$confname,$formname,$targetdir,$file,$logfile); + unless ($registered_cleanup) { + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\¬ifysubscribed,@{$handlers}]); + $registered_cleanup=1; + } + } else { + print $logfile "\nUnable to write ".$copyfile.':'.$!."\n"; + $output = &mt('Failed to copy file to RES space').", $!"; + } + if (($thumbwidth =~ /^\d+$/) && ($thumbheight =~ /^\d+$/)) { + my $inputfile = $filepath.'/'.$file; + my $outfile = $filepath.'/'.'tn-'.$file; + my ($fullwidth,$fullheight) = &check_dimensions($inputfile); + if ($fullwidth ne '' && $fullheight ne '') { + if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) { + my $thumbsize = $thumbwidth.'x'.$thumbheight; + my @args = ('convert','-sample',$thumbsize,$inputfile,$outfile); + system({$args[0]} @args); + chmod(0660, $filepath.'/tn-'.$file); + if (-e $outfile) { + my $copyfile=$targetdir.'/tn-'.$file; + if (copy($outfile,$copyfile)) { + print $logfile "\nCopied source to ".$copyfile."\n"; + my $thumb_metaoutput = + &write_metadata($dom,$confname,$formname, + $targetdir,'tn-'.$file,$logfile); + push(@{$modified_urls},[$copyfile,$outfile]); + unless ($registered_cleanup) { + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\¬ifysubscribed,@{$handlers}]); + $registered_cleanup=1; + } + $madethumb = 1; + } else { + print $logfile "\nUnable to write ".$copyfile. + ':'.$!."\n"; + } + } + } + } + } + } else { + $output = $versionresult; + } + } + return ($output,$logourl,$madethumb); +} + +sub logo_versioning { + my ($targetdir,$file,$logfile) = @_; + my $target = $targetdir.'/'.$file; + my ($maxversion,$fn,$extn,$output); + $maxversion = 0; + if ($file =~ /^(.+)\.(\w+)$/) { + $fn=$1; + $extn=$2; + } + opendir(DIR,$targetdir); + while (my $filename=readdir(DIR)) { + if ($filename=~/\Q$fn\E\.(\d+)\.\Q$extn\E$/) { + $maxversion=($1>$maxversion)?$1:$maxversion; + } + } + $maxversion++; + print $logfile "\nCreating old version ".$maxversion."\n"; + my $copyfile=$targetdir.'/'.$fn.'.'.$maxversion.'.'.$extn; + if (copy($target,$copyfile)) { + print $logfile "Copied old target to ".$copyfile."\n"; + $copyfile=$copyfile.'.meta'; + if (copy($target.'.meta',$copyfile)) { + print $logfile "Copied old target metadata to ".$copyfile."\n"; + $output = 'ok'; + } else { + print $logfile "Unable to write metadata ".$copyfile.':'.$!."\n"; + $output = &mt('Failed to copy old meta').", $!, "; + } + } else { + print $logfile "Unable to write ".$copyfile.':'.$!."\n"; + $output = &mt('Failed to copy old target').", $!, "; + } + return $output; +} + +sub write_metadata { + my ($dom,$confname,$formname,$targetdir,$file,$logfile) = @_; + my (%metadatafields,%metadatakeys,$output); + $metadatafields{'title'}=$formname; + $metadatafields{'creationdate'}=time; + $metadatafields{'lastrevisiondate'}=time; + $metadatafields{'copyright'}='public'; + $metadatafields{'modifyinguser'}=$env{'user.name'}.':'. + $env{'user.domain'}; + $metadatafields{'authorspace'}=$confname.':'.$dom; + $metadatafields{'domain'}=$dom; + { + print $logfile "\nWrite metadata file for ".$targetdir.'/'.$file; + my $mfh; + if (open($mfh,">",$targetdir.'/'.$file.'.meta')) { + foreach (sort(keys(%metadatafields))) { + unless ($_=~/\./) { + my $unikey=$_; + $unikey=~/^([A-Za-z]+)/; + my $tag=$1; + $tag=~tr/A-Z/a-z/; + print $mfh "\n\<$tag"; + foreach (split(/\,/,$metadatakeys{$unikey})) { + my $value=$metadatafields{$unikey.'.'.$_}; + $value=~s/\"/\'\'/g; + print $mfh ' '.$_.'="'.$value.'"'; + } + print $mfh '>'. + &HTML::Entities::encode($metadatafields{$unikey},'<>&"') + .'</'.$tag.'>'; + } + } + $output = 'ok'; + print $logfile "\nWrote metadata"; + close($mfh); + } else { + print $logfile "\nFailed to open metadata file"; + $output = &mt('Could not write metadata'); } } + return $output; } sub notifysubscribed { @@ -12822,21 +13882,15 @@ sub subscribed_hosts { sub check_switchserver { my ($dom,$confname) = @_; - my ($allowed,$switchserver,$home); - if ($confname eq '') { + my ($allowed,$switchserver); + my $home = &Apache::lonnet::homeserver($confname,$dom); + if ($home eq 'no_host') { $home = &Apache::lonnet::domain($dom,'primary'); - } else { - $home = &Apache::lonnet::homeserver($confname,$dom); - if ($home eq 'no_host') { - $home = &Apache::lonnet::domain($dom,'primary'); - } } 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>'; + $switchserver='<a href="/adm/switchserver?otherserver='.$home.'&role=dc./'.$dom.'/&destinationurl=/adm/domainprefs">'.&mt('Switch Server').'</a>'; } return $switchserver; } @@ -12848,12 +13902,12 @@ sub modify_quotas { $author_ok,$switchserver,$errors,$validationitemsref,$validationnamesref, $validationfieldsref); if ($action eq 'quotas') { - $context = 'tools'; + $context = 'tools'; } else { $context = $action; } if ($context eq 'requestcourses') { - @usertools = ('official','unofficial','community','textbook','lti'); + @usertools = ('official','unofficial','community','textbook','placement','lti'); @options =('norequest','approval','validate','autolimit'); %validations = &Apache::lonnet::auto_courserequest_checks($dom); %titles = &courserequest_titles(); @@ -12868,7 +13922,7 @@ sub modify_quotas { @usertools = ('author'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','portfolio','portaccess','timezone'); + @usertools = ('aboutme','blog','webdav','portfolio'); %titles = &tool_titles(); } my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); @@ -12891,6 +13945,8 @@ sub modify_quotas { } else { if ($key =~ /^form\.quota_(.+)$/) { $confhash{'defaultquota'}{$1} = $env{$key}; + } elsif ($key =~ /^form\.authorquota_(.+)$/) { + $confhash{'authorquota'}{$1} = $env{$key}; } elsif ($key =~ /^form\.\Q$context\E_(.+)$/) { @{$toolshash{$1}} = &Apache::loncommon::get_env_multiple($key); } @@ -12900,7 +13956,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','lti'); + 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)) { @@ -13184,6 +14240,7 @@ sub modify_quotas { } } else { $confhash{'defaultquota'}{'default'} = $env{'form.defaultquota'}; + $confhash{'authorquota'}{'default'} = $env{'form.authorquota'}; } foreach my $item (@usertools) { foreach my $type (@{$types},'default','_LC_adv') { @@ -13272,10 +14329,15 @@ sub modify_quotas { } } if (ref($domconfig{'quotas'}{'authorquota'}) eq 'HASH') { - $confhash{'authorquota'} = $domconfig{'quotas'}{'authorquota'}; - } - if (ref($domconfig{'quotas'}{'webdav'}) eq 'HASH') { - $confhash{'webdav'} = $domconfig{'quotas'}{'webdav'}; + foreach my $key (keys(%{$domconfig{'quotas'}{'authorquota'}})) { + if (exists($confhash{'authorquota'}{$key})) { + if ($confhash{'authorquota'}{$key} ne $domconfig{'quotas'}{'authorquota'}{$key}) { + $changes{'authorquota'}{$key} = 1; + } + } else { + $confhash{'authorquota'}{$key} = $domconfig{'quotas'}{'authorquota'}{$key}; + } + } } } if (ref($confhash{'defaultquota'}) eq 'HASH') { @@ -13295,6 +14357,21 @@ sub modify_quotas { } } } + if (ref($confhash{'authorquota'}) eq 'HASH') { + foreach my $key (keys(%{$confhash{'authorquota'}})) { + if (ref($domconfig{'quotas'}) eq 'HASH') { + if (ref($domconfig{'quotas'}{'authorquota'}) eq 'HASH') { + if (!exists($domconfig{'quotas'}{'authorquota'}{$key})) { + $changes{'authorquota'}{$key} = 1; + } + } else { + $changes{'authorquota'}{$key} = 1; + } + } else { + $changes{'authorquota'}{$key} = 1; + } + } + } } if ($context eq 'requestauthor') { @@ -13335,6 +14412,19 @@ sub modify_quotas { } $resulttext .= '</ul></li>'; } + if (ref($changes{'authorquota'}) eq 'HASH') { + $resulttext .= '<li>'.&mt('Authoring Space default quotas').'<ul>'; + foreach my $type (@{$types},'default') { + if (defined($changes{'authorquota'}{$type})) { + my $typetitle = $usertypes->{$type}; + if ($type eq 'default') { + $typetitle = $othertitle; + } + $resulttext .= '<li>'.&mt('[_1] set to [_2] MB',$typetitle,$confhash{'authorquota'}{$type}).'</li>'; + } + } + $resulttext .= '</ul></li>'; + } } my %newenv; foreach my $item (@usertools) { @@ -13486,7 +14576,6 @@ sub modify_quotas { $resulttext .= '<li>'.&mt('Validated course requests identified as processed by: [_1]', '<b>'.$changes{'validation'}{'dc'}.'</b>').'</li>'; } - $resulttext .= '</ul></li>'; } } } @@ -13524,14 +14613,11 @@ sub process_textbook_image { $error = &mt('Upload of textbook image is not permitted to this server: [_1]', $switchserver); } elsif ($author_ok eq 'ok') { - my $modified = []; my ($result,$imageurl) = - &Apache::lonconfigsettings::publishlogo($r,'upload',$caller,$dom,$confname, - "$type/$cdom/$cnum/cover",$width,$height, - '',$modified); + &publishlogo($r,'upload',$caller,$dom,$confname, + "$type/$cdom/$cnum/cover",$width,$height); if ($result eq 'ok') { $url = $imageurl; - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result); } @@ -13546,388 +14632,1192 @@ sub process_textbook_image { sub modify_ltitools { my ($r,$dom,$action,$lastactref,%domconfig) = @_; - my (%currtoolsec,%secchanges,%newtoolsec,%newkeyset); - &fetch_secrets($dom,'toolsec',\%domconfig,\%currtoolsec,\%secchanges,\%newtoolsec,\%newkeyset); - + 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 ($resulttext,$ltitoolsoutput,$is_home,$errors,%ltitoolschg,%newtoolsenc,%newltitools); - my $toolserror = - &Apache::courseprefs::process_ltitools($r,$dom,$confname,$domconfig{'ltitools'},\%ltitoolschg,'domain', - $lastactref,$configuserok,$switchserver,$author_ok); - - 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; last; } } - } - - if (keys(%ltitoolschg)) { - foreach my $id (keys(%ltitoolschg)) { - if (ref($ltitoolschg{$id}) eq 'HASH') { - foreach my $inner (keys(%{$ltitoolschg{$id}})) { - if (($inner eq 'secret') || ($inner eq 'key')) { - if ($is_home) { - $newtoolsenc{$id}{$inner} = $ltitoolschg{$id}{$inner}; + 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; } } } } - } - $ltitoolsoutput = &Apache::courseprefs::store_ltitools($dom,'','domain',\%ltitoolschg,$domconfig{'ltitools'}); - if (keys(%ltitoolschg)) { - %newltitools = %ltitoolschg; + 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{'ltitools'}) eq 'HASH') { - foreach my $id (%{$domconfig{'ltitools'}}) { - next if ($id !~ /^\d+$/); - unless (exists($ltitoolschg{$id})) { - if (ref($domconfig{'ltitools'}{$id}) eq 'HASH') { - foreach my $inner (keys(%{$domconfig{'ltitools'}{$id}})) { - if (($inner eq 'secret') || ($inner eq 'key')) { - if ($is_home) { - $newtoolsenc{$id}{$inner} = $domconfig{'ltitools'}{$id}{$inner}; + 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; } - } else { - $newltitools{$id}{$inner} = $domconfig{'ltitools'}{$id}{$inner}; } } - } else { - $newltitools{$id} = $domconfig{'ltitools'}{$id}; + 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 ($toolserror) { - $errors = '<li>'.$toolserror.'</li>'; - } - if ((keys(%ltitoolschg) == 0) && (keys(%secchanges) == 0)) { - $resulttext = &mt('No changes made.'); - if ($errors) { - $resulttext .= '<br />'.&mt('The following errors occurred: ').'<ul>'. - $errors.'</ul>'; + 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 ++; + } } - return $resulttext; } my %ltitoolshash = ( - $action => { %newltitools } + $action => { %confhash } ); - if (keys(%secchanges)) { - $ltitoolshash{'toolsec'} = \%newtoolsec; - } - my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash,$dom); + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash, + $dom); if ($putresult eq 'ok') { - my %keystore; - if ($is_home) { - my %toolsenchash = ( - $action => { %newtoolsenc } - ); - &Apache::lonnet::put_dom('encconfig',\%toolsenchash,$dom,undef,1); + my %ltienchash = ( + $action => { %encconfig } + ); + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); + if (keys(%changes) > 0) { my $cachetime = 24*60*60; - &Apache::lonnet::do_cache_new('ltitoolsenc',$dom,\%newtoolsenc,$cachetime); - &store_security($dom,'ltitools',\%secchanges,\%newkeyset,\%keystore,$lastactref); - } - $resulttext = &mt('Changes made:').'<ul>'; - if (keys(%secchanges) > 0) { - $resulttext .= <i_security_results($dom,'ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore); - } - if (keys(%ltitoolschg) > 0) { - $resulttext .= $ltitoolsoutput; - } - my $cachetime = 24*60*60; - &Apache::lonnet::do_cache_new('ltitools',$dom,\%newltitools,$cachetime); - if (ref($lastactref) eq 'HASH') { - $lastactref->{'ltitools'} = 1; + 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 .= '<p>'.&mt('The following errors occurred: ').'<ul>'. - $errors.'</ul></p>'; + $resulttext .= &mt('The following errors occurred: ').'<ul>'. + $errors.'</ul>'; } return $resulttext; } -sub fetch_secrets { - my ($dom,$context,$domconfig,$currsec,$secchanges,$newsec,$newkeyset) = @_; - my %keyset; - %{$currsec} = (); - $newsec->{'private'}{'keys'} = []; - $newsec->{'encrypt'} = {}; - $newsec->{'rules'} = {}; - if ($context eq 'ltisec') { - $newsec->{'linkprot'} = {}; - } - if (ref($domconfig->{$context}) eq 'HASH') { - %{$currsec} = %{$domconfig->{$context}}; - if ($context eq 'ltisec') { - if (ref($currsec->{'linkprot'}) eq 'HASH') { - foreach my $id (keys(%{$currsec->{'linkprot'}})) { - unless ($id =~ /^\d+$/) { - delete($currsec->{'linkprot'}{$id}); - } +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); } - if (ref($currsec->{'private'}) eq 'HASH') { - if (ref($currsec->{'private'}{'keys'}) eq 'ARRAY') { - $newsec->{'private'}{'keys'} = $currsec->{'private'}{'keys'}; - map { $keyset{$_} = 1; } @{$currsec->{'private'}{'keys'}}; - } - } + } 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); } - my @items= ('crs','dom'); - if ($context eq 'ltisec') { - push(@items,'consumers'); + 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); } - foreach my $item (@items) { - my $formelement; - if (($context eq 'toolsec') || ($item eq 'consumers')) { - $formelement = 'form.'.$context.'_'.$item; - } else { - $formelement = 'form.'.$context.'_'.$item.'linkprot'; - } - if ($env{$formelement}) { - $newsec->{'encrypt'}{$item} = 1; - if (ref($currsec->{'encrypt'}) eq 'HASH') { - unless ($currsec->{'encrypt'}{$item}) { - $secchanges->{'encrypt'} = 1; + 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 { - $secchanges->{'encrypt'} = 1; + $id = 1; } - } elsif (ref($currsec->{'encrypt'}) eq 'HASH') { - if ($currsec->{'encrypt'}{$item}) { - $secchanges->{'encrypt'} = 1; + if ($id) { + unless (&Apache::lonnet::newput_dom('ltitools',{ $id => $title },$cdom) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; } } - } - my $secrets; - if ($context eq 'ltisec') { - $secrets = 'ltisecrets'; + my $dellockoutcome = &Apache::lonnet::del_dom('ltitools',['lock'],$cdom); } else { - $secrets = 'toolsecrets'; - } - unless (exists($currsec->{'rules'})) { - $currsec->{'rules'} = {}; + $error = 'nolock'; } - &password_rule_changes($secrets,$newsec->{'rules'},$currsec->{'rules'},$secchanges); + return ($id,$error); +} - my @ids=&Apache::lonnet::current_machine_ids(); - my %servers = &Apache::lonnet::get_servers($dom,'library'); +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)); - foreach my $hostid (keys(%servers)) { - if (($hostid ne '') && (grep(/^\Q$hostid\E$/,@ids))) { - my $keyitem = 'form.'.$context.'_privkey_'.$hostid; - if (exists($env{$keyitem})) { - $env{$keyitem} =~ s/(`)/'/g; - if ($keyset{$hostid}) { - if ($env{'form.'.$context.'_changeprivkey_'.$hostid}) { - if ($env{$keyitem} ne '') { - $secchanges->{'private'} = 1; - $newkeyset->{$hostid} = $env{$keyitem}; - } - } - } elsif ($env{$keyitem} ne '') { - unless (grep(/^\Q$hostid\E$/,@{$newsec->{'private'}{'keys'}})) { - push(@{$newsec->{'private'}{'keys'}},$hostid); - } - $secchanges->{'private'} = 1; - $newkeyset->{$hostid} = $env{$keyitem}; + 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}); } } } - } -} - -sub store_security { - my ($dom,$context,$secchanges,$newkeyset,$keystore) = @_; - return unless ((ref($secchanges) eq 'HASH') && (ref($newkeyset) eq 'HASH') && - (ref($keystore) eq 'HASH')); - 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,$context,'private', - $dom,$hostid); + 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; } } - } -} - -sub lti_security_results { - my ($dom,$context,$secchanges,$newsec,$newkeyset,$keystore) = @_; - my $output; - my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); - my $needs_update; - foreach my $item (keys(%{$secchanges})) { - if ($item eq 'encrypt') { - $needs_update = 1; - my %encrypted; - if ($context eq 'lti') { - %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'), - }, - ); - } else { - %encrypted = ( - crs => { - on => &mt('Encryption of stored external tool secrets defined in courses enabled'), - off => &mt('Encryption of stored external tool secrets defined in courses disabled'), - }, - dom => { - on => &mt('Encryption of stored external tool secrets defined in domain enabled'), - off => &mt('Encryption of stored external tool secrets defined in domain disabled'), - }, - ); + 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; + } + } } - my @types= ('crs','dom'); - if ($context eq 'lti') { - foreach my $type (@types) { - undef($domdefaults{'linkprotenc_'.$type}); + 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; } - push(@types,'consumers'); - undef($domdefaults{'ltienc_consumers'}); - } elsif ($context eq 'ltitools') { - foreach my $type (@types) { - undef($domdefaults{'toolenc_'.$type}); + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; } + } elsif (exists($current{'image'})) { + $confhash{$provider}{'image'} = $current{'image'}; } - foreach my $type (@types) { - my $shown = $encrypted{$type}{'off'}; - if (ref($newsec->{$item}) eq 'HASH') { - if ($newsec->{$item}{$type}) { - if ($context eq 'lti') { - if ($type eq 'consumers') { - $domdefaults{'ltienc_consumers'} = 1; - } else { - $domdefaults{'linkprotenc_'.$type} = 1; + 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 ($context eq 'ltitools') { - $domdefaults{'toolenc_'.$type} = 1; + } elsif (keys(%{$confhash{$provider}{'defaults'}})) { + $changes{$provider} = 1; } - $shown = $encrypted{$type}{'on'}; } } - $output .= '<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 ($newsec->{rules}{$rule} eq '') { - if ($rule eq 'min') { - $output .= '<li>'.&mt('[_1] not set.',$titles{$rule}); - ' '.&mt('Default of [_1] will be used', - $Apache::lonnet::passwdmin).'</li>'; - } else { - $output .= '<li>'.&mt('[_1] set to none',$titles{$rule}).'</li>'; - } - } else { - $output .= '<li>'.&mt('[_1] set to [_2]',$titles{$rule},$newsec->{rules}{$rule}).'</li>'; - } - } - if (ref($newsec->{'rules'}{'chars'}) eq 'ARRAY') { - if (@{$newsec->{'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{$_} } @{$newsec->{'rules'}{'chars'}}). - '</li></ul>'; - $output .= '<li>'.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'</li>'; - } else { - $output .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>'; + 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; + } } - } else { - $output .= '<li>'.&mt('[_1] set to none',$titles{'chars'}).'</li>'; } - } elsif ($item eq 'private') { - $needs_update = 1; - if ($context eq 'lti') { - undef($domdefaults{'ltiprivhosts'}); - } elsif ($context eq 'ltitools') { - undef($domdefaults{'toolprivhosts'}); + 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 (keys(%{$newkeyset})) { - my @privhosts; - foreach my $hostid (sort(keys(%{$newkeyset}))) { - if ($keystore->{$hostid} eq 'ok') { - $output .= '<li>'.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'</li>'; - unless (grep(/^\Q$hostid\E$/,@privhosts)) { - push(@privhosts,$hostid); + 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 (@privhosts) { - if ($context eq 'lti') { - $domdefaults{'ltiprivhosts'} = \@privhosts; - } elsif ($context eq 'ltitools') { - $domdefaults{'toolprivhosts'} = \@privhosts; + } + 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 ++; } - } elsif ($item eq 'linkprot') { - next; - } elsif ($item eq 'suggested') { - if ((ref($secchanges->{'suggested'}) eq 'HASH') && - (ref($newsec->{'suggested'}) eq 'HASH')) { - my $suggestions; - foreach my $id (sort { $a <=> $b } keys(%{$secchanges->{'suggested'}})) { - if (ref($newsec->{'suggested'}->{$id}) eq 'HASH') { - my $name = $newsec->{'suggested'}->{$id}->{'name'}; - my $info = $newsec->{'suggested'}->{$id}->{'info'}; - $suggestions .= '<li>'.&mt('Launcher: [_1]',$name).'<br />'. - &mt('Recommend: [_1]','<pre>'.$info.'</pre>'). - '</li>'; - } else { - $suggestions .= '<li>'.&mt('Recommendations deleted for Launcher: [_1]', - $newsec->{'suggested'}->{$id}).'</li>'; - } - } - if ($suggestions) { - $output .= '<li>'.&mt('Hints in Courses for Link Protector Configuration'). - '<ul>'.$suggestions.'</ul>'. - '</li>'; + } + } + 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 ($needs_update) { - my $cachetime = 24*60*60; - &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if ($errors) { + $resulttext .= &mt('The following errors occurred: ').'<ul>'. + $errors.'</ul>'; } - return $output; + 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,%ltienc,$errors,$resulttext); + 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); @@ -13948,9 +15838,81 @@ sub modify_lti { map { $posscrstype{$_} = 1; } @coursetypes; my %menutitles = <imenu_titles(); - my (%currltisec,%secchanges,%newltisec,%newltienc,%newkeyset); - &fetch_secrets($dom,'ltisec',\%domconfig,\%currltisec,\%secchanges,\%newltisec,\%newkeyset); + 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'}, @@ -13986,7 +15948,7 @@ sub modify_lti { } } if (ref($currltisec{'linkprot'}) eq 'HASH') { - foreach my $id (keys(%{$currltisec{'linkprot'}})) { + foreach my $id (%{$currltisec{'linkprot'}}) { next if ($id !~ /^\d+$/); unless (exists($linkprotchg{$id})) { if (ref($currltisec{'linkprot'}{$id}) eq 'HASH') { @@ -14008,75 +15970,17 @@ sub modify_lti { if ($proterror) { $errors .= '<li>'.$proterror.'</li>'; } - - my (%delsuggested,%suggids,@suggested);; - if (ref($currltisec{'suggested'}) eq 'HASH') { - my $maxnum = $env{'form.linkprot_suggested_maxnum'}; - my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_suggested_del'); - for (my $i=0; $i<$maxnum; $i++) { - my $itemid = $env{'form.linkprot_suggested_id_'.$i}; - $itemid =~ s/\D+//g; - if ($itemid) { - if (ref($currltisec{'suggested'}->{$itemid}) eq 'HASH') { - push(@suggested,$i); - $suggids{$i} = $itemid; - if ((@todelete > 0) && (grep(/^$i$/,@todelete))) { - if (ref($currltisec{'suggested'}{$itemid}) eq 'HASH') { - $delsuggested{$itemid} = $currltisec{'suggested'}{$itemid}{'name'}; - } - } else { - if ($env{'form.linkprot_suggested_name_'.$i} eq '') { - $delsuggested{$itemid} = $currltisec{'suggested'}{$itemid}{'name'}; - } else { - $env{'form.linkprot_suggested_name_'.$i} =~ s/(`)/'/g; - $env{'form.linkprot_suggested_info_'.$i} =~ s/(`)/'/g; - $newltisec{'suggested'}{$itemid}{'name'} = $env{'form.linkprot_suggested_name_'.$i}; - $newltisec{'suggested'}{$itemid}{'info'} = $env{'form.linkprot_suggested_info_'.$i}; - if (($currltisec{'suggested'}{$itemid}{'name'} ne $newltisec{'suggested'}{$itemid}{'name'}) || - ($currltisec{'suggested'}{$itemid}{'info'} ne $newltisec{'suggested'}{$itemid}{'info'})) { - $secchanges{'suggested'}{$itemid} = 1; - } - } - } - } - } - } - } - foreach my $key (keys(%delsuggested)) { - $newltisec{'suggested'}{$key} = $delsuggested{$key}; - $secchanges{'suggested'}{$key} = 1; - } - if (($env{'form.linkprot_suggested_add'}) && - ($env{'form.linkprot_suggested_name_add'} ne '')) { - $env{'form.linkprot_suggested_name_add'} =~ s/(`)/'/g; - $env{'form.linkprot_suggested_info_add'} =~ s/(`)/'/g; - my ($newsuggid,$errormsg) = &get_lti_id($dom,$env{'form.linkprot_suggested_name_add'},'suggested'); - if ($newsuggid) { - $newltisec{'suggested'}{$newsuggid}{'name'} = $env{'form.linkprot_suggested_name_add'}; - $newltisec{'suggested'}{$newsuggid}{'info'} = $env{'form.linkprot_suggested_info_add'}; - $secchanges{'suggested'}{$newsuggid} = 1; - } else { - my $error = &mt('Failed to acquire unique ID for new Link Protectors in Courses Suggestion'); - if ($errormsg) { - $error .= ' ('.$errormsg.')'; - } - $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; - } - } my (@items,%deletions,%itemids); if ($env{'form.lti_add'}) { my $consumer = $env{'form.lti_consumer_add'}; $consumer =~ s/(`)/'/g; - ($newid,my $errormsg) = &get_lti_id($dom,$consumer,'lti'); + ($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'); - if ($errormsg) { - $error .= ' ('.$errormsg.')'; - } $errors .= '<li><span class="LC_error">'.$error.'</span></li>'; } } @@ -14099,43 +16003,29 @@ sub modify_lti { } } } - my (%keystore,$secstored); - if ($is_home) { - &store_security($dom,'lti',\%secchanges,\%newkeyset,\%keystore); - } - - my ($cipher,$privnum); - if ((@items > 0) && ($is_home)) { - ($cipher,$privnum) = &get_priv_creds($dom,$home,$secchanges{'encrypt'}, - $newltisec{'encrypt'},$keystore{$home}); - } foreach my $idx (@items) { my $itemid = $itemids{$idx}; next unless ($itemid); - my %currlti; - unless ($idx eq 'add') { - if (ref($domconfig{$action}) eq 'HASH') { - if (ref($domconfig{$action}{$itemid}) eq 'HASH') { - %currlti = %{$domconfig{$action}{$itemid}}; - } - } - } my $position = $env{'form.lti_pos_'.$itemid}; $position =~ s/\D+//g; if ($position ne '') { $allpos[$position] = $itemid; } - foreach my $item ('consumer','lifetime','requser','crsinc') { + 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 '') { - $confhash{$itemid}{$item} = $env{$formitem}; - unless (($idx eq 'add') || ($changes{$itemid})) { - if ($currlti{$item} ne $confhash{$itemid}{$item}) { - $changes{$itemid} = 1; + 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; + } } } } @@ -14215,7 +16105,7 @@ sub modify_lti { ($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}; + my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx}; $mapcrs =~ s/(`)/'/g; $mapcrs =~ s/^\s+|\s+$//g; $confhash{$itemid}{'mapcrs'} = $mapcrs; @@ -14230,7 +16120,7 @@ sub modify_lti { $confhash{$itemid}{'mapcrstype'} = \@crstypes; if ($env{'form.lti_storecrs_'.$idx}) { $confhash{$itemid}{'storecrs'} = 1; - } + } if ($env{'form.lti_makecrs_'.$idx}) { $confhash{$itemid}{'makecrs'} = 1; } @@ -14276,27 +16166,27 @@ sub modify_lti { unless (($idx eq 'add') || ($changes{$itemid})) { if ($confhash{$itemid}{'crsinc'}) { foreach my $field ('mapcrs','storecrs','makecrs','section','passback','roster') { - if ($currlti{$field} ne $confhash{$itemid}{$field}) { + if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { $changes{$itemid} = 1; } } unless ($changes{$itemid}) { - if ($currlti{'passback'} eq $confhash{$itemid}{'passback'}) { - if ($currlti{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) { + 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($currlti{$field}) eq 'ARRAY') { + if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { - my @diffs = &Apache::loncommon::compare_arrays($currlti{$field}, + my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, $confhash{$itemid}{$field}); if (@diffs) { $changes{$itemid} = 1; } - } elsif (@{$currlti{$field}} > 0) { + } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { $changes{$itemid} = 1; } } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { @@ -14307,10 +16197,10 @@ sub modify_lti { } } unless ($changes{$itemid}) { - if (ref($currlti{'maproles'}) eq 'HASH') { + if (ref($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') { if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { - foreach my $ltirole (keys(%{$currlti{'maproles'}})) { - if ($currlti{'maproles'}{$ltirole} ne + foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) { + if ($domconfig{$action}{$itemid}{'maproles'}{$ltirole} ne $confhash{$itemid}{'maproles'}{$ltirole}) { $changes{$itemid} = 1; last; @@ -14318,14 +16208,14 @@ sub modify_lti { } unless ($changes{$itemid}) { foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) { - if ($confhash{$itemid}{'maproles'}{$ltirole} ne - $currlti{'maproles'}{$ltirole}) { + if ($confhash{$itemid}{'maproles'}{$ltirole} ne + $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) { $changes{$itemid} = 1; last; } } } - } elsif (keys(%{$currlti{'maproles'}}) > 0) { + } elsif (keys(%{$domconfig{$action}{$itemid}{'maproles'}}) > 0) { $changes{$itemid} = 1; } } elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { @@ -14339,20 +16229,20 @@ sub modify_lti { } unless ($changes{$itemid}) { foreach my $field ('mapuser','lcauth','lcauthparm','topmenu','inlinemenu','callback') { - if ($currlti{$field} ne $confhash{$itemid}{$field}) { + if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { $changes{$itemid} = 1; } } unless ($changes{$itemid}) { foreach my $field ('makeuser','lcmenu') { - if (ref($currlti{$field}) eq 'ARRAY') { + if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { - my @diffs = &Apache::loncommon::compare_arrays($currlti{$field}, + my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, $confhash{$itemid}{$field}); if (@diffs) { $changes{$itemid} = 1; } - } elsif (@{$currlti{$field}} > 0) { + } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { $changes{$itemid} = 1; } } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { @@ -14365,71 +16255,6 @@ sub modify_lti { } } } - if ($is_home) { - my $keyitem = 'form.lti_key_'.$idx; - $env{$keyitem} =~ s/(`)/'/g; - if ($env{$keyitem} ne '') { - $ltienc{$itemid}{'key'} = $env{$keyitem}; - unless ($changes{$itemid}) { - if ($currlti{'key'} ne $env{$keyitem}) { - $changes{$itemid} = 1; - } - } - } - my $secretitem = 'form.lti_secret_'.$idx; - $env{$secretitem} =~ s/(`)/'/g; - if ($currlti{'usable'}) { - if ($env{'form.lti_changesecret_'.$idx}) { - if ($env{$secretitem} ne '') { - if ($privnum && $cipher) { - $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem}); - $confhash{$itemid}{'cipher'} = $privnum; - } else { - $ltienc{$itemid}{'secret'} = $env{$secretitem}; - } - $changes{$itemid} = 1; - } - } else { - $ltienc{$itemid}{'secret'} = $currlti{'secret'}; - $confhash{$itemid}{'cipher'} = $currlti{'cipher'}; - } - if (ref($ltienc{$itemid}) eq 'HASH') { - if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'secret'} ne '')) { - $confhash{$itemid}{'usable'} = 1; - } - } - } elsif ($env{$secretitem} ne '') { - if ($privnum && $cipher) { - $ltienc{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem}); - $confhash{$itemid}{'cipher'} = $privnum; - } else { - $ltienc{$itemid}{'secret'} = $env{$secretitem}; - } - if (ref($ltienc{$itemid}) eq 'HASH') { - if (($ltienc{$itemid}{'key'} ne '') && ($ltienc{$itemid}{'key'} ne '')) { - $confhash{$itemid}{'usable'} = 1; - } - } - $changes{$itemid} = 1; - } - } - unless ($changes{$itemid}) { - foreach my $key (keys(%currlti)) { - if (ref($currlti{$key}) eq 'HASH') { - if (ref($confhash{$itemid}{$key}) eq 'HASH') { - foreach my $innerkey (keys(%{$currlti{$key}})) { - unless (exists($confhash{$itemid}{$key}{$innerkey})) { - $changes{$itemid} = 1; - last; - } - } - } elsif (keys(%{$currlti{$key}}) > 0) { - $changes{$itemid} = 1; - } - } - last if ($changes{$itemid}); - } - } } if (@allpos > 0) { my $idx = 0; @@ -14447,21 +16272,12 @@ sub modify_lti { } } } - - if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) { - return &mt('No changes made.'); - } - my %ltihash = ( $action => { %confhash } ); - my %ltienchash; - - if ($is_home) { - %ltienchash = ( - $action => { %ltienc } - ); - } + my %ltienchash = ( + $action => { %encconfig } + ); if (keys(%secchanges)) { $ltihash{'ltisec'} = \%newltisec; if ($secchanges{'linkprot'}) { @@ -14472,32 +16288,130 @@ sub modify_lti { } my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash,$dom); if ($putresult eq 'ok') { - if (keys(%ltienchash)) { - &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); + 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) { - $resulttext .= <i_security_results($dom,'lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore); - if (exists($secchanges{'linkprot'})) { - $resulttext .= $linkprotoutput; + 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; - &Apache::lonnet::do_cache_new('lti',$dom,\%confhash,$cachetime); + 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))) { - if (ref($confhash{$itemid}) eq 'HASH') { - my $position = $confhash{$itemid}{'order'}; - $bynum{$position} = $itemid; - } + my $position = $confhash{$itemid}{'order'}; + $bynum{$position} = $itemid; } foreach my $pos (sort { $a <=> $b } keys(%bynum)) { my $itemid = $bynum{$pos}; - if (ref($confhash{$itemid}) eq 'HASH') { + 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>'; @@ -14506,11 +16420,13 @@ sub modify_lti { $resulttext .= '<li>'.$lt{$item}.': '.$confhash{$itemid}{$item}.'</li>'; } } - if ($ltienc{$itemid}{'key'} ne '') { - $resulttext .= '<li>'.$lt{'key'}.': '.$ltienc{$itemid}{'key'}.'</li>'; + if ($encconfig{$itemid}{'key'} ne '') { + $resulttext .= '<li>'.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'</li>'; } - if ($ltienc{$itemid}{'secret'} ne '') { - $resulttext .= '<li>'.$lt{'secret'}.': ['.&mt('not shown').']</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'}) { @@ -14530,7 +16446,7 @@ sub modify_lti { $resulttext .= '<li>'.&mt('LON-CAPA username').': '.$shownmapuser.'</li>'; } if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') { - if (@{$confhash{$itemid}{'makeuser'}} > 0) { + 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') { @@ -14625,7 +16541,7 @@ sub modify_lti { if ($confhash{$itemid}{'section'}) { if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') { $resulttext .= '<li>'.&mt('User section from standard field:'). - ' (course_section_sourcedid)'.'</li>'; + ' (course_section_sourcedid)'.'</li>'; } else { $resulttext .= '<li>'.&mt('User section from:').' '. $confhash{$itemid}{'section'}.'</li>'; @@ -14652,9 +16568,9 @@ sub modify_lti { if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') { if (@{$confhash{$itemid}{'lcmenu'}} > 0) { $resulttext .= '<li>'.&mt('Menu items:').' '. - join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>'; + join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'</li>'; } else { - $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>'; + $resulttext .= '<li>'.&mt('No menu items displayed in header or online menu').'</li>'; } } } @@ -14662,19 +16578,8 @@ sub modify_lti { $resulttext .= '</ul></li>'; } } - if (keys(%deletions)) { - foreach my $itemid (sort { $a <=> $b } keys(%deletions)) { - $resulttext .= '<li>'.&mt('Deleted: [_1]',$changes{$itemid}).'</li>'; - } - } } $resulttext .= '</ul>'; - if (ref($lastactref) eq 'HASH') { - if (($secchanges{'encrypt'}) || ($secchanges{'private'}) || (exists($secchanges{'suggested'}))) { - &Apache::lonnet::get_domain_defaults($dom,1); - $lastactref->{'domdefaults'} = 1; - } - } } else { $errors .= '<li><span class="LC_error">'.&mt('Failed to save changes').'</span></li>'; } @@ -14685,51 +16590,24 @@ sub modify_lti { return $resulttext; } -sub get_priv_creds { - my ($dom,$home,$encchg,$encrypt,$storedsec) = @_; - my ($needenc,$cipher,$privnum); - my %domdefs = &Apache::lonnet::get_domain_defaults($dom); - if (($encchg) && (ref($encrypt) eq 'HASH')) { - $needenc = $encrypt->{'consumers'} - } else { - $needenc = $domdefs{'ltienc_consumers'}; - } - if ($needenc) { - if (($storedsec eq 'ok') || ((ref($domdefs{'ltiprivhosts'}) eq 'ARRAY') && - (grep(/^\Q$home\E$/,@{$domdefs{'ltiprivhosts'}})))) { - my %privhash = &Apache::lonnet::restore_dom('lti','private',$dom,$home,1); - my $privkey = $privhash{'key'}; - $privnum = $privhash{'version'}; - if (($privnum) && ($privkey ne '')) { - $cipher = Crypt::CBC->new({'key' => $privkey, - 'cipher' => 'DES'}); - } - } - } - return ($cipher,$privnum); -} - sub get_lti_id { - my ($domain,$consumer,$dbname) = @_; - unless (($dbname eq 'lti') || ($dbname eq 'suggested')) { - return ('','invalid db'); - } - # get lock on db + 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($dbname,$lockhash,$domain); + 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($dbname,$lockhash,$domain); + $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); } if ($gotlock eq 'ok') { - my %currids = &Apache::lonnet::dump_dom($dbname,$domain); + my %currids = &Apache::lonnet::dump_dom('lti',$domain); if ($currids{'lock'}) { delete($currids{'lock'}); if (keys(%currids)) { @@ -14741,14 +16619,14 @@ sub get_lti_id { $id = 1; } if ($id) { - unless (&Apache::lonnet::newput_dom($dbname,{ $id => $consumer },$domain) eq 'ok') { + unless (&Apache::lonnet::newput_dom('lti',{ $id => $consumer },$domain) eq 'ok') { $error = 'nostore'; } } else { $error = 'nonumber'; } } - my $dellockoutcome = &Apache::lonnet::del_dom($dbname,['lock'],$domain); + my $dellockoutcome = &Apache::lonnet::del_dom('lti',['lock'],$domain); } else { $error = 'nolock'; } @@ -15514,7 +17392,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) { @@ -15572,12 +17450,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; } @@ -15594,7 +17472,7 @@ sub modify_contacts { } } else { foreach my $key (@overrides) { - push(@{$changes{'overrides'}},$key); + push(@{$changes{'overrides'}},$key); } } } @@ -15776,7 +17654,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}; @@ -15800,14 +17678,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>'); @@ -15964,6 +17842,230 @@ sub modify_contacts { } } else { $resulttext = '<span class="LC_error">'. + &mt('An error occurred: [_1].',$putresult).'</span>'; + } + 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', + ); + 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', + ); + 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 %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','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 '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; @@ -15994,6 +18096,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)', @@ -16144,15 +18247,12 @@ sub modify_passwords { $error = &mt("Upload of file containing domain-specific text is not permitted to this server: [_1]",$switchserver); } else { if ($author_ok eq 'ok') { - my $modified = []; my ($result,$customurl) = - &Apache::lonconfigsettings::publishlogo($r,'upload','passwords_customfile',$dom, - $confname,'customtext/resetpw','','',$customfn, - $modified); + &publishlogo($r,'upload','passwords_customfile',$dom, + $confname,'customtext/resetpw','','',$customfn); if ($result eq 'ok') { $newvalues{'resetcustom'} = $customurl; $changes{'reset'} = 1; - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$customfn,$result); } @@ -16377,7 +18477,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}); @@ -16400,7 +18500,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>'; @@ -16472,15 +18572,20 @@ sub password_rule_changes { (ref($changes) eq 'HASH')); my (@rules,%staticdefaults); if ($prefix eq 'passwords') { - @rules = ('min','max','numsaved'); - } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { + @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 'min') { + 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; @@ -16878,7 +18983,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})) { @@ -16890,7 +18995,7 @@ sub modify_selfcreation { } } elsif ($emaildom ne '') { push(@{$changes{'cancreate'}},'emaildomain'); - } + } } $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type}; } elsif ($chosen eq 'custom') { @@ -17317,7 +19422,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') { @@ -17403,7 +19508,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') { @@ -17421,7 +19526,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>'; } } } @@ -17525,7 +19630,7 @@ sub modify_selfcreation { $typename = $othertitle; } else { $typename = $usertypes{$type}; - } + } $chgtext .= &mt('(Affiliation: [_1])',$typename); } if (@{$email_rule{$type}} > 0) { @@ -17843,58 +19948,16 @@ sub modify_defaults { } } elsif ($item eq 'portal_def') { if ($newvalues{$item} ne '') { - 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 { + 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])\/?$/) { 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 = ( @@ -17909,41 +19972,6 @@ 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, ); @@ -17964,12 +19992,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); } } } @@ -18057,28 +20083,8 @@ sub modify_defaults { $resulttext =~ s/, $//; $resulttext .= '</li>'; } else { - $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>'; + $resulttext .= '<li>'.&mt('Institutional user status types deleted').'</li>'; } - } else { - $resulttext .= '<li>'.&mt('Mapping for missing usernames via standard log-in deleted').'</li>'; } } else { my $value = $env{'form.'.$item}; @@ -18096,20 +20102,7 @@ sub modify_defaults { $value = $authnames{$shortauth{$value}}; } $resulttext .= '<li>'.&mt('[_1] set to "[_2]"',$title->{$item},$value).'</li>'; - $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>'; - } - } - } + $mailmsgtext .= "$title->{$item} set to $value\n"; } } $resulttext .= '</ul>'; @@ -18164,15 +20157,12 @@ sub modify_scantron { $error = &mt("Upload of bubblesheet format file is not permitted to this server: [_1]",$switchserver); } else { if ($author_ok eq 'ok') { - my $modified = []; my ($result,$scantronurl) = - &Apache::lonconfigsettings::publishlogo($r,'upload','scantronformat',$dom, - $confname,'scantron','','',$custom, - $modified); + &publishlogo($r,'upload','scantronformat',$dom, + $confname,'scantron','','',$custom); if ($result eq 'ok') { $confhash{'scantron'}{'scantronformat'} = $scantronurl; $changes{'scantronformat'} = 1; - &update_modify_urls($r,$modified); } else { $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$custom,$result); } @@ -18321,7 +20311,7 @@ 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 .= '<p>'.&mt('The following errors occurred: ').'<ul>'. @@ -18354,6 +20344,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)) { @@ -18368,11 +20367,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') { @@ -18390,6 +20393,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') { @@ -18452,9 +20458,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; @@ -19053,36 +21073,29 @@ 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','ltiauth'); + my @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem','ltiauth'); my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial', - 'uploadquota_community','uploadquota_textbook','coursequota_official', - 'coursequota_unofficial','coursequota_community','coursequota_textbook', + 'uploadquota_community','uploadquota_textbook','uploadquota_placement', 'mysqltables_official','mysqltables_unofficial','mysqltables_community', - 'mysqltables_textbook'); - my @types = ('official','unofficial','community','textbook'); + 'mysqltables_textbook','mysqltables_placement'); + my @types = ('official','unofficial','community','textbook','placement'); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, - coursequota => 20, postsubmit => 60, mysqltables => 172800, - domexttool => 1, - crseditors => ['edit','xml'], ); my %texoptions = ( MathJax => 'MathJax', mimetex => &mt('Convert to Images'), tth => &mt('TeX to HTML'), ); - - my @editors = ('edit','xml','daxe'); - my %editornames = &crseditor_titles(); - $defaultshash{'coursedefaults'} = {}; if (ref($domconfig{'coursedefaults'}) ne 'HASH') { @@ -19121,7 +21134,7 @@ sub modify_coursedefaults { } $defaultshash{'coursedefaults'}{$item} = $newdef; } else { - my ($setting,$type) = ($item =~ /^(uploadquota|coursequota|mysqltables)_(\w+)$/); + my ($setting,$type) = ($item =~ /^(uploadquota|mysqltables)_(\w+)$/); if (ref($domconfig{'coursedefaults'}{$setting}) eq 'HASH') { $currdef = $domconfig{'coursedefaults'}{$setting}{$type}; } @@ -19133,7 +21146,7 @@ sub modify_coursedefaults { unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) { $changes{$item} = 1; } - } elsif ($item =~ /^(uploadquota|coursequota|mysqltables)_/) { + } elsif ($item =~ /^(uploadquota|mysqltables)_/) { my $setting = $1; unless (($currdef eq '') && ($newdef == $staticdefaults{$setting})) { $changes{$setting} = 1; @@ -19268,82 +21281,17 @@ sub modify_coursedefaults { $changes{'postsubmit'} = 1; } } - my (%newdomexttool,%newexttool,%olddomexttool,%oldexttool,%posscrseditors); - map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool'); - map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool'); - map { $posscrseditors{$_} = 1; } &Apache::loncommon::get_env_multiple('form.crseditors'); - if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') { - %olddomexttool = %{$domconfig{'coursedefaults'}{'domexttool'}}; - } else { - foreach my $type (@types) { - if ($staticdefaults{'domexttool'}) { - $olddomexttool{$type} = 1; - } else { - $olddomexttool{$type} = 0; - } - } - } - if (ref($domconfig{'coursedefaults'}{'exttool'}) eq 'HASH') { - %oldexttool = %{$domconfig{'coursedefaults'}{'exttool'}}; - } else { - foreach my $type (@types) { - if ($staticdefaults{'exttool'}) { - $oldexttool{$type} = 1; - } else { - $oldexttool{$type} = 0; - } - } - } - my @newcrseditors = (); - foreach my $editor (@editors) { - if ($posscrseditors{$editor}) { - push(@newcrseditors,$editor); - } - } - if (ref($domconfig{'coursedefaults'}{'crseditors'}) eq 'ARRAY') { - my @diffs = - &Apache::loncommon::compare_arrays($domconfig{'coursedefaults'}{'crseditors'}, - \@newcrseditors); - if (@diffs) { - $changes{'crseditors'} = 1; - } - } else { - my @diffs = - &Apache::loncommon::compare_arrays($staticdefaults{'crseditors'}, - \@newcrseditors); - unless (@diffs == 0) { - $changes{'crseditors'} = 1; - } - } - foreach my $type (@types) { - unless ($newdomexttool{$type}) { - $newdomexttool{$type} = 0; - } - unless ($newexttool{$type}) { - $newexttool{$type} = 0; - } - if ($newdomexttool{$type} != $olddomexttool{$type}) { - $changes{'domexttool'} = 1; - } - if ($newexttool{$type} != $oldexttool{$type}) { - $changes{'exttool'} = 1; - } - } - $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool; - $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool; - $defaultshash{'coursedefaults'}{'crseditors'} = \@newcrseditors; } my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); 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'}) || ($changes{'ltiauth'}) || ($changes{'domexttool'}) || - ($changes{'exttool'}) || ($changes{'coursequota'})) { - foreach my $item ('uselcmath','usejsme','inline_chem','texengine','ltiauth') { + ($changes{'inline_chem'}) || ($changes{'ltiauth'})) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine','ltiauth') { if ($changes{$item}) { $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; } @@ -19374,13 +21322,6 @@ sub modify_coursedefaults { } } } - if ($changes{'coursequota'}) { - if (ref($defaultshash{'coursedefaults'}{'coursequota'}) eq 'HASH') { - foreach my $type (@types) { - $domdefaults{$type.'coursequota'}=$defaultshash{'coursedefaults'}{'coursequota'}{$type}; - } - } - } if ($changes{'canclone'}) { if (ref($defaultshash{'coursedefaults'}{'canclone'}) eq 'HASH') { if (ref($defaultshash{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') { @@ -19393,25 +21334,6 @@ sub modify_coursedefaults { $domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'}; } } - if ($changes{'domexttool'}) { - if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') { - foreach my $type (@types) { - $domdefaults{$type.'domexttool'}=$defaultshash{'coursedefaults'}{'domexttool'}{$type}; - } - } - } - if ($changes{'exttool'}) { - if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') { - foreach my $type (@types) { - $domdefaults{$type.'exttool'}=$defaultshash{'coursedefaults'}{'exttool'}{$type}; - } - } - } - if ($changes{'crseditors'}) { - if (ref($defaultshash{'coursedefaults'}{'crseditors'}) eq 'ARRAY') { - $domdefaults{'crseditors'}=join(',',@{$defaultshash{'coursedefaults'}{'crseditors'}}); - } - } my $cachetime = 24*60*60; &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); if (ref($lastactref) eq 'HASH') { @@ -19420,7 +21342,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 { @@ -19451,30 +21379,20 @@ 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>'; } else { $resulttext .= '<li>'.&mt('Default quota for content uploaded via Course Editor remains default: [_1] MB',$staticdefaults{'uploadquota'}).'</li>'; } - } elsif ($item eq 'coursequota') { - if (ref($defaultshash{'coursedefaults'}{'coursequota'}) eq 'HASH') { - $resulttext .= '<li>'.&mt('Default cumulative quota for all group portfolio spaces in course set as follows:').'<ul>'. - '<li>'.&mt('Official courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'official'}.'</b>').'</li>'. - '<li>'.&mt('Unofficial courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'unofficial'}.'</b>').'</li>'. - '<li>'.&mt('Textbook courses: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'textbook'}.'</b>').'</li>'. - '<li>'.&mt('Communities: [_1] MB','<b>'.$defaultshash{'coursedefaults'}{'coursequota'}{'community'}.'</b>').'</li>'. - '</ul>'. - '</li>'; - } else { - $resulttext .= '<li>'.&mt('Default cumulative quota for all group portfolio spaces in course remains default: [_1] MB',$staticdefaults{'coursequota'}).'</li>'; - } } elsif ($item eq 'mysqltables') { if (ref($defaultshash{'coursedefaults'}{'mysqltables'}) eq 'HASH') { $resulttext .= '<li>'.&mt('Lifetime of "Temporary" MySQL tables (student performance data) on homeserver').'<ul>'. '<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>'; @@ -19510,6 +21428,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>'; } @@ -19551,44 +21471,6 @@ sub modify_coursedefaults { } else { $resulttext .= '<li>'.&mt('LTI launch of deep-linked URL will require re-authentication').'</li>'; } - } elsif ($item eq 'domexttool') { - my @noyes = (&mt('no'),&mt('yes')); - if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') { - $resulttext .= '<li>'.&mt('External Tools defined in the domain may be used as follows:').'<ul>'. - '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'official'}].'</b>').'</li>'. - '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'unofficial'}].'</b>').'</li>'. - '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'textbook'}].'</b>').'</li>'. - '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'placement'}].'</b>').'</li>'. - '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'community'}].'</b>').'</li>'. - '</ul>'. - '</li>'; - } else { - $resulttext .= '<li>'.&mt('External Tools defined in the domain may be used in all course types, by default').'</li>'; - } - } elsif ($item eq 'exttool') { - my @noyes = (&mt('no'),&mt('yes')); - if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') { - $resulttext .= '<li>'.&mt('External Tools can be defined and configured in course containers as follows:').'<ul>'. - '<li>'.&mt('Official courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'official'}].'</b>').'</li>'. - '<li>'.&mt('Unofficial courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'unofficial'}].'</b>').'</li>'. - '<li>'.&mt('Textbook courses: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'textbook'}].'</b>').'</li>'. - '<li>'.&mt('Placement tests: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'placement'}].'</b>').'</li>'. - '<li>'.&mt('Communities: [_1]','<b>'.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'community'}].'</b>').'</li>'. - '</ul>'. - '</li>'; - } else { - $resulttext .= '<li>'.&mt('External Tools can not be defined in any course types, by default').'</li>'; - } - } elsif ($item eq 'crseditors') { - if (ref($defaultshash{'coursedefaults'}{$item}) eq 'ARRAY') { - my $shown; - if (@{$defaultshash{'coursedefaults'}{$item}}) { - $shown = join(', ', map { $editornames{$_} } @{$defaultshash{'coursedefaults'}{$item}}); - } else { - $shown = &mt('None'); - } - $resulttext .= '<li>'.&mt('Available editors for course/community resources: [_1]',$shown).'</li>'; - } } } $resulttext .= '</ul>'; @@ -19605,7 +21487,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(); @@ -19982,7 +21864,7 @@ sub modify_wafproxy { } } elsif ($currvalue{$item}) { $changes{$item} = 1; - } + } } } else { if (keys(%curralias)) { @@ -19990,7 +21872,7 @@ sub modify_wafproxy { } if (keys(%currsaml)) { $changes{'saml'} = 1; - } + } if (keys(%currvalue)) { foreach my $key (keys(%currvalue)) { $changes{$key} = 1; @@ -20000,7 +21882,7 @@ sub modify_wafproxy { if (keys(%changes)) { my %defaultshash = ( wafproxy => \%wafproxy, - ); + ); my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); if ($putresult eq 'ok') { @@ -20016,7 +21898,7 @@ sub modify_wafproxy { $domdefaults{'waf_'.$item} = $wafproxy{$item}; } elsif (exists($domdefaults{'waf_'.$item})) { delete($domdefaults{'waf_'.$item}); - } + } } } if ($updatedomdefs) { @@ -20083,7 +21965,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'}}))); @@ -20147,7 +22029,6 @@ sub modify_wafproxy { } } } - $output .= '</ul>'; } else { $output = '<span class="LC_error">'. &mt('An error occurred: [_1]',$putresult).'</span>'; @@ -20191,8 +22072,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) { @@ -20413,7 +22294,7 @@ sub modify_usersessions { if (($offload eq 'offloadoth') && (@okoffloadoth)) { $changes{'offloadoth'} = 1; } - } + } } } else { if (@okoffload) { @@ -20481,14 +22362,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; } } } @@ -20496,6 +22381,8 @@ 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>'; } @@ -20555,6 +22442,324 @@ 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); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 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'); @@ -20625,7 +22830,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; } @@ -21311,6 +23516,7 @@ function balancerChange(balnum,baltotal, END } + sub new_spares_js { my @sparestypes = ('primary','default'); my $types = join("','",@sparestypes);