--- loncom/interface/courseprefs.pm 2009/12/06 22:17:07 1.8.4.1 +++ loncom/interface/courseprefs.pm 2022/02/15 04:28:01 1.102 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set configuration settings for a course # -# $Id: courseprefs.pm,v 1.8.4.1 2009/12/06 22:17:07 raeburn Exp $ +# $Id: courseprefs.pm,v 1.102 2022/02/15 04:28:01 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -28,6 +28,189 @@ ############################################################### ############################################################## +=pod + +=head1 NAME + +courseprefs- Handler to set/modify course configuration + +=head1 SYNOPSIS + +courseprefs provides an interface for setting general course configuration + +=head1 DESCRIPTION + +This module is used for configuration of a course + +=head1 INTERNAL SUBROUTINES + +=over + +=item get_allitems() + +=item print_config_box() + +=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() + +=item get_course() + +=item get_jscript() + +=item cloners_javascript() + +=item print_courseinfo() + +=item new_cloners_dom_row() + +=item can_modify_catsettings() + +=item assign_course_categories() + +=item print_localization() + +=item get_lang_choices() + +=item print_feedback() + +=item user_table() + +=item select_recipient() + +=item select_sections() + +=item print_discussion() + +=item role_checkboxes() + +=item print_classlists() + +=item print_appearance() + +=item print_grading() + +=item print_printouts() + +=item print_spreadsheet() + +=item print_bridgetasks() + +=item print_other() + +=item get_other_items() + +=item item_table_row_start() + +=item item_table_row_end() + +=item yes_no_radio() + +=item select_from_options() + +=item make_item_rows() + +Creates table used to display and set course configuration items. + +Inputs: $cdom,$items,$ordered,$settings,$rowtotal,$crstype +where $cdom is course's domain, $items is HASH ref for current config +item, $ordered is ARRAY ref of items to include in row in +display order, $settings is HASH ref of current values forrow, +$rowtotal is SCALAR ref used to accumulate row count, $crstype is +course type. + +Returns: $datatable +HTML mark-up of data table which accumulates individual rows. + +=item nothidepriv_row() + +Creates row containing form elements used to display and set +whether Domain coordinators who are currently included in +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,$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). + +Return: $datatable +HTML mark-up for Privileged users (Domain Coordinators) in staff listing. + +=item print_hdrfmt_row() + +Creates row containing form elements used to display and set +substitution items and text to be used in the header included +on printouts. + +Inputs: $item,$settings +where $item is print_header_format, and $settings is a HASH ref +of the current values stored for print_header_format. + +Returns: $output +HTML mark-up containing Javascript functions: reOrder() and getIndexByName() +used to dynamically update position selectboxes, and HTML table elements +for the "Print header format" row. + +=item position_selector() + +Creates a select box which can be used to reorder substitutions +and text included in a printout header. + +Inputs: $pos,$num,$maxnum +where $pos is current position, $num is the unique identifier, +and $maxnum is the total number of items (both substitutions +and text in the printout header. + +Returns: $output +HTML mark-up for the selectbox and a hidden form element containing +the current position. + +=item substitution_selector() + +Creates a combination of select box for choosing an item +(student name, course ID or assignment note) to substitute, +and a corresponding size limit in the header used for printouts. + +Inputs: $num,$subst,$limit,$crstype +where $num is the unique identifier, $subst is the current +substitution (n,c or a, for name, course or note respectively, +$limit is the current size limit (integer), and $crstype is +course type - course or community. + +Returns: $output +HTML mark-up for selectbox and textbox (separate table cells). + +=item change_clone() + +Modifies the list of courses a user can clone (stored +in the user's environment.db file), called when a +change is made to the list of users allowed to clone +a course. + +Inputs: $action,$cloner +where $action is add or drop, and $cloner is identity of +user for whom cloning ability is to be changed in course. + +Returns: nothing + +=back + +=cut + + package Apache::courseprefs; use strict; @@ -36,9 +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); +my $registered_cleanup; +my $modified_courses; + sub handler { my $r=shift; if ($r->header_only) { @@ -48,24 +237,38 @@ 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 { if ($navmap) { - $env{'user.error.msg'}= - "/adm/courseprefs:opa:0:0:Cannot modify course settings"; + if ($crstype eq 'Community') { + $env{'user.error.msg'}= + "/adm/courseprefs:opa:0:0:Cannot modify community settings"; + } else { + $env{'user.error.msg'}= + "/adm/courseprefs:opa:0:0:Cannot modify course settings"; + } } else { - $env{'user.error.msg'}= - "/adm/courseprefs::0:1:Course environment gone, reinitialize the course"; + if ($crstype eq 'Community') { + $env{'user.error.msg'}= + "/adm/courseprefs::0:1:Course environment gone, reinitialize the community"; + } else { + $env{'user.error.msg'}= + "/adm/courseprefs::0:1:Course environment gone, reinitialize the course"; + + } } return HTTP_NOT_ACCEPTABLE; } + $registered_cleanup=0; + @{$modified_courses}=(); + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['phase','actions','origin']); &Apache::lonhtmlcommon::clear_breadcrumbs(); @@ -73,133 +276,236 @@ sub handler { &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/parmset", text=>"Parameter Manager"}); } + my ($brtext,$brtitle,$crsinfotext,$crsinfodesc,$crscateg,$crshide); + my %lt; + if ($crstype eq 'Community') { + %lt = ( + 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', + catg => 'Categorize community', + excc => 'Exclude from community catalog', + clon => 'Users allowed to clone community', + rept => 'Replacement titles for standard community roles', + time => 'Timezone where the community is located', + date => 'Locale used for community calendar', + coco => 'Community Content', + copo => 'Community Policy', + priv => 'Domain Coordinators in community', + defd => 'Default dates for member access', + stuv => 'Member-viewable membership list options', + stul => 'Member agreement needed to be listed', + 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 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', + excc => 'Exclude from course catalog', + clon => 'Users allowed to clone course', + rept => 'Replacement titles for standard course roles', + time => 'Timezone in which the course takes place', + date => 'Locale used for course calendar', + coco => 'Course Content', + copo => 'Course Policy', + priv => 'Domain Coordinators in course', + defd => 'Default dates for student access', + stuv => 'Student-viewable classlist options', + stul => 'Student agreement needed to be listed', + 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=>"Course Configuration"}); + text=>$lt{'conf'}}); my $breadcrumbs = - &Apache::lonhtmlcommon::breadcrumbs('Edit Course Configuration'); + &Apache::lonhtmlcommon::breadcrumbs($lt{'edit'}); my $phase = 'pickactions'; if ( exists($env{'form.phase'}) ) { $phase = $env{'form.phase'}; } - if ($phase eq 'categorizecourse') { - &assign_course_categories($r); + 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 => 'General course settings', - help => 'Course_Environment', - header => [{col1 => 'Setting', - col2 => 'Value'}], - ordered => ['description','courseid','categories', - 'hidefromcat','cloners','externalsyllabus', - 'url','rolenames'], + { text => $lt{'gens'}, + help => 'Course_Prefs_General', + ordered => ['owner','co-owners','loncaparev','description', + 'clonedfrom','courseid','uniquecode','categories', + 'hidefromcat','syllabus','cloners','url', + 'rolenames'], itemtext => { - description => 'Course Description', - courseid => 'Course ID or number', - categories => 'Categorize course', - hidefromcat => 'Exclude from course catalog', - cloners => 'Users allowed to clone course', - externalsyllabus => 'URL of Syllabus', - url => 'Top Level Map', - rolenames => 'Replacement titles for standard course roles', + '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'}, + 'syllabus' => 'Syllabus status', + 'url' => 'Top Level Map', + 'rolenames' => $lt{'rept'}, + 'loncaparev' => $lt{'lcrv'}, + 'clonedfrom' => 'Cloned from', }, }, 'localization' => - { text => 'Language/TimeZone/Locale', - help => 'Course_Environment', - header => [{col1 => 'Setting', - col2 => 'Value',}], + { text => 'Language and time localization', + help => 'Course_Prefs_Language', ordered => ['languages','timezone','datelocale'], itemtext => { languages => 'Languages used', - timezone => 'Timezone in which the course takes place', - datelocale => 'Locale used for course calendar', + timezone => $lt{'time'}, + datelocale => $lt{'date'}, }, }, 'feedback' => { text => 'Feedback messages', - help => 'Course_Environment', + help => 'Course_Prefs_Feedback', header => [{col1 => 'Questions about:', - col2 => 'Recipients'}, - {col1 => 'Questions about:', - col2 => 'Custom Text'}], + col2 => 'Recipients'}], ordered => ['question.email','comment.email','policy.email'], itemtext => { 'question.email' => 'Resource Content', - 'comment.email' => 'Course Content', - 'policy.email' => 'Course Policy', + 'comment.email' => $lt{'coco'}, + 'policy.email' => $lt{'copo'}, }, }, 'discussion' => { text => 'Discussion and Chat', - help => 'Course_Environment', - header => [{col1 => 'Setting', - col2 => 'Value',}], - ordered => ['plc.roles.denied','plc.users.denied', - 'pch.roles.denied','pch.users.denied', + 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 => { - 'plc.roles.denied' => 'No Resource Discussion', - 'plc.users.denied' => 'No Resource Discussion', - 'pch.roles.denied' => 'No Chat room use', - 'pch.users.denied' => 'No Chat room use', - allow_limited_html_in_feedback => 'Allow limited HTML in discussion posts', + '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 => 'Classlists and Staff Listing', - help => 'Course_Environment', + { text => $lt{'clas'}, + help => 'Course_Prefs_Classlists', header => [{col1 => 'Type', - col2 => 'Default dates for student access'}, + col2 => $lt{'defd'}}, {col1 => 'Setting', - col2 => 'Privileged users (Domain Coordinators) in staff listing'}, + col2 => $lt{'priv'}}, {col1 => 'Setting', - col2 => 'Student-viewable classlist options'}], + col2 => $lt{'stuv'}}], ordered => ['default_enrollment_start_date', 'default_enrollment_end_date', - 'nothideprivileged','student_classlist_view', - 'student_opt_in','student_classlist_portfiles'], + '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 => 'Domain Coodinators in course', - student_classlist_view => 'Student-viewable classlist', - student_opt_in => 'Student agreement needed to be listed', + 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', - header => [{col1 => 'Setting', - col2 => 'Value'}], + 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', + 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', - header => [{col1 => 'Setting', - col2 => 'Value',}], + help => 'Course_Prefs_Grading', ordered => ['grading','rndseed', 'receiptalg','disablesigfigs'], itemtext => { @@ -212,73 +518,138 @@ sub handler { }, 'printouts' => { text => 'Printout generation', - help => 'Course_Environment', - header => [{col1 => 'Setting', - col2 => 'Value',}], + help => 'Course_Prefs_Printouts', ordered => ['problem_stream_switch','suppress_tries', 'default_paper_size','print_header_format', - 'disableexampointprint'], + 'disableexampointprint','canuse_pdfforms'], itemtext => { problem_stream_switch => 'Allow problems to be split over pages', suppress_tries => 'Suppress number of tries in printing', default_paper_size => 'Default paper type', print_header_format => 'Print header format', - disableexampointprint => 'Disable automatically printing point values on exams', + 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', - header => [{col1 => 'Setting', - col2 => 'Value'}], + help => 'Course_Prefs_Spreadsheet', ordered => ['spreadsheet_default_classcalc', 'spreadsheet_default_studentcalc', 'spreadsheet_default_assesscalc','hideemptyrows'], itemtext => { - spreadsheet_default_classcalc => 'Default Course Spreadsheet', - spreadsheet_default_studentcalc => 'Default Student Spreadsheet', + spreadsheet_default_classcalc => $lt{'defc'}, + spreadsheet_default_studentcalc => $lt{'defs'}, spreadsheet_default_assesscalc => 'Default Assessment Spreadsheet', hideemptyrows => 'Hide Empty Rows in Spreadsheets', }, }, 'bridgetasks' => { text => 'Bridge tasks', - help => 'Course_Environment', - header => [{col1 => 'Setting', - col2 => 'Value'}], + help => 'Course_Prefs_Bridgetasks', ordered => ['task_messages','task_grading', 'suppress_embed_prompt'], itemtext => { - task_messages => 'Send message to student when clicking Done on Tasks', + task_messages => $lt{'seme'}, task_grading => 'Bridge Task grading by instructors and TAs in sections' , 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); + $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); + \@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; @@ -296,91 +667,140 @@ sub get_allitems { } sub print_config_box { - my ($r,$cdom,$phase,$action,$item,$settings,$allitems) = @_; + 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 = '