--- loncom/interface/domainprefs.pm 2008/04/21 16:23:55 1.46 +++ loncom/interface/domainprefs.pm 2008/07/21 16:24:31 1.66 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.46 2008/04/21 16:23:55 raeburn Exp $ +# $Id: domainprefs.pm,v 1.66 2008/07/21 16:24:31 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -37,10 +37,11 @@ use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::lonlocal; use Apache::lonmsg(); -use LONCAPA(); +use LONCAPA; use LONCAPA::Enrollment; use File::Copy; use Locale::Language; +use DateTime::TimeZone; sub handler { my $r=shift; @@ -62,7 +63,7 @@ sub handler { } &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['phase']); + ['phase','actions']); my $phase = 'pickactions'; if ( exists($env{'form.phase'}) ) { $phase = $env{'form.phase'}; @@ -70,15 +71,16 @@ sub handler { my %domconfig = &Apache::lonnet::get_dom('configuration',['login','rolecolors', 'quotas','autoenroll','autoupdate','directorysrch', - 'usercreation','usermodification','contacts','defaults','scantron'], - $dom); + 'usercreation','usermodification','contacts','defaults', + 'scantron','coursecategories'],$dom); my @prefs_order = ('rolecolors','login','defaults','quotas','autoenroll', 'autoupdate','directorysrch','contacts', - 'usercreation','usermodification','scantron'); + 'usercreation','usermodification','scantron', + 'coursecategories'); my %prefs = ( 'rolecolors' => { text => 'Default color schemes', - help => 'Default_Color_Schemes', + help => 'Domconfig_Color_Schemes', header => [{col1 => 'Student Settings', col2 => '',}, {col1 => 'Coordinator Settings', @@ -90,31 +92,31 @@ sub handler { }, 'login' => { text => 'Log-in page options', - help => 'Domain_Log-in_Page', + help => 'Domconfig_Login_Page', header => [{col1 => 'Item', col2 => '',}], }, 'defaults' => - { text => 'Default authentication/language', - help => '', + { text => 'Default authentication/language/timezone', + help => 'Domconfig_LangTZAuth', header => [{col1 => 'Setting', col2 => 'Value'}], }, 'quotas' => { text => 'Default quotas for user portfolios', - help => 'Default_User_Quota', + help => 'Domconfig_Quotas', header => [{col1 => 'User type', col2 => 'Default quota'}], }, 'autoenroll' => { text => 'Auto-enrollment settings', - help => 'Domain_Auto_Enrollment', + help => 'Domconfig_Auto_Enrollment', header => [{col1 => 'Configuration setting', col2 => 'Value(s)'}], }, 'autoupdate' => { text => 'Auto-update settings', - help => 'Domain_Auto_Update', + help => 'Domconfig_Auto_Updates', header => [{col1 => 'Setting', col2 => 'Value',}, {col1 => 'User population', @@ -122,20 +124,20 @@ sub handler { }, 'directorysrch' => { text => 'Institutional directory searches', - help => 'Domain_Directory_Search', + help => 'Domconfig_InstDirectory_Search', header => [{col1 => 'Setting', col2 => 'Value',}], }, 'contacts' => { text => 'Contact Information', - help => 'Domain_Contact_Information', + help => 'Domconfig_Contact_Info', header => [{col1 => 'Setting', col2 => 'Value',}], }, 'usercreation' => { text => 'User creation', - help => 'Domain_User_Creation', + help => 'Domconfig_User_Creation', header => [{col1 => 'Format rule type', col2 => 'Format rules in force'}, {col1 => 'User account creation', @@ -145,19 +147,30 @@ sub handler { }, 'usermodification' => { text => 'User modification', - help => 'Domain_User_Modification', + help => 'Domconfig_User_Modification', header => [{col1 => 'Target user has role', col2 => 'User information updateable in author context'}, {col1 => 'Target user has role', - col2 => 'User information updateable in course context'}], + col2 => 'User information updateable in course context'}, + {col1 => "Status of user", + col2 => 'Information settable when self-creating account (if directory data blank)'}], }, 'scantron' => { text => 'Scantron format file', - help => 'Domain_Scantron_Formats', + help => 'Domconfig_Scantron_Format', header => [ {col1 => 'Item', col2 => '', }], }, + 'coursecategories' => + { text => 'Cataloging of courses', + help => 'Domconfig_Cataloging_Courses', + header => [{col1 => 'Category settings', + col2 => '',}, + {col1 => 'Categories', + col2 => '', + }], + } ); my @roles = ('student','coordinator','author','admin'); my @actions = &Apache::loncommon::get_env_multiple('form.actions'); @@ -292,10 +305,10 @@ sub handler { $r->print('

'.&mt('Functionality to display/modify').'

'); $r->print(''."\n".'

'."\n".'

  '. - '

'); my ($numitems,$midpoint,$seconddiv,$count); @@ -306,7 +319,10 @@ sub handler { } $count = 0; foreach my $item (@prefs_order) { - $r->print('

'); + $r->print('

'. + &Apache::loncommon::help_open_topic($prefs{$item}->{'help'}). + '

'); $count ++; if ((!$seconddiv) && ($count >= $midpoint)) { $r->print('
'."\n".'
'."\n"); @@ -316,9 +332,9 @@ sub handler { $r->print('

'. &mt('Display options').'

'."\n". '

'.&mt('Display using: ')."\n". - '  '. - ''. + '  

'); $r->print(&print_footer($r,$phase,'display','Go')); $r->print(''); @@ -352,7 +368,9 @@ sub process_changes { } elsif ($action eq 'defaults') { $output = &modify_defaults($dom,$r); } elsif ($action eq 'scantron') { - $output = &modify_scantron($r,$dom,$confname,\%domconfig); + $output = &modify_scantron($r,$dom,$confname,%domconfig); + } elsif ($action eq 'coursecategories') { + $output = &modify_coursecategories($dom,%domconfig); } return $output; } @@ -360,27 +378,32 @@ sub process_changes { sub print_config_box { my ($r,$dom,$confname,$phase,$action,$item,$settings) = @_; my $rowtotal = 0; - my $output = + my $output; + if ($action eq 'coursecategories') { + $output = &coursecategories_javascript($settings); + } + $output .= ' - '; -# -# FIXME - put the help link back in when the help files exist -# -# '); + '."\n". + ''; $rowtotal ++; if (($action eq 'autoupdate') || ($action eq 'rolecolors') || - ($action eq 'usercreation') || ($action eq 'usermodification')) { - my $colspan = ($action eq 'rolecolors')?' colspan="2"':''; + ($action eq 'usercreation') || ($action eq 'usermodification') || + ($action eq 'coursecategories')) { + my $colspan = ''; + if (($action eq 'rolecolors') || ($action eq 'coursecategories')) { + $colspan = ' colspan="2"'; + } $output .= '
'.&mt($item->{text}). - ' 
'.&mt($item->{text}).' '. -# &Apache::loncommon::help_open_topic($item->{'help'}).'
'. + &mt($item->{text}).' '. + &Apache::loncommon::help_open_topic($item->{'help'}).'
- - + + '; $rowtotal ++; if ($action eq 'autoupdate') { @@ -389,6 +412,8 @@ sub print_config_box { $output .= &print_usercreation('top',$dom,$settings,\$rowtotal); } elsif ($action eq 'usermodification') { $output .= &print_usermodification('top',$dom,$settings,\$rowtotal); + } elsif ($action eq 'coursecategories') { + $output .= &print_coursecategories('top',$dom,$item,$settings,\$rowtotal); } else { $output .= &print_rolecolors($phase,'student',$dom,$confname,$settings,\$rowtotal); } @@ -400,8 +425,9 @@ sub print_config_box {
'.$item->{'header'}->[0]->{'col1'}.''.$item->{'header'}->[0]->{'col2'}.''.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col2'}).'
- - + '; + $output .= ' + '; $rowtotal ++; if ($action eq 'autoupdate') { @@ -415,12 +441,26 @@ sub print_config_box { + + +
'.$item->{'header'}->[1]->{'col1'}.''.$item->{'header'}->[1]->{'col2'}.''.&mt($item->{'header'}->[1]->{'col1'}).''.&mt($item->{'header'}->[1]->{'col2'}).'
- - '. + + '. &print_usercreation('bottom',$dom,$settings,\$rowtotal); $rowtotal ++; } elsif ($action eq 'usermodification') { - $output .= &print_usermodification('bottom',$dom,$settings,\$rowtotal); + $output .= &print_usermodification('middle',$dom,$settings,\$rowtotal).' +
'.$item->{'header'}->[2]->{'col1'}.''.$item->{'header'}->[2]->{'col2'}.'
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
+
+ + + + '. + + &print_usermodification('bottom',$dom,$settings,\$rowtotal); + $rowtotal ++; + } elsif ($action eq 'coursecategories') { + $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal); } else { $output .= &print_rolecolors($phase,'coordinator',$dom,$confname,$settings,\$rowtotal).'
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
@@ -430,8 +470,8 @@ sub print_config_box {
- - + + '. &print_rolecolors($phase,'author',$dom,$confname,$settings,\$rowtotal).'
'.$item->{'header'}->[2]->{'col1'}.''.$item->{'header'}->[2]->{'col2'}.''.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
@@ -441,8 +481,8 @@ sub print_config_box {
- - + + '. &print_rolecolors($phase,'admin',$dom,$confname,$settings,\$rowtotal); $rowtotal += 2; @@ -455,15 +495,15 @@ sub print_config_box { '; if (($action eq 'login') || ($action eq 'directorysrch')) { $output .= ' - '; + '; } else { $output .= ' - '; + '; } $output .= ' - + '; - $rowtotal ++; + $rowtotal ++; if ($action eq 'login') { $output .= &print_login($dom,$confname,$phase,$settings,\$rowtotal); } elsif ($action eq 'quotas') { @@ -498,7 +538,7 @@ function changePage(formname,newphase) { numchecked = 0; if (formname == document.pickactions) { if (formname.actions.length > 0) { - for (var i = 0; i {'cancreate'}) eq 'HASH') { foreach my $item (@creators) { $checked{$item} = $settings->{'cancreate'}{$item}; } + if (ref($settings->{'cancreate'}{'selfcreate'}) eq 'ARRAY') { + @selfcreate = @{$settings->{'cancreate'}{'selfcreate'}}; + } elsif ($settings->{'cancreate'}{'selfcreate'} ne '') { + if ($settings->{'cancreate'}{'selfcreate'} eq 'any') { + @selfcreate = ('email','login','sso'); + } elsif ($settings->{'cancreate'}{'selfcreate'} ne 'none') { + @selfcreate = ($settings->{'cancreate'}{'selfcreate'}); + } + } } elsif (ref($settings->{'cancreate'}) eq 'ARRAY') { foreach my $item (@creators) { if (grep(/^\Q$item\E$/,@{$settings->{'cancreate'}})) { @@ -1542,10 +1592,8 @@ sub print_usercreation { my $rownum = 0; foreach my $item (@creators) { $rownum ++; - if ($checked{$item} eq '') { - if ($item eq 'selfcreate') { - $checked{$item} = 'none'; - } else { + if ($item ne 'selfcreate') { + if ($checked{$item} eq '') { $checked{$item} = 'any'; } } @@ -1558,24 +1606,33 @@ sub print_usercreation { $datatable .= ''. ''. - ''. - '
'.$item->{'header'}->[3]->{'col1'}.''.$item->{'header'}->[3]->{'col2'}.''.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
'.$item->{'header'}->[0]->{'col1'}.''.&mt($item->{'header'}->[0]->{'col1'}).''.$item->{'header'}->[0]->{'col1'}.''.&mt($item->{'header'}->[0]->{'col1'}).''.$item->{'header'}->[0]->{'col2'}.''.&mt($item->{'header'}->[0]->{'col2'}).'
'.$lt{$item}. ''; - my @options = ('any'); + my @options; if ($item eq 'selfcreate') { push(@options,('email','login','sso')); } else { + @options = ('any'); if (ref($rules) eq 'HASH') { if (keys(%{$rules}) > 0) { push(@options,('official','unofficial')); } } + push(@options,'none'); } - push(@options,'none'); foreach my $option (@options) { + my $type = 'radio'; my $check = ' '; - if ($checked{$item} eq $option) { - $check = ' checked="checked" '; + if ($item eq 'selfcreate') { + $type = 'checkbox'; + if (grep(/^\Q$option\E$/,@selfcreate)) { + $check = ' checked="checked" '; + } + } else { + if ($checked{$item} eq $option) { + $check = ' checked="checked" '; + } } $datatable .= '  '; } @@ -1642,10 +1699,14 @@ sub user_formats_row { ); my $css_class = $rowcount%2?' class="LC_odd_row"':''; $output = '
'. - &mt("Format rules to check for $text{$type}: "). - ''; + ''. + '
'; + if ($type eq 'email') { + $output .= &mt("Formats disallowed for $text{$type}: "); + } else { + $output .= &mt("Format rules to check for $text{$type}: "); + } + $output .= ''; my $rem; if (ref($ruleorder) eq 'ARRAY') { for (my $i=0; $i<@{$ruleorder}; $i++) { @@ -1699,7 +1760,7 @@ sub usercreation_types { none => 'None', ); return %lt; -} +} sub authtype_names { my %lt = &Apache::lonlocal::texthash( @@ -1733,7 +1794,7 @@ sub print_usermodification { $$rowtotal ++; $rowcount ++; } - } else { + } elsif ($position eq 'middle') { $context = 'course'; $rowcount = 0; foreach my $role ('st','ep','ta','in','cr') { @@ -1742,13 +1803,27 @@ sub print_usermodification { $$rowtotal ++; $rowcount ++; } + } elsif ($position eq 'bottom') { + $context = 'selfcreate'; + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + $usertypes->{'default'} = $othertitle; + if (ref($types) eq 'ARRAY') { + push(@{$types},'default'); + $usertypes->{'default'} = $othertitle; + foreach my $status (@{$types}) { + $datatable .= &modifiable_userdata_row($context,$status,$settings, + $numinrow,$rowcount,$usertypes); + $$rowtotal ++; + $rowcount ++; + } + } } return $datatable; } sub print_defaults { my ($dom,$rowtotal) = @_; - my @items = ('auth_def','auth_arg_def','lang_def'); + my @items = ('auth_def','auth_arg_def','lang_def','timezone_def'); my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); my $titles = &defaults_titles(); my $rownum = 0; @@ -1780,6 +1855,9 @@ sub print_defaults { '" value="'.$auth.'"'.$checked.'/>'. $authnames{$shortauth{$auth}}.'  '; } + } elsif ($item eq 'timezone_def') { + my $includeempty = 1; + $datatable .= &Apache::loncommon::select_timezone($item,$domdefaults{$item},undef,$includeempty); } else { $datatable .= ''; @@ -1796,6 +1874,7 @@ sub defaults_titles { 'auth_def' => 'Default authentication type', 'auth_arg_def' => 'Default authentication argument', 'lang_def' => 'Default language', + 'timezone_def' => 'Default timezone', ); return (\%titles); } @@ -1803,7 +1882,8 @@ sub defaults_titles { sub print_scantronformat { my ($r,$dom,$confname,$settings,$rowtotal) = @_; my $itemcount = 1; - my ($datatable,$css_class,$scantronurl,$is_custom,%error,%scantronurls); + my ($datatable,$css_class,$scantronurl,$is_custom,%error,%scantronurls, + %confhash); my $switchserver = &check_switchserver($dom,$confname); my %lt = &Apache::lonlocal::texthash ( default => 'Default scantron format file error', @@ -1830,23 +1910,52 @@ sub print_scantronformat { ); my %md5chk; foreach my $type (keys(%legacyfile)) { - $md5chk{$type} = split(/ /,`md5sum $legacyfile{$type}`); - chop($md5chk{$type}); + ($md5chk{$type}) = split(/ /,`md5sum $legacyfile{$type}`); + chomp($md5chk{$type}); } if ($md5chk{'default'} ne $md5chk{'custom'}) { foreach my $type (keys(%legacyfile)) { - ($scantronurls{$type},$error{$type}) = + ($scantronurls{$type},my $error) = &legacy_scantronformat($r,$dom,$confname, $type,$legacyfile{$type}, $scantronurls{$type}, $scantronfiles{$type}); + if ($error ne '') { + $error{$type} = $error; + } + } + if (keys(%error) == 0) { + $is_custom = 1; + $confhash{'scantron'}{'scantronformat'} = + $scantronurls{'custom'}; + my $putresult = + &Apache::lonnet::put_dom('configuration', + \%confhash,$dom); + if ($putresult ne 'ok') { + $error{'custom'} = + ''. + &mt('An error occurred updating the domain configuration: [_1]',$putresult).''; + } } } else { - ($scantronurls{'default'},$error{'default'}) = + ($scantronurls{'default'},my $error) = &legacy_scantronformat($r,$dom,$confname, 'default',$legacyfile{'default'}, $scantronurls{'default'}, $scantronfiles{'default'}); + if ($error eq '') { + $confhash{'scantron'}{'scantronformat'} = ''; + my $putresult = + &Apache::lonnet::put_dom('configuration', + \%confhash,$dom); + if ($putresult ne 'ok') { + $error{'default'} = + ''. + &mt('An error occurred updating the domain configuration: [_1]',$putresult).''; + } + } else { + $error{'default'} = $error; + } } } } @@ -1867,39 +1976,59 @@ sub print_scantronformat { $scantronurl = $scantronurls{'default'}; } } else { - $scantronurl = $scantronurls{'default'}; + if ($is_custom) { + $scantronurl = $scantronurls{'custom'}; + } else { + $scantronurl = $scantronurls{'default'}; + } } $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''; if (!$is_custom) { - $datatable .= ''; + if (keys(%error) == 0) { + $datatable .= '' - .''. + ''; @@ -1922,13 +2051,368 @@ sub legacy_scantronformat { return ($url,$error); } +sub print_coursecategories { + my ($position,$dom,$hdritem,$settings,$rowtotal) = @_; + my $datatable; + if ($position eq 'top') { + my $toggle_cats_crs = ' '; + my $toggle_cats_dom = ' checked="checked" '; + my $can_cat_crs = ' '; + my $can_cat_dom = ' checked="checked" '; + if (ref($settings) eq 'HASH') { + if ($settings->{'togglecats'} eq 'crs') { + $toggle_cats_crs = $toggle_cats_dom; + $toggle_cats_dom = ' '; + } + if ($settings->{'categorize'} eq 'crs') { + $can_cat_crs = $can_cat_dom; + $can_cat_dom = ' '; + } + } + my %title = &Apache::lonlocal::texthash ( + togglecats => 'Show/Hide a course in the catalog', + categorize => 'Assign a category to a course', + ); + my %level = &Apache::lonlocal::texthash ( + dom => 'Set in "Modify Course" (Domain)', + crs => 'Set in "Modify Parameters" (Course)', + ); + $datatable = ''. + ''. + ''. + ''. + ''. + ''. + ''; + $$rowtotal += 2; + } else { + my $css_class; + my $itemcount = 1; + my $cathash; + if (ref($settings) eq 'HASH') { + $cathash = $settings->{'cats'}; + } + if (ref($cathash) eq 'HASH') { + my (@cats,@trails,%allitems,%idx,@jsarray); + &Apache::loncommon::extract_categories($cathash,\@cats,\@trails, + \%allitems,\%idx,\@jsarray); + my $maxdepth = scalar(@cats); + my $colattrib = ''; + if ($maxdepth > 2) { + $colattrib = ' colspan="2" '; + } + my @path; + if (@cats > 0) { + if (ref($cats[0]) eq 'ARRAY') { + my $numtop = @{$cats[0]}; + my $maxnum = $numtop; + if ((!grep(/^instcode$/,@{$cats[0]})) || ($cathash->{'instcode::0'} eq '')) { + $maxnum ++; + } + my $lastidx; + for (my $i=0; $i<$numtop; $i++) { + my $parent = $cats[0][$i]; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $item = &escape($parent).'::0'; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$item','$idx{$item}'".');"'; + $lastidx = $idx{$item}; + $datatable .= '' + .' ' + .''; + } else { + $datatable .= $parent + .' '; + } + my $depth = 1; + push(@path,$parent); + $datatable .= &build_category_rows($itemcount,\@cats,$depth,$parent,\@path,\%idx); + pop(@path); + $datatable .= ''; + $itemcount ++; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','addcategory_pos','$lastidx'".');"'; + $datatable .= '' + .''."\n"; + $itemcount ++; + if ((!grep(/^instcode$/,@{$cats[0]})) || ($cathash->{'instcode::0'} eq '')) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','instcode_pos','$lastidx'".');"'; + $datatable .= '' + .''; + } + } + } else { + $datatable .= &initialize_categories($itemcount); + } + } else { + $datatable .= '' + .&initialize_categories($itemcount); + } + $$rowtotal += $itemcount; + } + return $datatable; +} + +sub coursecategories_javascript { + my ($settings) = @_; + my ($output,$jstext,$cathash); + if (ref($settings) eq 'HASH') { + $cathash = $settings->{'cats'}; + } + if (ref($cathash) eq 'HASH') { + my (@cats,@jsarray,%idx); + &Apache::loncommon::gather_categories($cathash,\@cats,\%idx,\@jsarray); + if (@jsarray > 0) { + $jstext = ' var categories = Array('.scalar(@jsarray).');'."\n"; + for (my $i=0; $i<@jsarray; $i++) { + if (ref($jsarray[$i]) eq 'ARRAY') { + my $catstr = join('","',@{$jsarray[$i]}); + $jstext .= ' categories['.$i.'] = Array("'.$catstr.'");'."\n"; + } + } + } + } else { + $jstext = ' var categories = Array(1);'."\n". + ' categories[0] = Array("instcode_pos");'."\n"; + } + $output = <<"ENDSCRIPT"; + + +ENDSCRIPT + return $output; +} + +sub initialize_categories { + my ($itemcount) = @_; + my $datatable; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','instcode_pos','0'".');"'; + + $datatable = ''; + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','addcategory_pos','0'".');"'; + $datatable .= ''; + return $datatable; +} + +sub build_category_rows { + my ($itemcount,$cats,$depth,$parent,$path,$idx) = @_; + my ($text,$name,$item,$chgstr); + if (ref($cats) eq 'ARRAY') { + my $maxdepth = scalar(@{$cats}); + if (ref($cats->[$depth]) eq 'HASH') { + if (ref($cats->[$depth]{$parent}) eq 'ARRAY') { + my $numchildren = @{$cats->[$depth]{$parent}}; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $text .= ''; + } else { + my $higher = $depth-1; + if ($higher == 0) { + $name = &escape($parent).'::'.$higher; + } else { + if (ref($path) eq 'ARRAY') { + $name = &escape($parent).':'.&escape($path->[-2]).':'.$higher; + } + } + my $colspan; + if ($parent ne 'instcode') { + $colspan = $maxdepth - $depth - 1; + $text .= ''; + } + } + } + } + return $text; +} + sub modifiable_userdata_row { - my ($context,$role,$settings,$numinrow,$rowcount) = @_; + my ($context,$role,$settings,$numinrow,$rowcount,$usertypes) = @_; my $rolename; - if ($role eq 'cr') { - $rolename = &mt('Custom role'); + if ($context eq 'selfcreate') { + if (ref($usertypes) eq 'HASH') { + $rolename = $usertypes->{$role}; + } else { + $rolename = $role; + } } else { - $rolename = &Apache::lonnet::plaintext($role); + if ($role eq 'cr') { + $rolename = &mt('Custom role'); + } else { + $rolename = &Apache::lonnet::plaintext($role); + } } my @fields = ('lastname','firstname','middlename','generation', 'permanentemail','id'); @@ -2138,34 +2622,36 @@ sub modify_login { 'adminmail' => 'off', 'newuser' => 'off', ); - foreach my $item (@toggles) { - if ($defaultchecked{$item} eq 'on') { - if (($domconfig{'login'}{$item} eq '0') && - ($env{'form.'.$item} eq '1')) { - $changes{$item} = 1; - } elsif (($domconfig{'login'}{$item} eq '' || - $domconfig{'login'}{$item} eq '1') && - ($env{'form.'.$item} eq '0')) { - $changes{$item} = 1; - } - } elsif ($defaultchecked{$item} eq 'off') { - if (($domconfig{'login'}{$item} eq '1') && - ($env{'form.'.$item} eq '0')) { - $changes{$item} = 1; - } elsif (($domconfig{'login'}{$item} eq '' || - $domconfig{'login'}{$item} eq '0') && - ($env{'form.'.$item} eq '1')) { - $changes{$item} = 1; + if (ref($domconfig{'login'}) eq 'HASH') { + foreach my $item (@toggles) { + if ($defaultchecked{$item} eq 'on') { + if (($domconfig{'login'}{$item} eq '0') && + ($env{'form.'.$item} eq '1')) { + $changes{$item} = 1; + } elsif (($domconfig{'login'}{$item} eq '' || + $domconfig{'login'}{$item} eq '1') && + ($env{'form.'.$item} eq '0')) { + $changes{$item} = 1; + } + } elsif ($defaultchecked{$item} eq 'off') { + if (($domconfig{'login'}{$item} eq '1') && + ($env{'form.'.$item} eq '0')) { + $changes{$item} = 1; + } elsif (($domconfig{'login'}{$item} eq '' || + $domconfig{'login'}{$item} eq '0') && + ($env{'form.'.$item} eq '1')) { + $changes{$item} = 1; + } } } - } - if (($domconfig{'login'}{'loginheader'} eq 'text') && - ($env{'form.loginheader'} eq 'image')) { - $changes{'loginheader'} = 1; - } elsif (($domconfig{'login'}{'loginheader'} eq '' || - $domconfig{'login'}{'loginheader'} eq 'image') && - ($env{'form.loginheader'} eq 'text')) { - $changes{'loginheader'} = 1; + if (($domconfig{'login'}{'loginheader'} eq 'text') && + ($env{'form.loginheader'} eq 'image')) { + $changes{'loginheader'} = 1; + } elsif (($domconfig{'login'}{'loginheader'} eq '' || + $domconfig{'login'}{'loginheader'} eq 'image') && + ($env{'form.loginheader'} eq 'text')) { + $changes{'loginheader'} = 1; + } } if (keys(%changes) > 0 || $colchgtext) { &Apache::loncommon::devalidate_domconfig_cache($dom); @@ -2198,6 +2684,7 @@ sub color_font_choices { img => "Header", bgs => "Background colors", links => "Link colors", + images => "Images", font => "Font color", pgbg => "Page", tabbg => "Header", @@ -2213,6 +2700,11 @@ sub modify_rolecolors { my ($r,$dom,$confname,$roles,%domconfig) = @_; my ($resulttext,%rolehash); $rolehash{'rolecolors'} = {}; + if (ref($domconfig{'rolecolors'}) ne 'HASH') { + if ($domconfig{'rolecolors'} eq '') { + $domconfig{'rolecolors'} = {}; + } + } my ($errors,%changes) = &modify_colors($r,$dom,$confname,$roles, $domconfig{'rolecolors'},$rolehash{'rolecolors'}); my $putresult = &Apache::lonnet::put_dom('configuration',\%rolehash, @@ -2239,7 +2731,7 @@ sub modify_rolecolors { sub modify_colors { my ($r,$dom,$confname,$roles,$domconfig,$confhash) = @_; my (%changes,%choices); - my @bgs = ('pgbg','mainbg','sidebg'); + my @bgs; my @links = ('link','alink','vlink'); my @logintext; my @images; @@ -2254,8 +2746,10 @@ sub modify_colors { } if ($role eq 'login') { @images = ('img','logo','domlogo','login'); + @bgs = ('pgbg','mainbg','sidebg'); } else { @images = ('img'); + @bgs = ('pgbg','tabbg','sidebg'); } $confhash->{$role}{'font'} = $env{'form.'.$role.'_font'}; foreach my $item (@bgs,@links,@logintext) { @@ -2438,7 +2932,7 @@ sub default_change_checker { if ($confhash->{$role}{'font'}) { $changes->{$role}{'font'} = 1; } -} +} sub display_colorchgs { my ($dom,$changes,$roles,$confhash) = @_; @@ -2543,7 +3037,7 @@ sub check_authorstatus { my $end = 0; $author_ok = &Apache::lonnet::assignrole($dom,$confname,'/'.$dom.'/', - 'au',$end,$start); + 'au',$end,$start,'','','domconfig'); } else { $author_ok = 'ok'; } @@ -2806,7 +3300,11 @@ sub modify_quotas { } foreach my $key (keys(%formhash)) { if ($formhash{$key} ne '') { - if (!exists($domconfig{'quotas'}{$key})) { + if (ref($domconfig{'quotas'}) eq 'HASH') { + if (!exists($domconfig{'quotas'}{$key})) { + $changes{$key} = 1; + } + } else { $changes{$key} = 1; } } @@ -3326,21 +3824,81 @@ sub modify_usercreation { my @email_rule = &Apache::loncommon::get_env_multiple('form.email_rule'); my @contexts = ('author','course','selfcreate'); foreach my $item(@contexts) { - $cancreate{$item} = $env{'form.can_createuser_'.$item}; if ($item eq 'selfcreate') { + @{$cancreate{$item}} = &Apache::loncommon::get_env_multiple('form.can_createuser_'.$item); my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); if (!((($domdefaults{'auth_def'} =~/^krb/) && ($domdefaults{'auth_arg_def'} ne '')) || ($domdefaults{'auth_def'} eq 'localauth'))) { - if (($cancreate{$item} eq 'any') || ($cancreate{$item} eq 'login')) { - $warningmsg = &mt('Although account creation has been set to be available for institutional logins, currently default authentication in this domain has not been set to support this.').' '.&mt('You need to set the default authentication type to Kerberos 4 or 5 (with a Kerberos domain specified), or to Local authentication, if the localauth module has been customized in your domain to authenticate institutional logins.'); + if (ref($cancreate{$item}) eq 'ARRAY') { + if (grep(/^login$/,@{$cancreate{$item}})) { + $warningmsg = &mt('Although account creation has been set to be available for institutional logins, currently default authentication in this domain has not been set to support this.').' '.&mt('You need to set the default authentication type to Kerberos 4 or 5 (with a Kerberos domain specified), or to Local authentication, if the localauth module has been customized in your domain to authenticate institutional logins.'); + } } } + } else { + $cancreate{$item} = $env{'form.can_createuser_'.$item}; } } if (ref($curr_usercreation{'cancreate'}) eq 'HASH') { foreach my $item (@contexts) { - if ($curr_usercreation{'cancreate'}{$item} ne $cancreate{$item}) { - push(@{$changes{'cancreate'}},$item); - } + if ($item eq 'selfcreate') { + if (ref($curr_usercreation{'cancreate'}{$item}) eq 'ARRAY') { + foreach my $curr (@{$curr_usercreation{'cancreate'}{$item}}) { + if (!grep(/^$curr$/,@{$cancreate{$item}})) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } else { + if ($curr_usercreation{'cancreate'}{$item} eq '') { + if (@{$cancreate{$item}} > 0) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } else { + if ($curr_usercreation{'cancreate'}{$item} eq 'any') { + if (@{$cancreate{$item}} < 3) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } elsif ($curr_usercreation{'cancreate'}{$item} eq 'none') { + if (@{$cancreate{$item}} > 0) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } elsif (!grep(/^$curr_usercreation{'cancreate'}{$item}$/,@{$cancreate{$item}})) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + foreach my $type (@{$cancreate{$item}}) { + if (ref($curr_usercreation{'cancreate'}{$item}) eq 'ARRAY') { + if (!grep(/^$type$/,@{$curr_usercreation{'cancreate'}{$item}})) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } elsif (($curr_usercreation{'cancreate'}{$item} ne 'any') && + ($curr_usercreation{'cancreate'}{$item} ne 'none')) { + if ($curr_usercreation{'cancreate'}{$item} ne $type) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } + } + } else { + if ($curr_usercreation{'cancreate'}{$item} ne $cancreate{$item}) { + push(@{$changes{'cancreate'}},$item); + } + } } } elsif (ref($curr_usercreation{'cancreate'}) eq 'ARRAY') { foreach my $item (@contexts) { @@ -3447,6 +4005,12 @@ sub modify_usercreation { my $putresult = &Apache::lonnet::put_dom('configuration',\%usercreation_hash, $dom); + + my %selfcreatetypes = ( + sso => 'users authenticated by institutional single sign on', + login => 'users authenticated by institutional log-in', + email => 'users who provide a valid e-mail address for use as the username', + ); if ($putresult eq 'ok') { if (keys(%changes) > 0) { $resulttext = &mt('Changes made:').'
    '; @@ -3455,16 +4019,14 @@ sub modify_usercreation { foreach my $type (@{$changes{'cancreate'}}) { my $chgtext = $lt{$type}.', '; if ($type eq 'selfcreate') { - if ($cancreate{$type} eq 'none') { + if (@{$cancreate{$type}} == 0) { $chgtext .= &mt('creation of a new user account is not permitted.'); - } elsif ($cancreate{$type} eq 'any') { - $chgtext .= &mt('creation of a new account is permitted for users authenticated by institutional log-in and SSO, and also for e-mail addresses used as usernames.'); - } elsif ($cancreate{$type} eq 'login') { - $chgtext .= &mt('creation of a new account is only permitted for users authenticated by institutional log-in.'); - } elsif ($cancreate{$type} eq 'sso') { - $chgtext .= &mt('creation of a new account is only permitted for users authenticated by institutional single sign on.'); - } elsif ($cancreate{$type} eq 'email') { - $chgtext .= &mt('creation of a new account is only permitted for users who provide a valid e-mail address for use as the username.'); + } else { + $chgtext .= &mt('creation of a new account is permitted for:
      '); + foreach my $case (@{$cancreate{$type}}) { + $chgtext .= '
    • '.$selfcreatetypes{$case}.'
    • '; + } + $chgtext .= '
    '; } } else { if ($cancreate{$type} eq 'none') { @@ -3573,10 +4135,11 @@ sub modify_usermodification { $curr_usermodification{$key} = $domconfig{'usermodification'}{$key}; } } - my @contexts = ('author','course'); + my @contexts = ('author','course','selfcreate'); my %context_title = ( author => 'In author context', course => 'In course context', + selfcreate => 'When self creating account', ); my @fields = ('lastname','firstname','middlename','generation', 'permanentemail','id'); @@ -3584,6 +4147,12 @@ sub modify_usermodification { author => ['ca','aa'], course => ['st','ep','ta','in','cr'], ); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + if (ref($types) eq 'ARRAY') { + push(@{$types},'default'); + $usertypes->{'default'} = $othertitle; + } + $roles{'selfcreate'} = $types; my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); my %modifyhash; foreach my $context (@contexts) { @@ -3631,13 +4200,26 @@ sub modify_usermodification { if (ref($changes{$context}) eq 'ARRAY') { foreach my $role (@{$changes{$context}}) { my $rolename; - if ($role eq 'cr') { - $rolename = &mt('Custom'); + if ($context eq 'selfcreate') { + $rolename = $role; + if (ref($usertypes) eq 'HASH') { + if ($usertypes->{$role} ne '') { + $rolename = $usertypes->{$role}; + } + } } else { - $rolename = &Apache::lonnet::plaintext($role); + if ($role eq 'cr') { + $rolename = &mt('Custom'); + } else { + $rolename = &Apache::lonnet::plaintext($role); + } } my @modifiable; - $resulttext .= '
  • '.&mt('Target user with [_1] role',$rolename).' - '.&mt('modifiable fields: '); + if ($context eq 'selfcreate') { + $resulttext .= '
  • '.&mt('Self-creation of account by users with status: [_1] ',$rolename).' - '.&mt('modifiable fields (if institutional data blank): '); + } else { + $resulttext .= '
  • '.&mt('Target user with [_1] role',$rolename).' - '.&mt('modifiable fields: '); + } foreach my $field (@fields) { if ($modifyhash{$context}{$role}{$field}) { push(@modifiable,$fieldtitles{$field}); @@ -3669,7 +4251,7 @@ sub modify_defaults { my ($dom,$r) = @_; my ($resulttext,$mailmsgtxt,%newvalues,%changes,@errors); my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); - my @items = ('auth_def','auth_arg_def','lang_def'); + my @items = ('auth_def','auth_arg_def','lang_def','timezone_def'); my @authtypes = ('internal','krb4','krb5','localauth'); foreach my $item (@items) { $newvalues{$item} = $env{'form.'.$item}; @@ -3690,6 +4272,12 @@ sub modify_defaults { push(@errors,$item); } } + } elsif ($item eq 'timezone_def') { + if ($newvalues{$item} ne '') { + if (!DateTime::TimeZone->is_valid_name($newvalues{$item})) { + push(@errors,$item); + } + } } if (grep(/^\Q$item\E$/,@errors)) { $newvalues{$item} = $domdefaults{$item}; @@ -3701,6 +4289,7 @@ sub modify_defaults { defaults => { auth_def => $newvalues{'auth_def'}, auth_arg_def => $newvalues{'auth_arg_def'}, lang_def => $newvalues{'lang_def'}, + timezone_def => $newvalues{'timezone_def'}, } ); my $title = &defaults_titles(); @@ -3733,10 +4322,12 @@ sub modify_defaults { my $cachetime = 24*60*60; &Apache::lonnet::do_cache_new('domdefaults',$dom, $defaults_hash{'defaults'},$cachetime); - my $sysmail = $r->dir_config('lonSysEMail'); - &Apache::lonmsg::sendemail($sysmail,"LON-CAPA Domain Settings Change - $dom",$mailmsgtext); + if ($changes{'auth_def'} || $changes{'auth_arg_def'} || $changes{'lang_def'}) { + my $sysmail = $r->dir_config('lonSysEMail'); + &Apache::lonmsg::sendemail($sysmail,"LON-CAPA Domain Settings Change - $dom",$mailmsgtext); + } } else { - $resulttext = &mt('No changes made to default authentication/language settings'); + $resulttext = &mt('No changes made to default authentication/language/timezone settings'); } } else { $resulttext = ''. @@ -3753,7 +4344,7 @@ sub modify_defaults { } sub modify_scantron { - my ($r,$dom,$confname,$domconfig) = @_; + my ($r,$dom,$confname,%domconfig) = @_; my ($resulttext,%confhash,%changes,$errors); my $custom = 'custom.tab'; my $default = 'default.tab'; @@ -3772,7 +4363,7 @@ sub modify_scantron { $confname,'scantron','','',$custom); if ($result eq 'ok') { $confhash{'scantron'}{'scantronformat'} = $scantronurl; - $changes{'scantron'}{'scantronformat'} = 1; + $changes{'scantronformat'} = 1; } else { $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$custom,$result); } @@ -3788,13 +4379,11 @@ sub modify_scantron { $errors .= '
  • '.$error.'
  • '; } } - if (ref($domconfig) eq 'HASH') { - if (ref($domconfig->{'scantron'}) eq 'HASH') { - if ($domconfig->{'scantron'}{'scantronformat'} ne '') { - if ($env{'form.scantronformat_del'}) { - $confhash{'scantron'}{'scantronformat'} = ''; - $changes{'scantron'}{'scantronformat'} = 1; - } + if (ref($domconfig{'scantron'}) eq 'HASH') { + if ($domconfig{'scantron'}{'scantronformat'} ne '') { + if ($env{'form.scantronformat_del'}) { + $confhash{'scantron'}{'scantronformat'} = ''; + $changes{'scantronformat'} = 1; } } } @@ -3803,15 +4392,16 @@ sub modify_scantron { $dom); if ($putresult eq 'ok') { if (keys(%changes) > 0) { - $resulttext = &mt('Changes made:').'
      '; - if (ref($changes{'scantron'}) eq 'HASH') { - if ($changes{'scantron'}{'scantronformat'}) { - if ($confhash{'scantron'}{'scantronformat'} eq '') { - $resulttext .= '
    • '.&mt('[_1] scantron format file removed; [_2] file will be used for courses in this domain.',$custom,$default).'
    • '; - } else { - $resulttext .= '
    • '.&mt('Custom scantron format file ([_1]) uploaded for use with courses in this domain.',$custom).'
    • '; - } + if (ref($confhash{'scantron'}) eq 'HASH') { + $resulttext = &mt('Changes made:').'
        '; + if ($confhash{'scantron'}{'scantronformat'} eq '') { + $resulttext .= '
      • '.&mt('[_1] scantron format file removed; [_2] file will be used for courses in this domain.',$custom,$default).'
      • '; + } else { + $resulttext .= '
      • '.&mt('Custom scantron format file ([_1]) uploaded for use with courses in this domain.',$custom).'
      • '; } + $resulttext .= '
      '; + } else { + $resulttext = &mt('Changes made to scantron format file.'); } $resulttext .= '
    '; &Apache::loncommon::devalidate_domconfig_cache($dom); @@ -3832,4 +4422,226 @@ sub modify_scantron { return $resulttext; } +sub modify_coursecategories { + my ($dom,%domconfig) = @_; + my ($resulttext,%deletions,%reorderings,%needreordering,%adds,%changes,$errors, + $cathash); + my @deletecategory = &Apache::loncommon::get_env_multiple('form.deletecategory'); + if (ref($domconfig{'coursecategories'}) eq 'HASH') { + $cathash = $domconfig{'coursecategories'}{'cats'}; + if ($domconfig{'coursecategories'}{'togglecats'} ne $env{'form.togglecats'}) { + $changes{'togglecats'} = 1; + $domconfig{'coursecategories'}{'togglecats'} = $env{'form.togglecats'}; + } + if ($domconfig{'coursecategories'}{'categorize'} ne $env{'form.categorize'}) { + $changes{'categorize'} = 1; + $domconfig{'coursecategories'}{'categorize'} = $env{'form.categorize'}; + } + } else { + $changes{'togglecats'} = 1; + $changes{'categorize'} = 1; + $domconfig{'coursecategories'}{'togglecats'} = $env{'form.togglecats'}; + $domconfig{'coursecategories'}{'categorize'} = $env{'form.categorize'}; + } + if (ref($cathash) eq 'HASH') { + if (($domconfig{'coursecategories'}{'cats'}{'instcode::0'} ne '') && ($env{'form.instcode'} == 0)) { + push (@deletecategory,'instcode::0'); + } + } + my (@predelcats,@predeltrails,%predelallitems,%sort_by_deltrail); + if (ref($cathash) eq 'HASH') { + if (@deletecategory > 0) { + #FIXME Need to remove category from all courses using a deleted category + &Apache::loncommon::extract_categories($cathash,\@predelcats,\@predeltrails,\%predelallitems); + foreach my $item (@deletecategory) { + if ($domconfig{'coursecategories'}{'cats'}{$item} ne '') { + delete($domconfig{'coursecategories'}{'cats'}{$item}); + $deletions{$item} = 1; + &recurse_cat_deletes($item,$cathash,\%deletions); + } + } + } + foreach my $item (keys(%{$cathash})) { + my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item); + if ($cathash->{$item} ne $env{'form.'.$item}) { + $reorderings{$item} = 1; + $domconfig{'coursecategories'}{'cats'}{$item} = $env{'form.'.$item}; + } + if ($env{'form.addcategory_name_'.$item} ne '') { + my $newcat = $env{'form.addcategory_name_'.$item}; + my $newdepth = $depth+1; + my $newitem = &escape($newcat).':'.&escape($cat).':'.$newdepth; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos_'.$item}; + $adds{$newitem} = 1; + } + if ($env{'form.subcat_'.$item} ne '') { + my $newcat = $env{'form.subcat_'.$item}; + my $newdepth = $depth+1; + my $newitem = &escape($newcat).':'.&escape($cat).':'.$newdepth; + $domconfig{'coursecategories'}{'cats'}{$newitem} = 0; + $adds{$newitem} = 1; + } + } + } + if ($env{'form.instcode'} eq '1') { + if (ref($cathash) eq 'HASH') { + my $newitem = 'instcode::0'; + if ($cathash->{$newitem} eq '') { + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.instcode_pos'}; + $adds{$newitem} = 1; + } + } else { + my $newitem = 'instcode::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.instcode_pos'}; + $adds{$newitem} = 1; + } + } + if ($env{'form.addcategory_name'} ne '') { + my $newitem = &escape($env{'form.addcategory_name'}).'::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos'}; + $adds{$newitem} = 1; + } + my $putresult; + if ((keys(%deletions) > 0) || (keys(%reorderings) > 0) || (keys(%adds) > 0)) { + if (keys(%deletions) > 0) { + foreach my $key (keys(%deletions)) { + if ($predelallitems{$key} ne '') { + $sort_by_deltrail{$predelallitems{$key}} = $predeltrails[$predelallitems{$key}]; + } + } + } + my (@chkcats,@chktrails,%chkallitems); + &Apache::loncommon::extract_categories($domconfig{'coursecategories'}{'cats'},\@chkcats,\@chktrails,\%chkallitems); + if (ref($chkcats[0]) eq 'ARRAY') { + my $depth = 0; + my $chg = 0; + for (my $i=0; $i<@{$chkcats[0]}; $i++) { + my $name = $chkcats[0][$i]; + my $item; + if ($name eq '') { + $chg ++; + } else { + $item = &escape($name).'::0'; + if ($chg) { + $domconfig{'coursecategories'}{'cats'}{$item} -= $chg; + } + $depth ++; + &recurse_check(\@chkcats,$domconfig{'coursecategories'}{'cats'},$depth,$name); + $depth --; + } + } + } + } + if ((keys(%changes) > 0) || (keys(%deletions) > 0) || (keys(%reorderings) > 0) || (keys(%adds) > 0)) { + $putresult = &Apache::lonnet::put_dom('configuration',\%domconfig,$dom); + if ($putresult eq 'ok') { + my %title = ( + togglecats => 'Show/Hide a course in the catalog', + categorize => 'Category assigned to course', + ); + my %level = ( + dom => 'set from "Modify Course" (Domain)', + crs => 'set from "Parameters" (Course)', + ); + $resulttext = &mt('Changes made:').'
      '; + if ($changes{'togglecats'}) { + $resulttext .= '
    • '.&mt("$title{'togglecats'} $level{$env{'form.togglecats'}}").'
    • '; + } + if ($changes{'categorize'}) { + $resulttext .= '
    • '.&mt("$title{'categorize'} $level{$env{'form.categorize'}}").'
    • '; + } + if ((keys(%deletions) > 0) || (keys(%reorderings) > 0) || (keys(%adds) > 0)) { + my $cathash; + if (ref($domconfig{'coursecategories'}) eq 'HASH') { + $cathash = $domconfig{'coursecategories'}{'cats'}; + } else { + $cathash = {}; + } + my (@cats,@trails,%allitems); + &Apache::loncommon::extract_categories($cathash,\@cats,\@trails,\%allitems); + if (keys(%deletions) > 0) { + $resulttext .= '
    • '.&mt('Deleted categories:').'
        '; + foreach my $predeltrail (sort {$a <=> $b } (keys(%sort_by_deltrail))) { + $resulttext .= '
      • '.$predeltrails[$predeltrail].'
      • '; + } + $resulttext .= '
    • '; + } + if (keys(%reorderings) > 0) { + my %sort_by_trail; + $resulttext .= '
    • '.&mt('Reordered categories:').'
        '; + foreach my $key (keys(%reorderings)) { + if ($allitems{$key} ne '') { + $sort_by_trail{$allitems{$key}} = $trails[$allitems{$key}]; + } + } + foreach my $trail (sort {$a <=> $b } (keys(%sort_by_trail))) { + $resulttext .= '
      • '.$trails[$trail].'
      • '; + } + $resulttext .= '
    • '; + } + if (keys(%adds) > 0) { + my %sort_by_trail; + $resulttext .= '
    • '.&mt('Added categories:').'
        '; + foreach my $key (keys(%adds)) { + if ($allitems{$key} ne '') { + $sort_by_trail{$allitems{$key}} = $trails[$allitems{$key}]; + } + } + foreach my $trail (sort {$a <=> $b } (keys(%sort_by_trail))) { + $resulttext .= '
      • '.$trails[$trail].'
      • '; + } + $resulttext .= '
    • '; + } + } + $resulttext .= '
    '; + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = &mt('No changes made to course categories'); + } + return $resulttext; +} + +sub recurse_check { + my ($chkcats,$categories,$depth,$name) = @_; + if (ref($chkcats->[$depth]{$name}) eq 'ARRAY') { + my $chg = 0; + for (my $j=0; $j<@{$chkcats->[$depth]{$name}}; $j++) { + my $category = $chkcats->[$depth]{$name}[$j]; + my $item; + if ($category eq '') { + $chg ++; + } else { + my $deeper = $depth + 1; + $item = &escape($category).':'.&escape($name).':'.$depth; + if ($chg) { + $categories->{$item} -= $chg; + } + &recurse_check($chkcats,$categories,$deeper,$category); + $deeper --; + } + } + } + return; +} + +sub recurse_cat_deletes { + my ($item,$coursecategories,$deletions) = @_; + my ($deleted,$container,$depth) = map { &unescape($_); } split(/:/,$item); + my $subdepth = $depth + 1; + if (ref($coursecategories) eq 'HASH') { + foreach my $subitem (keys(%{$coursecategories})) { + my ($child,$parent,$itemdepth) = map { &unescape($_); } split(/:/,$subitem); + if (($parent eq $deleted) && ($itemdepth == $subdepth)) { + delete($coursecategories->{$subitem}); + $deletions->{$subitem} = 1; + &recurse_cat_deletes($subitem,$coursecategories,$deletions); + } + } + } + return; +} + 1;
'.&mt('Default in use:').'
'; + $datatable .= '
'.&mt('Default in use:').'
'. + ''; if ($scantronurl) { $datatable .= ''. &mt('Default scantron format file').''; } else { $datatable = &mt('File unavailable for display'); } - $datatable .= '
'.&mt('Upload:').'
'; + $datatable .= '
'; + if (!$switchserver) { + $datatable .= &mt('Upload:').'
'; + } + } else { + my $errorstr; + foreach my $key (sort(keys(%error))) { + $errorstr .= $lt{$key}.': '.$error{$key}.'
'; + } + $datatable .= '
'.$errorstr; + } } else { if (keys(%error) > 0) { my $errorstr; foreach my $key (sort(keys(%error))) { $errorstr .= $lt{$key}.': '.$error{$key}.'
'; } - $datatable .= '
'.$errorstr.''; + $datatable .= ''.$errorstr.' '; } elsif ($scantronurl) { - $datatable .= '' .&mt('Custom scantron format file').'' - .''.&mt('Replace:').'
'; + $datatable .= '
'. + ''. + &mt('Custom scantron format file').' '. + &mt('Replace:').'
'; } } if (keys(%error) == 0) { if ($switchserver) { $datatable .= &mt('Upload to library server: [_1]',$switchserver); } else { - $datatable .=' '; + $datatable .=' '. + ''; } } $datatable .= '
'.$title{'togglecats'}.' '. + '
'.$title{'categorize'}.''. + ' '. + '
' + .''; + if ($parent eq 'instcode') { + $datatable .= ''.&mt('Official courses') + .'
(' + .&mt('with institutional codes').')
'.&mt('Add category:').' ' + .'
'. + '' + .&mt('Official courses').''.'
(' + .&mt('with institutional codes').')
 ' + .'
'.$hdritem->{'header'}->[0]->{'col2'}.'
' + .' ' + .&mt('Official courses (with institutional codes)') + .'' + .' 
' + .' ' + .&mt('Add category').''.&mt('Name:') + .' 
'; + my ($idxnum,$parent_name,$parent_item); + my $higher = $depth - 1; + if ($higher == 0) { + $parent_name = &escape($parent).'::'.$higher; + } else { + if (ref($path) eq 'ARRAY') { + $parent_name = &escape($parent).':'.&escape($path->[-2]).':'.$higher; + } + } + $parent_item = 'addcategory_pos_'.$parent_name; + for (my $j=0; $j<=$numchildren; $j++) { + if ($j < $numchildren) { + $name = $cats->[$depth]{$parent}[$j]; + $item = &escape($name).':'.&escape($parent).':'.$depth; + $idxnum = $idx->{$item}; + } else { + $name = $parent_name; + $item = $parent_item; + } + $chgstr = ' onchange="javascript:reorderCats(this.form,'."'$parent_name','$item','$idxnum'".');"'; + $text .= ''; + } + $text .= '
 '; + if ($j < $numchildren) { + my $deeper = $depth+1; + $text .= $name.' ' + .''; + if(ref($path) eq 'ARRAY') { + push(@{$path},$name); + $text .= &build_category_rows($itemcount,$cats,$deeper,$name,$path,$idx); + pop(@{$path}); + } + } else { + $text .= &mt('Add subcategory:').' '; + } + $text .= '
'.&mt('Add subcategory:').'