--- loncom/interface/courseprefs.pm 2010/09/17 23:53:01 1.31 +++ loncom/interface/courseprefs.pm 2022/02/16 00:06:04 1.103 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set configuration settings for a course # -# $Id: courseprefs.pm,v 1.31 2010/09/17 23:53:01 raeburn Exp $ +# $Id: courseprefs.pm,v 1.103 2022/02/16 00:06:04 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -52,12 +52,16 @@ This module is used for configuration of =item process_changes() +=item process_linkprot() + =item get_sec_str() =item check_clone() =item store_changes() +=item store_linkprot() + =item update_env() =item display_disallowed() @@ -137,7 +141,7 @@ whether Domain coordinators who are curr advanced course user .db file for a course are to be hidden (e.g., in syllabus, or from course user lists). -Inputs: $cdom,$item,$settings,$crstype +Inputs: $cdom,$item,$settings,$crstype,$noedit where $cdom is course domain, item is nothideprivileged, $settings is HASH ref of the current values for nothideprivileged, $crstype is course type (Course or Community). @@ -215,8 +219,15 @@ use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::lonconfigsettings; +use Apache::lonrelrequtils; +use Apache::lonparmset; +use Apache::courseclassifier; use Apache::lonlocal; use LONCAPA qw(:DEFAULT :match); +use Crypt::CBC; + +my $registered_cleanup; +my $modified_courses; sub handler { my $r=shift; @@ -227,11 +238,11 @@ sub handler { } my $context = 'course'; my $cid = $env{'request.course.id'}; - my ($cnum,$cdom) = &get_course($cid); + my ($cnum,$cdom,$chome) = &get_course($cid); my $crstype = &Apache::loncommon::course_type(); - my $parm_permission = &Apache::lonnet::allowed('opa',$cid); + my ($parm_permission,$allowed) = &get_permission($cid); my $navmap = Apache::lonnavmaps::navmap->new(); - if ($parm_permission && $navmap) { + if ($allowed && $navmap) { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; } else { @@ -256,6 +267,9 @@ sub handler { return HTTP_NOT_ACCEPTABLE; } + $registered_cleanup=0; + @{$modified_courses}=(); + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['phase','actions','origin']); &Apache::lonhtmlcommon::clear_breadcrumbs(); @@ -267,10 +281,11 @@ sub handler { my %lt; if ($crstype eq 'Community') { %lt = ( - conf => 'Community Configuration', - edit => 'Edit Community Configuration', + conf => 'Community Settings', + edit => 'Edit Community Settings', gens => 'General community settings', idnu => 'Community ID or number', + unco => 'Unique code', desc => 'Community Description', ownr => 'Community Owner', cown => 'Community Co-owners', @@ -286,19 +301,21 @@ sub handler { defd => 'Default dates for member access', stuv => 'Member-viewable membership list options', stul => 'Member agreement needed to be listed', - clas => 'Membership and Facilitator Listing', - priv => 'Privileged users (Domain Coordinators) in facilitator listing', + clas => 'Membership and facilitator listing', + prus => 'Privileged users (Domain Coordinators) in facilitator listing', defc => 'Default Community Spreadsheet', defs => 'Default User Spreadsheet', seme => 'Send message to member when clicking Done on Tasks' ); } else { %lt = ( - conf => 'Course Configuration', - edit => 'Edit Course Configuration', + conf => 'Course Settings', + edit => 'Edit Course Settings', gens => 'General course settings', idnu => 'Course ID or number', + unco => 'Unique code', desc => 'Course Description', + cred => 'Student credits', ownr => 'Course Owner', cown => 'Course Co-owners', catg => 'Categorize course', @@ -313,13 +330,15 @@ sub handler { defd => 'Default dates for student access', stuv => 'Student-viewable classlist options', stul => 'Student agreement needed to be listed', - clas => 'Classlists and Staff Listing', - priv => 'Privileged users (Domain Coordinators) in staff listing', + clas => 'Classlists and staff listing', + prus => 'Privileged users (Domain Coordinators) in staff listing', + prdo => "Domains to check for privileged users (besides course's domain)", defc => 'Default Course Spreadsheet', defs => 'Default Student Spreadsheet', seme => 'Send message to student when clicking Done on Tasks', ); } + $lt{'lcrv'} = 'Required LON-CAPA version'; &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/courseprefs', text=>$lt{'conf'}}); my $breadcrumbs = @@ -330,39 +349,78 @@ sub handler { $phase = $env{'form.phase'}; } - if ($phase eq 'categorizecourse') { + if (($phase eq 'categorizecourse') && ($parm_permission->{'categorizecourse'})) { &assign_course_categories($r,$crstype); return OK; } + if (($phase eq 'releaseinfo') && ($parm_permission->{'releaseinfo'})) { + my $loncaparev = $env{'course.'.$cid.'.internal.releaserequired'}; + if ($loncaparev) { + if (&display_loncaparev_constraints($r,$navmap,$loncaparev,$crstype)) { + push(@{$modified_courses},[$cdom,$cnum,$chome,$crstype]); + unless ($registered_cleanup) { + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\&update_releasereq,@{$handlers}]); + $registered_cleanup=1; + } + } + return OK; + } + } + my %values=&Apache::lonnet::dump('environment',$cdom,$cnum); + my %linkprot=&Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1); + my %ltienc = &Apache::lonnet::dump('nohist_ltienc',$cdom,$cnum,undef,undef,undef,1); + foreach my $id (keys(%linkprot)) { + if (ref($linkprot{$id}) eq 'HASH') { + if (ref($ltienc{$id}) eq 'HASH') { + $values{'linkprot'}{$id} = { %{$linkprot{$id}}, %{$ltienc{$id}} }; + } else { + $values{'linkprot'}{$id} = $linkprot{$id}; + } + } + unless ($phase eq 'process') { + if (ref($values{'linkprot'}{$id}) eq 'HASH') { + delete($values{'linkprot'}{$id}{'secret'}); + } + } + } + if ($linkprot{'lock'}) { + delete($linkprot{'lock'}); + } my @prefs_order = ('courseinfo','localization','feedback','discussion', 'classlists','appearance','grading','printouts', - 'spreadsheet','bridgetasks','other'); + 'menuitems','linkprot','spreadsheet','bridgetasks', + 'lti','other'); my %prefs = ( 'courseinfo' => { text => $lt{'gens'}, - help => 'Course_Environment', - ordered => ['owner','co-owners','description','courseid', - 'categories','hidefromcat','externalsyllabus', - 'cloners','url','rolenames'], + help => 'Course_Prefs_General', + ordered => ['owner','co-owners','loncaparev','description', + 'clonedfrom','courseid','uniquecode','categories', + 'hidefromcat','syllabus','cloners','url', + 'rolenames'], itemtext => { 'owner' => $lt{'ownr'}, 'co-owners' => $lt{'cown'}, 'description' => $lt{'desc'}, 'courseid' => $lt{'idnu'}, + 'uniquecode' => $lt{'unco'}, 'categories' => $lt{'catg'}, 'hidefromcat' => $lt{'excc'}, 'cloners' => $lt{'clon'}, - 'externalsyllabus' => 'URL of Syllabus', + 'syllabus' => 'Syllabus status', 'url' => 'Top Level Map', 'rolenames' => $lt{'rept'}, + 'loncaparev' => $lt{'lcrv'}, + 'clonedfrom' => 'Cloned from', }, }, 'localization' => - { text => 'Language and Time Localization', - help => 'Course_Environment', + { text => 'Language and time localization', + help => 'Course_Prefs_Language', ordered => ['languages','timezone','datelocale'], itemtext => { languages => 'Languages used', @@ -372,7 +430,7 @@ sub handler { }, 'feedback' => { text => 'Feedback messages', - help => 'Course_Environment', + help => 'Course_Prefs_Feedback', header => [{col1 => 'Questions about:', col2 => 'Recipients'}], ordered => ['question.email','comment.email','policy.email'], @@ -384,23 +442,28 @@ sub handler { }, 'discussion' => { text => 'Discussion and Chat', - help => 'Course_Environment', + help => 'Course_Prefs_Discussions', ordered => ['pch.roles.denied','pch.users.denied', + 'pac.roles.denied','pac.users.denied', 'plc.roles.denied','plc.users.denied', 'allow_limited_html_in_feedback', - 'allow_discussion_post_editing'], + 'allow_discussion_post_editing', + 'discussion_post_fonts'], itemtext => { 'pch.roles.denied' => 'No Resource Discussion', 'pch.users.denied' => 'No Resource Discussion', 'plc.roles.denied' => 'No Chat room use', 'plc.users.denied' => 'No Chat room use', + 'pac.roles.denied' => 'No Anonymous Resource Discussion', + 'pac.users.denied' => 'No Anonymous Resource Discussion', allow_limited_html_in_feedback => 'Allow limited HTML in discussion', allow_discussion_post_editing => 'Users can edit/delete own discussion posts', + discussion_post_fonts => 'Discussion post fonts based on likes/unlikes', }, }, 'classlists' => { text => $lt{'clas'}, - help => 'Course_Environment', + help => 'Course_Prefs_Classlists', header => [{col1 => 'Type', col2 => $lt{'defd'}}, {col1 => 'Setting', @@ -409,34 +472,41 @@ sub handler { col2 => $lt{'stuv'}}], ordered => ['default_enrollment_start_date', 'default_enrollment_end_date', - 'nothideprivileged','student_classlist_view', + 'defaultcredits', + 'nothideprivileged','checkforpriv', + 'student_classlist_view', 'student_classlist_opt_in','student_classlist_portfiles'], itemtext => { default_enrollment_start_date => 'Start date', default_enrollment_end_date => 'End date', - nothideprivileged => $lt{'priv'}, + nothideprivileged => $lt{'prus'}, + checkforpriv => $lt{'prdo'}, student_classlist_view => $lt{'stuv'}, student_classlist_opt_in => $lt{'stul'}, student_classlist_portfiles => 'Include link to accessible portfolio files', + defaultcredits => $lt{'cred'}, }, }, 'appearance' => { text => 'Display of resources ', - help => 'Course_Environment', + help => 'Course_Prefs_Display', ordered => ['default_xml_style','pageseparators', 'disable_receipt_display','texengine', - 'tthoptions'], + 'tthoptions','uselcmath','usejsme','inline_chem'], 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', + uselcmath => 'Student formula entry uses inline preview, not DragMath pop-up', + usejsme => 'Molecule editor uses JSME (HTML5) in place of JME (Java)', + inline_chem => 'Chemical reaction response uses inline preview, not pop-up', }, }, 'grading' => { text => 'Grading', - help => 'Course_Environment', + help => 'Course_Prefs_Grading', ordered => ['grading','rndseed', 'receiptalg','disablesigfigs'], itemtext => { @@ -449,7 +519,7 @@ sub handler { }, 'printouts' => { text => 'Printout generation', - help => 'Course_Environment', + help => 'Course_Prefs_Printouts', ordered => ['problem_stream_switch','suppress_tries', 'default_paper_size','print_header_format', 'disableexampointprint','canuse_pdfforms'], @@ -458,13 +528,13 @@ sub handler { 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', + disableexampointprint => 'Disable automatically printing point values on bubblesheet exams', canuse_pdfforms => 'Users can print problems as PDF forms and upload later for grading', }, }, 'spreadsheet' => { text => 'Spreadsheets', - help => 'Course_Environment', + help => 'Course_Prefs_Spreadsheet', ordered => ['spreadsheet_default_classcalc', 'spreadsheet_default_studentcalc', 'spreadsheet_default_assesscalc','hideemptyrows'], @@ -477,7 +547,7 @@ sub handler { }, 'bridgetasks' => { text => 'Bridge tasks', - help => 'Course_Environment', + help => 'Course_Prefs_Bridgetasks', ordered => ['task_messages','task_grading', 'suppress_embed_prompt'], itemtext => { @@ -486,31 +556,101 @@ sub handler { suppress_embed_prompt => 'Hide upload references prompt if uploading file to portfolio', }, }, + 'lti' => + { + text => 'LTI provider settings', + help => 'Course_Prefs_LTIProvider', + ordered => ['lti.override','lti.topmenu','lti.inlinemenu','lti.lcmenu'], + itemtext => { + 'lti.override' => 'Override domain defaults', + 'lti.topmenu' => 'Display LON-CAPA page header', + 'lti.inlinemenu' => 'Display LON-CAPA inline menu', + 'lti.lcmenu' => 'Menu items', + }, + }, + 'menuitems' => + { + text => 'Menu display', + help => 'Course_Prefs_Menus', + header => [{col1 => 'Default Menu', + col2 => 'Value',}, + {col1 => 'Menu collections', + col2 => 'Settings', + }], + ordered => ['menudefault','menucollections'], + itemtext => { + menudefault => 'Choose default collection of menu items for course', + menucollections => 'Menu collections', + }, + }, + 'linkprot' => + { + text => 'Link protection', + help => 'Course_Prefs_Linkprotection', + header => [{col1 => 'Item', + col2 => 'Settings', + }], + }, 'other' => { text => 'Other settings', - help => 'Course_Environment', + help => 'Course_Prefs_Other', header => [ {col1 => 'Item', col2 => 'Value', }], }, ); - if ($phase eq 'process') { + if (($phase eq 'process') && ($parm_permission->{'process'})) { my @allitems = &get_allitems(%prefs); &Apache::lonconfigsettings::make_changes($r,$cdom,$phase,$context, \@prefs_order,\%prefs,\%values, - $cnum,undef,\@allitems); - } elsif ($phase eq 'display') { - my $jscript = &get_jscript($cdom,$phase,$crstype); + $cnum,undef,\@allitems, + 'coursepref',$parm_permission); + } elsif (($phase eq 'display') && ($parm_permission->{'display'})) { + my $noedit; + if (ref($parm_permission) eq 'HASH') { + unless ($parm_permission->{'process'}) { + $noedit = 1; + } + } + my $jscript = &get_jscript($cid,$cdom,$phase,$crstype,\%values,$noedit); my @allitems = &get_allitems(%prefs); &Apache::lonconfigsettings::display_settings($r,$cdom,$phase,$context, - \@prefs_order,\%prefs,\%values,undef,$jscript,\@allitems,$crstype); + \@prefs_order,\%prefs,\%values,$cnum,$jscript,\@allitems,$crstype, + 'coursepref',$parm_permission); } else { &Apache::lonconfigsettings::display_choices($r,$phase,$context, - \@prefs_order,\%prefs); + \@prefs_order,\%prefs, + 'coursepref',$parm_permission); } return OK; } +sub get_permission { + my ($cid) = @_; + my %permission; + my $allowed = 0; + return (\%permission,$allowed) unless ($cid); + if (&Apache::lonnet::allowed('opa',$cid)) { + %permission= ( + 'pickactions' => 1, + 'categorizecourse' => 1, + 'releaseinfo' => 1, + 'process' => 1, + 'display' => 1, + ); + } elsif (&Apache::lonnet::allowed('vpa',$env{'request.course.id'})) { + %permission = ( + 'pickactions' => 1, + 'releaseinfo' => 1, + 'display' => 1, + ); + } + foreach my $perm (values(%permission)) { + if ($perm) { $allowed=1; last; } + } + return (\%permission,$allowed); +} + sub get_allitems { my (%prefs) = @_; my @allitems; @@ -528,98 +668,152 @@ sub get_allitems { } sub print_config_box { - my ($r,$cdom,$phase,$action,$item,$settings,$allitems,$crstype) = @_; + my ($r,$cdom,$cnum,$phase,$action,$item,$settings,$allitems,$crstype,$parm_permission) = @_; my $ordered = $item->{'ordered'}; my $itemtext = $item->{'itemtext'}; + my $noedit; + if (ref($parm_permission) eq 'HASH') { + unless ($parm_permission->{'process'}) { + $noedit = 1; + } + } my $rowtotal = 0; my $output = - '

'.&mt($item->{text}).'

-
 '. - &Apache::loncommon::help_open_topic($item->{'help'}).''; - if (($action eq 'feedback') || ($action eq 'classlists')) { + ' + + '."\n". + ''; + if (($action eq 'feedback') || ($action eq 'classlists') || ($action eq 'menuitems')) { $output .= ' + + + + + + + + '. + "'cloners_all'".');"'.$disabled.' />'.&mt('No').''. &Apache::loncommon::end_data_table_row(). &Apache::loncommon::end_data_table(). - '
'. + &mt($item->{text}); + if ($item->{'help'}) { + $output .= ' '. + &Apache::loncommon::help_open_topic($item->{'help'}); + } + $output .= '
'; if (exists $item->{'header'}->[0]->{'col1'} || exists $item->{'header'}->[0]->{'col2'}) { $output .= ' - - + '; + if ($action eq 'feedback') { + $output .= ' + '; } $rowtotal ++; if ($action eq 'feedback') { - $output .= &print_feedback('top',$cdom,$settings,$ordered,$itemtext,\$rowtotal); + $output .= &print_feedback('top',$cdom,$settings,$ordered,$itemtext,\$rowtotal,$noedit); } elsif ($action eq 'classlists') { - $output .= &print_classlists('top',$cdom,$settings,$itemtext,\$rowtotal,$crstype); + $output .= &print_classlists('top',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'menuitems') { + $output .= &print_menuitems('top',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); } $output .= '
'.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col2'}).''.&mt($item->{'header'}->[0]->{'col1'}).''; + } else { + $output .= ' + '; + } + $output .= &mt($item->{'header'}->[0]->{'col2'}).'
+
'; - $output .= ' - + if ($action eq 'classlists') { + $output .= ' + '; if ($action eq 'classlists') { - $output .= &print_classlists('middle',$cdom,$settings,$itemtext,\$rowtotal,$crstype). + $output .= &print_classlists('middle',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit). '
'.&mt($item->{'header'}->[1]->{'col1'}).''.&mt($item->{'header'}->[1]->{'col2'}).''; + } else { + $output .= ' + '; + } + $output .= &mt($item->{'header'}->[1]->{'col2'}).'
+
'; - if (exists $item->{'header'}->[0]->{'col1'} || - exists $item->{'header'}->[0]->{'col2'}) { + if (exists $item->{'header'}->[2]->{'col1'} || + exists $item->{'header'}->[2]->{'col2'}) { $output .= ' - - + + '; } } } else { $output .= ' + + + +
'.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col2'}).''.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
'; if (exists $item->{'header'}->[0]->{'col1'} || exists $item->{'header'}->[0]->{'col2'}) { $output .= ' - - + '; + if (($action eq 'courseinfo') || ($action eq 'localization') || + ($action eq 'print_discussion')) { + $output .= ''; } } $rowtotal ++; if ($action eq 'courseinfo') { - $output .= &print_courseinfo($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype); + $output .= &print_courseinfo($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'localization') { - $output .= &print_localization($cdom,$settings,$ordered,$itemtext,\$rowtotal); + $output .= &print_localization($cdom,$settings,$ordered,$itemtext,\$rowtotal,$noedit); } elsif ($action eq 'feedback') { - $output .= &print_feedback('bottom',$cdom,$settings,$ordered,$itemtext,\$rowtotal); + $output .= &print_feedback('bottom',$cdom,$settings,$ordered,$itemtext,\$rowtotal,$noedit); } elsif ($action eq 'discussion') { - $output .= &print_discussion($cdom,$settings,$ordered,$itemtext,\$rowtotal); - } elsif ($action eq 'classlists') { - $output .= &print_classlists('bottom',$cdom,$settings,$itemtext,\$rowtotal,$crstype); + $output .= &print_discussion($cdom,$settings,$ordered,$itemtext,\$rowtotal,$noedit); + } elsif (($action eq 'classlists') || ($action eq 'viewableroster')) { + $output .= &print_classlists('bottom',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'appearance') { - $output .= &print_appearance($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype); + $output .= &print_appearance($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'grading') { - $output .= &print_grading($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype); + $output .= &print_grading($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'printouts') { - $output .= &print_printouts($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype); + $output .= &print_printouts($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'spreadsheet') { - $output .= &print_spreadsheet($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype); + $output .= &print_spreadsheet($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); } elsif ($action eq 'bridgetasks') { - $output .= &print_bridgetasks($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype); + $output .= &print_bridgetasks($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'lti') { + $output .= &print_lti($cdom,$settings,$ordered,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'menuitems') { + $output .= &print_menuitems('bottom',$cdom,$settings,$itemtext,\$rowtotal,$crstype,$noedit); + } elsif ($action eq 'linkprot') { + $output .= &print_linkprotection($cdom,$cnum,$settings,\$rowtotal,$crstype,$noedit,'course'); } elsif ($action eq 'other') { - $output .= &print_other($cdom,$settings,$allitems,\$rowtotal,$crstype); + $output .= &print_other($cdom,$settings,$allitems,\$rowtotal,$crstype,$noedit); } $output .= '
'.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col2'}).''.&mt($item->{'header'}->[0]->{'col1'}).''; + } else { + $output .= ''; + } + $output .= &mt($item->{'header'}->[0]->{'col2'}).'
-'; +

'; return ($output,$rowtotal); } sub process_changes { - my ($cdom,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_; - my %newvalues; + my ($cdom,$cnum,$action,$values,$item,$changes,$allitems,$disallowed,$crstype) = @_; + my (%newvalues,$errors); if (ref($item) eq 'HASH') { if (ref($changes) eq 'HASH') { my @ordered; @@ -636,8 +830,41 @@ sub process_changes { } } } + } elsif ($action eq 'linkprot') { + if (ref($values->{$action}) eq 'HASH') { + foreach my $id (keys(%{$values->{$action}})) { + if ($id =~ /^\d+$/) { + push(@ordered,$id); + } + } + } + @ordered = sort { $a <=> $b } @ordered; + if (($env{'form.linkprot_add'}) && ($env{'form.linkprot_maxnum'} =~ /^\d+$/)) { + push(@ordered,$env{'form.linkprot_maxnum'}); + } } elsif (ref($item->{'ordered'}) eq 'ARRAY') { - @ordered = @{$item->{'ordered'}}; + if ($action eq 'courseinfo') { + my ($can_toggle_cat,$can_categorize) = + &can_modify_catsettings($cdom,$crstype); + foreach my $entry (@{$item->{'ordered'}}) { + next if (($entry eq 'hidefromcat') && + (!$can_toggle_cat)); + next if (($entry eq 'categories') && + (!$can_categorize)); + next if (($entry eq 'loncaparev') || + ($entry eq 'owner') || + ($entry eq 'clonedfrom') || + ($entry eq 'syllabus')); + push(@ordered,$entry); + } + } elsif ($action eq 'classlists') { + foreach my $entry (@{$item->{'ordered'}}) { + next if ($entry eq 'defaultcredits'); + push(@ordered,$entry); + } + } else { + @ordered = @{$item->{'ordered'}}; + } } if (@ordered > 0) { if ($action eq 'feedback') { @@ -677,6 +904,85 @@ sub process_changes { $changes->{$ext_entry} = $newvalues{$ext_entry}; } } + } elsif ($action eq 'menuitems') { + my (%current,@colls); + my $next = 1; + if ($values->{'menucollections'}) { + foreach my $item (split(/;/,$values->{'menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + unless (grep(/^$num$/,@colls)) { + push(@colls,$num); + } + my @entries = split(/\&/,$value); + foreach my $entry (@entries) { + my ($name,$fields) = split(/=/,$entry); + $current{$num}{$name} = $fields; + } + } + } + } + if (@colls) { + @colls = sort { $a <=> $b } @colls; + $next += $colls[-1]; + } + if ($env{'form.menucollections_add'} eq $next) { + push(@colls,$next); + } + my $currdef = $values->{'menudefault'}; + my $possdef = $env{'form.menudefault'}; + if (($possdef =~ /^\d+$/) && (grep(/^$possdef$/,@colls))) { + if ($currdef ne $possdef) { + $changes->{'menudefault'} = $possdef; + } + } elsif ($currdef) { + $changes->{'menudefault'} = ''; + } + my $menucoll; + if (@colls) { + my ($ordered,$cats) = &menuitems_categories(); + my %shortcats = &menuitems_abbreviations(); + foreach my $num (@colls) { + my ($entry,%include); + map { $include{$_}= 1; } &Apache::loncommon::get_env_multiple('form.menucollections_'.$num); + foreach my $item (@{$ordered}) { + if ($item eq 'shown') { + foreach my $type (@{$cats->{$item}}) { + $entry .= $type.'='; + if ($include{$type}) { + $entry .= 'y'; + } else { + $entry .= 'n'; + } + $entry .= '&'; + } + } else { + $entry .= $shortcats{$item}.'='; + foreach my $type (@{$cats->{$item}}) { + if ($include{$type}) { + $entry .= $type.','; + } + } + $entry =~ s/,$//; + $entry .= '&'; + } + } + $entry =~ s/\&$//; + if ($menucoll) { + $menucoll .= ';'; + } + $menucoll .= $num.'%'.$entry; + } + if ($menucoll ne $values->{'menucollections'}) { + $changes->{'menucollections'} = $menucoll; + } + } elsif ($values->{'menucollections'}) { + $changes->{'menucollections'} = ''; + } + } elsif ($action eq 'linkprot') { + if (ref($values) eq 'HASH') { + $errors = &process_linkprot($cdom,$cnum,$values->{$action},$changes,'course'); + } } else { foreach my $entry (@ordered) { if ($entry eq 'cloners') { @@ -686,9 +992,9 @@ sub process_changes { 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 ($actnum ne '-1') { + if ($env{'form.cloners_dom_'.$actnum} ne '') { + my $clonedom = $env{'form.cloners_dom_'.$actnum}; if (&check_clone($clonedom,$disallowed) eq 'ok') { $newvalues{$entry} = '*:'.$clonedom; push(@clonedoms,$newvalues{$entry}); @@ -704,7 +1010,7 @@ sub process_changes { my $clonedom = $env{'form.cloners_dom_'.$i}; if (&check_clone($clonedom,$disallowed) eq 'ok') { if (!grep(/^\*:\Q$clonedom\E$/,@clonedoms)) { - push (@clonedoms,'*:'.$clonedom); + push(@clonedoms,'*:'.$clonedom); } } } @@ -717,7 +1023,7 @@ sub process_changes { my $clonedom = $env{'form.cloners_newdom'}; if (&check_clone($clonedom,$disallowed) eq 'ok') { my $newdom = '*:'.$env{'form.cloners_newdom'}; - if (@clonedoms) { + if (@clonedoms) { if (!grep(/^\Q$newdom\E$/,@clonedoms)) { $newvalues{$entry} .= ','.$newdom; } @@ -750,6 +1056,37 @@ sub process_changes { } } } + if (ref($values) eq 'HASH') { + my (@code_order,%codedefaults); + &Apache::lonnet::auto_instcode_defaults($cdom,\%codedefaults, + \@code_order); + my $clonebycode; + if ($env{'form.cloners_instcode'}) { + if (@code_order > 0) { + my @standardnames = &Apache::loncommon::get_standard_codeitems(); + my %local_to_standard; + for (my $i=0; $i<@code_order; $i++) { + $local_to_standard{$code_order[$i]} = $standardnames[$i]; + } + foreach my $item (@code_order) { + my $key = $local_to_standard{$item}; + if ($key ne '') { + if ($env{'form.'.$key}) { + $clonebycode .= $key.'='.&escape($env{'form.'.$key}).'&'; + } + } + } + $clonebycode =~ s/\&$//; + } + } + if ($clonebycode) { + if ($newvalues{$entry}) { + $newvalues{$entry} .= ','.$clonebycode; + } else { + $newvalues{$entry} = $clonebycode; + } + } + } } if (ref($disallowed) eq 'HASH') { if (ref($disallowed->{'cloners'}) eq 'HASH') { @@ -775,7 +1112,9 @@ sub process_changes { $autocoowner = $domconf{'autoenroll'}{'co-owners'}; } } - unless ($autocoowner) { + if ($autocoowner) { + $newvalues{'co-owners'} = $values->{'internal.co-owners'}; + } else { my @keepcoowners = &Apache::loncommon::get_env_multiple('form.coowners'); my @pendingcoowners = &Apache::loncommon::get_env_multiple('form.pendingcoowners'); my @invitecoowners = &Apache::loncommon::get_env_multiple('form.invitecoowners'); @@ -799,19 +1138,19 @@ sub process_changes { my $udom = $env{'user.domain'}; my $pendingcoowners = $values->{'internal.pendingco-owners'}; my @pendingcoown = split(',',$pendingcoowners); - if ($env{'form.pending_coowoner'}) { + if ($env{'form.pending_coowner'}) { foreach my $item (@pendingcoown) { unless ($item eq $uname.':'.$udom) { push(@newpending,$item); } } @newcoown = @currcoown; - if ($env{'form.pending_coowoner'} eq 'accept') { + if ($env{'form.pending_coowner'} eq 'accept') { unless (grep(/^\Q$uname\E:\Q$udom\E$/,@currcoown)) { push(@newcoown,$uname.':'.$udom); } } - } elsif ($env{'form.remove_coowoner'}) { + } elsif ($env{'form.remove_coowner'}) { foreach my $item (@currcoown) { unless ($item eq $uname.':'.$udom) { push(@newcoown,$item); @@ -820,6 +1159,8 @@ sub process_changes { if ($pendingcoowners ne '') { @newpending = @pendingcoown; } + } else { + @newcoown = @currcoown; } $newvalues{'pendingco-owners'} = join(',',sort(@newpending)); $newvalues{'co-owners'} = join(',',sort(@newcoown)); @@ -889,7 +1230,8 @@ sub process_changes { } } } - } elsif (($entry eq 'plc.roles.denied') || ($entry eq 'pch.roles.denied')) { + } elsif (($entry eq 'plc.roles.denied') || ($entry eq 'pch.roles.denied') || + ($entry eq 'pac.roles.denied')) { my @denied = &Apache::loncommon::get_env_multiple('form.'.$entry); @denied = sort(@denied); my $deniedstr = ''; @@ -897,7 +1239,8 @@ sub process_changes { $deniedstr = join(',',@denied); } $newvalues{$entry} = $deniedstr; - } elsif (($entry eq 'plc.users.denied') || ($entry eq 'pch.users.denied')) { + } elsif (($entry eq 'plc.users.denied') || ($entry eq 'pch.users.denied') || + ($entry eq 'pac.users.denied')) { my $total = $env{'form.'.$entry.'_total'}; my $userstr = ''; my @denied; @@ -945,6 +1288,35 @@ sub process_changes { } } $newvalues{$entry} =~ s/,$//; + } elsif ($entry eq 'discussion_post_fonts') { + my ($classorder,$classtitles) = &discussion_vote_classes(); + my $fontchange = 0; + foreach my $class (@{$classorder}) { + my $ext_entry = $entry.'_'.$class; + my $size = $env{'form.'.$ext_entry.'_size'}; + my $unit = $env{'form.'.$ext_entry.'_unit'}; + my $weight = $env{'form.'.$ext_entry.'_weight'}; + my $style = $env{'form.'.$ext_entry.'_style'}; + my $other = $env{'form.'.$ext_entry.'_other'}; + $size =~ s/,//g; + $unit =~ s/,//g; + $weight =~ s/,//g; + $style =~ s/,//g; + $other =~ s/[^\w;:\s\-\%.]//g; + $other =~ s/(^\s+|\s+$)//g; + $newvalues{$ext_entry} = join(',',($size.$unit,$weight,$style,$other)); + my $current = $values->{$ext_entry}; + if ($values->{$ext_entry} eq '') { + $current = ',,,'; + } + if ($newvalues{$ext_entry} ne $current) { + $changes->{$ext_entry} = $newvalues{$ext_entry}; + $fontchange ++; + } + } + if ($fontchange) { + $changes->{$entry} = 1; + } } elsif ($entry eq 'nothideprivileged') { my @curr_nothide; my @new_nothide; @@ -1019,36 +1391,79 @@ sub process_changes { 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; + } elsif (($entry eq 'languages') || + ($entry eq 'checkforpriv')) { + my $settings; 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}.','; + $settings .= $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; + my $new = $env{'form.'.$entry.'_'.$total}; + if ($entry eq 'languages') { + my %langchoices = &get_lang_choices(); + if ($langchoices{$new}) { + $settings .= $new; + } else { + $settings =~ s/,$//; + $disallowed->{'localization'}{$entry} = $new; + } } else { - $langstr =~ s/,$//; - $disallowed->{'localization'}{$entry} = $newlang; + my %domains=map { $_ => 1; } &Apache::lonnet::all_domains(); + if ($domains{$new}) { + $settings .= $new; + } else { + $settings =~ s/,$//; + $disallowed->{'classlists'}{$entry} = $new; + } } } else { - $langstr =~ s/,$//; + $settings =~ s/,$//; + } + $newvalues{$entry} = $settings; + } elsif ($action eq 'lti') { + if ($entry eq 'lti.override') { + $newvalues{$entry} = $env{'form.'.$entry}; + } elsif (($entry eq 'lti.topmenu') || ($entry eq 'lti.inlinemenu')) { + if ($env{'form.lti.override'}) { + $newvalues{$entry} = $env{'form.'.$entry}; + } else { + $newvalues{$entry} = ''; + } + } elsif ($entry eq 'lti.lcmenu') { + if (($env{'form.lti.override'}) && + (($env{'form.lti.topmenu'}) || ($env{'form.lti.inlinemenu'}))) { + my @lcmenu = &Apache::loncommon::get_env_multiple('form.lti.lcmenu'); + my @newlcmenu; + if (@lcmenu) { + my @menuitems = ('fullname','coursetitle','role','logout','grades'); + foreach my $item (@menuitems) { + next if (($item eq 'grades') && (!$newvalues{'lti.inlinemenu'})); + if (grep(/^\Q$item\E$/,@lcmenu)) { + push(@newlcmenu,$item); + } + } + } + if (@newlcmenu) { + $newvalues{$entry} = join(',',@newlcmenu); + } else { + $newvalues{$entry} = 'none'; + } + } else { + $newvalues{$entry} = ''; + } } - $newvalues{$entry} = $langstr; } else { $newvalues{$entry} = $env{'form.'.$entry}; } - unless ($entry eq 'co-owners') { + unless (($entry eq 'co-owners') || ($entry eq 'discussion_post_fonts')) { if ($newvalues{$entry} ne $values->{$entry}) { $changes->{$entry} = $newvalues{$entry}; } @@ -1058,7 +1473,275 @@ sub process_changes { } } } - return; + return $errors; +} + +sub process_linkprot { + my ($cdom,$cnum,$values,$changes,$context) = @_; + my ($home,$dest,$ltiauth,$privkey,$privnum,$cipher,$errors,%linkprot); + if (ref($values) eq 'HASH') { + foreach my $id (keys(%{$values})) { + if ($id =~ /^\d+$/) { + unless (ref($values->{$id}) eq 'HASH') { + $linkprot{$id} = ''; + } + } + } + } + my %domdefs = &Apache::lonnet::get_domain_defaults($cdom); + my @ids=&Apache::lonnet::current_machine_ids(); + if ($context eq 'domain') { + $home = &Apache::lonnet::domain($cdom,'primary'); + } else { + $home = &Apache::lonnet::homeserver($cnum,$cdom); + } + if ((($context eq 'domain') && ($domdefs{'linkprotenc_dom'})) || + (($context eq 'course') && ($domdefs{'linkprotenc_crs'}))) { + unless (($home eq 'no_host') || ($home eq '')) { + if (grep(/^\Q$home\E$/,@ids)) { + if (ref($domdefs{'privhosts'}) eq 'ARRAY') { + if (grep(/^\Q$home\E$/,@{$domdefs{'privhosts'}})) { + my %privhash = &Apache::lonnet::restore_dom('lti','private',$cdom,$home,1); + $privkey = $privhash{'key'}; + $privnum = $privhash{'version'}; + if (($privnum) && ($privkey ne '')) { + $cipher = Crypt::CBC->new({'key' => $privkey, + 'cipher' => 'DES'}); + } + } + } + } + } + } + if ($context eq 'domain') { + $dest = '/adm/domainprefs'; + $ltiauth = 1; + } else { + $dest = '/adm/courseprefs'; + if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) { + $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'}; + } else { + my %domdefs = &Apache::lonnet::get_domain_defaults($cdom); + $ltiauth = $domdefs{'crsltiauth'}; + } + } + my $switchserver = &check_switchserver($cdom,$cnum,$context,$dest); + my (@items,%deletions,%itemids,%haschanges); + if ($env{'form.linkprot_add'}) { + my $name = $env{'form.linkprot_name_add'}; + $name =~ s/(`)/'/g; + my ($newid,$error) = &get_linkprot_id($cdom,$cnum,$name,$context); + if ($newid) { + $itemids{'add'} = $newid; + push(@items,'add'); + $haschanges{$newid} = 1; + } else { + $errors .= ''. + &mt('Failed to acquire unique ID for link protection'). + ''; + } + } + if (ref($values) eq 'HASH') { + my @todelete = &Apache::loncommon::get_env_multiple('form.linkprot_del'); + my $maxnum = $env{'form.linkprot_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.linkprot_id_'.$i}; + $itemid =~ s/\D+//g; + if ($itemid) { + if (ref($values->{$itemid}) eq 'HASH') { + push(@items,$i); + $itemids{$i} = $itemid; + if ((@todelete > 0) && (grep(/^$i$/,@todelete))) { + $deletions{$itemid} = $values->{$itemid}->{'name'}; + } + } + } + } + } + foreach my $idx (@items) { + my $itemid = $itemids{$idx}; + next unless ($itemid); + if (exists($deletions{$itemid})) { + $linkprot{$itemid} = $deletions{$itemid}; + $haschanges{$itemid} = 1; + next; + } + my %current; + if (ref($values) eq 'HASH') { + if (ref($values->{$itemid}) eq 'HASH') { + foreach my $key (keys(%{$values->{$itemid}})) { + $current{$key} = $values->{$itemid}->{$key}; + } + } + } + foreach my $inner ('name','lifetime','version') { + my $formitem = 'form.linkprot_'.$inner.'_'.$idx; + $env{$formitem} =~ s/(`)/'/g; + if ($inner eq 'lifetime') { + $env{$formitem} =~ s/[^\d.]//g; + } + unless ($idx eq 'add') { + if ($current{$inner} ne $env{$formitem}) { + $haschanges{$itemid} = 1; + } + } + if ($env{$formitem} ne '') { + $linkprot{$itemid}{$inner} = $env{$formitem}; + } + } + if ($ltiauth) { + my $reqitem = 'form.linkprot_requser_'.$idx; + $env{$reqitem} =~ s/(`)/'/g; + unless ($idx eq 'add') { + if ($current{'requser'} ne $env{$reqitem}) { + $haschanges{$itemid} = 1; + } + } + if ($env{$reqitem} == 1) { + $linkprot{$itemid}{'requser'} = $env{$reqitem}; + foreach my $inner ('mapuser','notstudent') { + my $formitem = 'form.linkprot_'.$inner.'_'.$idx; + $env{$formitem} =~ s/(`)/'/g; + if ($inner eq 'mapuser') { + if ($env{$formitem} eq 'other') { + my $mapuser = $env{'form.linkprot_customuser_'.$idx}; + $mapuser =~ s/(`)/'/g; + $mapuser =~ s/^\s+|\s+$//g; + if ($mapuser ne '') { + $linkprot{$itemid}{$inner} = $mapuser; + } else { + delete($linkprot{$itemid}{'requser'}); + last; + } + } elsif ($env{$formitem} eq 'sourcedid') { + $linkprot{$itemid}{$inner} = 'lis_person_sourcedid'; + } elsif ($env{$formitem} eq 'email') { + $linkprot{$itemid}{$inner} = 'lis_person_contact_email_primary'; + } + } else { + $linkprot{$itemid}{$inner} = $env{$formitem}; + } + unless ($idx eq 'add') { + if ($current{$inner} ne $linkprot{$itemid}{$inner}) { + $haschanges{$itemid} = 1; + } + } + } + } + } + unless ($switchserver) { + my $keyitem = 'form.linkprot_key_'.$idx; + $env{$keyitem} =~ s/(`)/'/g; + unless ($idx eq 'add') { + if ($current{'key'} ne $env{$keyitem}) { + $haschanges{$itemid} = 1; + } + } + if ($env{$keyitem} ne '') { + $linkprot{$itemid}{'key'} = $env{$keyitem}; + } + my $secretitem = 'form.linkprot_secret_'.$idx; + $env{$secretitem} =~ s/(`)/'/g; + if ($current{'usable'}) { + if ($env{'form.linkprot_changesecret_'.$idx}) { + if ($env{$secretitem} ne '') { + if ($privnum && $cipher) { + $linkprot{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem}); + $linkprot{$itemid}{'cipher'} = $privnum; + } else { + $linkprot{$itemid}{'secret'} = $env{$secretitem}; + } + $haschanges{$itemid} = 1; + } + } else { + $linkprot{$itemid}{'secret'} = $current{'secret'}; + } + } elsif ($env{$secretitem} ne '') { + if ($privnum && $cipher) { + $linkprot{$itemid}{'secret'} = $cipher->encrypt_hex($env{$secretitem}); + $linkprot{$itemid}{'cipher'} = $privnum; + } else { + $linkprot{$itemid}{'secret'} = $env{$secretitem}; + } + $haschanges{$itemid} = 1; + } + } + } + if (keys(%haschanges)) { + foreach my $entry (keys(%haschanges)) { + $changes->{$entry} = $linkprot{$entry}; + } + } + return $errors; +} + +sub get_linkprot_id { + my ($cdom,$cnum,$name,$context) = @_; + # get lock on lti db in course or linkprot db in domain + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock; + if ($context eq 'domain') { + $gotlock = &Apache::lonnet::newput_dom('linkprot',$lockhash,$cdom); + } else { + $gotlock = &Apache::lonnet::newput('lti',$lockhash,$cdom,$cnum); + } + my ($id,$error); + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + if ($context eq 'domain') { + $gotlock = &Apache::lonnet::newput_dom('linkprot',$lockhash,$cdom); + } else { + $gotlock = &Apache::lonnet::newput('lti',$lockhash,$cdom,$cnum); + } + } + if ($gotlock eq 'ok') { + my %currids; + if ($context eq 'domain') { + %currids = &Apache::lonnet::dump_dom('linkprot',$cdom); + } else { + %currids = &Apache::lonnet::dump('lti',$cdom,$cnum,undef,undef,undef,1); + } + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } else { + $id = 1; + } + } else { + $id = 1; + } + if ($id) { + if ($context eq 'domain') { + unless (&Apache::lonnet::newput_dom('linkprot',{ $id => $name },$cdom) eq 'ok') { + $error = 'nostore'; + } + } else { + unless (&Apache::lonnet::newput('lti',{ $id => $name },$cdom,$cnum) eq 'ok') { + $error = 'nostore'; + } + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome; + if ($context eq 'domain') { + $dellockoutcome = &Apache::lonnet::del_dom('linkprot',['lock'],$cdom); + } else { + $dellockoutcome = &Apache::lonnet::del('lti',['lock'],$cdom,$cnum); + } + } else { + $error = 'nolock'; + } + return ($id,$error); } sub get_sec_str { @@ -1103,8 +1786,12 @@ sub check_clone { sub store_changes { my ($cdom,$cnum,$prefs_order,$actions,$prefs,$values,$changes,$crstype) = @_; my ($chome,$output); - my (%storehash,@delkeys,@need_env_update,@oldcloner); + my (%storehash,@delkeys,@need_env_update,@oldcloner,%oldlinkprot); if ((ref($values) eq 'HASH') && (ref($changes) eq 'HASH')) { + if (ref($values->{'linkprot'}) eq 'HASH') { + %oldlinkprot = %{$values->{'linkprot'}}; + } + delete($values->{'linkprot'}); %storehash = %{$values}; } else { if ($crstype eq 'Community') { @@ -1114,6 +1801,20 @@ sub store_changes { } return $output; } + my ($numchanges,$skipstore); + if (ref($changes) eq 'HASH') { + $numchanges = scalar(keys(%{$changes})); + if (($numchanges == 1) && (exists($changes->{'linkprot'}))) { + $skipstore = 1; + } elsif (!$numchanges) { + if ($crstype eq 'Community') { + $output = &mt('No changes made to community settings.'); + } else { + $output = &mt('No changes made to course settings.'); + } + return $output; + } + } my %yesno = ( hidefromcat => '1', problem_stream_switch => '1', @@ -1139,6 +1840,8 @@ sub store_changes { "'$storehash{$key}'")).''; } } + } elsif ($item eq 'linkprot') { + $output .= &store_linkprot($cdom,$cnum,'course',$changes->{$item},\%oldlinkprot); } else { if (ref($prefs->{$item}->{'ordered'}) eq 'ARRAY') { my @settings = @{$prefs->{$item}->{'ordered'}}; @@ -1175,7 +1878,7 @@ sub store_changes { my ($displayname,$text); $text = $prefs->{$item}->{'itemtext'}{$key}; my $displayval; - unless ($key eq 'co-owners') { + unless (($key eq 'co-owners') || ($key eq 'discussion_post_fonts')) { $displayval = $changes->{$item}{$key}; } if ($item eq 'feedback') { @@ -1211,6 +1914,36 @@ sub store_changes { } $displayval = join(', ',@longroles); } + } elsif ($key eq 'discussion_post_fonts') { + my ($classorder,$classtitles)=&discussion_vote_classes(); + my ($styleorder,$styleitems)=&discussion_vote_styles(); + my $count = 1; + my $total = scalar(keys(%{$changes->{$item}})); + foreach my $class (@{$classorder}) { + $count ++; + next unless ($changes->{$item}{$key.'_'.$class}); + my @vals = split(/,/,$changes->{$item}{$key.'_'.$class}); + my $showval = ''; + for (my $i=0; $i<@{$styleorder}; $i++) { + if ($vals[$i] ne '') { + $showval .= 'font-'.$styleorder->[$i].': '.$vals[$i].'; '; + } + } + if ($vals[3] ne '') { + $showval .= $vals[3]; + } + if ($showval ne '') { + $displayval .= $classtitles->{$class}.':{ '. + $showval.'}'; + $storehash{$key.'_'.$class} = $changes->{$item}{$key.'_'.$class}; + } else { + $displayval .= $classtitles->{$class}.': '.&mt('None'); + push(@delkeys,$key.'_'.$class); + } + unless ($count == $total) { + $displayval .= (' 'x2); + } + } } $displayname = &mt($text); } @@ -1225,6 +1958,32 @@ sub store_changes { } } $displayname = &mt($text); + } elsif ($item eq 'lti') { + if ($key eq 'lti.lcmenu') { + if ($changes->{$item}{$key} eq 'none') { + $displayval = &mt('None of the configurable menu items displayed'); + } else { + my %ltititles = <imenu_titles(); + $displayval = join(', ', map { $ltititles{$_}; } split(/,/,$changes->{$item}{$key})); + } + } else { + if ($changes->{$item}{$key} eq '1') { + $displayval = &mt('Yes'); + } elsif ($changes->{$item}{$key} eq '') { + $displayval = &mt('No'); + } + } + $displayname = &mt($text); + } elsif ($item eq 'menuitems') { + unless ($changes->{$item}{$key} eq '') { + if ($key eq 'menudefault') { + $displayname = &mt('Default collection of menu items'); + $displayval = &mt('Collection: [_1]', + $changes->{$item}{$key}); + } elsif ($key eq 'menucollections') { + $displayval = &menucollections_display($changes->{$item}{$key}); + } + } } else { $displayname = &mt($text); } @@ -1237,7 +1996,8 @@ sub store_changes { $displayval = &Apache::lonlocal::locallocaltime($displayval); } elsif ($key eq 'categories') { $displayval = $env{'form.categories_display'}; - } elsif ($key eq 'canuse_pdfforms') { + } elsif (($key eq 'canuse_pdfforms') || ($key eq 'usejsme') || + ($key eq 'uselcmath') || ($key eq 'inline_chem')) { if ($changes->{$item}{$key} eq '1') { $displayval = &mt('Yes'); } elsif ($changes->{$item}{$key} eq '0') { @@ -1269,12 +2029,12 @@ sub store_changes { } } unless (&Apache::lonnet::is_course_owner($cdom,$cnum)) { - if ($env{'form.pending_coowoner'} eq 'accept') { + if ($env{'form.pending_coowner'} eq 'accept') { $displayval = &mt('on'); - } elsif ($env{'form.pending_coowoner'} eq 'decline') { + } elsif ($env{'form.pending_coowner'} eq 'decline') { $displayval = ''; $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Invitation to be co-owner declined')).'
  • '; - } elsif ($env{'form.remove_coowoner'}) { + } elsif ($env{'form.remove_coowner'}) { $displayval = &mt('off'); } if ($displayval) { @@ -1287,8 +2047,32 @@ sub store_changes { } } elsif ($changes->{$item}{$key} eq '') { push(@delkeys,$key); - $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]', - ''.$displayname.'')).'
  • '; + if ($item eq 'lti') { + if (($key eq 'lti.override') || (($key ne 'lti.override') && ($env{'form.lti.override'} ne ''))) { + if (($key eq 'lti.lcmenu') && + ((!$env{'form.lti.topmenu'}) && (!$env{'form.lti.inlinemenu'}))) { + $output .= '
  • '.&mt('LTI settings for menu items only saved if page header and/or inline menu is set to be displayed').'
  • '; + } else { + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]', + ''.$displayname.'', + "'$displayval'")).'
  • '; + } + } elsif (!exists($changes->{$item}{'lti.override'})) { + $output .= '
  • '.&mt('LTI settings only saved if Override is set to "Yes"').'
  • '; + } + } elsif ($item eq 'menuitems') { + if ($key eq 'menudefault') { + $output .= '
  • '.&mt("Default collection of menu items set to: 'Standard' (all menus shown)").'
  • '; + } elsif ($key eq 'menucollections') { + $output .= '
  • '.&mt('Specific collections of menus no longer available').'
  • '; + } + } else { + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]', + ''.$displayname.'')).'
  • '; + } + } elsif ($key eq 'menucollections') { + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Numbered menu collections:')).'
    '. + $displayval.'
  • '; } else { $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]', ''.$displayname.'', @@ -1296,7 +2080,7 @@ sub store_changes { if ($key eq 'url') { my $bkuptime=time; $output .= (' 'x2).&mt('(Previous URL backed up)').': '. - $storehash{'top level map backup '.$bkuptime} => $values->{$key}; + $storehash{'top level map backup '.$bkuptime} = $values->{$key}; } $output .= '
  • '; } @@ -1310,7 +2094,9 @@ sub store_changes { } } } else { - $storehash{$key} = $changes->{$item}{$key}; + unless ($key eq 'discussion_post_fonts') { + $storehash{$key} = $changes->{$item}{$key}; + } } } if ($key eq 'cloners') { @@ -1325,7 +2111,9 @@ sub store_changes { } if (($key eq 'description') || ($key eq 'cloners') || ($key eq 'hidefromcat') || ($key eq 'categories') || - ($key eq 'co-owners')) { + ($key eq 'co-owners') || ($key eq 'lti.override') || + ($key eq 'lti.topmenu') || ($key eq 'lti.inlinemenu') || + ($key eq 'lti.lcmenu')) { push(@need_env_update,$key); } } @@ -1334,14 +2122,17 @@ sub store_changes { $output .= ''; } else { if ($crstype eq 'Community') { - $output = &mt('No changes made to community settings.'); + $output .= &mt('No changes made to community settings.'); } else { - $output = &mt('No changes made to course settings.'); + $output .= &mt('No changes made to course settings.'); } } } } } + if ($skipstore) { + return $output; + } if (&Apache::lonnet::put('environment',\%storehash,$cdom,$cnum) eq 'ok') { if (ref($changes) eq 'HASH') { if (ref($changes->{'courseinfo'}) eq 'HASH') { @@ -1384,6 +2175,157 @@ sub store_changes { return $output; } +sub store_linkprot { + my ($cdom,$cnum,$context,$changes,$oldlinkprot) = @_; + my ($ltiauth,$home,$lti_save_error,$output,$error,%ltienc,@deletions); + if ($context eq 'domain') { + $ltiauth = 1; + $home = &Apache::lonnet::domain($cdom,'primary'); + } else { + $home = &Apache::lonnet::homeserver($cnum,$cdom); + if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) { + $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'}; + } else { + my %domdefs = &Apache::lonnet::get_domain_defaults($cdom); + $ltiauth = $domdefs{'crsltiauth'}; + } + } + if (ref($changes) eq 'HASH') { + foreach my $id (sort { $a <=> $b } keys(%{$changes})) { + if (ref($changes->{$id}) eq 'HASH') { + if (exists($changes->{$id}->{'key'})) { + $ltienc{$id}{'key'} = $changes->{$id}->{'key'}; + delete($changes->{$id}->{'key'}); + } + if (exists($changes->{$id}->{'secret'})) { + $ltienc{$id}{'secret'} = $changes->{$id}->{'secret'}; + delete($changes->{$id}->{'secret'}); + } elsif (ref($oldlinkprot->{$id}) eq 'HASH') { + if (exists($oldlinkprot->{$id}{'usable'})) { + $changes->{$id}->{'usable'} = 1; + } + } + } + } + } + my @ids=&Apache::lonnet::current_machine_ids(); + if (keys(%ltienc) > 0) { + if ($context eq 'domain') { + foreach my $id (keys(%ltienc)) { + if (exists($ltienc{$id}{'secret'})) { + $changes->{$id}->{'usable'} = 1; + } + } + } else { + unless (($home eq 'no_host') || ($home eq '')) { + my $allowed; + foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } } + if ($allowed) { + if (&Apache::lonnet::put('nohist_ltienc',\%ltienc,$cdom,$cnum,1) eq 'ok') { + foreach my $id (keys(%ltienc)) { + if (exists($ltienc{$id}{'secret'})) { + $changes->{$id}->{'usable'} = 1; + } + } + } else { + $lti_save_error = 1; + } + } + } + } + } + unless ($lti_save_error) { + if ($context eq 'course') { + if (&Apache::lonnet::put('lti',$changes,$cdom,$cnum,1) eq 'ok') { + my $hashid=$cdom.'_'.$cnum; + &Apache::lonnet::devalidate_cache_new('courselti',$hashid); + unless (($home eq 'no_host') || ($home eq '')) { + if (grep(/^\Q$home\E$/,@ids)) { + &Apache::lonnet::devalidate_cache_new('courseltienc',$hashid); + } + } + } else { + $lti_save_error = 1; + } + } + unless ($lti_save_error) { + foreach my $id (sort { $a <=> $b } %{$changes}) { + if (ref($changes->{$id}) eq 'HASH') { + my %values = %{$changes->{$id}}; + my %desc = &linkprot_names(); + my $display; + foreach my $title ('name','lifetime','version','key','secret') { + if (($title eq 'key') || ($title eq 'secret')) { + if (ref($ltienc{$id}) eq 'HASH') { + if (exists($ltienc{$id}{$title})) { + if ($title eq 'secret') { + my $length = length($ltienc{$id}{$title}); + $display .= $desc{$title}.': ['.&mt('not shown').'], '; + } else { + $display .= $desc{$title}.': '.$ltienc{$id}{$title}.', '; + } + } + } + } elsif ($title eq 'version') { + if ($values{$title} eq 'LTI-1p0') { + $display .= $desc{$title}.': 1.1, '; + } + } else { + $display .= $desc{$title}.': '.$values{$title}.', '; + } + } + if ($ltiauth) { + if (($values{'requser'}) && ($values{'mapuser'} ne '')) { + if ($values{'mapuser'} eq 'lis_person_contact_email_primary') { + $display .= &mt('Source of username: Email address [_1]', + '(lis_person_contact_email_primary)').', '; + } elsif ($values{'mapuser'} eq 'lis_person_sourcedid') { + $display .= &mt('Source of username: User ID [_1]', + '(lis_person_sourcedid)').', '; + } else { + $display .= &mt('Source of username: [_1]',$values{'mapuser'}).', '; + } + if ($values{'notstudent'} eq 'auth') { + $display .= &mt('Display LON-CAPA login page if no match').', '; + } elsif ($values{'notstudent'} eq 'reject') { + $display .= &mt('Discontinue launch if no match').', '; + } + } + } + $display =~ s/, $//; + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('[_1] set to [_2]',''.$id.'', + "'$display'")).'
  • '; + } elsif (ref($oldlinkprot->{$id}) eq 'HASH') { + my $oldname = $oldlinkprot->{$id}{'name'}; + $output .= '
  • '.&Apache::lonhtmlcommon::confirm_success(&mt('Deleted setting for [_1]',''."$id ($oldname)".'')).'
  • '; + } + } + } else { + $lti_save_error = 1; + } + } + unless ($lti_save_error) { + foreach my $id (sort { $a <=> $b } keys(%{$changes})) { + unless (ref($changes->{$id}) eq 'HASH') { + push(@deletions,$id); + } + } + if (@deletions) { + if ($context eq 'course') { + &Apache::lonnet::del('nohist_ltienc',\@deletions,$cdom,$cnum); + } + } + } + if ($lti_save_error) { + $output .= '
  • '. + ''. + &mt('An error occurred when saving changes to link protection settings, which remain unchanged.'). + ''. + '
  • '; + } + return $output; +} + sub update_env { my ($cnum,$cdom,$chome,$need_env_update,$storehash) = @_; my $count = 0; @@ -1409,6 +2351,8 @@ sub update_env { my @coowners = split(',',$storehash->{'internal.'.$key}); $crsinfo{$env{'request.course.id'}}{'co-owners'} = \@coowners; $count ++; + } elsif ($key =~ /^lti\./) { + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.'.$key => $storehash->{$key}}); } } if ($count) { @@ -1507,16 +2451,98 @@ sub get_course { } my $cdom=$env{'course.'.$courseid.'.domain'}; my $cnum=$env{'course.'.$courseid.'.num'}; - return ($cnum,$cdom); + my $chome=$env{'course.'.$courseid.'.home'}; + return ($cnum,$cdom,$chome); } sub get_jscript { - my ($cdom,$phase,$crstype) = @_; + my ($cid,$cdom,$phase,$crstype,$settings,$noedit) = @_; my ($can_toggle_cat,$can_categorize) = &can_modify_catsettings($cdom,$crstype); - my ($jscript,$categorize_js); + my ($jscript,$categorize_js,$loncaparev_js,$instcode_js); my $stubrowse_js = &Apache::loncommon::studentbrowser_javascript(); my $browse_js = &Apache::loncommon::browser_and_searcher_javascript('parmset'); my $cloners_js = &cloners_javascript($phase); + my @code_order; + if ($crstype ne 'Community') { + if (ref($settings) eq 'HASH') { + my %codedefaults; + &Apache::lonnet::auto_instcode_defaults($cdom,\%codedefaults, + \@code_order); + if (@code_order > 0) { + if ($phase eq 'display') { + my @actions = &Apache::loncommon::get_env_multiple('form.actions'); + if (grep(/^courseinfo$/,@actions)) { + if ($settings->{'cloners'} ne '') { + unless ($settings->{'cloners'} eq '*') { + my @cloners = split(/,/,$settings->{'cloners'}); + my @standardnames = &Apache::loncommon::get_standard_codeitems(); + my %local_to_standard; + for (my $i=0; $i<@code_order; $i++) { + $local_to_standard{$code_order[$i]} = $standardnames[$i]; + } + foreach my $cloner (@cloners) { + if (($cloner !~ /^\Q*:\E$match_domain$/) && + ($cloner !~ /^$match_username\:$match_domain$/)) { + foreach my $item (split(/\&/,$cloner)) { + my ($key,$val) = split(/\=/,$item); + $val = &unescape($val); + foreach my $code (@code_order) { + if ($key eq $local_to_standard{$code}) { + $env{'form.'.$key} = $val; + } + } + } + } + } + } + } + } + } + my @codetitles; + (undef,$instcode_js) = + &Apache::courseclassifier::instcode_selectors($cdom,'display',undef, + \@codetitles); + $instcode_js .= <{'menucollections'} ne '') { + my @current; + foreach my $item (split(/;/,$settings->{'menucollections'})) { + my ($num) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + push(@current,$num); + } + } + $collections = join("','",sort { $a <=> $b } @current); + if ($collections) { + $collections = "'$collections'"; + } + $next += $current[-1]; + } + } + my $deftext = &mt('Standard (all menus shown)'); + $menuitems_js = <'. - "\n".$stubrowse_js."\n"; + '// '."\n". + ''."\n".$stubrowse_js."\n"; return $jscript; } @@ -1566,7 +2705,7 @@ function update_cloners(caller,num) { if (document.$formname.cloners_activate[i].value == '0') { document.$formname.cloners_activate[i].checked = false; } - if (document.$formname.cloners_activate[i].value == '') { + if (document.$formname.cloners_activate[i].value == '-1') { document.$formname.cloners_activate[i].checked = true; } } @@ -1617,31 +2756,146 @@ function getIndexByName(item) { ENDSCRIPT } +sub linkprot_javascript { + return <<"ENDSCRIPT"; +function toggleLinkProt(form,num,item) { + var radioname = ''; + var currdivid = ''; + var newdivid = ''; + if ((document.getElementById('linkprot_divcurr'+item+'_'+num)) && + (document.getElementById('linkprot_divchg'+item+'_'+num))) { + currdivid = document.getElementById('linkprot_divcurr'+item+'_'+num); + newdivid = document.getElementById('linkprot_divchg'+item+'_'+num); + radioname = form.elements['linkprot_change'+item+'_'+num]; + if (radioname) { + if (radioname.length > 0) { + var setvis; + for (var i=0; i 0) { + var setvis; + for (var i=0; i{'categories'},$crstype)."\n"; + $settings->{'categories'},$crstype,$disabled)."\n"; } } if (ref($domconf{'autoenroll'}) eq 'HASH') { $autocoowner = $domconf{'autoenroll'}{'co-owners'}; } + my ($currcanclone,@code_order,$cloner_instcode); + my %codedefaults; + &Apache::lonnet::auto_instcode_defaults($cdom,\%codedefaults, + \@code_order); + if ($settings->{'cloners'}) { + unless ($settings->{'cloners'} eq '*') { + my @currclone = split(/,/,$settings->{'cloners'}); + foreach my $item (@currclone) { + unless ($item eq '*') { + if (($item !~ /\:/) && ($item =~ /=/)) { + $cloner_instcode = 1; + } + } + } + } + } + if (ref($domconf{'coursedefaults'}) eq 'HASH') { + my $canclone = $domconf{'coursedefaults'}{'canclone'}; + if (ref($canclone) eq 'HASH') { + if (ref($canclone->{'instcode'}) eq 'ARRAY') { + if ($settings->{'internal.coursecode'}) { + my @posscodes; + if (@code_order > 0) { + $currcanclone = 'instcode'; + foreach my $item (@{$canclone->{'instcode'}}) { + if (grep(/^\Q$item\E$/,@code_order)) { + push(@posscodes,$item); + } + } + my $codestr = join(' + ',@posscodes); + $clonedefaults = &mt('Default for official courses is to also allow cloning if [_1] match in cloner and cloned.', + '"'.$codestr.'"').'
    '; + } + } + } + } elsif ($canclone eq 'domain') { + $clonedefaults = &mt('Default is for any course requester in [_1] domain to be able to clone.', + ''.$cdom.'').'
    '; + $currcanclone = 'domain'; + } + if ($clonedefaults) { + if ($settings->{'cloners'} ne '') { + $clonedefaults .= '
    '.&mt('Default does [_1]not[_2] currently apply because values have been set in the course.','',''); + } else { + $clonedefaults .= &mt('Default is disregarded if [_1]any[_2] values are set here in the course.','',''); + } + $clonedefaults = '

    '.$clonedefaults; + } + } if (!defined($categoriesform)) { $categoriesform = &mt('No categories defined in this domain.'); } - my ($can_toggle_cat,$can_categorize) = &can_modify_catsettings($cdom,$crstype); + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $replace; if ($crstype eq 'Community') { @@ -1649,22 +2903,28 @@ sub print_courseinfo { } else { $replace = &mt('To replace the standard title for a course role, enter a title, otherwise leave blank'); } + my ($editmap,$editsyllabus); + unless ($noedit) { + $editmap = (' 'x2). + '". + &mt('Select Map').'
    '. + &mt('Modification may make assessment data inaccessible!'). + ''; + $editsyllabus = &mt('[_1]Edit[_2]','', + ''); + } my %items = ( 'url' => { - text => ''.&mt($itemtext->{'url'}).''.(' 'x2). - '". - &mt('Select Map').'
    '. - &mt('Modification may make assessment data inaccessible!'). - '', + text => ''.&mt($itemtext->{'url'}).''.$editmap, input => 'textbox', - size => '40', + size => '55', advanced => 1 }, 'description' => { text => ''.&mt($itemtext->{'description'}).'', input => 'textbox', - size => '25', + size => '55', }, 'owner' => { text => ''.&mt($itemtext->{'owner'}).'', @@ -1672,18 +2932,24 @@ sub print_courseinfo { 'co-owners' => { text => ''.&mt($itemtext->{'co-owners'}).'', }, + 'clonedfrom' => { + text => ''.&mt($itemtext->{'clonedfrom'}).'', + }, 'courseid' => { text => ''.&mt($itemtext->{'courseid'}).'
    '.'('. &mt('internal, optional').')', input => 'textbox', size => '25', }, + 'uniquecode' => { + text => ''.&mt($itemtext->{'uniquecode'}).'', + }, 'cloners' => { text => ''.&mt($itemtext->{'cloners'}).'
    '. - &mt('Owner and Coordinators included automatically'), + &mt('Owner and Coordinators included automatically'). + $clonedefaults, input => 'textbox', - size => '40', - advanced => 1 + size => '40' }, 'rolenames' => { text => ''.&mt($itemtext->{'rolenames'}).'
    '. @@ -1692,11 +2958,10 @@ sub print_courseinfo { size => '20', advanced => 1 }, - 'externalsyllabus' => { - text => ''.&mt($itemtext->{'externalsyllabus'}).'
    ('. - &mt('not using syllabus template)'), - input => 'textbox', - size => '40', + 'syllabus' => { + text => ''.&mt($itemtext->{'syllabus'}).'
    '. + &mt('(Syllabus type in use)').(' ' x2). + $editsyllabus, }, 'hidefromcat' => { text => ''.&mt($itemtext->{'hidefromcat'}).'
    '. @@ -1709,27 +2974,40 @@ sub print_courseinfo { input => 'textbox', size => '25', }, + 'loncaparev' => { + text => ''.&mt($itemtext->{'loncaparev'}).'', + }, + 'defaultcredits' => { + text => ''.&mt($itemtext->{'defaultcredits'}).'', + }, ); my $datatable; my $count = 0; foreach my $item (@{$ordered}) { + my $colspan; if ($item eq 'hidefromcat') { next if (!$can_toggle_cat); } elsif ($item eq 'categories') { next if (!$can_categorize); + } elsif ($item eq 'uniquecode') { + next if (!$env{'course.'.$env{'request.course.id'}.'.internal.uniquecode'}); + } + unless (($item eq 'cloners') || ($item eq 'rolenames')) { + $colspan = 2; } $count ++; if (exists $items{$item}{advanced} && $items{$item}{advanced} == 1) { - $datatable .= &item_table_row_start($items{$item}{text},$count,"advanced"); + $datatable .= &item_table_row_start($items{$item}{text},$count,"advanced",$colspan); } else { - $datatable .= &item_table_row_start($items{$item}{text},$count); + $datatable .= &item_table_row_start($items{$item}{text},$count,undef,$colspan); } if ($items{$item}{input} eq 'radio') { - $datatable .= &yesno_radio($item,$settings); + $datatable .= &yesno_radio($item,$settings,undef,undef,undef,$noedit); } elsif ($item eq 'cloners') { my $includeempty = 1; my $num = 0; - $datatable .= &Apache::loncommon::start_data_table(). + $datatable .= '
    '. + &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_row(). ''. - (' 'x2).''.&mt('Yes').''. + (' 'x2).'
    '.&mt('Or'). - '
    '. + '
    '.&mt('Or'). + '
    '. &Apache::loncommon::start_data_table(); my @cloners; if ($settings->{$item} eq '') { - $datatable .= &new_cloners_dom_row($cdom,'0'); + unless ($noedit) { + my $default; + if ($currcanclone eq 'domain') { + $default = '0'; + } + $datatable .= &new_cloners_dom_row($cdom,'0',$default); + } } elsif ($settings->{$item} ne '*') { my @entries = split(/,/,$settings->{$item}); if (@entries > 0) { @@ -1773,7 +3058,7 @@ sub print_courseinfo { '
    '. ''. &Apache::loncommon::end_data_table_row(); $num ++; @@ -1796,23 +3081,56 @@ sub print_courseinfo { ''. $add_domtitle.'
    '. &Apache::loncommon::select_dom_form('','cloners_newdom', - $includeempty). + $includeempty,undef,undef, + undef,undef,$noedit). ''. ''.&Apache::loncommon::end_data_table_row(). &Apache::loncommon::end_data_table(). - '
    '.&mt('And'). - '
    '. + '
    '.&mt('And'). + '
    '. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_row(). ''. &mt('Specific users').' ('. &mt('user:domain,user:domain').')
    '. &Apache::lonhtmlcommon::textbox($item,$cloners_str, - $items{$item}{'size'}). + $items{$item}{'size'},$disabled). ''.&Apache::loncommon::end_data_table_row(). &Apache::loncommon::end_data_table(); + if (@code_order > 0) { + my (%cat_items,@codetitles,%cat_titles,%cat_order); + my ($jscript,$totcodes,$numtitles,$lasttitle) = + &Apache::courseclassifier::instcode_selectors_data($cdom,'display', + \%cat_items,\@codetitles, + \%cat_titles,\%cat_order); + my $showncodes = 'off'; + my $checkedoff = ' checked="checked"'; + my $checkedon = ''; + if ($cloner_instcode) { + $checkedon = $checkedoff; + $checkedoff = ''; + $showncodes = 'on'; + } + $datatable .= '
    '.&mt('And'). + '
    '. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_row(). + ''. + &mt('Cloning by official course(s) based on course category').(' 'x2). + ''. + (' '). + ''. + &Apache::courseclassifier::build_instcode_selectors($numtitles, + $lasttitle,\%cat_items,\@codetitles,\%cat_titles,\%cat_order, + $showncodes,'LC_cloners_instcodes','LC_cloners_instcode',$noedit). + ''.&Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table(); + } } elsif ($item eq 'rolenames') { - $datatable .= &Apache::loncommon::start_data_table(); + $datatable .= ''. + &Apache::loncommon::start_data_table(); my @roles; if ($crstype eq 'Community') { @roles = ('co'); @@ -1827,12 +3145,17 @@ sub print_courseinfo { ''. &Apache::lonhtmlcommon::textbox('rolenames_'.$role, $settings->{$role.'.plaintext'}, - $items{$item}{size}).''. + $items{$item}{size},$disabled).''. &Apache::loncommon::end_data_table_row(); } $datatable .= &Apache::loncommon::end_data_table().''; } elsif ($item eq 'categories') { - my $launcher = 'onFocus="this.blur();javascript:catsbrowser();";'; + my $launcher; + if ($noedit) { + $launcher = $disabled; + } else { + $launcher = 'onfocus="this.blur();javascript:catsbrowser();"'; + } $datatable .= ''. &Apache::lonhtmlcommon::textbox($item.'_display',$settings->{$item}, $items{$item}{size},$launcher); @@ -1846,10 +3169,23 @@ sub print_courseinfo { } else { $owner = &mt('None specified'); } - my $domdesc = &Apache::lonnet::domain($cdom,'description'); $datatable .= $owner; + } elsif ($item eq 'clonedfrom') { + my $clonesrc = $env{'course.'.$env{'request.course.id'}.'.clonedfrom'}; + my $clonedfrom = &mt('None'); + if ($clonesrc =~ m{/$match_domain/$match_courseid}) { + my %clonesrcinfo = &Apache::lonnet::coursedescription($clonesrc); + if ($clonesrcinfo{'description'}) { + $clonedfrom = $clonesrcinfo{'description'}.' '.($clonesrc); + } + } + $datatable .= $clonedfrom; + } elsif ($item eq 'uniquecode') { + my $code = $env{'course.'.$env{'request.course.id'}.'.internal.uniquecode'}; + if ($code) { + $datatable .= $code; + } } elsif ($item eq 'co-owners') { - my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $coowners = $env{'course.'.$env{'request.course.id'}.'.internal.co-owners'}; my @currcoown; if ($coowners) { @@ -1859,17 +3195,57 @@ sub print_courseinfo { if (($crstype eq 'Course') && ($env{'course.'.$env{'request.course.id'}.'.internal.coursecode'}) && ($autocoowner)) { $datatable .= &show_autocoowners(@currcoown); } else { - $datatable .= &coowner_invitations($cnum,$cdom,@currcoown); + $datatable .= &coowner_invitations($cnum,$cdom,\@currcoown,$noedit); } } else { if (($crstype eq 'Course') && ($env{'course.'.$env{'request.course.id'}.'.internal.coursecode'}) && ($autocoowner)) { $datatable .= &show_autocoowners(@currcoown); } else { - $datatable .= &manage_coownership($cnum,$cdom,@currcoown); + $datatable .= &manage_coownership($cnum,$cdom,\@currcoown,$noedit); + } + } + } elsif ($item eq 'syllabus') { + my $external = $env{'course.'.$env{'request.course.id'}.'.externalsyllabus'}; + my $uploaded = $env{'course.'.$env{'request.course.id'}.'.uploadedsyllabus'}; + my $minimal = $env{'course.'.$env{'request.course.id'}.'.minimalsyllabus'}; + if (($minimal =~/\w/) || ($uploaded =~/\w/)) { + if ($minimal =~/\w/) { + if ($external =~ m{\Q$minimal\E$}) { + undef($external); + } + } elsif ($uploaded =~/\w/) { + if ($external =~ m{\Q$uploaded\E$}) { + undef($external); + } } + } elsif ($external!~/\w/) { + undef($external); } + if ($external) { + $datatable .= &mt('External URL'); + } elsif ($minimal) { + $datatable .= &mt('Minimal template'); + } elsif ($uploaded) { + $datatable .= &mt('Uploaded file'); + } else { + $datatable .= &mt('Standard template'); + } + $datatable .= (' ' x 2). + &mt('[_1]View[_2]', + '', + ''); + } elsif ($item eq 'loncaparev') { + my $loncaparev = $env{'course.'.$env{'request.course.id'}.'.internal.releaserequired'}; + my $showreqd; + if ($loncaparev) { + $showreqd = &mt('[_1] or newer',$loncaparev).' '. + &mt('Details').''; + } else { + $showreqd = &mt('No specific version required'); + } + $datatable .= $showreqd; } else { - $datatable .= &Apache::lonhtmlcommon::textbox($item,$settings->{$item},$items{$item}{size}); + $datatable .= &Apache::lonhtmlcommon::textbox($item,$settings->{$item},$items{$item}{size},$disabled); } $datatable .= &item_table_row_end(); } @@ -1878,19 +3254,24 @@ sub print_courseinfo { } sub new_cloners_dom_row { - my ($newdom,$num) = @_; - my $output; + my ($newdom,$num,$default) = @_; + my ($output,$checkedon,$checkedoff); if ($newdom ne '') { + if ($num eq $default) { + $checkedon = 'checked="checked" '; + } else { + $checkedoff = 'checked="checked" '; + } $output .= &Apache::loncommon::start_data_table_row(). ''. &mt('Any user in domain:').' '.$newdom.''. (' 'x2).''.(' 'x2). ' @@ -2025,9 +3407,744 @@ ENDSCRIPT return; } +sub display_loncaparev_constraints { + my ($r,$navmap,$loncaparev,$crstype) = @_; + my ($reqdmajor,$reqdminor); + my $cid = $env{'request.course.id'}; + my $cdom = $env{'course.'.$cid.'.domain'}; + my $cnum = $env{'course.'.$cid.'.num'}; + my $output; + my %lt = &Apache::lonlocal::texthash ( + 'all' => 'all', + 'section/group' => 'section/group', + 'user' => 'user', + ); + &Apache::lonrelrequtils::init_global_hashes(); + if (defined($Apache::lonrelrequtils::checkcrstypes{$crstype})) { + ($reqdmajor,$reqdminor) = split(/\./,$Apache::lonrelrequtils::checkcrstypes{$crstype}); + $output .= '

    '.&mt('Course type: [_1] requires [_2] or newer',$crstype, + $Apache::lonrelrequtils::checkcrstypes{$crstype}).'

    '; + } + my (%fromparam,%rowspan,%bymap,%byresource,@scopeorder,%toshow,%allmaps, + %byresponsetype,%bysubmission,%fromblocks,%bycrsrestype); + @scopeorder = ('all','section/group','user'); + my $now = time; + my $resourcedata = &Apache::lonparmset::readdata($cnum,$cdom); + if (ref($resourcedata) eq 'HASH') { + foreach my $key (keys(%{$resourcedata})) { + my %found; + foreach my $item (keys(%Apache::lonrelrequtils::checkparms)) { + if ($key =~ /(\Q$item\E)$/) { + if (ref($Apache::lonrelrequtils::checkparms{$item}) eq 'ARRAY') { + my $value = $resourcedata->{$key}; + if ($item eq 'examcode') { + if (&Apache::lonnet::validCODE($value)) { + $value = 'valid'; + } else { + $value = ''; + } + } elsif ($item eq 'printstartdate') { + if ($value =~ /^\d+$/) { + if ($value > $now) { + $value = 'future'; + } + } + } elsif ($item eq 'printenddate') { + if ($value =~ /^\d+$/) { + if ($value < $now) { + $value = 'past'; + } + } + } + if (grep(/^\Q$value\E$/,@{$Apache::lonrelrequtils::checkparms{$item}})) { + my $stdtype = &Apache::lonparmset::standard_parameter_types($item); + $found{$item}{'valname'} = &get_param_description($stdtype,$value); + $found{$item}{'rev'} = $Apache::lonnet::needsrelease{'parameter:'.$item.':'.$value.'::'}; + } + } + } + } + foreach my $item (keys(%Apache::lonrelrequtils::checkparmvalsmatch)) { + if (ref($Apache::lonrelrequtils::checkparmvalsmatch{$item}) eq 'ARRAY') { + my $value = $resourcedata->{$key}; + foreach my $valuematch (@{$Apache::lonrelrequtils::checkparmvalsmatch{$item}}) { + if ($value =~ /$valuematch/) { + my $stdtype = &Apache::lonparmset::standard_parameter_types($item); + $found{$item}{'valname'} = &get_param_description($stdtype,$value,1); + $found{$item}{'rev'} = + $Apache::lonnet::needsrelease{'parameter:'.$item.'::'.$valuematch.':'}; + last; + } + } + } + } + foreach my $item (keys(%Apache::lonrelrequtils::checkparmnamesmatch)) { + my $regexp; + if ($item eq 'maplevelrecurse') { + $regexp = '\.(?:sequence|page)___\(rec\)\.'; + } + if ($regexp ne '') { + if ($key =~ /$regexp.*\.(\w+)$/) { + my $name = $1; + my $value = $resourcedata->{$key}; + if ($name eq 'type') { + last unless (($value eq 'problem') || ($value eq 'practice') || ($value eq 'exam') || + ($value eq 'survey') || ($value eq 'surveycred') || ($value eq 'anonsurvey') || + ($value eq 'anonsurveycred') || ($value eq 'randomizetry') || ($value eq 'library')); + } + my $stdtype = &Apache::lonparmset::standard_parameter_types($name); + $found{$name}{'valname'} = &get_param_description($stdtype,$value); + $found{$name}{'extra'} = &mt('Recursive into sub-folders'); + $found{$name}{'rev'} = + $Apache::lonnet::needsrelease{'parameter::::'.$item}; + } + } + } + foreach my $item (keys(%found)) { + my $stdname = &Apache::lonparmset::standard_parameter_names($item); + my $rev = $found{$item}{'rev'}; + my $valname = $found{$item}{'valname'}; + my $extra = $found{$item}{'extra'}; + my ($middle,$scope,$which,$level,$map,$resource); + my $start = $cid.'.'; + if ($key =~ /^\Q$start\E(\[useropt\:($match_username\:$match_domain)\]\.)/) { + $middle = $1; + $which = $2; + $scope = 'user'; + } elsif ($key =~ /^\Q$start\E(\[(\w+)\]\.)/) { + $middle = $1; + $which = $2; + $scope = 'section/group'; + } else { + $scope = 'all'; + } + my $what="$stdname=$valname"; + if ($extra) { + $what .= "
    $extra
    "; + } + if ($key =~ /^\Q$start$middle\E\w+\.\Q$item\E$/) { + $level = 'general'; + if ($scope eq 'all') { + if (ref($fromparam{$rev}{$scope}) eq 'ARRAY') { + unless(grep(/^\Q$what\E$/,@{$fromparam{$rev}{$scope}})) { + push(@{$fromparam{$rev}{$scope}},$what); + } + } else { + push(@{$fromparam{$rev}{$scope}},$what); + } + } else { + if (ref($fromparam{$rev}{$scope}{$which}) eq 'ARRAY') { + unless (grep(/^\Q$what\E$/,@{$fromparam{$rev}{$scope}{$which}})) { + push(@{$fromparam{$rev}{$scope}{$which}},$what); + } + } else { + push(@{$fromparam{$rev}{$scope}{$which}},$what); + } + } + $rowspan{$rev} ++; + } elsif ($key =~ /^\Q$start$middle\E(.+)___\((all|rec)\).\w+\.\Q$item\E$/) { + $level = 'folder'; + $map = $1; + if ($scope eq 'all') { + if (ref($bymap{$map}{$rev}{$scope}) eq 'ARRAY') { + unless(grep(/^\Q$what\E$/,@{$bymap{$map}{$rev}{$scope}})) { + push(@{$bymap{$map}{$rev}{$scope}},$what); + } + } else { + push(@{$bymap{$map}{$rev}{$scope}},$what); + } + } else { + if (ref($bymap{$map}{$rev}{$scope}{$which}) eq 'ARRAY') { + unless(grep(/^\Q$what\E$/,@{$bymap{$map}{$rev}{$scope}{$which}})) { + push(@{$bymap{$map}{$rev}{$scope}{$which}},$what); + } + } else { + push(@{$bymap{$map}{$rev}{$scope}{$which}},$what); + } + } + } elsif ($key =~ /^\Q$start$middle\E(.+)\.\w+\.\Q$item\E$/) { + $level = 'resource'; + $resource = $1; + if ($scope eq 'all') { + if (ref($byresource{$resource}{$rev}{$scope}) eq 'ARRAY') { + unless(grep(/^\Q$what\E$/,@{$byresource{$resource}{$rev}{$scope}})) { + push(@{$byresource{$resource}{$rev}{$scope}},$what); + } + } else { + push(@{$byresource{$resource}{$rev}{$scope}},$what); + } + } else { + if (ref($byresource{$resource}{$rev}{$scope}{$which}) eq 'ARRAY') { + unless (grep(/^\Q$what\E$/,@{$byresource{$resource}{$rev}{$scope}{$which}})) { + push(@{$byresource{$resource}{$rev}{$scope}{$which}},$what); + } + } else { + push(@{$byresource{$resource}{$rev}{$scope}{$which}},$what); + } + } + } + } + } + if (keys(%fromparam)) { + $output .= '

    '.&mt('Requirements from general settings').'

    '. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''.&mt('Release').''.&mt('Scope').''. + ''.&mt('Extent').''.&mt('Setting').''. + &Apache::loncommon::end_data_table_header_row(); + foreach my $rev (keys(%fromparam)) { + my ($major,$minor) = split(/\./,$rev); + ($reqdmajor,$reqdminor) = + &Apache::lonrelrequtils::update_reqd_loncaparev($major,$minor,$reqdmajor,$reqdminor); + $output .= &Apache::loncommon::start_data_table_row(). + ''.$rev.''; + my $newrow; + foreach my $scope (@scopeorder) { + if (ref($fromparam{$rev}{$scope}) eq 'HASH') { + if ($newrow) { + $output .= &Apache::loncommon::continue_data_table_row(); + } + $output .= ''.$lt{$scope}.''; + foreach my $which (sort(keys(%{$fromparam{$rev}{$scope}}))) { + $output .= ''.$which.''. + join('
    ',@{$fromparam{$rev}{$scope}{$which}}).''; + } + $output .= &Apache::loncommon::end_data_table_row(); + $newrow = 1; + } elsif (ref($fromparam{$rev}{$scope}) eq 'ARRAY') { + if ($newrow) { + $output .= &Apache::loncommon::continue_data_table_row(); + } + $output .= ''.$lt{$scope}.' '. + join('
    ',@{$fromparam{$rev}{$scope}}).''. + &Apache::loncommon::end_data_table_row(); + $newrow = 1; + } + } + } + $output .= &Apache::loncommon::end_data_table().'
    '; + } + } + + my %comm_blocks = &Apache::lonnet::dump('comm_block',$cdom,$cnum); + my $now = time; + if (keys(%comm_blocks) > 0) { + foreach my $block (keys(%comm_blocks)) { + if ($block =~ /^firstaccess____(.+)$/) { + my $rev = $Apache::lonnet::needsrelease{'course:commblock:timer'}; + if (ref($comm_blocks{$block}) eq 'HASH') { + push(@{$fromblocks{'timer'}{$rev}},&unescape($comm_blocks{$block}{'event'}). + ' '.&mt('set by [_1]', + &Apache::loncommon::plainname(split(/:/,$comm_blocks{$block}{'setter'})))); + } + next; + } elsif ($block =~ /^(\d+)____(\d+)$/) { + my ($start,$end) = ($1,$2); + next if ($end < $now); + } + if (ref($comm_blocks{$block}) eq 'HASH') { + if (ref($comm_blocks{$block}{'blocks'}) eq 'HASH') { + if (ref($comm_blocks{$block}{'blocks'}{'docs'}) eq 'HASH') { + if (keys(%{$comm_blocks{$block}{'blocks'}{'docs'}}) > 0) { + my $rev = $Apache::lonnet::needsrelease{'course:commblock:docs'}; + push(@{$fromblocks{'docs'}{$rev}},&unescape($comm_blocks{$block}{'event'}). + ' '. + &mt('set by [_1]', + &Apache::loncommon::plainname(split(/:/,$comm_blocks{$block}{'setter'})))); + } + } elsif ($comm_blocks{$block}{'blocks'}{'printout'} eq 'on') { + my $rev = $Apache::lonnet::needsrelease{'course:commblock:printout'}; + push(@{$fromblocks{'printout'}{$rev}},&unescape($comm_blocks{$block}{'event'}). + ' '. + &mt('set by [_1]', + &Apache::loncommon::plainname(split(/:/,$comm_blocks{$block}{'setter'})))); + + } + } + } + } + if (keys(%fromblocks)) { + my %lt = ( + docs => 'Content blocking', + printout => 'Printout generation', + timer => 'Timed quiz trigger', + ); + $output .= '

    '.&mt('Requirements from exam blocking').'

    '. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row(). + ''.&mt('Release').''.&mt('Setting').''. + ''.&mt('Event(s)').''. + &Apache::loncommon::end_data_table_header_row(); + foreach my $type ('docs','printout','timer') { + if (ref($fromblocks{$type}) eq 'HASH') { + foreach my $rev (keys(%{$fromblocks{$type}})) { + my ($major,$minor) = split(/\./,$rev); + ($reqdmajor,$reqdminor) = + &Apache::lonrelrequtils::update_reqd_loncaparev($major,$minor,$reqdmajor,$reqdminor); + $output .= &Apache::loncommon::start_data_table_row(). + ''.$rev.''.$lt{$type}.''; + foreach my $event (sort(@{$fromblocks{$type}{$rev}})) { + $output .= $event.'
    '; + } + $output =~ s{\Q
    \E$}{}; + $output .= ''.&Apache::loncommon::end_data_table_row(); + } + } + } + $output .= &Apache::loncommon::end_data_table().'
    '; + } + } + + if (defined($navmap)) { + my %anonsubms=&Apache::lonnet::dump('nohist_anonsurveys',$cdom,$cnum); + my $rev_anonsurv=$Apache::lonnet::needsrelease{'parameter:type:anonsurvey::'}; + my %randtrysubms=&Apache::lonnet::dump('nohist_randomizetry',$cdom,$cnum); + my $rev_randtry=$Apache::lonnet::needsrelease{'parameter:type:randomizetry::'}; + my $rev_exttool=$Apache::lonnet::needsrelease{'course:courserestype:exttool'}; + my $stdtype=&Apache::lonparmset::standard_parameter_types('type'); + my $stdname=&Apache::lonparmset::standard_parameter_names('type'); + my $valanon=&get_param_description($stdtype,'anonsurvey'); + my $valrandtry=&get_param_description($stdtype,'randomizetry'); + my %checkedrev; + foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() || $_[0]->is_tool() },1,0)) { + my @parts = @{$res->parts()}; + my $symb = $res->symb(); + my $enclosing_map = &Apache::lonnet::declutter($res->enclosing_map_src()); + if ($res->is_tool()) { + my $rev = $rev_exttool; + $bycrsrestype{$symb}{$rev} = &mt('External Tool'); + $allmaps{$enclosing_map} = 1; + next; + } + foreach my $part (@parts) { + if (exists($anonsubms{$symb."\0".$part})) { + my $rev = $rev_anonsurv; + my $what="$stdname=$valanon"; + if (ref($bysubmission{$symb}{$rev}) eq 'ARRAY') { + unless (grep(/^\Q$what\E/,@{$bysubmission{$symb}{$rev}})) { + push(@{$bysubmission{$symb}{$rev}},$what); + } + } else { + push(@{$bysubmission{$symb}{$rev}},$what); + } + $allmaps{$enclosing_map} = 1; + unless ($checkedrev{'anonsurvey'}) { + my ($major,$minor) = split(/\./,$rev); + ($reqdmajor,$reqdminor) = + &Apache::lonrelrequtils::update_reqd_loncaparev($major,$minor, + $reqdmajor,$reqdminor); + $checkedrev{'anonsurvey'} = 1; + } + } + if (exists($randtrysubms{$symb."\0".$part})) { + my $rev = $rev_randtry; + my $what="$stdname=$valrandtry"; + if (ref($bysubmission{$symb}{$rev}) eq 'ARRAY') { + unless (grep(/^\Q$what\E/,@{$bysubmission{$symb}{$rev}})) { + push(@{$bysubmission{$symb}{$rev}},$what); + } + } else { + push(@{$bysubmission{$symb}{$rev}},$what); + } + $allmaps{$enclosing_map} = 1; + unless ($checkedrev{'randomizetry'}) { + my ($major,$minor) = split(/\./,$rev); + ($reqdmajor,$reqdminor) = + &Apache::lonrelrequtils::update_reqd_loncaparev($major,$minor, + $reqdmajor,$reqdminor); + $checkedrev{'randomizetry'} = 1; + } + } + } + my %responses = $res->responseTypes(); + foreach my $key (keys(%responses)) { + if (exists($Apache::lonrelrequtils::checkresponsetypes{$key})) { + my $rev = $Apache::lonrelrequtils::checkresponsetypes{$key}; + unless ($checkedrev{$key}) { + my ($major,$minor) = split(/\./,$rev); + ($reqdmajor,$reqdminor) = + &Apache::lonrelrequtils::update_reqd_loncaparev($major,$minor, + $reqdmajor,$reqdminor); + $checkedrev{$key} = 1; + } + push(@{$byresponsetype{$symb}{$rev}},$key); + $allmaps{$enclosing_map} = 1; + } + } + } + if (keys(%byresource) > 0) { + foreach my $symb (keys(%byresource)) { + my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb); + $allmaps{$map} = 1; + } + } + if (keys(%bymap) > 0) { + foreach my $map (keys(%bymap)) { + $allmaps{$map} = 1; + } + } + foreach my $map (keys(%allmaps)) { + my $mapres = $navmap->getResourceByUrl($map); + if (ref($mapres)) { + my $mapsymb = $mapres->symb(); + $toshow{$mapsymb} = 1; + my $pcslist = $mapres->map_hierarchy(); + if ($pcslist ne '') { + foreach my $pc (split(/,/,$pcslist)) { + my $res = $navmap->getByMapPc($pc); + if (ref($res)) { + my $symb = $res->symb(); + unless ($symb eq '______') { + $toshow{$symb} = 1; + } + } + } + } + } + } + } + my $suppmap = 'supplemental.sequence'; + my ($suppcount,$supptools,$errors) = (0,0,0); + ($suppcount,$supptools,$errors) = &Apache::loncommon::recurse_supplemental($cnum,$cdom, + $suppmap,$suppcount,$supptools,$errors); + my $mapres_header = '

    '. + &mt('Requirements for specific folders or resources'). + '

    '; + if ((keys(%toshow) > 0) || (keys(%byresource) > 0) || + (keys(%bysubmission) > 0) || (keys(%byresponsetype))) { + $output .= $mapres_header; + } + my $top_header = '

    '.&mt('LON-CAPA version dependencies').'

    '; + my $title = 'Version Constraints'; + my $start_page = + &Apache::loncommon::start_page($title,undef,{'only_body' => 1,}); + $r->print($start_page.$top_header); + if ($output) { + $r->print($output); + if (defined($navmap) || $supptools) { + &show_contents_view($r,$navmap,$cid,\%toshow,\%bymap,\%byresource,\%bysubmission, + \%byresponsetype,\%bycrsrestype,\@scopeorder,\%lt,$supptools); + } + $r->print('

    '); + } else { + $r->print('

    '. + &mt('No version requirements from resource content or settings.'). + '

    '); + } + $r->print(&Apache::loncommon::end_page()); + my ($currmajor,$currminor) = split(/\./,$loncaparev); + if (($currmajor != $reqdmajor) || ($currminor != $reqdminor)) { + return 1; + } + return; +} + +sub show_contents_view { + my ($r,$navmap,$cid,$toshow,$bymap,$byresource,$bysubmission,$byresponsetype, + $bycrsrestype,$scopeorder,$lt,$supptools) = @_; + my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons"); + my $whitespace = ''; + my $showmaincontent; + if ((ref($navmap)) && + ((keys(%{$toshow}) > 0) || (keys(%{$byresource}) > 0) || + (keys(%{$bysubmission}) > 0) || (keys(%{$byresponsetype}) > 0) || + (keys(%{$bycrsrestype}) > 0))) { + $showmaincontent = 1; + } + if ($showmaincontent || $supptools) { + $r->print(&Apache::loncommon::start_data_table()); + } + if ($showmaincontent) { + my $icon = ''; + my $topmap = $env{'course.'.$cid.'.url'}; + $r->print(&Apache::loncommon::start_data_table_header_row(). + ''.&mt('Location').''.&mt('Release').''. + ''.&mt('Attribute/Setting').''. + &Apache::loncommon::end_data_table_header_row(). + &Apache::loncommon::start_data_table_row(). + ' '.$icon.' '.&mt('Main Content').''); + &releases_by_map($r,$bymap,$topmap,$scopeorder,$lt); + $r->print(&Apache::loncommon::end_data_table_row()); + my $it=$navmap->getIterator(undef,undef,undef,1,1,undef); + my $curRes; + my $depth = 0; + my %parent = (); + my $startcount = 5; + my $lastcontainer = $startcount; + while ($curRes = $it->next()) { + if ($curRes == $it->BEGIN_MAP()) { + $depth++; + $parent{$depth}= $lastcontainer; + } + if ($curRes == $it->END_MAP()) { + $depth--; + $lastcontainer = $parent{$depth}; + } + my $indent; + for (my $i=0; $i<$depth; $i++) { + $indent.= $whitespace; + } + if (ref($curRes)) { + my $symb = $curRes->symb(); + my $ressymb = $symb; + my $srcf = $curRes->src(); + my $title = &Apache::lonnet::gettitle($srcf); + if (($curRes->is_sequence()) || ($curRes->is_page())) { + next unless($toshow->{$symb}); + my ($parent,$ind,$url) = &Apache::lonnet::decode_symb($symb); + $icon = ''; + if ($curRes->is_page()) { + $icon = ''; + } + my $rowspan = 1; + if (ref($bymap->{$url}) eq 'HASH') { + $rowspan = scalar(keys(%{$bymap->{$url}})); + } + $r->print(&Apache::loncommon::start_data_table_row(). + ''.$indent.$icon.' '.$title.''); + &releases_by_map($r,$bymap,$url,$scopeorder,$lt); + $r->print(&Apache::loncommon::end_data_table_row()); + } else { + my $rowspan; + if (ref($byresource->{$symb}) eq 'HASH') { + $rowspan += scalar(keys(%{$byresource->{$symb}})); + } + if (ref($bysubmission->{$symb}) eq 'HASH') { + $rowspan += scalar(keys(%{$bysubmission->{$symb}})); + } + if (ref($byresponsetype->{$symb}) eq 'HASH') { + $rowspan += scalar(keys(%{$byresponsetype->{$symb}})); + } + if (ref($bycrsrestype->{$symb}) eq 'HASH') { + $rowspan += scalar(keys(%{$bycrsrestype->{$symb}})); + } + next if (!$rowspan); + $icon = ''.
+                            &mt('Problem').''; + $r->print(&Apache::loncommon::start_data_table_row(). + ''.$indent.$icon.' '.$title.''); + my $newrow; + if (ref($byresource->{$symb}) eq 'HASH') { + foreach my $rev (sort(keys(%{$byresource->{$symb}}))) { + if ($newrow) { + $r->print(&Apache::loncommon::continue_data_table_row()); + } + $r->print(''.$rev.''); + if (ref($byresource->{$symb}{$rev}) eq 'HASH') { + $r->print(''); + foreach my $scope (@{$scopeorder}) { + if (ref($byresource->{$symb}{$rev}{$scope}) eq 'HASH') { + $r->print(''); + foreach my $which (sort(keys(%{$byresource->{$symb}{$rev}{$scope}}))) { + $r->print(''); + } + $r->print(''); + } elsif (ref($byresource->{$symb}{$rev}{$scope}) eq 'ARRAY') { + $r->print(''. + ''); + } + } + $r->print('
    '.&mt('Setting - scope: [_1]',$lt->{$scope}).''.$which.''. + join('
    ',@{$byresource->{$symb}{$rev}{$scope}{$which}}). + '
    '.&mt('Setting - scope: [_1]',$lt->{$scope}).' '. + join('
    ',@{$byresource->{$symb}{$rev}{$scope}}).'
    '); + } + $r->print(''); + $r->print(&Apache::loncommon::end_data_table_row()); + $newrow = 1; + } + } + if (ref($bysubmission->{$symb}) eq 'HASH') { + foreach my $rev (sort(keys(%{$bysubmission->{$symb}}))) { + if ($newrow) { + $r->print(&Apache::loncommon::continue_data_table_row()); + } + $r->print(''.$rev.''); + if (ref($bysubmission->{$symb}{$rev}) eq 'ARRAY') { + $r->print(&mt('Submissions to: ').' '. + join(', ',@{$bysubmission->{$symb}{$rev}})); + } + $r->print(''); + $r->print(&Apache::loncommon::end_data_table_row()); + $newrow = 1; + } + } + if (ref($byresponsetype->{$symb}) eq 'HASH') { + foreach my $rev (sort(keys(%{$byresponsetype->{$symb}}))) { + if ($newrow) { + $r->print(&Apache::loncommon::continue_data_table_row()); + } + $r->print(''.$rev.''); + if (ref($byresponsetype->{$symb}{$rev}) eq 'ARRAY') { + $r->print(&mt('Response Type(s): ').' '. + join('
    ',@{$byresponsetype->{$symb}{$rev}})); + } + $r->print(''); + } + $r->print(&Apache::loncommon::end_data_table_row()); + $newrow = 1; + } + if (ref($bycrsrestype->{$symb}) eq 'HASH') { + foreach my $rev (sort(keys(%{$bycrsrestype->{$symb}}))) { + if ($newrow) { + $r->print(&Apache::loncommon::continue_data_table_row()); + } + $r->print(''.$rev.''.$bycrsrestype->{$symb}{$rev}.''); + } + $r->print(&Apache::loncommon::end_data_table_row()); + } + } + } + } + } + if ($supptools) { + my $icon = ''; + $r->print(&Apache::loncommon::start_data_table_header_row(). + ''.&mt('Location').''.&mt('Release').''. + ''.&mt('Resource Type').''. + &Apache::loncommon::end_data_table_header_row(). + &Apache::loncommon::start_data_table_row(). + ' '.$icon.' '.&mt('Supplemental Content').''. + ''. + $Apache::lonrelrequtils::exttool{'major'}.'.'. + $Apache::lonrelrequtils::exttool{'minor'}. + ''.&mt('[quant,_1,External Tool,External Tools]', + $supptools).''. + &Apache::loncommon::end_data_table_row()); + } + if ($showmaincontent || $supptools) { + $r->print(&Apache::loncommon::end_data_table()); + } + return; +} + +sub releases_by_map { + my ($r,$bymap,$url,$scopeorder,$lt) = @_; + return unless ((ref($bymap) eq 'HASH') && (ref($scopeorder) eq 'ARRAY')); + my $newrow = 0; + if (ref($bymap->{$url}) eq 'HASH') { + foreach my $rev (sort(keys(%{$bymap->{$url}}))) { + if ($newrow) { + $r->print(&Apache::loncommon::end_data_table_row(). + &Apache::loncommon::continue_data_table_row()); + } + $r->print(''.$rev.''); + if (ref($bymap->{$url}{$rev}) eq 'HASH') { + $r->print(''); + foreach my $scope (@{$scopeorder}) { + if (ref($bymap->{$url}{$rev}{$scope}) eq 'HASH') { + $r->print(''); + foreach my $which (sort(keys(%{$bymap->{$url}{$rev}{$scope}}))) { + $r->print(''); + } + $r->print(''); + } elsif (ref($bymap->{$url}{$rev}{$scope}) eq 'ARRAY') { + $r->print(''); + } + } + $r->print('
    '.&mt('Setting - scope: [_1]',$lt->{$scope}).''.$which.''. + join('
    ',@{$bymap->{$url}{$rev}{$scope}{$which}}). + '
    '.&mt('Setting - scope: [_1]',$lt->{$scope}). + ' '. + join('
    ',@{$bymap->{$url}{$rev}{$scope}}).'
    '); + } + $r->print(''); + $newrow = 1; + } + } else { + $r->print(' '); + } + return; +} + +sub get_param_description { + my ($stdtype,$value,$regexp) = @_; + my ($name,$parammatches,$paramstrings,@possibles); + if ($stdtype =~ /^string/) { + $paramstrings = &Apache::lonparmset::standard_string_options($stdtype); + } elsif ($stdtype eq 'date_interval') { + $paramstrings = &Apache::lonparmset::standard_interval_options($stdtype); + } + if ($regexp) { + if ($stdtype =~ /^string/) { + $parammatches = &Apache::lonparmset::standard_string_matches($stdtype); + } elsif ($stdtype eq 'date_interval') { + $parammatches = &Apache::lonparmset::standard_interval_matches($stdtype); + } + if (ref($parammatches) eq 'ARRAY') { + @possibles = @{$parammatches}; + } else { + undef($regexp); + $name = $value; + } + } + unless ($regexp) { + $name = $value; + if (ref($paramstrings) eq 'ARRAY') { + @possibles = @{$paramstrings}; + } else { + if (($stdtype eq 'date_start') || ($stdtype eq 'date_end')) { + if ($name == 0) { + $name = &mt('Never'); + } elsif (($name ne '') && ($name !~ /\D/)) { + $name = &Apache::lonlocal::locallocaltime($name); + } + } + return $name; + } + } + foreach my $possibilities (@possibles) { + next unless (ref($possibilities) eq 'ARRAY'); + my $gotregexmatch = ''; + if ($regexp) { + last if ($gotregexmatch); + my ($item,$pattern) = @{ $possibilities }; + if ($value =~ /$pattern/) { + if (ref($paramstrings) eq 'ARRAY') { + foreach my $possibles (@{$paramstrings}) { + next unless (ref($possibles) eq 'ARRAY'); + my ($thing,$description) = @{$possibles}; + if ($thing eq $item) { + $name = $description; + $gotregexmatch = 1; + last; + } + } + last if ($gotregexmatch); + } + } + } else { + my ($thing,$description) = @{ $possibilities }; + if ($thing eq $value) { + $name = $description; + last; + } + } + } + return $name; +} + +sub update_releasereq { + my $readmap = 1; + my $getrelreq = 1; + if (ref($modified_courses) eq 'ARRAY') { + foreach my $item (@{$modified_courses}) { + if (ref($item) eq 'ARRAY') { + my ($cdom,$cnum,$chome,$crstype) = @{$item}; + &Apache::lonrelrequtils::modify_course_relreq(undef,undef,$cnum,$cdom, + $chome,$crstype,$cdom.'_'.$cnum, + $readmap,$getrelreq); + } + } + $modified_courses = []; + } + undef($registered_cleanup); + return; +} + sub show_autocoowners { my (@currcoown) = @_; - my $output = ''.&mt('Co-ownership is set automatically when a Course Coordinator role is assigned to official course personnel (from institutional data).').''; + my $output = ''.&mt('Co-ownership is set automatically when a Course Coordinator role[_1] is assigned to official course personnel (from institutional data).','
    ').'
    '; if (@currcoown > 0) { $output .= '
    '.&mt('Current co-owners are:').' '. join(', ',map { &Apache::loncommon::plainname(split(':',$_)); } (@currcoown)); @@ -2038,13 +4155,20 @@ sub show_autocoowners { } sub coowner_invitations { - my ($cnum,$cdom,@currcoown) = @_; - my ($output,@pendingcoown,@othercoords); + my ($cnum,$cdom,$currcoownref,$noedit) = @_; + my ($output,@pendingcoown,@othercoords,@currcoown); my $pendingcoowners = $env{'course.'.$env{'request.course.id'}.'.internal.pendingco-owners'}; if ($pendingcoowners) { @pendingcoown = split(',',$pendingcoowners); } + if (ref($currcoownref) eq 'ARRAY') { + @currcoown = @{$currcoownref}; + } + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } my $ccrole = 'cc'; my %ccroles = &Apache::lonnet::get_my_roles($cnum,$cdom,undef,undef,[$ccrole]); foreach my $key (sort(keys(%ccroles))) { @@ -2062,7 +4186,7 @@ sub coowner_invitations { ''.&mt('Current co-owners').''; foreach my $person (@currcoown) { my ($co_uname,$co_dom) = split(':',$person); - $output .= ''.(' 'x2).' '; + $output .= ''.(' 'x2).' '; } $output .= ''. &Apache::loncommon::end_data_table_row(); @@ -2072,7 +4196,7 @@ sub coowner_invitations { ''.&mt('Invited as co-owners [_1](agreement pending)','
    ').'
    '; foreach my $person (@pendingcoown) { my ($co_uname,$co_dom) = split(':',$person); - $output .= ''.(' 'x2).' '; + $output .= ''.(' 'x2).' '; } $output .= ''. &Apache::loncommon::end_data_table_row(); @@ -2082,7 +4206,7 @@ sub coowner_invitations { ''.&mt('Invite other Coordinators [_1]to become co-owners','
    ').'
    '; foreach my $person (@othercoords) { my ($co_uname,$co_dom) = split(':',$person); - $output .= ''.(' 'x2).' '; + $output .= ''.(' 'x2).' '; } $output .= ''. &Apache::loncommon::end_data_table_row(); @@ -2095,13 +4219,20 @@ sub coowner_invitations { } sub manage_coownership { - my ($cnum,$cdom,@currcoown) = @_; - my (@pendingcoown); + my ($cnum,$cdom,$currcoownref,$noedit) = @_; + my (@pendingcoown,@currcoown); my $pendingcoowners = $env{'course.'.$env{'request.course.id'}.'.internal.pendingco-owners'}; if ($pendingcoowners) { @pendingcoown = split(',',$pendingcoowners); } + if (ref($currcoownref) eq 'ARRAY') { + @currcoown = @{$currcoownref}; + } + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } my ($is_coowner,$is_pending,$output); my $uname = $env{'user.name'}; my $udom = $env{'user.domain'}; @@ -2131,10 +4262,10 @@ sub manage_coownership { } $output .= ''; if ($is_coowner) { - $output .= &mt('You are currently a co-owner:').' '; + $output .= &mt('You are currently a co-owner:').' '; } else { - $output .= &mt('The course owner has invited you to become a co-owner:').' '.(' 'x2). - ''; + $output .= &mt('The course owner has invited you to become a co-owner:').' '.(' 'x2). + ''; } $output .= ''; if (@currcoown) { @@ -2148,7 +4279,7 @@ sub manage_coownership { } sub print_localization { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2167,17 +4298,24 @@ sub print_localization { input => 'selectbox', }, ); - my $datatable; + my ($datatable,$disabled); my $count = 0; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } foreach my $item (@{$ordered}) { $count ++; - $datatable .= &item_table_row_start($items{$item}{text},$count); + my $colspan; + unless ($item eq 'languages') { + $colspan = 2; + } + $datatable .= &item_table_row_start($items{$item}{text},$count,undef,$colspan); if ($item eq 'timezone') { my $includeempty = 1; my $timezone = &Apache::lonlocal::gettimezone(); $datatable .= &Apache::loncommon::select_timezone($item,$timezone,undef, - $includeempty); + $includeempty,$disabled); } elsif ($item eq 'datelocale') { my $includeempty = 1; my $locale_obj = &Apache::lonlocal::getdatelocale(); @@ -2187,15 +4325,18 @@ sub print_localization { } $datatable .= &Apache::loncommon::select_datelocale($item,$currdatelocale, - undef,$includeempty); + undef,$includeempty,$disabled); } else { if ($settings->{$item} eq '') { - $datatable .= - &Apache::loncommon::select_language('languages_0','',1); + unless ($noedit) { + $datatable .= ''. + &Apache::loncommon::select_language('languages_0','',1); + } } else { my $num = 0; my @languages = split(/\s*[,;:]\s*/,$settings->{$item}); - $datatable .= &Apache::loncommon::start_data_table(); + $datatable .= '
    '. + &Apache::loncommon::start_data_table(); if (@languages > 0) { my %langchoices = &get_lang_choices(); foreach my $lang (@languages) { @@ -2210,19 +4351,22 @@ sub print_localization { '

    '. ''. &Apache::loncommon::end_data_table_row(); $num ++; } } - $datatable .= &Apache::loncommon::start_data_table_row(). + unless ($noedit) { + $datatable .= + &Apache::loncommon::start_data_table_row(). ''. &mt('Additional language:'). '
    '. &Apache::loncommon::select_language('languages_'.$num,'',1). ''. - ''.&Apache::loncommon::end_data_table_row(). - &Apache::loncommon::end_data_table(); + ''.&Apache::loncommon::end_data_table_row(); + } + $datatable .= &Apache::loncommon::end_data_table().'
    '; } } $datatable .= &item_table_row_end(); @@ -2243,7 +4387,7 @@ sub get_lang_choices { } sub print_feedback { - my ($position,$cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + my ($position,$cdom,$settings,$ordered,$itemtext,$rowtotal,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2278,21 +4422,25 @@ sub print_feedback { del => 'Delete?', sec => 'Sections:', ); - + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } foreach my $item (@{$ordered}) { $count ++; if ($position eq 'top') { $datatable .= &item_table_row_start($items{$item}{text},$count); } else { - $datatable .= &item_table_row_start($items{$item}{text}."
    (Custom text)",$count, "advanced"); + $datatable .= &item_table_row_start($items{$item}{text}."
    (Custom text)",$count, "advanced",2); } if ($position eq 'top') { my $includeempty = 0; - $datatable .= &user_table($cdom,$item,\@sections, - $settings->{$item},\%lt); + $datatable .= ''. + &user_table($cdom,$item,\@sections, + $settings->{$item},\%lt,$noedit); } else { $datatable .= &Apache::lonhtmlcommon::textbox($item.'.text', - $settings->{$item.'.text'},$items{$item}{size}); + $settings->{$item.'.text'},$items{$item}{size},$disabled); } $datatable .= &item_table_row_end(); } @@ -2301,15 +4449,21 @@ sub print_feedback { } sub user_table { - my ($cdom,$item,$sections,$currvalue,$lt) = @_; + my ($cdom,$item,$sections,$currvalue,$lt,$noedit) = @_; my $output; if ($currvalue eq '') { - $output .= &select_recipient($item,'0',$cdom,$sections); + unless ($noedit) { + $output .= &select_recipient($item,'0',$cdom,$sections); + } } else { my $num = 0; my @curr = split(/,/,$currvalue); - $output .= ''; + $output .= '
    '; my ($currusers); + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } foreach my $val (@curr) { next if ($val eq ''); my ($uname,$udom,$seclist) = ($val =~ /^($match_username):($match_domain)(\(?[^\)]*\)?)$/); @@ -2321,7 +4475,7 @@ sub user_table { $currusers .= ''. ''; @@ -2350,12 +4504,16 @@ sub user_table { '
    '. ''. ''.(' 'x2). @@ -2332,7 +4486,7 @@ sub user_table { if (@{$sections}) { $currusers.= (' 'x3).$lt->{'sec'}.' '. &select_sections($item,$num,$sections, - \@selsec); + \@selsec,$noedit); } } $currusers .= '
    '.$currusers.'
    '. ''; } - $output .= ''. + unless ($noedit) { + $output .= + ''. ''. $lt->{'add'}.'
    '. &select_recipient($item,$num,$cdom,$sections). ''. - ''; + ''; + } + $output .= ''; } return $output; } @@ -2365,7 +4523,7 @@ sub select_recipient { my $domform = &Apache::loncommon::select_dom_form($cdom,$item.'_udom_'.$num,$includeempty); my $selectlink = &Apache::loncommon::selectstudent_link('display',$item.'_uname_'.$num, - $item.'_udom_'.$num,1); + $item.'_udom_'.$num,'only'); my $output = ''. @@ -2382,14 +4540,17 @@ sub select_recipient { } sub select_sections { - my ($item,$num,$sections,$selected) = @_; - my ($output,@currsecs,$allsec); + my ($item,$num,$sections,$selected,$noedit) = @_; + my ($output,@currsecs,$allsec,$disabled); if (ref($selected) eq 'ARRAY') { @currsecs = @{$selected}; } if (!@currsecs) { $allsec = ' selected="selected"'; } + if ($noedit) { + $disabled = ' disabled="disabled"'; + } if (ref($sections) eq 'ARRAY') { if (@{$sections}) { my $mult; @@ -2399,7 +4560,7 @@ sub select_sections { $mult .= ' size="4"'; } } - $output = ''. ' '; foreach my $sec (@{$sections}) { my $is_sel; @@ -2415,7 +4576,7 @@ sub select_sections { } sub print_discussion { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2445,6 +4606,20 @@ sub print_discussion { &mt('(specific user(s))'), input => 'checkbox', }, + + 'pac.roles.denied' => { + text => ''.&mt($itemtext->{'pac.roles.denied'}).''. + &Apache::loncommon::help_open_topic("Course_Disable_Anonymous_Discussion").'
    '. + &mt('(role-based)'), + input => 'checkbox', + }, + + 'pac.users.denied' => { + text => ''.&mt($itemtext->{'pac.users.denied'}).'
    '. + &mt('(specific user(s))'), + input => 'checkbox', + }, + 'allow_limited_html_in_feedback' => { text => ''.&mt($itemtext->{'allow_limited_html_in_feedback'}).'', input => 'radio', @@ -2454,6 +4629,11 @@ sub print_discussion { text => ''.&mt($itemtext->{'allow_discussion_post_editing'}).'', input => 'checkbox', }, + 'discussion_post_fonts' => { + text => ''.&mt($itemtext->{'discussion_post_fonts'}).'', + input => 'textbox', + size => '20', + }, ); my $datatable; my $count; @@ -2467,32 +4647,60 @@ sub print_discussion { del => 'Delete?', sec => 'Sections:', ); - foreach my $item (@{$ordered}) { $count ++; - $datatable .= &item_table_row_start($items{$item}{text},$count); + my $colspan; + if ($item eq 'allow_limited_html_in_feedback') { + $colspan = 2; + } + $datatable .= &item_table_row_start($items{$item}{text},$count,undef,$colspan); if ($item eq 'plc.roles.denied') { - $datatable .= '
    '.&mt('Username').'
    '. '
    '.&role_checkboxes($cdom,$cnum,$item,$settings). + $datatable .= ''; + $itemcount ++; + } + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''."\n". + ''; + $$rowtotal ++; + return $datatable; +} + +sub linkprot_names { + return &Apache::lonlocal::texthash( + 'version' => 'LTI Version', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'name' => 'Launcher Application', + 'secret' => 'Secret', + 'requser' => 'Use identity', + 'email' => 'Email address', + 'sourcedid' => 'User ID', + 'other' => 'Other', + 'auth' => 'Display LON-CAPA login page', + 'reject' => 'Discontinue launch process', + ); +} + +sub check_switchserver { + my ($cdom,$cnum,$context,$dest) = @_; + my ($allowed,$switchserver,$home); + if ($context eq 'domain') { + $home = &Apache::lonnet::domain($cdom,'primary'); + } else { + $home = &Apache::lonnet::homeserver($cnum,$cdom); + } + unless (($home eq 'no_host') || ($home eq '')) { + my @ids=&Apache::lonnet::current_machine_ids(); + foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } } + if (!$allowed) { + $switchserver=''.&mt('Switch Server').''; + } + } + return $switchserver; +} + +sub linkprot_options { + my ($num,$itemcount,$disabled,$current,$desc) = @_; + my %lt; + if (ref($desc) eq 'HASH') { + %lt = %{$desc}; + } + my $userfieldsty = 'none'; + my (%checked,$userfield); + $checked{'sourcedid'} = ' checked="checked"'; + $checked{'reject'} = ' checked="checked"'; + if (ref($current) eq 'HASH') { + if (($current->{'mapuser'} ne '') && ($current->{'mapuser'} ne 'lis_person_sourcedid')) { + $checked{'sourcedid'} = ''; + if ($current->{'mapuser'} eq 'lis_person_contact_email_primary') { + $checked{'email'} = ' checked="checked"'; + } else { + $checked{'other'} = ' checked="checked"'; + $userfield = $current->{'mapuser'}; + $userfieldsty = 'inline-block'; + } + } + if (($current->{'notstudent'} ne '') && ($current->{'notstudent'} ne 'reject')) { + $checked{'reject'} = ''; + $checked{'auth'} = ' checked="checked"'; + } + } + my $onclickuser = ' onclick="toggleLinkProtReqUser(this.form,'."'mapuser','userfield','other','inline-block','$num'".');"'; + my $output = '
    '. + &mt('Source of LON-CAPA username in LTI request').': '; + foreach my $option ('sourcedid','email','other') { + $output .= ''. + ($option eq 'other' ? '' : (' 'x2) ); + } + $output .= '
    '. + '
    '. + '
    '; + $output .= '
    '. + '
    '. + &mt('Action when username is not for an enrolled student').': '; + foreach my $option ('reject','auth') { + $output .= ''. + ($option eq 'auth' ? '' : (' 'x2) ); + } + $output .= '
    '; + return $output; } sub print_other { - my ($cdom,$settings,$allitems,$rowtotal,$crstype) = @_; + my ($cdom,$settings,$allitems,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($allitems) eq 'ARRAY')) { return; } @@ -2936,7 +5945,7 @@ sub print_other { input => 'textbox', size => '30', }; - my $output = &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype,'other',$noedit); } sub get_other_items { @@ -2953,7 +5962,10 @@ sub get_other_items { unless (($parameter =~ m/^internal\./)||($parameter =~ m/^metadata\./) || ($parameter =~ m/^selfenroll_/) || ($parameter =~ /_selfenroll$/) || ($parameter eq 'type') || - ($parameter =~ m/^(cc|co|in|ta|ep|ad|st)\.plaintext$/)) { + ($parameter =~ m/^(cc|co|in|ta|ep|ad|st)\.plaintext$/) + || ($parameter =~ m/^discussion_post_fonts/) || + ($parameter =~ m{^(minimal|updated|uploaded|external)syllabus$}) || + ($parameter eq 'clonedfrom')) { push(@ordered,$parameter); } } @@ -2962,13 +5974,24 @@ sub get_other_items { } sub item_table_row_start { - my ($text,$count,$add_class) = @_; + my ($text,$count,$add_class,$colspan,$leftclass,$rightclass) = @_; my $output; - my $css_class = ($count % 2) ? 'LC_odd_row' : 'LC_even_row'; - $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq ''); - $output .= ''."\n";; - $output .= ''."\n". + ''; + if ($colspan > 1) { + $output .= ''. + &Apache::loncommon::end_data_table_row(); + $num ++; + unless (grep(/^\Q$currdom\E$/,@excdoms)) { + push(@excdoms,$currdom); + } + } + } + if ((scalar(keys(%domains)) - scalar(@excdoms)) > 0) { + $datatable .= + &Apache::loncommon::start_data_table_row(). + ''.&Apache::loncommon::end_data_table_row(); + } + $datatable .= &Apache::loncommon::end_data_table(). + ''. + '
    '; + } + return $datatable; +} + sub print_hdrfmt_row { - my ($item,$settings) = @_; + my ($item,$settings,$noedit) = @_; my @curr; my $currnum = 0; my $maxnum = 2; @@ -3226,37 +6377,45 @@ function getIndexByName(item) { ENDJS - $output .= $currstr.'
    '. + ''.&role_checkboxes($cdom,$cnum,$item,$settings,undef,undef,$noedit). '
    '; } elsif ($item eq 'plc.users.denied') { - $datatable .= &user_table($cdom,$item,undef, - $settings->{$item},\%lt); + $datatable .= '
    '. + &user_table($cdom,$item,undef, + $settings->{$item},\%lt,$noedit); } elsif ($item eq 'pch.roles.denied') { - $datatable .= ''.&role_checkboxes($cdom,$cnum,$item,$settings). + $datatable .= ''. &Apache::loncommon::end_data_table_row(). - &role_checkboxes($cdom,$cnum,$item,$settings,1). - &Apache::loncommon::end_data_table(); + &role_checkboxes($cdom,$cnum,$item,$settings,1,undef,$noedit). + &Apache::loncommon::end_data_table().'
    '; + } elsif ($item eq 'discussion_post_fonts') { + $datatable .= ''. + ''. + ''. + ''. + ''. + &Apache::loncommon::end_data_table_row(). + &set_discussion_fonts($cdom,$cnum,$item,$settings,$noedit). + &Apache::loncommon::end_data_table().'
    '; } $datatable .= &item_table_row_end(); } @@ -2501,7 +4709,7 @@ sub print_discussion { } sub role_checkboxes { - my ($cdom,$cnum,$item,$settings,$showsections,$crstype) = @_; + my ($cdom,$cnum,$item,$settings,$showsections,$crstype,$noedit) = @_; my @roles = ('st','ad','ta','ep','in'); if ($crstype eq 'Community') { push(@roles,'co'); @@ -2539,6 +4747,10 @@ sub role_checkboxes { } my $numinrow = 3; my $count = 0; + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } foreach my $role (@roles) { my $checked = ''; if (grep(/^\Q$role\E$/,@current)) { @@ -2557,11 +4769,11 @@ sub role_checkboxes { } } $output .= ''; if ($showsections) { $output .= ''; } $count ++; @@ -2589,11 +4801,11 @@ sub role_checkboxes { } } $output .= ''; if ($showsections) { $output .= ''.&Apache::loncommon::end_data_table_row(); } $total ++; @@ -2614,14 +4826,90 @@ sub role_checkboxes { return $output; } +sub set_discussion_fonts { + my ($cdom,$cnum,$item,$settings,$noedit) = @_; + my ($classorder,$classtitles) = &discussion_vote_classes(); + my ($styleorder,$styleitems) = &discussion_vote_styles(); + my $output; + if (ref($classorder) eq 'ARRAY') { + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + foreach my $cat (@{$classorder}) { + my %curr = (); + ($curr{'size'},$curr{'weight'},$curr{'style'},$curr{'other'}) = + split(/,/,$settings->{'discussion_post_fonts_'.$cat}); + $output .= &Apache::loncommon::start_data_table_row(). + ''; + if (ref($styleorder) eq 'ARRAY') { + foreach my $item (@{$styleorder}) { + $output .= ''; + } + $output .= &Apache::loncommon::end_data_table_row(); + } + return $output; +} + +sub discussion_vote_classes { + my $classorder = ['twoplus','oneplus','zero','oneminus','twominus']; + my %classtitles = &Apache::lonlocal::texthash( + 'twoplus' => 'Two sigma above mean', + 'oneplus' => 'One sigma above mean', + 'zero' => 'Within one sigma of mean', + 'oneminus' => 'One sigma below mean', + 'twominus' => 'Two sigma below mean', + ); + return ($classorder,\%classtitles); +} + +sub discussion_vote_styles { + my $styleorder = ['size','weight','style']; + my $styleitems = { + size => ['','px','%','xx-small','x-small','small','medium','large', + 'x-large','xx-large','smaller','larger'], + weight => ['','normal','bold','bolder','lighter','100','200','300','400', + '500','600','700','800','900'], + style => ['','normal','italic','oblique'], + }; + return ($styleorder,$styleitems); +} + sub print_classlists { - my ($position,$cdom,$settings,$itemtext,$rowtotal,$crstype) = @_; + my ($position,$cdom,$settings,$itemtext,$rowtotal,$crstype,$noedit) = @_; my @ordered; if ($position eq 'top') { @ordered = ('default_enrollment_start_date', 'default_enrollment_end_date'); + if ($crstype eq 'Course') { + push(@ordered,'defaultcredits'); + } } elsif ($position eq 'middle') { - @ordered = ('nothideprivileged'); + @ordered = ('nothideprivileged','checkforpriv'); } else { @ordered = ('student_classlist_view', 'student_classlist_opt_in', @@ -2652,12 +4940,20 @@ sub print_classlists { text => ''.&mt($itemtext->{'default_enrollment_end_date'}).'', input => 'dates', }, - + 'defaultcredits' => { + text => ''.&mt($itemtext->{'defaultcredits'}).'', + }, + 'nothideprivileged' => { text => ''.&mt($itemtext->{'nothideprivileged'}).'', input => 'checkbox', }, + 'checkforpriv' => { + text => ''.&mt($itemtext->{'checkforpriv'}).'', + input => 'selectbox', + }, + 'student_classlist_view' => { text => ''.&mt($itemtext->{'student_classlist_view'}).'', input => 'selectbox', @@ -2678,11 +4974,11 @@ sub print_classlists { ($settings->{'student_classlist_view'} eq 'section')) { $settings->{'student_classlist_view'} = 'disabled'; } - return &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,\@ordered,$settings,$rowtotal,$crstype,'classlists',$noedit); } sub print_appearance { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2714,11 +5010,11 @@ sub print_appearance { text => ''.&mt($itemtext->{'texengine'}).'', input => 'selectbox', options => { - jsMath => 'jsMath', + MathJax => 'MathJax', mimetex => &mt('Convert to Images'), tth => &mt('TeX to HTML'), }, - order => ['jsMath','mimetex','tth'], + order => ['MathJax','mimetex','tth'], nullval => $mathdef, }, 'tthoptions' => { @@ -2726,12 +5022,24 @@ sub print_appearance { input => 'textbox', size => 40, }, + 'uselcmath' => { + text => ''.&mt($itemtext->{'uselcmath'}).'', + input => 'radio', + }, + 'usejsme' => { + text => ''.&mt($itemtext->{'usejsme'}).'', + input => 'radio', + }, + 'inline_chem' => { + text => ''.&mt($itemtext->{'inline_chem'}).'', + input => 'radio', + }, ); - return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'appearance',$noedit); } sub print_grading { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2742,11 +5050,12 @@ sub print_grading { input => 'selectbox', options => { standard => &mt('Standard: shows points'), + categories => &mt('Categories: shows points according to categories'), external => &mt('External: shows number of completed parts and totals'), externalnototals => &mt('External: shows only number of completed parts'), spreadsheet => &mt('Spreadsheet: (with link to detailed scores)'), }, - order => ['standard','external','externalnototals','spreadsheet'], + order => ['standard','categories','external','externalnototals','spreadsheet'], }, 'rndseed' => { text => ''.&mt($itemtext->{'rndseed'}).''. @@ -2763,6 +5072,7 @@ sub print_grading { '64bit5' => '64bit5', }, order => ['32bit','64bit','64bit2','64bit3','64bit4','64bit5'], + advanced => 1 }, 'receiptalg' => { text => ''.&mt($itemtext->{'receiptalg'}).'
    '. @@ -2774,17 +5084,18 @@ sub print_grading { receipt3 => 'receipt3', }, order => ['receipt','receipt2','receipt3'], + advanced => 1 }, 'disablesigfigs' => { text => ''.&mt($itemtext->{'disablesigfigs'}).'', input => 'radio', }, ); - return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'grading',$noedit); } sub print_printouts { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2812,7 +5123,7 @@ sub print_printouts { A6 => &mt('A6').' [105x148 mm]', }, order => ['Letter','Legal','Tabloid','Executive','A2','A3','A4','A5','A6'], - nullval => 'None specified', + nullval => &mt('None specified'), }, print_header_format => { text => ''.&mt($itemtext->{'print_header_format'}).'', @@ -2830,14 +5141,14 @@ sub print_printouts { 0 => &mt('No'), }, order => ['1','0'], - nullval => 'None specified - use domain default', + nullval => &mt('None specified - use domain default'), } ); - return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'printouts',$noedit); } sub print_spreadsheet { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2869,11 +5180,11 @@ sub print_spreadsheet { input => 'radio', }, ); - return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'spreadsheet',$noedit); } sub print_bridgetasks { - my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype) = @_; + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { return; } @@ -2911,11 +5222,709 @@ sub print_bridgetasks { input => 'radio', }, ); - return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'bridgetasks',$noedit); +} + +sub print_lti { + my ($cdom,$settings,$ordered,$itemtext,$rowtotal,$crstype,$noedit) = @_; + unless ((ref($settings) eq 'HASH') && (ref($ordered) eq 'ARRAY') && (ref($itemtext) eq 'HASH')) { + return; + } + my @menuitems = ('fullname','coursetitle','role','logout','grades'); + my %menutitles = <imenu_titles(); + my ($domdefs,$displaydefs); + if ($env{'request.lti.login'}) { + my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider'); + my @domdefsmenu; + if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') { + if ($lti{$env{'request.lti.login'}}{'topmenu'}) { + $domdefs = &mt('Header shown').', '; + } + if ($lti{$env{'request.lti.login'}}{'inlinemenu'}) { + $domdefs .= &mt('Inline menu shown'); + } + $domdefs =~ s/,\s$//; + if (!$domdefs) { + $domdefs = &mt('No header or inline menu shown'); + } + $domdefs = ' ('.&mt('Domain default').': '.$domdefs.')'; + if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') { + @domdefsmenu = @{$lti{$env{'request.lti.login'}}{'lcmenu'}}; + } + } + $displaydefs = &mt('Domain defaults'); + if (@domdefsmenu) { + my @diffs = &Apache::loncommon::compare_arrays(\@menuitems,\@domdefsmenu); + if (@diffs) { + my $missing; + foreach my $item (@menuitems) { + unless (grep(/^\Q$item\E$/,@domdefsmenu)) { + $missing .= ' '.$menutitles{$item}.','; + } + } + $missing =~ s/,$//; + $displaydefs .= ': '.&mt('Not shown').' --'.$missing; + } else { + $displaydefs .= ': '.&mt('All shown'); + } + } else { + $displaydefs .= ': '.&mt('Not shown').' -- '.join(', ', map { $menutitles{$_}; } @menuitems); + } + } else { + $displaydefs = &mt('Domain defaults only available in LTI context'); + } + my %items = ( + 'lti.override' => { + text => ''.&mt($itemtext->{'lti.override'}).''.$domdefs, + input => 'radio', + }, + 'lti.topmenu' => { + text => ''.&mt($itemtext->{'lti.topmenu'}).'', + input => 'radio', + }, + 'lti.inlinemenu' => { + text => ''.&mt($itemtext->{'lti.inlinemenu'}).'', + input => 'radio', + }, + 'lti.lcmenu' => { + text => ''.&mt($itemtext->{'lti.lcmenu'}).'
    '.$displaydefs, + input => 'custom', + }, + ); + return &make_item_rows($cdom,\%items,$ordered,$settings,$rowtotal,$crstype,'lti',$noedit); +} + +sub lcmenu_checkboxes { + my ($cdom,$caller,$settings,$crstype,$noedit) = @_; + my @menuitems = ('fullname','coursetitle','role','logout','grades'); + my %menutitles = <imenu_titles(); + my (@current,@domdefs); + if ($env{'request.lti.login'}) { + my %lti = &Apache::lonnet::get_domain_lti($cdom,'provider'); + if (ref($lti{$env{'request.lti.login'}}) eq 'HASH') { + if (ref($lti{$env{'request.lti.login'}}{'lcmenu'}) eq 'ARRAY') { + @domdefs = @{$lti{$env{'request.lti.login'}}{'lcmenu'}}; + } + } + } + if (ref($settings) eq 'HASH') { + if ($settings->{'lti.lcmenu'}) { + unless ($settings->{'lti.lcmenu'} eq 'none') { + @current = split(',',$settings->{'lti.lcmenu'}); + } + } else { + @current = @domdefs; + } + } else { + @current = @domdefs; + } + my $numinrow = 3; + my $count = 0; + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + my $output = '
    '. + ''.&role_checkboxes($cdom,$cnum,$item,$settings,undef,undef,$noedit). '
    '; } elsif ($item eq 'pch.users.denied') { - $datatable .= &user_table($cdom,$item,undef, - $settings->{$item},\%lt); + $datatable .= '
    '. + &user_table($cdom,$item,undef, + $settings->{$item},\%lt,$noedit); + } elsif ($item eq 'pac.roles.denied') { + $datatable .= ''. + ''.&role_checkboxes($cdom,$cnum,$item,$settings,undef,undef,$noedit). + '
    '; + } elsif ($item eq 'pac.users.denied') { + $datatable .= '
    '. + &user_table($cdom,$item,undef, + $settings->{$item},\%lt,$noedit); } elsif ($item eq 'allow_limited_html_in_feedback') { - $datatable .= &yesno_radio($item,$settings); + $datatable .= &yesno_radio($item,$settings,undef,undef,undef,$noedit); } elsif ($item eq 'allow_discussion_post_editing') { - $datatable .= &Apache::loncommon::start_data_table(). + $datatable .= '
    '. + &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_row(). '
    '.&mt('Role').''. &mt('Sections').'
    '. + &Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_row(). + '
    '.&mt('Sum of likes/dislikes').''.&mt('font-size').''.&mt('font-weight').''.&mt('font-style').''.&mt('Other css').''. - &select_sections($item,$role,\@sections,$currsec{$role}). + &select_sections($item,$role,\@sections,$currsec{$role},$noedit). '
    '. - &select_sections($item,$role,\@sections,$currsec{$role}). + &select_sections($item,$role,\@sections,$currsec{$role},$noedit). ''.$classtitles->{$cat}.''; + my $selitem = $item; + if ($item eq 'size') { + my ($currsize,$currunit); + if ($curr{'size'} =~ /^(\d+)(px|%)$/) { + ($currsize,$currunit) = ($1,$2); + $curr{'size'} = $currunit; + } + $output .= ''. + ' '; + $selitem = 'unit'; + } + if ((ref($styleitems) eq 'HASH') && (ref($styleitems->{$item}) eq 'ARRAY')) { + $output .= '
    '; + foreach my $item (@menuitems) { + my $checked = ''; + if (grep(/^\Q$item\E$/,@current)) { + $checked = ' checked="checked" '; + } + my $rem = $count%($numinrow); + if ($rem == 0) { + if ($count > 0) { + $output .= ''; + } + $output .= ''; + } + $output .= ''; + $count ++; + } + my $rem = $count%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $output .= ''; + } elsif ($colsleft == 1) { + $output .= ''; + } + $output .= '
    '. + '  
    '; +} + +sub ltimenu_titles { + return &Apache::lonlocal::texthash( + fullname => 'Full name', + coursetitle => 'Course title', + role => 'Role', + logout => 'Logout', + grades => 'Grades', + ); +} + +sub print_menuitems { + my ($position,$cdom,$settings,$itemtext,$rowtotal,$crstype,$noedit) = @_; + unless ((ref($settings) eq 'HASH') && (ref($itemtext) eq 'HASH')) { + return; + } + if ($position eq 'top') { + my (%defaultmenu_options,@defaultmenu_order,$addcollection); + if ($settings->{'menucollections'} ne '') { + foreach my $item (split(/;/,$settings->{'menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + $defaultmenu_options{$num} = $num; + } + } + @defaultmenu_order = sort { $a <=> $b } keys(%defaultmenu_options); + $addcollection = $defaultmenu_order[-1] + 1; + } else { + $addcollection = 1; + } + $defaultmenu_options{$addcollection} = $addcollection; + my %items = ( + 'menudefault' => { + text => ''.&mt($itemtext->{'menudefault'}).'
    '. + &mt("(can be overriden in deep-link context)"), + input => 'selectbox', + options => \%defaultmenu_options, + order => \@defaultmenu_order, + nullval => &mt('Standard (all menus shown)'), + }, + ); + return &make_item_rows($cdom,\%items,['menudefault'],$settings,$rowtotal,$crstype,'menuitems',$noedit); + } else { + my %menu; + my $count = 0; + my $next = 1; + my ($datatable,$disabled); + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + + my ($ordered,$cats) = &menuitems_categories(); + my @order = @{$ordered}; + my %categories = %{$cats}; + my %menutitles = &menuitems_titles(); + my %menufields = &menuitems_fields(); + + if ($settings->{'menucollections'} ne '') { + foreach my $item (split(/;/,$settings->{'menucollections'})) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + my @entries = split(/\&/,$value); + foreach my $entry (@entries) { + my ($name,$fields) = split(/=/,$entry); + $menu{$num}{$name} = $fields; + } + } + } + if (keys(%menu)) { + my @current = sort { $a <=> $b } keys(%menu); + $next += $current[-1]; + foreach my $num (@current) { + my %checked; + my $on = ' checked="checked"'; + foreach my $key (keys(%{$menu{$num}})) { + if (($key eq 'top') || ($key eq 'inline') || ($key eq 'foot') || ($key eq 'main')) { + if ($menu{$num}{$key} eq 'y') { + $checked{$key} = $on; + } + } else { + foreach my $field (split(/,/,$menu{$num}{$key})) { + if (exists($menufields{$field})) { + $checked{$field} = $on; + } + } + } + } + if (ref($menu{$num}) eq 'HASH') { + $datatable .= &item_table_row_start(''.$num.'',$count,'','','','LC_left_item'); + foreach my $category (@order) { + if ((ref($categories{$category}) eq 'ARRAY') && (@{$categories{$category}} > 0)) { + $datatable .= '
    '.$menutitles{$category}.''."\n"; + if ($category eq 'text') { + $datatable .= ''.&mt('Header').'
    '; + } + foreach my $field (@{$categories{$category}}) { + if ($field eq 'disc') { + $datatable .= '
    '.&mt('Footer').'
    '; + } + $datatable .= '
    '; + } + $datatable .= '
    '; + } + } + $datatable .= &item_table_row_end(); + $count ++; + } + } + } + } elsif ($noedit) { + my $text = &mt('No menu collections defined for this course.'); + $datatable .= &item_table_row_start($text,$count); + } + unless ($noedit) { + my $add = ''; + $datatable .= &item_table_row_start($add,$count,'','','','LC_left_item'); + foreach my $category (@order) { + if ((ref($categories{$category}) eq 'ARRAY') && (@{$categories{$category}} > 0)) { + $datatable .= ''; + } + } + $datatable .= &item_table_row_end(); + $count ++; + } + return $datatable; + } +} + +sub menuitems_abbreviations { + my %briefcats = ( + text => 'pt', + links => 'p', + list => 'ps', + inline => 's', + ); + return %briefcats; +} + +sub menuitems_categories { + my @order = ('shown','text','links','list','inline'); + my %categories = ( + shown => ['top','inline','foot','main'], + text => ['name','role','crs','disc','fdbk'], + links => ['pers','logo','menu','comm','roles','help','logout'], + list => ['about','prefs','port','wish','anno','rss'], + inline => ['cont','grades','chat','people','groups','resv','syll','feeds'], + ); + return (\@order,\%categories); +} + +sub menuitems_titles { + return &Apache::lonlocal::texthash ( + shown => 'Hierarchy', + text => 'Text', + links => 'Header links', + list => 'Drop-down list', + inline => 'Inline links', + ); +} + +sub menuitems_fields { + return &Apache::lonlocal::texthash ( + top => 'Display header', + inline => 'Display inline menu', + foot => 'Display footer', + main => 'Access to main menu', + pers => 'Personal', + logo => 'LON-CAPA', + menu => 'Home', + comm => 'Messages', + roles => 'Roles/Courses', + help => 'Help', + logout => 'Logout', + name => 'Fullname', + crs => 'Course Title', + role => 'Current Role', + disc => 'Discussion', + fdbk => 'Feedback', + about => 'Information', + prefs => 'Preferences', + port => 'Portfolio', + wish => 'Stored Links', + anno => 'Calendar', + rss => 'RSS Feeds', + cont => 'Contents', + grades => 'Grades', + chat => 'Chat', + people => 'People', + groups => 'Groups', + resv => 'Reservations', + syll => 'Syllabus', + feeds => 'Feeds', + ); +} + +sub menucollections_display { + my ($collections) = @_; + my %menu; + my ($ordered,$cats) = &menuitems_categories(); + my @order = @{$ordered}; + my %categories = %{$cats}; + my %menutitles = &menuitems_titles(); + my %menufields = &menuitems_fields(); + foreach my $item (split(/;/,$collections)) { + my ($num,$value) = split(/\%/,$item); + if ($num =~ /^\d+$/) { + my @entries = split(/\&/,$value); + foreach my $entry (@entries) { + my ($name,$fields) = split(/=/,$entry); + $menu{$num}{$name} = $fields; + } + } + } + my $output = ''; + if (keys(%menu)) { + my @current = sort { $a <=> $b } keys(%menu); + foreach my $num (@current) { + my %checked; + foreach my $key (keys(%{$menu{$num}})) { + if (($key eq 'top') || ($key eq 'inline') || ($key eq 'foot') || ($key eq 'main')) { + if ($menu{$num}{$key} eq 'y') { + $checked{$key} = 1; + } + } else { + foreach my $field (split(/,/,$menu{$num}{$key})) { + if (exists($menufields{$field})) { + $checked{$field} = 1; + } + } + } + } + if (ref($menu{$num}) eq 'HASH') { + $output .= '
    '.&mt('Collection [_1]',$num).''; + foreach my $category (@order) { + if ((ref($categories{$category}) eq 'ARRAY') && (@{$categories{$category}} > 0)) { + $output .= '
    '. + ''.$menutitles{$category}.''."\n"; + if ($category eq 'text') { + $output .= ''.&mt('Header Text').'

    '; + } + foreach my $field (@{$categories{$category}}) { + if ($field eq 'disc') { + $output .= '
    '.&mt('Footer Text').'

    '; + } + if ($checked{$field}) { + $output .= &Apache::lonhtmlcommon::confirm_success($menufields{$field}); + } else { + $output .= &Apache::lonhtmlcommon::confirm_success($menufields{$field},1); + } + $output .= '
    '; + } + $output .= '
    '; + } + } + $output .= '
    '; + } + } + } + return $output; +} + +sub print_linkprotection { + my ($cdom,$cnum,$settings,$rowtotal,$crstype,$noedit,$context) = @_; + unless (ref($settings) eq 'HASH') { + return; + } + + my %linkprotection; + my $count = 0; + my $next = 1; + my ($datatable,$disabled,$css_class,$dest); + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + my %desc = &linkprot_names(); + my %lt = &Apache::lonlocal::texthash ( + 'requ' => 'Required settings', + 'opti' => 'Optional settings', + ); + my $itemcount = 0; + + my $ltiauth; + if ($context eq 'domain') { + $ltiauth = 1; + } else { + if (exists($env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'})) { + $ltiauth = $env{'course.'.$env{'request.course.id'}.'.internal.ltiauth'}; + } else { + my %domdefs = &Apache::lonnet::get_domain_defaults($cdom); + $ltiauth = $domdefs{'crsltiauth'}; + } + } + if ($context eq 'domain') { + $dest = '/adm/domainprefs'; + } else { + $dest = '/adm/courseprefs'; + } + + my ($switchserver,$switchmessage); + $switchserver = &check_switchserver($cdom,$cnum,$context,$dest); + if ($switchserver) { + if ($context eq 'domain') { + $switchmessage = &mt("submit from domain's primary library server: [_1].",$switchserver); + } elsif ($crstype eq 'Community') { + $switchmessage = &mt("submit from community's home server: [_1].",$switchserver); + } else { + $switchmessage = &mt("submit from course's home server: [_1].",$switchserver); + } + } + + if (ref($settings->{'linkprot'}) eq 'HASH') { + if (keys(%{$settings->{'linkprot'}})) { + my @current = sort { $a <=> $b } keys(%{$settings->{'linkprot'}}); + $next += $current[-1]; + for (my $i=0; $i<@current; $i++) { + my $num = $current[$i]; + my %values; + if (ref($settings->{'linkprot'}->{$num}) eq 'HASH') { + %values = %{$settings->{'linkprot'}->{$num}}; + } else { + next; + } + my $selected; + if (($values{'version'} eq 'LTI-1p0') || ($values{'version'} eq '')) { + $selected = ' selected="selected"'; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= + '
    '. + ''; + my ($usersty,$onclickrequser,%checkedrequser); + if ($ltiauth) { + $usersty = 'display:none'; + $onclickrequser = ' onclick="toggleLinkProtReqUser(this.form,'."'requser','optional','1','block','$i'".');"'; + %checkedrequser = ( + no => ' checked="checked"', + yes => '', + ); + if ($values{'requser'}) { + $checkedrequser{'yes'} = $checkedrequser{'no'}; + $checkedrequser{'no'} = ''; + } + $datatable .= '
    '.$lt{'requ'}.''; + if ($values{'requser'}) { + $usersty = 'display:inline-block'; + } + } + $datatable .= + ''.$desc{'name'}. + ': '. + (' 'x2). + ''.$desc{'version'}.': '."\n". + (' 'x2). + ''.$desc{'lifetime'}.':'; + if ($ltiauth) { + $datatable .= (' 'x2).''.$desc{'requser'}.'?'. + ' '. + ''; + } + $datatable .= '

    '; + if ($values{'key'} ne '') { + $datatable .= ''.$desc{'key'}; + if ($noedit) { + $datatable .= ': ['.&mt('not shown').']'; + } elsif ($switchserver) { + $datatable .= ': ['.&mt('[_1] to view/edit',$switchserver).']'; + } else { + $datatable .= ':'; + } + $datatable .= ' '.(' 'x2); + } elsif (!$switchserver) { + $datatable .= ''.$desc{'key'}.':'. + ''. + ' '.(' 'x2); + } + if ($switchserver) { + if ($values{'usable'} ne '') { + $datatable .= '
    '. + $desc{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'
    '. + ''.&mt('Change secret?'). + ''. + (' 'x2). + ''.(' 'x2). + ''; + } elsif ($values{'key'} eq '') { + $datatable .= ''.&mt('Key and Secret are required').' - '.$switchmessage.''."\n"; + } else { + $datatable .= ''.&mt('Secret required').' - '.$switchmessage.''."\n"; + } + } else { + if ($values{'usable'} ne '') { + $datatable .= '
    '. + $desc{'secret'}.': ['.&mt('not shown').'] '.(' 'x2).'
    '. + ''.&mt('Change?'). + ''. + (' 'x2). + '  '; + } else { + $datatable .= + ''.$desc{'secret'}.':'. + ''. + ''. + ''; + } + } + if ($ltiauth) { + $datatable .= + '
    '. + '
    '.$lt{'opti'}.''. + &linkprot_options($i,$itemcount,$disabled,\%values,\%desc). + '
    '; + } + $datatable .= '
    '."\n". + ''."\n". + ''.&mt('Add').''; + my ($usersty,$onclickrequser,%checkedrequser); + if ($ltiauth) { + $usersty = 'display:none'; + $onclickrequser = ' onclick="toggleLinkProtReqUser(this.form,'."'requser','optional','1','block','add'".');"'; + %checkedrequser = ( + no => ' checked="checked"', + yes => '', + ); + $datatable .= '
    '.$lt{'requ'}.''; + } + $datatable .= ''.$desc{'name'}. + ': '."\n". + (' 'x2). + ''.$desc{'version'}.': '."\n". + (' 'x2). + ''.$desc{'lifetime'}.': '."\n"; + if ($ltiauth) { + $datatable .= (' 'x2).''.$desc{'requser'}.'?'. + ' '. + ''; + } + $datatable .= '

    '; + if ($switchserver) { + $datatable .= ''.&mt('Key and Secret are required').' - '.$switchmessage.''."\n"; + } else { + $datatable .= ''.$desc{'key'}.': '."\n". + (' 'x2). + ''.$desc{'secret'}.':'. + ' '."\n"; + } + if ($ltiauth) { + $datatable .= '
    '. + '
    '.$lt{'opti'}.''. + &linkprot_options('add',$itemcount,$disabled,{},\%desc). + '
    '; + } + $datatable .= '
    '.$text. - ''; + my $css_class = ($count % 2) ? 'LC_odd_row' : 'LC_even_row'; + $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq ''); + if ($leftclass eq '') { + $leftclass = 'LC_left_item'; + } + if ($rightclass eq '') { + $rightclass = 'LC_right_item'; + } + $output .= '
    '.$text. + ''; + } else { + $output .= ''; + } return $output; } @@ -2977,25 +6000,43 @@ sub item_table_row_end { } sub yesno_radio { - my ($item,$settings) = @_; + my ($item,$settings,$unsetdefault,$valueyes,$valueno,$noedit) = @_; my $itemon = ' '; my $itemoff = ' checked="checked" '; + if (($valueyes eq '') && ($valueno eq '')) { + $valueyes = 'yes'; + $valueno = ''; + } if (ref($settings) eq 'HASH') { - if ($settings->{$item} eq 'yes') { + if ($settings->{$item} eq $valueyes) { $itemon = $itemoff; $itemoff = ' '; } + unless (exists($settings->{$item})) { + if ($unsetdefault eq $valueyes) { + $itemon = $itemoff; + $itemoff = ' '; + } + } + } + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; } return ' '. + $itemon.' value="'.$valueyes.'"'.$disabled.' />'.&mt('Yes').' '. ''; + $itemoff.' value="'.$valueno.'"'.$disabled.' />'.&mt('No').''; } sub select_from_options { - my ($item,$order,$options,$curr,$nullval,$multiple,$maxsize,$onchange) = @_; + my ($item,$order,$options,$curr,$nullval,$multiple,$maxsize,$onchange,$noedit,$id) = @_; my $output; + my $disabled; + if ($noedit) { + $disabled = ' disabled=disabled"'; + } if ((ref($order) eq 'ARRAY') && (ref($options) eq 'HASH')) { $output=''; foreach my $user (sort(@privusers)) { my $hideon = ' checked="checked" '; @@ -3139,8 +6225,9 @@ sub nothidepriv_row { ''. + $hideon.' value=""'.$disabled.' />'.&mt('Hidden').' '. + ''. ''; } $datatable .= '
    '. ' '. - '
    '; @@ -3154,8 +6241,72 @@ sub nothidepriv_row { return $datatable; } +sub checkforpriv_row { + my ($cdom,$item,$settings,$crstype,$noedit) = @_; + my $datatable; + my %domains=map { $_ => 1; } &Apache::lonnet::all_domains(); + if (keys(%domains) == 1) { + $datatable = '
    '. + &mt("Course's domain is only domain"); + return $datatable; + } + my @excdoms = ($cdom); + my $num = 0; + if ($settings->{$item} eq '') { + $datatable = ''. + &Apache::loncommon::select_dom_form('','checkforpriv_'.$num, + 1,1,undef,undef,\@excdoms, + $noedit); + } else { + my @privdoms = split(/,/,$settings->{$item}); + my %domains=map { $_ => 1; } &Apache::lonnet::all_domains(); + $datatable = '
    '. + &Apache::loncommon::start_data_table(); + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } + if (@privdoms > 0) { + foreach my $currdom (@privdoms) { + next unless ($domains{$currdom}); + my $domdesc = &Apache::lonnet::domain($currdom,'description'); + if ($domdesc eq '') { + $domdesc = $currdom; + } + $datatable .= + &Apache::loncommon::start_data_table_row(). + '
    '. + &mt('Domain:').' '.$domdesc. + '
    '. + '
    '. + &mt('Additional domain:'). '
    '. + &Apache::loncommon::select_dom_form('','checkforpriv_'.$num,1, + 1,undef,undef,\@excdoms,$noedit). + '
    '; + $output .= ''; + } + $output .= '

    '. + $currstr.''; + my $disabled; + if ($noedit) { + $disabled = ' disabled="disabled"'; + } if (@curr > 0) { for (my $i=0; $i<@curr; $i++) { my $pos = $i+1; $output .= ''. ''; + '"'.$disabled.' />'; if ($curr[$i] =~ /^%\d*[nca]$/) { my ($limit,$subst) = ($curr[$i] =~ /^%(\d*)([nca])$/); $output .= ''; + &substitution_selector($i,$subst,$limit,$disabled).''; } else { $output .= ''; + ' value="'.$curr[$i].'" size="25"'.$disabled.' />'; } $output .= ''; } } - my $pos = $currnum+1; - $output .= ''. + my $pos = $currnum+1; + unless ($noedit) { + $output .= + ''. ''. ''; - $pos ++; - $currnum ++; - $output .= ''. + $pos ++; + $currnum ++; + $output .= + ''. ''. @@ -3265,14 +6424,15 @@ ENDJS '" value="" size ="25" />'. ''. - ''. - '
    '. - &position_selector($pos,$i,$maxnum).&mt('Delete:'). + &position_selector($pos,$i,$maxnum,$disabled).&mt('Delete:'). ''. - &substitution_selector($i,$subst,$limit).''.&mt('Text').'
    '. '
    '. &position_selector($pos,$currnum,$maxnum). ''.&mt('New').''. &substitution_selector($currnum).'
    '. &position_selector($pos,$currnum,$maxnum). ''.&mt('New').'
    '; + '

    '; return $output; } sub position_selector { - my ($pos,$num,$maxnum) = @_; - my $output = ''; for (my $j=1; $j<=$maxnum; $j++) { my $sel = ''; if ($pos == $j) { @@ -3286,7 +6446,7 @@ sub position_selector { } sub substitution_selector { - my ($num,$subst,$limit,$crstype) = @_; + my ($num,$subst,$limit,$crstype,$disabled) = @_; my ($stunametxt,$crsidtxt); if ($crstype eq 'Community') { $stunametxt = 'member name'; @@ -3301,7 +6461,7 @@ sub substitution_selector { a => 'assignment note', ); my $output .= &mt('Substitution').'
    '. - ''; if ($subst eq '') { $output .= ''; } @@ -3315,7 +6475,7 @@ sub substitution_selector { } $output .= ''.&mt('Size limit').'
    '. ''; + '" value="'.$limit.'" size="5"'.$disabled.' />'; return $output; } @@ -3333,17 +6493,19 @@ sub change_clone { foreach my $currclone (@allowclone) { if (!grep(/^$currclone$/,@$oldcloner)) { if ($currclone ne '*') { - my ($uname,$udom) = split(/:/,$currclone); - if ($uname && $udom && $uname ne '*') { - if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') { - my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); - if ($currclonecrs{'cloneable'} !~ /\Q$clone_crs\E/) { - if ($currclonecrs{'cloneable'} eq '') { - $currclonecrs{'cloneable'} = $clone_crs; - } else { - $currclonecrs{'cloneable'} .= ','.$clone_crs; + if ($currclone =~ /:/) { + my ($uname,$udom) = split(/:/,$currclone); + if ($uname && $udom && $uname ne '*') { + if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') { + my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); + if ($currclonecrs{'cloneable'} !~ /\Q$clone_crs\E/) { + if ($currclonecrs{'cloneable'} eq '') { + $currclonecrs{'cloneable'} = $clone_crs; + } else { + $currclonecrs{'cloneable'} .= ','.$clone_crs; + } + &Apache::lonnet::put('environment',\%currclonecrs,$udom,$uname); } - &Apache::lonnet::put('environment',\%currclonecrs,$udom,$uname); } } } @@ -3353,24 +6515,26 @@ sub change_clone { foreach my $oldclone (@$oldcloner) { if (!grep(/^\Q$oldclone\E$/,@allowclone)) { if ($oldclone ne '*') { - my ($uname,$udom) = split(/:/,$oldclone); - if ($uname && $udom && $uname ne '*' ) { - if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') { - my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); - my %newclonecrs = (); - if ($currclonecrs{'cloneable'} =~ /\Q$clone_crs\E/) { - if ($currclonecrs{'cloneable'} =~ /,/) { - my @currclonecrs = split/,/,$currclonecrs{'cloneable'}; - foreach my $crs (@currclonecrs) { - if ($crs ne $clone_crs) { - $newclonecrs{'cloneable'} .= $crs.','; + if ($oldclone =~ /:/) { + my ($uname,$udom) = split(/:/,$oldclone); + if ($uname && $udom && $uname ne '*' ) { + if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') { + my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); + my %newclonecrs = (); + if ($currclonecrs{'cloneable'} =~ /\Q$clone_crs\E/) { + if ($currclonecrs{'cloneable'} =~ /,/) { + my @currclonecrs = split/,/,$currclonecrs{'cloneable'}; + foreach my $crs (@currclonecrs) { + if ($crs ne $clone_crs) { + $newclonecrs{'cloneable'} .= $crs.','; + } } + $newclonecrs{'cloneable'} =~ s/,$//; + } else { + $newclonecrs{'cloneable'} = ''; } - $newclonecrs{'cloneable'} =~ s/,$//; - } else { - $newclonecrs{'cloneable'} = ''; + &Apache::lonnet::put('environment',\%newclonecrs,$udom,$uname); } - &Apache::lonnet::put('environment',\%newclonecrs,$udom,$uname); } } } 500 Internal Server Error

    Internal Server Error

    The server encountered an internal error or misconfiguration and was unable to complete your request.

    Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

    More information about this error may be available in the server error log.