--- loncom/interface/domainprefs.pm 2022/02/16 04:33:43 1.407 +++ loncom/interface/domainprefs.pm 2023/04/11 20:35:19 1.422 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.407 2022/02/16 04:33:43 raeburn Exp $ +# $Id: domainprefs.pm,v 1.422 2023/04/11 20:35:19 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -167,6 +167,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(); @@ -220,8 +221,8 @@ sub handler { 'serverstatuses','requestcourses','helpsettings', 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', - 'ltitools','ssl','trust','lti','ltisec','privacy','passwords', - 'proctoring','wafproxy','ipaccess'],$dom); + 'ltitools','toolsec','ssl','trust','lti','ltisec', + 'privacy','passwords','proctoring','wafproxy','ipaccess'],$dom); my %encconfig = &Apache::lonnet::get_dom('encconfig',['ltitools','lti','proctoring','linkprot'],$dom,undef,1); if (ref($domconfig{'ltitools'}) eq 'HASH') { @@ -229,9 +230,7 @@ sub handler { 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'}; } } } @@ -327,7 +326,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, }, @@ -357,7 +358,7 @@ sub handler { modify => \&modify_passwords, }, 'quotas' => - { text => 'Blogs, personal web pages, webDAV/quotas, portfolios', + { text => 'Blogs, personal pages/timezones, webDAV/quotas, portfolio', help => 'Domain_Configuration_Quotas', header => [{col1 => 'User affiliation', col2 => 'Available tools', @@ -582,8 +583,12 @@ sub handler { '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, }, @@ -891,9 +896,9 @@ sub print_config_box { &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full, \@templateroles); } elsif ($action eq 'ltitools') { - $output .= <itools_javascript($settings); + $output .= &Apache::lonconfigsettings::ltitools_javascript($settings); } elsif ($action eq 'lti') { - $output .= &passwords_javascript('secrets')."\n". + $output .= &passwords_javascript('ltisecrets')."\n". <i_javascript($dom,$settings); } elsif ($action eq 'proctoring') { $output .= &proctoring_javascript($settings); @@ -948,7 +953,8 @@ sub print_config_box { ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') || ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') || - ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') || ($action eq 'lti')) { + ($action eq 'contacts') || ($action eq 'privacy') || ($action eq 'wafproxy') || + ($action eq 'lti') || ($action eq 'ltitools')) { $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal); } elsif ($action eq 'passwords') { $output .= $item->{'print'}->('top',$dom,$confname,$settings,\$rowtotal); @@ -983,8 +989,9 @@ sub print_config_box { if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') || ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'coursecategories') || - ($action eq 'trust') || ($action eq 'contacts') || - ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti')) { + ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'defaults') || + ($action eq 'privacy') || ($action eq 'passwords') || ($action eq 'lti') || + ($action eq 'ltitools')) { if ($action eq 'coursecategories') { $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal); $colspan = ' colspan="2"'; @@ -1071,8 +1078,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')) { $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); } elsif ($action eq 'scantron') { $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal); @@ -1275,8 +1282,7 @@ sub print_config_box { $output .= &print_quotas($dom,$settings,\$rowtotal,$action); } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || - ($action eq 'ltitools') || ($action eq 'proctoring') || - ($action eq 'ipaccess')) { + ($action eq 'proctoring') || ($action eq 'ipaccess')) { $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } } @@ -1626,13 +1632,13 @@ sub print_login { ''. ''. ''."\n"; - my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlnotsso,%styleon,%styleoff); + my (%saml,%samltext,%samlimg,%samlalt,%samlurl,%samltitle,%samlwindow,%samlnotsso,%styleon,%styleoff); foreach my $lonhost (keys(%domservers)) { $samlurl{$lonhost} = '/adm/sso'; $styleon{$lonhost} = 'display:none'; $styleoff{$lonhost} = ''; } - if (ref($settings->{'saml'}) eq 'HASH') { + if ((ref($settings) eq 'HASH') && (ref($settings->{'saml'}) eq 'HASH')) { foreach my $lonhost (keys(%{$settings->{'saml'}})) { if (ref($settings->{'saml'}{$lonhost}) eq 'HASH') { $saml{$lonhost} = 1; @@ -1641,6 +1647,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'; @@ -1658,6 +1665,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 .= ''. ''. ''. ''; @@ -2368,7 +2387,7 @@ sub print_quotas { @options = ('norequest','approval','automatic'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','webdav','portfolio'); + @usertools = ('aboutme','blog','webdav','portfolio','timezone'); %titles = &tool_titles(); } if (ref($types) eq 'ARRAY') { @@ -2472,9 +2491,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" '; @@ -3413,8 +3435,17 @@ ENDSCRIPT sub lti_javascript { my ($dom,$settings) = @_; my $togglejs = <i_toggle_js($dom); + my $linkprot_js = &Apache::courseprefs::linkprot_javascript(); unless (ref($settings) eq 'HASH') { - return $togglejs; + return $togglejs.' + +'; } my (%ordered,$total,%jstext); $total = scalar(keys(%{$settings})); @@ -3432,7 +3463,6 @@ sub lti_javascript { push(@jsarray,$ordered{$item}); } my $jstext = ' var lti = Array('."'".join("','",@jsarray)."'".');'."\n"; - my $linkprot_js = &Apache::courseprefs::linkprot_javascript(); return <<"ENDSCRIPT"; @@ -5372,421 +5284,50 @@ sub radiobutton_prefs { } sub print_ltitools { - my ($dom,$settings,$rowtotal) = @_; - my $rownum = 0; - my $css_class; - my $itemcount = 1; - my $maxnum = 0; - my %ordered; + my ($position,$dom,$settings,$rowtotal) = @_; + my (%rules,%encrypt,%privkeys,%linkprot); if (ref($settings) eq 'HASH') { - foreach my $item (keys(%{$settings})) { - if (ref($settings->{$item}) eq 'HASH') { - my $num = $settings->{$item}{'order'}; - $ordered{$num} = $item; - } - } - } - my $confname = $dom.'-domainconfig'; - my $switchserver = &check_switchserver($dom,$confname); - my $maxnum = scalar(keys(%ordered)); - my $datatable; - my %lt = <itools_names(); - my @courseroles = ('cc','in','ta','ep','st'); - my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); - my @fields = ('fullname','firstname','lastname','email','roles','user'); - if (keys(%ordered)) { - my @items = sort { $a <=> $b } keys(%ordered); - for (my $i=0; $i<@items; $i++) { - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $item = $ordered{$items[$i]}; - my ($title,$key,$secret,$url,$lifetime,$imgsrc,%sigsel); - if (ref($settings->{$item}) eq 'HASH') { - $title = $settings->{$item}->{'title'}; - $url = $settings->{$item}->{'url'}; - $key = $settings->{$item}->{'key'}; - $secret = $settings->{$item}->{'secret'}; - $lifetime = $settings->{$item}->{'lifetime'}; - my $image = $settings->{$item}->{'image'}; - if ($image ne '') { - $imgsrc = ''.&mt('Tool Provider icon').''; - } - if ($settings->{$item}->{'sigmethod'} eq 'HMAC-256') { - $sigsel{'HMAC-256'} = ' selected="selected"'; - } else { - $sigsel{'HMAC-SHA1'} = ' selected="selected"'; - } - } - my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_".$item."'".');"'; - $datatable .= ''. - ''."\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; } @@ -5826,6 +5367,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; @@ -6511,100 +6169,9 @@ sub print_lti { } } if ($position eq 'top') { - my @ids=&Apache::lonnet::current_machine_ids(); - my %servers = &Apache::lonnet::get_servers($dom,'library'); - my $primary = &Apache::lonnet::domain($dom,'primary'); - my ($extra,$numshown); - foreach my $hostid (sort(keys(%servers))) { - my ($showextra,$divsty,$switch); - if ($hostid eq $primary) { - if (($encrypt{'ltisec_consumers'}) || ($encrypt{'ltisec_domlinkprot'})) { - $showextra = 1; - } - } - if ($encrypt{'ltisec_crslinkprot'}) { - $showextra = 1; - } - unless (grep(/^\Q$hostid\E$/,@ids)) { - $switch = 1; - } - if ($showextra) { - $numshown ++; - $divsty = 'display:inline-block'; - } else { - $divsty = 'display:none'; - } - $extra .= '
'. - ''.$hostid.''; - if ($switch) { - my $switchserver = ''.&mt('Switch Server').''; - if (exists($privkeys{$hostid})) { - $extra .= '
'. - ''. - &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'
'. - ''.&mt('Change?'). - ''. - (' 'x2). - '  '; - } else { - $extra .= ''. - &mt('Key required').' - '.&mt('submit from server ([_1]): [_2].',$hostid,$switchserver). - ''."\n"; - } - } elsif (exists($privkeys{$hostid})) { - $extra .= '
'. - &mt('Encryption Key').': ['.&mt('not shown').'] '.(' 'x2).'
'. - ''.&mt('Change?'). - ''. - (' 'x2). - '  '; - } else { - $extra .= ''.&mt('Encryption Key').':'. - ''. - ''; - } - $extra .= '
'; - } - my %choices = &Apache::lonlocal::texthash ( - ltisec_crslinkprot => 'Encrypt stored link protection secrets defined in courses', - ltisec_domlinkprot => 'Encrypt stored link protection secrets defined in domain', - ltisec_consumers => 'Encrypt stored consumer secrets defined in domain', - ); - my @toggles = qw(ltisec_crslinkprot ltisec_domlinkprot ltisec_consumers); - my %defaultchecked = ( - 'ltisec_crslinkprot' => 'off', - 'ltisec_domlinkprot' => 'off', - 'ltisec_consumers' => 'off', - ); - my ($onclick,$itemcount); - $onclick = 'javascript:toggleLTIEncKey(this.form);'; - ($datatable,$itemcount) = &radiobutton_prefs(\%encrypt,\@toggles,\%defaultchecked, - \%choices,$itemcount,$onclick,'','left','no'); - - $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $noprivkeysty = 'display:inline-block'; - if ($numshown) { - $noprivkeysty = 'display:none'; - } - $datatable .= ''. - ''; - $itemcount ++; - $$rowtotal += $itemcount; + $datatable = &secrets_form($dom,'ltisec',\%encrypt,\%privkeys,$rowtotal); } elsif ($position eq 'middle') { - $datatable = &password_rules('secrets',\$itemcount,\%rules); + $datatable = &password_rules('ltisecrets',\$itemcount,\%rules); $$rowtotal += $itemcount; } elsif ($position eq 'lower') { $datatable .= &Apache::courseprefs::print_linkprotection($dom,'',$settings,$rowtotal,'','','domain'); @@ -7179,12 +6746,16 @@ sub print_coursedefaults { canclone => "People who may clone a course (besides course's owner and coordinators)", mysqltables => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver', ltiauth => 'Student username in LTI launch of deep-linked URL can be accepted without re-authentication', + domexttool => 'External Tools defined in the domain may be used in courses/communities (by type)', + exttool => 'External Tools can be defined and configured in courses/communities (by type)', ); my %staticdefaults = ( anonsurvey_threshold => 10, uploadquota => 500, postsubmit => 60, mysqltables => 172800, + domexttool => 1, + exttool => 0, ); if ($position eq 'top') { %defaultchecked = ( @@ -7302,11 +6873,33 @@ sub print_coursedefaults { my $currusecredits = 0; my $postsubmitclient = 1; my $ltiauth = 0; + my %domexttool; + my %exttool; my @types = ('official','unofficial','community','textbook','placement'); if (ref($settings) eq 'HASH') { if ($settings->{'ltiauth'}) { $ltiauth = 1; } + if (ref($settings->{'domexttool'}) eq 'HASH') { + foreach my $type (@types) { + if ($settings->{'domexttool'}->{$type}) { + $domexttool{$type} = ' checked="checked"'; + } + } + } else { + foreach my $type (@types) { + if ($staticdefaults{'domexttool'}) { + $domexttool{$type} = ' checked="checked"'; + } + } + } + if (ref($settings->{'exttool'}) eq 'HASH') { + foreach my $type (@types) { + if ($settings->{'exttool'}->{$type}) { + $exttool{$type} = ' checked="checked"'; + } + } + } $currdefresponder = $settings->{'anonsurvey_threshold'}; if (ref($settings->{'uploadquota'}) eq 'HASH') { foreach my $type (keys(%{$settings->{'uploadquota'}})) { @@ -7358,6 +6951,9 @@ sub print_coursedefaults { } else { foreach my $type (@types) { $deftimeout{$type} = $staticdefaults{'postsubmit'}; + if ($staticdefaults{'domexttool'}) { + $domexttool{$type} = ' checked="checked"'; + } } } if (!$currdefresponder) { @@ -7461,7 +7057,35 @@ sub print_coursedefaults { &radiobutton_prefs($current,\@toggles,\%defaultchecked, \%choices,$itemcount,undef,undef,'left'); $datatable .= $table; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + ''."\n"; $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + ''."\n"; } $$rowtotal += $itemcount; return $datatable; @@ -7696,13 +7320,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) = @@ -7721,6 +7347,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', @@ -7772,6 +7399,28 @@ sub print_privacy { $datatable .= ''; $itemcount ++; } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''; } elsif ($position eq 'middle') { if ((@instdoms > 1) || (keys(%by_location) > 0)) { if ((ref($types) eq 'ARRAY') && (ref($usertypes) eq 'HASH')) { @@ -8185,7 +7834,7 @@ sub password_rules { max => 'Maximum password length', chars => 'Required characters', ); - } elsif ($prefix eq 'secrets') { + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { %titles = &Apache::lonlocal::texthash ( min => 'Minimum secret length', max => 'Maximum secret length', @@ -9686,6 +9335,7 @@ sub tool_titles { blog => 'Blog', webdav => 'WebDAV', portfolio => 'Portfolio', + timezone => 'Can set time zone', official => 'Official courses (with institutional codes)', unofficial => 'Unofficial courses', community => 'Communities', @@ -10467,7 +10117,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 .= '
'.$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 .= '
'. @@ -1689,13 +1700,21 @@ sub print_login { $datatable .= ''; } $datatable .= '

'. + ''. + ''. + ''. + ''. + ''. - ''. - ''. + ''. '
'.&mt('SSO').''. + ''.&mt('Non-SSO').'
'.&mt('URL').''.&mt('Tool Tip').''.&mt('Pop-up if iframe').''.&mt('Text').'
'.(' 'x2).'
 
' - .''.(' 'x2). - ''. - '
'.&mt('Required settings').''. - ''.$lt{'title'}.': '. - (' 'x2). - ''.$lt{'version'}.': '. - (' 'x2). - ''.$lt{'msgtype'}.': '. - (' 'x2). - ''.$lt{'sigmethod'}.':'. - '

'. - ''.$lt{'url'}.':'. - (' 'x2). - ''.$lt{'key'}.':'. - ' '. - (' 'x2). - ''.$lt{'lifetime'}.':'. - ' '. - (' 'x2). - ''.$lt{'secret'}.':'. - ''. - ''. - ''. - '
'. - '
'.&mt('Optional settings').''. - ''.&mt('Display target:'); - my %currdisp; - if (ref($settings->{$item}->{'display'}) eq 'HASH') { - if ($settings->{$item}->{'display'}->{'target'} eq 'window') { - $currdisp{'window'} = ' checked="checked"'; - } elsif ($settings->{$item}->{'display'}->{'target'} eq 'tab') { - $currdisp{'tab'} = ' checked="checked"'; - } else { - $currdisp{'iframe'} = ' checked="checked"'; - } - if ($settings->{$item}->{'display'}->{'width'} =~ /^(\d+)$/) { - $currdisp{'width'} = $1; - } - if ($settings->{$item}->{'display'}->{'height'} =~ /^(\d+)$/) { - $currdisp{'height'} = $1; - } - $currdisp{'linktext'} = $settings->{$item}->{'display'}->{'linktext'}; - $currdisp{'explanation'} = $settings->{$item}->{'display'}->{'explanation'}; - } else { - $currdisp{'iframe'} = ' checked="checked"'; - } - foreach my $disp ('iframe','tab','window') { - $datatable .= ''.(' 'x2); - } - $datatable .= (' 'x4); - foreach my $dimen ('width','height') { - $datatable .= ''. - (' 'x2); - } - $datatable .= '
'. - '
'.$lt{'linktext'}.'
'. - '
'. - '
'.$lt{'explanation'}.'
'. - '

'; - my %units = ( - 'passback' => 'days', - 'roster' => 'seconds', - ); - foreach my $extra ('passback','roster') { - my $validsty = 'none'; - my $currvalid; - my $checkedon = ''; - my $checkedoff = ' checked="checked"'; - if ($settings->{$item}->{$extra}) { - $checkedon = $checkedoff; - $checkedoff = ''; - $validsty = 'inline-block'; - if ($settings->{$item}->{$extra.'valid'} =~ /^\d+\.?\d*$/) { - $currvalid = $settings->{$item}->{$extra.'valid'}; - } - } - my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','$i'".');"'; - $datatable .= '
'.$lt{$extra}.' '. - ''.(' 'x2). - '
'. - '
'. - ''. - &mt("at least [_1] $units{$extra} after launch", - ''). - '
'; - } - $datatable .= ''.$lt{'icon'}.': '; - if ($imgsrc) { - $datatable .= $imgsrc. - ' '. - ' '.&mt('Replace:').' '; - } else { - $datatable .= '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; - } - if ($switchserver) { - $datatable .= &mt('Upload to library server: [_1]',$switchserver); - } else { - $datatable .= ''; - } - $datatable .= '
'; - my (%checkedfields,%rolemaps,$userincdom); - if (ref($settings->{$item}) eq 'HASH') { - if (ref($settings->{$item}->{'fields'}) eq 'HASH') { - %checkedfields = %{$settings->{$item}->{'fields'}}; - } - $userincdom = $settings->{$item}->{'incdom'}; - if (ref($settings->{$item}->{'roles'}) eq 'HASH') { - %rolemaps = %{$settings->{$item}->{'roles'}}; - $checkedfields{'roles'} = 1; - } - } - $datatable .= '
'.&mt('User data sent on launch').''. - ''; - my $userfieldstyle = 'display:none;'; - my $seluserdom = ''; - my $unseluserdom = ' selected="selected"'; - foreach my $field (@fields) { - my ($checked,$onclick,$id,$spacer); - if ($checkedfields{$field}) { - $checked = ' checked="checked"'; - } - if ($field eq 'user') { - $id = ' id="ltitools_user_field_'.$i.'"'; - $onclick = ' onclick="toggleLTITools(this.form,'."'$field','$i'".')"'; - if ($checked) { - $userfieldstyle = 'display:inline-block'; - if ($userincdom) { - $seluserdom = $unseluserdom; - $unseluserdom = ''; - } + if ($position eq 'top') { + if (exists($settings->{'encrypt'})) { + if (ref($settings->{'encrypt'}) eq 'HASH') { + foreach my $key (keys(%{$settings->{'encrypt'}})) { + $encrypt{'toolsec_'.$key} = $settings->{'encrypt'}{$key}; } - } else { - $spacer = (' ' x2); } - $datatable .= ''.$spacer; } - $datatable .= ''; - $datatable .= '
'. - ' : '. - '
'; - $datatable .= '
'. - '
'.&mt('Role mapping').''; - foreach my $role (@courseroles) { - my ($selected,$selectnone); - if (!$rolemaps{$role}) { - $selectnone = ' selected="selected"'; - } - $datatable .= ''; } - $datatable .= '
'. - &Apache::lonnet::plaintext($role,'Course').'
'. - '
'; - my %courseconfig; - if (ref($settings->{$item}) eq 'HASH') { - if (ref($settings->{$item}->{'crsconf'}) eq 'HASH') { - %courseconfig = %{$settings->{$item}->{'crsconf'}}; - } - } - $datatable .= '
'.&mt('Configurable in course').''; - foreach my $item ('label','title','target','linktext','explanation','append') { - my $checked; - if ($courseconfig{$item}) { - $checked = ' checked="checked"'; + } elsif ($position eq 'middle') { + if (exists($settings->{'rules'})) { + if (ref($settings->{'rules'}) eq 'HASH') { + %rules = %{$settings->{'rules'}}; } - $datatable .= '  '."\n"; } - $datatable .= '
'. - '
'.&mt('Custom items sent on launch').''. - ''; - if (ref($settings->{$item}->{'custom'}) eq 'HASH') { - my %custom = %{$settings->{$item}->{'custom'}}; - if (keys(%custom) > 0) { - foreach my $key (sort(keys(%custom))) { - $datatable .= ''. - ''; - } + } else { + foreach my $key ('encrypt','private','rules') { + if (exists($settings->{$key})) { + delete($settings->{$key}); } } - $datatable .= ''; - $datatable .= '
'.&mt('Action').''.&mt('Name').''.&mt('Value').'
'. - ''.$key.'
'. - ''. - '
'."\n". - ''."\n". - ' '."\n". - ''.&mt('Add').''. - '
'.&mt('Required settings').''. - ''.$lt{'title'}.': '."\n". - (' 'x2). - ''.$lt{'version'}.': '."\n". - (' 'x2). - ''.$lt{'msgtype'}.': '. - ''.$lt{'sigmethod'}.':'. - '
'. - ''.$lt{'url'}.': '."\n". - (' 'x2). - ''.$lt{'key'}.': '."\n". - (' 'x2). - ''.$lt{'lifetime'}.': '."\n". - (' 'x2). - ''.$lt{'secret'}.':'. - ' '."\n". - '
'. - '
'.&mt('Optional settings').''. - ''.&mt('Display target:'); - my %defaultdisp; - $defaultdisp{'iframe'} = ' checked="checked"'; - foreach my $disp ('iframe','tab','window') { - $datatable .= ''.(' 'x2); - } - $datatable .= (' 'x4); - foreach my $dimen ('width','height') { - $datatable .= ''. - (' 'x2); - } - $datatable .= '
'. - '
'.$lt{'linktext'}.'
'. - '
'. - '
'.$lt{'explanation'}.'
'. - ''. - '

'; - my %units = ( - 'passback' => 'days', - 'roster' => 'seconds', - ); - my %defaulttimes = ( - 'passback' => '7', - 'roster' => '300', - ); - foreach my $extra ('passback','roster') { - my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','add'".');"'; - $datatable .= '
'.$lt{$extra}.' '. - ''.(' 'x2).''. - '
'. - '
'; - } - $datatable .= ''.$lt{'icon'}.': '. - '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; - if ($switchserver) { - $datatable .= &mt('Upload to library server: [_1]',$switchserver); - } else { - $datatable .= ''; - } - $datatable .= '
'. - '
'.&mt('User data sent on launch').''. - ''; - foreach my $field (@fields) { - my ($id,$onclick,$spacer); - if ($field eq 'user') { - $id = ' id="ltitools_user_field_add"'; - $onclick = ' onclick="toggleLTITools(this.form,'."'$field','add'".')"'; - } else { - $spacer = (' ' x2); - } - $datatable .= ''.$spacer; - } - $datatable .= ''. - '
'; - $datatable .= '
'.&mt('Role mapping').''; - foreach my $role (@courseroles) { - my ($checked,$checkednone); - $datatable .= ''; - } - $datatable .= '
'. - &Apache::lonnet::plaintext($role,'Course').'
'. - '
'. - '
'.&mt('Configurable in course').''; - foreach my $item ('label','title','target','linktext','explanation','append') { - $datatable .= ''.(' ' x2)."\n"; - } - $datatable .= '
'. - '
'.&mt('Custom items sent on launch').''. - ''. - ''. - '
'.&mt('Action').''.&mt('Name').''.&mt('Value').'
'. - ''. - '
'."\n". - '
'.&mt('Encryption Key(s)').'
'. + ''.&mt('Not in use').'
'. + $extra. + '
'.&mt('Encryption Key(s)').'
'. - ''.&mt('Not in use').'
'. - $extra. - '
'. + $choices{'domexttool'}. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''."\n"; + } + $datatable .= '
'. + ''. + ''. + &mt($type).'
'. + $choices{'exttool'}. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''."\n"; + } + $datatable .= '
'. + ''. + ''. + &mt($type).'
'.$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 .='
'. @@ -10522,9 +10172,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; } @@ -10656,18 +10306,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')) { @@ -10717,6 +10383,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; @@ -10742,6 +10424,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', @@ -10974,10 +10658,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); } } @@ -11456,6 +11143,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 '') { @@ -11509,6 +11213,17 @@ $jstext return; } +$portal_js + +// ]]> + + +ENDSCRIPT + } else { +return <<"ENDSCRIPT"; + @@ -11529,7 +11244,7 @@ sub passwords_javascript { passexp => 'Warning: days before password expiration must be a positive integer (or blank).', passnum => 'Warning: number of previous passwords to save must be a positive integer (or blank).', ); - } elsif ($prefix eq 'secrets') { + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { %intalert = &Apache::lonlocal::texthash ( passmin => 'Warning: minimum secret length must be a positive integer greater than 6.', passmax => 'Warning: maximum secret length must be a positive integer (or blank).', @@ -11568,7 +11283,7 @@ function warnInt$prefix(field) { field.value.replace(/^\s+/,''); field.value.replace(/\s+\$/,''); var regexdigit=/^\\d+\$/; - if (field.name == '$prefix\_min') { + if (field.name == '${prefix}_min') { if (field.value == '') { alert('$intalert{passmin}'); field.value = '$defmin'; @@ -11588,7 +11303,7 @@ function warnInt$prefix(field) { field.value = ''; } if (field.value != '') { - if (field.name == '$prefix\_expire') { + if (field.name == '${prefix}_expire') { var regexpposnum=/^\\d+(|\\.\\d*)\$/; if (!regexpposnum.test(field.value)) { alert('$intalert{passexp}'); @@ -11602,10 +11317,10 @@ function warnInt$prefix(field) { } } else { if (!regexdigit.test(field.value)) { - if (field.name == '$prefix\_max') { + if (field.name == '${prefix}_max') { alert('$intalert{passmax}'); } else { - if (field.name == '$prefix\_numsaved') { + if (field.name == '${prefix}_numsaved') { alert('$intalert{passnum}'); } } @@ -12236,7 +11951,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', @@ -12260,6 +11975,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'}; } } @@ -12402,13 +12118,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; @@ -12421,6 +12140,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); } @@ -12478,11 +12198,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; @@ -12499,6 +12222,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); } @@ -12517,10 +12241,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'}); @@ -12538,13 +12265,16 @@ 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 { @@ -12567,11 +12297,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; @@ -12582,6 +12315,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); } @@ -12745,19 +12479,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).'
    • '; @@ -13283,12 +13020,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); } @@ -13310,12 +13050,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); } } } @@ -13609,229 +13352,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; +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,$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'); - } - } - return $output; } sub notifysubscribed { @@ -13922,7 +13452,7 @@ sub modify_quotas { @usertools = ('author'); %titles = &authorrequest_titles(); } else { - @usertools = ('aboutme','blog','webdav','portfolio'); + @usertools = ('aboutme','blog','webdav','portfolio','timezone'); %titles = &tool_titles(); } my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); @@ -14613,11 +14143,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); } @@ -14632,656 +14165,319 @@ 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 $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}; + my %keystore; + if ($is_home) { + my %toolsenchash = ( + $action => { %newtoolsenc } + ); + &Apache::lonnet::put_dom('encconfig',\%toolsenchash,$dom,undef,1); + &store_security($dom,'ltitools',\%secchanges,\%newkeyset,\%keystore,$lastactref); + } + $resulttext = &mt('Changes made:').'
        '; + if (keys(%secchanges) > 0) { + $resulttext .= <i_security_results('ltitools',\%secchanges,\%newtoolsec,\%newkeyset,\%keystore); + } + if (keys(%ltitoolschg) > 0) { + $resulttext .= $ltitoolsoutput; + } + } 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 $newkey; + 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,$lastactref) = @_; + return unless ((ref($secchanges) eq 'HASH') && (ref($newkeyset) eq 'HASH') && + (ref($keystore) eq 'HASH')); + if (keys(%{$secchanges})) { + if ($secchanges->{'private'}) { + my $who = &escape($env{'user.name'}.':'.$env{'user.domain'}); + foreach my $hostid (keys(%{$newkeyset})) { + my $storehash = { + key => $newkeyset->{$hostid}, + who => $env{'user.name'}.':'.$env{'user.domain'}, + }; + $keystore->{$hostid} = &Apache::lonnet::store_dom($storehash,$context,'private', + $dom,$hostid); + } + } + if (ref($lastactref) eq 'HASH') { + if (($secchanges->{'encrypt'}) || ($secchanges->{'private'})) { + $lastactref->{'domdefaults'} = 1; } - } 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 ($context,$secchanges,$newsec,$newkeyset,$keystore) = @_; + my $output; + foreach my $item (keys(%{$secchanges})) { + if ($item eq 'encrypt') { + 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') { + push(@types,'consumers'); + } + foreach my $type (@types) { + my $shown = $encrypted{$type}{'off'}; + if (ref($newsec->{$item}) eq 'HASH') { + if ($newsec->{$item}{$type}) { + $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') { + if (keys(%{$newkeyset})) { + foreach my $hostid (sort(keys(%{$newkeyset}))) { + if ($keystore->{$hostid} eq 'ok') { + $output .= '
      • '.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'
      • '; + } + } + } + } elsif ($item eq 'linkprot') { + next; } - my $dellockoutcome = &Apache::lonnet::del_dom('ltitools',['lock'],$cdom); - } else { - $error = 'nolock'; } - return ($id,$error); + return $output; } sub modify_proctoring { @@ -15791,9 +14987,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{^(.+)/([^/]+)$}); @@ -15802,6 +15000,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); } @@ -15838,81 +15037,9 @@ sub modify_lti { map { $posscrstype{$_} = 1; } @coursetypes; my %menutitles = <imenu_titles(); + my (%currltisec,%secchanges,%newltisec,%newltienc,%newkeyset); - my (%currltisec,%secchanges,%newltisec,%newltienc,%keyset,%newkeyset); - $newltisec{'private'}{'keys'} = []; - $newltisec{'encrypt'} = {}; - $newltisec{'rules'} = {}; - $newltisec{'linkprot'} = {}; - if (ref($domconfig{'ltisec'}) eq 'HASH') { - %currltisec = %{$domconfig{'ltisec'}}; - if (ref($currltisec{'linkprot'}) eq 'HASH') { - foreach my $id (keys(%{$currltisec{'linkprot'}})) { - unless ($id =~ /^\d+$/) { - delete($currltisec{'linkprot'}{$id}); - } - } - } - if (ref($currltisec{'private'}) eq 'HASH') { - if (ref($currltisec{'private'}{'keys'}) eq 'ARRAY') { - $newltisec{'private'}{'keys'} = $currltisec{'private'}{'keys'}; - map { $keyset{$_} = 1; } @{$currltisec{'private'}{'keys'}}; - } - } - } - foreach my $item ('crs','dom','consumers') { - my $formelement; - if ($item eq 'consumers') { - $formelement = 'form.ltisec_'.$item; - } else { - $formelement = 'form.ltisec_'.$item.'linkprot'; - } - if ($env{$formelement}) { - $newltisec{'encrypt'}{$item} = 1; - if (ref($currltisec{'encrypt'}) eq 'HASH') { - unless ($currltisec{'encrypt'}{$item}) { - $secchanges{'encrypt'} = 1; - } - } else { - $secchanges{'encrypt'} = 1; - } - } elsif (ref($currltisec{'encrypt'}) eq 'HASH') { - if ($currltisec{'encrypt'}{$item}) { - $secchanges{'encrypt'} = 1; - } - } - } - unless (exists($currltisec{'rules'})) { - $currltisec{'rules'} = {}; - } - &password_rule_changes('secrets',$newltisec{'rules'},$currltisec{'rules'},\%secchanges); - - my @ids=&Apache::lonnet::current_machine_ids(); - my %servers = &Apache::lonnet::get_servers($dom,'library'); - - foreach my $hostid (keys(%servers)) { - if (($hostid ne '') && (grep(/^\Q$hostid\E$/,@ids))) { - my $newkey; - my $keyitem = 'form.ltisec_privkey_'.$hostid; - if (exists($env{$keyitem})) { - $env{$keyitem} =~ s/(`)/'/g; - if ($keyset{$hostid}) { - if ($env{'form.ltisec_changeprivkey_'.$hostid}) { - if ($env{$keyitem} ne '') { - $secchanges{'private'} = 1; - $newkeyset{$hostid} = $env{$keyitem}; - } - } - } elsif ($env{$keyitem} ne '') { - unless (grep(/^\Q$hostid\E$/,@{$newltisec{'private'}{'keys'}})) { - push(@{$newltisec{'private'}{'keys'}},$hostid); - } - $secchanges{'private'} = 1; - $newkeyset{$hostid} = $env{$keyitem}; - } - } - } - } + &fetch_secrets($dom,'ltisec',\%domconfig,\%currltisec,\%secchanges,\%newltisec,\%newkeyset); my (%linkprotchg,$linkprotoutput,$is_home); my $proterror = &Apache::courseprefs::process_linkprot($dom,'',$currltisec{'linkprot'}, @@ -16289,103 +15416,16 @@ sub modify_lti { my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash,$dom); if ($putresult eq 'ok') { my %keystore; - if (keys(%secchanges)) { - if ($secchanges{'private'}) { - my $who = &escape($env{'user.name'}.':'.$env{'user.domain'}); - foreach my $hostid (keys(%newkeyset)) { - my $storehash = { - key => $newkeyset{$hostid}, - who => $env{'user.name'}.':'.$env{'user.domain'}, - }; - $keystore{$hostid} = &Apache::lonnet::store_dom($storehash,'lti','private', - $dom,$hostid); - } - } - if (ref($lastactref) eq 'HASH') { - if (($secchanges{'encrypt'}) || ($secchanges{'private'})) { - $lastactref->{'domdefaults'} = 1; - } - } - } + &store_security($dom,'lti',\%secchanges,\%newkeyset,\%keystore,$lastactref); &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom,undef,1); if ((keys(%changes) == 0) && (keys(%secchanges) == 0)) { return &mt('No changes made.'); } $resulttext = &mt('Changes made:').'
          '; if (keys(%secchanges) > 0) { - foreach my $item (keys(%secchanges)) { - if ($item eq 'encrypt') { - my %encrypted = ( - crs => { - on => &mt('Encryption of stored link protection secrets defined in courses enabled'), - off => &mt('Encryption of stored link protection secrets defined in courses disabled'), - }, - dom => { - on => &mt('Encryption of stored link protection secrets defined in domain enabled'), - off => &mt('Encryption of stored link protection secrets defined in domain disabled'), - }, - consumers => { - on => &mt('Encryption of stored consumer secrets defined in domain enabled'), - off => &mt('Encryption of stored consumer secrets defined in domain disabled'), - }, - ); - foreach my $type ('crs','dom','consumers') { - my $shown = $encrypted{$type}{'off'}; - if (ref($newltisec{$item}) eq 'HASH') { - if ($newltisec{$item}{$type}) { - $shown = $encrypted{$type}{'on'}; - } - } - $resulttext .= '
        • '.$shown.'
        • '; - } - } elsif ($item eq 'rules') { - my %titles = &Apache::lonlocal::texthash( - min => 'Minimum password length', - max => 'Maximum password length', - chars => 'Required characters', - ); - foreach my $rule ('min','max') { - if ($newltisec{rules}{$rule} eq '') { - if ($rule eq 'min') { - $resulttext .= '
        • '.&mt('[_1] not set.',$titles{$rule}); - ' '.&mt('Default of [_1] will be used', - $Apache::lonnet::passwdmin).'
        • '; - } else { - $resulttext .= '
        • '.&mt('[_1] set to none',$titles{$rule}).'
        • '; - } - } else { - $resulttext .= '
        • '.&mt('[_1] set to [_2]',$titles{$rule},$newltisec{rules}{$rule}).'
        • '; - } - } - if (ref($newltisec{'rules'}{'chars'}) eq 'ARRAY') { - if (@{$newltisec{'rules'}{'chars'}} > 0) { - my %rulenames = &Apache::lonlocal::texthash( - uc => 'At least one upper case letter', - lc => 'At least one lower case letter', - num => 'At least one number', - spec => 'At least one non-alphanumeric', - ); - my $needed = '
          • '. - join('
          • ',map {$rulenames{$_} } @{$newltisec{'rules'}{'chars'}}). - '
          '; - $resulttext .= '
        • '.&mt('[_1] set to: [_2]',$titles{'chars'},$needed).'
        • '; - } else { - $resulttext .= '
        • '.&mt('[_1] set to none',$titles{'chars'}).'
        • '; - } - } else { - $resulttext .= '
        • '.&mt('[_1] set to none',$titles{'chars'}).'
        • '; - } - } elsif ($item eq 'private') { - if (keys(%newkeyset)) { - foreach my $hostid (sort(keys(%newkeyset))) { - if ($keystore{$hostid} eq 'ok') { - $resulttext .= '
        • '.&mt('Encryption key for storage of shared secrets saved for [_1]',$hostid).'
        • '; - } - } - } - } elsif ($item eq 'linkprot') { - $resulttext .= $linkprotoutput; - } + $resulttext .= <i_security_results('lti',\%secchanges,\%newltisec,\%newkeyset,\%keystore); + if (exists($secchanges{'linkprot'})) { + $resulttext .= $linkprotoutput; } } if (keys(%changes) > 0) { @@ -17859,7 +16899,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', @@ -17878,6 +16918,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', @@ -17903,7 +16944,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') { @@ -17995,6 +17036,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, @@ -18003,7 +17056,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') { @@ -18021,6 +17074,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') { @@ -18061,6 +17123,7 @@ sub modify_privacy { $resulttext .= '
          '; } } + $resulttext .= '
        '; } else { $resulttext = &mt('No changes made to user information settings'); } @@ -18247,12 +17310,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); } @@ -18573,7 +17639,7 @@ sub password_rule_changes { my (@rules,%staticdefaults); if ($prefix eq 'passwords') { @rules = ('min','max','expire','numsaved'); - } elsif ($prefix eq 'secrets') { + } elsif (($prefix eq 'ltisecrets') || ($prefix eq 'toolsecrets')) { @rules = ('min','max'); } $staticdefaults{'min'} = $Apache::lonnet::passwdmin; @@ -19508,7 +18574,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') { @@ -19526,7 +18592,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'}).''; } } } @@ -19948,16 +19014,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 = ( @@ -19972,6 +19080,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, ); @@ -20086,6 +19229,26 @@ sub modify_defaults { $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 '') { @@ -20102,7 +19265,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 .= '
      '; @@ -20157,12 +19333,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); } @@ -21090,6 +20269,7 @@ sub modify_coursedefaults { uploadquota => 500, postsubmit => 60, mysqltables => 172800, + domexttool => 1, ); my %texoptions = ( MathJax => 'MathJax', @@ -21281,6 +20461,47 @@ sub modify_coursedefaults { $changes{'postsubmit'} = 1; } } + my (%newdomexttool,%newexttool,%olddomexttool,%oldexttool); + map { $newdomexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.domexttool'); + map { $newexttool{$_} = 1; } &Apache::loncommon::get_env_multiple('form.exttool'); + if (ref($domconfig{'coursedefaults'}{'domexttool'}) eq 'HASH') { + %olddomexttool = %{$domconfig{'coursedefaults'}{'domexttool'}}; + } else { + foreach my $type (@types) { + if ($staticdefaults{'domexttool'}) { + $olddomexttool{$type} = 1; + } else { + $olddomexttool{$type} = 0; + } + } + } + if (ref($domconfig{'coursedefaults'}{'exttool'}) eq 'HASH') { + %oldexttool = %{$domconfig{'coursedefaults'}{'exttool'}}; + } else { + foreach my $type (@types) { + if ($staticdefaults{'exttool'}) { + $oldexttool{$type} = 1; + } else { + $oldexttool{$type} = 0; + } + } + } + foreach my $type (@types) { + unless ($newdomexttool{$type}) { + $newdomexttool{$type} = 0; + } + unless ($newexttool{$type}) { + $newexttool{$type} = 0; + } + if ($newdomexttool{$type} != $olddomexttool{$type}) { + $changes{'domexttool'} = 1; + } + if ($newexttool{$type} != $oldexttool{$type}) { + $changes{'exttool'} = 1; + } + } + $defaultshash{'coursedefaults'}{'domexttool'} = \%newdomexttool; + $defaultshash{'coursedefaults'}{'exttool'} = \%newexttool; } my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, $dom); @@ -21290,8 +20511,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'}) ) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','inline_chem','texengine', + 'ltiauth') { if ($changes{$item}) { $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; } @@ -21334,6 +20557,20 @@ sub modify_coursedefaults { $domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'}; } } + if ($changes{'domexttool'}) { + if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') { + foreach my $type (@types) { + $domdefaults{$type.'domexttool'}=$defaultshash{'coursedefaults'}{'domexttool'}{$type}; + } + } + } + if ($changes{'exttool'}) { + if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') { + foreach my $type (@types) { + $domdefaults{$type.'exttool'}=$defaultshash{'coursedefaults'}{'exttool'}{$type}; + } + } + } my $cachetime = 24*60*60; &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); if (ref($lastactref) eq 'HASH') { @@ -21471,6 +20708,34 @@ sub modify_coursedefaults { } else { $resulttext .= '
    • '.&mt('LTI launch of deep-linked URL will require re-authentication').'
    • '; } + } elsif ($item eq 'domexttool') { + my @noyes = (&mt('no'),&mt('yes')); + if (ref($defaultshash{'coursedefaults'}{'domexttool'}) eq 'HASH') { + $resulttext .= '
    • '.&mt('External Tools defined in the domain may be used as follows:').'
        '. + '
      • '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'official'}].'').'
      • '. + '
      • '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'unofficial'}].'').'
      • '. + '
      • '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'textbook'}].'').'
      • '. + '
      • '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'placement'}].'').'
      • '. + '
      • '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'domexttool'}{'community'}].'').'
      • '. + '
      '. + '
    • '; + } else { + $resulttext .= '
    • '.&mt('External Tools defined in the domain may be used in all course types, by default').'
    • '; + } + } elsif ($item eq 'exttool') { + my @noyes = (&mt('no'),&mt('yes')); + if (ref($defaultshash{'coursedefaults'}{'exttool'}) eq 'HASH') { + $resulttext .= '
    • '.&mt('External Tools can be defined and configured in course containers as follows:').'
        '. + '
      • '.&mt('Official courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'official'}].'').'
      • '. + '
      • '.&mt('Unofficial courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'unofficial'}].'').'
      • '. + '
      • '.&mt('Textbook courses: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'textbook'}].'').'
      • '. + '
      • '.&mt('Placement tests: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'placement'}].'').'
      • '. + '
      • '.&mt('Communities: [_1]',''.$noyes[$defaultshash{'coursedefaults'}{'exttool'}{'community'}].'').'
      • '. + '
      '. + '
    • '; + } else { + $resulttext .= '
    • '.&mt('External Tools can not be defined in any course types, by default').'
    • '; + } } } $resulttext .= '
    '; @@ -22029,6 +21294,7 @@ sub modify_wafproxy { } } } + $output .= ''; } else { $output = ''. &mt('An error occurred: [_1]',$putresult).''; @@ -22711,8 +21977,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(); @@ -23781,7 +23049,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') {