--- loncom/interface/domainprefs.pm 2022/02/06 21:36:59 1.404 +++ loncom/interface/domainprefs.pm 2024/02/24 23:41:44 1.434 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.404 2022/02/06 21:36:59 raeburn Exp $ +# $Id: domainprefs.pm,v 1.434 2024/02/24 23:41:44 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','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'],$dom,undef,1); + &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,11 +250,39 @@ 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'}{'linkprot'}) eq 'HASH') { + if (ref($encconfig{'linkprot'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'ltisec'}{'linkprot'}})) { + unless ($id =~ /^\d+$/) { + delete($domconfig{'ltisec'}{'linkprot'}{$id}); + } + if ((ref($domconfig{'ltisec'}{'linkprot'}{$id}) eq 'HASH') && + (ref($encconfig{'linkprot'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'ltisec'}{'linkprot'}{$id}{$item} = $encconfig{'linkprot'}{$id}{$item}; + } } } } @@ -265,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'}}; @@ -310,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, }, @@ -340,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, }, @@ -526,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'}, @@ -562,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, }, @@ -616,15 +662,23 @@ sub handler { print => \&print_trust, modify => \&modify_trust, }, - 'lti' => - {text => 'LTI Provider', + 'lti' => + {text => 'LTI Link Protection and LTI Consumers', help => 'Domain_Configuration_LTI_Provider', - header => [{col1 => 'Setting', - col2 => 'Value',}], + header => [{col1 => 'Encryption of shared secrets', + col2 => 'Settings'}, + {col1 => 'Rules for shared secrets', + col2 => 'Settings'}, + {col1 => 'Link Protectors in Courses', + col2 => 'Values'}, + {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', @@ -632,6 +686,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', @@ -639,7 +703,7 @@ sub handler { header => [{col1 => 'Log-in Service', col2 => 'Server Setting',}, {col1 => 'Log-in Page Items', - col2 => ''}, + col2 => 'Settings'}, {col1 => 'Log-in Help', col2 => 'Value'}, {col1 => 'Custom HTML in document head', @@ -830,13 +894,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; } @@ -850,7 +916,7 @@ sub print_config_box { } elsif ($action eq 'defaults') { $output = &defaults_javascript($settings); } elsif ($action eq 'passwords') { - $output = &passwords_javascript(); + $output = &passwords_javascript($action); } elsif ($action eq 'helpsettings') { my (%privs,%levelscurrent); my %full=(); @@ -865,12 +931,13 @@ 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 .= <i_javascript($settings); + $output .= &passwords_javascript('ltisecrets')."\n". + <i_javascript($dom,$settings); } elsif ($action eq 'proctoring') { $output .= &proctoring_javascript($settings); } elsif ($action eq 'wafproxy') { @@ -883,6 +950,8 @@ sub print_config_box { $output .= &saml_javascript(); } elsif ($action eq 'ipaccess') { $output .= &ipaccess_javascript($settings); + } elsif ($action eq 'authordefaults') { + $output .= &authordefaults_javascript(); } $output .= ' @@ -924,7 +993,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 '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); @@ -959,8 +1029,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 '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"'; @@ -968,6 +1039,19 @@ sub print_config_box { $output .= $item->{'print'}->('shared',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('middle',$dom,$confname,$settings,\$rowtotal); + } elsif ($action eq 'lti') { + $output .= $item->{'print'}->('upper',$dom,$settings,\$rowtotal).' +
+ + + + + + + + + '."\n". + $item->{'print'}->('middle',$dom,$settings,\$rowtotal); } else { $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal); } @@ -1000,6 +1084,10 @@ sub print_config_box { '. $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } else { + my $hdridx = 2; + if ($action eq 'lti') { + $hdridx = 3; + } $output .= '
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
'.&mt($item->{'header'}->[8]->{'col2'}).'
@@ -1008,17 +1096,19 @@ sub print_config_box { - - + + '."\n"; if ($action eq 'coursecategories') { $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal); - } elsif (($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'passwords')) { + } elsif (($action eq 'contacts') || ($action eq 'privacy') || + ($action eq 'passwords') || ($action eq 'lti')) { if ($action eq 'passwords') { $output .= $item->{'print'}->('lower',$dom,$confname,$settings,\$rowtotal); } else { $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal); } + $hdridx ++; $output .= '
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).''.&mt($item->{'header'}->[$hdridx]->{'col1'}).''.&mt($item->{'header'}->[$hdridx]->{'col2'}).'
@@ -1028,8 +1118,8 @@ sub print_config_box { - - '."\n"; + + '."\n"; if ($action eq 'passwords') { $output .= $item->{'print'}->('bottom',$dom,$confname,$settings,\$rowtotal); } else { @@ -1046,8 +1136,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); @@ -1250,7 +1340,6 @@ 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 'lti') || ($action eq 'proctoring') || ($action eq 'ipaccess')) { $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } @@ -1601,13 +1690,13 @@ sub print_login { '
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
'.&mt($item->{'header'}->[$hdridx]->{'col1'}).''.&mt($item->{'header'}->[$hdridx]->{'col2'}).'
'. ''. ''."\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; @@ -1616,6 +1705,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'; @@ -1633,6 +1723,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 .= ''. ''. ''. ''; @@ -2329,7 +2431,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); @@ -2343,12 +2445,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') { @@ -2357,9 +2459,6 @@ sub print_quotas { } else { $currdefquota = $settings->{$type}; } - if (ref($settings->{authorquota}) eq 'HASH') { - $currauthorquota = $settings->{authorquota}->{$type}; - } } } if (defined($usertypes->{$type})) { @@ -2447,9 +2546,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" '; @@ -2474,13 +2576,9 @@ sub print_quotas { ($context eq 'requestauthor')) { $datatable .= ''; } $datatable .= ''; @@ -2489,16 +2587,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 ++; @@ -2610,12 +2704,9 @@ sub print_quotas { $datatable .= ''; unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { $datatable .= ''; + $defaultquota.'" size="5" />'; } $datatable .= ''; $typecount ++; @@ -3386,10 +3477,19 @@ ENDSCRIPT sub lti_javascript { - my ($settings) = @_; - my $togglejs = <i_toggle_js(); + 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})); @@ -3451,6 +3551,9 @@ $jstext } return; } + +$linkprot_js + // ]]> @@ -3460,12 +3563,16 @@ ENDSCRIPT } sub lti_toggle_js { + my ($dom) = @_; my %lcauthparmtext = &Apache::lonlocal::texthash ( localauth => 'Local auth argument', krb => 'Kerberos domain', ); my $crsincalert = &mt('"User\'s identity sent" needs to be set to "Yes" first,[_1] before setting "Course\'s identity sent" to "Yes"',"\n"); &js_escape(\$crsincalert); + my %servers = &Apache::lonnet::get_servers($dom,'library'); + my $primary = &Apache::lonnet::domain($dom,'primary'); + my $course_servers = "'".join("','",keys(%servers))."'"; return <<"ENDSCRIPT"; @@ -3748,16 +3856,16 @@ sub saml_javascript { return <<"ENDSCRIPT"; + +ENDSCRIPT +} + sub print_autoenroll { my ($dom,$settings,$rowtotal) = @_; my $autorun = &Apache::lonnet::auto_run(undef,$dom), @@ -4525,7 +4666,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; } @@ -5647,7 +5415,7 @@ sub ltitools_names { 'key' => 'Key', 'lifetime' => 'Nonce lifetime (s)', 'secret' => 'Secret', - 'icon' => 'Icon', + 'icon' => 'Icon', 'user' => 'User', 'fullname' => 'Full Name', 'firstname' => 'First Name', @@ -5665,7 +5433,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', @@ -5673,6 +5441,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; @@ -6308,144 +6193,256 @@ sub proctoring_providernames { } sub print_lti { - my ($dom,$settings,$rowtotal) = @_; + my ($position,$dom,$settings,$rowtotal) = @_; my $itemcount = 1; - my $maxnum = 0; - my $css_class; - my %ordered; + my ($datatable,$css_class); + my (%rules,%encrypt,%privkeys,%linkprot,%suggestions); if (ref($settings) eq 'HASH') { - foreach my $item (keys(%{$settings})) { - if (ref($settings->{$item}) eq 'HASH') { - my $num = $settings->{$item}{'order'}; - if ($num eq '') { - $num = scalar(keys(%{$settings})); + if ($position eq 'top') { + if (exists($settings->{'encrypt'})) { + if (ref($settings->{'encrypt'}) eq 'HASH') { + foreach my $key (keys(%{$settings->{'encrypt'}})) { + if ($key eq 'consumers') { + $encrypt{'ltisec_'.$key} = $settings->{'encrypt'}{$key}; + } else { + $encrypt{'ltisec_'.$key.'linkprot'} = $settings->{'encrypt'}{$key}; + } + } + } + } + if (exists($settings->{'private'})) { + if (ref($settings->{'private'}) eq 'HASH') { + if (ref($settings->{'private'}) eq 'HASH') { + if (ref($settings->{'private'}{'keys'}) eq 'ARRAY') { + map { $privkeys{$_} = 1; } (@{$settings->{'private'}{'keys'}}); + } + } + } + } + } elsif ($position eq 'upper') { + if (exists($settings->{'rules'})) { + if (ref($settings->{'rules'}) eq 'HASH') { + %rules = %{$settings->{'rules'}}; + } + } + } elsif ($position eq 'middle') { + if (exists($settings->{'suggested'})) { + if (ref($settings->{'suggested'}) eq 'HASH') { + %suggestions = %{$settings->{'suggested'}}; + } + } + } elsif ($position eq 'lower') { + if (exists($settings->{'linkprot'})) { + if (ref($settings->{'linkprot'}) eq 'HASH') { + %linkprot = %{$settings->{'linkprot'}}; + if ($linkprot{'lock'}) { + delete($linkprot{'lock'}); + } + } + } + } else { + foreach my $key ('encrypt','private','rules','linkprot','suggestions') { + if (exists($settings->{$key})) { + delete($settings->{$key}); } - $ordered{$num} = $item; } } } - my $maxnum = scalar(keys(%ordered)); - my $datatable; - my %lt = <i_names(); - if (keys(%ordered)) { - my @items = sort { $a <=> $b } keys(%ordered); - for (my $i=0; $i<@items; $i++) { - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $item = $ordered{$items[$i]}; - my ($key,$secret,$lifetime,$consumer,$requser,$crsinc,$current); - if (ref($settings->{$item}) eq 'HASH') { - $key = $settings->{$item}->{'key'}; - $secret = $settings->{$item}->{'secret'}; - $lifetime = $settings->{$item}->{'lifetime'}; - $consumer = $settings->{$item}->{'consumer'}; - $requser = $settings->{$item}->{'requser'}; - $crsinc = $settings->{$item}->{'crsinc'}; - $current = $settings->{$item}; - } - my $onclickrequser = ' onclick="toggleLTI(this.form,'."'requser','$i'".');"'; - my %checkedrequser = ( - yes => ' checked="checked"', - no => '', - ); - if (!$requser) { - $checkedrequser{'no'} = $checkedrequser{'yes'}; - $checkedrequser{'yes'} = ''; + if ($position eq 'top') { + $datatable = &secrets_form($dom,'ltisec',\%encrypt,\%privkeys,$rowtotal); + } elsif ($position eq 'upper') { + $datatable = &password_rules('ltisecrets',\$itemcount,\%rules); + $$rowtotal += $itemcount; + } elsif ($position eq 'middle') { + $datatable = &linkprot_suggestions(\%suggestions,\$itemcount); + $$rowtotal += $itemcount; + } elsif ($position eq 'lower') { + $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') { + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + if ($num eq '') { + $num = scalar(keys(%{$settings})); + } + $ordered{$num} = $item; + } } - my $onclickcrsinc = ' onclick="toggleLTI(this.form,'."'crsinc','$i'".');"'; - my %checkedcrsinc = ( + } + $maxnum = scalar(keys(%ordered)); + my %lt = <i_names(); + if (keys(%ordered)) { + my @items = sort { $a <=> $b } keys(%ordered); + for (my $i=0; $i<@items; $i++) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $item = $ordered{$items[$i]}; + my ($key,$secret,$usable,$lifetime,$consumer,$requser,$crsinc,$current); + if (ref($settings->{$item}) eq 'HASH') { + $key = $settings->{$item}->{'key'}; + $usable = $settings->{$item}->{'usable'}; + $lifetime = $settings->{$item}->{'lifetime'}; + $consumer = $settings->{$item}->{'consumer'}; + $requser = $settings->{$item}->{'requser'}; + $crsinc = $settings->{$item}->{'crsinc'}; + $current = $settings->{$item}; + } + my $onclickrequser = ' onclick="toggleLTI(this.form,'."'requser','$i'".');"'; + my %checkedrequser = ( + yes => ' checked="checked"', + no => '', + ); + if (!$requser) { + $checkedrequser{'no'} = $checkedrequser{'yes'}; + $checkedrequser{'yes'} = ''; + } + my $onclickcrsinc = ' onclick="toggleLTI(this.form,'."'crsinc','$i'".');"'; + my %checkedcrsinc = ( yes => ' checked="checked"', no => '', - ); - if (!$crsinc) { - $checkedcrsinc{'no'} = $checkedcrsinc{'yes'}; - $checkedcrsinc{'yes'} = ''; - } - my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_".$item."'".');"'; - $datatable .= ''. + ''; + $itemcount ++; } - $datatable .= ''.(' 'x2). - ''. - ''; - $itemcount ++; } - } - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_add'".');"'; - $datatable .= ''."\n". + ''."\n". + ''."\n"; + $itemcount ++; } - $datatable .= ' '."\n". - ''.&mt('Add').''."\n". - ''."\n". - ''."\n"; - $$rowtotal ++; - return $datatable;; + $$rowtotal += $itemcount; + return $datatable; } sub lti_names { @@ -6511,7 +6508,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'}; @@ -6521,7 +6518,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'}; @@ -6549,7 +6546,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 { @@ -6566,7 +6563,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"'; @@ -6612,9 +6609,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 ( @@ -6667,7 +6664,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').''. @@ -6799,9 +6796,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})) { @@ -6847,6 +6844,58 @@ sub ltimenu_titles { ); } +sub linkprot_suggestions { + my ($suggested,$itemcount) = @_; + my $count = 0; + my $next = 1; + my %lt = &Apache::lonlocal::texthash( + 'name' => 'Suggested Launcher', + 'info' => 'Recommendations', + ); + my ($datatable,$css_class,$dest); + if (ref($suggested) eq 'HASH') { + my @current = sort { $a <=> $b } keys(%{$suggested}); + $next += $current[-1]; + for (my $i=0; $i<@current; $i++) { + my $num = $current[$i]; + my %values; + if (ref($suggested->{$num}) eq 'HASH') { + %values = %{$suggested->{$num}}; + } else { + next; + } + $css_class = $$itemcount%2?' class="LC_odd_row"':''; + $datatable .= + '
'."\n"; + $$itemcount ++; + } + } + $css_class = $$itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''."\n". + ''."\n"; + return $datatable; +} + sub print_coursedefaults { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles); @@ -6854,6 +6903,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 (MB)', anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys', coursecredits => 'Credits can be specified for courses', uselcmath => 'Math preview uses LON-CAPA previewer (javascript) in place of DragMath (Java)', @@ -6863,13 +6913,20 @@ sub print_coursedefaults { postsubmit => 'Disable submit button/keypress following student submission', canclone => "People who may clone a course (besides course's owner and coordinators)", mysqltables => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver', - ltiauth => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication', + 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)', + crsauthor => 'Standard LON-CAPA problems can be created within a course/community (by type)', ); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, + coursequota => 20, postsubmit => 60, mysqltables => 172800, + domexttool => 1, + exttool => 0, + crsauthor => 1, ); if ($position eq 'top') { %defaultchecked = ( @@ -6983,21 +7040,63 @@ 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 $ltiauth = 0; + my %domexttool; + my %exttool; + my %crsauthor; my @types = ('official','unofficial','community','textbook','placement'); if (ref($settings) eq 'HASH') { if ($settings->{'ltiauth'}) { $ltiauth = 1; - } + } + if (ref($settings->{'domexttool'}) eq 'HASH') { + foreach my $type (@types) { + if ($settings->{'domexttool'}->{$type}) { + $domexttool{$type} = ' checked="checked"'; + } + } + } else { + foreach my $type (@types) { + if ($staticdefaults{'domexttool'}) { + $domexttool{$type} = ' checked="checked"'; + } + } + } + if (ref($settings->{'exttool'}) eq 'HASH') { + foreach my $type (@types) { + if ($settings->{'exttool'}->{$type}) { + $exttool{$type} = ' checked="checked"'; + } + } + } + if (ref($settings->{'crsauthor'}) eq 'HASH') { + foreach my $type (@types) { + if ($settings->{'crsauthor'}->{$type}) { + $crsauthor{$type} = ' checked="checked"'; + } + } + } else { + foreach my $type (@types) { + if ($staticdefaults{'crsauthor'}) { + $crsauthor{$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'); @@ -7043,6 +7142,12 @@ sub print_coursedefaults { } else { foreach my $type (@types) { $deftimeout{$type} = $staticdefaults{'postsubmit'}; + if ($staticdefaults{'domexttool'}) { + $domexttool{$type} = ' checked="checked"'; + } + if ($staticdefaults{'crsauthor'}) { + $crsauthor{$type} = ' checked="checked"'; + } } } if (!$currdefresponder) { @@ -7054,6 +7159,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 .= '
'. @@ -1664,13 +1758,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').': '. - '
'. @@ -4607,7 +4748,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}); } @@ -4619,7 +4760,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); @@ -4686,7 +4826,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. + '
' - .'
' + .''.(' 'x2). + ''. + '
'.&mt('Required settings').''. + ''.$lt{'consumer'}. + ': '. + (' 'x2). + ''.$lt{'version'}.': '. + (' 'x2). + ''.$lt{'lifetime'}.':

'; + 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". + '

'. + ''.$lt{'crsinc'}.':'. + ' '."\n". + ''."\n". + (' 'x4). + '
'. + '
'.<i_options($i,$current,$itemcount,%lt).'
'. - '
'.&mt('Required settings').''. - ''.$lt{'consumer'}. - ': '. - (' 'x2). - ''.$lt{'version'}.': '. - (' 'x2). - ''.$lt{'lifetime'}.':'. - (' 'x2). - ''.$lt{'requser'}.':'. - ' '."\n". - ''."\n". - '

'. - ''.$lt{'crsinc'}.':'. - ' '."\n". - ''."\n". - (' 'x4). - ''.$lt{'key'}. - ': '. - (' 'x2). - ''.$lt{'secret'}.':'. - ''. - ''. - ''. - '
'.<i_options($i,$current,$itemcount,%lt).'
'."\n". - ''."\n". - '
'."\n". + ''."\n". + ' '."\n". + ''.&mt('Add').''. + '
'.&mt('Required settings').''. + ''.$lt{'consumer'}. + ': '."\n". + (' 'x2). + ''.$lt{'version'}.': '."\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". + '

'. + ''.$lt{'crsinc'}.':'. + ' '."\n". + ''."\n". + '
'.<i_options('add',undef,$itemcount,%lt). + '
'. - '
'.&mt('Required settings').''. - ''.$lt{'consumer'}. - ': '."\n". - (' 'x2). - ''.$lt{'version'}.': '."\n". - (' 'x2). - ''.$lt{'lifetime'}.': '."\n". - (' 'x2). - ''.$lt{'requser'}.':'. - ' '."\n". - ''."\n". - '

'. - ''.$lt{'crsinc'}.':'. - ' '."\n". - ''."\n". - (' 'x4). - ''.$lt{'key'}.': '."\n". - (' 'x2). - ''.$lt{'secret'}.':'. - ' '."\n". - '
'.<i_options('add',undef,$itemcount,%lt). - '
'."\n". + ''."\n". + '
'.$lt{'name'}.''."\n". + ''."\n". + '
'. + '
'.$lt{'info'}.''."\n". + ''. + '
'. + '
'."\n". + '
'."\n". + ''."\n". + ''.&mt('Add').''."\n". + '
'.$lt{'name'}.''."\n". + ''."\n". + '
'. + '
'.$lt{'info'}.''."\n". + ''. + '
'. + '
'."\n". + '
'. @@ -7077,6 +7185,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) { @@ -7146,12 +7267,263 @@ 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"; + $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $choices{'crsauthor'}. + ''. + ''. + ''; + 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); @@ -7381,13 +7753,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) = @@ -7406,6 +7780,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', @@ -7457,6 +7832,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')) { @@ -7800,95 +8197,7 @@ sub print_passwords { $itemcount ++; } } elsif ($position eq 'lower') { - my ($min,$max,%chars,$expire,$numsaved); - $min = $Apache::lonnet::passwdmin; - if (ref($settings) eq 'HASH') { - if ($settings->{min}) { - $min = $settings->{min}; - } - if ($settings->{max}) { - $max = $settings->{max}; - } - if (ref($settings->{chars}) eq 'ARRAY') { - map { $chars{$_} = 1; } (@{$settings->{chars}}); - } - if ($settings->{expire}) { - $expire = $settings->{expire}; - } - if ($settings->{numsaved}) { - $numsaved = $settings->{numsaved}; - } - } - my %rulenames = &Apache::lonlocal::texthash( - uc => 'At least one upper case letter', - lc => 'At least one lower case letter', - num => 'At least one number', - spec => 'At least one non-alphanumeric', - ); - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= ''.$titles{'min'}.''. - ''. - ''. - ' '.&mt('(Enter an integer: 7 or larger)').''. - ''; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= ''.$titles{'max'}.''. - ''. - ''. - ' '.&mt('(Leave blank for no maximum)').''. - ''; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= ''.$titles{'chars'}.'
'. - ''.&mt('(Leave unchecked if not required)'). - ''; - my $numinrow = 2; - my @possrules = ('uc','lc','num','spec'); - $datatable .= ''; - for (my $i=0; $i<@possrules; $i++) { - my ($rem,$checked); - if ($chars{$possrules[$i]}) { - $checked = ' checked="checked"'; - } - $rem = $i%($numinrow); - if ($rem == 0) { - if ($i > 0) { - $datatable .= ''; - } - $datatable .= ''; - } - $datatable .= ''; - } - my $rem = @possrules%($numinrow); - my $colsleft = $numinrow - $rem; - if ($colsleft > 1 ) { - $datatable .= ''; - } elsif ($colsleft == 1) { - $datatable .= ''; - } - $datatable .='
'. - '  
'; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= ''.$titles{'expire'}.''. - ''. - ''. - ' '.&mt('(Leave blank for no expiration)').''. - ''; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= ''.$titles{'numsaved'}.''. - ''. - ''. - ' '.&mt('(Leave blank to not save previous passwords)').''. - ''; + $datatable .= &password_rules('passwords',\$itemcount,$settings); } else { my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my %ownerchg = ( @@ -7948,6 +8257,129 @@ sub print_passwords { return $datatable; } +sub password_rules { + my ($prefix,$itemcountref,$settings) = @_; + my ($min,$max,%chars,$expire,$numsaved,$numinrow); + my %titles; + if ($prefix eq 'passwords') { + %titles = &Apache::lonlocal::texthash ( + min => 'Minimum password length', + max => 'Maximum password length', + chars => 'Required characters', + ); + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { + %titles = &Apache::lonlocal::texthash ( + min => 'Minimum secret length', + max => 'Maximum secret length', + chars => 'Required characters', + ); + } + $min = $Apache::lonnet::passwdmin; + my $datatable; + my $itemcount; + if (ref($itemcountref)) { + $itemcount = $$itemcountref; + } + if (ref($settings) eq 'HASH') { + if ($settings->{min}) { + $min = $settings->{min}; + } + if ($settings->{max}) { + $max = $settings->{max}; + } + if (ref($settings->{chars}) eq 'ARRAY') { + map { $chars{$_} = 1; } (@{$settings->{chars}}); + } + if ($prefix eq 'passwords') { + if ($settings->{expire}) { + $expire = $settings->{expire}; + } + if ($settings->{numsaved}) { + $numsaved = $settings->{numsaved}; + } + } + } + my %rulenames = &Apache::lonlocal::texthash( + uc => 'At least one upper case letter', + lc => 'At least one lower case letter', + num => 'At least one number', + spec => 'At least one non-alphanumeric', + ); + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{'min'}.''. + ''. + ''. + ' '.&mt('(Enter an integer: 7 or larger)').''. + ''; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{'max'}.''. + ''. + ''. + ' '.&mt('(Leave blank for no maximum)').''. + ''; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{'chars'}.'
'. + ''.&mt('(Leave unchecked if not required)'). + ''; + my $numinrow = 2; + my @possrules = ('uc','lc','num','spec'); + $datatable .= ''; + for (my $i=0; $i<@possrules; $i++) { + my ($rem,$checked); + if ($chars{$possrules[$i]}) { + $checked = ' checked="checked"'; + } + $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $datatable .= ''; + } + $datatable .= ''; + } + $datatable .= ''; + } + my $rem = @possrules%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $datatable .= ''; + } elsif ($colsleft == 1) { + $datatable .= ''; + } + $datatable .='
'. + '  
'; + $itemcount ++; + if ($prefix eq 'passwords') { + $titles{'expire'} = &mt('Password expiration (days)'); + $titles{'numsaved'} = &mt('Number of previous passwords to save and disallow reuse'); + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{'expire'}.''. + ''. + ''. + ' '.&mt('(Leave blank for no expiration)').''. + ''; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{'numsaved'}.''. + ''. + ''. + ' '.&mt('(Leave blank to not save previous passwords)').''. + ''; + $itemcount ++; + } + if (ref($itemcountref)) { + $$itemcountref += $itemcount; + } + return $datatable; +} + sub print_wafproxy { my ($position,$dom,$settings,$rowtotal) = @_; my $css_class; @@ -8001,7 +8433,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') { @@ -8018,8 +8450,10 @@ sub print_wafproxy { my $dom_in_effect; my $aliasrows = ''. ''. - &mt('Hostname').': '. - ''.&Apache::lonnet::hostname($server).' '; + &mt('Hostname').': '. + ''. + &Apache::lonnet::hostname($server). + ' '; if ($othercontrol{$server}) { $dom_in_effect = $othercontrol{$server}; my ($current,$forsaml); @@ -8066,10 +8500,10 @@ sub print_wafproxy { (' 'x2).''. &mt('Alias used for SSO Auth').':   '. - ''; + ''; } $aliasrows .= ''; $aliasinfo{$dom_in_effect} .= $aliasrows; @@ -8979,7 +9413,7 @@ sub print_loadbalancing { no => ' checked="checked"', ); my %balcookiechecked = ( - no => ' checked="checked"', + no => ' checked="checked"', ); foreach my $sparetype (@sparestypes) { my $targettable; @@ -9334,8 +9768,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', @@ -9565,7 +10000,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, @@ -9702,7 +10137,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, @@ -9729,8 +10164,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, @@ -9832,7 +10267,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') { @@ -9855,10 +10290,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.'"'; @@ -9873,10 +10308,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', @@ -10001,7 +10436,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 '') { @@ -10117,7 +10552,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 .= '
'. @@ -10172,9 +10607,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; } @@ -10306,18 +10741,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')) { @@ -10367,6 +10818,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; @@ -10392,6 +10859,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', @@ -10624,10 +11093,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); } } @@ -11106,6 +11578,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 '') { @@ -11159,6 +11648,17 @@ $jstext return; } +$portal_js + +// ]]> + + +ENDSCRIPT + } else { +return <<"ENDSCRIPT"; + @@ -11168,17 +11668,27 @@ ENDSCRIPT } sub passwords_javascript { - my %intalert = &Apache::lonlocal::texthash ( - authcheck => 'Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.', - authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.', - passmin => 'Warning: minimum password length must be a positive integer greater than 6.', - passmax => 'Warning: maximum password length must be a positive integer (or blank).', - passexp => 'Warning: days before password expiration must be a positive integer (or blank).', - passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).', - ); + my ($prefix) = @_; + my %intalert; + if ($prefix eq 'passwords') { + %intalert = &Apache::lonlocal::texthash ( + authcheck => 'Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.', + authcost => 'Warning: bcrypt encryption cost for internal authentication must be an integer.', + passmin => 'Warning: minimum password length must be a positive integer greater than 6.', + passmax => 'Warning: maximum password length must be a positive integer (or blank).', + passexp => 'Warning: days before password expiration must be a positive integer (or blank).', + passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).', + ); + } elsif (($prefix eq '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).', + ); + } &js_escape(\%intalert); my $defmin = $Apache::lonnet::passwdmin; - my $intauthjs = <<"ENDSCRIPT"; + my $intauthjs; + if ($prefix eq 'passwords') { $intauthjs = <<"ENDSCRIPT"; function warnIntAuth(field) { if (field.name == 'intauth_check') { @@ -11198,11 +11708,17 @@ function warnIntAuth(field) { return; } -function warnIntPass(field) { +ENDSCRIPT + + } + + $intauthjs .= <<"ENDSCRIPT"; + +function warnInt$prefix(field) { field.value.replace(/^\s+/,''); field.value.replace(/\s+\$/,''); var regexdigit=/^\\d+\$/; - if (field.name == 'passwords_min') { + if (field.name == '${prefix}_min') { if (field.value == '') { alert('$intalert{passmin}'); field.value = '$defmin'; @@ -11222,7 +11738,7 @@ function warnIntPass(field) { field.value = ''; } if (field.value != '') { - if (field.name == 'passwords_expire') { + if (field.name == '${prefix}_expire') { var regexpposnum=/^\\d+(|\\.\\d*)\$/; if (!regexpposnum.test(field.value)) { alert('$intalert{passexp}'); @@ -11236,10 +11752,10 @@ function warnIntPass(field) { } } else { if (!regexdigit.test(field.value)) { - if (field.name == 'passwords_max') { + if (field.name == '${prefix}_max') { alert('$intalert{passmax}'); } else { - if (field.name == 'passwords_numsaved') { + if (field.name == '${prefix}_numsaved') { alert('$intalert{passnum}'); } } @@ -11565,7 +12081,6 @@ sub modifiable_userdata_row { ''; my $rem; my %checks; - my %current; if (ref($settings) eq 'HASH') { my $hashref; if ($context eq 'lti') { @@ -11591,7 +12106,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') { @@ -11603,7 +12118,6 @@ sub modifiable_userdata_row { } } } - my $total = scalar(@fields); for (my $i=0; $i<$total; $i++) { $rem = $i%($numinrow); @@ -11692,15 +12206,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') { @@ -11737,25 +12255,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); @@ -11767,7 +12304,7 @@ sub insttypes_row { } else { $output .= ''; @@ -11777,20 +12314,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; @@ -11870,7 +12428,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', @@ -11894,6 +12452,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'}; } } @@ -12036,13 +12595,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; @@ -12055,6 +12617,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); } @@ -12112,11 +12675,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; @@ -12133,6 +12699,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); } @@ -12151,10 +12718,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'}); @@ -12172,25 +12742,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; @@ -12201,11 +12774,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; @@ -12216,6 +12792,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); } @@ -12379,19 +12956,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).'
    • '; @@ -12567,7 +13147,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); @@ -12600,9 +13180,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'; @@ -12634,7 +13214,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') { @@ -12714,7 +13294,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').':
      • '. @@ -12782,6 +13362,239 @@ 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)) { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); + if ((exists($changes{'authorquota'})) || (exists($changes{'webdav'})) || + ($changes{'webdav_LC_adv'})) { + if ((exists($changes{'authorquota'})) && (ref($save_quotas{'authorquota'}) eq 'HASH')) { + $domdefaults{'authorquota'} = $save_quotas{'authorquota'}; + } + if (((exists($changes{'webdav'})) || ($changes{'webdav_LC_adv'})) && + (ref($save_quotas{'webdav'}) eq 'HASH')) { + $domdefaults{'webdav'} = $save_quotas{'webdav'}; + } + } + $resulttext = &mt('Changes made:').'
          '; + my $authoroverride; + foreach my $key ('nocodemirror','domcoordacc','copyright','sourceavail') { + if (exists($changes{$key})) { + $domdefaults{$key} = $confhash{$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 (ref($confhash{'editors'}) eq 'ARRAY') { + $domdefaults{'editors'} = join(',',@{$confhash{'editors'}}); + if (@{$confhash{'editors'}}) { + $shown = join(', ', map { $titles{$_} } @{$confhash{'editors'}}); + } else { + $shown = &mt('None'); + } + } + } elsif ($key eq 'authorquota') { + foreach my $type (@insttypes) { + $shown .= $usertypes->{$type}.' -- '.$save_quotas{$key}{$type}.', '; + } + $shown .= $othertitle.' -- '.$save_quotas{$key}{'default'}; + } elsif ($key eq 'webdav') { + foreach my $type (@insttypes) { + $shown .= $usertypes->{$type}.' -- '. ($save_quotas{$key}{$type} ? &mt('Yes') : &mt('No')).', '; + } + $shown .= $othertitle.' -- '. ($save_quotas{$key}{'default'} ? &mt('Yes') : &mt('No')); + } elsif ($key eq 'webdav_LC_adv') { + if (exists($save_quotas{'webdav'}{'_LC_adv'})) { + $shown = ($save_quotas{'webdav'}{'_LC_adv'} ? $titles{'overon'} : $titles{'overoff'}); + } else { + $shown = $titles{'none'}; + } + } + $resulttext .= '
          • '.&mt('[_1] set to: [_2]',$titles{$key},$shown).'
          • '; + } + } + if ($domcoordoverride) { + $resulttext .= '
        • '; + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } else { + $resulttext = &mt('No changes made to Authoring Space defaults'); + } + } + return $resulttext; +} + sub modify_rolecolors { my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_; my ($resulttext,%rolehash); @@ -12917,12 +13730,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); } @@ -12944,12 +13760,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); } } } @@ -13243,229 +14062,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 { @@ -13516,15 +14122,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; } @@ -13536,7 +14148,7 @@ sub modify_quotas { $author_ok,$switchserver,$errors,$validationitemsref,$validationnamesref, $validationfieldsref); if ($action eq 'quotas') { - $context = 'tools'; + $context = 'tools'; } else { $context = $action; } @@ -13556,7 +14168,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); @@ -13579,8 +14191,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); } @@ -13874,7 +14484,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') { @@ -13963,15 +14572,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') { @@ -13991,21 +14595,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') { @@ -14046,19 +14635,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) { @@ -14247,11 +14823,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); } @@ -14266,656 +14845,382 @@ 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; } - 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; + $ltitoolsoutput = &Apache::courseprefs::store_ltitools($dom,'','domain',\%ltitoolschg,$domconfig{'ltitools'}); + if (keys(%ltitoolschg)) { + %newltitools = %ltitoolschg; } - 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; + } elsif ($item eq 'suggested') { + if ((ref($secchanges->{'suggested'}) eq 'HASH') && + (ref($newsec->{'suggested'}) eq 'HASH')) { + my $suggestions; + foreach my $id (sort { $a <=> $b } keys(%{$secchanges->{'suggested'}})) { + if (ref($newsec->{'suggested'}->{$id}) eq 'HASH') { + my $name = $newsec->{'suggested'}->{$id}->{'name'}; + my $info = $newsec->{'suggested'}->{$id}->{'info'}; + $suggestions .= '
        • '.&mt('Launcher: [_1]',$name).'
          '. + &mt('Recommend: [_1]','
          '.$info.'
          '). + '
        • '; + } else { + $suggestions .= '
        • '.&mt('Recommendations deleted for Launcher: [_1]', + $newsec->{'suggested'}->{$id}).'
        • '; + } + } + if ($suggestions) { + $output .= '
        • '.&mt('Hints in Courses for Link Protector Configuration'). + '
            '.$suggestions.'
          '. + '
        • '; + } } } - 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 { @@ -15425,9 +15730,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{^(.+)/([^/]+)$}); @@ -15436,6 +15743,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); } @@ -15451,7 +15759,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); @@ -15472,18 +15780,135 @@ sub modify_lti { map { $posscrstype{$_} = 1; } @coursetypes; my %menutitles = <imenu_titles(); + my (%currltisec,%secchanges,%newltisec,%newltienc,%newkeyset); + + &fetch_secrets($dom,'ltisec',\%domconfig,\%currltisec,\%secchanges,\%newltisec,\%newkeyset); + my (%linkprotchg,$linkprotoutput,$is_home); + my $proterror = &Apache::courseprefs::process_linkprot($dom,'',$currltisec{'linkprot'}, + \%linkprotchg,'domain'); + my $home = &Apache::lonnet::domain($dom,'primary'); + unless (($home eq 'no_host') || ($home eq '')) { + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $id (@ids) { if ($id eq $home) { $is_home=1; } } + } + + if (keys(%linkprotchg)) { + $secchanges{'linkprot'} = 1; + my %oldlinkprot; + if (ref($currltisec{'linkprot'}) eq 'HASH') { + %oldlinkprot = %{$currltisec{'linkprot'}}; + } + foreach my $id (keys(%linkprotchg)) { + if (ref($linkprotchg{$id}) eq 'HASH') { + foreach my $inner (keys(%{$linkprotchg{$id}})) { + if (($inner eq 'secret') || ($inner eq 'key')) { + if ($is_home) { + $newltienc{$id}{$inner} = $linkprotchg{$id}{$inner}; + } + } + } + } else { + $newltisec{'linkprot'}{$id} = $linkprotchg{$id}; + } + } + $linkprotoutput = &Apache::courseprefs::store_linkprot($dom,'','domain',\%linkprotchg,\%oldlinkprot); + if (keys(%linkprotchg)) { + %{$newltisec{'linkprot'}} = %linkprotchg; + } + } + if (ref($currltisec{'linkprot'}) eq 'HASH') { + foreach my $id (keys(%{$currltisec{'linkprot'}})) { + next if ($id !~ /^\d+$/); + unless (exists($linkprotchg{$id})) { + if (ref($currltisec{'linkprot'}{$id}) eq 'HASH') { + foreach my $inner (keys(%{$currltisec{'linkprot'}{$id}})) { + if (($inner eq 'secret') || ($inner eq 'key')) { + if ($is_home) { + $newltienc{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner}; + } + } else { + $newltisec{'linkprot'}{$id}{$inner} = $currltisec{'linkprot'}{$id}{$inner}; + } + } + } else { + $newltisec{'linkprot'}{$id} = $currltisec{'linkprot'}{$id}; + } + } + } + } + if ($proterror) { + $errors .= '
        • '.$proterror.'
        • '; + } + + my (%delsuggested,%suggids,@suggested);; + if (ref($currltisec{'suggested'}) eq 'HASH') { + my $maxnum = $env{'form.linkprot_suggested_maxnum'}; + my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_suggested_del'); + for (my $i=0; $i<$maxnum; $i++) { + my $itemid = $env{'form.linkprot_suggested_id_'.$i}; + $itemid =~ s/\D+//g; + if ($itemid) { + if (ref($currltisec{'suggested'}->{$itemid}) eq 'HASH') { + push(@suggested,$i); + $suggids{$i} = $itemid; + if ((@todelete > 0) && (grep(/^$i$/,@todelete))) { + if (ref($currltisec{'suggested'}{$itemid}) eq 'HASH') { + $delsuggested{$itemid} = $currltisec{'suggested'}{$itemid}{'name'}; + } + } else { + if ($env{'form.linkprot_suggested_name_'.$i} eq '') { + $delsuggested{$itemid} = $currltisec{'suggested'}{$itemid}{'name'}; + } else { + $env{'form.linkprot_suggested_name_'.$i} =~ s/(`)/'/g; + $env{'form.linkprot_suggested_info_'.$i} =~ s/(`)/'/g; + $newltisec{'suggested'}{$itemid}{'name'} = $env{'form.linkprot_suggested_name_'.$i}; + $newltisec{'suggested'}{$itemid}{'info'} = $env{'form.linkprot_suggested_info_'.$i}; + if (($currltisec{'suggested'}{$itemid}{'name'} ne $newltisec{'suggested'}{$itemid}{'name'}) || + ($currltisec{'suggested'}{$itemid}{'info'} ne $newltisec{'suggested'}{$itemid}{'info'})) { + $secchanges{'suggested'}{$itemid} = 1; + } + } + } + } + } + } + } + foreach my $key (keys(%delsuggested)) { + $newltisec{'suggested'}{$key} = $delsuggested{$key}; + $secchanges{'suggested'}{$key} = 1; + } + if (($env{'form.linkprot_suggested_add'}) && + ($env{'form.linkprot_suggested_name_add'} ne '')) { + $env{'form.linkprot_suggested_name_add'} =~ s/(`)/'/g; + $env{'form.linkprot_suggested_info_add'} =~ s/(`)/'/g; + my ($newsuggid,$errormsg) = &get_lti_id($dom,$env{'form.linkprot_suggested_name_add'},'suggested'); + if ($newsuggid) { + $newltisec{'suggested'}{$newsuggid}{'name'} = $env{'form.linkprot_suggested_name_add'}; + $newltisec{'suggested'}{$newsuggid}{'info'} = $env{'form.linkprot_suggested_info_add'}; + $secchanges{'suggested'}{$newsuggid} = 1; + } else { + my $error = &mt('Failed to acquire unique ID for new Link Protectors in Courses Suggestion'); + if ($errormsg) { + $error .= ' ('.$errormsg.')'; + } + $errors .= '
        • '.$error.'
        • '; + } + } my (@items,%deletions,%itemids); if ($env{'form.lti_add'}) { my $consumer = $env{'form.lti_consumer_add'}; $consumer =~ s/(`)/'/g; - ($newid,my $error) = &get_lti_id($dom,$consumer); + ($newid,my $errormsg) = &get_lti_id($dom,$consumer,'lti'); if ($newid) { $itemids{'add'} = $newid; push(@items,'add'); $changes{$newid} = 1; } else { my $error = &mt('Failed to acquire unique ID for new LTI configuration'); + if ($errormsg) { + $error .= ' ('.$errormsg.')'; + } $errors .= '
        • '.$error.'
        • '; } } @@ -15506,29 +15931,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; } } } @@ -15538,14 +15977,14 @@ sub modify_lti { } if ($confhash{$itemid}{'requser'}) { if ($env{'form.lti_mapuser_'.$idx} eq 'sourcedid') { - $confhash{$itemid}{'mapuser'} = 'lis_person_sourcedid'; + $confhash{$itemid}{'mapuser'} = 'lis_person_sourcedid'; } elsif ($env{'form.lti_mapuser_'.$idx} eq 'email') { $confhash{$itemid}{'mapuser'} = 'lis_person_contact_email_primary'; } elsif ($env{'form.lti_mapuser_'.$idx} eq 'other') { my $mapuser = $env{'form.lti_customuser_'.$idx}; $mapuser =~ s/(`)/'/g; - $mapuser =~ s/^\s+|\s+$//g; - $confhash{$itemid}{'mapuser'} = $mapuser; + $mapuser =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapuser'} = $mapuser; } my @possmakeuser = &Apache::loncommon::get_env_multiple('form.lti_makeuser_'.$idx); my @makeuser; @@ -15669,27 +16108,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') { @@ -15700,10 +16139,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; @@ -15712,13 +16151,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') { @@ -15732,20 +16171,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') { @@ -15758,6 +16197,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; @@ -15775,41 +16279,57 @@ sub modify_lti { } } } + + if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) { + return &mt('No changes made.'); + } + my %ltihash = ( - $action => { %confhash } - ); - my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash, - $dom); + $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 %ltienchash = ( - $action => { %encconfig } - ); - &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); + if (keys(%ltienchash)) { + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); + } + $resulttext = &mt('Changes made:').'
            '; + if (keys(%secchanges) > 0) { + $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; } - $resulttext = &mt('Changes made:').'
              '; 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).'
              • '; @@ -15818,13 +16338,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'}) { @@ -15976,9 +16494,18 @@ sub modify_lti { $resulttext .= '
            • '; } } - $resulttext .= '
            '; - } else { - $resulttext = &mt('No changes made.'); + 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'}) || (exists($secchanges{'suggested'}))) { + &Apache::lonnet::get_domain_defaults($dom,1); + $lastactref->{'domdefaults'} = 1; + } } } else { $errors .= '
        • '.&mt('Failed to save changes').'
        • '; @@ -15990,24 +16517,51 @@ 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 + my ($domain,$consumer,$dbname) = @_; + unless (($dbname eq 'lti') || ($dbname eq 'suggested')) { + return ('','invalid db'); + } + # get lock on db my $lockhash = { lock => $env{'user.name'}. ':'.$env{'user.domain'}, }; my $tries = 0; - my $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + my $gotlock = &Apache::lonnet::newput_dom($dbname,$lockhash,$domain); my ($id,$error); while (($gotlock ne 'ok') && ($tries<10)) { $tries ++; sleep (0.1); - $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + $gotlock = &Apache::lonnet::newput_dom($dbname,$lockhash,$domain); } if ($gotlock eq 'ok') { - my %currids = &Apache::lonnet::dump_dom('lti',$domain); + my %currids = &Apache::lonnet::dump_dom($dbname,$domain); if ($currids{'lock'}) { delete($currids{'lock'}); if (keys(%currids)) { @@ -16019,14 +16573,14 @@ sub get_lti_id { $id = 1; } if ($id) { - unless (&Apache::lonnet::newput_dom('lti',{ $id => $consumer },$domain) eq 'ok') { + unless (&Apache::lonnet::newput_dom($dbname,{ $id => $consumer },$domain) eq 'ok') { $error = 'nostore'; } } else { $error = 'nonumber'; } } - my $dellockoutcome = &Apache::lonnet::del_dom('lti',['lock'],$domain); + my $dellockoutcome = &Apache::lonnet::del_dom($dbname,['lock'],$domain); } else { $error = 'nolock'; } @@ -16792,7 +17346,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) { @@ -16850,12 +17404,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; } @@ -16872,7 +17426,7 @@ sub modify_contacts { } } else { foreach my $key (@overrides) { - push(@{$changes{'overrides'}},$key); + push(@{$changes{'overrides'}},$key); } } } @@ -17054,7 +17608,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}; @@ -17078,14 +17632,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).''); @@ -17248,7 +17802,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'}}; @@ -17259,7 +17813,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', @@ -17278,6 +17832,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', @@ -17303,7 +17858,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') { @@ -17395,6 +17950,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, @@ -17403,7 +17970,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') { @@ -17421,6 +17988,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') { @@ -17461,6 +18037,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'); } @@ -17647,12 +18246,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); } @@ -17705,61 +18307,7 @@ sub modify_passwords { $updatedefaults = 1; } } - foreach my $rule ('min','max','expire','numsaved') { - $env{'form.passwords_'.$rule} =~ s/^\s+|\s+$//g; - my $ruleok; - if ($rule eq 'expire') { - if (($env{'form.passwords_'.$rule} =~ /^\d+(|\.\d*)$/) && - ($env{'form.passwords_'.$rule} ne '0')) { - $ruleok = 1; - } - } elsif ($rule eq 'min') { - if ($env{'form.passwords_'.$rule} =~ /^\d+$/) { - if ($env{'form.passwords_'.$rule} >= $Apache::lonnet::passwdmin) { - $ruleok = 1; - } - } - } elsif (($env{'form.passwords_'.$rule} =~ /^\d+$/) && - ($env{'form.passwords_'.$rule} ne '0')) { - $ruleok = 1; - } - if ($ruleok) { - $newvalues{$rule} = $env{'form.passwords_'.$rule}; - if (exists($current{$rule})) { - if ($newvalues{$rule} ne $current{$rule}) { - $changes{'rules'} = 1; - } - } elsif ($rule eq 'min') { - if ($staticdefaults{$rule} ne $newvalues{$rule}) { - $changes{'rules'} = 1; - } - } else { - $changes{'rules'} = 1; - } - } elsif (exists($current{$rule})) { - $changes{'rules'} = 1; - } - } - my @posschars = &Apache::loncommon::get_env_multiple('form.passwords_chars'); - my @chars; - foreach my $item (sort(@posschars)) { - if ($item =~ /^(uc|lc|num|spec)$/) { - push(@chars,$item); - } - } - $newvalues{'chars'} = \@chars; - unless ($changes{'rules'}) { - if (ref($current{'chars'}) eq 'ARRAY') { - my @diffs = &Apache::loncommon::compare_arrays($current{'chars'},\@chars); - if (@diffs > 0) { - $changes{'rules'} = 1; - } - } else { - if (@chars > 0) { - $changes{'rules'} = 1; - } - } - } + &password_rule_changes('passwords',\%newvalues,\%current,\%changes); my %crsownerchg = ( by => [], for => [], @@ -17954,7 +18502,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'}).'
    • '; @@ -18019,6 +18567,76 @@ sub modify_passwords { return $resulttext; } +sub password_rule_changes { + my ($prefix,$newvalues,$current,$changes) = @_; + return unless ((ref($newvalues) eq 'HASH') && + (ref($current) eq 'HASH') && + (ref($changes) eq 'HASH')); + my (@rules,%staticdefaults); + if ($prefix eq 'passwords') { + @rules = ('min','max','expire','numsaved'); + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { + @rules = ('min','max'); + } + $staticdefaults{'min'} = $Apache::lonnet::passwdmin; + foreach my $rule (@rules) { + $env{'form.'.$prefix.'_'.$rule} =~ s/^\s+|\s+$//g; + my $ruleok; + if ($rule eq 'expire') { + if (($env{'form.'.$prefix.'_'.$rule} =~ /^\d+(|\.\d*)$/) && + ($env{'form.'.$prefix.'_'.$rule} ne '0')) { + $ruleok = 1; + } + } elsif ($rule eq 'min') { + if ($env{'form.'.$prefix.'_'.$rule} =~ /^\d+$/) { + if ($env{'form.'.$prefix.'_'.$rule} >= $staticdefaults{$rule}) { + $ruleok = 1; + } + } + } elsif (($env{'form.'.$prefix.'_'.$rule} =~ /^\d+$/) && + ($env{'form.'.$prefix.'_'.$rule} ne '0')) { + $ruleok = 1; + } + if ($ruleok) { + $newvalues->{$rule} = $env{'form.'.$prefix.'_'.$rule}; + if (exists($current->{$rule})) { + if ($newvalues->{$rule} ne $current->{$rule}) { + $changes->{'rules'} = 1; + } + } elsif ($rule eq 'min') { + if ($staticdefaults{$rule} ne $newvalues->{$rule}) { + $changes->{'rules'} = 1; + } + } else { + $changes->{'rules'} = 1; + } + } elsif (exists($current->{$rule})) { + $changes->{'rules'} = 1; + } + } + my @posschars = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_chars'); + my @chars; + foreach my $item (sort(@posschars)) { + if ($item =~ /^(uc|lc|num|spec)$/) { + push(@chars,$item); + } + } + $newvalues->{'chars'} = \@chars; + unless ($changes->{'rules'}) { + if (ref($current->{'chars'}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($current->{'chars'},\@chars); + if (@diffs > 0) { + $changes->{'rules'} = 1; + } + } else { + if (@chars > 0) { + $changes->{'rules'} = 1; + } + } + } + return; +} + sub modify_usercreation { my ($dom,%domconfig) = @_; my ($resulttext,%curr_usercreation,%changes,%authallowed,%cancreate,%save_usercreate); @@ -18367,7 +18985,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})) { @@ -18379,7 +18997,7 @@ sub modify_selfcreation { } } elsif ($emaildom ne '') { push(@{$changes{'cancreate'}},'emaildomain'); - } + } } $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type}; } elsif ($chosen eq 'custom') { @@ -18806,7 +19424,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') { @@ -18892,7 +19510,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') { @@ -18910,7 +19528,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'}).''; } } } @@ -19014,7 +19632,7 @@ sub modify_selfcreation { $typename = $othertitle; } else { $typename = $usertypes{$type}; - } + } $chgtext .= &mt('(Affiliation: [_1])',$typename); } if (@{$email_rule{$type}} > 0) { @@ -19332,16 +19950,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 = ( @@ -19356,6 +20016,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, ); @@ -19467,9 +20162,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 '') { @@ -19486,7 +20201,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 .= '
      '; @@ -19541,12 +20269,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); } @@ -20466,14 +21197,19 @@ 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, + crsauthor => 1, ); my %texoptions = ( MathJax => 'MathJax', @@ -20518,7 +21254,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}; } @@ -20530,7 +21266,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; @@ -20665,6 +21401,66 @@ sub modify_coursedefaults { $changes{'postsubmit'} = 1; } } + my (%newdomexttool,%newexttool,%newcrsauthor,%olddomexttool,%oldexttool,%oldcrsauthor); + map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool'); + map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool'); + map { $newcrsauthor{$_} = 1; } &Apache::loncommon::get_env_multiple('form.crsauthor'); + 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; + } + } + } + if (ref($domconfig{'coursedefaults'}{'crsauthor'}) eq 'HASH') { + %oldcrsauthor = %{$domconfig{'coursedefaults'}{'crsauthor'}}; + } else { + foreach my $type (@types) { + if ($staticdefaults{'crsauthor'}) { + $oldcrsauthor{$type} = 1; + } else { + $oldcrsauthor{$type} = 0; + } + } + } + foreach my $type (@types) { + unless ($newdomexttool{$type}) { + $newdomexttool{$type} = 0; + } + unless ($newexttool{$type}) { + $newexttool{$type} = 0; + } + unless ($newcrsauthor{$type}) { + $newcrsauthor{$type} = 0; + } + if ($newdomexttool{$type} != $olddomexttool{$type}) { + $changes{'domexttool'} = 1; + } + if ($newexttool{$type} != $oldexttool{$type}) { + $changes{'exttool'} = 1; + } + if ($newcrsauthor{$type} != $oldcrsauthor{$type}) { + $changes{'crsauthor'} = 1; + } + } + $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool; + $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool; + $defaultshash{'coursedefaults'}{'crsauthor'} = \%newcrsauthor; } my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); @@ -20674,8 +21470,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'}) || ($changes{'crsauthor'})) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine', + 'ltiauth') { if ($changes{$item}) { $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; } @@ -20706,6 +21504,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') { @@ -20718,6 +21523,27 @@ sub modify_coursedefaults { $domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'}; } } + if ($changes{'domexttool'}) { + if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') { + foreach my $type (@types) { + $domdefaults{$type.'domexttool'}=$defaultshash{'coursedefaults'}{'domexttool'}{$type}; + } + } + } + if ($changes{'exttool'}) { + if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') { + foreach my $type (@types) { + $domdefaults{$type.'exttool'}=$defaultshash{'coursedefaults'}{'exttool'}{$type}; + } + } + } + if ($changes{'crsauthor'}) { + if (ref($defaultshash{'coursedefaults'}{'crsauthor'}) eq 'HASH') { + foreach my $type (@types) { + $domdefaults{$type.'crsauthor'}=$defaultshash{'coursedefaults'}{'crsauthor'}{$type}; + } + } + } my $cachetime = 24*60*60; &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); if (ref($lastactref) eq 'HASH') { @@ -20770,6 +21596,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').'
        '. @@ -20855,6 +21694,35 @@ sub modify_coursedefaults { } else { $resulttext .= '
      • '.&mt('LTI launch of deep-linked URL will require re-authentication').'
      • '; } + } elsif (($item eq 'domexttool') || ($item eq 'exttool') || ($item eq 'crsauthor')) { + my @noyes = (&mt('no'),&mt('yes')); + my %status = ( + domexttool => { + ishash => &mt('External Tools defined in the domain may be used as follows:'), + default => &mt('External Tools defined in the domain may be used in all course types, by default'), + }, + exttool => { + ishash => &mt('External Tools can be defined and configured in course containers as follows:'), + default => &mt('External Tools can not be defined in any course types, by default'), + }, + crsauthor => { + ishash => &mt('Standard Problems can be created within course containers as follows:'), + default => &mt('Standard Problems can be created within any course type, by default'), + }, + ); + + if (ref($defaultshash{'coursedefaults'}{$item}) eq 'HASH') { + $resulttext .= '
      • '.$status{$item}{'ishash'}.'
          '. + '
        • '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{$item}{'official'}].'').'
        • '. + '
        • '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{$item}{'unofficial'}].'').'
        • '. + '
        • '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{$item}{'textbook'}].'').'
        • '. + '
        • '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{$item}{'placement'}].'').'
        • '. + '
        • '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{$item}{'community'}].'').'
        • '. + '
        '. + '
      • '; + } else { + $resulttext .= '
      • '.$status{$item}{'default'}.'
      • '; + } } } $resulttext .= '
      '; @@ -21248,7 +22116,7 @@ sub modify_wafproxy { } } elsif ($currvalue{$item}) { $changes{$item} = 1; - } + } } } else { if (keys(%curralias)) { @@ -21256,7 +22124,7 @@ sub modify_wafproxy { } if (keys(%currsaml)) { $changes{'saml'} = 1; - } + } if (keys(%currvalue)) { foreach my $key (keys(%currvalue)) { $changes{$key} = 1; @@ -21266,7 +22134,7 @@ sub modify_wafproxy { if (keys(%changes)) { my %defaultshash = ( wafproxy => \%wafproxy, - ); + ); my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); if ($putresult eq 'ok') { @@ -21282,7 +22150,7 @@ sub modify_wafproxy { $domdefaults{'waf_'.$item} = $wafproxy{$item}; } elsif (exists($domdefaults{'waf_'.$item})) { delete($domdefaults{'waf_'.$item}); - } + } } } if ($updatedomdefs) { @@ -21349,7 +22217,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'}}))); @@ -21413,6 +22281,7 @@ sub modify_wafproxy { } } } + $output .= '
    '; } else { $output = ''. &mt('An error occurred: [_1]',$putresult).''; @@ -21678,7 +22547,7 @@ sub modify_usersessions { if (($offload eq 'offloadoth') && (@okoffloadoth)) { $changes{'offloadoth'} = 1; } - } + } } } else { if (@okoffload) { @@ -22095,8 +22964,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(); @@ -22214,7 +23085,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; } @@ -22900,7 +23771,6 @@ function balancerChange(balnum,baltotal, END } - sub new_spares_js { my @sparestypes = ('primary','default'); my $types = join("','",@sparestypes); @@ -23165,7 +24035,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') {