AutorÃa | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.// Prevent direct access to this script.defined('MOODLE_INTERNAL') || die();// Include required files.require_once($CFG->dirroot.'/question/type/ordering/question.php');/*** Ordering editing form definition** @package qtype_ordering* @copyright 2013 Gordon Bateson (gordon.bateson@gmail.com)* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class qtype_ordering_edit_form extends question_edit_form {/** Rows count in answer field */const TEXTFIELD_ROWS = 2;/** Cols count in answer field */const TEXTFIELD_COLS = 60;/** Number of answers in question by default */const NUM_ITEMS_DEFAULT = 6;/** Minimum number of answers to show */const NUM_ITEMS_MIN = 3;/** Number of answers to add on demand */const NUM_ITEMS_ADD = 1;public function qtype(): string {return 'ordering';}/*** Plugin name is class name without trailing "_edit_form"** @return string*/public function plugin_name(): string {return 'qtype_ordering';}public function definition_inner($mform): void {// Field for layouttype.$options = qtype_ordering_question::get_layout_types();$mform->addElement('select', 'layouttype', get_string('layouttype', 'qtype_ordering'), $options);$mform->addHelpButton('layouttype', 'layouttype', 'qtype_ordering');$mform->setDefault('layouttype', $this->get_default_value('layouttype', qtype_ordering_question::LAYOUT_VERTICAL));// Field for selecttype.$options = qtype_ordering_question::get_select_types();$mform->addElement('select', 'selecttype', get_string('selecttype', 'qtype_ordering'), $options);$mform->addHelpButton('selecttype', 'selecttype', 'qtype_ordering');$mform->setDefault('selecttype', $this->get_default_value('selecttype', qtype_ordering_question::SELECT_ALL));// Field for selectcount.$mform->addElement('text', 'selectcount', get_string('selectcount', 'qtype_ordering'), ['size' => 2]);$mform->setDefault('selectcount', qtype_ordering_question::MIN_SUBSET_ITEMS);$mform->setType('selectcount', PARAM_INT);// Hide the field if 'Item selection type' is set to select all items.$mform->hideIf('selectcount', 'selecttype', 'eq', qtype_ordering_question::SELECT_ALL);$mform->addHelpButton('selectcount', 'selectcount', 'qtype_ordering');$mform->addRule('selectcount', null, 'numeric', null, 'client');// Field for gradingtype.$options = qtype_ordering_question::get_grading_types();$mform->addElement('select', 'gradingtype', get_string('gradingtype', 'qtype_ordering'), $options);$mform->addHelpButton('gradingtype', 'gradingtype', 'qtype_ordering');$mform->setDefault('gradingtype',$this->get_default_value('gradingtype', qtype_ordering_question::GRADING_ABSOLUTE_POSITION));// Field for showgrading.$options = [0 => get_string('hide'), 1 => get_string('show')];$mform->addElement('select', 'showgrading', get_string('showgrading', 'qtype_ordering'), $options);$mform->addHelpButton('showgrading', 'showgrading', 'qtype_ordering');$mform->setDefault('showgrading', $this->get_default_value('showgrading', 1));// Field for numberingstyle.$options = qtype_ordering_question::get_numbering_styles();$mform->addElement('select', 'numberingstyle', get_string('numberingstyle', 'qtype_ordering'), $options);$mform->addHelpButton('numberingstyle', 'numberingstyle', 'qtype_ordering');$mform->setDefault('numberingstyle',$this->get_default_value('numberingstyle', qtype_ordering_question::NUMBERING_STYLE_DEFAULT));$mform->addElement('header', 'answersheader', get_string('draggableitems', 'qtype_ordering'));$mform->setExpanded('answersheader', true);// Field for the answers.$elements = [];$options = [];$elements[] = $mform->createElement('editor', 'answer', get_string('draggableitemno', 'qtype_ordering'),$this->get_editor_attributes(), $this->get_editor_options());$elements[] = $mform->createElement('submit', 'answer' . 'removeeditor', get_string('removeeditor', 'qtype_ordering'),['onclick' => 'skipClientValidation = true;']);$options['answer'] = ['type' => PARAM_RAW];$this->add_repeat_elements($mform, 'answer', $elements, $options);// Adjust HTML editor and removal buttons.$this->adjust_html_editors($mform, 'answer');// Adding feedback fields (=Combined feedback).$this->add_combined_feedback_fields(true);// Adding interactive settings (=Multiple tries).$this->add_interactive_settings(false, true);}/*** Returns answer repeats count** @param stdClass $question* @return int*/protected function get_answer_repeats(stdClass $question): int {if (isset($question->id)) {$repeats = count($question->options->answers);} else {$repeats = self::NUM_ITEMS_DEFAULT;}if ($repeats < self::NUM_ITEMS_MIN) {$repeats = self::NUM_ITEMS_MIN;}return $repeats;}/*** Returns editor attributes** @return array*/protected function get_editor_attributes(): array {return ['rows' => self::TEXTFIELD_ROWS,'cols' => self::TEXTFIELD_COLS,];}/*** Returns editor options** @return array*/protected function get_editor_options(): array {return ['context' => $this->context,'maxfiles' => EDITOR_UNLIMITED_FILES,'noclean' => true,];}/*** Resets editor format to specified** @param MoodleQuickForm_editor $editor* @param int|string $format* @return int*/protected function reset_editor_format(MoodleQuickForm_editor $editor, int|string $format = FORMAT_MOODLE): int {$value = $editor->getValue();$value['format'] = $format;$editor->setValue($value);return $format;}/*** Adjust HTML editor and removal buttons.** @param MoodleQuickForm $mform* @param string $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 = [];if (isset($this->question->options->answers)) {$ids = array_keys($this->question->options->answers);} else {$ids = [];}$defaultanswerformat = get_config('qtype_ordering', 'defaultanswerformat');$repeats = 'count'.$name.'s'; // E.g. countanswers.if ($mform->elementExists($repeats)) {// Use mform element to get number of repeats.$repeats = $mform->getElement($repeats)->getValue();} else {// Determine number of repeats by object sniffing.$repeats = 0;while ($mform->elementExists($name."[$repeats]")) {$repeats++;}}for ($i = 0; $i < $repeats; $i++) {$editor = $mform->getElement($name."[$i]");$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// new : the simplified name of the button to satisfy "no_submit_button_pressed()" in lib/formslib.php.$oldname = $name.'removeeditor['.$i.']';$newname = $name.'removeeditor_'.$i;// Remove HTML editor, if necessary.if (optional_param($newname, 0, PARAM_RAW)) {$format = $this->reset_editor_format($editor);$_POST['answer'][$i]['format'] = $format; // Overwrite incoming data.} else if ($id) {$format = $this->question->options->answers[$id]->answerformat;} else {$format = $this->reset_editor_format($editor, $defaultanswerformat);}// Check we have a submit button - it should always be there !!if ($mform->elementExists($oldname)) {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 {$submit = $mform->getElement($oldname);$submit->setName($newname);}$mform->registerNoSubmitButton($newname);}}}protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false): array {$mform = $this->_form;$repeated = [];$repeated[] = $mform->createElement('editor', 'hint', get_string('hintn', 'question'),['rows' => 5], $this->editoroptions);$repeatedoptions['hint']['type'] = PARAM_RAW;$optionelements = [];if ($withshownumpartscorrect) {$optionelements[] = $mform->createElement('advcheckbox', 'hintshownumcorrect', '',get_string('shownumpartscorrect', 'question'));}$optionelements[] = $mform->createElement('advcheckbox', 'hintoptions', '',get_string('highlightresponse', 'qtype_ordering'));if (count($optionelements)) {$repeated[] = $mform->createElement('group', 'hintoptions',get_string('hintnoptions', 'question'), $optionelements, null, false);}return [$repeated, $repeatedoptions];}public function data_preprocessing($question): stdClass {$question = parent::data_preprocessing($question);$question = $this->data_preprocessing_answers($question, true);// Preprocess feedback.$question = $this->data_preprocessing_combined_feedback($question, true);$question = $this->data_preprocessing_hints($question, false, true);// Preprocess answers and fractions.$question->answer = [];$question->fraction = [];if (empty($question->options->answers)) {$answerids = [];} else {$answerids = array_keys($question->options->answers);}$defaultanswerformat = get_config('qtype_ordering', 'defaultanswerformat');$repeats = $this->get_answer_repeats($question);for ($i = 0; $i < $repeats; $i++) {if ($answerid = array_shift($answerids)) {$answer = $question->options->answers[$answerid];} else {$answer = (object) ['answer' => '', 'answerformat' => $defaultanswerformat];$answerid = 0;}if (empty($question->id)) {$question->answer[$i] = $answer->answer;} else {$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] = ['text' => $text,'format' => $format,'itemid' => $itemid,];}$question->fraction[$i] = ($i + 1);}// Defining default values.$names = ['layouttype' => qtype_ordering_question::LAYOUT_VERTICAL,'selecttype' => qtype_ordering_question::SELECT_ALL,'selectcount' => qtype_ordering_question::MIN_SUBSET_ITEMS,'gradingtype' => qtype_ordering_question::GRADING_ABSOLUTE_POSITION,'showgrading' => 1, // 1 means SHOW.'numberingstyle' => qtype_ordering_question::NUMBERING_STYLE_DEFAULT,];foreach ($names as $name => $default) {$question->$name = $question->options->$name ?? $this->get_default_value($name, $default);}return $question;}protected function data_preprocessing_hints($question, $withclearwrong = false, $withshownumpartscorrect = false): stdClass {if (empty($question->hints)) {return $question;}parent::data_preprocessing_hints($question, $withclearwrong, $withshownumpartscorrect);$question->hintoptions = [];foreach ($question->hints as $hint) {$question->hintoptions[] = $hint->options;}return $question;}public function validation($data, $files): array {$errors = [];$minsubsetitems = qtype_ordering_question::MIN_SUBSET_ITEMS;// Make sure the entered size of the subset is no less than the defined minimum.if ($data['selecttype'] != qtype_ordering_question::SELECT_ALL && $data['selectcount'] < $minsubsetitems) {$errors['selectcount'] = get_string('notenoughsubsetitems', 'qtype_ordering', $minsubsetitems);}// Identify duplicates and report as an error.$answers = [];$answercount = 0;foreach ($data['answer'] as $answer) {if (is_array($answer)) {$answer = $answer['text'];}if ($answer = trim($answer)) {if (in_array($answer, $answers)) {$i = array_search($answer, $answers);$item = get_string('draggableitemno', 'qtype_ordering');$item = str_replace('{no}', $i + 1, $item);$item = html_writer::link("#id_answer_$i", $item);$a = (object) ['text' => $answer, 'item' => $item];$errors["answer[$answercount]"] = get_string('duplicatesnotallowed', 'qtype_ordering', $a);} else {$answers[] = $answer;}$answercount++;}}// 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', 'qtype_ordering', 2);if ($answercount == 0) {$errors['answer[0]'] = get_string('notenoughanswers', 'qtype_ordering', 2);}}// If adding a new ordering question, update defaults.if (empty($errors) && empty($data['id'])) {$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]);}}}return $errors;}/*** Get array of countable item types** @param string $type* @param int $max* @return array (type => description)*/protected function get_addcount_options(string $type, int $max = 10): array {// Generate options.$options = [];for ($i = 1; $i <= $max; $i++) {if ($i == 1) {$options[$i] = get_string('addsingle'.$type, 'qtype_ordering');} else {$options[$i] = get_string('addmultiple'.$type.'s', 'qtype_ordering', $i);}}return $options;}/*** Add repeated elements with a button allowing a selectable number of new elements** @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(MoodleQuickForm $mform, string $type, array $elements, array $options): void {// Cache element names.$types = $type.'s';$addtypes = 'add'.$types;$counttypes = 'count'.$types;$addtypescount = $addtypes.'count';$addtypesgroup = $addtypes.'group';$repeats = $this->get_answer_repeats($this->question);$count = optional_param($addtypescount, self::NUM_ITEMS_ADD, PARAM_INT);$label = ($count == 1 ? 'addsingle'.$type : 'addmultiple'.$types);$label = get_string($label, 'qtype_ordering', $count);$this->repeat_elements($elements, $repeats, $options, $counttypes, $addtypes, $count, $label, true);// Remove the original "Add xxx" button ...$mform->removeElement($addtypes);// ... and replace it with "Add" button + select group.$options = $this->get_addcount_options($type);$mform->addGroup([$mform->createElement('submit', $addtypes, get_string('add')),$mform->createElement('select', $addtypescount, '', $options),], $addtypesgroup, '', ' ', false);// Set default value and type of select element.$mform->setDefault($addtypescount, $count);$mform->setType($addtypescount, PARAM_INT);}}