diff --git a/question/type/ordering/.github/workflows/ci.yml b/question/type/ordering/.github/workflows/ci.yml index 3430e6529b6..628e4b395fb 100644 --- a/question/type/ordering/.github/workflows/ci.yml +++ b/question/type/ordering/.github/workflows/ci.yml @@ -9,15 +9,18 @@ jobs: fail-fast: false matrix: include: - - php: '8.0' + - php: '8.2' moodle-branch: 'master' + database: 'mariadb' + - php: '8.2' + moodle-branch: 'MOODLE_403_STABLE' database: 'pgsql' - php: '8.1' moodle-branch: 'MOODLE_402_STABLE' database: 'mariadb' - php: '8.0' moodle-branch: 'MOODLE_401_STABLE' - database: 'mariadb' + database: 'pgsql' - php: '7.4' moodle-branch: 'MOODLE_400_STABLE' database: 'mariadb' diff --git a/question/type/ordering/backup/moodle1/lib.php b/question/type/ordering/backup/moodle1/lib.php index 442ca9334b5..905e5f30dd9 100644 --- a/question/type/ordering/backup/moodle1/lib.php +++ b/question/type/ordering/backup/moodle1/lib.php @@ -34,13 +34,13 @@ class moodle1_qtype_ordering_handler extends moodle1_qtype_handler { * Returns the list of paths within one that this qtype needs to have included * in the grouped question structure * - * @return array of strings + * @return string[] */ - public function get_question_subpaths() { - return array( + public function get_question_subpaths(): array { + return [ 'ANSWERS/ANSWER', 'ORDERING', - ); + ]; } /** @@ -49,7 +49,7 @@ class moodle1_qtype_ordering_handler extends moodle1_qtype_handler { * @param array $data grouped question data * @param array $raw grouped raw QUESTION data */ - public function process_question(array $data, array $raw) { + public function process_question(array $data, array $raw): void { // Convert and write the answers first. if (isset($data['answers'])) { @@ -59,7 +59,7 @@ class moodle1_qtype_ordering_handler extends moodle1_qtype_handler { // Convert and write the ordering extra fields. foreach ($data['ordering'] as $ordering) { $ordering['id'] = $this->converter->get_nextid(); - $this->write_xml('ordering', $ordering, array('/ordering/id')); + $this->write_xml('ordering', $ordering, ['/ordering/id']); } } } diff --git a/question/type/ordering/backup/moodle2/backup_qtype_ordering_plugin.class.php b/question/type/ordering/backup/moodle2/backup_qtype_ordering_plugin.class.php index 2ed041a066d..b07a04e8047 100644 --- a/question/type/ordering/backup/moodle2/backup_qtype_ordering_plugin.class.php +++ b/question/type/ordering/backup/moodle2/backup_qtype_ordering_plugin.class.php @@ -32,8 +32,10 @@ class backup_qtype_ordering_plugin extends backup_qtype_plugin { /** * Returns the qtype information to attach to question element + * + * @return backup_plugin_element */ - protected function define_question_plugin_structure() { + protected function define_question_plugin_structure(): backup_plugin_element { // Define the virtual plugin element with the condition to fulfill. $plugin = $this->get_plugin_element(null, '../../qtype', 'ordering'); @@ -49,18 +51,18 @@ class backup_qtype_ordering_plugin extends backup_qtype_plugin { $this->add_question_question_answers($pluginwrapper); // Now create the qtype own structures. - $fields = array('layouttype', 'selecttype', 'selectcount', - 'gradingtype', 'showgrading', 'numberingstyle', - 'correctfeedback', 'correctfeedbackformat', - 'incorrectfeedback', 'incorrectfeedbackformat', - 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', 'shownumcorrect'); - $ordering = new backup_nested_element('ordering', array('id'), $fields); + $fields = ['layouttype', 'selecttype', 'selectcount', + 'gradingtype', 'showgrading', 'numberingstyle', + 'correctfeedback', 'correctfeedbackformat', + 'incorrectfeedback', 'incorrectfeedbackformat', + 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', 'shownumcorrect']; + $ordering = new backup_nested_element('ordering', ['id'], $fields); // Now the own qtype tree. $pluginwrapper->add_child($ordering); // Set source to populate the data. - $params = array('questionid' => backup::VAR_PARENTID); + $params = ['questionid' => backup::VAR_PARENTID]; $ordering->set_source_table('qtype_ordering_options', $params); // Don't need to annotate ids nor files. diff --git a/question/type/ordering/backup/moodle2/restore_qtype_ordering_plugin.class.php b/question/type/ordering/backup/moodle2/restore_qtype_ordering_plugin.class.php index 28dc71ea5fc..593cabda1ed 100644 --- a/question/type/ordering/backup/moodle2/restore_qtype_ordering_plugin.class.php +++ b/question/type/ordering/backup/moodle2/restore_qtype_ordering_plugin.class.php @@ -32,10 +32,11 @@ class restore_qtype_ordering_plugin extends restore_qtype_plugin { /** * Returns the paths to be handled by the plugin at question level + * + * @return restore_path_element[] */ - protected function define_question_plugin_structure() { - - $paths = array(); + protected function define_question_plugin_structure(): array { + $paths = []; // This qtype uses question_answers, add them. $this->add_question_question_answers($paths); @@ -53,7 +54,7 @@ class restore_qtype_ordering_plugin extends restore_qtype_plugin { * * @param array $data */ - public function process_ordering($data) { + public function process_ordering(array $data): void { global $DB; $data = (object)$data; @@ -61,8 +62,8 @@ class restore_qtype_ordering_plugin extends restore_qtype_plugin { // Detect if the question is created or mapped // "question" is the XML tag name, not the DB field name. - $oldquestionid = $this->get_old_parentid('question'); - $newquestionid = $this->get_new_parentid('question'); + $oldquestionid = $this->get_old_parentid('question'); + $newquestionid = $this->get_new_parentid('question'); // If the question has been created by restore, // we need to create a "qtype_ordering_options" record @@ -83,8 +84,9 @@ class restore_qtype_ordering_plugin extends restore_qtype_plugin { * If not empty, answer is one question_answers->id. * * @param object $state + * @return string|false */ - public function recode_legacy_state_answer($state) { + public function recode_legacy_state_answer($state): string|false { $answer = $state->answer; $result = ''; if ($answer) { diff --git a/question/type/ordering/classes/privacy/provider.php b/question/type/ordering/classes/privacy/provider.php index dd7a751b3d7..d2011c228c9 100644 --- a/question/type/ordering/classes/privacy/provider.php +++ b/question/type/ordering/classes/privacy/provider.php @@ -30,7 +30,7 @@ class provider implements \core_privacy\local\metadata\null_provider { * Get the language string identifier with the component's language * file to explain why this plugin stores no data. * - * @return string + * @return string */ public static function get_reason(): string { return 'privacy:metadata'; diff --git a/question/type/ordering/classes/question_hint_ordering.php b/question/type/ordering/classes/question_hint_ordering.php index fb52441cfbd..2f7a56cd26b 100644 --- a/question/type/ordering/classes/question_hint_ordering.php +++ b/question/type/ordering/classes/question_hint_ordering.php @@ -29,8 +29,8 @@ use question_hint_with_parts; /** * Question hint for ordering. * - * An extension of {@link question_hint} for questions like match and multiple - * choice with multile answers, where there are options for whether to show the + * An extension of {@see question_hint} for questions like match and multiple + * choice with multiple answers, where there are options for whether to show the * number of parts right at each stage, and to reset the wrong parts. * * @package qtype_ordering @@ -39,18 +39,20 @@ use question_hint_with_parts; */ class question_hint_ordering extends question_hint_with_parts { /** Highlight response in the hint options. */ - public $highlightresponse; + public bool $highlightresponse; /** * Constructor. * - * @param int The hint id from the database. + * @param int $id The hint id from the database. * @param string $hint The hint text. - * @param int The corresponding text FORMAT_... type. + * @param int $hintformat The corresponding text FORMAT_... type. * @param bool $shownumcorrect Whether the number of right parts should be shown. * @param bool $clearwrong Whether the wrong parts should be reset. + * @param bool $highlightresponse Whether to highlight response. */ - public function __construct($id, $hint, $hintformat, $shownumcorrect, $clearwrong, $highlightresponse) { + public function __construct(int $id, string $hint, int $hintformat, bool $shownumcorrect, + bool $clearwrong, bool $highlightresponse) { parent::__construct($id, $hint, $hintformat, $shownumcorrect, $clearwrong); $this->highlightresponse = $highlightresponse; } @@ -61,7 +63,7 @@ class question_hint_ordering extends question_hint_with_parts { * @param object $row With property options as well as hint, shownumcorrect and clearwrong set. * @return question_hint_ordering */ - public static function load_from_record($row) { + public static function load_from_record($row): question_hint_ordering { global $DB; // Initialize with the old questions. @@ -74,14 +76,4 @@ class question_hint_ordering extends question_hint_with_parts { return new question_hint_ordering($row->id, $row->hint, $row->hintformat, $row->shownumcorrect, $row->clearwrong, $row->options); } - - /** - * Adjust this display options according to the hint settings. - * - * @param question_display_options $options - */ - public function adjust_display_options(question_display_options $options) { - parent::adjust_display_options($options); - $options->highlightresponse = $this->highlightresponse; - } } diff --git a/question/type/ordering/edit_ordering_form.php b/question/type/ordering/edit_ordering_form.php index 70415ba7942..635e17b3c27 100644 --- a/question/type/ordering/edit_ordering_form.php +++ b/question/type/ordering/edit_ordering_form.php @@ -53,24 +53,28 @@ class qtype_ordering_edit_form extends question_edit_form { /** * qtype is plugin name without leading "qtype_" + * + * @return string */ - public function qtype() { + public function qtype(): string { return 'ordering'; } /** * Plugin name is class name without trailing "_edit_form" + * + * @return string */ - public function plugin_name() { + public function plugin_name(): string { return 'qtype_ordering'; } /** * Add question-type specific form fields. * - * @param object $mform the form being built. + * @param MoodleQuickForm $mform the form being built. */ - public function definition_inner($mform) { + public function definition_inner($mform): void { // Cache this plugins name. $plugin = 'qtype_ordering'; @@ -94,7 +98,7 @@ class qtype_ordering_edit_form extends question_edit_form { // Field for selectcount. $name = 'selectcount'; $label = get_string($name, $plugin); - $options = array(0 => get_string('all')); + $options = [0 => get_string('all')]; for ($i = 3; $i <= 20; $i++) { $options[$i] = $i; } @@ -114,8 +118,7 @@ class qtype_ordering_edit_form extends question_edit_form { // Field for showgrading. $name = 'showgrading'; $label = get_string($name, $plugin); - $options = array(0 => get_string('hide'), - 1 => get_string('show')); + $options = [0 => get_string('hide'), 1 => get_string('show')]; $mform->addElement('select', $name, $label, $options); $mform->addHelpButton($name, $name, $plugin); $mform->setDefault($name, $this->get_default_value($name, 1)); @@ -127,19 +130,19 @@ class qtype_ordering_edit_form extends question_edit_form { $mform->addHelpButton($name, $name, $plugin); $mform->setDefault($name, $this->get_default_value($name, qtype_ordering_question::NUMBERING_STYLE_DEFAULT)); - $elements = array(); - $options = array(); + $elements = []; + $options = []; $name = 'answerheader'; $label = get_string($name, $plugin); $elements[] = $mform->createElement('header', $name, $label); - $options[$name] = array('expanded' => true); + $options[$name] = ['expanded' => true]; $name = 'answer'; $elements[] = $mform->createElement('editor', $name, $label, $this->get_editor_attributes(), $this->get_editor_options()); $elements[] = $mform->createElement('submit', $name . 'removeeditor', get_string('removeeditor', $plugin), - array('onclick' => 'skipClientValidation = true;')); - $options[$name] = array('type' => PARAM_RAW); + ['onclick' => 'skipClientValidation = true;']); + $options[$name] = ['type' => PARAM_RAW]; $this->add_repeat_elements($mform, $name, $elements, $options); @@ -156,10 +159,10 @@ class qtype_ordering_edit_form extends question_edit_form { /** * Returns answer repeats count * - * @param object $question + * @param stdClass $question * @return int */ - protected function get_answer_repeats($question) { + protected function get_answer_repeats(stdClass $question): int { if (isset($question->id)) { $repeats = count($question->options->answers); } else { @@ -176,11 +179,11 @@ class qtype_ordering_edit_form extends question_edit_form { * * @return array */ - protected function get_editor_attributes() { - return array( - 'rows' => self::TEXTFIELD_ROWS, - 'cols' => self::TEXTFIELD_COLS - ); + protected function get_editor_attributes(): array { + return [ + 'rows' => self::TEXTFIELD_ROWS, + 'cols' => self::TEXTFIELD_COLS, + ]; } /** @@ -188,45 +191,44 @@ class qtype_ordering_edit_form extends question_edit_form { * * @return array */ - protected function get_editor_options() { - return array( - 'context' => $this->context, + protected function get_editor_options(): array { + return [ + 'context' => $this->context, 'maxfiles' => EDITOR_UNLIMITED_FILES, - 'noclean' => true - ); + 'noclean' => true, + ]; } /** * Resets editor format to specified * - * @param object $editor - * @param int $format + * @param MoodleQuickForm_editor $editor + * @param int|string $format * @return int */ - protected function reset_editor_format($editor, $format=FORMAT_MOODLE) { + protected function reset_editor_format(MoodleQuickForm_editor $editor, int|string $format = FORMAT_MOODLE): int { $value = $editor->getValue(); $value['format'] = $format; - $value = $editor->setValue($value); + $editor->setValue($value); return $format; } /** * Adjust HTML editor and removal buttons. * - * @param object $mform + * @param MoodleQuickForm $mform * @param string $name - * @param int $repeats */ - protected function adjust_html_editors($mform, $name) { + protected function adjust_html_editors(MoodleQuickForm $mform, string $name): void { // Cache the number of formats supported // by the preferred editor for each format. - $count = array(); + $count = []; if (isset($this->question->options->answers)) { $ids = array_keys($this->question->options->answers); } else { - $ids = array(); + $ids = []; } $defaultanswerformat = get_config('qtype_ordering', 'defaultanswerformat'); @@ -245,12 +247,7 @@ class qtype_ordering_edit_form extends question_edit_form { for ($i = 0; $i < $repeats; $i++) { $editor = $mform->getElement($name."[$i]"); - - if (isset($ids[$i])) { - $id = $ids[$i]; - } else { - $id = 0; - } + $id = $ids[$i] ?? 0; // The old/new name of the button to remove the HTML editor // old : the name of the button when added by repeat_elements @@ -260,7 +257,7 @@ class qtype_ordering_edit_form extends question_edit_form { // Remove HTML editor, if necessary. if (optional_param($newname, 0, PARAM_RAW)) { - $format = $this->reset_editor_format($editor, FORMAT_MOODLE); + $format = $this->reset_editor_format($editor); $_POST['answer'][$i]['format'] = $format; // Overwrite incoming data. } else if ($id) { $format = $this->question->options->answers[$id]->answerformat; @@ -270,11 +267,12 @@ class qtype_ordering_edit_form extends question_edit_form { // Check we have a submit button - it should always be there !! if ($mform->elementExists($oldname)) { - if (! isset($count[$format])) { + if (!isset($count[$format])) { $editor = editors_get_preferred_editor($format); $count[$format] = $editor->get_supported_formats(); $count[$format] = count($count[$format]); } + if ($count[$format] > 1) { $mform->removeElement($oldname); } else { @@ -293,7 +291,7 @@ class qtype_ordering_edit_form extends question_edit_form { * @param string $withshownumpartscorrect Whether this quesiton type uses the 'Show num parts correct' option on hints. * @return array Form field elements for one hint. */ - protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) { + protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false): array { $mform = $this->_form; $repeated = []; @@ -320,12 +318,12 @@ class qtype_ordering_edit_form extends question_edit_form { } /** - * Perform an preprocessing needed on the data passed to {@link set_data()} + * Perform any preprocessing needed on the data passed to {@see set_data()} * before it is used to initialise the form. * @param object $question the data being passed to the form. - * @return object $question the modified data. + * @return stdClass $question the modified data. */ - public function data_preprocessing($question) { + public function data_preprocessing($question): stdClass { $question = parent::data_preprocessing($question); $question = $this->data_preprocessing_answers($question, true); @@ -336,11 +334,11 @@ class qtype_ordering_edit_form extends question_edit_form { $question = $this->data_preprocessing_hints($question, false, true); // Preprocess answers and fractions. - $question->answer = array(); - $question->fraction = array(); + $question->answer = []; + $question->fraction = []; if (empty($question->options->answers)) { - $answerids = array(); + $answerids = []; } else { $answerids = array_keys($question->options->answers); } @@ -352,8 +350,7 @@ class qtype_ordering_edit_form extends question_edit_form { if ($answerid = array_shift($answerids)) { $answer = $question->options->answers[$answerid]; } else { - $answer = (object)array('answer' => '', - 'answerformat' => $defaultanswerformat); + $answer = (object) ['answer' => '', 'answerformat' => $defaultanswerformat]; $answerid = 0; } @@ -363,29 +360,25 @@ class qtype_ordering_edit_form extends question_edit_form { $itemid = file_get_submitted_draft_itemid("answer[$i]"); $format = $answer->answerformat; $text = file_prepare_draft_area($itemid, $this->context->id, 'question', 'answer', - $answerid, $this->editoroptions, $answer->answer); - $question->answer[$i] = array('text' => $text, - 'format' => $format, - 'itemid' => $itemid); + $answerid, $this->editoroptions, $answer->answer); + $question->answer[$i] = ['text' => $text, + 'format' => $format, + 'itemid' => $itemid]; } $question->fraction[$i] = ($i + 1); } // Defining default values. - $names = array( - 'layouttype' => qtype_ordering_question::LAYOUT_VERTICAL, - 'selecttype' => qtype_ordering_question::SELECT_ALL, + $names = [ + 'layouttype' => qtype_ordering_question::LAYOUT_VERTICAL, + 'selecttype' => qtype_ordering_question::SELECT_ALL, 'selectcount' => 0, // 0 means ALL. 'gradingtype' => qtype_ordering_question::GRADING_ABSOLUTE_POSITION, 'showgrading' => 1, // 1 means SHOW. - 'numberingstyle' => qtype_ordering_question::NUMBERING_STYLE_DEFAULT - ); + 'numberingstyle' => qtype_ordering_question::NUMBERING_STYLE_DEFAULT, + ]; foreach ($names as $name => $default) { - if (isset($question->options->$name)) { - $question->$name = $question->options->$name; - } else { - $question->$name = $this->get_default_value($name, $default); - } + $question->$name = $question->options->$name ?? $this->get_default_value($name, $default); } return $question; @@ -397,9 +390,9 @@ class qtype_ordering_edit_form extends question_edit_form { * @param object $question The data being passed to the form. * @param bool $withclearwrong Clear wrong hints. * @param bool $withshownumpartscorrect Show number correct. - * @return object The modified data. + * @return stdClass The modified data. */ - protected function data_preprocessing_hints($question, $withclearwrong = false, $withshownumpartscorrect = false) { + protected function data_preprocessing_hints($question, $withclearwrong = false, $withshownumpartscorrect = false): stdClass { if (empty($question->hints)) { return $question; } @@ -421,8 +414,8 @@ class qtype_ordering_edit_form extends question_edit_form { * @return array of "element_name"=>"error_description" if there are errors, * or an empty array if everything is OK (true allowed for backwards compatibility too). */ - public function validation($data, $files) { - $errors = array(); + public function validation($data, $files): array { + $errors = []; $plugin = 'qtype_ordering'; // Identify duplicates and report as an error. @@ -438,7 +431,7 @@ class qtype_ordering_edit_form extends question_edit_form { $item = get_string('answerheader', $plugin); $item = str_replace('{no}', $i + 1, $item); $item = html_writer::link("#id_answerheader_$i", $item); - $a = (object)array('text' => $answer, 'item' => $item); + $a = (object) ['text' => $answer, 'item' => $item]; $errors["answer[$answercount]"] = get_string('duplicatesnotallowed', $plugin, $a); } else { $answers[] = $answer; @@ -447,15 +440,20 @@ class qtype_ordering_edit_form extends question_edit_form { } } - switch ($answercount) { - case 0: $errors['answer[0]'] = get_string('notenoughanswers', $plugin, 2); - case 1: $errors['answer[1]'] = get_string('notenoughanswers', $plugin, 2); + // If there are no answers provided, show error message under first 2 answer boxes + // If only 1 answer provided, show error message under second answer box + if ($answercount < 2) { + $errors['answer[1]'] = get_string('notenoughanswers', $plugin, 2); + + if ($answercount == 0) { + $errors['answer[0]'] = get_string('notenoughanswers', $plugin, 2); + } } // If adding a new ordering question, update defaults. if (empty($errors) && empty($data['id'])) { - $fields = array('layouttype', 'selecttype', 'selectcount', - 'gradingtype', 'showgrading', 'numberingstyle'); + $fields = ['layouttype', 'selecttype', 'selectcount', + 'gradingtype', 'showgrading', 'numberingstyle']; foreach ($fields as $field) { if (array_key_exists($field, $data)) { question_bank::get_qtype($this->qtype())->set_default_value($field, $data[$field]); @@ -473,22 +471,24 @@ class qtype_ordering_edit_form extends question_edit_form { * @param string $name Item name * @return string full preference name */ - protected function get_my_preference_name($name) { + protected function get_my_preference_name(string $name): string { return $this->plugin_name()."_$name"; } /** * Get array of countable item types * - * @return array(type => description) + * @param string $type + * @param int $max + * @return array (type => description) */ - protected function get_addcount_options($type, $max=10) { + protected function get_addcount_options(string $type, int $max = 10): array { // Cache plugin name. $plugin = $this->plugin_name(); // Generate options. - $options = array(); + $options = []; for ($i = 1; $i <= $max; $i++) { if ($i == 1) { $options[$i] = get_string('addsingle'.$type, $plugin); @@ -502,10 +502,13 @@ class qtype_ordering_edit_form extends question_edit_form { /** * Add repeated elements with a button allowing a selectable number of new elements * - * @param object $mform the Moodle form object - * @return voide, but will update $mform + * @param MoodleQuickForm $mform the Moodle form object + * @param string $type + * @param array $elements + * @param array $options + * @return void, but will update $mform */ - protected function add_repeat_elements($mform, $type, $elements, $options) { + protected function add_repeat_elements(MoodleQuickForm $mform, string $type, array $elements, array $options): void { // Cache plugin name. $plugin = $this->plugin_name(); @@ -531,10 +534,10 @@ class qtype_ordering_edit_form extends question_edit_form { // ... and replace it with "Add" button + select group. $options = $this->get_addcount_options($type); - $mform->addGroup(array( + $mform->addGroup([ $mform->createElement('submit', $addtypes, get_string('add')), $mform->createElement('select', $addtypescount, '', $options) - ), $addtypesgroup, '', ' ', false); + ], $addtypesgroup, '', ' ', false); // Set default value and type of select element. $mform->setDefault($addtypescount, $count); diff --git a/question/type/ordering/question.php b/question/type/ordering/question.php index f7b0a288708..9c6167e001e 100644 --- a/question/type/ordering/question.php +++ b/question/type/ordering/question.php @@ -67,6 +67,24 @@ class qtype_ordering_question extends question_graded_automatically { /** @var int Items are graded relative to their position in the correct answer */ const GRADING_RELATIVE_TO_CORRECT = 7; + /** @var int {@see LAYOUT_VERTICAL} or {@see LAYOUT_HORIZONTAL}. */ + public $layouttype; + + /** @var int {@see SELECT_ALL}, {@see SELECT_RANDOM} or {@see SELECT_CONTIGUOUS}. */ + public $selecttype; + + /** @var int if {@see $selecttype} is not SELECT_ALL, then the number to select. */ + public $selectcount; + + /** @var int Which grading strategy to use. One of the GRADING_... constants. */ + public $gradingtype; + + /** @var bool Should details of the grading calculation be shown to students. */ + public $showgrading; + + /** @var string How to number the items. A key from the array returned by {@see get_numbering_styles()}. */ + public $numberingstyle; + // Fields from "qtype_ordering_options" table. /** @var string */ public $correctfeedback; diff --git a/question/type/ordering/questiontype.php b/question/type/ordering/questiontype.php index b54a5a09602..f34f11ad38c 100644 --- a/question/type/ordering/questiontype.php +++ b/question/type/ordering/questiontype.php @@ -36,28 +36,30 @@ class qtype_ordering extends question_type { const DEFAULT_NUM_HINTS = 2; /** @var array Combined feedback fields */ - public $feedbackfields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'); + public array $feedbackfields = ['correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback']; /** * @return bool whether the question_answers.answer field needs to have * restore_decode_content_links_worker called on it. */ - public function has_html_answers() { + public function has_html_answers(): bool { return true; } /** * If your question type has a table that extends the question table, and * you want the base class to automatically save, backup and restore the extra fields, - * override this method to return an array wherer the first element is the table name, + * override this method to return an array where the first element is the table name, * and the subsequent entries are the column names (apart from id and questionid). * - * @return mixed array as above, or null to tell the base class to do nothing. + * @return array extra fields */ - public function extra_question_fields() { - return array('qtype_ordering_options', - 'layouttype', 'selecttype', 'selectcount', - 'gradingtype', 'showgrading', 'numberingstyle'); + public function extra_question_fields(): array { + return [ + 'qtype_ordering_options', + 'layouttype', 'selecttype', 'selectcount', + 'gradingtype', 'showgrading', 'numberingstyle', + ]; } /** @@ -65,15 +67,14 @@ class qtype_ordering extends question_type { * @param question_definition $question the question_definition we are creating. * @param object $questiondata the question data loaded from the database. */ - protected function initialise_question_instance(question_definition $question, $questiondata) { + protected function initialise_question_instance(question_definition $question, $questiondata): void { global $CFG; parent::initialise_question_instance($question, $questiondata); $question->answers = $questiondata->options->answers; foreach ($question->answers as $answerid => $answer) { - $question->answers[$answerid]->md5key = - 'ordering_item_' . md5(($CFG->passwordsaltmain ?? '') . $answer->answer); + $question->answers[$answerid]->md5key = 'ordering_item_' . md5(($CFG->passwordsaltmain ?? '') . $answer->answer); } $question->options = clone($questiondata->options); @@ -85,19 +86,20 @@ class qtype_ordering extends question_type { /** * Saves question-type specific options * - * This is called by {@link save_question()} to save the question-type specific data - * @return object $result->error or $result->notice - * @param object $question This holds the information from the editing form, + * This is called by {@see save_question()} to save the question-type specific data + * + * @param object $question This holds the information from the editing form, * it is not a standard question object. + * @return bool|stdClass $result->error or $result->notice */ - public function save_question_options($question) { + public function save_question_options($question): bool|stdClass { global $DB; $result = new stdClass(); $context = $question->context; // Remove empty answers. - $question->answer = array_filter($question->answer, array($this, 'is_not_blank')); + $question->answer = array_filter($question->answer, [$this, 'is_not_blank']); $question->answer = array_values($question->answer); // Make keys sequential. // Count how many answers we have. @@ -119,10 +121,10 @@ class qtype_ordering extends question_type { $question->feedback = range(1, $countanswers); - if ($answerids = $DB->get_records('question_answers', array('question' => $question->id), 'id ASC', 'id,question')) { + if ($answerids = $DB->get_records('question_answers', ['question' => $question->id], 'id ASC', 'id,question')) { $answerids = array_keys($answerids); } else { - $answerids = array(); + $answerids = []; } // Insert all the new answers. @@ -163,24 +165,24 @@ class qtype_ordering extends question_type { $answertext = preg_replace($imgsearch, $imgreplace, $answertext); // Prepare the $answer object. - $answer = (object)array( - 'question' => $question->id, - 'fraction' => ($i + 1), // Start at 1. - 'answer' => $answertext, - 'answerformat' => $answerformat, - 'feedback' => '', + $answer = (object) [ + 'question' => $question->id, + 'fraction' => ($i + 1), // Start at 1. + 'answer' => $answertext, + 'answerformat' => $answerformat, + 'feedback' => '', 'feedbackformat' => FORMAT_MOODLE, - ); + ]; // Add/insert $answer into the database. if ($answer->id = array_shift($answerids)) { - if (! $DB->update_record('question_answers', $answer)) { + if (!$DB->update_record('question_answers', $answer)) { $result->error = get_string('cannotupdaterecord', 'error', 'question_answers (id='.$answer->id.')'); return $result; } } else { unset($answer->id); - if (! $answer->id = $DB->insert_record('question_answers', $answer)) { + if (!$answer->id = $DB->insert_record('question_answers', $answer)) { $result->error = get_string('cannotinsertrecord', 'error', 'question_answers'); return $result; } @@ -191,32 +193,32 @@ class qtype_ordering extends question_type { // because the answer id is used as the file's "itemid". if ($answeritemid) { $answertext = file_save_draft_area_files($answeritemid, $context->id, 'question', 'answer', $answer->id, - $this->fileoptions, $answertext); - $DB->set_field('question_answers', 'answer', $answertext, array('id' => $answer->id)); + $this->fileoptions, $answertext); + $DB->set_field('question_answers', 'answer', $answertext, ['id' => $answer->id]); } } // Create $options for this ordering question. - $options = (object)array( + $options = (object) [ 'questionid' => $question->id, 'layouttype' => $question->layouttype, 'selecttype' => $question->selecttype, 'selectcount' => $question->selectcount, 'gradingtype' => $question->gradingtype, 'showgrading' => $question->showgrading, - 'numberingstyle' => $question->numberingstyle - ); + 'numberingstyle' => $question->numberingstyle, + ]; $options = $this->save_combined_feedback_helper($options, $question, $context, true); $this->save_hints($question, true); // Add/update $options for this ordering question. - if ($options->id = $DB->get_field('qtype_ordering_options', 'id', array('questionid' => $question->id))) { - if (! $DB->update_record('qtype_ordering_options', $options)) { + if ($options->id = $DB->get_field('qtype_ordering_options', 'id', ['questionid' => $question->id])) { + if (!$DB->update_record('qtype_ordering_options', $options)) { $result->error = get_string('cannotupdaterecord', 'error', 'qtype_ordering_options (id='.$options->id.')'); return $result; } } else { unset($options->id); - if (! $options->id = $DB->insert_record('qtype_ordering_options', $options)) { + if (!$options->id = $DB->insert_record('qtype_ordering_options', $options)) { $result->error = get_string('cannotinsertrecord', 'error', 'qtype_ordering_options'); return $result; } @@ -227,7 +229,7 @@ class qtype_ordering extends question_type { $fs = get_file_storage(); foreach ($answerids as $answerid) { $fs->delete_area_files($context->id, 'question', 'answer', $answerid); - $DB->delete_records('question_answers', array('id' => $answerid)); + $DB->delete_records('question_answers', ['id' => $answerid]); } } @@ -241,7 +243,7 @@ class qtype_ordering extends question_type { * @param bool $withparts Whether to take into account clearwrong and shownumcorrect options. * @return int Count of hints on the form. */ - protected function count_hints_on_form($formdata, $withparts) { + protected function count_hints_on_form($formdata, $withparts): int { $numhints = parent::count_hints_on_form($formdata, $withparts); if (!empty($formdata->hintoptions)) { @@ -260,7 +262,7 @@ class qtype_ordering extends question_type { * @param bool $withparts whether to take into account clearwrong and shownumcorrect options. * @return bool is this particular hint data empty. */ - protected function is_hint_empty_in_form_data($formdata, $number, $withparts) { + protected function is_hint_empty_in_form_data($formdata, $number, $withparts): bool { return parent::is_hint_empty_in_form_data($formdata, $number, $withparts) && empty($formdata->hintoptions[$number]); } @@ -271,9 +273,9 @@ class qtype_ordering extends question_type { * @param object $formdata the data from the form. * @param int $number number of hint to get options from. * @param bool $withparts whether question have parts. - * @return string value to save into the options field of question_hints table. + * @return bool value to save into the options field of question_hints table. */ - protected function save_hint_options($formdata, $number, $withparts) { + protected function save_hint_options($formdata, $number, $withparts): bool { return !empty($formdata->hintoptions[$number]); } @@ -283,7 +285,7 @@ class qtype_ordering extends question_type { * @param object $hint The DB row from the question hints table. * @return question_hint_ordering Hints of question from record. */ - protected function make_hint($hint) { + protected function make_hint($hint): question_hint_ordering { return question_hint_ordering::load_from_record($hint); } @@ -314,18 +316,18 @@ class qtype_ordering extends question_type { * @return array keys are subquestionid, values are arrays of possible * responses to that subquestion. */ - public function get_possible_responses($questiondata) { - $responseclasses = array(); + public function get_possible_responses($questiondata): array { + $responseclasses = []; $itemcount = count($questiondata->options->answers); $position = 0; - foreach ($questiondata->options->answers as $answerid => $answer) { + foreach ($questiondata->options->answers as $answer) { $position += 1; - $classes = array(); + $classes = []; for ($i = 1; $i <= $itemcount; $i++) { $classes[$i] = new question_possible_response( - get_string('positionx', 'qtype_ordering', $i), - ($i === $position) / $itemcount); + get_string('positionx', 'qtype_ordering', $i), + ($i === $position) / $itemcount); } $subqid = question_utils::to_plain_text($answer->answer, $answer->answerformat); @@ -342,7 +344,7 @@ class qtype_ordering extends question_type { * @param mixed $value * @return bool If true, this item should be saved. */ - public function is_not_blank($value) { + public function is_not_blank(mixed $value): bool { if (is_array($value)) { $value = $value['text']; } @@ -356,17 +358,17 @@ class qtype_ordering extends question_type { * This function loads any question type specific options for the * question from the database into the question object. This information * is placed in the $question->options field. A question type is - * free, however, to decide on a internal structure of the options field. + * free, however, to decide on an internal structure of the options field. * @return bool Indicates success or failure. * @param object $question The question object for the question. This object * should be updated to include the question type * specific information (it is passed by reference). */ - public function get_question_options($question) { + public function get_question_options($question): bool { global $DB, $OUTPUT; // Load the options. - if (!$question->options = $DB->get_record('qtype_ordering_options', array('questionid' => $question->id))) { + if (!$question->options = $DB->get_record('qtype_ordering_options', ['questionid' => $question->id])) { echo $OUTPUT->notification('Error: Missing question options!'); return false; } @@ -374,7 +376,7 @@ class qtype_ordering extends question_type { // Load the answers - "fraction" is used to signify the order of the answers, // with id as a tie-break which should not be required. if (!$question->options->answers = $DB->get_records('question_answers', - array('question' => $question->id), 'fraction, id')) { + ['question' => $question->id], 'fraction, id')) { echo $OUTPUT->notification('Error: Missing question answers for ordering question ' . $question->id . '!'); return false; } @@ -389,9 +391,9 @@ class qtype_ordering extends question_type { * @param int $questionid The id of question being deleted. * @param int $contextid the context this quesiotn belongs to. */ - public function delete_question($questionid, $contextid) { + public function delete_question($questionid, $contextid): void { global $DB; - $DB->delete_records('qtype_ordering_options', array('questionid' => $questionid)); + $DB->delete_records('qtype_ordering_options', ['questionid' => $questionid]); parent::delete_question($questionid, $contextid); } @@ -399,43 +401,42 @@ class qtype_ordering extends question_type { * Import question from GIFT format * * @param array $lines - * @param object $question + * @param stdClass $question * @param qformat_gift $format - * @param string $extra (optional, default=null) - * @return object Question instance + * @param string|null $extra (optional, default=null) + * @return stdClass|bool Question instance */ - public function import_from_gift($lines, $question, $format, $extra=null) { + public function import_from_gift(array $lines, stdClass $question, qformat_gift $format, string $extra = null): bool|stdClass { global $CFG; require_once($CFG->dirroot.'/question/type/ordering/question.php'); // Extract question info from GIFT file $lines. - $questionname = '[^{]*'; $selectcount = '\d+'; $selecttype = '(?:ALL|EXACT|'. - 'RANDOM|REL|'. - 'CONTIGUOUS|CONTIG)?'; + 'RANDOM|REL|'. + 'CONTIGUOUS|CONTIG)?'; $layouttype = '(?:HORIZONTAL|HORI|H|1|'. - 'VERTICAL|VERT|V|0)?'; + 'VERTICAL|VERT|V|0)?'; $gradingtype = '(?:ALL_OR_NOTHING|'. - 'ABSOLUTE_POSITION|'. - 'ABSOLUTE|ABS|'. - 'RELATIVE_NEXT_EXCLUDE_LAST|'. - 'RELATIVE_NEXT_INCLUDE_LAST|'. - 'RELATIVE_ONE_PREVIOUS_AND_NEXT|'. - 'RELATIVE_ALL_PREVIOUS_AND_NEXT|'. - 'RELATIVE_TO_CORRECT|'. - 'RELATIVE|REL'. - 'LONGEST_ORDERED_SUBSET|'. - 'LONGEST_CONTIGUOUS_SUBSET)?'; + 'ABSOLUTE_POSITION|'. + 'ABSOLUTE|ABS|'. + 'RELATIVE_NEXT_EXCLUDE_LAST|'. + 'RELATIVE_NEXT_INCLUDE_LAST|'. + 'RELATIVE_ONE_PREVIOUS_AND_NEXT|'. + 'RELATIVE_ALL_PREVIOUS_AND_NEXT|'. + 'RELATIVE_TO_CORRECT|'. + 'RELATIVE|REL'. + 'LONGEST_ORDERED_SUBSET|'. + 'LONGEST_CONTIGUOUS_SUBSET)?'; $showgrading = '(?:SHOW|TRUE|YES|1|HIDE|FALSE|NO|0)?'; $numberingstyle = '(?:none|123|abc|ABCD|iii|IIII)?'; $search = '/^\s*>\s*('.$selectcount.')\s*'. - '('.$selecttype.')\s*'. - '('.$layouttype.')\s*'. - '('.$gradingtype.')\s*'. - '('.$showgrading.')\s*'. - '('.$numberingstyle.')\s*'. - '(.*?)\s*$/s'; + '('.$selecttype.')\s*'. + '('.$layouttype.')\s*'. + '('.$gradingtype.')\s*'. + '('.$showgrading.')\s*'. + '('.$numberingstyle.')\s*'. + '(.*?)\s*$/s'; // Item $1 the number of items to be shown. // Item $2 the extraction/grading type. // Item $3 the layout type. @@ -443,10 +444,10 @@ class qtype_ordering extends question_type { // Item $5 show the grading details (SHOW/HIDE). // Item $6 the numbering style (none/123/abc/...). // Item $7 the lines of items to be ordered. - if (! $extra) { + if (!$extra) { return false; // Format not recognized. } - if (! preg_match($search, $extra, $matches)) { + if (!preg_match($search, $extra, $matches)) { return false; // Format not recognized. } @@ -469,7 +470,7 @@ class qtype_ordering extends question_type { // Extract name. $name = false; - if (substr($text, 0, 2) == '::') { + if (str_starts_with($text, '::')) { $text = substr($text, 2); $pos = strpos($text, '::'); if (is_numeric($pos)) { @@ -481,7 +482,7 @@ class qtype_ordering extends question_type { // Extract question text format. $format = FORMAT_MOODLE; - if (substr($text, 0, 1) == '[') { + if (str_starts_with($text, '[')) { $text = substr($text, 1); $pos = strpos($text, ']'); if (is_numeric($pos)) { @@ -521,18 +522,18 @@ class qtype_ordering extends question_type { $selectcount = min(6, count($answers)); } $this->set_options_for_import($question, $layouttype, $selecttype, $selectcount, - $gradingtype, $showgrading, $numberingstyle); + $gradingtype, $showgrading, $numberingstyle); // Remove blank items. $answers = array_map('trim', $answers); $answers = array_filter($answers); // Remove blanks. // Set up answer arrays. - $question->answer = array(); - $question->answerformat = array(); - $question->fraction = array(); - $question->feedback = array(); - $question->feedbackformat = array(); + $question->answer = []; + $question->answerformat = []; + $question->fraction = []; + $question->feedback = []; + $question->feedbackformat = []; // Note that "fraction" field is used to denote sort order // "fraction" fields will be set to correct values later @@ -555,12 +556,12 @@ class qtype_ordering extends question_type { /** * Check that the required feedback fields exist * - * @param object $question + * @param stdClass $question */ - protected function check_ordering_combined_feedback(&$question) { + protected function check_ordering_combined_feedback(stdClass $question): void { foreach ($this->feedbackfields as $field) { if (empty($question->$field)) { - $question->$field = array('text' => '', 'format' => FORMAT_MOODLE, 'itemid' => 0, 'files' => null); + $question->$field = ['text' => '', 'format' => FORMAT_MOODLE, 'itemid' => 0, 'files' => null]; } } } @@ -569,78 +570,42 @@ class qtype_ordering extends question_type { * Given question object, returns array with array layouttype, selecttype, selectcount, gradingtype, showgrading * where layouttype, selecttype, gradingtype and showgrading are string representations. * - * @param object $question + * @param stdClass $question * @return array(layouttype, selecttype, selectcount, gradingtype, $showgrading, $numberingstyle) */ - public function extract_options_for_export($question) { + public function extract_options_for_export(stdClass $question): array { - switch ($question->options->layouttype) { - case qtype_ordering_question::LAYOUT_VERTICAL: - $layouttype = 'VERTICAL'; - break; - case qtype_ordering_question::LAYOUT_HORIZONTAL: - $layouttype = 'HORIZONTAL'; - break; - default: - $layouttype = ''; // Shouldn't happen !! - } + $layouttype = match ($question->options->layouttype) { + qtype_ordering_question::LAYOUT_VERTICAL => 'VERTICAL', + qtype_ordering_question::LAYOUT_HORIZONTAL => 'HORIZONTAL', + default => '', // Shouldn't happen !! + }; - switch ($question->options->selecttype) { - case qtype_ordering_question::SELECT_ALL: - $selecttype = 'ALL'; - break; - case qtype_ordering_question::SELECT_RANDOM: - $selecttype = 'RANDOM'; - break; - case qtype_ordering_question::SELECT_CONTIGUOUS: - $selecttype = 'CONTIGUOUS'; - break; - default: - $selecttype = ''; // Shouldn't happen !! - } + $selecttype = match ($question->options->selecttype) { + qtype_ordering_question::SELECT_ALL => 'ALL', + qtype_ordering_question::SELECT_RANDOM => 'RANDOM', + qtype_ordering_question::SELECT_CONTIGUOUS => 'CONTIGUOUS', + default => '', // Shouldn't happen !! + }; - switch ($question->options->gradingtype) { - case qtype_ordering_question::GRADING_ALL_OR_NOTHING: - $gradingtype = 'ALL_OR_NOTHING'; - break; - case qtype_ordering_question::GRADING_ABSOLUTE_POSITION: - $gradingtype = 'ABSOLUTE_POSITION'; - break; - case qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST: - $gradingtype = 'RELATIVE_NEXT_EXCLUDE_LAST'; - break; - case qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST: - $gradingtype = 'RELATIVE_NEXT_INCLUDE_LAST'; - break; - case qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT: - $gradingtype = 'RELATIVE_ONE_PREVIOUS_AND_NEXT'; - break; - case qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT: - $gradingtype = 'RELATIVE_ALL_PREVIOUS_AND_NEXT'; - break; - case qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET: - $gradingtype = 'LONGEST_ORDERED_SUBSET'; - break; - case qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET: - $gradingtype = 'LONGEST_CONTIGUOUS_SUBSET'; - break; - case qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT: - $gradingtype = 'RELATIVE_TO_CORRECT'; - break; - default: - $gradingtype = ''; // Shouldn't happen !! - } + $gradingtype = match ($question->options->gradingtype) { + qtype_ordering_question::GRADING_ALL_OR_NOTHING => 'ALL_OR_NOTHING', + qtype_ordering_question::GRADING_ABSOLUTE_POSITION => 'ABSOLUTE_POSITION', + qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST => 'RELATIVE_NEXT_EXCLUDE_LAST', + qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST => 'RELATIVE_NEXT_INCLUDE_LAST', + qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT => 'RELATIVE_ONE_PREVIOUS_AND_NEXT', + qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT => 'RELATIVE_ALL_PREVIOUS_AND_NEXT', + qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET => 'LONGEST_ORDERED_SUBSET', + qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET => 'LONGEST_CONTIGUOUS_SUBSET', + qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT => 'RELATIVE_TO_CORRECT', + default => '', // Shouldn't happen !! + }; - switch ($question->options->showgrading) { - case 0: - $showgrading = 'HIDE'; - break; - case 1: - $showgrading = 'SHOW'; - break; - default: - $showgrading = ''; // Shouldn't happen !! - } + $showgrading = match ($question->options->showgrading) { + 0 => 'HIDE', + 1 => 'SHOW', + default => '', // Shouldn't happen !! + }; if (empty($question->options->numberingstyle)) { $numberingstyle = qtype_ordering_question::NUMBERING_STYLE_DEFAULT; @@ -651,18 +616,18 @@ class qtype_ordering extends question_type { // Note: this used to be (selectcount + 2). $selectcount = $question->options->selectcount; - return array($layouttype, $selecttype, $selectcount, $gradingtype, $showgrading, $numberingstyle); + return [$layouttype, $selecttype, $selectcount, $gradingtype, $showgrading, $numberingstyle]; } /** * Exports question to GIFT format * - * @param object $question + * @param stdClass $question * @param qformat_gift $format - * @param string $extra (optional, default=null) + * @param string|null $extra (optional, default=null) * @return string GIFT representation of question */ - public function export_to_gift($question, $format, $extra=null) { + public function export_to_gift(stdClass $question, qformat_gift $format, string $extra = null): string { global $CFG; require_once($CFG->dirroot.'/question/type/ordering/question.php'); @@ -672,25 +637,18 @@ class qtype_ordering extends question_type { $output .= '::'.$question->name.'::'; } - switch ($question->questiontextformat) { - case FORMAT_HTML: - $output .= '[html]'; - break; - case FORMAT_PLAIN: - $output .= '[plain]'; - break; - case FORMAT_MARKDOWN: - $output .= '[markdown]'; - break; - case FORMAT_MOODLE: - $output .= '[moodle]'; - break; - } + $output .= match ($question->questiontextformat) { + FORMAT_HTML => '[html]', + FORMAT_PLAIN => '[plain]', + FORMAT_MARKDOWN => '[markdown]', + FORMAT_MOODLE => '[moodle]', + default => '', + }; $output .= $question->questiontext.'{'; list($layouttype, $selecttype, $selectcount, $gradingtype, $showgrading, $numberingstyle) = - $this->extract_options_for_export($question); + $this->extract_options_for_export($question); $output .= ">$selectcount $selecttype $layouttype $gradingtype $showgrading $numberingstyle".PHP_EOL; foreach ($question->options->answers as $answer) { @@ -709,12 +667,12 @@ class qtype_ordering extends question_type { * @param string $extra (optional, default=null) * @return string XML representation of question */ - public function export_to_xml($question, qformat_xml $format, $extra=null) { + public function export_to_xml($question, qformat_xml $format, $extra = null): string { global $CFG; require_once($CFG->dirroot.'/question/type/ordering/question.php'); list($layouttype, $selecttype, $selectcount, $gradingtype, $showgrading, $numberingstyle) = - $this->extract_options_for_export($question); + $this->extract_options_for_export($question); $output = ''; $output .= " $layouttype\n"; @@ -734,7 +692,7 @@ class qtype_ordering extends question_type { foreach ($question->options->answers as $answer) { $output .= ' format($answer->answerformat).">\n"; $output .= $format->writetext($answer->answer, 3); - if ($feedback = trim($answer->feedback)) { // Usually there is no feedback. + if (trim($answer->feedback)) { // Usually there is no feedback. $output .= ' format($answer->feedbackformat).">\n"; $output .= $format->writetext($answer->feedback, 4); $output .= $format->write_files($answer->feedbackfiles); @@ -755,14 +713,14 @@ class qtype_ordering extends question_type { * @param array $data * @param qtype_ordering $question (or null) * @param qformat_xml $format - * @param string $extra (optional, default=null) - * @return object New question object + * @param null $extra (optional, default=null) + * @return object|bool New question object */ - public function import_from_xml($data, $question, qformat_xml $format, $extra=null) { + public function import_from_xml($data, $question, qformat_xml $format, $extra = null): object|bool { global $CFG; require_once($CFG->dirroot.'/question/type/ordering/question.php'); - $questiontype = $format->getpath($data, array('@', 'type'), ''); + $questiontype = $format->getpath($data, ['@', 'type'], ''); if ($questiontype != 'ordering') { return false; @@ -783,23 +741,23 @@ class qtype_ordering extends question_type { $selecttype = 'logical'; $selectcount = 'studentsee'; } - $layouttype = $format->getpath($data, array('#', 'layouttype', 0, '#'), 'VERTICAL'); - $selecttype = $format->getpath($data, array('#', $selecttype, 0, '#'), 'RANDOM'); - $selectcount = $format->getpath($data, array('#', $selectcount, 0, '#'), 6); - $gradingtype = $format->getpath($data, array('#', 'gradingtype', 0, '#'), 'RELATIVE'); - $showgrading = $format->getpath($data, array('#', 'showgrading', 0, '#'), '1'); - $numberingstyle = $format->getpath($data, array('#', 'numberingstyle', 0, '#'), '1'); + $layouttype = $format->getpath($data, ['#', 'layouttype', 0, '#'], 'VERTICAL'); + $selecttype = $format->getpath($data, ['#', $selecttype, 0, '#'], 'RANDOM'); + $selectcount = $format->getpath($data, ['#', $selectcount, 0, '#'], 6); + $gradingtype = $format->getpath($data, ['#', 'gradingtype', 0, '#'], 'RELATIVE'); + $showgrading = $format->getpath($data, ['#', 'showgrading', 0, '#'], '1'); + $numberingstyle = $format->getpath($data, ['#', 'numberingstyle', 0, '#'], '1'); $this->set_options_for_import($newquestion, $layouttype, $selecttype, $selectcount, - $gradingtype, $showgrading, $numberingstyle); + $gradingtype, $showgrading, $numberingstyle); - $newquestion->answer = array(); - $newquestion->answerformat = array(); - $newquestion->fraction = array(); - $newquestion->feedback = array(); - $newquestion->feedbackformat = array(); + $newquestion->answer = []; + $newquestion->answerformat = []; + $newquestion->fraction = []; + $newquestion->feedback = []; + $newquestion->feedbackformat = []; $i = 0; - while ($answer = $format->getpath($data, array('#', 'answer', $i), '')) { + while ($answer = $format->getpath($data, ['#', 'answer', $i], '')) { $ans = $format->import_answer($answer, true, $format->get_format($newquestion->questiontextformat)); $newquestion->answer[$i] = $ans->answer; $newquestion->fraction[$i] = 1; // Will be reset later in save_question_options(). @@ -807,7 +765,7 @@ class qtype_ordering extends question_type { $i++; } - $format->import_combined_feedback($newquestion, $data, false); + $format->import_combined_feedback($newquestion, $data); $newquestion->shownumcorrect = $format->getpath($data, ['#', 'shownumcorrect', 0, '#'], null); // Check that the required feedback fields exist. $this->check_ordering_combined_feedback($newquestion); @@ -842,7 +800,7 @@ class qtype_ordering extends question_type { * @param integer $maxnamelength (optional, default=42) * @return string Fixed name */ - public function fix_questionname($name, $defaultname='', $maxnamelength = 42) { + public function fix_questionname(string $name, string $defaultname = '', int $maxnamelength = 42): string { if (trim($name) == '') { if ($defaultname) { $name = $defaultname; @@ -863,58 +821,29 @@ class qtype_ordering extends question_type { /** * Set layouttype, selecttype, selectcount, gradingtype, showgrading based on their textual representation * - * @param object $question (passed by reference) - * @param string $layout the layout type - * @param string $select the select type - * @param string $count the number of items to display - * @param string $grading the grading type - * @param string $show the grading details or not + * @param stdClass $question the question object + * @param string $layouttype the layout type + * @param string $selecttype the select type + * @param string $selectcount the number of items to display + * @param string $gradingtype the grading type + * @param string $showgrading the grading details or not + * @param string $numberingstyle the numbering style */ - public function set_options_for_import(&$question, $layouttype, $selecttype, $selectcount, - $gradingtype, $showgrading, $numberingstyle) { + public function set_options_for_import(stdClass $question, string $layouttype, string $selecttype, string $selectcount, + string $gradingtype, string $showgrading, string $numberingstyle): void { // Set "layouttype" option. - switch (strtoupper($layouttype)) { - - case 'HORIZONTAL': - case 'HORI': - case 'H': - case '1': - $question->layouttype = qtype_ordering_question::LAYOUT_HORIZONTAL; - break; - - case 'VERTICAL': - case 'VERT': - case 'V': - case '0': - $question->layouttype = qtype_ordering_question::LAYOUT_VERTICAL; - break; - - default: - $question->layouttype = qtype_ordering_question::LAYOUT_VERTICAL; - } + $question->layouttype = match (strtoupper($layouttype)) { + 'HORIZONTAL', 'HORI', 'H', '1' => qtype_ordering_question::LAYOUT_HORIZONTAL, + default => qtype_ordering_question::LAYOUT_VERTICAL, + }; // Set "selecttype" option. - switch (strtoupper($selecttype)) { - - case 'ALL': - case 'EXACT': - $question->selecttype = qtype_ordering_question::SELECT_ALL; - break; - - case 'RANDOM': - case 'REL': - $question->selecttype = qtype_ordering_question::SELECT_RANDOM; - break; - - case 'CONTIGUOUS': - case 'CONTIG': - $question->selecttype = qtype_ordering_question::SELECT_CONTIGUOUS; - break; - - default: - $question->selecttype = qtype_ordering_question::SELECT_RANDOM; - } + $question->selecttype = match (strtoupper($selecttype)) { + 'ALL', 'EXACT' => qtype_ordering_question::SELECT_ALL, + 'CONTIGUOUS', 'CONTIG' => qtype_ordering_question::SELECT_CONTIGUOUS, + default => qtype_ordering_question::SELECT_RANDOM, + }; // Set "selectcount" option - this used to be ($count - 2). if (is_numeric($selectcount)) { @@ -924,96 +853,39 @@ class qtype_ordering extends question_type { } // Set "gradingtype" option. - switch (strtoupper($gradingtype)) { - - case 'ALL_OR_NOTHING': - $question->gradingtype = qtype_ordering_question::GRADING_ALL_OR_NOTHING; - break; - - case 'ABS': - case 'ABSOLUTE': - case 'ABSOLUTE_POSITION': - $question->gradingtype = qtype_ordering_question::GRADING_ABSOLUTE_POSITION; - break; - - case 'REL': - case 'RELATIVE': - case 'RELATIVE_NEXT_EXCLUDE_LAST': - $question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST; - break; - - case 'RELATIVE_NEXT_INCLUDE_LAST': - $question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST; - break; - - case 'RELATIVE_ONE_PREVIOUS_AND_NEXT': - $question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT; - break; - - case 'RELATIVE_ALL_PREVIOUS_AND_NEXT': - $question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT; - break; - - case 'LONGEST_ORDERED_SUBSET': - $question->gradingtype = qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET; - break; - - case 'LONGEST_CONTIGUOUS_SUBSET': - $question->gradingtype = qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET; - break; - - case 'RELATIVE_TO_CORRECT': - $question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT; - break; - - default: - $question->gradingtype = qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST; - } + $question->gradingtype = match (strtoupper($gradingtype)) { + 'ALL_OR_NOTHING' => qtype_ordering_question::GRADING_ALL_OR_NOTHING, + 'ABS', 'ABSOLUTE', 'ABSOLUTE_POSITION' => qtype_ordering_question::GRADING_ABSOLUTE_POSITION, + 'RELATIVE_NEXT_INCLUDE_LAST' => qtype_ordering_question::GRADING_RELATIVE_NEXT_INCLUDE_LAST, + 'RELATIVE_ONE_PREVIOUS_AND_NEXT' => qtype_ordering_question::GRADING_RELATIVE_ONE_PREVIOUS_AND_NEXT, + 'RELATIVE_ALL_PREVIOUS_AND_NEXT' => qtype_ordering_question::GRADING_RELATIVE_ALL_PREVIOUS_AND_NEXT, + 'LONGEST_ORDERED_SUBSET' => qtype_ordering_question::GRADING_LONGEST_ORDERED_SUBSET, + 'LONGEST_CONTIGUOUS_SUBSET' => qtype_ordering_question::GRADING_LONGEST_CONTIGUOUS_SUBSET, + 'RELATIVE_TO_CORRECT' => qtype_ordering_question::GRADING_RELATIVE_TO_CORRECT, + default => qtype_ordering_question::GRADING_RELATIVE_NEXT_EXCLUDE_LAST, + }; // Set "showgrading" option. - switch (strtoupper($showgrading)) { - - case 'SHOW': - case 'TRUE': - case 'YES': - $question->showgrading = 1; - break; - - case 'HIDE': - case 'FALSE': - case 'NO': - $question->showgrading = 0; - break; - - default: - $question->showgrading = 1; - break; - } + $question->showgrading = match (strtoupper($showgrading)) { + 'HIDE', 'FALSE', 'NO' => 0, + default => 1, + }; // Set "numberingstyle" option. - switch ($numberingstyle) { - - case 'none': - case '123': - case 'abc': - case 'ABCD': - case 'iii': - case 'IIII': - $question->numberingstyle = $numberingstyle; - break; - - default: - $question->numberingstyle = qtype_ordering_question::NUMBERING_STYLE_DEFAULT; - } + $question->numberingstyle = match ($numberingstyle) { + 'none', '123', 'abc', 'ABCD', 'iii', 'IIII' => $numberingstyle, + default => qtype_ordering_question::NUMBERING_STYLE_DEFAULT, + }; } /** * Return the answer numbering style. * This method is used by "tests/questiontype_test.php". - * @param $questiondata + * + * @param stdClass $questiondata * @return string */ - public function get_numberingstyle($questiondata) { + public function get_numberingstyle(stdClass $questiondata): string { return $questiondata->options->numberingstyle; } } diff --git a/question/type/ordering/renderer.php b/question/type/ordering/renderer.php index 26871249918..c98b96f68da 100644 --- a/question/type/ordering/renderer.php +++ b/question/type/ordering/renderer.php @@ -93,7 +93,12 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer { } // In the multi-tries, the highlight response base on the hint highlight option. - if (isset($options->highlightresponse) && $options->highlightresponse) { + $hint = null; + if (method_exists($qa->get_behaviour(), 'get_applicable_hint')) { + /** @var \qtype_ordering\question_hint_ordering $hint */ + $hint = $qa->get_behaviour()->get_applicable_hint(); + } + if ($hint && $hint->highlightresponse) { $sortablelist .= ' notactive'; } @@ -153,7 +158,7 @@ class qtype_ordering_renderer extends qtype_with_combined_feedback_renderer { break; } - if (isset($options->highlightresponse) && $options->highlightresponse) { + if ($hint && $hint->highlightresponse) { $score = $this->get_ordering_item_score($question, $position, $answerid); // To do: we need image calculation here in MDL-79873. list($score, $maxscore, $fraction, $percent, $class, $img) = $score;