From 6a7c5b5407282087e355a8c9dac639a37d6772f2 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Wed, 16 Aug 2023 21:29:44 -0500 Subject: [PATCH] Move the ConfigValues array out of defaults.config. This array is now defined in `lib/WeBWorK/ConfigValues.pm` and is returned by the `getConfigValues` method. The `$LTIConfigValues` hash is also defined in that file and added to the array when `getConfigValues` is called if LTI authentication is enabled for the course. This means that these are no longer configurable. No config file can modify them. Furthermore, the array is not even ever added to the course environment. Instead it is just used by the course configuration module which is the only place it was ever used. This means that if a course's course.conf file includes authen_LTI.conf, it does not also need to include the LTIConfigValues.config file. That file in fact no longer exists. Also remove all access to the `paramcache` outside of the `WeBWorK::Controller` module. That is an internal implementation detail that should never be accessed directly outside of that file. --- bin/dev_scripts/update-localization-files | 3 +- conf/LTIConfigValues.config | 106 -- conf/defaults.config | 781 -------------- lib/WeBWorK/Authen/LTIAdvanced.pm | 14 +- lib/WeBWorK/ConfigObject.pm | 11 +- lib/WeBWorK/ConfigValues.pm | 993 ++++++++++++++++++ .../ContentGenerator/Instructor/Config.pm | 165 +-- lib/WebworkWebservice/CourseActions.pm | 61 +- .../Instructor/Config.html.ep | 92 +- 9 files changed, 1101 insertions(+), 1125 deletions(-) delete mode 100644 conf/LTIConfigValues.config create mode 100644 lib/WeBWorK/ConfigValues.pm diff --git a/bin/dev_scripts/update-localization-files b/bin/dev_scripts/update-localization-files index 6b2193d97f..8ad0cc1c11 100755 --- a/bin/dev_scripts/update-localization-files +++ b/bin/dev_scripts/update-localization-files @@ -55,8 +55,7 @@ cd $LOCDIR echo "Updating $LOCDIR/webwork2.pot" -xgettext.pl -o webwork2.pot -D $WEBWORK_ROOT/lib -D $WEBWORK_ROOT/templates \ - $WEBWORK_ROOT/conf/defaults.config $WEBWORK_ROOT/conf/LTIConfigValues.config +xgettext.pl -o webwork2.pot -D $WEBWORK_ROOT/lib -D $WEBWORK_ROOT/templates if $UPDATE_PO; then find $LOCDIR -name '*.po' -exec bash -c "echo \"Updating {}\"; msgmerge -qUN {} webwork2.pot" \; diff --git a/conf/LTIConfigValues.config b/conf/LTIConfigValues.config deleted file mode 100644 index 7edc62163f..0000000000 --- a/conf/LTIConfigValues.config +++ /dev/null @@ -1,106 +0,0 @@ -#!perl -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2023 The WeBWorK Project, https://github.com/openwebwork -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -# These are the LTI authentication variables that may be added to the 'LTI' tab -# on the Course Configuration page. These are added by setting the variables -# near the end of authen_LTI.conf. - -# YOU SHOULD NOT NEED TO EDIT THIS FILE!! - -$LTIConfigValues = { - 'LTI{v1p1}{LMS_name}' => { - var => 'LTI{v1p1}{LMS_name}', - doc => x('The name of the LMS'), - doc2 => x( - 'The name of the LMS. This is used in messages to users that direct them to go back to ' - . 'the LMS to access something in the WeBWorK course.' - ), - type => 'text' - }, - 'LTI{v1p1}{LMS_url}' => { - var => 'LTI{v1p1}{LMS_url}', - doc => x('A URL for the LMS'), - doc2 => x( - 'An address that can be used to log in to the LMS. This is used in messages to users ' - . 'that direct them to go back to the LMS to access something in the WeBWorK course.' - ), - type => 'text', - width => 30, - }, - external_auth => { - var => 'external_auth', - doc => x('Require users to log in through the LMS'), - doc2 => x( - 'If this is set, all users (including the instructor) must enter the WeBWorK course through the LMS. If ' - . 'a user reaches the regular WeBWorK login screen, they receive a message directing them back to ' - . 'the LMS.' - ), - type => 'boolean' - }, - LTIGradeMode => { - var => 'LTIGradeMode', - doc => x('Grade passback mode'), - doc2 => x( - 'Sets how grades will be passed back from WeBWorK to the LMS.
course
Sends a single ' - . 'grade back to the LMS. This grade is calculated out of the total question set that has been ' - . 'assigned to a user and made open. Therefore it can appear low, since it counts problem sets with ' - . 'future due dates as zero.
homework
Sends back a score for each problem set ' - . '(including for each quiz). To use this, the external links from the LMS must be problem set ' - . 'specific. For example, webwork.myschool.edu/webwork2/course-name/problem_set_name. ' - . 'If the problem set name has space characters, they should be underscores in these addresses. ' - . 'Also, to initialize the communication between WeBWorK and the LMS, the user must follow each of ' - . 'these external learning tools at least one time. Since there must be a separate external tool link ' - . 'for each problem set, this option requires more maintenance of the LMS course.
' - ), - values => [ '', qw(course homework) ], - labels => { '' => 'None', 'course' => 'Course', 'homework' => 'Homework' }, - type => 'popuplist' - }, - LMSManageUserData => { - var => 'LMSManageUserData', - doc => x('Allow the LMS to update user account data'), - doc2 => x( - 'WeBWorK will automatically create users when logging in via the LMS for the first time. If this flag is ' - . 'enabled then it will also keep the user account data (first name, last name, section, recitation) ' - . 'up to date with the LMS. If a user\'s information changes in the LMS then it will change in ' - . 'WeBWorK. However, any changes to the user data via WeBWorK will be overwritten the next time the ' - . 'user logs in.' - ), - type => 'boolean' - }, - debug_lti_parameters => { - var => 'debug_lti_parameters', - doc => x('Show LTI parameters (for debugging)'), - doc2 => x( - 'When this is true, then when a user enters WeBWorK from an external tool link in the LMS, the bottom of ' - . 'the screen will display the data that the LMS passed to WeBWorK. This may be useful to debug LTI, ' - . 'especially because different LMS systems have different parameters.' - ), - type => 'boolean' - }, -}; - -$LTIConfigValues->{'LTI{v1p3}{LMS_name}'} = - { %{ $LTIConfigValues->{'LTI{v1p1}{LMS_name}'} }, var => 'LTI{v1p3}{LMS_name}' }; -$LTIConfigValues->{'LTI{v1p3}{LMS_url}'} = - { %{ $LTIConfigValues->{'LTI{v1p1}{LMS_url}'} }, var => 'LTI{v1p3}{LMS_url}' }; - -if (@LTIConfigVariables && !(grep { $_->[0] eq 'LTI' } @$ConfigValues)) { - push(@$ConfigValues, - [ x('LTI'), map { $LTIConfigValues->{$_} } grep { defined $LTIConfigValues->{$_} } @LTIConfigVariables ]); -} - -1; # final line of the file to reassure perl that it was read properly. diff --git a/conf/defaults.config b/conf/defaults.config index 6936fc6a04..08fbd048ed 100644 --- a/conf/defaults.config +++ b/conf/defaults.config @@ -1450,787 +1450,6 @@ $pg{options}{enableProgressBar} = 1; ################################################################################ # Site wide overrides are entered into the file localOverrides.conf ################################################################################ -# This is loaded before the $ConfigValues definition, so that the if values of -# $hardcopyThemes are overridden in localOverrides.conf, then those values are -# used and shown in the course configuration. include("conf/localOverrides.conf"); -################################################################################ -# WeBWorK::ContentGenerator::Instructor::Config -################################################################################ - -# Configuration data -# It is organized by section. The allowable types are -# 'text' for a text string (no quotes allowed), -# 'number' for a number, -# 'list' for a list of text strings, -# 'permission' for a permission value, -# 'boolean' for variables which really hold 0/1 values as flags, -# 'timezone' for a time zone, -# 'time' for a time, -# 'checkboxlist' for variables that hold a list of values which can be independently picked yes/no as checkboxes, -# 'popuplist' for variables that hold a list of values to be selected from. - -# var key indicates this is a course environment variable -# setting key indicates this is a setting from the course's setting table in the database -# and in this case, only text type is presently supported since there are not presently -# other types of data in the setting table that could conceivably be up for manual reassignment - -# Localization Info: The doc strings in this portion are reproduced in -# lib/WeBWorK/Localize.pm solely so that xgettext.pl will -# include them when creating .pot files. -# If you change these strings you should make the corresponding changes in -# Localize.pm - -# This is a dummy function used to mark strings in the config values for localization. -# The method in lib/WeBWorK/Utils.pm cannot be used here. -sub x { return @_; } - -$ConfigValues = [ - [ - x('General'), - { - setting => 'courseTitle', - doc => 'Title for course displayed on the Assignments page', - type => 'text' - }, - { - var => 'courseFiles{course_info}', - doc => x('Name of course information file'), - doc2 => x( - 'The name of course information file (located in the templates directory). ' - . 'Its contents are displayed in the right panel next to the list of homework sets.' - ), - type => 'text' - }, - { - var => 'defaultTheme', - doc => x('Theme (refresh page after saving changes to reveal new theme.)'), - doc2 => x( - 'There is one main theme to choose from: math4. It has three variants: math4-green, math4-red, and ' - . 'math4-yellow. The theme specifies a unified look and feel for the WeBWorK course web pages.' - ), - values => [qw(math4 math4-green math4-red)], - type => 'popuplist', - hashVar => '{defaultTheme}' - }, - { - var => 'language', - doc => x('Language (refresh page after saving changes to reveal new language.)'), - doc2 => x('WeBWorK currently has translations for the languages listed in the course configuration.'), - values => [qw(en tr es fr zh-HK he)], - type => 'popuplist' - }, - { - var => 'studentDateDisplayFormat', - doc => x('Format of dates that are displayed for students'), - doc2 => x( - 'This is the format of the dates displayed for students. This can be created from ' - . 'strftime patterns, e.g., ' - . '"%a %b %d at %l:%M%P", or can be one of the ' - . 'localizable formats ' - . '"datetime_format_short", "datetime_format_medium", "datetime_format_long", or ' - . '"datetime_format_full".' - ), - type => 'text' - }, - { - var => 'perProblemLangAndDirSettingMode', - doc => x('Mode in which the LANG and DIR settings for a single problem are determined.'), - doc2 => x( - '

Mode in which the LANG and DIR settings for a single problem are determined.

The system will ' - . 'set the LANGuage attribute to either a value determined from the problem, a course-wide ' - . 'default, or the system default, depending on the mode selected. The tag will only be added to ' - . 'the DIV enclosing the problem if it is different than the value which should be set in the main ' - . 'HTML tag set for the entire course based on the course language.

There are two options ' - . 'for the DIRection attribute: "ltr" for left-to-write scripts, and "rtl" for right-to-left ' - . 'scripts like Arabic and Hebrew.

The DIRection attribute is needed to trigger proper ' - . 'display of the question text when the problem text-direction is different than that used by the ' - . 'current language of the course. For example, English problems from the library browser would ' - . 'display improperly in RTL mode for a Hebrew course, unless the problen Direction is set to LTR.' - . '

The feature to set a problem language and direction was only added in 2018 to the PG ' - . 'language, so most problems will not declare their language, and the system needs to fall back ' - . 'to determining the language and direction in a different manner. The OPL itself is all English, ' - . 'so the system wide fallback is to en-US in LTR mode.

Since the defaults fall back ' - . 'to the LTR direction, most sites should be fine with the "auto::" mode, but may want to select ' - . 'the one which matches their course language. The mode "force::ltr" would also be an option for ' - . 'a course which runs into trouble with the "auto" modes.

Modes:

' - ), - values => [ - qw(none auto:: force::ltr force::rtl force:en:ltr auto:en:ltr force:tr:ltr auto:tr:ltr force:es:ltr - auto:es:ltr force:fr:ltr auto:fr:ltr force:zh_hk:ltr auto:zh_hk:ltr force:he:rtl auto:he:rtl) - ], - type => 'popuplist' - }, - { - var => 'sessionKeyTimeout', - doc => x('Inactivity time before a user is required to login again'), - doc2 => x( - 'Length of time, in seconds, a user has to be inactive before he is required to login again. ' - . 'This value should be entered as a number, so as 3600 instead of 60*60 for one hour.' - ), - type => 'number' - }, - { - var => 'siteDefaults{timezone}', - doc => x('Timezone for the course'), - doc2 => x( - '

Some servers handle courses taking place in different timezones. If this course is not showing ' - . 'the correct timezone, enter the correct value here. The format consists of unix times, such ' - . 'as "America/New_York", "America/Chicago", "America/Denver", "America/Phoenix" or ' - . '"America/Los_Angeles".

Complete list: ' - . 'TimeZoneFiles' - ), - type => 'timezone', - hashVar => '{siteDefaults}->{timezone}' - }, - { - var => 'hardcopyThemes', - doc => x('Enabled Site Hardcopy Themes'), - doc2 => x( - 'Choose which of the site PDF hardcopy themes are available. In addition to the themes selected here, ' - . 'all themes in the course hardcopyThemes folder will be available. Your selection must be saved ' - . 'and then the page must be reloaded before the new list of enabled themes will be reflected ' - . 'in the selections that follows.' - ), - values => [qw(empty.xml)], - type => 'checkboxlist', - min => 1, - hashVar => '{hardcopyThemes}' - }, - { - var => 'hardcopyTheme', - doc => x('Hardcopy Theme'), - doc2 => x('Choose a layout/styling theme for PDF hardcopy production.'), - values => [qw(empty.xml)], - type => 'popuplist', - hashVar => '{hardcopyTheme}' - }, - { - var => 'hardcopyThemePGEditor', - doc => x('Hardcopy Theme for Problem Editor'), - doc2 => x('Choose a layout/styling theme for PDF hardcopy production from the Prooblem Editor.'), - values => [qw(empty.xml)], - type => 'popuplist', - hashVar => '{hardcopyThemePGEditor}' - }, - { - var => 'showCourseHomeworkTotals', - doc => x('Show Total Homework Grade on Grades Page'), - doc2 => x( - 'When this is on students will see a line on the Grades page which has their total cumulative ' - . 'homework score. This score includes all sets assigned to the student.' - ), - type => 'boolean' - }, - { - var => 'pg{options}{enableProgressBar}', - doc => x('Enable Progress Bar and current problem highlighting'), - doc2 => x( - 'A switch to govern the use of a Progress Bar for the student; this also enables/disables the ' - . 'highlighting of the current problem in the side bar, and whether it is correct (✓), ' - . 'in progress (…), incorrect (✗), or unattempted (no symbol).' - ), - type => 'boolean' - }, - { - var => 'pg{timeAssignDue}', - doc => x('Default Time that the Assignment is Due'), - doc2 => x( - 'The time of the day that the assignment is due. This can be changed on an individual basis, ' - . 'but WeBWorK will use this value for default when a set is created.' - ), - type => 'time', - hashVar => '{pg}->{timeAssignDue}' - }, - { - var => 'pg{assignOpenPriorToDue}', - doc => x('Default Amount of Time (in minutes) before Due Date that the Assignment is Open'), - doc2 => x( - 'The amount of time (in minutes) before the due date when the assignment is opened. You can change ' - . 'this for individual homework, but WeBWorK will use this value when a set is created.' - ), - type => 'number', - hashVar => '{pg}->{assignOpenPriorToDue}' - }, - { - var => 'pg{answersOpenAfterDueDate}', - doc => x('Default Amount of Time (in minutes) after Due Date that Answers are Open'), - doc2 => x( - 'The amount of time (in minutes) after the due date that the Answers are available to student to ' - . 'view. You can change this for individual homework, but WeBWorK will use this value when a set ' - . 'is created.' - ), - type => 'number', - hashVar => '{pg}->{answersOpenAfterDueDate}' - }, - ], - [ - x('Optional Modules'), - { - var => 'achievementsEnabled', - doc => x('Enable Course Achievements'), - doc2 => x( - 'Activiating this will enable Mathchievements for webwork. Mathchievements can be managed ' - . 'by using the Achievements Manager link.' - ), - type => 'boolean' - }, - { - var => 'achievementPointsPerProblem', - doc => x('Achievement Points Per Problem'), - doc2 => x('This is the number of achievement points given to each user for completing a problem.'), - type => 'number' - }, - { - var => 'achievementPointsPerProblemReduced', - doc => x('Achievement Points Per Problem in Reduced Scoring Period'), - doc2 => x( - 'This is the number of achievement points given to each user for completing a problem if the ' - . 'problem is in a set that is in the reduced scoring period.' - ), - type => 'number' - }, - { - var => 'achievementItemsEnabled', - doc => x('Enable Achievement Rewards'), - doc2 => x( - 'Activating this will enable achievement rewards. This feature allows students to earn rewards by ' - . 'completing achievements that allow them to affect their homework in a limited way.' - ), - type => 'boolean' - }, - { - var => 'achievementExcludeSet', - doc => x('List of sets excluded from achievements'), - doc2 => x( - 'Comma separated list of set names that are excluded from all achievements. ' - . 'No achievement points and badges can be earned for submitting problems in these sets. ' - . 'Note that underscores (_) must be used for spaces in set names.' - ), - type => 'list' - }, - { - var => 'mail{achievementEmailFrom}', - doc => x('Email address to use when sending Achievement notifications.'), - doc2 => x( - 'This email address will be used as the sender for achievement notifications. ' - . 'Achievement notifications will not be sent unless this is set.' - ), - width => 45, - type => 'text' - }, - { - var => 'options{enableConditionalRelease}', - doc => x('Enable Conditional Release'), - doc2 => x( - 'Enables the use of the conditional release system. To use conditional release you need to specify a ' - . 'list of set names on the Problem Set Detail Page, along with a minimum score. Students will ' - . 'not be able to access that homework set until they have achieved the minimum score on all of ' - . 'the listed sets.' - ), - type => 'boolean' - }, - { - var => 'pg{ansEvalDefaults}{enableReducedScoring}', - doc => x('Enable Reduced Scoring'), - doc2 => x( - '

This sets whether the Reduced Scoring system will be enabled. If enabled you will need to set the ' - . 'default length of the reduced scoring period and the value of work done in the reduced scoring ' - . 'period below.

To use this, you also have to enable Reduced Scoring for individual ' - . 'assignments and set their Reduced Scoring Dates by editing the set data.

This works with ' - . 'the avg_problem_grader (which is the the default grader) and the std_problem_grader (the all ' - . 'or nothing grader). It will work with custom graders if they are written appropriately.

' - ), - type => 'boolean' - }, - { - var => 'pg{ansEvalDefaults}{reducedScoringValue}', - doc => x('Value of work done in Reduced Scoring Period'), - doc2 => x( - '

After the Reduced Scoring Date all additional work done by the student counts at a reduced rate. ' - . 'Here is where you set the reduced rate which must be a percentage. For example if this value ' - . 'is 50% and a student views a problem during the Reduced Scoring Period, they will see the ' - . 'message "You are in the Reduced Scoring Period: All additional work done counts 50% of the ' - . 'original."

To use this, you also have to enable Reduced Scoring and set the Reduced ' - . 'Scoring Date for individual assignments by editing the set data using the Sets Manager.

' - . '

This works with the avg_problem_grader (which is the the default grader) and the ' - . 'std_problem_grader (the all or nothing grader). It will work with custom graders if they ' - . 'are written appropriately.

' - ), - labels => { - '0.1' => '10%', - '0.15' => '15%', - '0.2' => '20%', - '0.25' => '25%', - '0.3' => '30%', - '0.35' => '35%', - '0.4' => '40%', - '0.45' => '45%', - '0.5' => '50%', - '0.55' => '55%', - '0.6' => '60%', - '0.65' => '65%', - '0.7' => '70%', - '0.75' => '75%', - '0.8' => '80%', - '0.85' => '85%', - '0.9' => '90%', - '0.95' => '95%', - '1' => '100%' - }, - values => [qw(1 0.95 0.9 0.85 0.8 0.75 0.7 0.65 0.6 0.55 0.5 0.45 0.4 0.35 0.3 0.25 0.2 0.15 0.1)], - type => 'popuplist' - }, - { - var => 'pg{ansEvalDefaults}{reducedScoringPeriod}', - doc => x('Default Length of Reduced Scoring Period in minutes'), - doc2 => x( - 'The Reduced Scoring Period is the default period before the due date during which all additional work ' - . 'done by the student counts at a reduced rate. When enabling reduced scoring for a set the ' - . 'reduced scoring date will be set to the due date minus this number. The reduced scoring date ' - . 'can then be changed. If the Reduced Scoring is enabled and if it is after the reduced scoring ' - . 'date, but before the due date, a message like "This assignment has a Reduced Scoring Period ' - . 'that begins 11/08/2009 at 06:17pm EST and ends on the due date, 11/10/2009 at 06:17pm EST. ' - . 'During this period all additional work done counts 50% of the original." will be displayed.' - ), - type => 'number' - }, - { - var => 'pg{options}{enableShowMeAnother}', - doc => x('Enable Show Me Another button'), - doc2 => x( - 'Enables use of the Show Me Another button, which offers the student a newly-seeded version ' - . 'of the current problem, complete with solution (if it exists for that problem).' - ), - type => 'boolean' - }, - { - var => 'pg{options}{showMeAnotherDefault}', - doc => x('Default number of attempts before Show Me Another can be used (-1 => Never)'), - doc2 => x( - 'This is the default number of attempts before show me another becomes available to students. ' - . 'It can be set to -1 to disable show me another by default.' - ), - type => 'number' - }, - { - var => 'pg{options}{showMeAnotherMaxReps}', - doc => x('Maximum times Show me Another can be used per problem (-1 => unlimited)'), - doc2 => x( - 'The Maximum number of times Show me Another can be used per problem by a student. ' - . 'If set to -1 then there is no limit to the number of times that Show Me Another can be used.' - ), - type => 'number' - }, - { - var => 'pg{options}{showMeAnother}', - doc => x('List of options for Show Me Another button'), - doc2 => x( - 'Note: there is very little point enabling the ' - . 'button unless you check at least one of these options - the students would simply see a new ' - . 'version that they can not attempt or learn from.' - ), - min => 0, - values => [ "SMAcheckAnswers", "SMAshowSolutions", "SMAshowCorrect", "SMAshowHints" ], - type => 'checkboxlist' - }, - { - var => 'pg{options}{enablePeriodicRandomization}', - doc => x('Enable periodic re-randomization of problems'), - doc2 => x( - 'Enables periodic re-randomization of problems after a given number of attempts. Student would have ' - . 'to click Request New Version to obtain new version of the problem and to continue working on ' - . 'the problem' - ), - type => 'boolean' - }, - { - var => 'pg{options}{periodicRandomizationPeriod}', - doc => x('The default number of attempts between re-randomization of the problems ( 0 => never)'), - doc2 => x('The default number of attempts before the problem is re-randomized. ( 0 => never )'), - type => 'number' - }, - { - var => 'pg{options}{showCorrectOnRandomize}', - doc => x('Show the correct answer to the current problem before re-randomization.'), - doc2 => x( - 'Show the correct answer to the current problem on the last attempt before a new version is ' - . 'requested.' - ), - type => 'boolean' - }, - ], - [ - x('Permissions'), - { - var => 'permissionLevels{login}', - doc => x('Allowed to login to the course'), - type => 'permission' - }, - { - var => 'permissionLevels{change_password}', - doc => x('Allowed to change their password'), - doc2 => x( - 'Users at this level and higher are allowed to change their password. ' - . 'Normally guest users are not allowed to change their password.' - ), - type => 'permission' - }, - { - var => 'permissionLevels{become_student}', - doc => x('Allowed to act as another user'), - type => 'permission' - }, - { - var => 'permissionLevels{submit_feedback}', - doc => x('Can email instructor'), - doc2 => x('Only this permission level and higher get buttons for sending email to the instructor.'), - type => 'permission' - }, - { - var => 'permissionLevels{record_answers_when_acting_as_student}', - doc => x('Can submit answers for a student'), - doc2 => - x('When acting as a student, this permission level and higher can submit answers for that student.'), - type => 'permission' - }, - { - var => 'permissionLevels{report_bugs}', - doc => x('Can report bugs'), - doc2 => x( - 'Users with at least this permission level get a link in the left panel for reporting bugs to the ' - . 'bug tracking system at bugs.webwork.maa.org.' - ), - type => 'permission' - }, - { - var => 'permissionLevels{change_email_address}', - doc => x('Allowed to change their email address'), - doc2 => x( - 'Users at this level and higher are allowed to change their email address. Normally guest users are ' - . 'not allowed to change the email address since it does not make sense to send email to ' - . 'anonymous accounts.' - ), - type => 'permission' - }, - { - var => 'permissionLevels{change_pg_display_settings}', - doc => x('Allowed to change display settings used in pg problems'), - doc2 => x( - 'Users at this level and higher are allowed to change display settings used in pg problems.' - . 'Note that if it is expected that there will be students that have vision impairments and ' - . 'MathQuill is enabled to assist with answer entry, then you should not set this permission to a ' - . 'level above student as those students may need to disable MathQuill.' - ), - type => 'permission' - }, - { - var => 'permissionLevels{view_answers}', - doc => x('Allowed to view past answers'), - doc2 => x('These users and higher get the "Show Past Answers" button on the problem page.'), - type => 'permission' - }, - { - var => 'permissionLevels{view_unopened_sets}', - doc => x('Allowed to view problems in sets which are not open yet'), - type => 'permission' - }, - { - var => 'permissionLevels{show_correct_answers_before_answer_date}', - doc => x('Allowed to see the correct answers before the answer date'), - type => 'permission' - }, - { - var => 'permissionLevels{show_solutions_before_answer_date}', - doc => x('Allowed to see solutions before the answer date'), - type => 'permission' - }, - { - var => 'permissionLevels{can_show_old_answers}', - doc => x('Can show old answers'), - doc2 => x( - 'When viewing a problem, WeBWorK usually puts the previously submitted answer in the answer blank. ' - . 'Below this level, old answers are never shown. Typically, that is the desired behaviour for ' - . 'guest accounts.' - ), - type => 'permission' - }, - { var => 'permissionLevels{navigation_allowed}', - doc => 'Allowed to view course home page', - doc2 => 'If a user does not have this permission, then the user will not be allowed to navigate to the ' - . 'course home page, i.e., the Assignments page. This should only be used for a course when LTI ' - . 'authentication is used, and is most useful when LTIGradeMode is set to homework. In this case the ' - . 'Assignments page is not useful and can even be confusing to students. To use this feature set ' - . 'this permission to "login_proctor".', - type => 'permission' - }, - ], - [ - x('Problem Display/Answer Checking'), - { - var => 'pg{displayModes}', - doc => x('List of display modes made available to students'), - doc2 => x( - '

When viewing a problem, users may choose different methods of rendering formulas via an options ' - . 'box in the left panel. Here, you can adjust what display modes are listed.

Some display ' - . 'modes require other software to be installed on the server. Be sure to check that all display ' - . 'modes selected here work from your server.

The display modes are

You must use at least one display mode. If you select only ' - . 'one, then the options box will not give a choice of modes (since there will only be one active).' - . '

' - ), - min => 1, - values => [ "MathJax", "images", "plainText" ], - type => 'checkboxlist' - }, - { - var => 'pg{options}{displayMode}', - doc => x('The default display mode'), - doc2 => - x('Enter one of the allowed display mode types above. See \'display modes entry\' for descriptions.'), - min => 1, - values => [qw(MathJax images plainText)], - type => 'popuplist' - }, - { - var => 'pg{specialPGEnvironmentVars}{entryAssist}', - doc => x('Assist with the student answer entry process.'), - doc2 => x( - '

MathQuill renders students answers in real-time as they type on the keyboard.

MathView ' - . 'allows students to choose from a variety of common math structures (such as fractions and ' - . 'square roots) as they attempt to input their answers.

' - ), - min => 1, - values => [qw(None MathQuill MathView)], - type => 'popuplist' - }, - { - var => 'pg{options}{showEvaluatedAnswers}', - doc => x('Display the evaluated student answer'), - doc2 => x( - 'Set to true to display the "Entered" column which automatically shows the evaluated student answer, ' - . 'e.g., 1 if student input is sin(pi/2). If this is set to false, e.g., to save space in the ' - . 'response area, the student can still see their evaluated answer by clicking on the typeset ' - . 'version of their answer.' - ), - type => 'boolean' - }, - { - var => 'pg{ansEvalDefaults}{useBaseTenLog}', - doc => x('Use log base 10 instead of base e'), - doc2 => x('Set to true for log to mean base 10 log and false for log to mean natural logarithm.'), - type => 'boolean' - }, - { - var => 'pg{specialPGEnvironmentVars}{useOldAnswerMacros}', - doc => x('Use older answer checkers'), - doc2 => x( - '

During summer 2005, a newer version of the answer checkers was implemented for answers which are ' - . 'functions and numbers. The newer checkers allow more functions in student answers, and behave ' - . 'better in certain cases. Some problems are specifically coded to use new (or old) answer ' - . 'checkers. However, for the bulk of the problems, you can choose what the default will be here.' - . '

Choosing false here means that the newer answer checkers will be used by default, ' - . 'and choosing true means that the old answer checkers will be used by default.

' - ), - type => 'boolean' - }, - { - var => 'pg{specialPGEnvironmentVars}{parseAlternatives}', - doc => x('Allow Unicode alternatives in student answers'), - doc2 => x( - 'Set to true to allow students to enter Unicode versions of some characters (like U+2212 for the ' - . 'minus sign) in their answers. One reason to allow this is that copying and pasting output ' - . 'from MathJax can introduce these characters, but it is also getting easier to enter these ' - . 'characters directory from the keyboard.' - ), - type => 'boolean' - }, - { - var => 'pg{specialPGEnvironmentVars}{convertFullWidthCharacters}', - doc => x('Automatically convert Full Width Unicode characters to their ASCII equivalents'), - doc2 => x( - 'Set to true to have Full Width Unicode character (U+FF01 to U+FF5E) converted to their ASCII ' - . 'equivalents (U+0021 to U+007E) automatically in MathObjects. This may be valuable for Chinese ' - . 'keyboards, for example, that automatically use Full Width characters for parentheses and ' - . 'commas.' - ), - type => 'boolean' - }, - { - var => 'pg{ansEvalDefaults}{numRelPercentTolDefault}', - doc => x('Allowed error, as a percentage, for numerical comparisons'), - doc2 => x( - 'When numerical answers are checked, most test if the student\'s answer is close enough to the ' - . 'programmed answer be computing the error as a percentage of the correct answer. This value ' - . 'controls the default for how close the student answer has to be in order to be marked correct.' - . '

A value such as 0.1 means 0.1 percent error is allowed.

' - ), - type => 'number' - }, - { - var => 'pg{specialPGEnvironmentVars}{waiveExplanations}', - doc => x('Skip explanation essay answer fields'), - doc2 => x( - 'Some problems have an explanation essay answer field, typically following a simpler answer field. ' - . 'For example, find a certain derivative using the definition. An answer blank would be present ' - . 'for the derivative to be automatically checked, and then there would be a separate essay answer ' - . 'field to show the steps of actually using the definition of the derivative, to be scored ' - . 'manually. With this setting, the essay explanation fields are supperessed. Instructors may ' - . 'use the exercise without incurring the manual grading.' - ), - type => 'boolean' - }, - { - var => 'pg{options}{showHintsAfter}', - doc => x('Default number of attempts before hints are shown in a problem (-1 => hide hints)'), - doc2 => x( - 'This is the default number of attempts a student must make before hints will be shown to the student. ' - . 'Set this to -1 to hide hints. Note that this can be overridden with a per problem setting.' - ), - type => 'number' - }, - { - var => 'problemGraderScore', - doc => x('Method to enter problem scores in the single problem manual grader'), - doc2 => x( - 'This configures if the single problem manual grader has inputs to enter problem scores as a percent, ' - . 'a point value, or both. Note, the problem score is always saved as a percent, so when ' - . 'using a point value, the problem score will be rounded to the nearest whole percent.' - ), - values => [qw(Percent Point Both)], - type => 'popuplist' - }, - { - var => 'pg{options}{enterKey}', - doc => x('Enter Key Behavior'), - doc2 => x( - 'If this is set to "preview", hitting the enter key on a homework problem page activates the "Preview ' - . 'My Answers" button. If this is set to "submit", then the enter key activates the "Submit ' - . 'Answers" button instead. Or if that button is not present, it will activate the "Check ' - . 'Answers" button. Or if that button is also not present, it will activate the "Preview My ' - . 'Answers" button. A third option is "conservative". In this case, the enter key behaves like ' - . '"preview" when the "Submit" button is available and there are only finitely many attempts ' - . 'allowed. Otherise the enter key behaves like "submit". Note that this is only affects ' - . 'homework problem pages, not test/quiz pages, and not instructor pages like the PG Editor ' - . 'and the Library Browser.' - ), - type => 'popuplist', - values => ['preview', 'submit', 'conservative'] - }, - { - var => 'pg{options}{automaticAnswerFeedback}', - doc => x('Show automatic answer feedback'), - doc2 => x( - 'Answer feedback will be available in problems when returning to a previously worked problem and ' - . 'after answers are available. Students will not need to click "Submit Answers" to make this ' - . 'feedback appear. Furthermore, the $showPartialCorrectAnswers variable set in some problems ' - . 'that prevents showing which of the answers are correct is ignored after the answer date.' - ), - type => 'boolean' - }, - { - var => 'pg{options}{correctRevealBtnAlways}', - doc => x('Show correct answer "Reveal" button always'), - doc2 => x( - 'A "Reveal" button must be clicked to make a correct answer visible any time that correct answers for ' - . ' a problem are shown. Note that this is always the case for instructors before answers are ' - . ' available to students, and in "Show Me Another" problems.' - ), - type => 'boolean' - } - ], - [ - x('E-Mail'), - { - var => 'mail{feedbackSubjectFormat}', - doc => x('Format for the subject line in feedback emails'), - doc2 => x( - 'When students click the Email Instructor button to send feedback, WeBWorK fills in the ' - . 'subject line. Here you can set the subject line. In it, you can have various bits of ' - . 'information filled in with the following escape sequences.

' - ), - width => 45, - type => 'text' - }, - { - var => 'mail{feedbackVerbosity}', - doc => x('E-mail verbosity level'), - doc2 => x( - 'The email verbosity level controls how much information is automatically added to feedback emails. ' - . 'Levels are
  1. Simple: send only the feedback comment and context link
  2. ' - . '
  3. Standard: as in Simple, plus user, set, problem, and PG data
  4. ' - . '
  5. Debug: as in Standard, plus the problem environment (debugging data)
  6. ' - . '
' - ), - labels => { - '0' => 'Simple', - '1' => 'Standard', - '2' => 'Debug' - }, - values => [qw(0 1 2)], - type => 'popuplist' - - }, - { - var => 'permissionLevels{receive_feedback}', - doc => x('Permission levels for receiving feedback email'), - doc2 => x( - 'Users with these permission levels will be sent feedback emails from students when they use the ' - . 'feedback button.' - ), - type => 'permission_checkboxlist', - }, - { - var => 'mail{feedbackRecipients}', - doc => x('Additional addresses for receiving feedback email'), - doc2 => x( - 'By default, feedback is sent to all users above who have permission to receive feedback. Feedback ' - . 'is also sent to any addresses specified here. Separate email address entries with commas.' - ), - type => 'list' - }, - { - var => 'feedback_by_section', - doc => x('Feedback by Section.'), - doc2 => x( - 'By default, feedback is always sent to all users specified to recieve feedback. This variable sets ' - . 'the system to only email feedback to users who have the same section as the user initiating the ' - . 'feedback. I.e., feedback will only be sent to section leaders.' - ), - type => 'boolean' - }, - ], -]; - -include('conf/LTIConfigValues.config'); - 1; #final line of the file to reassure perl that it was read properly. diff --git a/lib/WeBWorK/Authen/LTIAdvanced.pm b/lib/WeBWorK/Authen/LTIAdvanced.pm index 1d00eb9f79..c28599381c 100644 --- a/lib/WeBWorK/Authen/LTIAdvanced.pm +++ b/lib/WeBWorK/Authen/LTIAdvanced.pm @@ -365,12 +365,11 @@ sub verify_normal_user { debug("auth_result=|${auth_result}|"); - # Parameters CANNOT be modified until after LTIAdvanced authentication - # has been done, because the parameters passed with the request - # are used in computing the OAuth_signature. If there - # are any changes in $c->{paramcache} (see Controller.pm) - # before authentication occurs, then authentication will FAIL - # even if the consumer_secret is correct. + # Parameters CANNOT be modified until after LTIAdvanced authentication has + # been done, because the parameters passed with the request are used in + # computing the OAuth_signature. If there are any changes to the parameters + # before authentication occurs, then authentication will FAIL even if the + # consumer_secret is correct. $c->param("user" => $user_id); @@ -391,7 +390,6 @@ sub authenticate { debug("LTIAdvanced::authenticate called for user |$user|"); debug "ref(c) = |" . ref($c) . "|"; - debug "ref of c->{paramcache} = |" . ref($c->{paramcache}) . "|"; my $ce = $c->ce; my $db = $c->db; @@ -410,7 +408,7 @@ sub authenticate { debug("c->param(oauth_signature) = |" . $c->param("oauth_signature") . "|"); my %request_hash; - my @keys = keys %{ $c->{paramcache} }; + my @keys = $c->param; foreach my $key (@keys) { $request_hash{$key} = $c->param($key); debug("$key->|" . $request_hash{$key} . "|"); diff --git a/lib/WeBWorK/ConfigObject.pm b/lib/WeBWorK/ConfigObject.pm index cf5781f3a2..a4d83ec4a4 100644 --- a/lib/WeBWorK/ConfigObject.pm +++ b/lib/WeBWorK/ConfigObject.pm @@ -3,11 +3,12 @@ use Mojo::Base -signatures; # Base object class for all config objects -sub new ($class, $self, $c) { - # The current content generator controller object. - $self->{c} = $c; - $self->{name} = defined $self->{var} ? ($self->{var} =~ s/[{]/_/gr) =~ s/[}]//gr : $self->{setting}; - return bless $self, $class; +sub new ($class, $data, $c) { + return bless { + %$data, # Make a copy of the data. + c => $c, # The current content generator controller object. + name => defined $data->{var} ? ($data->{var} =~ s/[{]/_/gr) =~ s/[}]//gr : $data->{setting} + }, $class; } # Only input is a value to display, and should produce an html string. diff --git a/lib/WeBWorK/ConfigValues.pm b/lib/WeBWorK/ConfigValues.pm new file mode 100644 index 0000000000..303c9e9f8d --- /dev/null +++ b/lib/WeBWorK/ConfigValues.pm @@ -0,0 +1,993 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2023 The WeBWorK Project, https://github.com/openwebwork +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +package WeBWorK::ConfigValues; +use Mojo::Base 'Exporter', -signatures; + +=head1 NAME + +WeBWorK::ConfigValues - Configuration values for a course. + +=head1 DESCRIPTION + +Configuration values. These are organized by section. The following types are +allowed. + +=over + +=item text + +A text string (no quotes allowed). + +=item number + +A number. + +=item list + +A list of text strings. + +=item permission + +A permission value. + +=item boolean + +Variables which really hold 0/1 values as flags. + +=item timezone + +A time zone. + +=item time + +A time. + +=item checkboxlist + +Variables that hold a list of values which can be independently picked yes/no as +checkboxes. + +=item popuplist + +Variables that hold a list of values to be selected from. + +=back + +Each configuration value must have either a C or a C key. A var +key indicates that the configuration value is a course environment variable, and +a setting key indicates it is a setting from the course's setting table in the +database. In the latter case, only text type is presently supported since there +are not presently other types of data in the setting table that could +conceivably be up for manual reassignment. + +=cut + +use Mojo::File qw(path); + +use WeBWorK::Utils qw(x); + +our @EXPORT_OK = qw(getConfigValues); + +sub getConfigValues ($ce) { + my $configValues = [ + [ + x('General'), + { + setting => 'courseTitle', + doc => x('Title for course displayed on the Assignments page'), + type => 'text' + }, + { + var => 'courseFiles{course_info}', + doc => x('Name of course information file'), + doc2 => x( + 'The name of course information file (located in the templates directory). ' + . 'Its contents are displayed in the right panel next to the list of homework sets.' + ), + type => 'text' + }, + { + var => 'defaultTheme', + doc => x('Theme (refresh page after saving changes to reveal new theme.)'), + doc2 => x( + 'There is one main theme to choose from: math4. It has three variants: math4-green, ' + . 'math4-red, and math4-yellow. The theme specifies a unified look and feel for the ' + . 'WeBWorK course web pages.' + ), + values => [qw(math4 math4-green math4-red)], + type => 'popuplist', + hashVar => '{defaultTheme}' + }, + { + var => 'language', + doc => x('Language (refresh page after saving changes to reveal new language.)'), + doc2 => + x('WeBWorK currently has translations for the languages listed in the course configuration.'), + values => [qw(en tr es fr zh-HK he)], + type => 'popuplist' + }, + { + var => 'studentDateDisplayFormat', + doc => x('Format of dates that are displayed for students'), + doc2 => x( + 'This is the format of the dates displayed for students. This can be created from ' + . 'strftime patterns, ' + . 'e.g., "%a %b %d at %l:%M%P", or can be one of the ' + . 'localizable formats ' + . '"datetime_format_short", "datetime_format_medium", "datetime_format_long", or ' + . '"datetime_format_full".' + ), + type => 'text' + }, + { + var => 'perProblemLangAndDirSettingMode', + doc => x('Mode in which the LANG and DIR settings for a single problem are determined.'), + doc2 => x( + '

Mode in which the LANG and DIR settings for a single problem are determined.

The ' + . 'system will set the LANGuage attribute to either a value determined from the problem, a ' + . 'course-wide default, or the system default, depending on the mode selected. The tag will ' + . 'only be added to the DIV enclosing the problem if it is different than the value which ' + . 'should be set in the main HTML tag set for the entire course based on the course language.' + . '

There are two options for the DIRection attribute: "ltr" for left-to-write scripts, ' + . 'and "rtl" for right-to-left scripts like Arabic and Hebrew.

The DIRection attribute ' + . 'is needed to trigger proper display of the question text when the problem text-direction ' + . 'is different than that used by the current language of the course. For example, English ' + . 'problems from the library browser would display improperly in RTL mode for a Hebrew course, ' + . 'unless the problen Direction is set to LTR.

The feature to set a problem language and ' + . 'direction was only added in 2018 to the PG language, so most problems will not declare ' + . 'their language, and the system needs to fall back to determining the language and direction ' + . 'in a different manner. The OPL itself is all English, so the system wide fallback is to ' + . 'en-US in LTR mode.

Since the defaults fall back to the LTR direction, most sites ' + . 'should be fine with the "auto::" mode, but may want to select the one which matches their ' + . 'course language. The mode "force::ltr" would also be an option for a course which runs into ' + . 'trouble with the "auto" modes.

Modes:

  • "none" prevents any additional LANG ' + . 'and/or DIR tag being added. The browser will use the main setting which was applied to the ' + . 'entire HTML page. This is likely to cause trouble when a problem of the other direction is ' + . 'displayed.
  • "auto::" allows the system to make the settings based on the language ' + . 'and direction reported by the problem (a new feature, so not set in almost all existing ' + . 'problems) and falling back to the expected default of en-US in LTR mode.
  • ' + . '
  • "auto:LangCode:Dir" allows the system to make the settings based on the language and ' + . 'direction reported by the problem (a new feature, so not set in almost all existing ' + . 'problems) but falling back to the language with the given LangCode and the direction Dir ' + . 'when problem settings are not available from PG.
  • "auto::Dir" for problems without ' + . 'PG settings, this will use the default en=english language, but force the direction to ' + . 'Dir. Problems with PG settings will get those settings.
  • "auto:LangCode:" for ' + . 'problems without PG settings, this will use the default LTR direction, but will set the ' + . 'language to LangCode.Problems with PG settings will get those settings.
  • ' + . '
  • "force:LangCode:Dir" will ignore any setting made by the PG code of the problem, ' + . 'and will force the system to set the language with the given LangCode and the direction to ' + . 'Dir for all problems.
  • "force::Dir" will ignore any setting made by ' + . 'the PG code of the problem, and will force the system to set the direction to Dir for ' + . 'all problems, but will avoid setting any language attribute for individual ' + . 'problem.
' + ), + values => [ + qw(none auto:: force::ltr force::rtl force:en:ltr auto:en:ltr force:tr:ltr auto:tr:ltr force:es:ltr + auto:es:ltr force:fr:ltr auto:fr:ltr force:zh_hk:ltr auto:zh_hk:ltr force:he:rtl auto:he:rtl) + ], + type => 'popuplist' + }, + { + var => 'sessionKeyTimeout', + doc => x('Inactivity time before a user is required to login again'), + doc2 => x( + 'Length of time, in seconds, a user has to be inactive before he is required to login again. ' + . 'This value should be entered as a number, so as 3600 instead of 60*60 for one hour.' + ), + type => 'number' + }, + { + var => 'siteDefaults{timezone}', + doc => x('Timezone for the course'), + doc2 => x( + '

Some servers handle courses taking place in different timezones. If this course is not ' + . 'showing the correct timezone, enter the correct value here. The format consists of unix ' + . 'times, such as "America/New_York", "America/Chicago", "America/Denver", "America/Phoenix" ' + . 'or "America/Los_Angeles".

Complete list: ' + . 'TimeZoneFiles' + ), + type => 'timezone', + hashVar => '{siteDefaults}->{timezone}' + }, + { + var => 'hardcopyThemes', + doc => x('Enabled Site Hardcopy Themes'), + doc2 => x( + 'Choose which of the site PDF hardcopy themes are available. In addition to the themes selected ' + . 'here, all themes in the course hardcopyThemes folder will be available. Your selection ' + . 'must be saved and then the page must be reloaded before the new list of enabled themes ' + . 'will be reflected in the selections that follows.' + ), + values => [qw(empty.xml)], + type => 'checkboxlist', + min => 1, + hashVar => '{hardcopyThemes}' + }, + { + var => 'hardcopyTheme', + doc => x('Hardcopy Theme'), + doc2 => x('Choose a layout/styling theme for PDF hardcopy production.'), + values => [qw(empty.xml)], + type => 'popuplist', + hashVar => '{hardcopyTheme}' + }, + { + var => 'hardcopyThemePGEditor', + doc => x('Hardcopy Theme for Problem Editor'), + doc2 => x('Choose a layout/styling theme for PDF hardcopy production from the Prooblem Editor.'), + values => [qw(empty.xml)], + type => 'popuplist', + hashVar => '{hardcopyThemePGEditor}' + }, + { + var => 'showCourseHomeworkTotals', + doc => x('Show Total Homework Grade on Grades Page'), + doc2 => x( + 'When this is on students will see a line on the Grades page which has their total cumulative ' + . 'homework score. This score includes all sets assigned to the student.' + ), + type => 'boolean' + }, + { + var => 'pg{options}{enableProgressBar}', + doc => x('Enable Progress Bar and current problem highlighting'), + doc2 => x( + 'A switch to govern the use of a Progress Bar for the student; this also enables/disables the ' + . 'highlighting of the current problem in the side bar, and whether it is correct (✓), ' + . 'in progress (…), incorrect (✗), or unattempted (no symbol).' + ), + type => 'boolean' + }, + { + var => 'pg{timeAssignDue}', + doc => x('Default Time that the Assignment is Due'), + doc2 => x( + 'The time of the day that the assignment is due. This can be changed on an individual basis, ' + . 'but WeBWorK will use this value for default when a set is created.' + ), + type => 'time', + hashVar => '{pg}->{timeAssignDue}' + }, + { + var => 'pg{assignOpenPriorToDue}', + doc => x('Default Amount of Time (in minutes) before Due Date that the Assignment is Open'), + doc2 => x( + 'The amount of time (in minutes) before the due date when the assignment is opened. You can ' + . 'change this for individual homework, but WeBWorK will use this value when a set is created.' + ), + type => 'number', + hashVar => '{pg}->{assignOpenPriorToDue}' + }, + { + var => 'pg{answersOpenAfterDueDate}', + doc => x('Default Amount of Time (in minutes) after Due Date that Answers are Open'), + doc2 => x( + 'The amount of time (in minutes) after the due date that the Answers are available to student to ' + . 'view. You can change this for individual homework, but WeBWorK will use this value when ' + . 'a set is created.' + ), + type => 'number', + hashVar => '{pg}->{answersOpenAfterDueDate}' + }, + ], + [ + x('Optional Modules'), + { + var => 'achievementsEnabled', + doc => x('Enable Course Achievements'), + doc2 => x( + 'Activiating this will enable Mathchievements for webwork. Mathchievements can be managed ' + . 'by using the Achievements Manager link.' + ), + type => 'boolean' + }, + { + var => 'achievementPointsPerProblem', + doc => x('Achievement Points Per Problem'), + doc2 => x('This is the number of achievement points given to each user for completing a problem.'), + type => 'number' + }, + { + var => 'achievementPointsPerProblemReduced', + doc => x('Achievement Points Per Problem in Reduced Scoring Period'), + doc2 => x( + 'This is the number of achievement points given to each user for completing a problem if the ' + . 'problem is in a set that is in the reduced scoring period.' + ), + type => 'number' + }, + { + var => 'achievementItemsEnabled', + doc => x('Enable Achievement Rewards'), + doc2 => x( + 'Activating this will enable achievement rewards. This feature allows students to earn rewards by ' + . 'completing achievements that allow them to affect their homework in a limited way.' + ), + type => 'boolean' + }, + { + var => 'achievementExcludeSet', + doc => x('List of sets excluded from achievements'), + doc2 => x( + 'Comma separated list of set names that are excluded from all achievements. ' + . 'No achievement points and badges can be earned for submitting problems in these sets. ' + . 'Note that underscores (_) must be used for spaces in set names.' + ), + type => 'list' + }, + { + var => 'mail{achievementEmailFrom}', + doc => x('Email address to use when sending Achievement notifications.'), + doc2 => x( + 'This email address will be used as the sender for achievement notifications. ' + . 'Achievement notifications will not be sent unless this is set.' + ), + width => 45, + type => 'text' + }, + { + var => 'options{enableConditionalRelease}', + doc => x('Enable Conditional Release'), + doc2 => x( + 'Enables the use of the conditional release system. To use conditional release you need to ' + . 'specify a list of set names on the Problem Set Detail Page, along with a minimum score. ' + . 'Students will not be able to access that homework set until they have achieved the ' + . 'minimum score on all of the listed sets.' + ), + type => 'boolean' + }, + { + var => 'pg{ansEvalDefaults}{enableReducedScoring}', + doc => x('Enable Reduced Scoring'), + doc2 => x( + '

This sets whether the Reduced Scoring system will be enabled. If enabled you will need ' + . 'to set the default length of the reduced scoring period and the value of work done in ' + . 'the reduced scoring period below.

To use this, you also have to enable Reduced ' + . 'Scoring for individual assignments and set their Reduced Scoring Dates by editing the ' + . 'set data.

This works with the avg_problem_grader (which is the the default grader) ' + . 'and the std_problem_grader (the all or nothing grader). It will work with custom graders ' + . 'if they are written appropriately.

' + ), + type => 'boolean' + }, + { + var => 'pg{ansEvalDefaults}{reducedScoringValue}', + doc => x('Value of work done in Reduced Scoring Period'), + doc2 => x( + '

After the Reduced Scoring Date all additional work done by the student counts at a reduced ' + . 'rate. Here is where you set the reduced rate which must be a percentage. For example if ' + . 'this value is 50% and a student views a problem during the Reduced Scoring Period, they ' + . 'will see the message "You are in the Reduced Scoring Period: All additional work done ' + . 'counts 50% of the original."

To use this, you also have to enable Reduced Scoring ' + . 'and set the Reduced Scoring Date for individual assignments by editing the set data ' + . 'using the Sets Manager.

This works with the avg_problem_grader (which is the the ' + . 'default grader) and the std_problem_grader (the all or nothing grader). It will work ' + . 'with custom graders if they are written appropriately.

' + ), + labels => { + '0.1' => '10%', + '0.15' => '15%', + '0.2' => '20%', + '0.25' => '25%', + '0.3' => '30%', + '0.35' => '35%', + '0.4' => '40%', + '0.45' => '45%', + '0.5' => '50%', + '0.55' => '55%', + '0.6' => '60%', + '0.65' => '65%', + '0.7' => '70%', + '0.75' => '75%', + '0.8' => '80%', + '0.85' => '85%', + '0.9' => '90%', + '0.95' => '95%', + '1' => '100%' + }, + values => [qw(1 0.95 0.9 0.85 0.8 0.75 0.7 0.65 0.6 0.55 0.5 0.45 0.4 0.35 0.3 0.25 0.2 0.15 0.1)], + type => 'popuplist' + }, + { + var => 'pg{ansEvalDefaults}{reducedScoringPeriod}', + doc => x('Default Length of Reduced Scoring Period in minutes'), + doc2 => x( + 'The Reduced Scoring Period is the default period before the due date during which all ' + . 'additional work done by the student counts at a reduced rate. When enabling reduced ' + . 'scoring for a set the reduced scoring date will be set to the due date minus this ' + . 'number. The reduced scoring date can then be changed. If the Reduced Scoring is enabled ' + . 'and if it is after the reduced scoring date, but before the due date, a message like ' + . '"This assignment has a Reduced Scoring Period that begins 11/08/2009 at 06:17pm EST ' + . 'and ends on the due date, 11/10/2009 at 06:17pm EST. During this period all additional ' + . 'work done counts 50% of the original." will be displayed.' + ), + type => 'number' + }, + { + var => 'pg{options}{enableShowMeAnother}', + doc => x('Enable Show Me Another button'), + doc2 => x( + 'Enables use of the Show Me Another button, which offers the student a newly-seeded version ' + . 'of the current problem, complete with solution (if it exists for that problem).' + ), + type => 'boolean' + }, + { + var => 'pg{options}{showMeAnotherDefault}', + doc => x('Default number of attempts before Show Me Another can be used (-1 => Never)'), + doc2 => x( + 'This is the default number of attempts before show me another becomes available to students. ' + . 'It can be set to -1 to disable show me another by default.' + ), + type => 'number' + }, + { + var => 'pg{options}{showMeAnotherMaxReps}', + doc => x('Maximum times Show me Another can be used per problem (-1 => unlimited)'), + doc2 => x( + 'The Maximum number of times Show me Another can be used per problem by a student. ' + . 'If set to -1 then there is no limit to the number of times that Show Me Another can be used.' + ), + type => 'number' + }, + { + var => 'pg{options}{showMeAnother}', + doc => x('List of options for Show Me Another button'), + doc2 => x( + '
  • SMAcheckAnswers: enables the Check Answers button for the new problem when ' + . 'Show Me Another is clicked
  • SMAshowSolutions: shows walk-through solution ' + . 'for the new problem when Show Me Another is clicked; a check is done first to make ' + . 'sure that a solution exists
  • SMAshowCorrect: correct answers for the new ' + . 'problem can be viewed when Show Me Another is clicked; note that SMAcheckAnswers' + . 'needs to be enabled at the same time
  • SMAshowHints: show hints for the new ' + . 'problem (assuming they exist)
Note: there is very little point enabling the ' + . 'button unless you check at least one of these options - the students would simply see a new ' + . 'version that they can not attempt or learn from.' + ), + min => 0, + values => [ 'SMAcheckAnswers', 'SMAshowSolutions', 'SMAshowCorrect', 'SMAshowHints' ], + type => 'checkboxlist' + }, + { + var => 'pg{options}{enablePeriodicRandomization}', + doc => x('Enable periodic re-randomization of problems'), + doc2 => x( + 'Enables periodic re-randomization of problems after a given number of attempts. Student would ' + . 'have to click Request New Version to obtain new version of the problem and to continue ' + . 'working on the problem' + ), + type => 'boolean' + }, + { + var => 'pg{options}{periodicRandomizationPeriod}', + doc => x('The default number of attempts between re-randomization of the problems ( 0 => never)'), + doc2 => x('The default number of attempts before the problem is re-randomized. ( 0 => never )'), + type => 'number' + }, + { + var => 'pg{options}{showCorrectOnRandomize}', + doc => x('Show the correct answer to the current problem before re-randomization.'), + doc2 => x( + 'Show the correct answer to the current problem on the last attempt before a new version is ' + . 'requested.' + ), + type => 'boolean' + }, + ], + [ + x('Permissions'), + { + var => 'permissionLevels{login}', + doc => x('Allowed to login to the course'), + type => 'permission' + }, + { + var => 'permissionLevels{change_password}', + doc => x('Allowed to change their password'), + doc2 => x( + 'Users at this level and higher are allowed to change their password. ' + . 'Normally guest users are not allowed to change their password.' + ), + type => 'permission' + }, + { + var => 'permissionLevels{become_student}', + doc => x('Allowed to act as another user'), + type => 'permission' + }, + { + var => 'permissionLevels{submit_feedback}', + doc => x('Can email instructor'), + doc2 => x('Only this permission level and higher get buttons for sending email to the instructor.'), + type => 'permission' + }, + { + var => 'permissionLevels{record_answers_when_acting_as_student}', + doc => x('Can submit answers for a student'), + doc2 => x( + 'When acting as a student, this permission level and higher can submit answers for that student.'), + type => 'permission' + }, + { + var => 'permissionLevels{report_bugs}', + doc => x('Can report bugs'), + doc2 => x( + 'Users with at least this permission level get a link in the left panel for reporting bugs to the ' + . 'bug tracking system at bugs.webwork.maa.org.' + ), + type => 'permission' + }, + { + var => 'permissionLevels{change_email_address}', + doc => x('Allowed to change their email address'), + doc2 => x( + 'Users at this level and higher are allowed to change their email address. Normally guest ' + . 'users are not allowed to change the email address since it does not make sense to send ' + . 'email to anonymous accounts.' + ), + type => 'permission' + }, + { + var => 'permissionLevels{change_pg_display_settings}', + doc => x('Allowed to change display settings used in pg problems'), + doc2 => x( + 'Users at this level and higher are allowed to change display settings used in pg problems.' + . 'Note that if it is expected that there will be students that have vision impairments and ' + . 'MathQuill is enabled to assist with answer entry, then you should not set this ' + . 'permission to a level above student as those students may need to disable MathQuill.' + ), + type => 'permission' + }, + { + var => 'permissionLevels{view_answers}', + doc => x('Allowed to view past answers'), + doc2 => x('These users and higher get the "Show Past Answers" button on the problem page.'), + type => 'permission' + }, + { + var => 'permissionLevels{view_unopened_sets}', + doc => x('Allowed to view problems in sets which are not open yet'), + type => 'permission' + }, + { + var => 'permissionLevels{show_correct_answers_before_answer_date}', + doc => x('Allowed to see the correct answers before the answer date'), + type => 'permission' + }, + { + var => 'permissionLevels{show_solutions_before_answer_date}', + doc => x('Allowed to see solutions before the answer date'), + type => 'permission' + }, + { + var => 'permissionLevels{can_show_old_answers}', + doc => x('Can show old answers'), + doc2 => x( + 'When viewing a problem, WeBWorK usually puts the previously submitted answer in the answer ' + . 'blank. Below this level, old answers are never shown. Typically, that is the desired ' + . 'behaviour for guest accounts.' + ), + type => 'permission' + }, + { + var => 'permissionLevels{navigation_allowed}', + doc => x('Allowed to view course home page'), + doc2 => x( + 'If a user does not have this permission, then the user will not be allowed to navigate to the ' + . 'course home page, i.e., the Assignments page. This should only be used for a course when LTI ' + . 'authentication is used, and is most useful when LTIGradeMode is set to homework. In this case ' + . 'the Assignments page is not useful and can even be confusing to students. To use this feature ' + . 'set this permission to "login_proctor".' + ), + type => 'permission' + }, + ], + [ + x('Problem Display/Answer Checking'), + { + var => 'pg{displayModes}', + doc => x('List of display modes made available to students'), + doc2 => x( + '

When viewing a problem, users may choose different methods of rendering formulas via an ' + . 'options box in the left panel. Here, you can adjust what display modes are listed.

' + . '

The display modes are

  • plainText: shows the raw LaTeX strings for formulas.' + . '
  • images: produces images using the external programs LaTeX and dvipng.
  • ' + . '
  • MathJax: uses javascript to render mathematics.

You must use at least ' + . 'one display mode. If you select only one, then the options box will not give a choice of ' + . 'modes (since there will only be one active).

' + ), + min => 1, + values => [ 'MathJax', 'images', 'plainText' ], + type => 'checkboxlist' + }, + { + var => 'pg{options}{displayMode}', + doc => x('The default display mode'), + doc2 => x( + 'Enter one of the allowed display mode types above. See \'display modes entry\' for descriptions.'), + min => 1, + values => [qw(MathJax images plainText)], + type => 'popuplist' + }, + { + var => 'pg{specialPGEnvironmentVars}{entryAssist}', + doc => x('Assist with the student answer entry process.'), + doc2 => x( + '

MathQuill renders students answers in real-time as they type on the keyboard.

MathView ' + . 'allows students to choose from a variety of common math structures (such as fractions and ' + . 'square roots) as they attempt to input their answers.

' + ), + min => 1, + values => [qw(None MathQuill MathView)], + type => 'popuplist' + }, + { + var => 'pg{options}{showEvaluatedAnswers}', + doc => x('Display the evaluated student answer'), + doc2 => x( + 'Set to true to display the "Entered" column which automatically shows the evaluated student ' + . 'answer, e.g., 1 if student input is sin(pi/2). If this is set to false, e.g., to save ' + . 'space in the response area, the student can still see their evaluated answer by clicking ' + . 'on the typeset version of their answer.' + ), + type => 'boolean' + }, + { + var => 'pg{ansEvalDefaults}{useBaseTenLog}', + doc => x('Use log base 10 instead of base e'), + doc2 => x('Set to true for log to mean base 10 log and false for log to mean natural logarithm.'), + type => 'boolean' + }, + { + var => 'pg{specialPGEnvironmentVars}{useOldAnswerMacros}', + doc => x('Use older answer checkers'), + doc2 => x( + '

During summer 2005, a newer version of the answer checkers was implemented for answers ' + . 'which are functions and numbers. The newer checkers allow more functions in student ' + . 'answers, and behave better in certain cases. Some problems are specifically coded to ' + . 'use new (or old) answer checkers. However, for the bulk of the problems, you can ' + . 'choose what the default will be here.

Choosing false here means that the ' + . 'newer answer checkers will be used by default, and choosing true means that the ' + . 'old answer checkers will be used by default.

' + ), + type => 'boolean' + }, + { + var => 'pg{specialPGEnvironmentVars}{parseAlternatives}', + doc => x('Allow Unicode alternatives in student answers'), + doc2 => x( + 'Set to true to allow students to enter Unicode versions of some characters (like U+2212 for the ' + . 'minus sign) in their answers. One reason to allow this is that copying and pasting output ' + . 'from MathJax can introduce these characters, but it is also getting easier to enter these ' + . 'characters directory from the keyboard.' + ), + type => 'boolean' + }, + { + var => 'pg{specialPGEnvironmentVars}{convertFullWidthCharacters}', + doc => x('Automatically convert Full Width Unicode characters to their ASCII equivalents'), + doc2 => x( + 'Set to true to have Full Width Unicode character (U+FF01 to U+FF5E) converted to their ASCII ' + . 'equivalents (U+0021 to U+007E) automatically in MathObjects. This may be valuable for ' + . 'Chinese keyboards, for example, that automatically use Full Width characters for ' + . 'parentheses and commas.' + ), + type => 'boolean' + }, + { + var => 'pg{ansEvalDefaults}{numRelPercentTolDefault}', + doc => x('Allowed error, as a percentage, for numerical comparisons'), + doc2 => x( + 'When numerical answers are checked, most test if the student\'s answer is close enough to the ' + . 'programmed answer be computing the error as a percentage of the correct answer. This ' + . 'value controls the default for how close the student answer has to be in order to be ' + . 'marked correct.

A value such as 0.1 means 0.1 percent error is allowed.

' + ), + type => 'number' + }, + { + var => 'pg{specialPGEnvironmentVars}{waiveExplanations}', + doc => x('Skip explanation essay answer fields'), + doc2 => x( + 'Some problems have an explanation essay answer field, typically following a simpler answer ' + . 'field. For example, find a certain derivative using the definition. An answer blank ' + . 'would be present for the derivative to be automatically checked, and then there would ' + . 'be a separate essay answer field to show the steps of actually using the definition of ' + . 'the derivative, to be scored manually. With this setting, the essay explanation fields ' + . 'are supperessed. Instructors may use the exercise without incurring the manual grading.' + ), + type => 'boolean' + }, + { + var => 'pg{options}{showHintsAfter}', + doc => x('Default number of attempts before hints are shown in a problem (-1 => hide hints)'), + doc2 => x( + 'This is the default number of attempts a student must make before hints will be shown to the ' + . 'student. Set this to -1 to hide hints. Note that this can be overridden with a per ' + . 'problem setting.' + ), + type => 'number' + }, + { + var => 'problemGraderScore', + doc => x('Method to enter problem scores in the single problem manual grader'), + doc2 => x( + 'This configures if the single problem manual grader has inputs to enter problem scores as ' + . 'a percent, a point value, or both. Note, the problem score is always saved as a ' + . 'percent, so when using a point value, the problem score will be rounded to the ' + . 'nearest whole percent.' + ), + values => [qw(Percent Point Both)], + type => 'popuplist' + }, + { + var => 'pg{options}{enterKey}', + doc => x('Enter Key Behavior'), + doc2 => x( + 'If this is set to "preview", hitting the enter key on a homework problem page activates the ' + . '"Preview My Answers" button. If this is set to "submit", then the enter key activates ' + . 'the "Submit Answers" button instead. Or if that button is not present, it will activate ' + . 'the "Check Answers" button. Or if that button is also not present, it will activate ' + . 'the "Preview My Answers" button. A third option is "conservative". In this case, the ' + . 'enter key behaves like "preview" when the "Submit" button is available and there are ' + . 'only finitely many attempts allowed. Otherise the enter key behaves like "submit". ' + . 'Note that this is only affects homework problem pages, not test/quiz pages, and not ' + . 'instructor pages like the PG Editor and the Library Browser.' + ), + type => 'popuplist', + values => [ 'preview', 'submit', 'conservative' ] + }, + { + var => 'pg{options}{automaticAnswerFeedback}', + doc => x('Show automatic answer feedback'), + doc2 => x( + 'Answer feedback will be available in problems when returning to a previously worked problem and ' + . 'after answers are available. Students will not need to click "Submit Answers" to make this ' + . 'feedback appear. Furthermore, the $showPartialCorrectAnswers variable set in some problems ' + . 'that prevents showing which of the answers are correct is ignored after the answer date.' + ), + type => 'boolean' + }, + { + var => 'pg{options}{correctRevealBtnAlways}', + doc => x('Show correct answer "Reveal" button always'), + doc2 => x( + 'A "Reveal" button must be clicked to make a correct answer visible any time that correct ' + . 'answers for a problem are shown. Note that this is always the case for instructors ' + . 'before answers are available to students, and in "Show Me Another" problems.' + ), + type => 'boolean' + } + ], + [ + x('E-Mail'), + { + var => 'mail{feedbackSubjectFormat}', + doc => x('Format for the subject line in feedback emails'), + doc2 => x( + 'When students click the Email Instructor button to send feedback, WeBWorK fills in the ' + . 'subject line. Here you can set the subject line. In it, you can have various bits of ' + . 'information filled in with the following escape sequences.

  • %c = course ID
  • ' + . '
  • %u = user ID
  • %s = set ID
  • %p = problem ID
  • %x = section
  • ' + . '
  • %r = recitation
  • %% = literal percent sign
' + ), + width => 45, + type => 'text' + }, + { + var => 'mail{feedbackVerbosity}', + doc => x('E-mail verbosity level'), + doc2 => x( + 'The email verbosity level controls how much information is automatically added to feedback ' + . 'emails. Levels are
  1. Simple: send only the feedback comment and context link
  2. ' + . '
  3. Standard: as in Simple, plus user, set, problem, and PG data
  4. ' + . '
  5. Debug: as in Standard, plus the problem environment (debugging data)
' + ), + labels => { + '0' => 'Simple', + '1' => 'Standard', + '2' => 'Debug' + }, + values => [qw(0 1 2)], + type => 'popuplist' + + }, + { + var => 'permissionLevels{receive_feedback}', + doc => x('Permission levels for receiving feedback email'), + doc2 => x( + 'Users with these permission levels will be sent feedback emails from students when they use the ' + . 'feedback button.' + ), + type => 'permission_checkboxlist', + }, + { + var => 'mail{feedbackRecipients}', + doc => x('Additional addresses for receiving feedback email'), + doc2 => x( + 'By default, feedback is sent to all users above who have permission to receive feedback. Feedback ' + . 'is also sent to any addresses specified here. Separate email address entries with commas.' + ), + type => 'list' + }, + { + var => 'feedback_by_section', + doc => x('Feedback by Section.'), + doc2 => x( + 'By default, feedback is always sent to all users specified to recieve feedback. This ' + . 'variable sets the system to only email feedback to users who have the same section as ' + . 'the user initiating the feedback. I.e., feedback will only be sent to section leaders.' + ), + type => 'boolean' + }, + ], + ]; + + # These are the LTI authentication variables that may be added to the 'LTI' tab on the Course Configuration page. + # These are added if the variables near the end of authen_LTI.conf are set. + my $LTIConfigValues = { + 'LTI{v1p1}{LMS_name}' => { + var => 'LTI{v1p1}{LMS_name}', + doc => x('The name of the LMS'), + doc2 => x( + 'The name of the LMS. This is used in messages to users that direct them to go back to ' + . 'the LMS to access something in the WeBWorK course.' + ), + type => 'text' + }, + 'LTI{v1p1}{LMS_url}' => { + var => 'LTI{v1p1}{LMS_url}', + doc => x('A URL for the LMS'), + doc2 => x( + 'An address that can be used to log in to the LMS. This is used in messages to users ' + . 'that direct them to go back to the LMS to access something in the WeBWorK course.' + ), + type => 'text', + width => 30, + }, + external_auth => { + var => 'external_auth', + doc => x('Require users to log in through the LMS'), + doc2 => x( + 'If this is set, all users (including the instructor) must enter the WeBWorK course through the LMS. ' + . 'If a user reaches the regular WeBWorK login screen, they receive a message directing them ' + . 'back to the LMS.' + ), + type => 'boolean' + }, + LTIGradeMode => { + var => 'LTIGradeMode', + doc => x('Grade passback mode'), + doc2 => x( + 'Sets how grades will be passed back from WeBWorK to the LMS.
course
Sends a single ' + . 'grade back to the LMS. This grade is calculated out of the total question set that has been ' + . 'assigned to a user and made open. Therefore it can appear low, since it counts problem sets ' + . 'with future due dates as zero.
homework
Sends back a score for each problem ' + . 'set (including for each quiz). To use this, the external links from the LMS must be problem ' + . 'set specific. For example, webwork.myschool.edu/webwork2/course-name/problem_set_name' + . '. If the problem set name has space characters, they should be underscores in these ' + . 'addresses. Also, to initialize the communication between WeBWorK and the LMS, the user must ' + . 'follow each of these external learning tools at least one time. Since there must be a ' + . 'separate external tool link for each problem set, this option requires more maintenance ' + . 'of the LMS course.
' + ), + values => [ '', qw(course homework) ], + labels => { '' => 'None', 'course' => 'Course', 'homework' => 'Homework' }, + type => 'popuplist' + }, + LMSManageUserData => { + var => 'LMSManageUserData', + doc => x('Allow the LMS to update user account data'), + doc2 => x( + 'WeBWorK will automatically create users when logging in via the LMS for the first time. If ' + . 'this flag is enabled then it will also keep the user account data (first name, last ' + . 'name, section, recitation) up to date with the LMS. If a user\'s information changes ' + . 'in the LMS then it will change in WeBWorK. However, any changes to the user data via ' + . 'WeBWorK will be overwritten the next time the user logs in.' + ), + type => 'boolean' + }, + debug_lti_parameters => { + var => 'debug_lti_parameters', + doc => x('Show LTI parameters (for debugging)'), + doc2 => x( + 'When this is true, then when a user enters WeBWorK from an external tool link in the LMS, ' + . 'the bottom of the screen will display the data that the LMS passed to WeBWorK. This may ' + . 'be useful to debug LTI, especially because different LMS systems have different parameters.' + ), + type => 'boolean' + }, + }; + + # Get the list of theme folders in the theme directory. + my $themes = eval { path($ce->{webworkDirs}{themes})->list({ dir => 1 })->map('basename')->sort; }; + die "can't opendir $ce->{webworkDirs}{themes}: $@" if $@; + + # Get the list of all site hardcopy theme files. + my $hardcopyThemesSite = + eval { path($ce->{webworkDirs}{hardcopyThemes})->list->grep(qr/\.xml$/)->map('basename')->sort }; + die "Unabled to list files in $ce->{webworkDirs}{hardcopyThemes}: $@" if $@; + + my $hardcopyThemesCourse = eval { + path($ce->{courseDirs}{hardcopyThemes})->list->grep(sub { + /\.xml$/ + && eval { + # Check that the file is valid XML. + XML::LibXML->load_xml(location => $_->to_string); + 1; + }; + })->map('basename'); + } || []; + + # Get unique file names, merging lists from site and course folders. + my $hardcopyThemes = [ + sort(do { + my %seen; + grep { !$seen{$_}++ } (@$hardcopyThemesSite, @$hardcopyThemesCourse); + }) + ]; + + # Get enabled site themes plus all course themes. + my $hardcopyThemesAvailable = [ + sort(do { + my %seen; + grep { !$seen{$_}++ } @{ $ce->{hardcopyThemes} }, @$hardcopyThemesCourse; + }) + ]; + + # Get list of localization dictionaries. + my $languages = eval { + my %seen; + path($ce->{webworkDirs}{localize})->list->grep(qr/\.mo$|\.po$/)->map(sub { $_->basename =~ s/\.[pm]o$//r }) + ->grep(sub { !$seen{$_}++ }); + }; + die "Unable to list files in $ce->{webworkDirs}{localize}: $@" if $@; + + for my $oneConfig (@$configValues) { + for my $item (@$oneConfig) { + next unless ref($item) eq 'HASH' && $item->{var}; + + $item->{values} = $themes if $item->{var} eq 'defaultTheme'; + $item->{values} = $hardcopyThemesAvailable + if $item->{var} eq 'hardcopyTheme' || $item->{var} eq 'hardcopyThemePGEditor'; + $item->{values} = $hardcopyThemesSite if $item->{var} eq 'hardcopyThemes'; + + $item->{values} = $languages if $item->{var} eq 'language'; + } + } + + if ($ce->{LTIVersion} && ref($ce->{LTIConfigVariables}) eq 'ARRAY' && @{ $ce->{LTIConfigVariables} }) { + if ($ce->{LTIVersion} eq 'v1p3') { + $LTIConfigValues->{'LTI{v1p3}{LMS_name}'} = + { %{ delete $LTIConfigValues->{'LTI{v1p1}{LMS_name}'} }, var => 'LTI{v1p3}{LMS_name}' }; + $LTIConfigValues->{'LTI{v1p3}{LMS_url}'} = + { %{ delete $LTIConfigValues->{'LTI{v1p1}{LMS_url}'} }, var => 'LTI{v1p3}{LMS_url}' }; + } + + push( + @$configValues, + [ + x('LTI'), + map { $LTIConfigValues->{$_} } + grep { defined $LTIConfigValues->{$_} } @{ $ce->{LTIConfigVariables} } + ] + ); + } + + return $configValues; +} + +1; diff --git a/lib/WeBWorK/ContentGenerator/Instructor/Config.pm b/lib/WeBWorK/ContentGenerator/Instructor/Config.pm index c5a7bbf9ad..c475d7b943 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/Config.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/Config.pm @@ -35,18 +35,7 @@ use WeBWorK::ConfigObject::permission_checkboxlist; use WeBWorK::ConfigObject::list; use WeBWorK::ConfigObject::checkboxlist; use WeBWorK::ConfigObject::popuplist; - -# Configuation data -# It is organized by section. The allowable types are -# 'Text' for a text string, -# 'Number' for a number, -# 'List' for a list of text strings, -# 'Permission' for a permission value, -# 'Boolean' for variables which really hold 0/1 values as flags, -# 'TimeZone' for a time zone, -# 'Time' for a time, -# 'CheckboxList' for variables that hold a list of values which can be independently picked yes/no as checkboxes, -# 'PopupList' for variables that hold a list of values to be selected from. +use WeBWorK::ConfigValues qw(getConfigValues); # Write contents to outputFilePath and return error messages if any. sub writeFile ($outputFilePath, $contents) { @@ -88,150 +77,56 @@ sub generate_navigation_tabs ($c, $current_tab, @tab_names) { return $c->tag('nav', class => 'config-tabs nav nav-pills justify-content-center my-4', $tabs->join('')); } -sub getConfigValues ($c, $ce) { - my $configValues = $ce->{ConfigValues}; +sub pre_header_initialize ($c) { + $c->stash->{configValues} = []; - # Get the list of theme folders in the theme directory and remove . and .. and 'layouts'. - my $themeDir = $ce->{webworkDirs}{themes}; - opendir(my $dh, $themeDir) || die "can't opendir $themeDir: $!"; - my $themes = [ grep { !/^\.{1,2}$/ && $_ ne 'layouts' } sort readdir($dh) ]; + return unless $c->authz->hasPermissions($c->param('user'), 'modify_problem_sets'); - # Get the list of all site hardcopy theme files - opendir(my $dhS, $ce->{webworkDirs}{hardcopyThemes}) || die "can't opendir $ce->{webworkDirs}{hardcopyThemes}: $!"; - my $hardcopyThemesSite = [ grep {/\.xml$/} (sort readdir($dhS)) ]; - my @files; - if (opendir(my $dhC, $ce->{courseDirs}{hardcopyThemes})) { - @files = grep { /\.xml$/ && !/^\./ } sort readdir($dhC); - } - my @hardcopyThemesCourse; - for my $hardcopyTheme (@files) { - eval { - # check that file is valid XML - my $themeTree = XML::LibXML->load_xml(location => "$ce->{courseDirs}{hardcopyThemes}/$hardcopyTheme"); - push(@hardcopyThemesCourse, $hardcopyTheme); - }; - } - # get unique file names, merging lists from site and course folders - my $hardcopyThemes = [ - sort(do { - my %seen; - grep { !$seen{$_}++ } (@$hardcopyThemesSite, @hardcopyThemesCourse); - }) - ]; - # get enabled site themes plus all course themes - my $hardcopyThemesAvailable = [ - sort(do { - my %seen; - grep { !$seen{$_}++ } (@{ $ce->{hardcopyThemes} }, @hardcopyThemesCourse); - }) - ]; - - # get list of localization dictionaries - my $localizeDir = $ce->{webworkDirs}{localize}; - opendir(my $dh2, $localizeDir) || die "can't opendir $localizeDir: $!"; - my %seen = (); # find the languages in the localize direction - my $languages = [ - grep { !$seen{$_}++ } # remove duplicate items - map { $_ =~ s/\.[pm]o$//r } # get rid of suffix - grep {/\.mo$|\.po$/} sort readdir($dh2) #look at only .mo and .po files - - ]; - - # insert the anonymous array of theme names into configValues - # FIXME? Is there a reason this is an array? Couldn't we replace this - # with a hash and conceptually simplify this routine? MEG - my $modifyThemes = sub { - my $item = shift; - if ( - ref($item) =~ /HASH/ - && defined $item->{var} - && ($item->{var} =~ - /^(defaultTheme|hardcopyThemesSite|hardcopyThemes|hardcopyTheme|hardcopyThemePGEditor)$/) - ) - { - $item->{values} = $themes if ($item->{var} eq 'defaultTheme'); - $item->{values} = $hardcopyThemesAvailable - if ($item->{var} eq 'hardcopyTheme' || $item->{var} eq 'hardcopyThemePGEditor'); - $item->{values} = $hardcopyThemesSite if ($item->{var} eq 'hardcopyThemes'); - } - }; - my $modifyLanguages = sub { - my $item = shift; - if (ref($item) =~ /HASH/ && defined $item->{var} && $item->{var} eq 'language') { - $item->{values} = $languages; - } - }; - foreach my $oneConfig (@$configValues) { - foreach my $hash (@$oneConfig) { - &$modifyThemes($hash); - &$modifyLanguages($hash); - } - } + my $ce = $c->ce; + $c->stash->{configValues} = getConfigValues($ce); - if (!$ce->{LTIVersion}) { - # If LTI authentication is not enabled for this course, then remove the LTI tab. - $configValues = [ grep { $_->[0] ne 'LTI' } @$configValues ]; - } else { - # Remove the LTI settings for the LTI version that is not enabled for this course. - for my $oneConfig (@$configValues) { - next unless $oneConfig->[0] eq 'LTI'; - $oneConfig = [ - grep { - ref($_) ne 'HASH' || $_->{var} !~ /^LTI\{v1p[13]\}/ || $_->{var} =~ /^LTI\{$ce->{LTIVersion}\}/ - } @$oneConfig - ]; - last; - } - } + if ($c->param('make_changes')) { + # Get a copy of the course environment which does not have simple.conf loaded + my $ce3 = WeBWorK::CourseEnvironment->new({ + courseName => $ce->{courseName}, + web_config_filename => 'noSuchFilePlease' + }); - return $configValues; -} + my $fileoutput = <<~ 'END_SIMPLE_CONF_HEADER'; + #!perl + # This file is automatically generated by WeBWorK's web-based + # configuration module. Do not make changes directly to this + # file. It will be overwritten the next time configuration + # changes are saved. -sub pre_header_initialize ($c) { - my $ce = $c->ce; - my $configValues = $c->getConfigValues($ce); - # Get a course environment without course.conf - $c->{default_ce} = WeBWorK::CourseEnvironment->new; - - $c->{ce_file_dir} = $ce->{courseDirs}{root}; - - # Get a copy of the course environment which does not have simple.conf loaded - my $ce3 = WeBWorK::CourseEnvironment->new({ - courseName => $ce->{courseName}, - web_config_filename => 'noSuchFilePlease' - }); - if ($c->param('make_changes')) { - my $fileoutput = "#!perl -# This file is automatically generated by WeBWorK's web-based -# configuration module. Do not make changes directly to this -# file. It will be overwritten the next time configuration -# changes are saved.\n\n"; + END_SIMPLE_CONF_HEADER # Get the number of the current tab - my $tab = $c->param('section_tab') || 'tab0'; - $tab =~ s/tab//; + my $tab = ($c->param('section_tab') || 'tab0') =~ s/tab//r; + # We completely rewrite the simple configuration file, so we need to go through all sections. - for my $configSection (@{$configValues}) { - my @configSectionArray = @{$configSection}; + for my $configSection (@{ $c->stash->{configValues} }) { + my @configSectionArray = @$configSection; shift @configSectionArray; for my $con (@configSectionArray) { my $conobject = $c->objectify($con); if ($tab) { # This tab is hidden so use the current course environment value. - $fileoutput .= $conobject->save_string($con->get_value($ce3), 1); + $fileoutput .= $conobject->save_string($conobject->get_value($ce3), 1); } else { - # We reached the tab with entry objects + # We reached the tab with entry objects. if (defined $conobject->{var}) { - $fileoutput .= $conobject->save_string($con->get_value($ce3)); + $fileoutput .= $conobject->save_string($conobject->get_value($ce3)); } else { - # this was something to set in the course's setting table - $c->db->setSettingValue($conobject->{setting}, $c->{paramcache}{ $conobject->{setting} }[0]); + # This is something to set in the course's setting table. + $c->db->setSettingValue($conobject->{setting}, scalar $c->param($conobject->{setting})); } } } $tab--; } - my @write_result = writeFile("$c->{ce_file_dir}/simple.conf", $fileoutput); + + my @write_result = writeFile("$ce->{courseDirs}{root}/simple.conf", $fileoutput); if (@write_result) { $c->addbadmessage($c->c(@write_result)->join($c->tag('br'))); } else { diff --git a/lib/WebworkWebservice/CourseActions.pm b/lib/WebworkWebservice/CourseActions.pm index dc3dd81a17..757b410bce 100644 --- a/lib/WebworkWebservice/CourseActions.pm +++ b/lib/WebworkWebservice/CourseActions.pm @@ -28,6 +28,7 @@ use WeBWorK::DB::Utils qw(initializeUserProblem); use WeBWorK::Utils qw(cryptPassword); use WeBWorK::Utils::CourseManagement qw(addCourse); use WeBWorK::Utils::Files qw(surePathToFile path_is_subdir); +use WeBWorK::ConfigValues qw(getConfigValues); use WeBWorK::Debug; sub createCourse { @@ -405,53 +406,35 @@ sub assignVisibleSets { return 0; } -sub getConfigValues { - my $ce = shift; - my $ConfigValues = $ce->{ConfigValues}; +sub getCourseSettings { + my ($invocant, $self, $params) = @_; + my $ce = $self->ce; + my $ConfigValues = getConfigValues($ce); for my $oneConfig (@$ConfigValues) { for my $hash (@$oneConfig) { - if (ref($hash) eq 'HASH') { - if (defined $hash->{hashVar}) { - my $var = $hash->{hashVar}; - $hash->{value} = eval { $ce->$var }; - } else { - $hash->{value} = undef; - } + next unless ref $hash eq 'HASH'; + my $value; + if (defined $hash->{var}) { + my @keys = $hash->{var} =~ m/([^{}]+)/g; + next unless @keys; + + $value = $ce; + for (@keys) { $value = $value->{$_}; } } else { - debug($hash); + $value = $self->db->getSettingValue($self->{setting}); } + $hash->{value} = $value if defined $value; } } - # Get the list of theme folders in the theme directory and remove . and .. - my $themeDir = $ce->{webworkDirs}{themes}; - opendir(my $dh, $themeDir) or die "Can't open directory $themeDir: $!\n"; - my $themes = [ grep { !/^\.{1,2}$/ } sort readdir($dh) ]; - - # Insert the anonymous array of theme folder names into ConfigValues. - my $modifyThemes = sub { - my $item = shift; - if (ref($item) =~ /HASH/ and $item->{var} eq 'defaultTheme') { $item->{values} = $themes } - }; - - for my $oneConfig (@$ConfigValues) { - for my $hash (@$oneConfig) { - &$modifyThemes($hash); - } - } - - return $ConfigValues; -} - -sub getCourseSettings { - my ($invocant, $self, $params) = @_; - my $ce = $self->ce; - my $db = $self->db; - my $ConfigValues = getConfigValues($ce); - - my $tz = DateTime::TimeZone->new(name => $ce->{siteDefaults}->{timezone}); - push(@$ConfigValues, [ 'tz_abbr', $tz->short_name_for_datetime(DateTime->now) ]); + push( + @$ConfigValues, + [ + 'tz_abbr', + DateTime::TimeZone->new(name => $ce->{siteDefaults}->{timezone})->short_name_for_datetime(DateTime->now) + ] + ); return { ra_out => $ConfigValues, diff --git a/templates/ContentGenerator/Instructor/Config.html.ep b/templates/ContentGenerator/Instructor/Config.html.ep index 13053edafe..6366191e0e 100644 --- a/templates/ContentGenerator/Instructor/Config.html.ep +++ b/templates/ContentGenerator/Instructor/Config.html.ep @@ -1,3 +1,5 @@ +% use WeBWorK::CourseEnvironment; +% % unless ($authz->hasPermissions(param('user'), 'modify_problem_sets')) {
<%= maketext('You are not authorized to modify the course configuration.') %> @@ -5,59 +7,51 @@ % last; % } % -% my $configValues = $c->getConfigValues($ce); +% # Get a course environment without course.conf for site default values. +% my $default_ce = WeBWorK::CourseEnvironment->new; % % # Get the current course environment again in case changes were just saved. % my $ce4 = WeBWorK::CourseEnvironment->new({ courseName => $ce->{courseName}, }); % -% if (@$configValues == 0) { -

- <%= maketext( - 'The configuration module did not find the data it needs to function. ' - . 'Have your site administrator check that site configuration files are up to date.' - ) =%> -

-% } else { - % my $current_tab = param('section_tab') || 'tab0'; - <%= $c->generate_navigation_tabs($current_tab, map { $_->[0] } @$configValues) =%> +% my $current_tab = param('section_tab') || 'tab0'; +<%= $c->generate_navigation_tabs($current_tab, map { $_->[0] } @$configValues) =%> +% +<%= form_for current_route, method => 'POST', id => 'config-form', name => 'config-form', begin =%> + <%= $c->hidden_authen_fields =%> + <%= hidden_field section_tab => $current_tab =%> + % + % my $tabnumber = $current_tab =~ s/tab//r; + % my @configSectionArray = @{ $configValues->[$tabnumber] }; % - <%= form_for current_route, method => 'POST', id => 'config-form', name => 'config-form', begin =%> - <%= $c->hidden_authen_fields =%> - <%= hidden_field section_tab => $current_tab =%> - % - % my $tabnumber = $current_tab =~ s/tab//r; - % my @configSectionArray = @{ $configValues->[$tabnumber] }; - % -

<%= maketext(shift @configSectionArray) %>

- % -
- +

<%= maketext(shift @configSectionArray) %>

+ % +
+
+ + + + + + % for my $con (@configSectionArray) { + % my $conobject = $c->objectify($con); + % next unless ( + % defined $conobject->{var} + % || $authz->hasPermissions(param('user'), "setting_table_$conobject->{setting}") + % ); + % my $name = defined $conobject->{var} + % ? ($conobject->{var} =~ s/[{]/-/gr) =~ s/[}]//gr + % : $conobject->{setting}; - - - + + + - % for my $con (@configSectionArray) { - % my $conobject = $c->objectify($con); - % next unless ( - % defined $conobject->{var} - % || $authz->hasPermissions(param('user'), "setting_table_$conobject->{setting}") - % ); - % my $name = defined $conobject->{var} - % ? ($conobject->{var} =~ s/[{]/-/gr) =~ s/[}]//gr - % : $conobject->{setting}; - - - - - - % } -
<%= maketext('Setting') %><%= maketext('Default') %><%= maketext('Current') %>
<%= maketext('Setting') %><%= maketext('Default') %><%= maketext('Current') %><%= $conobject->what_string %> + % if (defined $conobject->{var}) { + <%= $conobject->display_value($conobject->get_value($default_ce)) %> + % } + <%= $conobject->entry_widget($conobject->get_value($ce4)) =%>
<%= $conobject->what_string %> - % if (defined $conobject->{var}) { - <%= $conobject->display_value($con->get_value($c->{default_ce})) %> - % } - <%= $conobject->entry_widget($con->get_value($ce4)) =%>
-
-

<%= submit_button maketext('Save Changes'), name => 'make_changes', class => 'btn btn-primary' =%>

- <% end =%> -% } + % } + +
+

<%= submit_button maketext('Save Changes'), name => 'make_changes', class => 'btn btn-primary' =%>

+<% end =%>