--- loncom/interface/courseprefs.pm 2009/04/26 00:07:16 1.1 +++ loncom/interface/courseprefs.pm 2009/10/13 04:48:03 1.8 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA -# Handler to set domain-wide configuration settings +# Handler to set configuration settings for a course # -# $Id: courseprefs.pm,v 1.1 2009/04/26 00:07:16 raeburn Exp $ +# $Id: courseprefs.pm,v 1.8 2009/10/13 04:48:03 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -58,7 +58,7 @@ sub handler { } else { if ($navmap) { $env{'user.error.msg'}= - "/adm/courseprefs:mau:0:0:Cannot modify course settings"; + "/adm/courseprefs:opa:0:0:Cannot modify course settings"; } else { $env{'user.error.msg'}= "/adm/courseprefs::0:1:Course environment gone, reinitialize the course"; @@ -66,15 +66,18 @@ sub handler { return HTTP_NOT_ACCEPTABLE; } + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, + ['phase','actions','origin']); &Apache::lonhtmlcommon::clear_breadcrumbs(); + if ($env{'form.origin'} eq 'params') { + &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/parmset", + text=>"Parameter Manager"}); + } &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/courseprefs', text=>"Course Configuration"}); my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Edit Course Configuration'); - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['phase','actions']); - my $phase = 'pickactions'; if ( exists($env{'form.phase'}) ) { $phase = $env{'form.phase'}; @@ -99,6 +102,16 @@ sub handler { ordered => ['description','courseid','categories', 'hidefromcat','cloners','externalsyllabus', 'url','rolenames'], + itemtext => { + description => 'Course Description', + courseid => 'Course ID or number', + categories => 'Categorize course', + hidefromcat => 'Exclude from course catalog', + cloners => 'Users allowed to clone course', + externalsyllabus => 'URL of Syllabus', + url => 'Top Level Map', + rolenames => 'Replacement titles for standard course roles', + }, }, 'localization' => { text => 'Language/TimeZone/Locale', @@ -106,6 +119,11 @@ sub handler { header => [{col1 => 'Setting', col2 => 'Value',}], ordered => ['languages','timezone','datelocale'], + itemtext => { + languages => 'Languages used', + timezone => 'Timezone in which the course takes place', + datelocale => 'Locale used for course calendar', + }, }, 'feedback' => { text => 'Feedback messages', @@ -114,7 +132,12 @@ sub handler { col2 => 'Recipients'}, {col1 => 'Questions about:', col2 => 'Custom Text'}], - ordered => ['question','comment','policy'], + ordered => ['question.email','comment.email','policy.email'], + itemtext => { + 'question.email' => 'Resource Content', + 'comment.email' => 'Course Content', + 'policy.email' => 'Course Policy', + }, }, 'discussion' => { text => 'Discussion and Chat', @@ -125,6 +148,14 @@ sub handler { 'pch.roles.denied','pch.users.denied', 'allow_limited_html_in_feedback', 'allow_discussion_post_editing'], + itemtext => { + 'plc.roles.denied' => 'No Resource Discussion', + 'plc.users.denied' => 'No Resource Discussion', + 'pch.roles.denied' => 'No Chat room use', + 'pch.users.denied' => 'No Chat room use', + allow_limited_html_in_feedback => 'Allow limited HTML in discussion posts', + allow_discussion_post_editing => 'Users can edit/delete own discussion posts', + }, }, 'classlists' => { text => 'Classlists and Staff Listing', @@ -139,6 +170,14 @@ sub handler { 'default_enrollment_end_date', 'nothideprivileged','student_classlist_view', 'student_opt_in','student_classlist_portfiles'], + itemtext => { + default_enrollment_start_date => 'Start date', + default_enrollment_end_date => 'End date', + nothideprivileged => 'Domain Coodinators in course', + student_classlist_view => 'Student-viewable classlist', + student_opt_in => 'Student agreement needed to be listed', + student_classlist_portfiles => 'Include link to accessible portfolio files', + }, }, 'appearance' => { text => 'Display of resources ', @@ -148,6 +187,13 @@ sub handler { ordered => ['default_xml_style','pageseparators', 'disable_receipt_display','texengine', 'tthoptions'], + itemtext => { + default_xml_style => 'Default XML Style File', + pageseparators => 'Visibly Separate Items on Pages', + disable_receipt_display => 'Disable display of problem receipts', + texengine => 'Force use of a specific math rendering engine', + tthoptions => 'Default set of options to pass to tth/m when converting TeX', + }, }, 'grading' => { text => 'Grading', @@ -156,6 +202,13 @@ sub handler { col2 => 'Value',}], ordered => ['grading','rndseed', 'receiptalg','disablesigfigs'], + itemtext => { + grading => 'Grading', + rndseed => 'Randomization algorithm used', + receiptalg => 'Receipt algorithm used', + disablesigfigs => 'Disable checking of Significant Figures', + }, + }, 'printouts' => { text => 'Printout generation', @@ -165,6 +218,13 @@ sub handler { ordered => ['problem_stream_switch','suppress_tries', 'default_paper_size','print_header_format', 'disableexampointprint'], + itemtext => { + problem_stream_switch => 'Allow problems to be split over pages', + suppress_tries => 'Suppress number of tries in printing', + default_paper_size => 'Default paper type', + print_header_format => 'Print header format', + disableexampointprint => 'Disable automatically printing point values on exams', + }, }, 'spreadsheet' => { text => 'Spreadsheets', @@ -174,6 +234,12 @@ sub handler { ordered => ['spreadsheet_default_classcalc', 'spreadsheet_default_studentcalc', 'spreadsheet_default_assesscalc','hideemptyrows'], + itemtext => { + spreadsheet_default_classcalc => 'Default Course Spreadsheet', + spreadsheet_default_studentcalc => 'Default Student Spreadsheet', + spreadsheet_default_assesscalc => 'Default Assessment Spreadsheet', + hideemptyrows => 'Hide Empty Rows in Spreadsheets', + }, }, 'bridgetasks' => { text => 'Bridge tasks', @@ -182,6 +248,11 @@ sub handler { col2 => 'Value'}], ordered => ['task_messages','task_grading', 'suppress_embed_prompt'], + itemtext => { + task_messages => 'Send message to student when clicking Done on Tasks', + task_grading => 'Bridge Task grading by instructors and TAs in sections' , + suppress_embed_prompt => 'Hide upload references prompt if uploading file to portfolio', + }, }, 'other' => { text => 'Other settings', @@ -192,21 +263,13 @@ sub handler { }, ); if ($phase eq 'process') { + my @allitems = &get_allitems(%prefs); &Apache::lonconfigsettings::make_changes($r,$cdom,$phase,$context, - \@prefs_order,\%prefs,\%values); + \@prefs_order,\%prefs,\%values, + $cnum,undef,\@allitems); } elsif ($phase eq 'display') { - my $jscript = &get_jscript($cdom); - my @allitems; - foreach my $item (keys(%prefs)) { - if (ref($prefs{$item}) eq 'HASH') { - if (ref($prefs{$item}{'ordered'}) eq 'ARRAY') { - push(@allitems,@{$prefs{$item}{'ordered'}}); - if ($item eq 'feedback') { - push(@allitems,(map { $_.'.email'; } @{$prefs{$item}{'ordered'}})); - } - } - } - } + my $jscript = &get_jscript($cdom,$phase); + my @allitems = &get_allitems(%prefs); &Apache::lonconfigsettings::display_settings($r,$cdom,$phase,$context, \@prefs_order,\%prefs,\%values,undef,$jscript,\@allitems); } else { @@ -216,9 +279,26 @@ sub handler { return OK; } +sub get_allitems { + my (%prefs) = @_; + my @allitems; + foreach my $item (keys(%prefs)) { + if (ref($prefs{$item}) eq 'HASH') { + if (ref($prefs{$item}{'ordered'}) eq 'ARRAY') { + push(@allitems,@{$prefs{$item}{'ordered'}}); + if ($item eq 'feedback') { + push(@allitems,(map { $_.'.text'; } @{$prefs{$item}{'ordered'}})); + } + } + } + } + return @allitems; +} + sub print_config_box { my ($r,$cdom,$phase,$action,$item,$settings,$allitems) = @_; my $ordered = $item->{'ordered'}; + my $itemtext = $item->{'itemtext'}; my $rowtotal = 0; my $output = '<table class="LC_nested_outer"> @@ -238,9 +318,9 @@ sub print_config_box { </tr>'; $rowtotal ++; if ($action eq 'feedback') { - $output .= &print_feedback('top',$cdom,$settings,$ordered,\$rowtotal); + $output .= &print_feedback('top',$cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'classlists') { - $output .= &print_classlists('top',$cdom,$settings,\$rowtotal); + $output .= &print_classlists('top',$cdom,$settings,$itemtext,\$rowtotal); } $output .= ' </table> @@ -255,7 +335,7 @@ sub print_config_box { <td class="LC_right_item">'.&mt($item->{'header'}->[1]->{'col2'}).'</td> </tr>'; if ($action eq 'classlists') { - $output .= &print_classlists('middle',$cdom,$settings,\$rowtotal). + $output .= &print_classlists('middle',$cdom,$settings,$itemtext,\$rowtotal). ' </table> </td> @@ -280,25 +360,25 @@ sub print_config_box { } $rowtotal ++; if ($action eq 'courseinfo') { - $output .= &print_courseinfo($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_courseinfo($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'localization') { - $output .= &print_localization($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_localization($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'feedback') { - $output .= &print_feedback('bottom',$cdom,$settings,$ordered,\$rowtotal); + $output .= &print_feedback('bottom',$cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'discussion') { - $output .= &print_discussion($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_discussion($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'classlists') { - $output .= &print_classlists('bottom',$cdom,$settings,\$rowtotal); + $output .= &print_classlists('bottom',$cdom,$settings,$itemtext,\$rowtotal); } elsif ($action eq 'appearance') { - $output .= &print_appearance($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_appearance($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'grading') { - $output .= &print_grading($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_grading($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'printouts') { - $output .= &print_printouts($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_printouts($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'spreadsheet') { - $output .= &print_spreadsheet($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_spreadsheet($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'bridgetasks') { - $output .= &print_bridgetasks($cdom,$settings,$ordered,\$rowtotal); + $output .= &print_bridgetasks($cdom,$settings,$ordered,$itemtext,\$rowtotal); } elsif ($action eq 'other') { $output .= &print_other($cdom,$settings,$allitems,\$rowtotal); } @@ -311,12 +391,675 @@ sub print_config_box { } sub process_changes { - my ($r,$cdom,$action,$values) = @_; - my %domconfig; - if (ref($values) eq 'HASH') { - %domconfig = %{$values}; + my ($cdom,$action,$values,$item,$changes,$allitems,$disallowed) = @_; + my %newvalues; + if (ref($item) eq 'HASH') { + if (ref($changes) eq 'HASH') { + my @ordered; + if ($action eq 'other') { + @ordered = &get_other_items($cdom,$values,$allitems); + if ($env{'form.newp_name'} ne '') { + my $newp = $env{'form.newp_name'}; + if ($env{'form.newp_value'} ne '') { + if (ref($allitems) eq 'ARRAY') { + unless ((grep(/^\Q$newp\E$/,@ordered)) || + (grep(/^\Q$newp\E$/,@{$allitems}))) { + $changes->{$newp} = $env{'form.newp_value'}; + } + } + } + } + } elsif (ref($item->{'ordered'}) eq 'ARRAY') { + @ordered = @{$item->{'ordered'}}; + } + if (@ordered > 0) { + if ($action eq 'feedback') { + foreach my $entry (@ordered) { + my $userstr = ''; + my $total = $env{'form.'.$entry.'_total'}; + if ($total) { + my @deletes = &Apache::loncommon::get_env_multiple('form.'.$entry.'_delete'); + for (my $i=0; $i<$total; $i++) { + unless (grep(/^$i$/,@deletes)) { + $userstr .= $env{'form.'.$entry.'_user_'.$i}. + &get_sec_str($entry,$i).','; + } + } + } else { + $total = 0; + } + if ($env{'form.'.$entry.'_uname_'.$total} ne '') { + my $uname = $env{'form.'.$entry.'_uname_'.$total}; + my $udom = $env{'form.'.$entry.'_udom_'.$total}; + if (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') { + $userstr =~ s/,$//; + $disallowed->{'feedback'}{$entry} = $uname.':'.$udom; + } else { + $userstr .= $uname.':'.$udom.&get_sec_str($entry,$total); + } + } else { + $userstr =~ s/,$//; + } + $newvalues{$entry} = $userstr; + if ($newvalues{$entry} ne $values->{$entry}) { + $changes->{$entry} = $newvalues{$entry}; + } + my $ext_entry = $entry.'.text'; + $newvalues{$ext_entry} = $env{'form.'.$ext_entry}; + if ($newvalues{$ext_entry} ne $values->{$ext_entry}) { + $changes->{$ext_entry} = $newvalues{$ext_entry}; + } + } + } else { + foreach my $entry (@ordered) { + if ($entry eq 'cloners') { + if ($env{'form.cloners_all'}) { + $newvalues{$entry} = '*'; + } else { + my @clonedoms; + if (exists($env{'form.cloners_activate'})) { + my $actnum = $env{'form.cloners_activate'}; + if ($actnum ne '') { + if ($env{'form.clonersdom_'.$actnum} ne '') { + my $clonedom = $env{'form.clonersdom_'.$actnum}; + if (&check_clone($clonedom,$disallowed) eq 'ok') { + $newvalues{$entry} = '*:'.$clonedom; + push(@clonedoms,$newvalues{$entry}); + } + } + } + } else { + my $num = $env{'form.cloners_total'}; + my @deletes = + &Apache::loncommon::get_env_multiple('form.cloners_delete'); + for (my $i=0; $i<$num; $i++) { + if (!grep(/^$i$/,@deletes)) { + my $clonedom = $env{'form.cloners_dom_'.$i}; + if (&check_clone($clonedom,$disallowed) eq 'ok') { + if (!grep(/^\*:\Q$clonedom\E$/,@clonedoms)) { + push (@clonedoms,'*:'.$clonedom); + } + } + } + } + if (@clonedoms) { + $newvalues{$entry}=join(',',@clonedoms); + } + } + if ($env{'form.cloners_newdom'} ne '') { + my $clonedom = $env{'form.cloners_newdom'}; + if (&check_clone($clonedom,$disallowed) eq 'ok') { + my $newdom = '*:'.$env{'form.cloners_newdom'}; + if (@clonedoms) { + if (!grep(/^\Q$newdom\E$/,@clonedoms)) { + $newvalues{$entry} .= ','.$newdom; + } + } else { + $newvalues{$entry} = $newdom; + } + } + } + if ($env{'form.'.$entry} ne '') { + my @cloners = split(',',$env{'form.'.$entry}); + my @okcloners; + foreach my $cloner (@cloners) { + my ($uname,$udom) = split(':',$cloner); + if (&check_clone($udom,$disallowed,$uname) eq 'ok') { + if (!grep(/^\Q$cloner\E$/,@okcloners)) { + push(@okcloners,$cloner); + } + } + } + if (@okcloners) { + my $okclonestr = join(',',@okcloners); + if ($newvalues{$entry} ne '') { + $newvalues{$entry} .= ','.$okclonestr; + } else { + $newvalues{$entry} = $okclonestr; + } + } + } + } + if (ref($disallowed) eq 'HASH') { + if (ref($disallowed->{'cloners'}) eq 'HASH') { + foreach my $key (keys(%{$disallowed->{'cloners'}})) { + $disallowed->{'cloners'}{$key} =~ s/,$//; + } + } + } + } elsif ($entry =~ /^default_enrollment_(start|end)_date$/) { + $newvalues{$entry}=&Apache::lonhtmlcommon::get_date_from_form($entry); + } elsif ($entry eq 'rolenames') { + my $crstype = &Apache::loncommon::course_type(); + my %adv_roles = + &Apache::lonnet::get_course_adv_roles($env{'request.course.id'},1); + my @stds = ('cc','in','ta','ep','ad','st'); + my (@replacements,@regulars); + foreach my $role (@stds) { + if ($values->{$role.'.plaintext'} ne '') { + push(@replacements,$role); + } else { + push(@regulars,$role); + } + } + foreach my $stdrole (@stds) { + my $ext_entry = $entry.'_'.$stdrole; + my $stdname = &Apache::lonnet::plaintext($stdrole,$crstype, + $env{'request.course.id'},1); + if ($env{'form.'.$ext_entry} eq $stdname) { + $newvalues{$ext_entry} = ''; + } else { + $newvalues{$ext_entry} = $env{'form.'.$ext_entry}; + } + if ($newvalues{$ext_entry} ne $values->{$stdrole.'.plaintext'}) { + my $dupname = 0; + if ($newvalues{$ext_entry} ne '') { + if (grep(/^\Q$newvalues{$ext_entry}\E$/,@replacements)) { + $dupname = 1; + push(@{$disallowed->{'rolenames'}{'replacements'}},$newvalues{$ext_entry}); + } + if (!$dupname) { + if (grep(/^\Q$newvalues{$ext_entry}\E$/,@regulars)) { + $dupname = 1; + push(@{$disallowed->{rolenames}{'regulars'}},$newvalues{$ext_entry}); + } + } + if (!$dupname) { + foreach my $role (keys(%adv_roles)) { + if ($role =~ m{^cr/$match_domain/$match_name/\Q$newvalues{$ext_entry}\E$}) { + $dupname = 1; + push(@{$disallowed->{rolenames}{'customrole'}},$newvalues{$ext_entry}); + last; + } + } + } + } + if (!$dupname) { + $changes->{$ext_entry} = $newvalues{$ext_entry}; + } + } + } + } elsif (($entry eq 'plc.roles.denied') || ($entry eq 'pch.roles.denied')) { + my @denied = &Apache::loncommon::get_env_multiple('form.'.$entry); + @denied = sort(@denied); + my $deniedstr = ''; + if (@denied > 0) { + $deniedstr = join(',',@denied); + } + $newvalues{$entry} = $deniedstr; + } elsif (($entry eq 'plc.users.denied') || ($entry eq 'pch.users.denied')) { + my $total = $env{'form.'.$entry.'_total'}; + my $userstr = ''; + my @denied; + if ($total > 0) { + my @deletes = + &Apache::loncommon::get_env_multiple('form.'.$entry.'_delete'); + for (my $i=0; $i<$total; $i++) { + unless (grep(/^$i$/,@deletes)) { + $userstr .= $env{'form.'.$entry.'_user_'.$i}.','; + push(@denied,$env{'form.'.$entry.'_user_'.$i}); + } + } + } else { + $total = 0; + } + if ($env{'form.'.$entry.'_uname_'.$total} ne '') { + my $uname = $env{'form.'.$entry.'_uname_'.$total}; + my $udom = $env{'form.'.$entry.'_udom_'.$total}; + if (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') { + $userstr =~ s/,$//; + $disallowed->{'discussion'}{$entry} = $uname.':'.$udom; + } else { + my $newuser .= $uname.':'.$udom; + if (grep(/^\Q$newuser\E$/,@denied)) { + $userstr =~ s/,$//; + } else { + $userstr .= $newuser; + } + } + } else { + $userstr =~ s/,$//; + } + $newvalues{$entry} = $userstr; + } elsif ($entry eq 'allow_discussion_post_editing') { + my @canedit = &Apache::loncommon::get_env_multiple('form.'.$entry); + @canedit = sort(@canedit); + foreach my $role (@canedit) { + my @secs = &Apache::loncommon::get_env_multiple('form.'.$entry.'_sections_'.$role); + if ((grep(/^\s*$/,@secs)) || (@secs == 0)) { + $newvalues{$entry} .= $role.','; + } else { + foreach my $sec (@secs) { + $newvalues{$entry} .= $role.':'.$sec.','; + } + } + } + $newvalues{$entry} =~ s/,$//; + } elsif ($entry eq 'nothideprivileged') { + my @curr_nothide; + my @new_nothide; + if ($values->{$entry} ne '') { + foreach my $user (split(/\s*\,\s*/,$values->{$entry})) { + my $nothide; + if ($user !~ /:/) { + $nothide = join(':',split(/[\@]/,$user)); + } else { + $nothide = $user; + } + if ((defined($nothide)) && + (!grep(/^\Q$nothide\E$/,@curr_nothide))) { + push(@curr_nothide,$nothide); + } + } + } + foreach my $key (keys(%env)) { + if ($key =~ /^form\.\Q$entry\E_($match_username:$match_domain)$/) { + if ($env{$key}) { + my $nothide = $1; + if (!grep(/^\Q$nothide\E$/,@new_nothide)) { + push(@new_nothide,$nothide); + } + } + } + } + @new_nothide = sort(@new_nothide); + my @differences = + &Apache::loncommon::compare_arrays(\@curr_nothide, + \@new_nothide); + if (@differences > 0) { + if (@new_nothide > 0) { + $newvalues{$entry} = join(',',@new_nothide); + } else { + $newvalues{$entry} = ''; + } + } else { + $newvalues{$entry} = $values->{$entry}; + } + } elsif ($entry eq 'print_header_format') { + my $maxnum = $env{'form.printfmthdr_maxnum'}; + my @newhdr; + if ($maxnum > 2) { + for (my $i=0; $i<$maxnum-2; $i++) { + if ($env{'form.printfmthdr_del_'.$i}) { + $newhdr[$env{'form.printfmthdr_pos_'.$i}] = ''; + } else { + my $hdr; + if ($env{'form.printfmthdr_sub_'.$i} =~ /^[nca]$/) { + $hdr = '%'; + if ($env{'form.printfmthdr_limit_'.$i} =~ /^\d+$/) { + $hdr .= $env{'form.printfmthdr_limit_'.$i}; + } + $hdr .= $env{'form.printfmthdr_sub_'.$i}; + } elsif ($env{'form.printfmthdr_sub_'.$i} ne '') { + $hdr = $env{'form.printfmthdr_sub_'.$i}; + } + $newhdr[$env{'form.printfmthdr_pos_'.$i}] = $hdr; + } + } + } + my $newsub = $maxnum-2; + if ($env{'form.printfmthdr_sub_'.$newsub} =~ /^[nca]$/) { + my $hdr = '%'; + if ($env{'form.printfmthdr_limit_'.$newsub} =~ /^\d+$/) { + $hdr .= $env{'form.printfmthdr_limit_'.$newsub}; + } + $hdr .= $env{'form.printfmthdr_sub_'.$newsub}; + $newhdr[$env{'form.printfmthdr_pos_'.$newsub}] = $hdr; + } + my $newtext = $maxnum-1; + $newhdr[$env{'form.printfmthdr_pos_'.$newtext}] = $env{'form.printfmthdr_text_'.$newtext}; + $newvalues{$entry} = join('',@newhdr); + } elsif ($entry eq 'languages') { + my $langstr; + my $total = $env{'form.'.$entry.'_total'}; + if ($total) { + my @deletes = &Apache::loncommon::get_env_multiple('form.'.$entry.'_delete'); + for (my $i=0; $i<$total; $i++) { + unless (grep(/^$i$/,@deletes)) { + $langstr .= $env{'form.'.$entry.'_'.$i}.','; + } + } + } else { + $total = 0; + } + if ($env{'form.'.$entry.'_'.$total} ne '') { + my $newlang = $env{'form.'.$entry.'_'.$total}; + my %langchoices = &get_lang_choices(); + if ($langchoices{$newlang}) { + $langstr .= $newlang; + } else { + $langstr =~ s/,$//; + $disallowed->{'localization'}{$entry} = $newlang; + } + } else { + $langstr =~ s/,$//; + } + $newvalues{$entry} = $langstr; + } else { + $newvalues{$entry} = $env{'form.'.$entry}; + } + if ($newvalues{$entry} ne $values->{$entry}) { + $changes->{$entry} = $newvalues{$entry}; + } + } + } + } + } + } + return; +} + +sub get_sec_str { + my ($entry,$num) = @_; + my @secs = &Apache::loncommon::get_env_multiple('form.'.$entry.'_sections_'.$num); + my $secstr; + if (grep(/^\s*$/,@secs)) { + $secstr = ''; + } elsif (@secs > 0) { + $secstr = join(';',@secs); } - return &modify_crsenv($action,$cdom,%domconfig); + if ($secstr ne '') { + return '('.$secstr.')'; + } + return; +} + +sub check_clone { + my ($clonedom,$disallowed,$clonename) = @_; + return if (ref($disallowed) ne 'HASH'); + if ($clonedom !~ /^$match_domain$/) { + $disallowed->{'cloners'}{'format'} .= $clonedom.','; + return; + } elsif (!&Apache::lonnet::domain($clonedom)) { + $disallowed->{'cloners'}{'domain'} .= $clonedom.','; + return; + } + if ($clonename ne '') { + if ($clonename !~ /^$match_username$/) { + $disallowed->{'cloners'}{'format'} .= $clonename.':'.$clonedom.','; + return; + } else { + if (&Apache::lonnet::homeserver($clonename,$clonedom) eq 'no_host') { + $disallowed->{'cloners'}{'newuser'} .= $clonename.':'.$clonedom.','; + return; + } + } + } + return 'ok'; +} + +sub store_changes { + my ($cdom,$cnum,$prefs_order,$actions,$prefs,$values,$changes) = @_; + my ($chome,$output); + my (%storehash,@delkeys,@need_env_update); + if ((ref($values) eq 'HASH') && (ref($changes) eq 'HASH')) { + %storehash = %{$values}; + } else { + $output = &mt('No changes made to course settings.'); + return; + } + my %yesno = ( + hidefromcat => '1', + problem_stream_switch => '1', + suppress_tries => '1', + disableexampointprint => '1', + hideemptyrows => '1', + suppress_embed_prompt => '1', + ); + foreach my $item (@{$prefs_order}) { + if (grep(/^\Q$item\E$/,@{$actions})) { + $output .= '<h3>'.&mt($prefs->{$item}{'text'}).'</h3>'; + if (ref($changes->{$item}) eq 'HASH') { + if (keys(%{$changes->{$item}}) > 0) { + $output .= &mt('Changes made:').'<ul>'; + if ($item eq 'other') { + foreach my $key (sort(keys(%{$changes->{$item}}))) { + $storehash{$key} = $changes->{$item}{$key}; + if ($changes->{$item}{$key} eq '') { + push(@delkeys,$key); + $output .= '<li>'.&mt('Deleted setting for [_1]','<i>'.$key.'</i>').'</li>'; + } else { + $output .= '<li>'.&mt('[_1] set to [_2]','<i>'.$key.'</i>', + "'$storehash{$key}'").'</li>'; + } + } + } else { + if (ref($prefs->{$item}->{'ordered'}) eq 'ARRAY') { + my @settings = @{$prefs->{$item}->{'ordered'}}; + if ($item eq 'feedback') { + push(@settings,(map { $_.'.text'; } @settings)); + } + foreach my $key (@settings) { + if ($key eq 'rolenames') { + my $displayname = $prefs->{$item}->{'itemtext'}{$key}; + my $crstype = &Apache::loncommon::course_type(); + my $msg; + foreach my $role ('cc','in','ta','ep','ad','st') { + next if (!exists($changes->{$item}{$key.'_'.$role})); + my $stdname = &Apache::lonnet::plaintext($role,$crstype,undef,1); + my $newname = $changes->{$item}{$key.'_'.$role}; + $storehash{$role.'.plaintext'} = $newname; + if ($newname eq '') { + $newname = $stdname; + } + $msg .= '<li>'.&mt('[_1] set to [_2]','<i>'.$stdname.'</i>', + "'<b>".$newname."</b>'").'</li>'; + } + if ($msg ne '') { + $output .= '<li>'.&mt($displayname).'<ul>'.$msg.'</ul></li>'; + } + } else { + next if (!exists($changes->{$item}{$key})); + my ($displayname,$text); + $text = $prefs->{$item}->{'itemtext'}{$key}; + my $displayval = $changes->{$item}{$key}; + if ($item eq 'feedback') { + if ($key =~ /^(question|policy|comment)(\.email)\.text$/) { + $text = $prefs->{$item}->{'itemtext'}{$1.$2}; + $displayname = &mt('Custom text for '.$text.' questions'); + } else { + $displayname = &mt('Recipients of '.$text.' questions'); + } + } elsif ($item eq 'discussion') { + if ($key =~ /^p(lc|ch)\.roles\.denied/) { + $displayname = &mt("$text (role-based)"); + if ($displayval ne '') { + my @roles = split(',',$displayval); + @roles = map { &Apache::lonnet::plaintext($_); } @roles; + $displayval = join(', ',@roles); + } + } elsif ($key =~ /^p(lc|ch)\.users\.denied/) { + $displayname = &mt("$text (specific user(s))"); + } else { + if ($key eq 'allow_discussion_post_editing') { + if ($displayval ne '') { + my @roles = split(',',$displayval); + my @longroles; + foreach my $role (@roles) { + my ($trole,$sec) = split(':',$role); + my $rolename = + &Apache::lonnet::plaintext($trole); + if ($sec ne '') { + $rolename .= ':'.$sec; + } + push(@longroles,$rolename); + } + $displayval = join(', ',@longroles); + } + } + $displayname = &mt($text); + } + } elsif ($item eq 'spreadsheet') { + if ($key =~ /^spreadsheet_default_(studentcalc|assesscalc)$/x) { + my $sheettype = $1; + if ($sheettype eq 'studentcalc') { + &Apache::lonnet::expirespread('','','studentcalc'); + } else { + &Apache::lonnet::expirespread('','','assesscalc'); + &Apache::lonnet::expirespread('','','studentcalc'); + } + } + $displayname = &mt($text); + } else { + $displayname = &mt($text); + } + if (defined($yesno{$key})) { + $displayval = 'no'; + if ($changes->{$item}{$key} eq 'yes') { + $displayval = 'yes'; + } + } elsif (($key =~ /^default_enrollment_(start|end)_date$/) && ($displayval)) { + $displayval = &Apache::lonlocal::locallocaltime($displayval); + } elsif ($key eq 'categories') { + $displayval = $env{'form.categories_display'}; + } + if ($changes->{$item}{$key} eq '') { + push(@delkeys,$key); + $output .= '<li>'.&mt('Deleted setting for [_1]', + '<i>'.$displayname.'</i>').'</li>'; + } else { + $output .= '<li>'.&mt('[_1] set to [_2]', + '<i>'.$displayname.'</i>', + "'<b>$displayval</b>'"); + if ($key eq 'url') { + my $bkuptime=time; + $output .= (' 'x2).&mt('(Previous URL backed up)').': '. + $storehash{'top level map backup '.$bkuptime} => $values->{$key}; + } + $output .= '</li>'; + } + $storehash{$key} = $changes->{$item}{$key}; + } + if (($key eq 'description') || ($key eq 'cloners') || + ($key eq 'hidefromcat') || ($key eq 'categories')) { + push(@need_env_update,$key); + } + } + } + } + $output .= '</ul>'; + } else { + $output = &mt('No changes made to course settings.'); + } + } + } + } + if (&Apache::lonnet::put('environment',\%storehash,$cdom,$cnum) eq 'ok') { + if (@delkeys) { + if (&Apache::lonnet::del('environment',\@delkeys,$cdom,$cnum) ne 'ok') { + $output .= '<br /><span class="LC_error">'. + &mt('An error occurred when removing course settings which are no longer in use.').'</span>'; + } + } + if (@need_env_update) { + $chome = &Apache::lonnet::homeserver($cnum,$cdom); + &update_env($cnum,$cdom,$chome,\@need_env_update,\%storehash); + } + &Apache::lonnet::coursedescription($env{'request.course.id'}, + {'freshen_cache' => 1}); + } else { + $output = '<span class="LC_error">'. + &mt('An error occurred when saving changes to course settings, which remain unchanged.').'</span>'; + } + return $output; +} + +sub update_env { + my ($cnum,$cdom,$chome,$need_env_update,$storehash) = @_; + my $count = 0; + if ((ref($need_env_update) eq 'ARRAY') && (ref($storehash) eq 'HASH')) { + my %crsinfo = &Apache::lonnet::courseiddump($cdom,'.',1,'.','.',$cnum,undef,undef,'.'); + if (ref($crsinfo{$env{'request.course.id'}}) eq 'HASH') { + foreach my $key (@{$need_env_update}) { + if ($key eq 'description' && defined($storehash->{$key})) { + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.description' => $storehash->{$key}}); + $crsinfo{$env{'request.course.id'}}{'description'} = $storehash->{$key}; + $count ++; + } elsif (($key eq 'cloners') || ($key eq 'hidefromcat') || ($key eq 'categories')) { + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.'.$key => $storehash->{$key}}); + $crsinfo{$env{'request.course.id'}}{$key} = $storehash->{$key}; + $count ++; + } + } + if ($count) { + my $putresult = &Apache::lonnet::courseidput($cdom,\%crsinfo,$chome,'notime'); + } + } + } + return; +} + +sub display_disallowed { + my ($item,$disallowed,$prefs) = @_; + my $output; + if ((ref($disallowed) eq 'HASH') && (ref($prefs) eq 'HASH')) { + if (keys(%{$disallowed})) { + if ($item eq 'cloners') { + my @fails; + my %lt = &Apache::lonlocal::texthash ( + format => 'Invalid format', + domain => 'Domain does not exist', + newuser => 'LON-CAPA user(s) do(es) not exist.', + ); + foreach my $error ('format','domain','newuser') { + if (defined($disallowed->{$error})) { + my $msg = '<b>'.$disallowed->{$error}.'</b>, '.&mt('reason').' - '. + $lt{$error}; + if ($error eq 'newuser') { + $msg .= '<br />'.&mt('Please [_1]add the user(s)[_2] before returning to the [_3]Course Configuration[_2] to add as potential cloners.','<a href="/adm/createuser">','</a>','<a href="/adm/courseprefs">'); + } + push(@fails,$msg); + } + } + if (@fails) { + $output .= '<span class="LC_warning">'.&mt('Unable to add to allowed cloners: '). + '</span>'.join('; ',@fails).'.<br />'; + } + } elsif ($item eq 'rolenames') { + my %lt = &Apache::lonlocal::texthash ( + replacements => 'Name already used to replace a different standard role name', + regulars => 'Name already used as a standard role name', + customrole => 'Name already used as the name of a custom role', + ); + my @fails; + foreach my $error ('replacements','regulars','customrole') { + if (ref($disallowed->{$error}) eq 'ARRAY') { + push(@fails,'<b>'.join(', ',@{$disallowed->{$error}}). + '</b>, '.&mt('reason').' - '.$lt{'error'}); + } + } + if (@fails) { + $output .= '<span class="LC_warning">'. + &mt('Unable to include amongst replacements for role names: '). + '</span>'.join('; ',@fails).'.<br />'; + } + + } elsif (($item eq 'feedback') || ($item eq 'discussion') || ($item eq 'localization')) { + $output .= '<span class="LC_warning">'; + if ($item eq 'feedback') { + $output .= &mt('Unable to include as a recipient of course feedback for:'); + } elsif ($item eq 'discussion') { + $output .= &mt('Unable to include in user-based access control for:'); + } elsif ($item eq 'localization') { + $output .= &mt('Unable to include in course localization:'); + } + $output .= '</span><ul>'; + foreach my $key (sort(keys(%{$disallowed}))) { + my $itemtext = $prefs->{$item}{'itemtext'}{$key}; + $output .= '<li><i>'.$itemtext.'</i> - '; + if ($item eq 'localization') { + $output .= &mt('reason - unsupported language: [_1]', + '<b>'.$disallowed->{$key}.'</b>'); + } else { + $output .= &mt('reason - invalid user: [_1]', + '<b>'.$disallowed->{$key}.'</b>').'</li>'; + } + } + $output .= '</ul><br />'; + } + } + } + return $output; } sub get_course { @@ -330,11 +1073,12 @@ sub get_course { } sub get_jscript { - my ($cdom) = @_; + my ($cdom,$phase) = @_; my ($can_toggle_cat,$can_categorize) = &can_modify_catsettings($cdom); my ($jscript,$categorize_js); my $stubrowse_js = &Apache::loncommon::studentbrowser_javascript(); my $browse_js = &Apache::loncommon::browser_and_searcher_javascript('parmset'); + my $cloners_js = &cloners_javascript($phase); if ($can_categorize) { $categorize_js = <<ENDSCRIPT; function catsbrowser() { @@ -349,13 +1093,96 @@ function catsbrowser() { ENDSCRIPT } $jscript = '<script type="text/javascript" language="Javascript">'."\n". - $browse_js."\n".$categorize_js."\n".'</script>'."\n".$stubrowse_js."\n"; + $browse_js."\n".$categorize_js."\n".$cloners_js."\n".'</script>'. + "\n".$stubrowse_js."\n"; return $jscript; } +sub cloners_javascript { + my ($formname) = @_; + return <<"ENDSCRIPT"; + +function update_cloners(caller,num) { + var delidx = getIndexByName('cloners_delete'); + var actidx = getIndexByName('cloners_activate'); + if (caller == 'cloners_all') { + var selall; + for (var i=0; i<document.$formname.cloners_all.length; i++) { + if (document.$formname.cloners_all[i].checked) { + selall = document.$formname.cloners_all[i].value; + } + } + if (selall == 1) { + if (delidx != -1) { + if (document.$formname.cloners_delete.length) { + for (var j=0; j<document.$formname.cloners_delete.length; j++) { + document.$formname.cloners_delete[j].checked = true; + } + } else { + document.$formname.elements[delidx].checked = true; + } + } + if (actidx != -1) { + if (document.$formname.cloners_activate.length) { + for (var i=0; i<document.$formname.cloners_activate.length; i++) { + if (document.$formname.cloners_activate[i].value == '0') { + document.$formname.cloners_activate[i].checked = false; + } + if (document.$formname.cloners_activate[i].value == '') { + document.$formname.cloners_activate[i].checked = true; + } + } + } + } + document.$formname.cloners_newdom.selectedIndex = 0; + } + } + if (caller == 'cloners_activate') { + if (document.$formname.cloners_activate.length) { + for (var j=0; j<document.$formname.cloners_activate.length; j++) { + if (document.$formname.cloners_activate[j].value == num) { + if (document.$formname.cloners_activate[j].checked) { + for (var i=0; i<document.$formname.cloners_all.length; i++) { + if (document.$formname.cloners_all[i].value == '1') { + document.$formname.cloners_all[i].checked = false; + } + if (document.$formname.cloners_all[i].value == '0') { + document.$formname.cloners_all[i].checked = true; + } + } + } + } + } + } else { + for (var i=0; i<document.$formname.cloners_all.length; i++) { + if (document.$formname.cloners_all[i].value == '1') { + document.$formname.cloners_all[i].checked = false; + } + if (document.$formname.cloners_all[i].value == '0') { + document.$formname.cloners_all[i].checked = true; + } + } + } + } + return; +} + +function getIndexByName(item) { + for (var i=0;i<document.$formname.elements.length;i++) { + if (document.$formname.elements[i].name == item) { + return i; + } + } + return -1; +} + +ENDSCRIPT +} + + sub print_courseinfo { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my ($cathash,$categoriesform); @@ -377,8 +1204,8 @@ sub print_courseinfo { my ($can_toggle_cat,$can_categorize) = &can_modify_catsettings($cdom); my %items = ( - 'url' => { - text => '<b>'.&mt('Top Level Map').'</b>'.(' 'x2). + 'url' => { + text => '<b>'.&mt($itemtext->{'url'}).'</b>'.(' 'x2). '<a href="javascript:openbrowser'. "('display','url','sequence')\">". &mt('Select Map').'</a><br /><span class="LC_warning"> '. @@ -388,41 +1215,41 @@ sub print_courseinfo { size => '40', }, 'description' => { - text => '<b>'.&mt('Course Description').'</b>', + text => '<b>'.&mt($itemtext->{'description'}).'</b>', input => 'textbox', size => '25', }, 'courseid' => { - text => '<b>'.&mt('Course ID or number'). - '</b><br />'. - '('.&mt('internal, optional').')', + text => '<b>'.&mt($itemtext->{'courseid'}).'</b><br />'.'('. + &mt('internal, optional').')', input => 'textbox', size => '25', }, 'cloners' => { - text => '<b>'.&mt('Users allowed to clone course').'</b><br />'. + text => '<b>'.&mt($itemtext->{'cloners'}).'</b><br />'. &mt('Course Coordinators included automatically'), input => 'textbox', size => '40', }, 'rolenames' => { - text => '<b>'.&mt('Replacement titles for standard course roles').'</b><br />'. + text => '<b>'.&mt($itemtext->{'rolenames'}).'</b><br />'. '('.&mt('To replace the standard title for a course role, enter a title, otherwise leave blank').')', input => 'textbox', size => '20', }, 'externalsyllabus' => { - text => '<b>'.&mt('URL of Syllabus').'</b><br />('.&mt('not using internal handler)'), + text => '<b>'.&mt($itemtext->{'externalsyllabus'}).'</b><br />('. + &mt('not using syllabus template)'), input => 'textbox', size => '40', }, 'hidefromcat' => { - text => '<b>'.&mt('Exclude from course catalog').'</b><br />'. + text => '<b>'.&mt($itemtext->{'hidefromcat'}).'</b><br />'. ' ('.&mt('included by default if assigned institutional code, or categorized').')', input => 'radio', }, 'categories' => { - text => '<b>'.&mt('Categorize course').'</b> <a href="javascript:catsbrowser()">'. + text => '<b>'.&mt($itemtext->{'categories'}).'</b> <a href="javascript:catsbrowser()">'. &mt('Display Categories').'</a>', input => 'textbox', size => '25', @@ -475,12 +1302,12 @@ sub print_courseinfo { if ($uname eq '*') { $datatable .= &Apache::loncommon::start_data_table_row(). - '<td valign="top"><span class="LC_nobreak">'. + '<td valign="top" align="left"><span class="LC_nobreak">'. &mt('Domain:').'<b> '.$udom. '</b><input type="hidden" name="cloners_dom_'.$num. '" value="'.$udom.'" /></span><br />'. '<span class="LC_nobreak"><label><input type="checkbox" '. - 'name="cloners_delete" value="'.$num.'" onchange="javascript:update_types('."'cloners_delete','$num'".');" />'. + 'name="cloners_delete" value="'.$num.'" onchange="javascript:update_cloners('."'cloners_delete','$num'".');" />'. &mt('Delete').'</label></span></td>'. &Apache::loncommon::end_data_table_row(); $num ++; @@ -504,7 +1331,9 @@ sub print_courseinfo { $includeempty). '<input type="hidden" name="cloners_total" value="'.$num.'" />'. '</td>'.&Apache::loncommon::end_data_table_row(). - &Apache::loncommon::end_data_table().'<br />'. + &Apache::loncommon::end_data_table(). + '<table><tr><td align="left">'.&mt('And'). + '</td></tr></table>'. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_row(). '<td align="left">'. @@ -528,7 +1357,10 @@ sub print_courseinfo { } $datatable .= &Apache::loncommon::end_data_table().'</td>'; } elsif ($item eq 'categories') { - $datatable .= '<input type="hidden" name="categories" value="'.$settings->{$item}.'" />'.&Apache::lonhtmlcommon::textbox($item.'_display',$settings->{$item},$items{$item}{size}); + my $launcher = 'onFocus="this.blur();javascript:catsbrowser();";'; + $datatable .= '<input type="hidden" name="categories" value="'.$settings->{$item}.'" />'. + &Apache::lonhtmlcommon::textbox($item.'_display',$settings->{$item}, + $items{$item}{size},$launcher); } else { $datatable .= &Apache::lonhtmlcommon::textbox($item,$settings->{$item},$items{$item}{size}); } @@ -547,14 +1379,15 @@ sub new_cloners_dom_row { &mt('Any user in domain:').' <b>'.$newdom.'</b>'. (' 'x2).'<label><input type="radio" '. 'name="cloners_activate" value="'.$num.'" '. - 'onchange="javascript:update_types('. + 'onchange="javascript:update_cloners('. "'cloners_activate','$num'".');" />'. &mt('Yes').'</label>'.(' 'x2). '<label><input type="radio" '. 'name="cloners_activate" value="" checked="checked" '. - 'onchange="javascript:update_types('. + 'onchange="javascript:update_cloners('. "'cloners_activate','$num'".');" />'. - &mt('No').'</label></span></td>'. + &mt('No').'</label><input type="hidden" name="cloners_dom_'. + $num.'" value="'.$newdom.'" /></span></td>'. &Apache::loncommon::end_data_table_row(); } return $output; @@ -590,7 +1423,7 @@ sub assign_course_categories { } my $catwin_js; if ($hascats) { - my $alert = &mt('Use \"Save\" in the main window to save course categories'); + my $alert = &mt("Use 'Save' in the main window to save course categories"); $catwin_js = <<ENDSCRIPT; <script type="text/javascript"> @@ -651,21 +1484,22 @@ ENDSCRIPT } sub print_localization { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( languages => { - text => &mt('Languages used').'<br />'.&mt("(will override user's preference)"), + text => &mt($itemtext->{'languages'}).'<br />'. + &mt("(will override user's preference)"), input => 'selectbox', }, timezone => { - text => &mt('Timezone in which the course takes place'), + text => &mt($itemtext->{'timezone'}), input => 'selectbox', }, datelocale => { - text => &mt('Locale used for course calendar'), + text => &mt($itemtext->{'datelocale'}), input => 'selectbox', }, ); @@ -693,35 +1527,35 @@ sub print_localization { } else { if ($settings->{$item} eq '') { $datatable .= - &Apache::loncommon::select_language('language_0','',1); + &Apache::loncommon::select_language('languages_0','',1); } else { my $num = 0; - my @languages = split(/\s*(\,|\;|\:)\s*/,$settings->{$item}); + my @languages = split(/\s*[,;:]\s*/,$settings->{$item}); $datatable .= &Apache::loncommon::start_data_table(); if (@languages > 0) { + my %langchoices = &get_lang_choices(); foreach my $lang (@languages) { my $showlang = $lang; - my $code = &Apache::loncommon::supportedlanguagecode($lang); - if ($code) { - $showlang = &plainlanguagedescription($lang); + if (exists($langchoices{$lang})) { + $showlang = $langchoices{$lang}; } $datatable .= &Apache::loncommon::start_data_table_row(). - '<td valign="top"><span class="LC_nobreak">'. + '<td align="left"><span class="LC_nobreak">'. &mt('Language:').'<b> '.$showlang. '</b><input type="hidden" name="languages_'.$num. '" value="'.$lang.'" /></span><br />'. '<span class="LC_nobreak"><label><input type="checkbox" '. 'name="languages_delete" value="'.$num.'" />'. &mt('Delete').'</label></span></td>'. - &Apache::loncommon::end_data_table_row().'</td>'; + &Apache::loncommon::end_data_table_row(); $num ++; } } $datatable .= &Apache::loncommon::start_data_table_row(). - '<td><span class="LC_nobreak">'. - &mt('Additional language:').'</span><br />'. - &Apache::loncommon::select_language($item); + '<td align="left"><span class="LC_nobreak">'. + &mt('Additional language:'). '</span><br />'. + &Apache::loncommon::select_language('languages_'.$num,'',1). '<input type="hidden" name="languages_total" value="'.$num.'" />'. '</td>'.&Apache::loncommon::end_data_table_row(). &Apache::loncommon::end_data_table(); @@ -733,26 +1567,37 @@ sub print_localization { return $datatable; } +sub get_lang_choices { + my %langchoices; + foreach my $id (&Apache::loncommon::languageids()) { + my $code = &Apache::loncommon::supportedlanguagecode($id); + if ($code) { + $langchoices{$code} = &Apache::loncommon::plainlanguagedescription($id); + } + } + return %langchoices; +} + sub print_feedback { - my ($position,$cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($position,$cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( - 'question' => { - text => '<b>'.&mt('Resource Content').'</b>', + 'question.email' => { + text => '<b>'.&mt($itemtext->{'question.email'}).'</b>', input => 'textbox', size => '50', }, - 'comment' => { - text => '<b>'.&mt('Course Content').'</b>', + 'comment.email' => { + text => '<b>'.&mt($itemtext->{'comment.email'}).'</b>', input => 'textbox', size => '50', }, - 'policy' => { - text => '<b>'.&mt('Course Policy').'</b>', + 'policy.email' => { + text => '<b>'.&mt($itemtext->{'policy.email'}).'</b>', input => 'textbox', size => '50', }, @@ -776,10 +1621,10 @@ sub print_feedback { if ($position eq 'top') { my $includeempty = 0; $datatable .= &user_table($cdom,$item,\@sections, - $settings->{$item.'.email'},\%lt); + $settings->{$item},\%lt); } else { - $datatable .= &Apache::lonhtmlcommon::textbox($item.'_text', - $settings->{$item},$items{$item}{size}); + $datatable .= &Apache::lonhtmlcommon::textbox($item.'.text', + $settings->{$item.'.text'},$items{$item}{size}); } $datatable .= &item_table_row_end(); } @@ -811,7 +1656,7 @@ sub user_table { 'name="'.$item.'_delete" value="'.$num.'" />'. $lt->{'del'}.'</label>'. '<input type="hidden" name="'.$item.'_user_'. - $num.'" value="'.$val.'" />'.(' 'x2). + $num.'" value="'.$uname.':'.$udom.'" />'.(' 'x2). &Apache::loncommon::aboutmewrapper( &Apache::loncommon::plainname($uname,$udom,'firstname'), $uname,$udom,'aboutuser'); @@ -842,8 +1687,8 @@ sub user_table { $lt->{'add'}.'</i></span><br />'. &select_recipient($item,$num,$cdom,$sections). '<input type="hidden" name="'.$item.'_total" value="'.$num.'" />'. - '</td>'.&Apache::loncommon::end_data_table_row(). - &Apache::loncommon::end_data_table(); + '</td>'.&Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table(); } return $output; } @@ -854,8 +1699,9 @@ sub select_recipient { my $selectlink = &Apache::loncommon::selectstudent_link('display',$item.'_uname_'.$num, $item.'_udom_'.$num,1); + my $css_class = ($num % 2)?'LC_even_row':'LC_odd_row'; my $output = - '<table><tr><td align="center">'.&mt('Username').'<br />'. + '<table><tr class="'.$css_class.'"><td align="center">'.&mt('Username').'<br />'. '<input type="text" name="'.$item.'_uname_'.$num.'" value="" /></td>'. '<td align="center">'.&mt('Domain').'<br />'.$domform.'</td>'; if (ref($sections) eq 'ARRAY') { @@ -903,39 +1749,43 @@ sub select_sections { } sub print_discussion { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( 'plc.roles.denied' => { - text => '<b>'.&mt('No Chat room use').'</b>'. - &Apache::loncommon::help_open_topic("Course_Disable_Discussion"), + text => '<b>'.&mt($itemtext->{'plc.roles.denied'}).'</b>'. + &Apache::loncommon::help_open_topic("Course_Disable_Discussion"). + &mt('(role-based)'), input => 'checkbox', }, 'plc.users.denied' => { - text => '<b>'.&mt('No Chat room use').'</b>', + text => '<b>'.&mt($itemtext->{'plc.users.denied'}).'</b><br />'. + &mt('(specific user(s))'), input => 'checkbox', }, 'pch.roles.denied' => { - text => '<b>'.&mt('No Resource Discussion').'</b>'. - &Apache::loncommon::help_open_topic("Course_Disable_Discussion"), + text => '<b>'.&mt($itemtext->{'pch.roles.denied'}).'</b>'. + &Apache::loncommon::help_open_topic("Course_Disable_Discussion").'<br />'. + &mt('(role-based)'), input => 'checkbox', }, 'pch.users.denied' => { - text => '<b>'.&mt('No Resource Discussion').'</b>', + text => '<b>'.&mt($itemtext->{'pch.users.denied'}).'</b><br />'. + &mt('(specific user(s))'), input => 'checkbox', }, 'allow_limited_html_in_feedback' => { - text => '<b>'.&mt('Allow limited HTML in discussion posts').'</b>', + text => '<b>'.&mt($itemtext->{'allow_limited_html_in_feedback'}).'</b>', input => 'radio', }, 'allow_discussion_post_editing' => { - text => '<b>'.&mt('Users can edit/delete own discussion posts').'</b>', + text => '<b>'.&mt($itemtext->{'allow_discussion_post_editing'}).'</b>', input => 'checkbox', }, ); @@ -1005,6 +1855,8 @@ sub role_checkboxes { if (!grep(/^\Q$sec\E/,@{$currsec{$trole}})) { push(@{$currsec{$trole}},$sec); } + } else { + $currsec{$trole} = [$sec]; } } else { push(@curr_roles,$role); @@ -1092,7 +1944,7 @@ sub role_checkboxes { } sub print_classlists { - my ($position,$cdom,$settings,$rowtotal) = @_; + my ($position,$cdom,$settings,$itemtext,$rowtotal) = @_; my @ordered; if ($position eq 'top') { @ordered = ('default_enrollment_start_date', @@ -1105,21 +1957,21 @@ sub print_classlists { } my %items = ( 'default_enrollment_start_date' => { - text => '<b>'.&mt('Start date').'</b>', + text => '<b>'.&mt($itemtext->{'default_enrollment_start_date'}).'</b>', input => 'dates', }, 'default_enrollment_end_date' => { - text => '<b>'.&mt('End date').'</b>', + text => '<b>'.&mt($itemtext->{'default_enrollment_end_date'}).'</b>', input => 'dates', }, 'nothideprivileged' => { - text => '<b>'.&mt('Domain Coodinators in course').'</b>', + text => '<b>'.&mt($itemtext->{'nothideprivileged'}).'</b>', input => 'checkbox', }, 'student_classlist_view' => { - text => '<b>'.&mt('Student-viewable classlist').'</b>', + text => '<b>'.&mt($itemtext->{'student_classlist_view'}).'</b>', input => 'selectbox', options => { disabled => &mt('No viewable classlist'), @@ -1129,12 +1981,12 @@ sub print_classlists { order => ['disabled','all','section'], }, 'student_opt_in' => { - text => '<b>'.&mt('Student agreement needed to be listed').'</b>', + text => '<b>'.&mt($itemtext->{'student_opt_in'}).'</b>', input => 'radio', }, 'student_classlist_portfiles' => { - text => '<b>'.&mt('Include link to accessible portfolio files').'</b>', + text => '<b>'.&mt($itemtext->{'student_classlist_portfiles'}).'</b>', input => 'radio', }, ); @@ -1146,13 +1998,13 @@ sub print_classlists { } sub print_appearance { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( 'default_xml_style' => { - text => '<b>'.&mt('Default XML Style File').'</b> '. + text => '<b>'.&mt($itemtext->{'default_xml_style'}).'</b> '. '<a href="javascript:openbrowser'. "('display','default_xml_style'". ",'sty')".'">'.&mt('Select Style File').'</a>', @@ -1161,15 +2013,15 @@ sub print_appearance { }, 'pageseparators' => { - text => '<b>'.&mt('Visibly Separate Items on Pages').'</b>', + text => '<b>'.&mt($itemtext->{'pageseparators'}).'</b>', input => 'radio', }, 'disable_receipt_display' => { - text => '<b>'.&mt('Disable display of problem receipts').'</b>', + text => '<b>'.&mt($itemtext->{'disable_receipt_display'}).'</b>', input => 'radio', }, 'texengine' => { - text => '<b>'.&mt('Force use of a specific math rendering engine.').'</b>', + text => '<b>'.&mt($itemtext->{'texengine'}).'</b>', input => 'selectbox', options => { jsMath => 'jsMath', @@ -1180,7 +2032,7 @@ sub print_appearance { nullval => &mt("None specified - use student's choice"), }, 'tthoptions' => { - text => '<b>'.&mt('Default set of options to pass to tth/m when converting TeX').'</b>', + text => '<b>'.&mt($itemtext->{'tthoptions'}).'</b>', input => 'textbox', size => 40, }, @@ -1189,13 +2041,13 @@ sub print_appearance { } sub print_grading { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( 'grading' => { - text => '<b>'.&mt('Grading').'</b>'. + text => '<b>'.&mt($itemtext->{'grading'}).'</b>'. &Apache::loncommon::help_open_topic('GradingOptions'), input => 'selectbox', options => { @@ -1206,7 +2058,7 @@ sub print_grading { order => ['standard','external','spreadsheet'], }, 'rndseed' => { - text => '<b>'.&mt('Randomization algorithm used').'</b>'. + text => '<b>'.&mt($itemtext->{'rndseed'}).'</b>'. '<span class="LC_error">'.'<br />'. &mt('Modifying this will make problems have different numbers and answers!'). '</span>', @@ -1222,8 +2074,8 @@ sub print_grading { order => ['32bit','64bit','64bit2','64bit3','64bit4','64bit5'], }, 'receiptalg' => { - text => '<b>'.&mt('Receipt algorithm used').'</b><br />'. - &mt('This controls how receipt numbers are generated.'), + text => '<b>'.&mt($itemtext->{'receiptalg'}).'</b><br />'. + &mt('This controls how receipt numbers are generated'), input => 'selectbox', options => { receipt => 'receipt', @@ -1233,7 +2085,7 @@ sub print_grading { order => ['receipt','receipt2','receipt3'], }, 'disablesigfigs' => { - text => '<b>'.&mt('Disable checking of Significant Figures').'</b>', + text => '<b>'.&mt($itemtext->{'disablesigfigs'}).'</b>', input => 'radio', }, ); @@ -1241,21 +2093,21 @@ sub print_grading { } sub print_printouts { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( problem_stream_switch => { - text => '<b>'.&mt('Allow problems to be split over pages').'</b>', + text => '<b>'.&mt($itemtext->{'problem_stream_switch'}).'</b>', input => 'radio', }, suppress_tries => { - text => '<b>'.&mt('Suppress number of tries in printing').'</b>', + text => '<b>'.&mt($itemtext->{'suppress_tries'}).'</b>', input => 'radio', }, default_paper_size => { - text => '<b>'.&mt('Default paper type').'</b>', + text => '<b>'.&mt($itemtext->{'default_paper_size'}).'</b>', input => 'selectbox', options => { Letter => &mt('Letter').' [8 1/2x11 in]', @@ -1272,11 +2124,11 @@ sub print_printouts { nullval => 'None specified', }, print_header_format => { - text => '<b>'.&mt('Print header format').'</b>', + text => '<b>'.&mt($itemtext->{'print_header_format'}).'</b>', input => 'checkbox', }, disableexampointprint => { - text => '<b>'.&mt('Disable automatically printing point values on exams').'</b>', + text => '<b>'.&mt($itemtext->{'disableexampointprint'}).'</b>', input => 'radio', }, ); @@ -1284,35 +2136,35 @@ sub print_printouts { } sub print_spreadsheet { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my $SelectSpreadsheetFile=&mt('Select Spreadsheet File'); my %items = ( spreadsheet_default_classcalc => { - text => '<b>'.&mt('Default Course Spreadsheet').'</b> '. + text => '<b>'.&mt($itemtext->{'spreadsheet_default_classcalc'}).'</b> '. '<span class="LC_nobreak"><a href="javascript:openbrowser'. "('display','spreadsheet_default_classcalc'". ",'spreadsheet')".'">'.$SelectSpreadsheetFile.'</a></span>', input => 'textbox', }, spreadsheet_default_studentcalc => { - text => '<b>'.&mt('Default Student Spreadsheet').'</b> '. + text => '<b>'.&mt($itemtext->{'spreadsheet_default_studentcalc'}).'</b> '. '<span class="LC_nobreak"><a href="javascript:openbrowser'. "('display','spreadsheet_default_calc'". ",'spreadsheet')".'">'.$SelectSpreadsheetFile.'</a></span>', input => 'textbox', }, spreadsheet_default_assesscalc => { - text => '<b>'.&mt('Default Assessment Spreadsheet').'</b> '. + text => '<b>'.&mt($itemtext->{'spreadsheet_default_assesscalc'}).'</b> '. '<span class="LC_nobreak"><a href="javascript:openbrowser'. "('display','spreadsheet_default_assesscalc'". ",'spreadsheet')".'">'.$SelectSpreadsheetFile.'</a></span>', input => 'textbox', }, hideemptyrows => { - text => '<b>'.&mt('Hide Empty Rows in Spreadsheets').'</b>', + text => '<b>'.&mt($itemtext->{'hideemptyrows'}).'</b>', input => 'radio', }, ); @@ -1320,13 +2172,13 @@ sub print_spreadsheet { } sub print_bridgetasks { - my ($cdom,$settings,$ordered,$rowtotal) = @_; - unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY')) { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } my %items = ( task_messages => { - text => '<b>'.&mt('Send message to student when clicking Done on Tasks').'</b>', + text => '<b>'.&mt($itemtext->{'task_messages'}).'</b>', input => 'selectbox', options => { only_student => &mt('Send message to student'), @@ -1336,7 +2188,7 @@ sub print_bridgetasks { nullval => &mt('No message or record in user notes'), }, task_grading => { - text => '<b>'.&mt('Bridge Task grading by instructors and TAs in sections').'</b>', + text => '<b>'.&mt($itemtext->{'task_grading'}).'</b>', input => 'selectbox', options => { any => &mt('Grade BTs in any section'), @@ -1345,8 +2197,8 @@ sub print_bridgetasks { order => ['any','section'], }, suppress_embed_prompt => { - text => '<b>'.&mt('Hi$de upload references prompt if uploading file to portfolio').'</b><span class="LC_nobreak"> '. - &mt('(applies when current role is student)').'</span>', + text => '<b>'.&mt($itemtext->{'suppress_embed_prompt'}).'</b><span class="LC_nobreak">'. + ' '.&mt('(applies when current role is student)').'</span>', input => 'radio', }, ); @@ -1358,27 +2210,46 @@ sub print_other { unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) { return; } - my @ordered; + my @ordered = &get_other_items($cdom,$settings,$allitems); my %items; + foreach my $parameter (@ordered) { + $items{$parameter} = { + text => '<b>'.$parameter.'</b>', + input => 'textbox', + size => '15', + }, + } + push (@ordered,'newp_value'); + $items{'newp_value'} = { + text => '<b>'.&mt('Create New Environment Variable').'</b><br />'. + '<input type="textbox" name="newp_name"'. + ' value="" size="30" />', + input => 'textbox', + size => '30', + }; + my $output = &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal); +} + +sub get_other_items { + my ($cdom,$settings,$allitems) = @_; + unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) { + return; + } + my @ordered; if (ref($settings) eq 'HASH') { foreach my $parameter (sort(keys(%{$settings}))) { next if (grep/^\Q$parameter\E$/,@{$allitems}); - next if (($parameter eq 'course.helper.not.run') && - (!exists($env{'user.role.dc./'.$env{'request.role.domain'}.'/'}))); + next if (($parameter eq 'course.helper.not.run') && + (!exists($env{'user.role.dc./'.$env{'request.role.domain'}.'/'}))); unless (($parameter =~ m/^internal\./)||($parameter =~ m/^metadata\./) || ($parameter =~ m/^selfenroll_/) || ($parameter =~ /_selfenroll$/) || ($parameter eq 'type') || ($parameter =~ m/^(cc|in|ta|ep|ad|st)\.plaintext$/)) { push(@ordered,$parameter); - $items{$parameter} = { - text => $parameter, - input => 'textbox', - size => '15', - }, } } } - return &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal); + return @ordered; } sub item_table_row_start { @@ -1390,7 +2261,7 @@ sub item_table_row_start { $output .= '<tr>'; } $output .= '<td class="LC_left_item">'.$text. - '</td><td class="LC_right_item">'; + '</td><td class="LC_right_item" align="right">'; return $output; } @@ -1517,6 +2388,7 @@ sub nothidepriv_row { my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum); my $now = time; my @privusers; + my %privileged; foreach my $person (keys(%coursepersonnel)) { my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/); $user =~ s/:$//; @@ -1525,8 +2397,22 @@ sub nothidepriv_row { next; } my ($uname,$udom) = split(':',$user); - if (&Apache::lonnet::privileged($uname,$udom)) { - push(@privusers,$user); + unless (ref($privileged{$udom}) eq 'HASH') { + my %dompersonnel = &Apache::lonnet::get_domain_roles($udom,['dc'],undef,$now); + $privileged{$udom} = {}; + if (keys(%dompersonnel)) { + foreach my $server (keys(%dompersonnel)) { + foreach my $user (sort(keys(%{$dompersonnel{$server}}))) { + my ($trole,$uname,$udom) = split(/:/,$user); + $privileged{$udom}{$uname} = $trole; + } + } + } + } + if (exists($privileged{$udom}{$uname})) { + unless (grep(/^\Q$user\E$/,@privusers)) { + push(@privusers,$user); + } } } if (@privusers) { @@ -1546,8 +2432,8 @@ sub nothidepriv_row { '</td><td align="left">'. '<span class="LC_nobreak"><label>'. '<input type="radio" name="'.$item.'_'.$user.'"'. - $hideon.' value="yes" />'.&mt('Hidden').'</label> '. - '<label><input type="radio" name="'.$item.'_'.$user.'"'. $hideoff.' value="" />'.&mt('Shown').'</label></span></td>'. + $hideon.' value="" />'.&mt('Hidden').'</label> '. + '<label><input type="radio" name="'.$item.'_'.$user.'"'. $hideoff.' value="yes" />'.&mt('Shown').'</label></span></td>'. '</tr>'; } $datatable .= '</table>'; @@ -1665,7 +2551,9 @@ ENDJS '<b>'.&mt('New').'</b></span></td>'. '<td colspan="2" align="left">'.&mt('Text').'<br />'. '<input type="textbox" name="printfmthdr_text_'.$currnum. - '" value="" size ="25" /></td>'. + '" value="" size ="25" />'. + '<input type="hidden" name="printfmthdr_maxnum" value="'. + $maxnum.'" /></td>'. &Apache::loncommon::end_data_table_row(). &Apache::loncommon::end_data_table(); return $output; @@ -1712,10 +2600,4 @@ sub substitution_selector { return $output; } -sub modify_crsenv { - my ($action,$cdom,%domconfig) = @_; - return; -} - 1; -