--- loncom/interface/domainprefs.pm 2022/02/14 02:48:46 1.405 +++ loncom/interface/domainprefs.pm 2023/11/03 01:12:15 1.430 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.405 2022/02/14 02:48:46 raeburn Exp $ +# $Id: domainprefs.pm,v 1.430 2023/11/03 01:12:15 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -95,8 +95,7 @@ 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, and to display/store -default quota sizes for Authoring Spaces. +used by course owners to request creation of a course. Outputs: 1 @@ -105,7 +104,7 @@ $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, placement, and lti). -In each case the radio buttons allow the selection of one of four values: +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: @@ -167,6 +166,7 @@ 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(); @@ -177,6 +177,7 @@ use DateTime::TimeZone; use DateTime::Locale; use Time::HiRes qw( sleep ); use Net::CIDR; +use Crypt::CBC; my $registered_cleanup; my $modified_urls; @@ -220,17 +221,28 @@ sub handler { 'serverstatuses','requestcourses','helpsettings', 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', - 'ltitools','ssl','trust','lti','ltisec','privacy','passwords', - 'proctoring','wafproxy','ipaccess'],$dom); + 'ltitools','toolsec','ssl','trust','lti','ltisec', + 'privacy','passwords','proctoring','wafproxy', + 'ipaccess','authordefaults'],$dom); my %encconfig = &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1); + my ($checked_is_home,$is_home); 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')) { - foreach my $item ('key','secret') { - $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item}; + $domconfig{'ltitools'}{$id}{'key'} = $encconfig{'ltitools'}{$id}{'key'}; + if (($is_home) && ($phase eq 'process')) { + $domconfig{'ltitools'}{$id}{'secret'} = $encconfig{'ltitools'}{$id}{'secret'}; } } } @@ -238,24 +250,38 @@ 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')) { - foreach my $item ('key','secret') { - $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item}; + $domconfig{'lti'}{$id}{'key'} = $encconfig{'lti'}{$id}{'key'}; + if (($is_home) && ($phase eq 'process')) { + $domconfig{'lti'}{$id}{'secret'} = $encconfig{'lti'}{$id}{'secret'}; } } } } } if (ref($domconfig{'ltisec'}) eq 'HASH') { - if (ref($domconfig{'ltisec'}{'prot'}) eq 'HASH') { + if (ref($domconfig{'ltisec'}{'linkprot'}) eq 'HASH') { if (ref($encconfig{'linkprot'}) eq 'HASH') { - foreach my $id (keys(%{$domconfig{'ltisec'}{'prot'}})) { - if ((ref($domconfig{'ltisec'}{'prot'}{$id}) eq 'HASH') && + foreach my $id (keys(%{$domconfig{'ltisec'}{'linkprot'}})) { + unless ($id =~ /^\d+$/) { + delete($domconfig{'ltisec'}{'linkprot'}{$id}); + } + if ((ref($domconfig{'ltisec'}{'linkprot'}{$id}) eq 'HASH') && (ref($encconfig{'linkprot'}{$id}) eq 'HASH')) { foreach my $item ('key','secret') { - $domconfig{'ltisec'}{'prot'}{$id}{$item} = $encconfig{'linkprot'}{$id}{$item}; + $domconfig{'ltisec'}{'linkprot'}{$id}{$item} = $encconfig{'linkprot'}{$id}{$item}; } } } @@ -279,8 +305,8 @@ sub handler { 'contacts','privacy','usercreation','selfcreation', 'usermodification','scantron','requestcourses','requestauthor', 'coursecategories','serverstatuses','helpsettings','coursedefaults', - 'ltitools','proctoring','selfenrollment','usersessions','ssl', - 'trust','lti'); + 'authordefaults','ltitools','proctoring','selfenrollment', + 'usersessions','ssl','trust','lti'); my %existing; if (ref($domconfig{'loadbalancing'}) eq 'HASH') { %existing = %{$domconfig{'loadbalancing'}}; @@ -324,7 +350,9 @@ sub handler { header => [{col1 => 'Setting', col2 => 'Value'}, {col1 => 'Institutional user types', - col2 => 'Name displayed'}], + col2 => 'Name displayed'}, + {col1 => 'Mapping for missing usernames via standard log-in', + col2 => 'Rules in use'}], print => \&print_defaults, modify => \&modify_defaults, }, @@ -354,11 +382,11 @@ sub handler { modify => \&modify_passwords, }, 'quotas' => - { text => 'Blogs, personal web pages, webDAV/quotas, portfolios', + { text => 'Blogs, personal pages/timezones, portfolio/quotas', help => 'Domain_Configuration_Quotas', header => [{col1 => 'User affiliation', col2 => 'Available tools', - col3 => 'Quotas, MB; (Authoring requires role)',}], + col3 => 'Portfilo quota (MB)',}], print => \&print_quotas, modify => \&modify_quotas, }, @@ -540,7 +568,7 @@ sub handler { modify => \&modify_selfenrollment, }, 'privacy' => - {text => 'Availability of User Information', + {text => 'Role assignments and user privacy', help => 'Domain_Configuration_User_Privacy', header => [{col1 => 'Role assigned in different domain', col2 => 'Approval options'}, @@ -576,11 +604,15 @@ sub handler { print => \&print_loadbalancing, modify => \&modify_loadbalancing, }, - 'ltitools' => + 'ltitools' => {text => 'External Tools (LTI)', help => 'Domain_Configuration_LTI_Tools', - header => [{col1 => 'Setting', - col2 => 'Value',}], + header => [{col1 => 'Encryption of shared secrets', + col2 => 'Settings'}, + {col1 => 'Rules for shared secrets', + col2 => 'Settings'}, + {col1 => 'Providers', + col2 => 'Settings',}], print => \&print_ltitools, modify => \&modify_ltitools, }, @@ -630,21 +662,21 @@ sub handler { print => \&print_trust, modify => \&modify_trust, }, - 'lti' => + '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 (domain)', + {col1 => 'Link Protectors', col2 => 'Settings'}, {col1 => 'Consumers', col2 => 'Settings'},], print => \&print_lti, modify => \&modify_lti, }, - 'ipaccess' => + 'ipaccess' => {text => 'IP-based access control', help => 'Domain_Configuration_IP_Access', header => [{col1 => 'Setting', @@ -652,6 +684,16 @@ 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', @@ -850,13 +892,15 @@ sub process_changes { } elsif ($action eq 'lti') { $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig); } elsif ($action eq 'privacy') { - $output = &modify_privacy($dom,%domconfig); + $output = &modify_privacy($dom,$lastactref,%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; } @@ -885,12 +929,12 @@ sub print_config_box { &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent); my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); $output = - &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full, + &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full, \@templateroles); } elsif ($action eq 'ltitools') { - $output .= <itools_javascript($settings); + $output .= &Apache::lonconfigsettings::ltitools_javascript($settings); } elsif ($action eq 'lti') { - $output .= &passwords_javascript('secrets')."\n". + $output .= &passwords_javascript('ltisecrets')."\n". <i_javascript($dom,$settings); } elsif ($action eq 'proctoring') { $output .= &proctoring_javascript($settings); @@ -904,6 +948,8 @@ sub print_config_box { $output .= &saml_javascript(); } elsif ($action eq 'ipaccess') { $output .= &ipaccess_javascript($settings); + } elsif ($action eq 'authordefaults') { + $output .= &authordefaults_javascript(); } $output .= ' @@ -945,7 +991,8 @@ sub print_config_box { ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') || ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') || - ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') || ($action eq 'lti')) { + ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') || + ($action eq 'lti') || ($action eq 'ltitools') || ($action eq 'authordefaults')) { $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal); @@ -980,8 +1027,9 @@ sub print_config_box { if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'coursecategories') || - ($action eq 'trust') || ($action eq 'contacts') || - ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti')) { + ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'defaults') || + ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti') || + ($action eq 'ltitools')) { if ($action eq 'coursecategories') { $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal); $colspan = ' colspan="2"'; @@ -1068,8 +1116,8 @@ sub print_config_box { } $rowtotal ++; } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') || - ($action eq 'defaults') || ($action eq 'directorysrch') || - ($action eq 'helpsettings') || ($action eq 'wafproxy')) { + ($action eq 'directorysrch') || ($action eq 'helpsettings') || + ($action eq 'wafproxy') || ($action eq 'authordefaults')) { $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } elsif ($action eq 'scantron') { $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal); @@ -1272,8 +1320,7 @@ sub print_config_box { $output .= &print_quotas($dom,$settings,\$rowtotal,$action); } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || - ($action eq 'ltitools') || ($action eq 'proctoring') || - ($action eq 'ipaccess')) { + ($action eq 'proctoring') || ($action eq 'ipaccess')) { $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } } @@ -1623,13 +1670,13 @@ sub print_login { '
'. ''. ''."\n"; - my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso,%styleon,%styleoff); + my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso,%styleon,%styleoff); foreach my $lonhost (keys(%domservers)) { $samlurl{$lonhost} = '/adm/sso'; $styleon{$lonhost} = 'display:none'; $styleoff{$lonhost} = ''; } - if (ref($settings->{'saml'}) eq 'HASH') { + if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) { foreach my $lonhost (keys(%{$settings->{'saml'}})) { if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') { $saml{$lonhost} = 1; @@ -1638,6 +1685,7 @@ sub print_login { $samlalt{$lonhost} = $settings->{'saml'}{$lonhost}{'alt'}; $samlurl{$lonhost} = $settings->{'saml'}{$lonhost}{'url'}; $samltitle{$lonhost} = $settings->{'saml'}{$lonhost}{'title'}; + $samlwindow{$lonhost} = $settings->{'saml'}{$lonhost}{'window'}; $samlnotsso{$lonhost} = $settings->{'saml'}{$lonhost}{'notsso'}; $styleon{$lonhost} = ''; $styleoff{$lonhost} = 'display:none'; @@ -1655,6 +1703,12 @@ sub print_login { $samlon = $samloff; $samloff = ' '; } + my $samlwinon = ''; + my $samlwinoff = ' checked="checked"'; + if ($samlwindow{$lonhost}) { + $samlwinon = $samlwinoff; + $samlwinoff = ''; + } my $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. ''. ''. ''; @@ -2351,7 +2411,7 @@ sub print_quotas { } else { $context = $action; } - my ($datatable,$defaultquota,$authorquota,@usertools,@options,%validations); + my ($datatable,$defaultquota,@usertools,@options,%validations); my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my $typecount = 0; my ($css_class,%titles); @@ -2365,12 +2425,12 @@ sub print_quotas { @options = ('norequest','approval','automatic'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','webdav','portfolio'); + @usertools = ('aboutme','blog','portfolio','portaccess','timezone'); %titles = &tool_titles(); } if (ref($types) eq 'ARRAY') { foreach my $type (@{$types}) { - my ($currdefquota,$currauthorquota); + my $currdefquota; unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { if (ref($settings) eq 'HASH') { @@ -2379,9 +2439,6 @@ sub print_quotas { } else { $currdefquota = $settings->{$type}; } - if (ref($settings->{authorquota}) eq 'HASH') { - $currauthorquota = $settings->{authorquota}->{$type}; - } } } if (defined($usertypes->{$type})) { @@ -2469,9 +2526,12 @@ sub print_quotas { } } else { my $checked = 'checked="checked" '; + if ($item eq 'timezone') { + $checked = ''; + } if (ref($settings) eq 'HASH') { if (ref($settings->{$item}) eq 'HASH') { - if ($settings->{$item}->{$type} == 0) { + if (!$settings->{$item}->{$type}) { $checked = ''; } elsif ($settings->{$item}->{$type} == 1) { $checked = 'checked="checked" '; @@ -2496,13 +2556,9 @@ sub print_quotas { ($context eq 'requestauthor')) { $datatable .= ''; } $datatable .= ''; @@ -2511,16 +2567,12 @@ 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 ++; @@ -2632,12 +2684,9 @@ sub print_quotas { $datatable .= ''; unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { $datatable .= ''; + $defaultquota.'" size="5" />'; } $datatable .= ''; $typecount ++; @@ -3410,8 +3459,17 @@ 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; + return $togglejs.' + +'; } my (%ordered,$total,%jstext); $total = scalar(keys(%{$settings})); @@ -3473,6 +3531,9 @@ $jstext } return; } + +$linkprot_js + // ]]> @@ -3492,7 +3553,6 @@ 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"; @@ -3894,16 +3836,16 @@ sub saml_javascript { return <<"ENDSCRIPT"; + +ENDSCRIPT +} + sub print_autoenroll { my ($dom,$settings,$rowtotal) = @_; my $autorun = &Apache::lonnet::auto_run(undef,$dom), @@ -4671,7 +4646,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 .= ''. ''. - ''."\n"; - $itemcount ++; } } - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_add_pos'".');"'; - $datatable .= ''."\n". - ''."\n". - ''."\n"; - $itemcount ++; return $datatable; } @@ -5793,7 +5395,7 @@ sub ltitools_names { 'key' => 'Key', 'lifetime' => 'Nonce lifetime (s)', 'secret' => 'Secret', - 'icon' => 'Icon', + 'icon' => 'Icon', 'user' => 'User', 'fullname' => 'Full Name', 'firstname' => 'First Name', @@ -5811,7 +5413,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', @@ -5819,6 +5421,123 @@ 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; + } + if ($encrypt->{'ltisec_crslinkprot'}) { + $showextra = 1; + } + } else { + if (($encrypt->{'toolsec_crs'}) || ($encrypt->{'toolsec_dom'})) { + $showextra = 1; + } + } + unless (grep(/^\Q$hostid\E$/,@ids)) { + $switch = 1; + } + if ($showextra) { + $numshown ++; + $divsty = 'display:inline-block'; + } else { + $divsty = 'display:none'; + } + $extra .= '
'. + ''.$hostid.''; + if ($switch) { + my $switchserver = ''.&mt('Switch Server').''; + if (exists($privkeys->{$hostid})) { + $extra .= '
'. + ''. + &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'
'. + ''.&mt('Change?'). + ''. + (' 'x2). + '  '; + } else { + $extra .= ''. + &mt('Key required').' - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver). + ''."\n"; + } + } elsif (exists($privkeys->{$hostid})) { + $extra .= '
'. + &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'
'. + ''.&mt('Change?'). + ''. + (' 'x2). + '  '; + } else { + $extra .= ''.&mt('Encryption Key').':'. + ''. + ''; + } + $extra .= '
'; + } + } + 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', + ); + } + my ($onclick,$itemcount); + $onclick = 'javascript:toggleLTIEncKey(this.form,'."'$context'".');'; + ($output,$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'; + } + $output .= ''. + ''; + $itemcount ++; + $$rowtotal += $itemcount; + return $output; +} + sub print_proctoring { my ($dom,$settings,$rowtotal) = @_; my $itemcount = 1; @@ -6490,6 +6209,9 @@ sub print_lti { if (exists($settings->{'linkprot'})) { if (ref($settings->{'linkprot'}) eq 'HASH') { %linkprot = %{$settings->{'linkprot'}}; + if ($linkprot{'lock'}) { + delete($linkprot{'lock'}); + } } } } else { @@ -6501,103 +6223,16 @@ sub print_lti { } } if ($position eq 'top') { - my @ids=&Apache::lonnet::current_machine_ids(); - my %servers = &Apache::lonnet::get_servers($dom,'library'); - my $primary = &Apache::lonnet::domain($dom,'primary'); - my ($extra,$numshown); - foreach my $hostid (sort(keys(%servers))) { - my ($showextra,$divsty,$switch); - if ($hostid eq $primary) { - if (($encrypt{'ltisec_consumers'}) || ($encrypt{'ltisec_domlinkprot'})) { - $showextra = 1; - } - } - if ($encrypt{'ltisec_crslinkprot'}) { - $showextra = 1; - } - unless (grep(/^\Q$hostid\E$/,@ids)) { - $switch = 1; - } - if ($showextra) { - $numshown ++; - $divsty = 'display:inline-block'; - } else { - $divsty = 'display:none'; - } - $extra .= '
'. - ''.$hostid.''; - if ($switch) { - my $switchserver = ''.&mt('Switch Server').''; - if (exists($privkeys{$hostid})) { - $extra .= '
'. - ''. - &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'
'. - ''.&mt('Change?'). - ''. - (' 'x2). - '  '; - } else { - $extra .= ''. - &mt('Key required').' - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver). - ''."\n"; - } - } elsif (exists($privkeys{$hostid})) { - $extra .= '
'. - &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'
'. - ''.&mt('Change?'). - ''. - (' 'x2). - '  '; - } else { - $extra .= ''.&mt('Encryption Key').':'. - ''. - ''; - } - $extra .= '
'; - } - 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 .= ''. - ''; - $itemcount ++; - $$rowtotal += $itemcount; + $datatable = &secrets_form($dom,'ltisec',\%encrypt,\%privkeys,$rowtotal); } elsif ($position eq 'middle') { - $datatable = &password_rules('secrets',\$itemcount,\%rules); + $datatable = &password_rules('ltisecrets',\$itemcount,\%rules); + $$rowtotal += $itemcount; } elsif ($position eq 'lower') { - $datatable .= ''; + $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') { @@ -6618,10 +6253,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,$lifetime,$consumer,$requser,$crsinc,$current); + my ($key,$secret,$usable,$lifetime,$consumer,$requser,$crsinc,$current); if (ref($settings->{$item}) eq 'HASH') { $key = $settings->{$item}->{'key'}; - $secret = $settings->{$item}->{'secret'}; + $usable = $settings->{$item}->{'usable'}; $lifetime = $settings->{$item}->{'lifetime'}; $consumer = $settings->{$item}->{'consumer'}; $requser = $settings->{$item}->{'requser'}; @@ -6669,8 +6304,56 @@ sub print_lti { ' '. (' 'x2). ''.$lt{'lifetime'}.':'. - (' 'x2). + 'value="'.$lifetime.'" size="3" />

'; + if ($key ne '') { + $datatable .= ''.$lt{'key'}; + if ($switchserver) { + $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']'; + } else { + $datatable .= ':'; + } + $datatable .= ' '.(' 'x2); + } elsif (!$switchserver) { + $datatable .= ''.$lt{'key'}.':'. + ''. + ' '.(' 'x2); + } + if ($switchserver) { + if ($usable ne '') { + $datatable .= '
'. + $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'
'. + ''.&mt('Change secret?'). + ''. + (' 'x2). + ''.(' 'x2). + ''; + } elsif ($key eq '') { + $datatable .= ''.&mt('Key and Secret are required').' - '.$switchmessage.''."\n"; + } else { + $datatable .= ''.&mt('Secret required').' - '.$switchmessage.''."\n"; + } + } else { + if ($usable ne '') { + $datatable .= '
'. + $lt{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'
'. + ''.&mt('Change?'). + ''. + (' 'x2). + '  '; + } else { + $datatable .= + ''.$lt{'secret'}.':'. + ''. + ''; + } + } + $datatable .= '

'. ''.$lt{'requser'}.':'. ' '."\n". ''."\n". @@ -6679,12 +6362,6 @@ sub print_lti { ' '."\n". '
'."\n". (' 'x4). - ''.$lt{'key'}. - ': '. - (' 'x2). - ''.$lt{'secret'}.':'. - ''. - ''. ''. ''.<i_options($i,$current,$itemcount,%lt).''; $itemcount ++; @@ -6713,8 +6390,16 @@ sub print_lti { ''.$lt{'version'}.': '."\n". (' 'x2). - ''.$lt{'lifetime'}.': '."\n". - (' 'x2). + ''.$lt{'lifetime'}.':

'."\n"; + if ($switchserver) { + $datatable .= ''.&mt('Key and Secret are required').' - '.$switchmessage.''."\n"; + } else { + $datatable .= ''.$lt{'key'}.': '."\n". + (' 'x2). + ''.$lt{'secret'}.':'. + ' '."\n"; + } + $datatable .= '

'. ''.$lt{'requser'}.':'. ' '."\n". ''."\n". @@ -6722,11 +6407,6 @@ sub print_lti { ''.$lt{'crsinc'}.':'. ' '."\n". ''."\n". - (' 'x4). - ''.$lt{'key'}.': '."\n". - (' 'x2). - ''.$lt{'secret'}.':'. - ' '."\n". ''.<i_options('add',undef,$itemcount,%lt). ''."\n". ''."\n"; @@ -6799,7 +6479,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'}; @@ -6809,7 +6489,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'}; @@ -6837,7 +6517,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 { @@ -6854,7 +6534,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"'; @@ -6900,9 +6580,9 @@ 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','placement','lti'); my %coursetypetitles = &Apache::lonlocal::texthash ( @@ -6955,7 +6635,7 @@ sub lti_options { '
'.&mt('Roles which may create user accounts').''; foreach my $ltirole (@ltiroles) { $output .= '  '; + $checked{'makeuser'}{$ltirole}.' />'.$ltirole.'  '; } $output .= '
'. '
'.&mt('New user accounts created for LTI users').''. @@ -7087,9 +6767,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})) { @@ -7135,22 +6815,6 @@ sub ltimenu_titles { ); } -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=''.&mt('Switch Server').''; - } - } - return $switchserver; -} - sub print_coursedefaults { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles); @@ -7158,6 +6822,7 @@ sub print_coursedefaults { 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', 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)', @@ -7168,12 +6833,17 @@ 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)', ); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, + coursequota => 20, postsubmit => 60, mysqltables => 172800, + domexttool => 1, + exttool => 0, ); if ($position eq 'top') { %defaultchecked = ( @@ -7287,21 +6957,49 @@ sub print_coursedefaults { $itemcount ++; } else { $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; - my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql); + my ($currdefresponder,%defcredits,%curruploadquota,%currcoursequota, + %deftimeout,%currmysql); my $currusecredits = 0; my $postsubmitclient = 1; my $ltiauth = 0; + my %domexttool; + my %exttool; 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"'; + } + } + } $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'); @@ -7347,6 +7045,9 @@ sub print_coursedefaults { } else { foreach my $type (@types) { $deftimeout{$type} = $staticdefaults{'postsubmit'}; + if ($staticdefaults{'domexttool'}) { + $domexttool{$type} = ' checked="checked"'; + } } } if (!$currdefresponder) { @@ -7358,6 +7059,9 @@ sub print_coursedefaults { if ($curruploadquota{$type} eq '') { $curruploadquota{$type} = $staticdefaults{'uploadquota'}; } + if ($currcoursequota{$type} eq '') { + $currcoursequota{$type} = $staticdefaults{'coursequota'}; + } } $datatable .= '
'.$choices{'hostid'}.''.$choices{'samllanding'}.''.$choices{'samloptions'}.'
'.$domservers{$lonhost}.''. - ''. + '
'.&mt('SSO').''. - ''.&mt('Non-SSO').'
'. ''. - ''. - ''. - ''. - ''. - '
'.&mt('SSO').'
'.&mt('Text').''.&mt('Image').''.&mt('Alt Text').''.&mt('URL').''.&mt('Tool Tip').''.&mt('Text').'
'; if ($samlimg{$lonhost}) { $datatable .= '
'. @@ -1686,13 +1738,21 @@ sub print_login { $datatable .= ''; } $datatable .= '

'. + ''. + ''. + ''. + ''. + ''. - ''. - ''. + ''. '
'.&mt('SSO').''. + ''.&mt('Non-SSO').'
'.&mt('URL').''.&mt('Tool Tip').''.&mt('Pop-up if iframe').''.&mt('Text').'
'.(' 'x2).'
 
'. - ''.&mt('Portfolio').': '. + ''. ''.(' ' x 2). - ''.&mt('Authoring').': '. - '
'. - ''.&mt('Portfolio').': '. + ''. ''.(' ' x2). - ''.&mt('Authoring').': '. - '
'. @@ -4753,7 +4728,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}); } @@ -4765,7 +4740,6 @@ 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); @@ -4832,7 +4806,7 @@ sub overridden_helpdesk { } my $title; if (ref($short_titles) eq 'HASH') { - $title = $short_titles->{$item}; + $title = $short_titles->{$item}; } $output .= '
' - .''.(' 'x2). - ''. - '
'.&mt('Required settings').''. - ''.$lt{'title'}.': '. - (' 'x2). - ''.$lt{'version'}.': '. - (' 'x2). - ''.$lt{'msgtype'}.': '. - (' 'x2). - ''.$lt{'sigmethod'}.':'. - '

'. - ''.$lt{'url'}.':'. - (' 'x2). - ''.$lt{'key'}.':'. - ' '. - (' 'x2). - ''.$lt{'lifetime'}.':'. - ' '. - (' 'x2). - ''.$lt{'secret'}.':'. - ''. - ''. - ''. - '
'. - '
'.&mt('Optional settings').''. - ''.&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 .= ''.(' 'x2); - } - $datatable .= (' 'x4); - foreach my $dimen ('width','height') { - $datatable .= ''. - (' 'x2); - } - $datatable .= '
'. - '
'.$lt{'linktext'}.'
'. - '
'. - '
'.$lt{'explanation'}.'
'. - '

'; - 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 .= '
'.$lt{$extra}.' '. - ''.(' 'x2). - '
'. - '
'. - ''. - &mt("at least [_1] $units{$extra} after launch", - ''). - '
'; - } - $datatable .= ''.$lt{'icon'}.': '; - if ($imgsrc) { - $datatable .= $imgsrc. - ' '. - ' '.&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 .= ''; - } - $datatable .= '
'; - 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 .= '
'.&mt('User data sent on launch').''. - ''; - 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 = ''; - } + 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}; } - } else { - $spacer = (' ' x2); } - $datatable .= ''.$spacer; } - $datatable .= ''; - $datatable .= '
'. - ' : '. - '
'; - $datatable .= '
'. - '
'.&mt('Role mapping').''; - foreach my $role (@courseroles) { - my ($selected,$selectnone); - if (!$rolemaps{$role}) { - $selectnone = ' selected="selected"'; - } - $datatable .= ''; } - $datatable .= '
'. - &Apache::lonnet::plaintext($role,'Course').'
'. - '
'; - my %courseconfig; - if (ref($settings->{$item}) eq 'HASH') { - if (ref($settings->{$item}->{'crsconf'}) eq 'HASH') { - %courseconfig = %{$settings->{$item}->{'crsconf'}}; - } - } - $datatable .= '
'.&mt('Configurable in course').''; - foreach my $item ('label','title','target','linktext','explanation','append') { - my $checked; - if ($courseconfig{$item}) { - $checked = ' checked="checked"'; + } elsif ($position eq 'middle') { + if (exists($settings->{'rules'})) { + if (ref($settings->{'rules'}) eq 'HASH') { + %rules = %{$settings->{'rules'}}; } - $datatable .= '  '."\n"; } - $datatable .= '
'. - '
'.&mt('Custom items sent on launch').''. - ''; - if (ref($settings->{$item}->{'custom'}) eq 'HASH') { - my %custom = %{$settings->{$item}->{'custom'}}; - if (keys(%custom) > 0) { - foreach my $key (sort(keys(%custom))) { - $datatable .= ''. - ''; - } + } else { + foreach my $key ('encrypt','private','rules') { + if (exists($settings->{$key})) { + delete($settings->{$key}); } } - $datatable .= ''; - $datatable .= '
'.&mt('Action').''.&mt('Name').''.&mt('Value').'
'. - ''.$key.'
'. - ''. - '
'."\n". - ''."\n". - ' '."\n". - ''.&mt('Add').''. - '
'.&mt('Required settings').''. - ''.$lt{'title'}.': '."\n". - (' 'x2). - ''.$lt{'version'}.': '."\n". - (' 'x2). - ''.$lt{'msgtype'}.': '. - ''.$lt{'sigmethod'}.':'. - '
'. - ''.$lt{'url'}.': '."\n". - (' 'x2). - ''.$lt{'key'}.': '."\n". - (' 'x2). - ''.$lt{'lifetime'}.': '."\n". - (' 'x2). - ''.$lt{'secret'}.':'. - ' '."\n". - '
'. - '
'.&mt('Optional settings').''. - ''.&mt('Display target:'); - my %defaultdisp; - $defaultdisp{'iframe'} = ' checked="checked"'; - foreach my $disp ('iframe','tab','window') { - $datatable .= ''.(' 'x2); - } - $datatable .= (' 'x4); - foreach my $dimen ('width','height') { - $datatable .= ''. - (' 'x2); - } - $datatable .= '
'. - '
'.$lt{'linktext'}.'
'. - '
'. - '
'.$lt{'explanation'}.'
'. - ''. - '

'; - 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 .= '
'.$lt{$extra}.' '. - ''.(' 'x2).''. - '
'. - '
'; - } - $datatable .= ''.$lt{'icon'}.': '. - '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; - if ($switchserver) { - $datatable .= &mt('Upload to library server: [_1]',$switchserver); - } else { - $datatable .= ''; - } - $datatable .= '
'. - '
'.&mt('User data sent on launch').''. - ''; - 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 .= ''.$spacer; - } - $datatable .= ''. - '
'; - $datatable .= '
'.&mt('Role mapping').''; - foreach my $role (@courseroles) { - my ($checked,$checkednone); - $datatable .= ''; - } - $datatable .= '
'. - &Apache::lonnet::plaintext($role,'Course').'
'. - '
'. - '
'.&mt('Configurable in course').''; - foreach my $item ('label','title','target','linktext','explanation','append') { - $datatable .= ''.(' ' x2)."\n"; - } - $datatable .= '
'. - '
'.&mt('Custom items sent on launch').''. - ''. - ''. - '
'.&mt('Action').''.&mt('Name').''.&mt('Value').'
'. - ''. - '
'."\n". - '
'.&mt('Encryption Key(s)').'
'. + ''.&mt('Not in use').'
'. + $extra. + '
'.&mt('Encryption Key(s)').'
'. - ''.&mt('Not in use').'
'. - $extra. - '
Not set yetTo be done
'. @@ -7381,6 +7085,19 @@ sub print_coursedefaults { } $datatable .= '
'."\n"; $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $choices{'coursequota'}. + ''. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''; + } + $datatable .= '
'.&mt($type).'
'. + '
'."\n"; + $itemcount ++; my $onclick = "toggleDisplay(this.form,'credits');"; my $display = 'none'; if ($currusecredits) { @@ -7450,12 +7167,248 @@ sub print_coursedefaults { &radiobutton_prefs($current,\@toggles,\%defaultchecked, \%choices,$itemcount,undef,undef,'left'); $datatable .= $table; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $choices{'domexttool'}. + ''. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''."\n"; + } + $datatable .= '
'. + ''. + ''. + &mt($type).'
'."\n"; + $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $choices{'exttool'}. + ''. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''."\n"; + } + $datatable .= '
'. + ''. + ''. + &mt($type).'
'."\n"; + } + $$rowtotal += $itemcount; + return $datatable; +} + +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', + 'domcoordacc' => 'on', + ); + my @toggles = ('nocodemirror','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 .= ''. + ''.$titles{'copyright'}. + ''. + &selectbox('copyright',$currrights{'copyright'},'', + \&Apache::loncommon::copyrightdescription, + (grep !/^priv|custom$/,(&Apache::loncommon::copyrightids))). + ''."\n"; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.$titles{'sourceavail'}. + ''. + &selectbox('sourceavail',$currrights{'sourceavail'},'', + \&Apache::loncommon::source_copyrightdescription, + (&Apache::loncommon::source_copyrightids)). + ''."\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 = ''."\n". + ''.$titles{'editors'}.''."\n". + ''."\n". + ''; + foreach my $editor (@editors) { + my $checked; + if (grep(/^\Q$editor\E$/,@{$curreditors})) { + $checked = ' checked="checked"'; + } + $datatable .= ' '; + } + $datatable .= ''."\n".''."\n".''."\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 .= ''. + ''.$titles{'webdav_LC_adv'}.'
'. + $titles{'webdav_LC_adv_over'}. + ''. + ''; + 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 .= '  '; + } + $datatable .= ''; $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', + 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', + ); +} + +sub selectbox { + my ($name,$value,$readonly,$functionref,@idlist)=@_; + my $selout = ''; + return $selout; +} + sub print_selfenrollment { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable); @@ -7685,13 +7638,15 @@ 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') { + if ($position eq 'top') { + $numinrow = 2; + } else { @items = ('domain','author','course','community'); %names = &Apache::lonlocal::texthash ( domain => 'Assigned domain role(s)', author => 'Assigned co-author role(s)', course => 'Assigned course role(s)', - community => 'Assigned community role', + community => 'Assigned community role(s)', ); $numinrow = 4; ($othertitle,$usertypes,$types) = @@ -7710,6 +7665,7 @@ sub print_privacy { auto => 'Unrestricted', instdom => 'Other domain shares institution/provider', extdom => 'Other domain has different institution/provider', + notify => 'Notify when role needs authorization', ); my %names = &Apache::lonlocal::texthash ( domain => 'Domain role', @@ -7761,6 +7717,28 @@ sub print_privacy { $datatable .= ''; $itemcount ++; } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{'notify'}.''. + ''; + if ((@instdoms > 1) || (keys(%by_location) > 0)) { + my %curr; + if (ref($settings) eq 'HASH') { + if ($settings->{'notify'} ne '') { + map {$curr{$_}=1;} split(/,/,$settings->{'notify'}); + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my ($numdc,$table,$rows) = &active_dc_picker($dom,$numinrow,'checkbox', + 'privacy_notify',%curr); + if ($numdc > 0) { + $datatable .= $table; + } else { + $datatable .= &mt('There are no active Domain Coordinators'); + } + } else { + $datatable .= &mt('Nothing to set here, as there are no other domains'); + } + $datatable .=''; } elsif ($position eq 'middle') { if ((@instdoms > 1) || (keys(%by_location) > 0)) { if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) { @@ -8174,7 +8152,7 @@ sub password_rules { max => 'Maximum password length', chars => 'Required characters', ); - } elsif ($prefix eq 'secrets') { + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { %titles = &Apache::lonlocal::texthash ( min => 'Minimum secret length', max => 'Maximum secret length', @@ -8197,7 +8175,7 @@ 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}; } @@ -8340,7 +8318,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') { @@ -8405,10 +8383,10 @@ sub print_wafproxy { (' 'x2).''. &mt('Alias used for SSO Auth').':   '. - ''; + ''; } $aliasrows .= ''; $aliasinfo{$dom_in_effect} .= $aliasrows; @@ -9318,7 +9296,7 @@ sub print_loadbalancing { no => ' checked="checked"', ); my %balcookiechecked = ( - no => ' checked="checked"', + no => ' checked="checked"', ); foreach my $sparetype (@sparestypes) { my $targettable; @@ -9673,8 +9651,9 @@ 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', @@ -9904,7 +9883,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, @@ -10041,7 +10020,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, @@ -10068,8 +10047,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, @@ -10171,7 +10150,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') { @@ -10194,10 +10173,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.'"'; @@ -10212,10 +10191,10 @@ sub noninst_users { $description = &mt('Requests for: [_1] (status self-reported)',$typetitle); } $output = ''. - "$description\n". + "$description\n". ''. ''; - my %headers = &Apache::lonlocal::texthash( + my %headers = &Apache::lonlocal::texthash( approve => 'Processing', email => 'E-mail', username => 'Username', @@ -10340,7 +10319,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 '') { @@ -10456,7 +10435,7 @@ sub user_formats_row { 'username' => 'new usernames', 'id' => 'IDs', ); - unless ($type eq 'email') { + unless (($type eq 'email') || ($type eq 'unamemap')) { my $css_class = $rowcount%2?' class="LC_odd_row"':''; $output = ''. ''; } - $output .= '
'. @@ -10511,9 +10490,9 @@ sub user_formats_row { } elsif ($colsleft == 1) { $output .= ' 
'; - unless ($type eq 'email') { - $output .= ''; + $output .= ''; + unless (($type eq 'email') || ($type eq 'unamemap')) { + $output .= ''; } return $output; } @@ -10645,18 +10624,34 @@ sub print_defaults { } elsif ($item eq 'lang_def') { my $includeempty = 1; $datatable .= &Apache::loncommon::select_language($item,$defaults{$item},$includeempty); - } else { - my $size; - if ($item eq 'portal_def') { - $size = ' size="25"'; - } + } elsif ($item eq 'portal_def') { $datatable .= ''; + $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 .= '
'. + ''.$titles->{$field}.' '. + ''. + (' 'x2). + ''. + '
'; + } + } else { + $datatable .= ''; } $datatable .= ''; $rownum ++; } - } else { + } elsif ($position eq 'middle') { my %defaults; if (ref($settings) eq 'HASH') { if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) { @@ -10706,6 +10701,22 @@ sub print_defaults { $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 .= ''.&mt('Available conversions').''. + &user_formats_row('unamemap',$settings,$unamemaprules, + $ruleorder,$numinrow). + '
'; + } + if ($datatable eq '') { + $datatable .= ''. + &mt('No rules set for domain in customized localenroll.pm'). + ''; + } } $$rowtotal += $rownum; return $datatable; @@ -10731,6 +10742,8 @@ sub defaults_titles { 'timezone_def' => 'Default timezone', 'datelocale_def' => 'Default locale for dates', 'portal_def' => 'Portal/Default URL', + 'email' => 'Email links use portal URL', + 'web' => 'Public web links use portal URL', 'intauth_cost' => 'Encryption cost for bcrypt (positive integer)', 'intauth_check' => 'Check bcrypt cost if authenticated', 'intauth_switch' => 'Existing crypt-based switched to bcrypt on authentication', @@ -10963,10 +10976,13 @@ 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) = - &publishlogo($r,'copy',$legacyfile,$dom,$confname,'scantron', - '','',$newfile); - if ($result ne 'ok') { + &Apache::lonconfigsettings::publishlogo($r,'copy',$legacyfile,$dom,$confname, + 'scantron','','',$newfile,$modified); + if ($result eq 'ok') { + &update_modify_urls($r,$modified); + } else { $error = &mt("An error occurred publishing the [_1] bubblesheet format file in RES space. Error was: [_2].",$newfile,$result); } } @@ -11445,6 +11461,23 @@ sub serverstatus_pages { 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 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 '') { @@ -11498,6 +11531,17 @@ $jstext return; } +$portal_js + +// ]]> + + +ENDSCRIPT + } else { +return <<"ENDSCRIPT"; + @@ -11518,7 +11562,7 @@ sub passwords_javascript { passexp => 'Warning: days before password expiration must be a positive integer (or blank).', passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).', ); - } elsif ($prefix eq 'secrets') { + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { %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).', @@ -11526,7 +11570,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) { @@ -11557,7 +11601,7 @@ function warnInt$prefix(field) { field.value.replace(/^\s+/,''); field.value.replace(/\s+\$/,''); var regexdigit=/^\\d+\$/; - if (field.name == '$prefix\_min') { + if (field.name == '${prefix}_min') { if (field.value == '') { alert('$intalert{passmin}'); field.value = '$defmin'; @@ -11577,7 +11621,7 @@ function warnInt$prefix(field) { field.value = ''; } if (field.value != '') { - if (field.name == '$prefix\_expire') { + if (field.name == '${prefix}_expire') { var regexpposnum=/^\\d+(|\\.\\d*)\$/; if (!regexpposnum.test(field.value)) { alert('$intalert{passexp}'); @@ -11591,10 +11635,10 @@ function warnInt$prefix(field) { } } else { if (!regexdigit.test(field.value)) { - if (field.name == '$prefix\_max') { + if (field.name == '${prefix}_max') { alert('$intalert{passmax}'); } else { - if (field.name == '$prefix\_numsaved') { + if (field.name == '${prefix}_numsaved') { alert('$intalert{passnum}'); } } @@ -11920,7 +11964,6 @@ sub modifiable_userdata_row { ''; my $rem; my %checks; - my %current; if (ref($settings) eq 'HASH') { my $hashref; if ($context eq 'lti') { @@ -11946,7 +11989,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') { @@ -11958,7 +12001,6 @@ sub modifiable_userdata_row { } } } - my $total = scalar(@fields); for (my $i=0; $i<$total; $i++) { $rem = $i%($numinrow); @@ -12047,15 +12089,19 @@ 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", + 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)', ); - my $showdom; + my ($showdom,$defaultquota); if ($context eq 'cansearch') { $showdom = ' ('.$dom.')'; + } elsif ($context eq 'authorquota') { + $defaultquota = 500; } my $class = 'LC_left_item'; if ($context eq 'statustocreate') { @@ -12092,25 +12138,44 @@ sub insttypes_row { } $output .= ''; } - 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') { + if ($context eq 'authorquota') { + my $currquota; + if ($settings->{$context}->{$types->[$i]} =~ /^\d+$/) { + $currquota = $settings->{$context}->{$types->[$i]}; + } else { + $currquota = $defaultquota; + } + $output .= ''; + } 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') { $check = ' checked="checked" '; } - } elsif ($context eq 'statustocreate') { - $check = ' checked="checked" '; } + $output .= ''; } - $output .= ''; } } $rem = @{$types}%($numinrow); @@ -12122,7 +12187,7 @@ sub insttypes_row { } else { $output .= ''; @@ -12132,20 +12197,41 @@ sub insttypes_row { } else { $output .= '
'."\n". + ''. + ''. - ''; } - $output .= ' '; + $output .= ' '; } else { if ($rem == 0) { $output .= '
'; } - my $defcheck = ' '; - if (ref($settings) eq 'HASH') { - if (ref($settings->{$context}) eq 'ARRAY') { - if (grep(/^default$/,@{$settings->{$context}})) { + 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 .= ''; + } 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') { $defcheck = ' checked="checked" '; } - } elsif ($context eq 'statustocreate') { - $defcheck = ' checked="checked" '; } + $output .= ''; } - $output .= ''; } $output .= '
'; return $output; @@ -12225,7 +12311,7 @@ sub modify_login { my ($r,$dom,$confname,$lastactref,%domconfig) = @_; my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl, %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon, - %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso); + %currsaml,%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso); %title = ( coursecatalog => 'Display course catalog', adminmail => 'Display administrator E-mail address', helpdesk => 'Display "Contact Helpdesk" link', @@ -12249,6 +12335,7 @@ sub modify_login { $samlalt{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'alt'}; $samlimg{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'img'}; $samltitle{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'title'}; + $samlwindow{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'window'}; $samlnotsso{$lonhost} = $domconfig{login}{'saml'}{$lonhost}{'notsso'}; } } @@ -12391,13 +12478,16 @@ 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}) = &publishlogo($r,'upload',$formelem,$dom,$confname, - "help/$lang",'','',$newfile{$lang}); + (my $result,$newurl{$lang}) = + &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname, + "help/$lang",'','',$newfile{$lang}, + $modified); if ($result eq 'ok') { $loginhash{'login'}{'helpurl'}{$lang} = $newurl{$lang}; $changes{'helpurl'}{$lang} = 1; @@ -12410,6 +12500,7 @@ 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); } @@ -12467,11 +12558,14 @@ 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}) = &publishlogo($r,'upload',$formelem,$dom,$confname, - "login/headtag/$lonhost",'','', - $env{'form.loginheadtag_'.$lonhost.'.filename'}); + (my $result,$newheadtagurls{$lonhost}) = + &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname, + "login/headtag/$lonhost",'','', + $env{'form.loginheadtag_'.$lonhost.'.filename'}, + $modified); if ($result eq 'ok') { $loginhash{'login'}{'headtag'}{$lonhost}{'url'} = $newheadtagurls{$lonhost}; $changes{'headtag'}{$lonhost} = 1; @@ -12488,6 +12582,7 @@ 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); } @@ -12506,10 +12601,13 @@ sub modify_login { if ($env{'form.saml_img_'.$lonhost.'.filename'}) { push(@newsamlimgs,$lonhost); } - foreach my $item ('text','alt','url','title','notsso') { + foreach my $item ('text','alt','url','title','window','notsso') { $env{'form.saml_'.$item.'_'.$lonhost} =~ s/^\s+|\s+$//g; } if ($saml{$lonhost}) { + if ($env{'form.saml_window_'.$lonhost} ne '1') { + $env{'form.saml_window_'.$lonhost} = ''; + } if (grep(/^\Q$lonhost\E$/,@delsamlimg)) { #FIXME Need to obsolete published image delete($currsaml{$lonhost}{'img'}); @@ -12527,25 +12625,28 @@ sub modify_login { if ($env{'form.saml_title_'.$lonhost} ne $samltitle{$lonhost}) { $changes{'saml'}{$lonhost} = 1; } + if ($env{'form.saml_window_'.$lonhost} ne $samlwindow{$lonhost}) { + $changes{'saml'}{$lonhost} = 1; + } if ($env{'form.saml_notsso_'.$lonhost} ne $samlnotsso{$lonhost}) { $changes{'saml'}{$lonhost} = 1; } } else { $changes{'saml'}{$lonhost} = 1; } - foreach my $item ('text','alt','url','title','notsso') { + foreach my $item ('text','alt','url','title','window','notsso') { $currsaml{$lonhost}{$item} = $env{'form.saml_'.$item.'_'.$lonhost}; } } else { - if ($saml{$lonhost}) { + if ($saml{$lonhost}) { $changes{'saml'}{$lonhost} = 1; delete($currsaml{$lonhost}); } } } foreach my $posshost (keys(%currsaml)) { - unless (exists($domservers{$posshost})) { - delete($currsaml{$posshost}); + unless (exists($domservers{$posshost})) { + delete($currsaml{$posshost}); } } %{$loginhash{'login'}{'saml'}} = %currsaml; @@ -12556,11 +12657,14 @@ 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) = &publishlogo($r,'upload',$formelem,$dom,$confname, - "login/saml/$lonhost",'','', - $env{'form.saml_img_'.$lonhost.'.filename'}); + my ($result,$imgurl) = + &Apache::lonconfigsettings::publishlogo($r,'upload',$formelem,$dom,$confname, + "login/saml/$lonhost",'','', + $env{'form.saml_img_'.$lonhost.'.filename'}, + $modified); if ($result eq 'ok') { $currsaml{$lonhost}{'img'} = $imgurl; $loginhash{'login'}{'saml'}{$lonhost}{'img'} = $imgurl; @@ -12571,6 +12675,7 @@ sub modify_login { $errors .= '
  • '.$puberror.'
  • '; } } + &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); } @@ -12734,19 +12839,22 @@ sub modify_login { alt => 'Alt text for button image', url => 'SSO URL', title => 'Tooltip for SSO link', + window => 'Pop-up window if iframe', notsso => 'Text for non-SSO log-in', ); foreach my $lonhost (sort(keys(%{$changes{$item}}))) { if (ref($currsaml{$lonhost}) eq 'HASH') { $resulttext .= '
  • '.&mt("$title{$item} in use for [_1]","$lonhost"). '
      '; - foreach my $key ('text','img','alt','url','title','notsso') { + foreach my $key ('text','img','alt','url','title','window','notsso') { if ($currsaml{$lonhost}{$key} eq '') { $resulttext .= '
    • '.&mt("$notlt{$key} not in use").'
    • '; } else { my $value = "'$currsaml{$lonhost}{$key}'"; if ($key eq 'img') { $value = ''; + } elsif ($key eq 'window') { + $value = 'On'; } $resulttext .= '
    • '.&mt("$notlt{$key} set to: [_1]", $value).'
    • '; @@ -12922,7 +13030,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); @@ -12955,9 +13063,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'; @@ -12989,7 +13097,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') { @@ -13069,7 +13177,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 .= '
    • '.&mt('Courses/Communities allowed').':
      • '. @@ -13137,6 +13245,235 @@ 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', + 'domcoordacc' => 'on', + 'editors' => ['edit','xml']. + 'authorquota' => 500, + 'webdav' => 0, + ); + my %titles = &authordefaults_titles(); + foreach my $item ('nocodemirror','domcoordacc') { + 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','domcoordacc','copyright','sourceavail') { + 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','domcoordacc') { + if ($offon[$confhash{$item}] ne $staticdefaults{$item}) { + $changes{$item} = 1; + } + } + foreach my $item ('copyright','sourceavail') { + if ($confhash{$item} ne $staticdefaults{$item}) { + $changes{$item} = 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)) { + if ((exists($changes{'authorquota'})) || (exists($changes{'webdav'})) || + ($changes{'webdav_LC_adv'})) { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + 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'}; + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } + $resulttext = &mt('Changes made:').'
          '; + my $authoroverride; + foreach my $key ('nocodemirror','domcoordacc','copyright','sourceavail') { + if (exists($changes{$key})) { + my $shown; + unless ($authoroverride) { + $resulttext .= '
        • '.&mt('Defaults which can be overridden by Author').'
            '; + $authoroverride = 1; + } + if (($key eq 'nocodemirror') || ($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 .= '
          • '.&mt('[_1] set to: [_2]',$titles{$key},$shown).'
          • '; + } + } + if ($authoroverride) { + $resulttext .= '
        • '; + } + my $domcoordoverride; + foreach my $key ('editors','authorquota','webdav','webdav_LC_adv') { + if (exists($changes{$key})) { + my $shown; + unless ($domcoordoverride) { + $resulttext .= '
        • '.&mt('Defaults which can be overridden by a Domain Coodinator').'
            '; + $domcoordoverride = 1; + } + if ($key eq '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'}; + } + } + $resulttext .= '
          • '.&mt('[_1] set to: [_2]',$titles{$key},$shown).'
          • '; + } + } + if ($domcoordoverride) { + $resulttext .= '
        • '; + } + } 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); @@ -13272,12 +13609,15 @@ 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) = - &publishlogo($r,'upload',$role.'_'.$img, - $dom,$confname,$img,$width,$height); + &Apache::lonconfigsettings::publishlogo($r,'upload',$role.'_'.$img, + $dom,$confname,$img,$width,$height, + '',$modified); 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); } @@ -13299,12 +13639,15 @@ sub modify_colors { # is confname an author? if ($switchserver eq '') { if ($author_ok eq 'ok') { + my $modified = []; my ($result,$logourl) = - &publishlogo($r,'copy',$domconfig->{$role}{$img}, - $dom,$confname,$img,$width,$height); + &Apache::lonconfigsettings::publishlogo($r,'copy',$domconfig->{$role}{$img}, + $dom,$confname,$img,$width,$height, + '',$modified); if ($result eq 'ok') { $confhash->{$role}{$img} = $logourl; $changes{$role}{'images'}{$img} = 1; + &update_modify_urls($r,$modified); } } } @@ -13598,229 +13941,16 @@ sub check_authorstatus { return $author_ok; } -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},'<>&"') - .''; - } - } - $output = 'ok'; - print $logfile "\nWrote metadata"; - close($mfh); - } else { - print $logfile "\nFailed to open metadata file"; - $output = &mt('Could not write metadata'); +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; } } - return $output; } sub notifysubscribed { @@ -13871,15 +14001,21 @@ sub subscribed_hosts { sub check_switchserver { my ($dom,$confname) = @_; - my ($allowed,$switchserver); - my $home = &Apache::lonnet::homeserver($confname,$dom); - if ($home eq 'no_host') { + my ($allowed,$switchserver,$home); + if ($confname eq '') { $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=''.&mt('Switch Server').''; + $switchserver=''.&mt('Switch Server').''; } return $switchserver; } @@ -13891,7 +14027,7 @@ sub modify_quotas { $author_ok,$switchserver,$errors,$validationitemsref,$validationnamesref, $validationfieldsref); if ($action eq 'quotas') { - $context = 'tools'; + $context = 'tools'; } else { $context = $action; } @@ -13911,7 +14047,7 @@ sub modify_quotas { @usertools = ('author'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','webdav','portfolio'); + @usertools = ('aboutme','blog','portfolio','portaccess','timezone'); %titles = &tool_titles(); } my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); @@ -13934,8 +14070,6 @@ 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); } @@ -14229,7 +14363,6 @@ 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') { @@ -14318,15 +14451,10 @@ sub modify_quotas { } } if (ref($domconfig{'quotas'}{'authorquota'}) eq 'HASH') { - 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}; - } - } + $confhash{'authorquota'} = $domconfig{'quotas'}{'authorquota'}; + } + if (ref($domconfig{'quotas'}{'webdav'}) eq 'HASH') { + $confhash{'webdav'} = $domconfig{'quotas'}{'webdav'}; } } if (ref($confhash{'defaultquota'}) eq 'HASH') { @@ -14346,21 +14474,6 @@ 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') { @@ -14401,19 +14514,6 @@ sub modify_quotas { } $resulttext .= '
      • '; } - if (ref($changes{'authorquota'}) eq 'HASH') { - $resulttext .= '
      • '.&mt('Authoring Space default quotas').'
          '; - foreach my $type (@{$types},'default') { - if (defined($changes{'authorquota'}{$type})) { - my $typetitle = $usertypes->{$type}; - if ($type eq 'default') { - $typetitle = $othertitle; - } - $resulttext .= '
        • '.&mt('[_1] set to [_2] MB',$typetitle,$confhash{'authorquota'}{$type}).'
        • '; - } - } - $resulttext .= '
      • '; - } } my %newenv; foreach my $item (@usertools) { @@ -14602,11 +14702,14 @@ 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) = - &publishlogo($r,'upload',$caller,$dom,$confname, - "$type/$cdom/$cnum/cover",$width,$height); + &Apache::lonconfigsettings::publishlogo($r,'upload',$caller,$dom,$confname, + "$type/$cdom/$cnum/cover",$width,$height, + '',$modified); 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); } @@ -14621,656 +14724,361 @@ sub process_textbook_image { sub modify_ltitools { my ($r,$dom,$action,$lastactref,%domconfig) = @_; - my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); - my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my (%currtoolsec,%secchanges,%newtoolsec,%newkeyset); + &fetch_secrets($dom,'toolsec',\%domconfig,\%currtoolsec,\%secchanges,\%newtoolsec,\%newkeyset); + my $confname = $dom.'-domainconfig'; my $servadm = $r->dir_config('lonAdmEMail'); my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); - my (%posslti,%possfield); - my @courseroles = ('cc','in','ta','ep','st'); - my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); - map { $posslti{$_} = 1; } @ltiroles; - my @allfields = ('fullname','firstname','lastname','email','user','roles'); - map { $possfield{$_} = 1; } @allfields; - my %lt = <itools_names(); - if ($env{'form.ltitools_add'}) { - my $title = $env{'form.ltitools_add_title'}; - $title =~ s/(`)/'/g; - ($newid,my $error) = &get_ltitools_id($dom,$title); - if ($newid) { - my $position = $env{'form.ltitools_add_pos'}; - $position =~ s/\D+//g; - if ($position ne '') { - $allpos[$position] = $newid; - } - $changes{$newid} = 1; - foreach my $item ('title','url','key','secret','lifetime') { - $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g; - if ($item eq 'lifetime') { - $env{'form.ltitools_add_'.$item} =~ s/[^\d.]//g; - } - if ($env{'form.ltitools_add_'.$item}) { - if (($item eq 'key') || ($item eq 'secret')) { - $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item}; - } else { - $confhash{$newid}{$item} = $env{'form.ltitools_add_'.$item}; - } - } - } - if ($env{'form.ltitools_add_version'} eq 'LTI-1p0') { - $confhash{$newid}{'version'} = $env{'form.ltitools_add_version'}; - } - if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') { - $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'}; - } - if ($env{'form.ltitools_add_sigmethod'} eq 'HMAC-SHA256') { - $confhash{$newid}{'sigmethod'} = $env{'form.ltitools_add_sigmethod'}; - } else { - $confhash{$newid}{'sigmethod'} = 'HMAC-SHA1'; - } - foreach my $item ('width','height','linktext','explanation') { - $env{'form.ltitools_add_'.$item} =~ s/^\s+//; - $env{'form.ltitools_add_'.$item} =~ s/\s+$//; - if (($item eq 'width') || ($item eq 'height')) { - if ($env{'form.ltitools_add_'.$item} =~ /^\d+$/) { - $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; - } - } else { - if ($env{'form.ltitools_add_'.$item} ne '') { - $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; - } - } - } - if ($env{'form.ltitools_add_target'} eq 'window') { - $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'}; - } elsif ($env{'form.ltitools_add_target'} eq 'tab') { - $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'}; - } else { - $confhash{$newid}{'display'}{'target'} = 'iframe'; - } - foreach my $item ('passback','roster') { - if ($env{'form.ltitools_'.$item.'_add'}) { - $confhash{$newid}{$item} = 1; - if ($env{'form.ltitools_'.$item.'valid_add'} ne '') { - my $lifetime = $env{'form.ltitools_'.$item.'valid_add'}; - $lifetime =~ s/^\s+|\s+$//g; - if ($lifetime =~ /^\d+\.?\d*$/) { - $confhash{$newid}{$item.'valid'} = $lifetime; - } - } - } - } - if ($env{'form.ltitools_add_image.filename'} ne '') { - my ($imageurl,$error) = - &process_ltitools_image($r,$dom,$confname,'ltitools_add_image',$newid, - $configuserok,$switchserver,$author_ok); - if ($imageurl) { - $confhash{$newid}{'image'} = $imageurl; - } - if ($error) { - &Apache::lonnet::logthis($error); - $errors .= '
      • '.$error.'
      • '; - } - } - 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; - } - } + + 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}; } - } 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 .= '
      • '.$error.'
      • '; } - } - if (ref($domconfig{$action}) eq 'HASH') { - my %deletions; - my @todelete = &Apache::loncommon::get_env_multiple('form.ltitools_del'); - if (@todelete) { - map { $deletions{$_} = 1; } @todelete; + $ltitoolsoutput = &Apache::courseprefs::store_ltitools($dom,'','domain',\%ltitoolschg,$domconfig{'ltitools'}); + if (keys(%ltitoolschg)) { + %newltitools = %ltitoolschg; } - my %customadds; - my @newcustom = &Apache::loncommon::get_env_multiple('form.ltitools_customadd'); - if (@newcustom) { - map { $customadds{$_} = 1; } @newcustom; - } - my %imgdeletions; - my @todeleteimages = &Apache::loncommon::get_env_multiple('form.ltitools_image_del'); - if (@todeleteimages) { - map { $imgdeletions{$_} = 1; } @todeleteimages; - } - my $maxnum = $env{'form.ltitools_maxnum'}; - for (my $i=0; $i<=$maxnum; $i++) { - my $itemid = $env{'form.ltitools_id_'.$i}; - $itemid =~ s/\D+//g; - if (ref($domconfig{$action}{$itemid}) eq 'HASH') { - if ($deletions{$itemid}) { - if ($domconfig{$action}{$itemid}{'image'}) { - #FIXME need to obsolete item in RES space - } - $changes{$itemid} = $domconfig{$action}{$itemid}{'title'}; - next; - } else { - my $newpos = $env{'form.ltitools_'.$itemid}; - $newpos =~ s/\D+//g; - foreach my $item ('title','url','lifetime') { - $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; - if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { - $changes{$itemid} = 1; - } - } - foreach my $item ('key','secret') { - $encconfig{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; - if ($domconfig{$action}{$itemid}{$item} ne $encconfig{$itemid}{$item}) { - $changes{$itemid} = 1; - } - } - if ($env{'form.ltitools_version_'.$i} eq 'LTI-1p0') { - $confhash{$itemid}{'version'} = $env{'form.ltitools_version_'.$i}; - } - if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') { - $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i}; - } - if ($env{'form.ltitools_sigmethod_'.$i} eq 'HMAC-SHA256') { - $confhash{$itemid}{'sigmethod'} = $env{'form.ltitools_sigmethod_'.$i}; - } else { - $confhash{$itemid}{'sigmethod'} = 'HMAC-SHA1'; - } - if ($domconfig{$action}{$itemid}{'sigmethod'} eq '') { - if ($confhash{$itemid}{'sigmethod'} ne 'HMAC-SHA1') { - $changes{$itemid} = 1; - } - } elsif ($domconfig{$action}{$itemid}{'sigmethod'} ne $confhash{$itemid}{'sigmethod'}) { - $changes{$itemid} = 1; - } - foreach my $size ('width','height') { - $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//; - $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//; - if ($env{'form.ltitools_'.$size.'_'.$i} =~ /^\d+$/) { - $confhash{$itemid}{'display'}{$size} = $env{'form.ltitools_'.$size.'_'.$i}; - if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'display'}{$size} ne $confhash{$itemid}{'display'}{$size}) { - $changes{$itemid} = 1; - } - } else { - $changes{$itemid} = 1; - } - } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'display'}{$size} ne '') { - $changes{$itemid} = 1; - } - } - } - foreach my $item ('linktext','explanation') { - $env{'form.ltitools_'.$item.'_'.$i} =~ s/^\s+//; - $env{'form.ltitools_'.$item.'_'.$i} =~ s/\s+$//; - if ($env{'form.ltitools_'.$item.'_'.$i} ne '') { - $confhash{$itemid}{'display'}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; - if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'display'}{$item} ne $confhash{$itemid}{'display'}{$item}) { - $changes{$itemid} = 1; - } - } else { - $changes{$itemid} = 1; - } - } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'display'}{$item} ne '') { - $changes{$itemid} = 1; - } - } - } - if ($env{'form.ltitools_target_'.$i} eq 'window') { - $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i}; - } elsif ($env{'form.ltitools_target_'.$i} eq 'tab') { - $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i}; - } else { - $confhash{$itemid}{'display'}{'target'} = 'iframe'; - } - if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'display'}{'target'} ne $confhash{$itemid}{'display'}{'target'}) { - $changes{$itemid} = 1; - } - } else { - $changes{$itemid} = 1; - } - foreach my $extra ('passback','roster') { - if ($env{'form.ltitools_'.$extra.'_'.$i}) { - $confhash{$itemid}{$extra} = 1; - if ($env{'form.ltitools_'.$extra.'valid_'.$i} ne '') { - my $lifetime = $env{'form.ltitools_'.$extra.'valid_'.$i}; - $lifetime =~ s/^\s+|\s+$//g; - if ($lifetime =~ /^\d+\.?\d*$/) { - $confhash{$itemid}{$extra.'valid'} = $lifetime; - } - } - } - if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) { - $changes{$itemid} = 1; - } - if ($domconfig{$action}{$itemid}{$extra.'valid'} ne $confhash{$itemid}{$extra.'valid'}) { - $changes{$itemid} = 1; - } - } - my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i); - foreach my $item ('label','title','target','linktext','explanation','append') { - if (grep(/^\Q$item\E$/,@courseconfig)) { - $confhash{$itemid}{'crsconf'}{$item} = 1; - if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'crsconf'}{$item} ne $confhash{$itemid}{'crsconf'}{$item}) { - $changes{$itemid} = 1; - } - } else { - $changes{$itemid} = 1; - } - } - } - my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_fields_'.$i); - foreach my $field (@fields) { - if ($possfield{$field}) { - if ($field eq 'roles') { - foreach my $role (@courseroles) { - my $choice = $env{'form.ltitools_roles_'.$role.'_'.$i}; - if (($choice ne '') && ($posslti{$choice})) { - $confhash{$itemid}{'roles'}{$role} = $choice; - if ($role eq 'cc') { - $confhash{$itemid}{'roles'}{'co'} = $choice; - } - } - if (ref($domconfig{$action}{$itemid}{'roles'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'roles'}{$role} ne $confhash{$itemid}{'roles'}{$role}) { - $changes{$itemid} = 1; - } - } elsif ($confhash{$itemid}{'roles'}{$role}) { - $changes{$itemid} = 1; - } - } - } else { - $confhash{$itemid}{'fields'}{$field} = 1; - if (ref($domconfig{$action}{$itemid}{'fields'}) eq 'HASH') { - if ($domconfig{$action}{$itemid}{'fields'}{$field} ne $confhash{$itemid}{'fields'}{$field}) { - $changes{$itemid} = 1; - } - } else { - $changes{$itemid} = 1; - } - } - } - } - if (ref($confhash{$itemid}{'fields'}) eq 'HASH') { - if ($confhash{$itemid}{'fields'}{'user'}) { - if ($env{'form.ltitools_userincdom_'.$i}) { - $confhash{$itemid}{'incdom'} = 1; - } - if ($domconfig{$action}{$itemid}{'incdom'} ne $confhash{$itemid}{'incdom'}) { - $changes{$itemid} = 1; - } - } - } - $allpos[$newpos] = $itemid; - } - if ($imgdeletions{$itemid}) { - $changes{$itemid} = 1; - #FIXME need to obsolete item in RES space - } elsif ($env{'form.ltitools_image_'.$i.'.filename'}) { - my ($imgurl,$error) = &process_ltitools_image($r,$dom,$confname,'ltitools_image_'.$i, - $itemid,$configuserok,$switchserver, - $author_ok); - if ($imgurl) { - $confhash{$itemid}{'image'} = $imgurl; - $changes{$itemid} = 1; - } - if ($error) { - &Apache::lonnet::logthis($error); - $errors .= '
      • '.$error.'
      • '; - } - } 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; + } + 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}; } + } else { + $newltitools{$id}{$inner} = $domconfig{'ltitools'}{$id}{$inner}; } - last if ($changes{$itemid}); } + } else { + $newltitools{$id} = $domconfig{'ltitools'}{$id}; } } } } - 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 ++; - } + if ($toolserror) { + $errors = '
      • '.$toolserror.'
      • '; + } + if ((keys(%ltitoolschg) == 0) && (keys(%secchanges) == 0)) { + $resulttext = &mt('No changes made.'); + if ($errors) { + $resulttext .= '
        '.&mt('The following errors occurred: ').'
          '. + $errors.'
        '; } + return $resulttext; } my %ltitoolshash = ( - $action => { %confhash } + $action => { %newltitools } ); - my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash, - $dom); + if (keys(%secchanges)) { + $ltitoolshash{'toolsec'} = \%newtoolsec; + } + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash,$dom); if ($putresult eq 'ok') { - my %ltienchash = ( - $action => { %encconfig } - ); - &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); - if (keys(%changes) > 0) { + my %keystore; + if ($is_home) { + my %toolsenchash = ( + $action => { %newtoolsenc } + ); + &Apache::lonnet::put_dom('encconfig',\%toolsenchash,$dom,undef,1); my $cachetime = 24*60*60; - my %ltiall = %confhash; - foreach my $id (keys(%ltiall)) { - if (ref($encconfig{$id}) eq 'HASH') { - foreach my $item ('key','secret') { - $ltiall{$id}{$item} = $encconfig{$id}{$item}; + &Apache::lonnet::do_cache_new('ltitoolsenc',$dom,\%newtoolsenc,$cachetime); + &store_security($dom,'ltitools',\%secchanges,\%newkeyset,\%keystore,$lastactref); + } + $resulttext = &mt('Changes made:').'
          '; + 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; + } + } else { + $errors .= '
        • '.&mt('Failed to save changes').'
        • '; + } + if ($errors) { + $resulttext .= '

          '.&mt('The following errors occurred: ').'

            '. + $errors.'

          '; + } + 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}); } } } - &Apache::lonnet::do_cache_new('ltitools',$dom,\%ltiall,$cachetime); - if (ref($lastactref) eq 'HASH') { - $lastactref->{'ltitools'} = 1; + } + 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'}}; } - $resulttext = &mt('Changes made:').'
            '; - my %bynum; - foreach my $itemid (sort(keys(%changes))) { - my $position = $confhash{$itemid}{'order'}; - $bynum{$position} = $itemid; + } + } + my @items= ('crs','dom'); + if ($context eq 'ltisec') { + push(@items,'consumers'); + } + 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; + } + } else { + $secchanges->{'encrypt'} = 1; } - foreach my $pos (sort { $a <=> $b } keys(%bynum)) { - my $itemid = $bynum{$pos}; - if (ref($confhash{$itemid}) ne 'HASH') { - $resulttext .= '
          • '.&mt('Deleted: [_1]',$changes{$itemid}).'
          • '; - } else { - $resulttext .= '
          • '.$confhash{$itemid}{'title'}.''; - if ($confhash{$itemid}{'image'}) { - $resulttext .= ' '. - ''.&mt('Tool Provider icon').''; - } - $resulttext .= '
            • '; - my $position = $pos + 1; - $resulttext .= '
            • '.&mt('Order: [_1]',$position).'
            • '; - foreach my $item ('version','msgtype','sigmethod','url','lifetime') { - if ($confhash{$itemid}{$item} ne '') { - $resulttext .= '
            • '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
            • '; - } - } - if ($encconfig{$itemid}{'key'} ne '') { - $resulttext .= '
            • '.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'
            • '; - } - if ($encconfig{$itemid}{'secret'} ne '') { - $resulttext .= '
            • '.$lt{'secret'}.': '; - my $num = length($encconfig{$itemid}{'secret'}); - $resulttext .= ('*'x$num).'
            • '; - } - $resulttext .= '
            • '.&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 .= '
            • '; - foreach my $item ('passback','roster') { - $resulttext .= '
            • '.$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 .= '
            • '; - } - 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 .= '
            • '.$displaylist.'
            • '; - } - foreach my $item ('linktext','explanation') { - if ($confhash{$itemid}{'display'}{$item}) { - $resulttext .= '
            • '.$lt{$item}.': '.$confhash{$itemid}{'display'}{$item}.'
            • '; - } - } - } - 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 .= '
            • '.&mt('Data sent').':'.$fieldlist.'
            • '; + } elsif (ref($currsec->{'encrypt'}) eq 'HASH') { + if ($currsec->{'encrypt'}{$item}) { + $secchanges->{'encrypt'} = 1; + } + } + } + my $secrets; + if ($context eq 'ltisec') { + $secrets = 'ltisecrets'; + } else { + $secrets = 'toolsecrets'; + } + unless (exists($currsec->{'rules'})) { + $currsec->{'rules'} = {}; + } + &password_rule_changes($secrets,$newsec->{'rules'},$currsec->{'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 $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}; } } - 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 .= '
            • '.&mt('Role mapping:').$rolemaps.'
            • '; - } + } elsif ($env{$keyitem} ne '') { + unless (grep(/^\Q$hostid\E$/,@{$newsec->{'private'}{'keys'}})) { + push(@{$newsec->{'private'}{'keys'}},$hostid); } - 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 .= '
            • '.&mt('Custom items').': '.$customlist.'
            • '; - } - } - $resulttext .= '
            '; + $secchanges->{'private'} = 1; + $newkeyset->{$hostid} = $env{$keyitem}; } } - $resulttext .= '
          '; - } else { - $resulttext = &mt('No changes made.'); } - } else { - $errors .= '
        • '.&mt('Failed to save changes').'
        • '; - } - if ($errors) { - $resulttext .= &mt('The following errors occurred: ').'
            '. - $errors.'
          '; } - return $resulttext; } -sub process_ltitools_image { - my ($r,$dom,$confname,$caller,$itemid,$configuserok,$switchserver,$author_ok) = @_; - my $filename = $env{'form.'.$caller.'.filename'}; - my ($error,$url); - my ($width,$height) = (21,21); - if ($configuserok eq 'ok') { - if ($switchserver) { - $error = &mt('Upload of Tool Provider (LTI) icon is not permitted to this server: [_1]', - $switchserver); - } elsif ($author_ok eq 'ok') { - my ($result,$imageurl,$madethumb) = - &publishlogo($r,'upload',$caller,$dom,$confname, - "ltitools/$itemid/icon",$width,$height); - if ($result eq 'ok') { - if ($madethumb) { - my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$}); - my $imagethumb = "$path/tn-".$imagefile; - $url = $imagethumb; - } else { - $url = $imageurl; - } - } else { - $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result); +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); } - } else { - $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$author_ok); } - } else { - $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$configuserok); } - return ($url,$error); } -sub get_ltitools_id { - my ($cdom,$title) = @_; - # get lock on ltitools db - my $lockhash = { - lock => $env{'user.name'}. - ':'.$env{'user.domain'}, - }; - my $tries = 0; - my $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom); - my ($id,$error); - - while (($gotlock ne 'ok') && ($tries<10)) { - $tries ++; - sleep (0.1); - $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom); - } - if ($gotlock eq 'ok') { - my %currids = &Apache::lonnet::dump_dom('ltitools',$cdom); - if ($currids{'lock'}) { - delete($currids{'lock'}); - if (keys(%currids)) { - my @curr = sort { $a <=> $b } keys(%currids); - if ($curr[-1] =~ /^\d+$/) { - $id = 1 + $curr[-1]; - } +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 { - $id = 1; + %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 ($id) { - unless (&Apache::lonnet::newput_dom('ltitools',{ $id => $title },$cdom) eq 'ok') { - $error = 'nostore'; + my @types= ('crs','dom'); + if ($context eq 'lti') { + foreach my $type (@types) { + undef($domdefaults{'linkprotenc_'.$type}); + } + push(@types,'consumers'); + undef($domdefaults{'ltienc_consumers'}); + } elsif ($context eq 'ltitools') { + foreach my $type (@types) { + undef($domdefaults{'toolenc_'.$type}); + } + } + 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; + } + } elsif ($context eq 'ltitools') { + $domdefaults{'toolenc_'.$type} = 1; + } + $shown = $encrypted{$type}{'on'}; + } + } + $output .= '
        • '.$shown.'
        • '; + } + } 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 .= '
        • '.&mt('[_1] not set.',$titles{$rule}); + ' '.&mt('Default of [_1] will be used', + $Apache::lonnet::passwdmin).'
        • '; + } else { + $output .= '
        • '.&mt('[_1] set to none',$titles{$rule}).'
        • '; + } + } else { + $output .= '
        • '.&mt('[_1] set to [_2]',$titles{$rule},$newsec->{rules}{$rule}).'
        • '; + } + } + 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 = '
          • '. + join('
          • ',map {$rulenames{$_} } @{$newsec->{'rules'}{'chars'}}). + '
          '; + $output .= '
        • '.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'
        • '; + } else { + $output .= '
        • '.&mt('[_1] set to none',$titles{'chars'}).'
        • '; } } else { - $error = 'nonumber'; + $output .= '
        • '.&mt('[_1] set to none',$titles{'chars'}).'
        • '; + } + } elsif ($item eq 'private') { + $needs_update = 1; + if ($context eq 'lti') { + undef($domdefaults{'ltiprivhosts'}); + } elsif ($context eq 'ltitools') { + undef($domdefaults{'toolprivhosts'}); + } + if (keys(%{$newkeyset})) { + my @privhosts; + foreach my $hostid (sort(keys(%{$newkeyset}))) { + if ($keystore->{$hostid} eq 'ok') { + $output .= '
        • '.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'
        • '; + unless (grep(/^\Q$hostid\E$/,@privhosts)) { + push(@privhosts,$hostid); + } + } + } + if (@privhosts) { + if ($context eq 'lti') { + $domdefaults{'ltiprivhosts'} = \@privhosts; + } elsif ($context eq 'ltitools') { + $domdefaults{'toolprivhosts'} = \@privhosts; + } + } } + } elsif ($item eq 'linkprot') { + next; } - my $dellockoutcome = &Apache::lonnet::del_dom('ltitools',['lock'],$cdom); - } else { - $error = 'nolock'; } - return ($id,$error); + if ($needs_update) { + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + } + return $output; } sub modify_proctoring { @@ -15780,9 +15588,11 @@ sub process_proctoring_image { $error = &mt('Upload of Remote Proctoring Provider icon is not permitted to this server: [_1]', $switchserver); } elsif ($author_ok eq 'ok') { + my $modified = []; my ($result,$imageurl,$madethumb) = - &publishlogo($r,'upload',$caller,$dom,$confname, - "proctoring/$provider/icon",$width,$height); + &Apache::lonconfigsettings::publishlogo($r,'upload',$caller,$dom,$confname, + "proctoring/$provider/icon",$width,$height, + '',$modified); if ($result eq 'ok') { if ($madethumb) { my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$}); @@ -15791,6 +15601,7 @@ sub process_proctoring_image { } else { $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); } @@ -15806,7 +15617,7 @@ sub process_proctoring_image { sub modify_lti { my ($r,$dom,$action,$lastactref,%domconfig) = @_; my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); - my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my ($newid,@allpos,%changes,%confhash,%ltienc,$errors,$resulttext); my (%posslti,%posslticrs,%posscrstype); my @courseroles = ('cc','in','ta','ep','st'); my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); @@ -15827,74 +15638,66 @@ sub modify_lti { map { $posscrstype{$_} = 1; } @coursetypes; my %menutitles = <imenu_titles(); + my (%currltisec,%secchanges,%newltisec,%newltienc,%newkeyset); - my (%currltisec,%secchanges,%newltisec,%keyset,%newkeyset); - $newltisec{'private'}{'keys'} = []; - $newltisec{'encrypt'} = {}; - $newltisec{'rules'} = {}; - if (ref($domconfig{'ltisec'}) eq 'HASH') { - %currltisec = %{$domconfig{'ltisec'}}; - 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'}}; - } - } + &fetch_secrets($dom,'ltisec',\%domconfig,\%currltisec,\%secchanges,\%newltisec,\%newkeyset); + + my (%linkprotchg,$linkprotoutput,$is_home); + my $proterror = &Apache::courseprefs::process_linkprot($dom,'',$currltisec{'linkprot'}, + \%linkprotchg,'domain'); + my $home = &Apache::lonnet::domain($dom,'primary'); + unless (($home eq 'no_host') || ($home eq '')) { + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $id (@ids) { if ($id eq $home) { $is_home=1; } } } - foreach my $item ('crs','dom','consumers') { - my $formelement; - if ($item eq 'consumers') { - $formelement = 'form.ltisec_'.$item; - } else { - $formelement = 'form.ltisec_'.$item.'linkprot'; + + if (keys(%linkprotchg)) { + $secchanges{'linkprot'} = 1; + my %oldlinkprot; + if (ref($currltisec{'linkprot'}) eq 'HASH') { + %oldlinkprot = %{$currltisec{'linkprot'}}; } - if ($env{$formelement}) { - $newltisec{'encrypt'}{$item} = 1; - if (ref($currltisec{'encrypt'}) eq 'HASH') { - unless ($currltisec{'encrypt'}{$item}) { - $secchanges{'encrypt'} = 1; + foreach my $id (keys(%linkprotchg)) { + if (ref($linkprotchg{$id}) eq 'HASH') { + foreach my $inner (keys(%{$linkprotchg{$id}})) { + if (($inner eq 'secret') || ($inner eq 'key')) { + if ($is_home) { + $newltienc{$id}{$inner} = $linkprotchg{$id}{$inner}; + } + } } } else { - $secchanges{'encrypt'} = 1; - } - } elsif (ref($currltisec{'encrypt'}) eq 'HASH') { - if ($currltisec{'encrypt'}{$item}) { - $secchanges{'encrypt'} = 1; + $newltisec{'linkprot'}{$id} = $linkprotchg{$id}; } } + $linkprotoutput = &Apache::courseprefs::store_linkprot($dom,'','domain',\%linkprotchg,\%oldlinkprot); + if (keys(%linkprotchg)) { + %{$newltisec{'linkprot'}} = %linkprotchg; + } } - 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}; + if (ref($currltisec{'linkprot'}) eq 'HASH') { + foreach my $id (%{$currltisec{'linkprot'}}) { + next if ($id !~ /^\d+$/); + unless (exists($linkprotchg{$id})) { + if (ref($currltisec{'linkprot'}{$id}) eq 'HASH') { + foreach my $inner (keys(%{$currltisec{'linkprot'}{$id}})) { + if (($inner eq 'secret') || ($inner eq 'key')) { + if ($is_home) { + $newltienc{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner}; + } + } else { + $newltisec{'linkprot'}{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner}; } } - } elsif ($env{$keyitem} ne '') { - unless (grep(/^\Q$hostid\E$/,@{$newltisec{'private'}{'keys'}})) { - push(@{$newltisec{'private'}{'keys'}},$hostid); - } - $secchanges{'private'} = 1; - $newkeyset{$hostid} = $env{$keyitem}; + } else { + $newltisec{'linkprot'}{$id} = $currltisec{'linkprot'}{$id}; } } } } - + if ($proterror) { + $errors .= '
        • '.$proterror.'
        • '; + } my (@items,%deletions,%itemids); if ($env{'form.lti_add'}) { my $consumer = $env{'form.lti_consumer_add'}; @@ -15928,29 +15731,43 @@ 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','key','secret','lifetime','requser','crsinc') { + foreach my $item ('consumer','lifetime','requser','crsinc') { my $formitem = 'form.lti_'.$item.'_'.$idx; $env{$formitem} =~ s/(`)/'/g; if ($item eq 'lifetime') { $env{$formitem} =~ s/[^\d.]//g; } if ($env{$formitem} ne '') { - if (($item eq 'key') || ($item eq 'secret')) { - $encconfig{$itemid}{$item} = $env{$formitem}; - } else { - $confhash{$itemid}{$item} = $env{$formitem}; - unless (($idx eq 'add') || ($changes{$itemid})) { - if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { - $changes{$itemid} = 1; - } + $confhash{$itemid}{$item} = $env{$formitem}; + unless (($idx eq 'add') || ($changes{$itemid})) { + if ($currlti{$item} ne $confhash{$itemid}{$item}) { + $changes{$itemid} = 1; } } } @@ -16091,27 +15908,27 @@ sub modify_lti { unless (($idx eq 'add') || ($changes{$itemid})) { if ($confhash{$itemid}{'crsinc'}) { foreach my $field ('mapcrs','storecrs','makecrs','section','passback','roster') { - if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { + if ($currlti{$field} ne $confhash{$itemid}{$field}) { $changes{$itemid} = 1; } } unless ($changes{$itemid}) { - if ($domconfig{$action}{$itemid}{'passback'} eq $confhash{$itemid}{'passback'}) { - if ($domconfig{$action}{$itemid}{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) { + if ($currlti{'passback'} eq $confhash{$itemid}{'passback'}) { + if ($currlti{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) { $changes{$itemid} = 1; } } } foreach my $field ('mapcrstype','selfenroll') { unless ($changes{$itemid}) { - if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { + if (ref($currlti{$field}) eq 'ARRAY') { if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { - my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, + my @diffs = &Apache::loncommon::compare_arrays($currlti{$field}, $confhash{$itemid}{$field}); if (@diffs) { $changes{$itemid} = 1; } - } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { + } elsif (@{$currlti{$field}} > 0) { $changes{$itemid} = 1; } } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { @@ -16122,10 +15939,10 @@ sub modify_lti { } } unless ($changes{$itemid}) { - if (ref($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') { + if (ref($currlti{'maproles'}) eq 'HASH') { if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { - foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) { - if ($domconfig{$action}{$itemid}{'maproles'}{$ltirole} ne + foreach my $ltirole (keys(%{$currlti{'maproles'}})) { + if ($currlti{'maproles'}{$ltirole} ne $confhash{$itemid}{'maproles'}{$ltirole}) { $changes{$itemid} = 1; last; @@ -16134,13 +15951,13 @@ sub modify_lti { unless ($changes{$itemid}) { foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) { if ($confhash{$itemid}{'maproles'}{$ltirole} ne - $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) { + $currlti{'maproles'}{$ltirole}) { $changes{$itemid} = 1; last; } } } - } elsif (keys(%{$domconfig{$action}{$itemid}{'maproles'}}) > 0) { + } elsif (keys(%{$currlti{'maproles'}}) > 0) { $changes{$itemid} = 1; } } elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { @@ -16154,20 +15971,20 @@ sub modify_lti { } unless ($changes{$itemid}) { foreach my $field ('mapuser','lcauth','lcauthparm','topmenu','inlinemenu','callback') { - if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { + if ($currlti{$field} ne $confhash{$itemid}{$field}) { $changes{$itemid} = 1; } } unless ($changes{$itemid}) { foreach my $field ('makeuser','lcmenu') { - if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { + if (ref($currlti{$field}) eq 'ARRAY') { if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { - my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, + my @diffs = &Apache::loncommon::compare_arrays($currlti{$field}, $confhash{$itemid}{$field}); if (@diffs) { $changes{$itemid} = 1; } - } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { + } elsif (@{$currlti{$field}} > 0) { $changes{$itemid} = 1; } } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { @@ -16180,6 +15997,71 @@ 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; @@ -16197,134 +16079,57 @@ 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 } + ); + } if (keys(%secchanges)) { $ltihash{'ltisec'} = \%newltisec; + if ($secchanges{'linkprot'}) { + if ($is_home) { + $ltienchash{'linkprot'} = \%newltienc; + } + } } my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash,$dom); if ($putresult eq 'ok') { - my %keystore; - if (keys(%secchanges)) { - if ($secchanges{'private'}) { - my $who = &escape($env{'user.name'}.':'.$env{'user.domain'}); - foreach my $hostid (keys(%newkeyset)) { - my $storehash = { - key => $newkeyset{$hostid}, - who => $env{'user.name'}.':'.$env{'user.domain'}, - }; - $keystore{$hostid} = &Apache::lonnet::store_dom($storehash,'lti','private', - $dom,$hostid); - } - } - } - my %ltienchash = ( - $action => { %encconfig } - ); - &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); - if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) { - return &mt('No changes made.'); + if (keys(%ltienchash)) { + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); } $resulttext = &mt('Changes made:').'
            '; if (keys(%secchanges) > 0) { - foreach my $item (keys(%secchanges)) { - if ($item eq 'encrypt') { - my %encrypted = ( - crs => { - on => &mt('Encryption of stored link protection secrets defined in courses enabled'), - off => &mt('Encryption of stored link protection secrets defined in courses disabled'), - }, - dom => { - on => &mt('Encryption of stored link protection secrets defined in domain enabled'), - off => &mt('Encryption of stored link protection secrets defined in domain disabled'), - }, - consumers => { - on => &mt('Encryption of stored consumer secrets defined in domain enabled'), - off => &mt('Encryption of stored consumer secrets defined in domain disabled'), - }, - ); - foreach my $type ('crs','dom','consumers') { - my $shown = $encrypted{$type}{'off'}; - if (ref($newltisec{$item}) eq 'HASH') { - if ($newltisec{$item}{$type}) { - $shown = $encrypted{$type}{'on'}; - } - } - $resulttext .= '
          • '.$shown.'
          • '; - } - } 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 .= '
          • '.&mt('[_1] not set.',$titles{$rule}); - ' '.&mt('Default of [_1] will be used', - $Apache::lonnet::passwdmin).'
          • '; - } else { - $resulttext .= '
          • '.&mt('[_1] set to none',$titles{$rule}).'
          • '; - } - } else { - $resulttext .= '
          • '.&mt('[_1] set to [_2]',$titles{$rule},$newltisec{rules}{$rule}).'
          • '; - } - } - 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 = '
            • '. - join('
            • ',map {$rulenames{$_} } @{$newltisec{'rules'}{'chars'}}). - '
            '; - $resulttext .= '
          • '.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'
          • '; - } else { - $resulttext .= '
          • '.&mt('[_1] set to none',$titles{'chars'}).'
          • '; - } - } else { - $resulttext .= '
          • '.&mt('[_1] set to none',$titles{'chars'}).'
          • '; - } - } elsif ($item eq 'private') { - if (keys(%newkeyset)) { - foreach my $hostid (sort(keys(%newkeyset))) { - if ($keystore{$hostid} eq 'ok') { - $resulttext .= '
          • '.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'
          • '; - } - } - } - } + $resulttext .= <i_security_results($dom,'lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore); + if (exists($secchanges{'linkprot'})) { + $resulttext .= $linkprotoutput; } } if (keys(%changes) > 0) { my $cachetime = 24*60*60; - my %ltiall = %confhash; - foreach my $id (keys(%ltiall)) { - if (ref($encconfig{$id}) eq 'HASH') { - foreach my $item ('key','secret') { - $ltiall{$id}{$item} = $encconfig{$id}{$item}; - } - } - } - &Apache::lonnet::do_cache_new('lti',$dom,\%ltiall,$cachetime); + &Apache::lonnet::do_cache_new('lti',$dom,\%confhash,$cachetime); if (ref($lastactref) eq 'HASH') { $lastactref->{'lti'} = 1; } my %bynum; foreach my $itemid (sort(keys(%changes))) { - my $position = $confhash{$itemid}{'order'}; - $bynum{$position} = $itemid; + if (ref($confhash{$itemid}) eq 'HASH') { + 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 .= '
          • '.&mt('Deleted: [_1]',$changes{$itemid}).'
          • '; - } else { + if (ref($confhash{$itemid}) eq 'HASH') { $resulttext .= '
          • '.$confhash{$itemid}{'consumer'}.'
              '; my $position = $pos + 1; $resulttext .= '
            • '.&mt('Order: [_1]',$position).'
            • '; @@ -16333,13 +16138,11 @@ sub modify_lti { $resulttext .= '
            • '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
            • '; } } - if ($encconfig{$itemid}{'key'} ne '') { - $resulttext .= '
            • '.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'
            • '; + if ($ltienc{$itemid}{'key'} ne '') { + $resulttext .= '
            • '.$lt{'key'}.': '.$ltienc{$itemid}{'key'}.'
            • '; } - if ($encconfig{$itemid}{'secret'} ne '') { - $resulttext .= '
            • '.$lt{'secret'}.': '; - my $num = length($encconfig{$itemid}{'secret'}); - $resulttext .= ('*'x$num).'
            • '; + if ($ltienc{$itemid}{'secret'} ne '') { + $resulttext .= '
            • '.$lt{'secret'}.': ['.&mt('not shown').']
            • '; } if ($confhash{$itemid}{'requser'}) { if ($confhash{$itemid}{'callback'}) { @@ -16491,8 +16294,18 @@ sub modify_lti { $resulttext .= '
          • '; } } + if (keys(%deletions)) { + foreach my $itemid (sort { $a <=> $b } keys(%deletions)) { + $resulttext .= '
          • '.&mt('Deleted: [_1]',$changes{$itemid}).'
          • '; + } + } } $resulttext .= '
          '; + if (ref($lastactref) eq 'HASH') { + if (($secchanges{'encrypt'}) || ($secchanges{'private'})) { + $lastactref->{'domdefaults'} = 1; + } + } } else { $errors .= '
        • '.&mt('Failed to save changes').'
        • '; } @@ -16503,6 +16316,30 @@ 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) = @_; # get lock on lti db @@ -17305,7 +17142,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) { @@ -17363,12 +17200,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; } @@ -17385,7 +17222,7 @@ sub modify_contacts { } } else { foreach my $key (@overrides) { - push(@{$changes{'overrides'}},$key); + push(@{$changes{'overrides'}},$key); } } } @@ -17567,7 +17404,7 @@ sub modify_contacts { $resulttext .= $bcctext.': '.$bcc{$type}.''; } elsif (!@text) { $resulttext .= &mt('No one'); - } + } if ($includestr{$type} ne '') { if ($includeloc{$type} eq 'b') { $resulttext .= '
          '.&mt('Text automatically added to e-mail body:').' '.$includestr{$type}; @@ -17591,14 +17428,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]', ''.join(', ',@text).''); @@ -17761,7 +17598,7 @@ sub modify_contacts { } sub modify_privacy { - my ($dom,%domconfig) = @_; + my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,%current,%changes); if (ref($domconfig{'privacy'}) eq 'HASH') { %current = %{$domconfig{'privacy'}}; @@ -17772,7 +17609,7 @@ sub modify_privacy { domain => 'Assigned domain role(s)', author => 'Assigned co-author role(s)', course => 'Assigned course role(s)', - community => 'Assigned community role', + community => 'Assigned community role(s)', ); my %roles = &Apache::lonlocal::texthash ( domain => 'Domain role', @@ -17791,6 +17628,7 @@ sub modify_privacy { user => 'User authorizes', domain => 'Domain Coordinator authorizes', auto => 'Unrestricted', + notify => 'Notify when role needs authorization', ); my %fieldnames = &Apache::lonlocal::texthash ( id => 'Student/Employee ID', @@ -17816,7 +17654,7 @@ sub modify_privacy { ); foreach my $item (@items) { if (@instdoms > 1) { - if ($env{'form.privacy_approval_instdom'.$item} =~ /^(none|user|domain|auto)$/) { + 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') { @@ -17908,6 +17746,18 @@ sub modify_privacy { } } } + my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']); + my %notify; + foreach my $possdc (&Apache::loncommon::get_env_multiple('form.privacy_notify')) { + if (exists($domcoords{$possdc})) { + $notify{$possdc} = 1; + } + } + my $notify = join(',',sort(keys(%notify))); + if ($current{'notify'} ne $notify) { + $changes{'notify'} = 1; + } + $privacyhash{'notify'} = $notify; } my %confighash = ( privacy => \%privacyhash, @@ -17916,7 +17766,7 @@ sub modify_privacy { if ($putresult eq 'ok') { if (keys(%changes) > 0) { $resulttext = &mt('Changes made: ').'
            '; - foreach my $key ('approval','othdom','priv','unpriv') { + foreach my $key ('approval','notify','othdom','priv','unpriv') { if ($changes{$key}) { $resulttext .= '
          • '.$titles{$key}.':
              '; if ($key eq 'approval') { @@ -17934,6 +17784,15 @@ sub modify_privacy { } $resulttext .= '
          • '; } + } elsif ($key eq 'notify') { + if ($privacyhash{$key}) { + foreach my $dc (split(/,/,$privacyhash{$key})) { + my ($dcname,$dcdom) = split(/:/,$dc); + $resulttext .= '
          • '.&Apache::loncommon::plainname($dcname,$dcdom).'
          • '; + } + } else { + $resulttext .= '
          • '.&mt('No DCs to notify').'
          • '; + } } elsif ($key eq 'othdom') { my @statuses; if (ref($types) eq 'ARRAY') { @@ -17974,6 +17833,29 @@ sub modify_privacy { $resulttext .= '
          '; } } + $resulttext .= '
        '; + if ($changes{'approval'}) { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); + delete($domdefaults{'userapprovals'}); + if (ref($privacyhash{'approval'}) eq 'HASH') { + foreach my $domtype ('instdom','extdom') { + if (ref($privacyhash{'approval'}{$domtype}) eq 'HASH') { + foreach my $roletype ('domain','author','course','community') { + if ($privacyhash{'approval'}{$domtype}{$roletype} eq 'user') { + $domdefaults{'userapprovals'} = 1; + last; + } + } + } + last if ($domdefaults{'userapprovals'}); + } + } + 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 user information settings'); } @@ -18160,12 +18042,15 @@ 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) = - &publishlogo($r,'upload','passwords_customfile',$dom, - $confname,'customtext/resetpw','','',$customfn); + &Apache::lonconfigsettings::publishlogo($r,'upload','passwords_customfile',$dom, + $confname,'customtext/resetpw','','',$customfn, + $modified); 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); } @@ -18413,7 +18298,7 @@ sub modify_passwords { ); my $needed = '
        • '. join('
        • ',map {$rulenames{$_} } @{$confighash{'passwords'}{'chars'}}). - '
        '; + '
      '; $resulttext .= '
    • '.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'
    • '; } else { $resulttext .= '
    • '.&mt('[_1] set to none',$titles{'chars'}).'
    • '; @@ -18486,7 +18371,7 @@ sub password_rule_changes { my (@rules,%staticdefaults); if ($prefix eq 'passwords') { @rules = ('min','max','expire','numsaved'); - } elsif ($prefix eq 'secrets') { + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { @rules = ('min','max'); } $staticdefaults{'min'} = $Apache::lonnet::passwdmin; @@ -18896,7 +18781,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})) { @@ -18908,7 +18793,7 @@ sub modify_selfcreation { } } elsif ($emaildom ne '') { push(@{$changes{'cancreate'}},'emaildomain'); - } + } } $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type}; } elsif ($chosen eq 'custom') { @@ -19335,7 +19220,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:'). '
        '; foreach my $status (@statuses) { if ($status eq 'default') { @@ -19421,7 +19306,7 @@ sub modify_selfcreation { $output = '
      • '.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'
      • '; } else { $output = '
      • '.$usertypes{$type}.' -- '.&mt("User's e-mail address needs to end: [_1]", - $cancreate{'emaildomain'}{$type}{'inst'}).'
      • '; + $cancreate{'emaildomain'}{$type}{'inst'}).''; } } } elsif ($cancreate{'emailoptions'}{$type} eq 'noninst') { @@ -19439,7 +19324,7 @@ sub modify_selfcreation { $output = '
      • '.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'
      • '; } else { $output = '
      • '.$usertypes{$type}.' -- '.&mt("User's e-mail address must not end: [_1]", - $cancreate{'emaildomain'}{$type}{'noninst'}).'
      • '; + $cancreate{'emaildomain'}{$type}{'noninst'}).''; } } } @@ -19543,7 +19428,7 @@ sub modify_selfcreation { $typename = $othertitle; } else { $typename = $usertypes{$type}; - } + } $chgtext .= &mt('(Affiliation: [_1])',$typename); } if (@{$email_rule{$type}} > 0) { @@ -19861,16 +19746,58 @@ sub modify_defaults { } } elsif ($item eq 'portal_def') { if ($newvalues{$item} ne '') { - unless ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) { + if ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) { + foreach my $field ('email','web') { + if ($env{'form.'.$item.'_'.$field}) { + $newvalues{$item.'_'.$field} = $env{'form.'.$item.'_'.$field}; + } + } + } else { push(@errors,$item); } } } if (grep(/^\Q$item\E$/,@errors)) { $newvalues{$item} = $domdefaults{$item}; + if ($item eq 'portal_def') { + if ($domdefaults{$item}) { + foreach my $field ('email','web') { + if (exists($domdefaults{$item.'_'.$field})) { + $newvalues{$item.'_'.$field} = $domdefaults{$item.'_'.$field}; + } + } + } + } } elsif ($domdefaults{$item} ne $newvalues{$item}) { $changes{$item} = 1; } + if ($item eq 'portal_def') { + unless (grep(/^\Q$item\E$/,@errors)) { + if ($newvalues{$item} eq '') { + foreach my $field ('email','web') { + if (exists($domdefaults{$item.'_'.$field})) { + delete($domdefaults{$item.'_'.$field}); + } + } + } else { + unless ($changes{$item}) { + foreach my $field ('email','web') { + if ($domdefaults{$item.'_'.$field} ne $newvalues{$item.'_'.$field}) { + $changes{$item} = 1; + last; + } + } + } + foreach my $field ('email','web') { + if ($newvalues{$item.'_'.$field}) { + $domdefaults{$item.'_'.$field} = $newvalues{$item.'_'.$field}; + } elsif (exists($domdefaults{$item.'_'.$field})) { + delete($domdefaults{$item.'_'.$field}); + } + } + } + } + } $domdefaults{$item} = $newvalues{$item}; } my %staticdefaults = ( @@ -19885,6 +19812,41 @@ sub modify_defaults { $newvalues{$item} = $staticdefaults{$item}; } } + my ($unamemaprules,$ruleorder); + my @possunamemaprules = &Apache::loncommon::get_env_multiple('form.unamemap_rule'); + if (@possunamemaprules) { + ($unamemaprules,$ruleorder) = + &Apache::lonnet::inst_userrules($dom,'unamemap'); + if ((ref($unamemaprules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) { + if (@{$ruleorder} > 0) { + my %possrules; + map { $possrules{$_} = 1; } @possunamemaprules; + foreach my $rule (@{$ruleorder}) { + if ($possrules{$rule}) { + push(@{$newvalues{'unamemap_rule'}},$rule); + } + } + } + } + } + if (ref($domdefaults{'unamemap_rule'}) eq 'ARRAY') { + if (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') { + my @rulediffs = &Apache::loncommon::compare_arrays($domdefaults{'unamemap_rule'}, + $newvalues{'unamemap_rule'}); + if (@rulediffs) { + $changes{'unamemap_rule'} = 1; + $domdefaults{'unamemap_rule'} = $newvalues{'unamemap_rule'}; + } + } elsif (@{$domdefaults{'unamemap_rule'}} > 0) { + $changes{'unamemap_rule'} = 1; + delete($domdefaults{'unamemap_rule'}); + } + } elsif (ref($newvalues{'unamemap_rule'}) eq 'ARRAY') { + if (@{$newvalues{'unamemap_rule'}} > 0) { + $changes{'unamemap_rule'} = 1; + $domdefaults{'unamemap_rule'} = $newvalues{'unamemap_rule'}; + } + } my %defaults_hash = ( defaults => \%newvalues, ); @@ -19996,9 +19958,29 @@ sub modify_defaults { $resulttext =~ s/, $//; $resulttext .= ''; } else { - $resulttext .= '
      • '.&mt('Institutional user status types deleted').'
      • '; + $resulttext .= '
      • '.&mt('Institutional user status types deleted').'
      • '; } } + } 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 .= '
      • '.&mt('Mapping for missing usernames includes: [_1]', + '
        • '.join('
        • ',@rulenames).'
        '). + '
      • '; + } else { + $resulttext .= '
      • '.&mt('No mapping for missing usernames via standard log-in').'
      • '; + } + } else { + $resulttext .= '
      • '.&mt('Mapping for missing usernames via standard log-in deleted').'
      • '; + } } else { my $value = $env{'form.'.$item}; if ($value eq '') { @@ -20015,7 +19997,20 @@ sub modify_defaults { $value = $authnames{$shortauth{$value}}; } $resulttext .= '
      • '.&mt('[_1] set to "[_2]"',$title->{$item},$value).'
      • '; - $mailmsgtext .= "$title->{$item} set to $value\n"; + $mailmsgtext .= "$title->{$item} set to $value\n"; + if ($item eq 'portal_def') { + if ($env{'form.'.$item} ne '') { + foreach my $field ('email','web') { + $value = $env{'form.'.$item.'_'.$field}; + if ($value) { + $value = &mt('Yes'); + } else { + $value = &mt('No'); + } + $resulttext .= '
      • '.&mt('[_1] set to "[_2]"',$title->{$field},$value).'
      • '; + } + } + } } } $resulttext .= '
      '; @@ -20070,12 +20065,15 @@ 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) = - &publishlogo($r,'upload','scantronformat',$dom, - $confname,'scantron','','',$custom); + &Apache::lonconfigsettings::publishlogo($r,'upload','scantronformat',$dom, + $confname,'scantron','','',$custom, + $modified); 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); } @@ -20995,14 +20993,18 @@ sub modify_coursedefaults { my @toggles = ('canuse_pdfforms','uselcmath','usejsme','inline_chem','ltiauth'); my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial', 'uploadquota_community','uploadquota_textbook','uploadquota_placement', - 'mysqltables_official','mysqltables_unofficial','mysqltables_community', - 'mysqltables_textbook','mysqltables_placement'); + 'coursequota_official','coursequota_unofficial','coursequota_community', + 'coursequota_textbook','coursequota_placement','mysqltables_official', + 'mysqltables_unofficial','mysqltables_community','mysqltables_textbook', + 'mysqltables_placement'); my @types = ('official','unofficial','community','textbook','placement'); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, + coursequota => 20, postsubmit => 60, mysqltables => 172800, + domexttool => 1, ); my %texoptions = ( MathJax => 'MathJax', @@ -21047,7 +21049,7 @@ sub modify_coursedefaults { } $defaultshash{'coursedefaults'}{$item} = $newdef; } else { - my ($setting,$type) = ($item =~ /^(uploadquota|mysqltables)_(\w+)$/); + my ($setting,$type) = ($item =~ /^(uploadquota|coursequota|mysqltables)_(\w+)$/); if (ref($domconfig{'coursedefaults'}{$setting}) eq 'HASH') { $currdef = $domconfig{'coursedefaults'}{$setting}{$type}; } @@ -21059,7 +21061,7 @@ sub modify_coursedefaults { unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) { $changes{$item} = 1; } - } elsif ($item =~ /^(uploadquota|mysqltables)_/) { + } elsif ($item =~ /^(uploadquota|coursequota|mysqltables)_/) { my $setting = $1; unless (($currdef eq '') && ($newdef == $staticdefaults{$setting})) { $changes{$setting} = 1; @@ -21194,6 +21196,47 @@ sub modify_coursedefaults { $changes{'postsubmit'} = 1; } } + my (%newdomexttool,%newexttool,%olddomexttool,%oldexttool); + map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool'); + map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool'); + 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; + } + } + } + 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; } my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); @@ -21203,8 +21246,10 @@ sub modify_coursedefaults { 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'})) { - foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine','ltiauth') { + ($changes{'inline_chem'}) || ($changes{'ltiauth'}) || ($changes{'domexttool'}) || + ($changes{'exttool'}) || ($changes{'coursequota'})) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine', + 'ltiauth') { if ($changes{$item}) { $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; } @@ -21235,6 +21280,13 @@ 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') { @@ -21247,6 +21299,20 @@ 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}; + } + } + } my $cachetime = 24*60*60; &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); if (ref($lastactref) eq 'HASH') { @@ -21299,6 +21365,19 @@ sub modify_coursedefaults { } else { $resulttext .= '
    • '.&mt('Default quota for content uploaded via Course Editor remains default: [_1] MB',$staticdefaults{'uploadquota'}).'
    • '; } + } elsif ($item eq 'coursequota') { + if (ref($defaultshash{'coursedefaults'}{'coursequota'}) eq 'HASH') { + $resulttext .= '
    • '.&mt('Default cumulative quota for all group portfolio spaces in course set as follows:').'
        '. + '
      • '.&mt('Official courses: [_1] MB',''.$defaultshash{'coursedefaults'}{'coursequota'}{'official'}.'').'
      • '. + '
      • '.&mt('Unofficial courses: [_1] MB',''.$defaultshash{'coursedefaults'}{'coursequota'}{'unofficial'}.'').'
      • '. + '
      • '.&mt('Textbook courses: [_1] MB',''.$defaultshash{'coursedefaults'}{'coursequota'}{'textbook'}.'').'
      • '. + '
      • '.&mt('Placement tests: [_1] MB',''.$defaultshash{'coursedefaults'}{'coursequota'}{'placement'}.'').'
      • '. + '
      • '.&mt('Communities: [_1] MB',''.$defaultshash{'coursedefaults'}{'coursequota'}{'community'}.'').'
      • '. + '
      '. + '
    • '; + } else { + $resulttext .= '
    • '.&mt('Default cumulative quota for all group portfolio spaces in course remains default: [_1] MB',$staticdefaults{'coursequota'}).'
    • '; + } } elsif ($item eq 'mysqltables') { if (ref($defaultshash{'coursedefaults'}{'mysqltables'}) eq 'HASH') { $resulttext .= '
    • '.&mt('Lifetime of "Temporary" MySQL tables (student performance data) on homeserver').'
        '. @@ -21384,6 +21463,34 @@ sub modify_coursedefaults { } else { $resulttext .= '
      • '.&mt('LTI launch of deep-linked URL will require re-authentication').'
      • '; } + } elsif ($item eq 'domexttool') { + my @noyes = (&mt('no'),&mt('yes')); + if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') { + $resulttext .= '
      • '.&mt('External Tools defined in the domain may be used as follows:').'
          '. + '
        • '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'official'}].'').'
        • '. + '
        • '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'unofficial'}].'').'
        • '. + '
        • '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'textbook'}].'').'
        • '. + '
        • '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'placement'}].'').'
        • '. + '
        • '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'community'}].'').'
        • '. + '
        '. + '
      • '; + } else { + $resulttext .= '
      • '.&mt('External Tools defined in the domain may be used in all course types, by default').'
      • '; + } + } elsif ($item eq 'exttool') { + my @noyes = (&mt('no'),&mt('yes')); + if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') { + $resulttext .= '
      • '.&mt('External Tools can be defined and configured in course containers as follows:').'
          '. + '
        • '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'official'}].'').'
        • '. + '
        • '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'unofficial'}].'').'
        • '. + '
        • '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'textbook'}].'').'
        • '. + '
        • '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'placement'}].'').'
        • '. + '
        • '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'community'}].'').'
        • '. + '
        '. + '
      • '; + } else { + $resulttext .= '
      • '.&mt('External Tools can not be defined in any course types, by default').'
      • '; + } } } $resulttext .= '
      '; @@ -21777,7 +21884,7 @@ sub modify_wafproxy { } } elsif ($currvalue{$item}) { $changes{$item} = 1; - } + } } } else { if (keys(%curralias)) { @@ -21785,7 +21892,7 @@ sub modify_wafproxy { } if (keys(%currsaml)) { $changes{'saml'} = 1; - } + } if (keys(%currvalue)) { foreach my $key (keys(%currvalue)) { $changes{$key} = 1; @@ -21795,7 +21902,7 @@ sub modify_wafproxy { if (keys(%changes)) { my %defaultshash = ( wafproxy => \%wafproxy, - ); + ); my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); if ($putresult eq 'ok') { @@ -21811,7 +21918,7 @@ sub modify_wafproxy { $domdefaults{'waf_'.$item} = $wafproxy{$item}; } elsif (exists($domdefaults{'waf_'.$item})) { delete($domdefaults{'waf_'.$item}); - } + } } } if ($updatedomdefs) { @@ -21878,7 +21985,7 @@ sub modify_wafproxy { $output .= '
    • '.&mt('Aliases deleted for hostnames').'
    • '; } } elsif ($item eq 'saml') { - my $shown; + my $shown; if (ref($wafproxy{'saml'}) eq 'HASH') { if (keys(%{$wafproxy{'saml'}})) { $shown = join(', ',sort(keys(%{$wafproxy{'saml'}}))); @@ -21942,6 +22049,7 @@ sub modify_wafproxy { } } } + $output .= '
    '; } else { $output = ''. &mt('An error occurred: [_1]',$putresult).''; @@ -22207,7 +22315,7 @@ sub modify_usersessions { if (($offload eq 'offloadoth') && (@okoffloadoth)) { $changes{'offloadoth'} = 1; } - } + } } } else { if (@okoffload) { @@ -22624,8 +22732,10 @@ sub modify_trust { } my $cachetime = 24*60*60; &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + &Apache::lonnet::do_cache_new('trust',$dom,$defaultshash{'trust'},3600); if (ref($lastactref) eq 'HASH') { $lastactref->{'domdefaults'} = 1; + $lastactref->{'trust'} = 1; } if (keys(%changes) > 0) { my %lt = &trust_titles(); @@ -22743,7 +22853,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; } @@ -23429,7 +23539,6 @@ function balancerChange(balnum,baltotal, END } - sub new_spares_js { my @sparestypes = ('primary','default'); my $types = join("','",@sparestypes); @@ -23694,7 +23803,7 @@ sub devalidate_remote_domconfs { map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); my @posscached = ('domainconfig','domdefaults','ltitools','usersessions', 'directorysrch','passwdconf','cats','proxyalias','proxysaml', - 'ipaccess'); + 'ipaccess','trust'); my %cache_by_lonhost; if (exists($cachekeys->{'samllanding'})) { if (ref($cachekeys->{'samllanding'}) eq 'HASH') {