Skip to content
Snippets Groups Projects
Commit f80434ca authored by Tim Hunt's avatar Tim Hunt
Browse files

Don't let users type $$ into the editing form.

Instead, there is a new option at the end of the form, to replace any
dollar delimiters when you save. This is useful if your are copying and
pasting from other sources of TeX, or just if you are used to typing $$
and $.

If that option is selected, it does what it says. If that option is not
selected, then the form validation will complain about any $$. However,
it will not complain about $, for the benefit of Americans who want to
ask about $2 + $2 = $4.
parent 2ce38efa
Branches
No related tags found
No related merge requests found
...@@ -235,6 +235,23 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -235,6 +235,23 @@ class qtype_stack_edit_form extends question_edit_form {
return $potential_response_tree->get_required_variables($input_keys); return $potential_response_tree->get_required_variables($input_keys);
} }
protected function definition() {
parent::definition();
$mform = $this->_form;
$fixdollars = $mform->createElement('checkbox', 'fixdollars',
stack_string('fixdollars'), stack_string('fixdollarslabel'));
$mform->insertElementBefore($fixdollars, 'buttonar');
$mform->addHelpButton('fixdollars', 'fixdollars', 'qtype_stack');
$mform->closeHeaderBefore('fixdollars');
// There is no un-closeHeaderBefore, so fake it.
$closebeforebuttonarr = array_search('buttonar', $mform->defaultRenderer()->_stopFieldsetElements);
if ($closebeforebuttonarr !== false) {
unset($mform->defaultRenderer()->_stopFieldsetElements[$closebeforebuttonarr]);
}
}
protected function definition_inner(/* MoodleQuickForm */ $mform) { protected function definition_inner(/* MoodleQuickForm */ $mform) {
// Prepare input types. // Prepare input types.
...@@ -722,6 +739,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -722,6 +739,7 @@ class qtype_stack_edit_form extends question_edit_form {
$inputs = $this->get_input_names_from_question_text(); $inputs = $this->get_input_names_from_question_text();
$prts = $this->get_prt_names_from_question(); $prts = $this->get_prt_names_from_question();
$fixingdollars = !empty($fromform->fixdollars);
$this->options = new stack_options(); $this->options = new stack_options();
$this->options->set_option('multiplicationsign', $fromform['multiplicationsign']); $this->options->set_option('multiplicationsign', $fromform['multiplicationsign']);
...@@ -742,7 +760,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -742,7 +760,7 @@ class qtype_stack_edit_form extends question_edit_form {
// Question text. // Question text.
$errors['questiontext'] = array(); $errors['questiontext'] = array();
$errors = $this->validate_cas_text($errors, $fromform['questiontext']['text'], 'questiontext'); $errors = $this->validate_cas_text($errors, $fromform['questiontext']['text'], 'questiontext', $fixingdollars);
foreach ($inputs as $inputname => $counts) { foreach ($inputs as $inputname => $counts) {
list($numinputs, $numvalidations) = $counts; list($numinputs, $numvalidations) = $counts;
...@@ -772,7 +790,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -772,7 +790,7 @@ class qtype_stack_edit_form extends question_edit_form {
// Specific feedback. // Specific feedback.
$errors['specificfeedback'] = array(); $errors['specificfeedback'] = array();
$errors = $this->validate_cas_text($errors, $fromform['specificfeedback']['text'], 'specificfeedback'); $errors = $this->validate_cas_text($errors, $fromform['specificfeedback']['text'], 'specificfeedback', $fixingdollars);
$errors['specificfeedback'] += $this->check_no_placeholders( $errors['specificfeedback'] += $this->check_no_placeholders(
stack_string('specificfeedback'), $fromform['specificfeedback']['text'], stack_string('specificfeedback'), $fromform['specificfeedback']['text'],
...@@ -787,7 +805,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -787,7 +805,7 @@ class qtype_stack_edit_form extends question_edit_form {
// General feedback. // General feedback.
$errors['generalfeedback'] = array(); $errors['generalfeedback'] = array();
$errors = $this->validate_cas_text($errors, $fromform['generalfeedback']['text'], 'generalfeedback'); $errors = $this->validate_cas_text($errors, $fromform['generalfeedback']['text'], 'generalfeedback', $fixingdollars);
$errors['generalfeedback'] += $this->check_no_placeholders( $errors['generalfeedback'] += $this->check_no_placeholders(
get_string('generalfeedback', 'question'), $fromform['generalfeedback']['text']); get_string('generalfeedback', 'question'), $fromform['generalfeedback']['text']);
...@@ -799,7 +817,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -799,7 +817,7 @@ class qtype_stack_edit_form extends question_edit_form {
} }
} else { } else {
// Note, the 'questionnote' does not have an editor field and hence no 'text' sub-clause. // Note, the 'questionnote' does not have an editor field and hence no 'text' sub-clause.
$errors = $this->validate_cas_text($errors, $fromform['questionnote'], 'questionnote'); $errors = $this->validate_cas_text($errors, $fromform['questionnote'], 'questionnote', $fixingdollars);
} }
$errors['questionnote'] += $this->check_no_placeholders( $errors['questionnote'] += $this->check_no_placeholders(
...@@ -813,12 +831,12 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -813,12 +831,12 @@ class qtype_stack_edit_form extends question_edit_form {
// 3) Validate all prts. // 3) Validate all prts.
foreach ($prts as $prtname => $notused) { foreach ($prts as $prtname => $notused) {
$errors = $this->validate_prt($errors, $fromform, $prtname); $errors = $this->validate_prt($errors, $fromform, $prtname, $fixingdollars);
} }
// 4) Validate all hints. // 4) Validate all hints.
foreach ($fromform['hint'] as $index => $hint) { foreach ($fromform['hint'] as $index => $hint) {
$errors = $this->validate_cas_text($errors, $hint['text'], 'hint[' . $index . ']'); $errors = $this->validate_cas_text($errors, $hint['text'], 'hint[' . $index . ']', $fixingdollars);
} }
// Clear out any empty $errors elements, ready for the next check. // Clear out any empty $errors elements, ready for the next check.
...@@ -830,7 +848,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -830,7 +848,7 @@ class qtype_stack_edit_form extends question_edit_form {
// If everything else is OK, try executing the CAS code to check for errors. // If everything else is OK, try executing the CAS code to check for errors.
if (empty($errors)) { if (empty($errors)) {
$errors = $this->validate_question_cas_code($errors, $fromform); $errors = $this->validate_question_cas_code($errors, $fromform, $fixingdollars);
} }
// Convert the $errors array from our array of arrays format to the // Convert the $errors array from our array of arrays format to the
...@@ -853,7 +871,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -853,7 +871,7 @@ class qtype_stack_edit_form extends question_edit_form {
* @param string $prtname the name of the PRT to validate. * @param string $prtname the name of the PRT to validate.
* @return array the update $errors array. * @return array the update $errors array.
*/ */
protected function validate_prt($errors, $fromform, $prtname) { protected function validate_prt($errors, $fromform, $prtname, $fixingdollars) {
if (!array_key_exists($prtname . 'feedbackvariables', $fromform)) { if (!array_key_exists($prtname . 'feedbackvariables', $fromform)) {
// This happens when you edit the question text to add more PRTs. // This happens when you edit the question text to add more PRTs.
...@@ -875,7 +893,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -875,7 +893,7 @@ class qtype_stack_edit_form extends question_edit_form {
foreach ($nodes as $nodekey) { foreach ($nodes as $nodekey) {
// Check the fields the belong to this node individually. // Check the fields the belong to this node individually.
$errors = $this->validate_prt_node($errors, $fromform, $prtname, $nodekey); $errors = $this->validate_prt_node($errors, $fromform, $prtname, $nodekey, $fixingdollars);
if (is_null($textformat)) { if (is_null($textformat)) {
$textformat = $fromform[$prtname . 'truefeedback'][$nodekey]['format']; $textformat = $fromform[$prtname . 'truefeedback'][$nodekey]['format'];
...@@ -930,7 +948,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -930,7 +948,7 @@ class qtype_stack_edit_form extends question_edit_form {
* @param string $nodekey the name of the node to validate. * @param string $nodekey the name of the node to validate.
* @return array the update $errors array. * @return array the update $errors array.
*/ */
protected function validate_prt_node($errors, $fromform, $prtname, $nodekey) { protected function validate_prt_node($errors, $fromform, $prtname, $nodekey, $fixingdollars) {
$nodegroup = $prtname . 'node[' . $nodekey . ']'; $nodegroup = $prtname . 'node[' . $nodekey . ']';
$errors = $this->validate_cas_string($errors, $fromform[$prtname . 'sans'][$nodekey], $errors = $this->validate_cas_string($errors, $fromform[$prtname . 'sans'][$nodekey],
...@@ -988,7 +1006,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -988,7 +1006,7 @@ class qtype_stack_edit_form extends question_edit_form {
} }
$errors = $this->validate_cas_text($errors, $fromform[$prtname . $branch . 'feedback'][$nodekey]['text'], $errors = $this->validate_cas_text($errors, $fromform[$prtname . $branch . 'feedback'][$nodekey]['text'],
$prtname . $branch . 'feedback[' . $nodekey . ']'); $prtname . $branch . 'feedback[' . $nodekey . ']', $fixingdollars);
} }
return $errors; return $errors;
...@@ -1032,7 +1050,11 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -1032,7 +1050,11 @@ class qtype_stack_edit_form extends question_edit_form {
* @param string $savesession the array key to save the session to in $this->validationcasstrings. * @param string $savesession the array key to save the session to in $this->validationcasstrings.
* @return array updated $errors array. * @return array updated $errors array.
*/ */
protected function validate_cas_text($errors, $value, $fieldname, $session = null) { protected function validate_cas_text($errors, $value, $fieldname, $fixingdollars, $session = null) {
if (!$fixingdollars && strpos($value, '$$') !== false) {
$errors[$fieldname][] = stack_string('forbiddendoubledollars');
}
$castext = new stack_cas_text($value, $session, $this->seed, 't'); $castext = new stack_cas_text($value, $session, $this->seed, 't');
if (!$castext->get_valid()) { if (!$castext->get_valid()) {
$errors[$fieldname][] = $castext->get_errors(); $errors[$fieldname][] = $castext->get_errors();
...@@ -1084,7 +1106,7 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -1084,7 +1106,7 @@ class qtype_stack_edit_form extends question_edit_form {
* @param array $fromform the submitted data to validate. * @param array $fromform the submitted data to validate.
* @return array updated $errors array. * @return array updated $errors array.
*/ */
protected function validate_question_cas_code($errors, $fromform) { protected function validate_question_cas_code($errors, $fromform, $fixingdollars) {
$keyval = new stack_cas_keyval($fromform['questionvariables'], $this->options, $this->seed, 't'); $keyval = new stack_cas_keyval($fromform['questionvariables'], $this->options, $this->seed, 't');
$keyval->instantiate(); $keyval->instantiate();
...@@ -1097,9 +1119,9 @@ class qtype_stack_edit_form extends question_edit_form { ...@@ -1097,9 +1119,9 @@ class qtype_stack_edit_form extends question_edit_form {
// Instantiate all text fields and look for errors. // Instantiate all text fields and look for errors.
$castextfields = array('questiontext', 'specificfeedback', 'generalfeedback'); $castextfields = array('questiontext', 'specificfeedback', 'generalfeedback');
foreach ($castextfields as $field) { foreach ($castextfields as $field) {
$errors = $this->validate_cas_text($errors, $fromform[$field]['text'], $field, clone $session); $errors = $this->validate_cas_text($errors, $fromform[$field]['text'], $field, $fixingdollars, clone $session);
} }
$errors = $this->validate_cas_text($errors, $fromform['questionnote'], 'questionnote', clone $session); $errors = $this->validate_cas_text($errors, $fromform['questionnote'], 'questionnote', $fixingdollars, clone $session);
// Make a list of all inputs, instantiate it and then look for errors. // Make a list of all inputs, instantiate it and then look for errors.
$inputs = $this->get_input_names_from_question_text(); $inputs = $this->get_input_names_from_question_text();
......
...@@ -83,6 +83,10 @@ $string['feedbackvariables'] = 'Feedback variables'; ...@@ -83,6 +83,10 @@ $string['feedbackvariables'] = 'Feedback variables';
$string['feedbackvariables_help'] = 'The feedback variables enable you to manipulate any of the inputs, together with the question variables, prior to traversing the tree. Variables defined here may be used anywhere else in this tree.'; $string['feedbackvariables_help'] = 'The feedback variables enable you to manipulate any of the inputs, together with the question variables, prior to traversing the tree. Variables defined here may be used anywhere else in this tree.';
$string['feedbackvariables_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/KeyVals.md#Feedback_variables'; $string['feedbackvariables_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/KeyVals.md#Feedback_variables';
$string['fieldshouldnotcontainplaceholder'] = '{$a->field} should not contain any [[{$a->type}:...]] placeholders.'; $string['fieldshouldnotcontainplaceholder'] = '{$a->field} should not contain any [[{$a->type}:...]] placeholders.';
$string['fixdollars'] = 'Fix dollars';
$string['fixdollarslabel'] = 'Replace <code>$...$</code> with <code>\(...\)</code> and <code>$$...$$</code> with <code>\[...\]</code> on save.';
$string['fixdollars_help'] = 'This option is useful if are copying and pasting (or typing) TeX with <code>$...$</code> and <code>$$...$$</code> delimiters. Those delimiters will be replaced by the recommended delimiters during the save process.';
$string['forbiddendoubledollars'] = 'Please use the delimiters <code>\(...\)</code> for inline maths and <code>\[...\]</code> for display maths. <code>$...$</code> and <code>$$...$$</code> are not permitted. There is an option at the end of the form to fix this automatically.';
$string['forbidfloat'] = 'Forbid float'; $string['forbidfloat'] = 'Forbid float';
$string['forbidfloat_help'] = 'If set to yes, then any answer of the student which has a floating point number will be rejected as invalid.'; $string['forbidfloat_help'] = 'If set to yes, then any answer of the student which has a floating point number will be rejected as invalid.';
$string['forbidfloat_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/Inputs.md#Forbid_Floats'; $string['forbidfloat_link'] = '%%WWWROOT%%/question/type/stack/doc/doc.php/Authoring/Inputs.md#Forbid_Floats';
......
...@@ -37,6 +37,45 @@ require_once(dirname(__FILE__) . '/stack/questiontest.php'); ...@@ -37,6 +37,45 @@ require_once(dirname(__FILE__) . '/stack/questiontest.php');
*/ */
class qtype_stack extends question_type { class qtype_stack extends question_type {
public function save_question($question, $fromform) {
if (!empty($fromform->fixdollars)) {
$this->fix_dollars_in_form_data($fromform);
}
return parent::save_question($question, $fromform);
}
/**
* Replace any $...$ and $$...$$ delimiters in the question text from the
* form with the recommended delimiters.
* @param object $fromform the data from the form.
*/
protected function fix_dollars_in_form_data($fromform) {
$questionfields = array('questiontext', 'generalfeedback', 'specificfeedback',
'prtcorrect', 'prtpartiallycorrect', 'prtincorrect');
foreach ($questionfields as $field) {
$fromform->{$field}['text'] = stack_maths::replace_dollars($fromform->{$field}['text']);
}
$fromform->questionnote = stack_maths::replace_dollars($fromform->questionnote);
$prtnames = stack_utils::extract_placeholders(
$fromform->questiontext['text'] . $fromform->specificfeedback['text'], 'feedback');
foreach ($prtnames as $prt) {
foreach ($fromform->{$prt . 'truefeedback'} as &$feedback) {
$feedback['text'] = stack_maths::replace_dollars($feedback['text']);
}
foreach ($fromform->{$prt . 'falsefeedback'} as &$feedback) {
$feedback['text'] = stack_maths::replace_dollars($feedback['text']);
}
}
foreach ($fromform->hint as &$hint) {
$hint['text'] = stack_maths::replace_dollars($hint['text']);
}
}
public function save_question_options($fromform) { public function save_question_options($fromform) {
global $DB; global $DB;
$context = $fromform->context; $context = $fromform->context;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment