MDL-79863 qtype_ordering: Coding style improvements part 2

Co-authored by: Tim Hunt <[email protected]>
This commit is contained in:
Kevin Percy
2024-04-04 16:20:50 +08:00
committed by Mathew May
parent d00061da26
commit e1357ee51c
10 changed files with 347 additions and 450 deletions
+5 -2
View File
@@ -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'
@@ -34,13 +34,13 @@ class moodle1_qtype_ordering_handler extends moodle1_qtype_handler {
* Returns the list of paths within one <QUESTION> 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']);
}
}
}
@@ -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.
@@ -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) {
@@ -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';
@@ -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;
}
}
+86 -83
View File
@@ -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);
+18
View File
@@ -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;
+196 -324
View File
@@ -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>$layouttype</layouttype>\n";
@@ -734,7 +692,7 @@ class qtype_ordering extends question_type {
foreach ($question->options->answers as $answer) {
$output .= ' <answer fraction="'.$answer->fraction.'" '.$format->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 .= ' <feedback '.$format->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;
}
}
+7 -2
View File
@@ -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;