From b3878dbdb8946735ee31c31460a69071a6b8031c Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 24 Sep 2011 15:47:37 +0700 Subject: [PATCH 001/178] MDL-47494 ddmarker: NOBUG initial commit of new ddmarker code. Have changed file names and occurances of 'ddmarker' in code. Also changed relevant tables and table contents in install.xml and changed file names. --- question/type/ddmarker/README.txt | 20 + .../backup_qtype_ddmarker_plugin.class.php | 97 ++ .../restore_qtype_ddmarker_plugin.class.php | 155 ++++ question/type/ddmarker/db/install.xml | 54 ++ question/type/ddmarker/edit_ddmarker_form.php | 381 ++++++++ .../type/ddmarker/lang/en/qtype_ddmarker.php | 66 ++ question/type/ddmarker/lib.php | 39 + question/type/ddmarker/pix/icon.gif | Bin 0 -> 361 bytes question/type/ddmarker/question.php | 224 +++++ question/type/ddmarker/questiontype.php | 464 ++++++++++ question/type/ddmarker/renderer.php | 180 ++++ question/type/ddmarker/simpletest/helper.php | 131 +++ .../type/ddmarker/simpletest/testquestion.php | 269 ++++++ .../ddmarker/simpletest/testquestiontype.php | 58 ++ .../ddmarker/simpletest/testwalkthrough.php | 857 ++++++++++++++++++ question/type/ddmarker/styles.css | 76 ++ question/type/ddmarker/version.php | 29 + question/type/ddmarker/yui/dd/dd.js | 468 ++++++++++ question/type/ddmarker/yui/form/form.js | 341 +++++++ 19 files changed, 3909 insertions(+) create mode 100644 question/type/ddmarker/README.txt create mode 100644 question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php create mode 100644 question/type/ddmarker/backup/moodle2/restore_qtype_ddmarker_plugin.class.php create mode 100644 question/type/ddmarker/db/install.xml create mode 100644 question/type/ddmarker/edit_ddmarker_form.php create mode 100644 question/type/ddmarker/lang/en/qtype_ddmarker.php create mode 100644 question/type/ddmarker/lib.php create mode 100644 question/type/ddmarker/pix/icon.gif create mode 100644 question/type/ddmarker/question.php create mode 100644 question/type/ddmarker/questiontype.php create mode 100644 question/type/ddmarker/renderer.php create mode 100644 question/type/ddmarker/simpletest/helper.php create mode 100644 question/type/ddmarker/simpletest/testquestion.php create mode 100644 question/type/ddmarker/simpletest/testquestiontype.php create mode 100644 question/type/ddmarker/simpletest/testwalkthrough.php create mode 100644 question/type/ddmarker/styles.css create mode 100644 question/type/ddmarker/version.php create mode 100644 question/type/ddmarker/yui/dd/dd.js create mode 100644 question/type/ddmarker/yui/form/form.js diff --git a/question/type/ddmarker/README.txt b/question/type/ddmarker/README.txt new file mode 100644 index 00000000000..7f37695349c --- /dev/null +++ b/question/type/ddmarker/README.txt @@ -0,0 +1,20 @@ +A drag-and-drop question type. +You can use text and / or images as drag items onto defined drop zones on a background image. + +This question type requires that gapselect question type +https://github.com/timhunt/moodle-qtype_gapselect/ +to be installed in order to work. + +This question type was written by Jamie Pratt (http://jamiep.org/). + +This question type is compatible with Moodle 2.1. + +To install using git, type this command in the root of your Moodle install : + +git clone git://github.com/jamiepratt/moodle-qtype_ddmarker.git question/type/ddmarker + +Then add question/type/ddmarker to your git ignore. + +Alternatively, download the zip from + https://github.com/jamiepratt/moodle-qtype_ddmarker/zipball/master +unzip it into the question/type folder, and then rename the new folder to ddmarker. diff --git a/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php b/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php new file mode 100644 index 00000000000..5ddaaea2137 --- /dev/null +++ b/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php @@ -0,0 +1,97 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +defined('MOODLE_INTERNAL') || die(); + + +/** + * Provides the information to backup ddmarker questions + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class backup_qtype_ddmarker_plugin extends backup_qtype_plugin { + + protected static function qtype_name() { + return 'ddmarker'; + } + + /** + * Returns the qtype information to attach to question element + */ + protected function define_question_plugin_structure() { + $qtype = self::qtype_name(); + $plugin = $this->get_plugin_element(null, '../../qtype', $qtype); + + $pluginwrapper = new backup_nested_element($this->get_recommended_name()); + + $plugin->add_child($pluginwrapper); + + $dds = new backup_nested_element($qtype, array('id'), array( + 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', + 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', + 'incorrectfeedback', 'incorrectfeedbackformat', 'shownumcorrect')); + + $pluginwrapper->add_child($dds); + $drags = new backup_nested_element('drags'); + + $drag = new backup_nested_element('drag', array('id'), + array('no', 'draggroup', 'infinite', 'label')); + $drops = new backup_nested_element('drops'); + + $drop = new backup_nested_element('drop', array('id'), + array('no', 'xleft', 'ytop', 'choice', 'label')); + + $dds->set_source_table("qtype_{$qtype}", + array('questionid' => backup::VAR_PARENTID)); + + $pluginwrapper->add_child($drags); + $drags->add_child($drag); + $pluginwrapper->add_child($drops); + $drops->add_child($drop); + + $drag->set_source_table("qtype_{$qtype}_drags", + array('questionid' => backup::VAR_PARENTID)); + + $drop->set_source_table("qtype_{$qtype}_drops", + array('questionid' => backup::VAR_PARENTID)); + + return $plugin; + } + + /** + * Returns one array with filearea => mappingname elements for the qtype + * + * Used by {@link get_components_and_fileareas} to know about all the qtype + * files to be processed both in backup and restore. + */ + public static function get_qtype_fileareas() { + $qtype = self::qtype_name(); + return array( + 'correctfeedback' => 'question_created', + 'partiallycorrectfeedback' => 'question_created', + 'incorrectfeedback' => 'question_created', + + 'bgimage' => 'question_created', + 'dragimage' => "qtype_{$qtype}_drags"); + } +} diff --git a/question/type/ddmarker/backup/moodle2/restore_qtype_ddmarker_plugin.class.php b/question/type/ddmarker/backup/moodle2/restore_qtype_ddmarker_plugin.class.php new file mode 100644 index 00000000000..ea190807504 --- /dev/null +++ b/question/type/ddmarker/backup/moodle2/restore_qtype_ddmarker_plugin.class.php @@ -0,0 +1,155 @@ +. + +/** + * @package moodlecore + * @subpackage backup-moodle2 + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * restore plugin class that provides the necessary information + * needed to restore one ddmarker qtype plugin + * + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class restore_qtype_ddmarker_plugin extends restore_qtype_plugin { + + protected static function qtype_name() { + return 'ddmarker'; + } + + /** + * Returns the paths to be handled by the plugin at question level + */ + protected function define_question_plugin_structure() { + + $paths = array(); + + // Add own qtype stuff + $elename = 'dds'; + $elepath = $this->get_pathfor('/'.self::qtype_name()); + $paths[] = new restore_path_element($elename, $elepath); + + $elename = 'drag'; + $elepath = $this->get_pathfor('/drags/drag'); + $paths[] = new restore_path_element($elename, $elepath); + + $elename = 'drop'; + $elepath = $this->get_pathfor('/drops/drop'); + $paths[] = new restore_path_element($elename, $elepath); + + return $paths; // And we return the interesting paths + } + + /** + * Process the qtype/{qtypename} element + */ + public function process_dds($data) { + global $DB; + + $prefix = 'qtype_'.self::qtype_name(); + + $data = (object)$data; + $oldid = $data->id; + + // Detect if the question is created or mapped + $oldquestionid = $this->get_old_parentid('question'); + $newquestionid = $this->get_new_parentid('question'); + $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false; + + // If the question has been created by restore, + // we need to create its qtype_ddmarker too + if ($questioncreated) { + // Adjust some columns + $data->questionid = $newquestionid; + // Insert record + $newitemid = $DB->insert_record($prefix, $data); + // Create mapping (needed for decoding links) + $this->set_mapping($prefix, $oldid, $newitemid); + } + } + + /** + * Process the qtype/drags/drag element + */ + public function process_drag($data) { + global $DB; + + $prefix = 'qtype_'.self::qtype_name(); + + $data = (object)$data; + $oldid = $data->id; + + // Detect if the question is created or mapped + $oldquestionid = $this->get_old_parentid('question'); + $newquestionid = $this->get_new_parentid('question'); + $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false; + + if ($questioncreated) { + $data->questionid = $newquestionid; + // Insert record + $newitemid = $DB->insert_record("{$prefix}_drags", $data); + // Create mapping (there are files and states based on this) + $this->set_mapping("{$prefix}_drags", $oldid, $newitemid); + + } + } + + /** + * Process the qtype/drags/drag element + */ + public function process_drop($data) { + global $DB; + + $prefix = 'qtype_'.self::qtype_name(); + + $data = (object)$data; + $oldid = $data->id; + + // Detect if the question is created or mapped + $oldquestionid = $this->get_old_parentid('question'); + $newquestionid = $this->get_new_parentid('question'); + $questioncreated = $this->get_mappingid('question_created', $oldquestionid) ? true : false; + + if ($questioncreated) { + $data->questionid = $newquestionid; + // Insert record + $newitemid = $DB->insert_record("{$prefix}_drops", $data); + // Create mapping (there are files and states based on this) + $this->set_mapping("{$prefix}_drops", $oldid, $newitemid); + } + } + /** + * Return the contents of this qtype to be processed by the links decoder + */ + public static function define_decode_contents() { + + $prefix = 'qtype_'.self::qtype_name(); + + $contents = array(); + + $fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback'); + $contents[] = + new restore_decode_content($prefix, $fields, $prefix); + + return $contents; + } +} diff --git a/question/type/ddmarker/db/install.xml b/question/type/ddmarker/db/install.xml new file mode 100644 index 00000000000..22d6c462261 --- /dev/null +++ b/question/type/ddmarker/db/install.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+
+
\ No newline at end of file diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php new file mode 100644 index 00000000000..cb4fdee4d1b --- /dev/null +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -0,0 +1,381 @@ +. + +/** + * Defines the editing form for the drag-and-drop images onto images question type. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Drag-and-drop images onto images editing form definition. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_edit_form extends question_edit_form { + const MAX_GROUPS = 8; + const START_NUM_ITEMS = 6; + const ADD_NUM_ITEMS = 3; + + public function qtype() { + return 'ddmarker'; + } + + /** + * + * Options shared by all file pickers in the form. + */ + public static function file_picker_options() { + $filepickeroptions = array(); + $filepickeroptions['accepted_types'] = array('web_image'); + $filepickeroptions['maxbytes'] = 0; + $filepickeroptions['maxfiles'] = 1; + $filepickeroptions['subdirs'] = 0; + return $filepickeroptions; + } + + protected function get_drag_image_repeats() { + $countimages = 0; + if (isset($this->question->id)) { + foreach ($this->question->options->drags as $drag) { + $countimages = max($countimages, $drag->no); + } + } + if ($this->question->formoptions->repeatelements) { + $imagerepeatsatstart = max(self::START_NUM_ITEMS, $countimages + self::ADD_NUM_ITEMS); + } else { + $imagerepeatsatstart = $countimages; + } + $imagerepeats = optional_param('noitems', $imagerepeatsatstart, PARAM_INT); + $addfields = optional_param('additems', '', PARAM_TEXT); + if (!empty($addfields)) { + $imagerepeats += self::ADD_NUM_ITEMS; + } + return array($imagerepeatsatstart, $imagerepeats); + } + + /** + * definition_inner adds all specific fields to the form. + * @param object $mform (the form being built). + */ + protected function definition_inner($mform) { + + $mform->addElement('header', 'previewareaheader', + get_string('previewareaheader', 'qtype_ddmarker')); + $mform->addElement('static', 'previewarea', + get_string('previewarea', 'qtype_ddmarker'), + get_string('previewareamessage', 'qtype_ddmarker')); + + $mform->registerNoSubmitButton('refresh'); + $mform->addElement('submit', 'refresh', get_string('refresh', 'qtype_ddmarker')); + $mform->closeHeaderBefore('refresh'); + + list($imagerepeatsatstart, $imagerepeats) = $this->get_drag_image_repeats(); + $this->definition_drop_zones($mform, $imagerepeats); + $mform->addElement('advcheckbox', 'shuffleanswers', ' ', + get_string('shuffleimages', 'qtype_ddmarker')); + $mform->setDefault('shuffleanswers', 0); + $mform->closeHeaderBefore('shuffleanswers'); + //add the draggable image fields to the form + $this->definition_draggable_images($mform, $imagerepeatsatstart); + + $this->add_combined_feedback_fields(true); + $this->add_interactive_settings(true, true); + } + + protected function definition_drop_zones($mform, $imagerepeats) { + $mform->addElement('header', 'dropzoneheader', + get_string('dropzoneheader', 'qtype_ddmarker')); + + $mform->addElement('filepicker', 'bgimage', get_string('bgimage', 'qtype_ddmarker'), + null, self::file_picker_options()); + + $countdropzones = 0; + if (isset($this->question->id)) { + foreach ($this->question->options->drops as $drop) { + $countdropzones = max($countdropzones, $drop->no); + } + } + if ($this->question->formoptions->repeatelements) { + $dropzonerepeatsatstart = max(self::START_NUM_ITEMS, + $countdropzones + self::ADD_NUM_ITEMS); + } else { + $dropzonerepeatsatstart = $countdropzones; + } + + $this->repeat_elements($this->drop_zone($mform, $imagerepeats), $dropzonerepeatsatstart, + $this->drop_zones_repeated_options(), + 'nodropzone', 'adddropzone', self::ADD_NUM_ITEMS, + get_string('addmoredropzones', 'qtype_ddmarker')); + } + + protected function drop_zone($mform, $imagerepeats) { + $dropzoneitem = array(); + + $grouparray = array(); + $grouparray[] = $mform->createElement('static', 'xleftlabel', '', + ' '.get_string('xleft', 'qtype_ddmarker').' '); + $grouparray[] = $mform->createElement('text', 'xleft', + get_string('xleft', 'qtype_ddmarker'), + array('size'=>5, 'class'=>'tweakcss')); + $mform->setType('xleft', PARAM_NOTAGS); + $grouparray[] = $mform->createElement('static', 'ytoplabel', '', + ' '.get_string('ytop', 'qtype_ddmarker').' '); + $grouparray[] = $mform->createElement('text', 'ytop', + get_string('ytop', 'qtype_ddmarker'), + array('size'=>5, 'class'=>'tweakcss')); + $mform->setType('ytop', PARAM_NOTAGS); + $options = array(); + + $options[0] = ''; + for ($i = 1; $i <= $imagerepeats; $i += 1) { + $options[$i] = $i; + } + $grouparray[] = $mform->createElement('static', '', '', ' ' . + get_string('draggableitem', 'qtype_ddmarker').' '); + $grouparray[] = $mform->createElement('select', 'choice', + get_string('draggableitem', 'qtype_ddmarker'), $options); + $grouparray[] = $mform->createElement('static', '', '', ' ' . + get_string('label', 'qtype_ddmarker').' '); + $grouparray[] = $mform->createElement('text', 'droplabel', + get_string('label', 'qtype_ddmarker'), + array('size'=>10, 'class'=>'tweakcss')); + $mform->setType('droplabel', PARAM_NOTAGS); + $dropzone = $mform->createElement('group', 'drops', + get_string('dropzone', 'qtype_ddmarker', '{no}'), $grouparray); + return array($dropzone); + } + + protected function drop_zones_repeated_options() { + $repeatedoptions = array(); + $repeatedoptions['choice']['default'] = '0'; + return $repeatedoptions; + } + + protected function definition_draggable_images($mform, $imagerepeatsatstart) { + + $this->repeat_elements($this->draggable_image($mform), $imagerepeatsatstart, + $this->draggable_images_repeated_options(), + 'noitems', 'additems', self::ADD_NUM_ITEMS, + get_string('addmoreimages', 'qtype_ddmarker')); + } + + protected function draggable_image($mform) { + $draggableimageitem = array(); + + $draggableimageitem[] = $mform->createElement('header', 'draggableitemheader', + get_string('draggableitemheader', 'qtype_ddmarker', '{no}')); + $dragitemtypes = array('image' => get_string('draggableimage', 'qtype_ddmarker'), + 'word' => get_string('draggableword', 'qtype_ddmarker')); + $draggableimageitem[] = $mform->createElement('select', 'dragitemtype', + get_string('draggableitemtype', 'qtype_ddmarker'), + $dragitemtypes, + array('class' => 'dragitemtype')); + $draggableimageitem[] = $mform->createElement('filepicker', 'dragitem', '', null, + self::file_picker_options()); + + $grouparray = array(); + $grouparray[] = $mform->createElement('text', 'draglabel', + get_string('label', 'qtype_ddmarker'), + array('size'=>30, 'class'=>'tweakcss')); + $mform->setType('draglabel', PARAM_NOTAGS); + $options = array(); + for ($i = 1; $i <= self::MAX_GROUPS; $i += 1) { + $options[$i] = $i; + } + $grouparray[] = $mform->createElement('static', '', '', ' ' . + get_string('group', 'qtype_gapselect').' '); + $grouparray[] = $mform->createElement('select', 'draggroup', + get_string('group', 'qtype_gapselect'), + $options, + array('class' => 'draggroup')); + $grouparray[] = $mform->createElement('advcheckbox', 'infinite', ' ', + get_string('infinite', 'qtype_ddmarker')); + $draggableimageitem[] = $mform->createElement('group', 'drags', + get_string('label', 'qtype_ddmarker'), $grouparray); + return $draggableimageitem; + } + + protected function draggable_images_repeated_options() { + $repeatedoptions = array(); + $repeatedoptions['draggroup']['default'] = '1'; + return $repeatedoptions; + } + + public function data_preprocessing($question) { + global $PAGE; + + $question = parent::data_preprocessing($question); + $question = $this->data_preprocessing_combined_feedback($question, true); + $question = $this->data_preprocessing_hints($question, true, true); + + $dragids = array(); // drag no -> dragid + if (!empty($question->options)) { + $question->shuffleanswers = $question->options->shuffleanswers; + $question->drags = array(); + foreach ($question->options->drags as $drag) { + $dragindex = $drag->no -1; + $question->drags[$dragindex] = array(); + $question->drags[$dragindex]['draglabel'] = $drag->label; + $question->drags[$dragindex]['infinite'] = $drag->infinite; + $question->drags[$dragindex]['draggroup'] = $drag->draggroup; + $dragids[$dragindex] = $drag->id; + } + $question->drops = array(); + foreach ($question->options->drops as $drop) { + $question->drops[$drop->no -1] = array(); + $question->drops[$drop->no -1]['choice'] = $drop->choice; + $question->drops[$drop->no -1]['droplabel'] = $drop->label; + $question->drops[$drop->no -1]['xleft'] = $drop->xleft; + $question->drops[$drop->no -1]['ytop'] = $drop->ytop; + } + } + //initialise file picker for bgimage + $draftitemid = file_get_submitted_draft_itemid('bgimage'); + + file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddmarker', + 'bgimage', !empty($question->id) ? (int) $question->id : null, + self::file_picker_options()); + $question->bgimage = $draftitemid; + + //initialise file picker for dragimages + list(, $imagerepeats) = $this->get_drag_image_repeats(); + $draftitemids = optional_param_array('dragitem', array(), PARAM_INT); + for ($imageindex = 0; $imageindex < $imagerepeats; $imageindex++) { + $draftitemid = isset($draftitemids[$imageindex]) ? $draftitemids[$imageindex] :0; + //numbers not allowed in filearea name + $itemid = isset($dragids[$imageindex]) ? $dragids[$imageindex] : null; + file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddmarker', + 'dragimage', $itemid, self::file_picker_options()); + $question->dragitem[$imageindex] = $draftitemid; + } + if (!empty($question->options)) { + foreach ($question->options->drags as $drag) { + $dragindex = $drag->no -1; + if (!isset($question->dragitem[$dragindex])) { + $fileexists = false; + } else { + $fileexists = self::file_uploaded($question->dragitem[$dragindex]); + } + $labelexists = $question->drags[$dragindex]['draglabel']; + if ($labelexists && !$fileexists) { + $question->dragitemtype[$dragindex] = 'word'; + } else { + $question->dragitemtype[$dragindex] = 'image'; + } + } + } + $maxsizes =new stdClass(); + $maxsizes->bgimage = new stdClass(); + $maxsizes->bgimage->width = QTYPE_ddmarker_BGIMAGE_MAXWIDTH; + $maxsizes->bgimage->height = QTYPE_ddmarker_BGIMAGE_MAXHEIGHT; + $maxsizes->dragimage = new stdClass(); + $maxsizes->dragimage->width = QTYPE_ddmarker_DRAGIMAGE_MAXWIDTH; + $maxsizes->dragimage->height = QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT; + + $params = array('maxsizes' => $maxsizes, + 'topnode' => 'fieldset#previewareaheader'); + + $PAGE->requires->yui_module('moodle-qtype_ddmarker-form', + 'M.qtype_ddmarker.init_form', + array($params)); + return $question; + } + + + public static function file_uploaded($draftitemid) { + $draftareafiles = file_get_drafarea_files($draftitemid); + do { + $draftareafile = array_shift($draftareafiles->list); + } while ($draftareafile !== null && $draftareafile->filename == '.'); + if ($draftareafile === null) { + return false; + } + return true; + } + + public function validation($data, $files) { + $errors = parent::validation($data, $files); + if (!self::file_uploaded($data['bgimage'])) { + $errors["bgimage"] = get_string('formerror_nobgimage', 'qtype_ddmarker'); + } + + $allchoices = array(); + for ($i=0; $i < $data['nodropzone']; $i++) { + $ytoppresent = (trim($data['drops'][$i]['ytop']) !== ''); + $xleftpresent = (trim($data['drops'][$i]['ytop']) !== ''); + $labelpresent = (trim($data['drops'][$i]['droplabel']) !== ''); + $choice = $data['drops'][$i]['choice']; + $imagechoicepresent = ($choice !== '0'); + + if ($imagechoicepresent) { + if (!$ytoppresent) { + $errors["drops[$i]"] = + get_string('formerror_noytop', 'qtype_ddmarker'); + } + if (!$xleftpresent) { + $errors["drops[$i]"] = + get_string('formerror_noxleft', 'qtype_ddmarker'); + } + + if ($data['dragitemtype'][$choice - 1] != 'word' && + !self::file_uploaded($data['dragitem'][$choice - 1])) { + $errors['dragitem['.($choice - 1).']'] = + get_string('formerror_nofile', 'qtype_ddmarker', $i); + } + + if (isset($allchoices[$choice]) && !$data['drags'][$choice-1]['infinite']) { + $errors["drops[$i]"] = + get_string('formerror_multipledraginstance', 'qtype_ddmarker', $choice); + $errors['drops['.($allchoices[$choice]).']'] = + get_string('formerror_multipledraginstance', 'qtype_ddmarker', $choice); + $errors['drags['.($choice-1).']'] = + get_string('formerror_multipledraginstance2', 'qtype_ddmarker', $choice); + } + $allchoices[$choice] = $i; + } else { + if ($ytoppresent || $xleftpresent || $labelpresent) { + $errors["drops[$i]"] = + get_string('formerror_noimageselected', 'qtype_ddmarker'); + } + } + } + for ($dragindex=0; $dragindex < $data['noitems']; $dragindex++) { + $label = $data['drags'][$dragindex]['draglabel']; + if ($data['dragitemtype'][$dragindex] == 'word') { + $allowedtags = '
'; + $errormessage = get_string('formerror_disallowedtags', 'qtype_ddmarker'); + } else { + $allowedtags = ''; + $errormessage = get_string('formerror_noallowedtags', 'qtype_ddmarker'); + } + if ($label != strip_tags($label, $allowedtags)) { + $errors["drags[{$dragindex}]"] = $errormessage; + } + + } + return $errors; + } + + + +} diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php new file mode 100644 index 00000000000..6695efdaac6 --- /dev/null +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -0,0 +1,66 @@ +. + +/** + * + * @package qtype + * @subpackage ddmarker + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['addingddmarker'] = 'Adding drag and drop: images or text onto image'; +$string['addmoredropzones'] = 'Blanks for {no} More Drop Zones'; +$string['addmoreimages'] = 'Blanks for {no} More Draggable Items'; +$string['answer'] = 'Answer'; +$string['bgimage'] = 'Background Image'; +$string['correctansweris'] = 'The correct answer is: {$a}'; +$string['ddmarker'] = 'Drag and drop: images or text onto image'; +$string['ddmarker_help'] = 'Select a background image file, select draggable images or enter text and define the drop zones on the background image to which they must be dragged.'; +$string['ddmarkersummary'] = 'Images or text labels are dragged and dropped into drop zones on a background image.'; +$string['draggableimage'] = 'Draggable image'; +$string['draggableitem'] = 'Draggable item'; +$string['draggableitemheader'] = 'Draggable item {$a}'; +$string['draggableitemtype'] = 'Type'; +$string['draggableword'] = 'Draggable text'; +$string['dropzone'] = 'Drop zone {$a}'; +$string['dropzoneheader'] = 'Drop zones'; +$string['editingddmarker'] = 'Editing drag and drop: images or text onto image'; +$string['formerror_disallowedtags'] = 'You have used html tags here that are not allowed in a draggable text drag item type.'; +$string['formerror_noallowedtags'] = 'No html tags are allowed in this text which is the alt text for a draggable image'; +$string['formerror_noytop'] = 'You must provide a value for the y coords for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.'; +$string['formerror_noxleft'] = 'You must provide a value for the y coords for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.'; +$string['formerror_nofile'] = 'You need to upload or select a file to use here.'; +$string['formerror_nofile3'] = 'You need to select an image file here, or delete the associated label and uncheck the infinite checkbox.'; +$string['formerror_multipledraginstance'] = 'You have selected this image {$a} more than once as the correct choice for a drop zone but it is not marked as being an infinite drag item.'; +$string['formerror_multipledraginstance2'] = 'You have selected this image more than once as the correct choice for a drop zone but it is not marked as being an infinite drag item.'; +$string['formerror_noimageselected'] = 'You need to select a drag item to be the correct choice for this drop zone.'; +$string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; +$string['infinite'] = 'Infinite'; +$string['label'] = 'Text'; +$string['nolabel'] = 'No label text'; +$string['pleasedraganimagetoeachdropregion'] = 'Your answer is not complete, please drag an item to each drop region.'; +$string['previewarea'] = 'Preview area -'; +$string['previewareaheader'] = 'Preview'; +$string['previewareamessage'] = 'Select a background image file and select draggable images or just enter text that will be made draggable. Then choose a drag item for each \'drop zone\', and drag the drag item to where the student should drag it to.'; +$string['refresh'] = 'Refresh Preview'; +$string['shuffleimages'] = 'Shuffle Drag Items Each Time Question Is Attempted'; +$string['summarisechoice'] = '{$a->no}. {$a->text}'; +$string['summariseplace'] = '{$a->no}. {$a->text}'; +$string['summarisechoiceno'] = 'Item {$a}'; +$string['summariseplaceno'] = 'Drop zone {$a}'; +$string['xleft'] = 'Left'; +$string['ytop'] = 'Top'; \ No newline at end of file diff --git a/question/type/ddmarker/lib.php b/question/type/ddmarker/lib.php new file mode 100644 index 00000000000..d648284fb54 --- /dev/null +++ b/question/type/ddmarker/lib.php @@ -0,0 +1,39 @@ +. + +/** + * Serve question type files + * + * @since 2.0 + * @package qtype + * @subpackage essay + * @copyright Dongsheng Cai + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Checks file access for essay questions. + */ +function qtype_ddmarker_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) { + global $CFG; + require_once($CFG->libdir . '/questionlib.php'); + question_pluginfile($course, $context, 'qtype_ddmarker', + $filearea, $args, $forcedownload); +} diff --git a/question/type/ddmarker/pix/icon.gif b/question/type/ddmarker/pix/icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..f5705c637b7bd1003995fef64a48e51ae378b29e GIT binary patch literal 361 zcmV-v0hazpNk%w1VGsZi0M!5hBQ{wtLSi#VYBWe{H%DSPK~XzRY&}qMKT&T(RB}aB zZb(;iNm+AIQBhKKqgQyQU3{rwf2(7Gu4i?RXoRY1hOuplvvG~5a*eiikhge}xq60} zeTkicikg9qqJx^fhLNL)iiL=t!HuHAkEXAarpA@1$CjzcnXAd1r@5W4$DXLVp|iuH zv(BWrsiwBjsJGFmxYD%4w8YV`#nG|R+_Bu{x#H`-?(@s`_V)Ms-~9dV{r&v_00000 z00000A^8Le002DzEC2ui01yBW000Jtz>siAEDCwZq*8g@5T4NJCU-Ertp!prI)Z4IPFA2C1n50Hg({2?+-V0|KO^rydi#yu5}^QNdG3ImO1u#XLsK HL_q*M7u=r% literal 0 HcmV?d00001 diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php new file mode 100644 index 00000000000..54ca57b1af1 --- /dev/null +++ b/question/type/ddmarker/question.php @@ -0,0 +1,224 @@ +. + +/** + * Drag-and-drop words into sentences question definition class. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/type/gapselect/questionbase.php'); + + +/** + * Represents a drag-and-drop images to image question. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_question extends qtype_gapselect_question_base { + public function clear_wrong_from_response(array $response) { + foreach ($this->places as $place => $notused) { + if (array_key_exists($this->field($place), $response) && + $response[$this->field($place)] != $this->get_right_choice_for($place)) { + $response[$this->field($place)] = ''; + } + } + return $response; + } + + public function get_right_choice_for($placeno) { + $place = $this->places[$placeno]; + foreach ($this->choiceorder[$place->group] as $choicekey => $choiceid) { + if ($this->rightchoices[$placeno] == $choiceid) { + return $choicekey; + } + } + } + public function summarise_response(array $response) { + $allblank = true; + foreach ($this->places as $placeno => $place) { + $summariseplace = $place->summarise(); + if (array_key_exists($this->field($placeno), $response) && + $response[$this->field($placeno)]) { + $selected = $this->get_selected_choice($place->group, + $response[$this->field($placeno)]); + $summarisechoice = $selected->summarise(); + $allblank = false; + } else { + $summarisechoice = ''; + } + $choices[] = "$summariseplace -> {{$summarisechoice}}"; + } + if ($allblank) { + return null; + } + return implode(' ', $choices); + } + + public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { + if ($filearea == 'bgimage' || $filearea == 'dragimage') { + $validfilearea = true; + } else { + $validfilearea = false; + } + if ($component == 'qtype_ddmarker' && $validfilearea) { + $question = $qa->get_question(); + $itemid = reset($args); + if ($filearea == 'bgimage') { + return $itemid == $question->id; + } else if ($filearea == 'dragimage') { + foreach ($question->choices as $group) { + foreach ($group as $drag) { + if ($drag->id == $itemid) { + return true; + } + } + } + return false; + } + } else { + return parent::check_file_access($qa, $options, $component, + $filearea, $args, $forcedownload); + } + } + public function get_validation_error(array $response) { + if ($this->is_complete_response($response)) { + return ''; + } + return get_string('pleasedraganimagetoeachdropregion', 'qtype_ddmarker'); + } + + public function classify_response(array $response) { + $parts = array(); + foreach ($this->places as $placeno => $place) { + $group = $place->group; + if (!array_key_exists($this->field($placeno), $response) || + !$response[$this->field($placeno)]) { + $parts[$placeno] = question_classified_response::no_response(); + continue; + } + + $fieldname = $this->field($placeno); + $choiceno = $this->choiceorder[$group][$response[$fieldname]]; + $choice = $this->choices[$group][$choiceno]; + + $correct = $this->get_right_choice_for($placeno) == $response[$fieldname]; + $parts[$placeno] = new question_classified_response( + $choiceno, $choice->summarise(), $correct?1:0); + } + return $parts; + } + + public function get_random_guess_score() { + $accum = 0; + + foreach ($this->places as $place) { + $accum += 1 / count($this->choices[$place->group]); + } + + return $accum / count($this->places); + } + + + public function get_question_summary() { + $summary = ''; + if (!html_is_blank($this->questiontext)) { + $question = $this->html_to_text($this->questiontext, $this->questiontextformat); + $summary .= $question . '; '; + } + $places = array(); + foreach ($this->places as $place) { + $cs = array(); + foreach ($this->choices[$place->group] as $choice) { + $cs[] = $choice->summarise(); + } + $places[] = '[[' . $place->summarise() . ']] -> {' . implode(' / ', $cs) . '}'; + } + $summary .= implode('; ', $places); + return $summary; + } +} + + +/** + * Represents one of the choices (draggable images). + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_drag_item { + public $id; + public $text; + public $no; + public $group; + public $isinfinite; + + public function __construct($alttextlabel, $no, $group = 1, $isinfinite = false, $id = 0) { + $this->id = $id; + $this->text = $alttextlabel; + $this->no = $no; + $this->group = $group; + $this->isinfinite = $isinfinite; + } + public function choice_group() { + return $this->group; + } + + public function summarise() { + if (trim($this->text) != '') { + return get_string('summarisechoice', 'qtype_ddmarker', $this); + } else { + return get_string('summarisechoiceno', 'qtype_ddmarker', $this->no); + } + } +} +/** + * Represents one of the places (drop zones). + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_drop_zone { + public $no; + public $text; + public $group; + public $xy; + + public function __construct($alttextlabel, $no, $group = 1, $x = '', $y = '') { + $this->no = $no; + $this->text = $alttextlabel; + $this->group = $group; + $this->xy = array($x, $y); + } + + public function summarise() { + if (trim($this->text) != '') { + $summariseplace = + get_string('summariseplace', 'qtype_ddmarker', $this); + } else { + $summariseplace = + get_string('summariseplaceno', 'qtype_ddmarker', $this->no); + } + return $summariseplace; + } +} \ No newline at end of file diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php new file mode 100644 index 00000000000..106ca0f1b27 --- /dev/null +++ b/question/type/ddmarker/questiontype.php @@ -0,0 +1,464 @@ +. + +/** + * Question type class for the drag-and-drop images onto images question type. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir . '/questionlib.php'); +require_once($CFG->dirroot . '/question/engine/lib.php'); +require_once($CFG->dirroot . '/question/format/xml/format.php'); +require_once($CFG->dirroot . '/question/type/gapselect/questiontypebase.php'); + +define('QTYPE_ddmarker_BGIMAGE_MAXWIDTH', 600); +define('QTYPE_ddmarker_BGIMAGE_MAXHEIGHT', 400); +define('QTYPE_ddmarker_DRAGIMAGE_MAXWIDTH', 150); +define('QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT', 100); + +/** + * The drag-and-drop words into sentences question type class. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker extends question_type { + protected function choice_group_key() { + return 'draggroup'; + } + + public function requires_qtypes() { + return array('gapselect'); + } + + public function get_question_options($question) { + global $DB; + $question->options = $DB->get_record('qtype_ddmarker', + array('questionid' => $question->id), '*', MUST_EXIST); + $question->options->drags = $DB->get_records('qtype_ddmarker_drags', + array('questionid' => $question->id), 'no ASC', '*'); + $question->options->drops = $DB->get_records('qtype_ddmarker_drops', + array('questionid' => $question->id), 'no ASC', '*'); + parent::get_question_options($question); + } + + protected function make_choice($dragdata) { + return new qtype_ddmarker_drag_item($dragdata->label, $dragdata->no, + $dragdata->draggroup, $dragdata->infinite, $dragdata->id); + } + + protected function make_place($dropzonedata) { + return new qtype_ddmarker_drop_zone($dropzonedata->label, $dropzonedata->no, + $dropzonedata->group, + $dropzonedata->xleft, $dropzonedata->ytop); + } + + protected function make_hint($hint) { + return question_hint_with_parts::load_from_record($hint); + } + + protected function initialise_question_instance(question_definition $question, $questiondata) { + parent::initialise_question_instance($question, $questiondata); + $question->shufflechoices = $questiondata->options->shuffleanswers; + + $this->initialise_combined_feedback($question, $questiondata, true); + + $question->choices = array(); + $choiceindexmap= array(); + + // Store the choices in arrays by group. + foreach ($questiondata->options->drags as $dragdata) { + + $choice = $this->make_choice($dragdata); + + if (array_key_exists($choice->choice_group(), $question->choices)) { + $question->choices[$choice->choice_group()][$dragdata->no] = $choice; + } else { + $question->choices[$choice->choice_group()][1] = $choice; + } + + end($question->choices[$choice->choice_group()]); + $choiceindexmap[$dragdata->no] = array($choice->choice_group(), + key($question->choices[$choice->choice_group()])); + } + + $question->places = array(); + $question->rightchoices = array(); + + $i = 1; + + foreach ($questiondata->options->drops as $dropdata) { + list($group, $choiceindex) = $choiceindexmap[$dropdata->choice]; + $dropdata->group = $group; + $question->places[$dropdata->no] = $this->make_place($dropdata); + $question->rightchoices[$dropdata->no] = $choiceindex; + } + } + + public function save_question_options($formdata) { + global $DB, $USER; + $context = $formdata->context; + + $options = $DB->get_record('qtype_ddmarker', array('questionid' => $formdata->id)); + if (!$options) { + $options = new stdClass(); + $options->questionid = $formdata->id; + $options->correctfeedback = ''; + $options->partiallycorrectfeedback = ''; + $options->incorrectfeedback = ''; + $options->id = $DB->insert_record('qtype_ddmarker', $options); + } + + $options->shuffleanswers = !empty($formdata->shuffleanswers); + $options = $this->save_combined_feedback_helper($options, $formdata, $context, true); + $this->save_hints($formdata, true); + $DB->update_record('qtype_ddmarker', $options); + $DB->delete_records('qtype_ddmarker_drops', array('questionid' => $formdata->id)); + foreach (array_keys($formdata->drops) as $dropno) { + if ($formdata->drops[$dropno]['choice'] == 0) { + continue; + } + $drop = new stdClass(); + $drop->questionid = $formdata->id; + $drop->no = $dropno + 1; + $drop->xleft = $formdata->drops[$dropno]['xleft']; + $drop->ytop = $formdata->drops[$dropno]['ytop']; + $drop->choice = $formdata->drops[$dropno]['choice']; + $drop->label = $formdata->drops[$dropno]['droplabel']; + + $DB->insert_record('qtype_ddmarker_drops', $drop); + } + + //an array of drag no -> drag id + $olddragids = $DB->get_records_menu('qtype_ddmarker_drags', + array('questionid' => $formdata->id), + '', 'no, id'); + foreach (array_keys($formdata->drags) as $dragno) { + $info = file_get_draft_area_info($formdata->dragitem[$dragno]); + if ($info['filecount'] > 0 || !empty($formdata->drags[$dragno]['draglabel'])) { + $draftitemid = $formdata->dragitem[$dragno]; + + $drag = new stdClass(); + $drag->questionid = $formdata->id; + $drag->no = $dragno + 1; + $drag->draggroup = $formdata->drags[$dragno]['draggroup']; + $drag->infinite = empty($formdata->drags[$dragno]['infinite'])? 0 : 1; + $drag->label = $formdata->drags[$dragno]['draglabel']; + + if (isset($olddragids[$dragno +1])) { + $drag->id = $olddragids[$dragno +1]; + unset($olddragids[$dragno +1]); + $DB->update_record('qtype_ddmarker_drags', $drag); + } else { + $drag->id = $DB->insert_record('qtype_ddmarker_drags', $drag); + } + + if ($formdata->dragitemtype[$dragno] == 'image') { + self::constrain_image_size_in_draft_area($draftitemid, + QTYPE_ddmarker_DRAGIMAGE_MAXWIDTH, + QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT); + file_save_draft_area_files($draftitemid, $formdata->context->id, + 'qtype_ddmarker', 'dragimage', $drag->id, + array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 1)); + } else { + //delete any existing files for draggable text item type + $fs = get_file_storage(); + $fs->delete_area_files($formdata->context->id, 'qtype_ddmarker', + 'dragimage', $drag->id); + } + + } + + } + if (!empty($olddragids)) { + list($sql, $params) = $DB->get_in_or_equal(array_values($olddragids)); + $DB->delete_records_select('qtype_ddmarker_drags', "id $sql", $params); + } + + self::constrain_image_size_in_draft_area($formdata->bgimage, + QTYPE_ddmarker_BGIMAGE_MAXWIDTH, + QTYPE_ddmarker_BGIMAGE_MAXHEIGHT); + file_save_draft_area_files($formdata->bgimage, $formdata->context->id, + 'qtype_ddmarker', 'bgimage', $formdata->id, + array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 1)); + } + + + public static function constrain_image_size_in_draft_area($draftitemid, $maxwidth, $maxheight) { + global $USER; + $usercontext = get_context_instance(CONTEXT_USER, $USER->id); + $fs = get_file_storage(); + $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id'); + if ($draftfiles) { + foreach ($draftfiles as $file) { + if ($file->is_directory()) { + continue; + } + $imageinfo = $file->get_imageinfo(); + $width = $imageinfo['width']; + $height = $imageinfo['height']; + $mimetype = $imageinfo['mimetype']; + switch ($mimetype) { + case 'image/jpeg' : + $quality = 80; + break; + case 'image/png' : + $quality = 8; + break; + default : + $quality = null; + } + $newwidth = min($maxwidth, $width); + $newheight = min($maxheight, $height); + if ($newwidth != $width || $newheight != $height) { + $newimagefilename = $file->get_filename(); + $newimagefilename = + preg_replace('!\.!', "_{$newwidth}x{$newheight}.", $newimagefilename, 1); + $newrecord = new stdClass(); + $newrecord->contextid = $usercontext->id; + $newrecord->component = 'user'; + $newrecord->filearea = 'draft'; + $newrecord->itemid = $draftitemid; + $newrecord->filepath = '/'; + $newrecord->filename = $newimagefilename; + $fs->convert_image($newrecord, $file, $newwidth, $newheight, true, $quality); + $file->delete(); + } + } + } + } + public function move_files($questionid, $oldcontextid, $newcontextid) { + global $DB; + $fs = get_file_storage(); + + parent::move_files($questionid, $oldcontextid, $newcontextid); + $fs->move_area_files_to_new_context($oldcontextid, + $newcontextid, 'qtype_ddmarker', 'bgimage', $questionid); + $dragids = $DB->get_records_menu('qtype_ddmarker_drags', + array('questionid' => $questionid), 'id', 'id,1'); + foreach ($dragids as $dragid => $notused) { + $fs->move_area_files_to_new_context($oldcontextid, + $newcontextid, 'qtype_ddmarker', 'dragimage', $dragid); + } + + $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); + } + + /** + * Delete all the files belonging to this question. + * @param int $questionid the question being deleted. + * @param int $contextid the context the question is in. + */ + + protected function delete_files($questionid, $contextid) { + global $DB; + $fs = get_file_storage(); + + parent::delete_files($questionid, $contextid); + + $dragids = $DB->get_records_menu('qtype_ddmarker_drags', + array('questionid' => $questionid), 'id', 'id,1'); + foreach ($dragids as $dragid => $notused) { + $fs->delete_area_files($contextid, 'qtype_ddmarker', 'dragimage', $dragid); + } + + $this->delete_files_in_combined_feedback($questionid, $contextid); + } + + public function export_to_xml($question, $format, $extra = null) { + $fs = get_file_storage(); + $contextid = $question->contextid; + $output = ''; + + if ($question->options->shuffleanswers) { + $output .= " \n"; + } + $output .= $format->write_combined_feedback($question->options); + $output .= $format->write_hints($question); + $files = $fs->get_area_files($contextid, 'qtype_ddmarker', 'bgimage', $question->id); + $output .= " ".$this->write_files($files, 2)."\n";; + + foreach ($question->options->drags as $drag) { + $files = + $fs->get_area_files($contextid, 'qtype_ddmarker', 'dragimage', $drag->id); + $output .= " \n"; + $output .= " {$drag->no}\n"; + $output .= $format->writetext($drag->label, 3)."\n"; + $output .= " {$drag->draggroup}\n"; + if ($drag->infinite) { + $output .= " \n"; + } + $output .= $this->write_files($files, 3); + $output .= " \n"; + } + foreach ($question->options->drops as $drop) { + $output .= " \n"; + $output .= $format->writetext($drop->label, 3); + $output .= " {$drop->no}\n"; + $output .= " {$drop->choice}\n"; + $output .= " {$drop->xleft}\n"; + $output .= " {$drop->ytop}\n"; + $output .= " \n"; + } + + return $output; + } + + public function import_from_xml($data, $question, $format, $extra=null) { + if (!isset($data['@']['type']) || $data['@']['type'] != 'ddmarker') { + return false; + } + + $question = $format->import_headers($data); + $question->qtype = 'ddmarker'; + + $question->shuffleanswers = array_key_exists('shuffleanswers', + $format->getpath($data, array('#'), array())); + + $filexml = $format->getpath($data, array('#', 'file'), array()); + $question->bgimage = $this->import_files_to_draft_file_area($format, $filexml); + $drags = $data['#']['drag']; + $question->drags = array(); + + foreach ($drags as $dragxml) { + $dragno = $format->getpath($dragxml, array('#', 'no', 0, '#'), 0); + $dragindex = $dragno -1; + $question->drags[$dragindex] = array(); + $question->drags[$dragindex]['draglabel'] = + $format->getpath($dragxml, array('#', 'text', 0, '#'), '', true); + $question->drags[$dragindex]['infinite'] = array_key_exists('infinite', $dragxml['#']); + $question->drags[$dragindex]['draggroup'] = + $format->getpath($dragxml, array('#', 'draggroup', 0, '#'), 1); + $filexml = $format->getpath($dragxml, array('#', 'file'), array()); + $question->dragitem[$dragindex] = + $this->import_files_to_draft_file_area($format, $filexml); + if (count($filexml)) { + $question->dragitemtype[$dragindex] = 'image'; + } else { + $question->dragitemtype[$dragindex] = 'word'; + } + } + + $drops = $data['#']['drop']; + $question->drops = array(); + foreach ($drops as $dropxml) { + $dropno = $format->getpath($dropxml, array('#', 'no', 0, '#'), 0); + $dropindex = $dropno -1; + $question->drops[$dropindex] = array(); + $question->drops[$dropindex]['choice'] = + $format->getpath($dropxml, array('#', 'choice', 0, '#'), 0); + $question->drops[$dropindex]['droplabel'] = + $format->getpath($dropxml, array('#', 'text', 0, '#'), '', true); + $question->drops[$dropindex]['xleft'] = + $format->getpath($dropxml, array('#', 'xleft', 0, '#'), ''); + $question->drops[$dropindex]['ytop'] = + $format->getpath($dropxml, array('#', 'ytop', 0, '#'), ''); + } + + $format->import_combined_feedback($question, $data, true); + $format->import_hints($question, $data, true); + + return $question; + } + + + /** + * Create a draft files area, import files into it and return the draft item id. + * @param qformat_xml $format + * @param array $xml an array of nodes from the the parsed XML. + * @return integer draftitemid + */ + public function import_files_to_draft_file_area($format, $xml) { + global $USER; + $fs = get_file_storage(); + $files = $format->import_files($xml); + $usercontext = get_context_instance(CONTEXT_USER, $USER->id); + $draftitemid = file_get_unused_draft_itemid(); + foreach ($files as $file) { + $record = new stdClass(); + $record->contextid = $usercontext->id; + $record->component = 'user'; + $record->filearea = 'draft'; + $record->itemid = $draftitemid; + $record->filename = $file->name; + $record->filepath = '/'; + $fs->create_file_from_string($record, $this->decode_file($file)); + } + return $draftitemid; + } + + /** + * Convert files into text output in the given format. + * This method is copied from qformat_default as a quick fix, as the method there is + * protected. + * @param array + * @param string encoding method + * @return string $string + */ + public function write_files($files, $indent) { + if (empty($files)) { + return ''; + } + $string = ''; + foreach ($files as $file) { + if ($file->is_directory()) { + continue; + } + $string .= str_repeat(' ', $indent); + $string .= ''; + $string .= base64_encode($file->get_content()); + $string .= "\n"; + } + return $string; + } + + public function get_possible_responses($questiondata) { + $question = $this->make_question($questiondata); + + $parts = array(); + foreach ($question->places as $placeno => $place) { + $group = $place->group; + $choices = array(); + + foreach ($question->choices[$group] as $i => $choice) { + $summarisechoice = $choice->summarise(); + + $correct = $question->rightchoices[$placeno] == $i; + $choices[$choice->no] = new question_possible_response( + $summarisechoice, + $correct?1:0); + } + $choices[null] = question_possible_response::no_response(); + + $parts[$placeno] = $choices; + } + + return $parts; + } + + public function get_random_guess_score($questiondata) { + $question = $this->make_question($questiondata); + return $question->get_random_guess_score(); + } + +} diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php new file mode 100644 index 00000000000..da83b871c60 --- /dev/null +++ b/question/type/ddmarker/renderer.php @@ -0,0 +1,180 @@ +. + +/** + * Drag-and-drop words into sentences question renderer class. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/type/gapselect/rendererbase.php'); + + +/** + * Generates the output for drag-and-drop words into sentences questions. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_renderer extends qtype_with_combined_feedback_renderer { + + public function head_code(question_attempt $qa) { + return parent::head_code($qa); + } + public function clear_wrong(question_attempt $qa) { + $question = $qa->get_question(); + $response = $qa->get_last_qt_data(); + + if (!empty($response)) { + $cleanresponse = $question->clear_wrong_from_response($response); + } else { + $cleanresponse = $response; + } + $cleanresponsehtml = ''; + foreach ($cleanresponse as $fieldname => $value) { + list (, $html) = $this->hidden_field_for_qt_var($qa, $fieldname, $value); + $cleanresponsehtml .= $html; + } + return $cleanresponsehtml; + } + + public function formulation_and_controls(question_attempt $qa, + question_display_options $options) { + global $PAGE; + + $question = $qa->get_question(); + $response = $qa->get_last_qt_data(); + + $questiontext = $question->format_questiontext($qa); + + $output = html_writer::tag('div', $questiontext, array('class' => 'qtext')); + + $bgimage = self::get_url_for_image($qa, 'bgimage'); + + $img = html_writer::empty_tag('img', array('src'=>$bgimage, 'class'=>'dropbackground')); + $droparea = html_writer::tag('div', $img, array('class'=>'droparea')); + + $dragimagehomes = ''; + foreach ($question->choices as $groupno => $group) { + $dragimagehomesgroup = ''; + $orderedgroup = $question->get_ordered_choices($groupno); + foreach ($orderedgroup as $choiceno => $dragimage) { + $dragimageurl = self::get_url_for_image($qa, 'dragimage', $dragimage->id); + $classes = array("group{$groupno}", + 'draghome', + "dragitemhomes{$dragimage->no}", + "choice{$choiceno}"); + if ($dragimage->isinfinite) { + $classes[] = 'infinite'; + } + if ($dragimageurl === null) { + $classes[] = 'yui3-cssfonts'; + $dragimagehomesgroup .= html_writer::tag('div', + $dragimage->text, + array('src'=>$dragimageurl, + 'class'=>join(' ', $classes))); + } else { + $dragimagehomesgroup .= html_writer::empty_tag('img', + array('src'=>$dragimageurl, + 'alt' => $dragimage->text, + 'class'=>join(' ', $classes))); + } + } + $dragimagehomes .= html_writer::tag('div', $dragimagehomesgroup, + array('class'=>'dragitemgroup'.$groupno)); + } + + $dragitemsclass = 'dragitems'; + if ($options->readonly) { + $dragitemsclass .= ' readonly'; + } + $dragitems = html_writer::tag('div', $dragimagehomes, array('class'=> $dragitemsclass)); + $dropzones = html_writer::empty_tag('div', array('class'=>'dropzones')); + $output .= html_writer::tag('div', $droparea.$dragitems.$dropzones, + array('class'=>'ddarea')); + foreach ($question->places as $placeno => $place) { + $varname = $question->field($placeno); + list($fieldname, $html) = $this->hidden_field_for_qt_var($qa, $varname); + $output .= $html; + $question->places[$placeno]->fieldname = $fieldname; + } + $topnode = 'div#q'.$qa->get_slot().' div.ddarea'; + $params = array('drops' => $question->places, + 'topnode' => $topnode, + 'readonly' => $options->readonly); + + $PAGE->requires->yui_module('moodle-qtype_ddmarker-dd', + 'M.qtype_ddmarker.init_question', + array($params)); + + if ($qa->get_state() == question_state::$invalid) { + $output .= html_writer::nonempty_tag('div', + $question->get_validation_error($qa->get_last_qt_data()), + array('class' => 'validationerror')); + } + return $output; + } + + protected static function get_url_for_image(question_attempt $qa, $filearea, $itemid = 0) { + $question = $qa->get_question(); + $qubaid = $qa->get_usage_id(); + $slot = $qa->get_slot(); + $fs = get_file_storage(); + if ($filearea == 'bgimage') { + $itemid = $question->id; + } + $draftfiles = $fs->get_area_files($question->contextid, 'qtype_ddmarker', + $filearea, $itemid, 'id'); + if ($draftfiles) { + foreach ($draftfiles as $file) { + if ($file->is_directory()) { + continue; + } + $url = moodle_url::make_pluginfile_url($question->contextid, 'qtype_ddmarker', + $filearea, "$qubaid/$slot/{$itemid}", '/', + $file->get_filename()); + return $url->out(); + } + } + return null; + } + + protected function hidden_field_for_qt_var(question_attempt $qa, $varname, $value = null) { + if ($value === null) { + $value = $qa->get_last_qt_var($varname); + } + $fieldname = $qa->get_qt_field_name($varname); + $attributes = array('type' => 'hidden', + 'id' => str_replace(':', '_', $fieldname), + 'name' => $fieldname, + 'value' => $value); + return array($fieldname, html_writer::empty_tag('input', $attributes)."\n"); + } + + public function specific_feedback(question_attempt $qa) { + return $this->combined_feedback($qa); + } + + public function correct_response(question_attempt $qa) { + return ''; + } +} diff --git a/question/type/ddmarker/simpletest/helper.php b/question/type/ddmarker/simpletest/helper.php new file mode 100644 index 00000000000..e0b2b16b50a --- /dev/null +++ b/question/type/ddmarker/simpletest/helper.php @@ -0,0 +1,131 @@ +. + +/** + * Test helpers for the drag-and-drop words into sentences question type. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Test helper class for the drag-and-drop words into sentences question type. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_test_helper extends question_test_helper { + public function get_test_questions() { + return array('fox', 'maths'); + } + + /** + * @return qtype_ddmarker_question + */ + public function make_ddmarker_question_fox() { + question_bank::load_question_definition_classes('ddmarker'); + $dd = new qtype_ddmarker_question(); + + test_question_maker::initialise_a_question($dd); + + $dd->name = 'Drag-and-drop words into sentences question'; + $dd->questiontext = 'The quick brown fox jumped over the lazy dog.'; + $dd->generalfeedback = 'This sentence uses each letter of the alphabet.'; + $dd->qtype = question_bank::get_qtype('ddmarker'); + + $dd->shufflechoices = true; + + test_question_maker::set_standard_combined_feedback_fields($dd); + + $dd->choices = $this->make_choice_structure(array( + new qtype_ddmarker_drag_item('quick', 1, 1), + new qtype_ddmarker_drag_item('fox', 2, 1), + new qtype_ddmarker_drag_item('lazy', 1, 2), + new qtype_ddmarker_drag_item('dog', 2, 2) + + )); + + $dd->places = $this->make_place_structure(array( + new qtype_ddmarker_drop_zone('', 1, 1), + new qtype_ddmarker_drop_zone('', 2, 1), + new qtype_ddmarker_drop_zone('', 3, 2), + new qtype_ddmarker_drop_zone('', 4, 2) + )); + $dd->rightchoices = array(1 => 1, 2 => 2, 3 => 1, 4 => 2); + + return $dd; + } + + protected function make_choice_structure($choices) { + $choicestructure = array(); + foreach ($choices as $choice) { + if (!isset($choicestructure[$choice->group])) { + $choicestructure[$choice->group] = array(); + } + $choicestructure[$choice->group][$choice->no] = $choice; + } + return $choicestructure; + } + + protected function make_place_structure($places) { + $placestructure = array(); + foreach ($places as $place) { + $placestructure[$place->no] = $place; + } + return $placestructure; + } + + /** + * @return qtype_ddmarker_question + */ + public function make_ddmarker_question_maths() { + question_bank::load_question_definition_classes('ddmarker'); + $dd = new qtype_ddmarker_question(); + + test_question_maker::initialise_a_question($dd); + + $dd->name = 'Drag-and-drop words into sentences question'; + $dd->questiontext = 'Fill in the operators to make this equation work: ' . + '7 [[1]] 11 [[2]] 13 [[1]] 17 [[2]] 19 = 3'; + $dd->generalfeedback = 'This sentence uses each letter of the alphabet.'; + $dd->qtype = question_bank::get_qtype('ddmarker'); + + $dd->shufflechoices = true; + + test_question_maker::set_standard_combined_feedback_fields($dd); + + $dd->choices = $this->make_choice_structure(array( + new qtype_ddmarker_drag_item('+', 1, 1), + new qtype_ddmarker_drag_item('-', 2, 1) + )); + + $dd->places = $this->make_place_structure(array( + new qtype_ddmarker_drop_zone('', 1, 1), + new qtype_ddmarker_drop_zone('', 2, 1), + new qtype_ddmarker_drop_zone('', 3, 1), + new qtype_ddmarker_drop_zone('', 4, 1) + )); + $dd->rightchoices = array(1 => 1, 2 => 2, 3 => 1, 4 => 2); + + return $dd; + } +} diff --git a/question/type/ddmarker/simpletest/testquestion.php b/question/type/ddmarker/simpletest/testquestion.php new file mode 100644 index 00000000000..3ab04998412 --- /dev/null +++ b/question/type/ddmarker/simpletest/testquestion.php @@ -0,0 +1,269 @@ +. + +/** + * Unit tests for the drag-and-drop words into sentences question definition class. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php'); +require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); + + +/** + * Unit tests for the matching question definition class. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_question_test extends UnitTestCase { + + public function test_get_question_summary() { + $dd = test_question_maker::make_question('ddmarker'); + $this->assertEqual('The quick brown fox jumped over the lazy dog.; '. + '[[Drop zone 1]] -> {1. quick / 2. fox}; '. + '[[Drop zone 2]] -> {1. quick / 2. fox}; '. + '[[Drop zone 3]] -> {1. lazy / 2. dog}; '. + '[[Drop zone 4]] -> {1. lazy / 2. dog}', + $dd->get_question_summary()); + } + + public function test_get_question_summary_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $this->assertEqual('Fill in the operators to make this equation work: '. + '7 [[1]] 11 [[2]] 13 [[1]] 17 [[2]] 19 = 3; '. + '[[Drop zone 1]] -> {1. + / 2. -}; '. + '[[Drop zone 2]] -> {1. + / 2. -}; '. + '[[Drop zone 3]] -> {1. + / 2. -}; '. + '[[Drop zone 4]] -> {1. + / 2. -}', + $dd->get_question_summary()); + } + + public function test_summarise_response() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual('Drop zone 1 -> {1. quick} '. + 'Drop zone 2 -> {1. quick} '. + 'Drop zone 3 -> {1. lazy} '. + 'Drop zone 4 -> {1. lazy}', + $dd->summarise_response(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + } + + public function test_summarise_response_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual('Drop zone 1 -> {1. +} '. + 'Drop zone 2 -> {2. -} '. + 'Drop zone 3 -> {1. +} '. + 'Drop zone 4 -> {2. -}', + $dd->summarise_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + } + + public function test_get_random_guess_score() { + $dd = test_question_maker::make_question('ddmarker'); + $this->assertEqual(0.5, $dd->get_random_guess_score()); + } + + public function test_get_random_guess_score_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $this->assertEqual(0.5, $dd->get_random_guess_score()); + } + + public function test_get_right_choice_for() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(1, $dd->get_right_choice_for(1)); + $this->assertEqual(2, $dd->get_right_choice_for(2)); + } + + public function test_get_right_choice_for_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(1, $dd->get_right_choice_for(1)); + $this->assertEqual(2, $dd->get_right_choice_for(2)); + $this->assertEqual(1, $dd->get_right_choice_for(3)); + $this->assertEqual(2, $dd->get_right_choice_for(4)); + } + + public function test_clear_wrong_from_response() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $initialresponse = array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'); + $this->assertEqual(array('p1' => '1', 'p2' => '', 'p3' => '1', 'p4' => ''), + $dd->clear_wrong_from_response($initialresponse)); + } + + public function test_get_num_parts_right() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array(2, 4), + $dd->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '2'))); + $this->assertEqual(array(4, 4), + $dd->get_num_parts_right(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + } + + public function test_get_num_parts_right_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array(2, 4), + $dd->get_num_parts_right(array( + 'p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + } + + public function test_get_expected_data() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual( + array('p1' => PARAM_INT, 'p2' => PARAM_INT, 'p3' => PARAM_INT, 'p4' => PARAM_INT), + $dd->get_expected_data() + ); + } + + public function test_get_correct_response() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'), + $dd->get_correct_response()); + } + + public function test_get_correct_response_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'), + $dd->get_correct_response()); + } + + public function test_is_same_response() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertTrue($dd->is_same_response( + array(), + array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => ''))); + + $this->assertFalse($dd->is_same_response( + array(), + array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => ''))); + + $this->assertFalse($dd->is_same_response( + array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => ''), + array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => ''))); + + $this->assertTrue($dd->is_same_response( + array('p1' => '1', 'p2' => '2', 'p3' => '3', 'p4' => '4'), + array('p1' => '1', 'p2' => '2', 'p3' => '3', 'p4' => '4'))); + + $this->assertFalse($dd->is_same_response( + array('p1' => '1', 'p2' => '2', 'p3' => '3', 'p4' => '4'), + array('p1' => '1', 'p2' => '2', 'p3' => '2', 'p4' => '4'))); + } + public function test_is_complete_response() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertFalse($dd->is_complete_response(array())); + $this->assertFalse($dd->is_complete_response( + array('p1' => '1', 'p2' => '1', 'p3' => '', 'p4' => ''))); + $this->assertFalse($dd->is_complete_response(array('p1' => '1'))); + $this->assertTrue($dd->is_complete_response( + array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + } + + public function test_is_gradable_response() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertFalse($dd->is_gradable_response(array())); + $this->assertFalse($dd->is_gradable_response( + array('p1' => '', 'p2' => '', 'p3' => '', 'p3' => ''))); + $this->assertTrue($dd->is_gradable_response( + array('p1' => '1', 'p2' => '1', 'p3' => ''))); + $this->assertTrue($dd->is_gradable_response(array('p1' => '1'))); + $this->assertTrue($dd->is_gradable_response( + array('p1' => '1', 'p2' => '1', 'p3' => '1'))); + } + + public function test_grading() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array(1, question_state::$gradedright), + $dd->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + $this->assertEqual(array(0.25, question_state::$gradedpartial), + $dd->grade_response(array('p1' => '1'))); + $this->assertEqual(array(0, question_state::$gradedwrong), + $dd->grade_response(array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1'))); + } + + public function test_grading_maths() { + $dd = test_question_maker::make_question('ddmarker', 'maths'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array(1, question_state::$gradedright), + $dd->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + $this->assertEqual(array(0.5, question_state::$gradedpartial), + $dd->grade_response(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + $this->assertEqual(array(0, question_state::$gradedwrong), + $dd->grade_response(array('p1' => '', 'p2' => '1', 'p3' => '2', 'p4' => '1'))); + } + + public function test_classify_response() { + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $dd->start_attempt(new question_attempt_step(), 1); + + $this->assertEqual(array( + 1 => new question_classified_response(1, '1. quick', 1), + 2 => new question_classified_response(2, '2. fox', 1), + 3 => new question_classified_response(1, '1. lazy', 1), + 4 => new question_classified_response(2, '2. dog', 1) + ), $dd->classify_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + $this->assertEqual(array( + 1 => question_classified_response::no_response(), + 2 => new question_classified_response(1, '1. quick', 0), + 3 => new question_classified_response(2, '2. dog', 0), + 4 => new question_classified_response(2, '2. dog', 1) + ), $dd->classify_response(array('p1' => '', 'p2' => '1', 'p3' => '2', 'p4' => '2'))); + } +} diff --git a/question/type/ddmarker/simpletest/testquestiontype.php b/question/type/ddmarker/simpletest/testquestiontype.php new file mode 100644 index 00000000000..4e2a35a84a0 --- /dev/null +++ b/question/type/ddmarker/simpletest/testquestiontype.php @@ -0,0 +1,58 @@ +. + +/** + * Unit tests for the drag-and-drop words into sentences question definition class. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php'); +require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); + + +/** + * Unit tests for the drag-and-drop words into sentences question definition class. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_test extends UnitTestCase { + /** @var qtype_ddmarker instance of the question type class to test. */ + protected $qtype; + + public function setUp() { + $this->qtype = question_bank::get_qtype('ddmarker');; + } + + public function tearDown() { + $this->qtype = null; + } + + public function test_name() { + $this->assertEqual($this->qtype->name(), 'ddmarker'); + } + + public function test_can_analyse_responses() { + $this->assertTrue($this->qtype->can_analyse_responses()); + } +} diff --git a/question/type/ddmarker/simpletest/testwalkthrough.php b/question/type/ddmarker/simpletest/testwalkthrough.php new file mode 100644 index 00000000000..a5049119605 --- /dev/null +++ b/question/type/ddmarker/simpletest/testwalkthrough.php @@ -0,0 +1,857 @@ +. + +/** + * Unit tests for the drag-and-drop words into sentences question type. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/engine/simpletest/helpers.php'); +require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); + + +/** + * Unit tests for the drag-and-drop words into sentences question type. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { + + protected function get_contains_drag_image_home_expectation($dragitemno, $choice, $group) { + $class = 'group' . $group; + $class .= ' draghome dragitemhomes' . $dragitemno. ' choice'.$choice.' yui3-cssfonts'; + + $expectedattrs = array(); + $expectedattrs['class'] = $class; + + return new ContainsTagWithAttributes('div', $expectedattrs); + } + + public function test_interactive_behaviour() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->hints = array( + new question_hint_with_parts(13, 'This is the first hint.', FORMAT_HTML, false, false), + new question_hint_with_parts(14, 'This is the second hint.', FORMAT_HTML, true, true), + ); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'interactive', 12); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(3), + $this->get_no_hint_visible_expectation()); + + // Save the wrong answer. + $this->process_submission(array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1')); + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 1), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(3), + $this->get_no_hint_visible_expectation()); + // Submit the wrong answer. + $this->process_submission( + array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1', '-submit' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 1), + $this->get_contains_try_again_button_expectation(true), + new PatternExpectation('/' . + preg_quote(get_string('notcomplete', 'qbehaviour_interactive')) . '/'), + $this->get_contains_hint_expectation('This is the first hint')); + + // Do try again. + $this->process_submission(array('-tryagain' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', '2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', '1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', '2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', '1'), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(2), + $this->get_no_hint_visible_expectation()); + + // Submit the right answer. + $this->process_submission( + array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2', '-submit' => 1)); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(8); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', '1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', '2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', '1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', '2'), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_correct_expectation(), + $this->get_no_hint_visible_expectation()); + + // Check regrading does not mess anything up. + $this->quba->regrade_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(8); + } + + public function test_deferred_feedback() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'deferredfeedback', 12); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_does_not_contain_feedback_expectation()); + + // Save a partial answer. + $this->process_submission(array('p1' => '2', 'p2' => '1')); + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + // Save the right answer. + $this->process_submission( + array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(12); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_correct_expectation()); + + // Change the right answer a bit. + $dd->rightchoices[2] = 1; + + // Check regrading does not mess anything up. + $this->quba->regrade_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedpartial); + $this->check_current_mark(9); + } + + public function test_deferred_feedback_unanswered() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'deferredfeedback', 12); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + $this->check_step_count(1); + + // Save a blank response. + $this->process_submission(array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => '')); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + $this->check_step_count(1); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gaveup); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2)); + } + + public function test_deferred_feedback_partial_answer() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'deferredfeedback', 3); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + + // Save a partial response. + $this->process_submission(array('p1' => '1', 'p2' => '2', 'p3' => '', 'p4' => '')); + + // Verify. + $this->check_current_state(question_state::$invalid); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedpartial); + $this->check_current_mark(1.5); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_partcorrect_expectation()); + } + + public function test_interactive_grading() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->hints = array( + new question_hint_with_parts(1, 'This is the first hint.', + FORMAT_MOODLE, true, true), + new question_hint_with_parts(2, 'This is the second hint.', + FORMAT_MOODLE, true, true), + ); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'interactive', 12); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->assertEqual('interactivecountback', + $this->quba->get_question_attempt($this->slot)->get_behaviour_name()); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(3), + $this->get_does_not_contain_num_parts_correct(), + $this->get_no_hint_visible_expectation()); + + // Submit an response with the first two parts right. + $this->process_submission( + array('p1' => '1', 'p2' => '2', 'p3' => '2', 'p4' => '1', '-submit' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_try_again_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + new PatternExpectation('/' . + preg_quote(get_string('notcomplete', 'qbehaviour_interactive')) . '/'), + $this->get_contains_hint_expectation('This is the first hint'), + $this->get_contains_num_parts_correct(2), + $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 1)); + + // Check that extract responses will return the reset data. + $prefix = $this->quba->get_field_prefix($this->slot); + $this->assertEqual(array('p1' => '1', 'p2' => '2'), + $this->quba->extract_responses($this->slot, + array($prefix . 'p1' => '1', $prefix . 'p2' => '2', '-tryagain' => 1))); + + // Do try again. + // keys p3 and p4 are extra hidden fields to clear data. + $this->process_submission( + array('p1' => '1', 'p2' => '2', 'p3' => '', 'p4' => '', '-tryagain' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_try_again_button_expectation(), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(2), + $this->get_no_hint_visible_expectation()); + + // Submit an response with the first and last parts right. + $this->process_submission( + array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '2', '-submit' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_try_again_button_expectation(true), + $this->get_does_not_contain_correctness_expectation(), + new PatternExpectation('/' . + preg_quote(get_string('notcomplete', 'qbehaviour_interactive')) . '/'), + $this->get_contains_hint_expectation('This is the second hint'), + $this->get_contains_num_parts_correct(2), + $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 2)); + + // Do try again. + $this->process_submission( + array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => '2', '-tryagain' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_try_again_button_expectation(), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(1), + $this->get_no_hint_visible_expectation()); + + // Submit the right answer. + $this->process_submission( + array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2', '-submit' => 1)); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(7); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', 1), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_submit_button_expectation(false), + $this->get_does_not_contain_try_again_button_expectation(), + $this->get_contains_correct_expectation(), + $this->get_no_hint_visible_expectation(), + $this->get_does_not_contain_num_parts_correct(), + $this->get_contains_standard_correct_combined_feedback_expectation()); + } + + public function test_interactive_correct_no_submit() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->hints = array( + new question_hint_with_parts(23, 'This is the first hint.', + FORMAT_MOODLE, false, false), + new question_hint_with_parts(24, 'This is the second hint.', + FORMAT_MOODLE, true, true), + ); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'interactive', 3); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(3), + $this->get_no_hint_visible_expectation()); + + // Save the right answer. + $this->process_submission(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')); + + // Finish the attempt without clicking check. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(3); + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_correct_expectation(), + $this->get_no_hint_visible_expectation()); + + // Check regrading does not mess anything up. + $this->quba->regrade_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(3); + } + + public function test_interactive_partial_no_submit() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->hints = array( + new question_hint_with_parts(23, 'This is the first hint.', + FORMAT_MOODLE, false, false), + new question_hint_with_parts(24, 'This is the second hint.', + FORMAT_MOODLE, true, true), + ); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'interactive', 4); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(3), + $this->get_no_hint_visible_expectation()); + + // Save the a partially right answer. + $this->process_submission(array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '1')); + + // Finish the attempt without clicking check. + $this->quba->finish_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedpartial); + $this->check_current_mark(1); + + $this->check_current_output( + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_partcorrect_expectation(), + $this->get_no_hint_visible_expectation()); + + // Check regrading does not mess anything up. + $this->quba->regrade_all_questions(); + + // Verify. + $this->check_current_state(question_state::$gradedpartial); + $this->check_current_mark(1); + } + + public function test_interactive_no_right_clears() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $dd->hints = array( + new question_hint_with_parts(23, 'This is the first hint.', FORMAT_MOODLE, false, true), + new question_hint_with_parts(24, 'This is the second hint.', FORMAT_MOODLE, true, true), + ); + $dd->shufflechoices = false; + $this->start_attempt_at_question($dd, 'interactive', 3); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(3), + $this->get_no_hint_visible_expectation()); + + // Save the a completely wrong answer. + $this->process_submission( + array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1', '-submit' => 1)); + + // Verify. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_submit_button_expectation(false), + $this->get_contains_hint_expectation('This is the first hint')); + + // Do try again. + $this->process_submission( + array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => '', '-tryagain' => 1)); + + // Check that all the wrong answers have been cleared. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_marked_out_of_summary(), + $this->get_contains_drag_image_home_expectation(1, 1, 1), + $this->get_contains_drag_image_home_expectation(2, 2, 1), + $this->get_contains_drag_image_home_expectation(1, 1, 2), + $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', ''), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_submit_button_expectation(true), + $this->get_does_not_contain_feedback_expectation(), + $this->get_tries_remaining_expectation(2), + $this->get_no_hint_visible_expectation()); + } + + public function test_display_of_right_answer_when_shuffled() { + + // Create a drag-and-drop question. + $dd = test_question_maker::make_question('ddmarker'); + $this->start_attempt_at_question($dd, 'deferredfeedback', 3); + + // Check the initial state. + $this->check_current_state(question_state::$todo); + $this->check_current_mark(null); + + $this->check_current_output( + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3'), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_does_not_contain_feedback_expectation()); + + // Save a partial answer. + $this->process_submission($dd->get_correct_response()); + + // Verify. + $this->check_current_state(question_state::$complete); + $this->check_current_mark(null); + $this->check_current_output( + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p1', + $dd->get_right_choice_for(1)), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p2', + $dd->get_right_choice_for(2)), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p3', + $dd->get_right_choice_for(3)), + $this->get_contains_hidden_expectation( + $this->quba->get_field_prefix($this->slot) . 'p4', + $dd->get_right_choice_for(4)), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); + + // Finish the attempt. + $this->quba->finish_all_questions(); + + // Verify. + $this->displayoptions->rightanswer = question_display_options::VISIBLE; + $this->assertEqual('Drop zone 1 -> {1. quick} '. + 'Drop zone 2 -> {2. fox} '. + 'Drop zone 3 -> {1. lazy} '. + 'Drop zone 4 -> {2. dog}', + $dd->get_right_answer_summary()); + $this->check_current_state(question_state::$gradedright); + $this->check_current_mark(3); + } +} diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css new file mode 100644 index 00000000000..30ee6a32c20 --- /dev/null +++ b/question/type/ddmarker/styles.css @@ -0,0 +1,76 @@ +.que.ddmarker .qtext { + margin-bottom: 0.5em; + display: block; +} + +.que.ddmarker div.droparea img, form.mform fieldset#previewareaheader div.droparea img { + border: 1px solid #000000; +} + +.que.ddmarker .draghome, form.mform fieldset#previewareaheader .draghome { + vertical-align: top; + margin: 5px; + visibility : hidden; +} + +.que.ddmarker div.draghome, form.mform fieldset#previewareaheader div.draghome { + border: 1px solid black; + cursor: move; + background-color: #B0C4DE; + display: -moz-inline-stack; + display:inline-block; + height: auto; + width: auto; + zoom: 1; + *display: inline; +} + +.que.ddmarker .group1, form.mform fieldset#previewareaheader .group1 { + background-color: #FFFFFF; +} +.que.ddmarker .group2, form.mform fieldset#previewareaheader .group2 { + background-color: #B0C4DE; +} +.que.ddmarker .group3, form.mform fieldset#previewareaheader .group3 { + background-color: #DCDCDC; +} +.que.ddmarker .group4, form.mform fieldset#previewareaheader .group4 { + background-color: #D8BFD8; +} +.que.ddmarker .group5, form.mform fieldset#previewareaheader .group5 { + background-color: #87CEFA; +} +.que.ddmarker .group6, form.mform fieldset#previewareaheader .group6 { + background-color: #DAA520; +} +.que.ddmarker .group7, form.mform fieldset#previewareaheader .group7 { + background-color: #FFD700; +} +.que.ddmarker .group8, form.mform fieldset#previewareaheader .group8 { + background-color: #F0E68C; +} +.que.ddmarker .drag, form.mform fieldset#previewareaheader .drag { + border: 1px solid black; + cursor: move; + z-index: 2; +} +.que.ddmarker .dragitems.readonly .drag { + cursor: auto; +} +.que.ddmarker div.ddarea, form.mform fieldset#previewareaheader div.ddarea { + text-align : center; +} +.que.ddmarker .dropbackground, form.mform fieldset#previewareaheader .dropbackground { + margin:0 auto; +} +.que.ddmarker .dropzone { + border: 1px solid black; + position: absolute; + z-index: 1; +} +.que.ddmarker div.dragitems div.draghome, .que.ddmarker div.dragitems div.drag, +form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewareaheader div.drag { + font:13px/1.231 arial,helvetica,clean,sans-serif; + *font-size:small; /* for IE */ + *font:x-small; /* for IE in quirks mode */ +} diff --git a/question/type/ddmarker/version.php b/question/type/ddmarker/version.php new file mode 100644 index 00000000000..fc9ce78417d --- /dev/null +++ b/question/type/ddmarker/version.php @@ -0,0 +1,29 @@ +. + +/** + * Version information for the drag-and-drop words into sentences question type. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2011051200; +$plugin->requires = 2011051212; diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js new file mode 100644 index 00000000000..6e37bb1832b --- /dev/null +++ b/question/type/ddmarker/yui/dd/dd.js @@ -0,0 +1,468 @@ +YUI.add('moodle-qtype_ddmarker-dd', function(Y) { + var ddmarkerDDNAME = 'ddmarker_dd'; + var ddmarker_DD = function() { + ddmarker_DD.superclass.constructor.apply(this, arguments); + } + /** + * This is the base class for the question rendering and question editing form code. + */ + Y.extend(ddmarker_DD, Y.Base, { + doc : null, + polltimer : null, + afterimageloaddone : false, + poll_for_image_load : function (e, waitforimageconstrain, pause, doafterwords) { + if (this.afterimageloaddone) { + return; + } + var bgdone = this.doc.bg_img().get('complete'); + if (waitforimageconstrain) { + bgdone = bgdone && this.doc.bg_img().hasClass('constrained'); + } + var alldragsloaded = !this.doc.drag_item_homes().some(function(dragitemhome){ + //in 'some' loop returning true breaks the loop and is passed as return value from + //'some' else returns false. Can be though of as equivalent to ||. + if (dragitemhome.get('tagName') !== 'IMG'){ + return false; + } + var done = (dragitemhome.get('complete')); + if (waitforimageconstrain) { + done = done && dragitemhome.hasClass('constrained'); + } + return !done; + }); + if (bgdone && alldragsloaded) { + if (this.polltimer !== null) { + this.polltimer.cancel(); + this.polltimer = null; + } + this.doc.drag_item_homes().detach('load', this.poll_for_image_load); + this.doc.bg_img().detach('load', this.poll_for_image_load); + if (pause !== 0) { + Y.later(pause, this, doafterwords); + } else { + doafterwords.call(this); + } + this.afterimageloaddone = true; + } else if (this.polltimer === null) { + var pollarguments = [null, waitforimageconstrain, pause, doafterwords]; + this.polltimer = + Y.later(1000, this, this.poll_for_image_load, pollarguments, true); + } + }, + /** + * Object to encapsulate operations on dd area. + */ + doc_structure : function (mainobj) { + var topnode = Y.one(this.get('topnode')); + var dragitemsarea = topnode.one('div.dragitems'); + var dropbgarea = topnode.one('div.droparea'); + return { + top_node : function() { + return topnode; + }, + drag_items : function() { + return dragitemsarea.all('.drag'); + }, + drop_zones : function() { + return topnode.all('div.dropzones div.dropzone'); + }, + drop_zone_group : function(groupno) { + return topnode.all('div.dropzones div.group' + groupno); + }, + drag_items_cloned_from : function(dragitemno) { + return dragitemsarea.all('.dragitems'+dragitemno); + }, + drag_item : function(draginstanceno) { + return dragitemsarea.one('.draginstance' + draginstanceno); + }, + drag_items_in_group : function(groupno) { + return dragitemsarea.all('.drag.group' + groupno); + }, + drag_item_homes : function() { + return dragitemsarea.all('.draghome'); + }, + bg_img : function() { + return topnode.one('.dropbackground'); + }, + load_bg_img : function (url) { + dropbgarea.setContent(''); + this.bg_img().on('load', this.on_image_load, this, 'bg_image'); + }, + add_or_update_drag_item_home : function (dragitemno, url, alt, group) { + var oldhome = this.drag_item_home(dragitemno); + var classes = 'draghome dragitemhomes'+dragitemno+' group'+group; + var imghtml = ''+alt+''; + var divhtml = '
'+alt+'
'; + if (oldhome === null) { + if (url) { + dragitemsarea.append(imghtml); + } else if (alt !== '') { + dragitemsarea.append(divhtml); + } + } else { + if (url) { + dragitemsarea.insert(imghtml, oldhome); + } else if (alt !== '') { + dragitemsarea.insert(divhtml, oldhome); + } + oldhome.remove(true); + } + var newlycreated = dragitemsarea.one('.dragitemhomes'+dragitemno); + if (newlycreated !== null) { + newlycreated.setData('groupno', group); + newlycreated.setData('dragitemno', dragitemno); + } + }, + drag_item_home : function (dragitemno) { + return dragitemsarea.one('.dragitemhomes' + dragitemno); + }, + get_classname_numeric_suffix : function(node, prefix) { + var classes = node.getAttribute('class'); + if (classes !== '') { + var classesarr = classes.split(' '); + for (index in classesarr) { + var patt1 = new RegExp('^'+prefix+'([0-9])+$'); + if (patt1.test(classesarr[index])) { + var patt2 = new RegExp('([0-9])+$'); + var match = patt2.exec(classesarr[index]); + return +match[0]; + } + } + } + throw 'Prefix "'+prefix+'" not found in class names.'; + }, + clone_new_drag_item : function (draginstanceno, dragitemno) { + var draghome = this.drag_item_home(dragitemno); + if (draghome === null) { + return null; + } + var drag = draghome.cloneNode(true); + drag.removeClass('dragitemhomes' + dragitemno); + drag.addClass('dragitems' + dragitemno); + drag.addClass('draginstance' + draginstanceno); + drag.removeClass('draghome'); + drag.addClass('drag'); + drag.setStyles({'visibility': 'visible', 'position' : 'absolute'}); + drag.setData('draginstanceno', draginstanceno); + drag.setData('dragitemno', dragitemno); + draghome.get('parentNode').appendChild(drag); + return drag; + }, + draggable_for_question : function (drag, group, choice) { + var dd = new Y.DD.Drag({ + node: drag, + dragMode: 'intersect' + }).plug(Y.Plugin.DDConstrained, {constrain2node: topnode}); + + drag.setData('group', group); + drag.setData('choice', choice); + + }, + draggable_for_form : function (drag) { + var dd = new Y.DD.Drag({ + node: drag, + dragMode: 'intersect' + }).plug(Y.Plugin.DDConstrained, {constrain2node: topnode}); + dd.on('drag:end', function(e) { + var dragnode = e.target.get('node'); + var draginstanceno = dragnode.getData('draginstanceno'); + var gooddrop = dragnode.getData('gooddrop'); + var endxy; + + if (!gooddrop) { + mainobj.reset_drag_xy(draginstanceno); + } else { + mainobj.set_drag_xy(draginstanceno, [e.pageX, e.pageY]); + } + }, this); + dd.on('drag:start', function(e) { + var drag = e.target; + drag.get('node').setData('gooddrop', false); + }, this); + + } + + } + }, + + update_padding_sizes_all : function () { + for (var groupno = 1; groupno <= 8; groupno++) { + this.update_padding_size_for_group(groupno); + } + }, + update_padding_size_for_group : function (groupno) { + var groupitems = this.doc.top_node().all('.draghome.group'+groupno); + if (groupitems.size() !== 0) { + var maxwidth = 0; + var maxheight = 0; + groupitems.each(function(item){ + maxwidth = Math.max(maxwidth, item.get('clientWidth')); + maxheight = Math.max(maxheight, item.get('clientHeight')); + }, this); + groupitems.each(function(item) { + var margintopbottom = Math.round((10 + maxheight - item.get('clientHeight')) / 2); + var marginleftright = Math.round((10 + maxwidth - item.get('clientWidth')) / 2); + item.setStyle('padding', margintopbottom+'px '+marginleftright+'px ' + +margintopbottom+'px '+marginleftright+'px'); + }, this); + this.doc.drop_zone_group(groupno).setStyles({'width': maxwidth + 10, + 'height': maxheight + 10}); + } + }, + convert_to_window_xy : function (bgimgxy) { + return [+bgimgxy[0] + this.doc.bg_img().getX() + 1, + +bgimgxy[1] + this.doc.bg_img().getY() + 1]; + } + }, { + NAME : ddmarkerDDNAME, + ATTRS : { + drops : {value : null}, + readonly : {value : false}, + topnode : {value : null} + } + }); + M.qtype_ddmarker = M.qtype_ddmarker || {}; + M.qtype_ddmarker.dd_base_class = ddmarker_DD; + + var ddmarkerQUESTIONNAME = 'ddmarker_question'; + var ddmarker_QUESTION = function() { + ddmarker_QUESTION.superclass.constructor.apply(this, arguments); + }; + /** + * This is the code for question rendering. + */ + Y.extend(ddmarker_QUESTION, M.qtype_ddmarker.dd_base_class, { + initializer : function(params) { + this.doc = this.doc_structure(this); + this.poll_for_image_load(null, false, 0, this.create_all_drag_and_drops); + this.doc.bg_img().after('load', this.poll_for_image_load, this, + false, 0, this.create_all_drag_and_drops); + this.doc.drag_item_homes().after('load', this.poll_for_image_load, this, + false, 0, this.create_all_drag_and_drops); + Y.later(500, this, this.reposition_drags_for_question, [], true); + }, + create_all_drag_and_drops : function () { + this.init_drops(); + this.update_padding_sizes_all(); + var i = 0; + this.doc.drag_item_homes().each(function(dragitemhome){ + var dragitemno = + +this.doc.get_classname_numeric_suffix(dragitemhome, 'dragitemhomes'); + var choice = +this.doc.get_classname_numeric_suffix(dragitemhome, 'choice'); + var group = +this.doc.get_classname_numeric_suffix(dragitemhome, 'group') + var groupsize = this.doc.drop_zone_group(group).size(); + var dragnode = this.doc.clone_new_drag_item(i, dragitemno); + i++; + if (!this.get('readonly')) { + this.doc.draggable_for_question(dragnode, group, choice); + } + if (dragnode.hasClass('infinite')) { + var dragstocreate = groupsize - 1; + while (dragstocreate > 0) { + dragnode = this.doc.clone_new_drag_item(i, dragitemno); + i++; + if (!this.get('readonly')) { + this.doc.draggable_for_question(dragnode, group, choice); + } + dragstocreate--; + } + } + }, this); + this.reposition_drags_for_question(); + if (!this.get('readonly')) { + this.doc.drop_zones().set('tabIndex', 0); + this.doc.drop_zones().each( + function(v){ + v.on('dragchange', this.drop_zone_key_press, this); + } + , this); + } + }, + drop_zone_key_press : function (e) { + switch (e.direction) { + case 'next' : + this.place_next_drag_in(e.target); + break; + case 'previous' : + this.place_previous_drag_in(e.target); + break; + case 'remove' : + this.remove_drag_from_drop(e.target); + break; + } + e.preventDefault(); + this.reposition_drags_for_question(); + }, + place_next_drag_in : function (drop) { + this.search_for_unplaced_drop_choice(drop, 1); + }, + place_previous_drag_in : function (drop) { + this.search_for_unplaced_drop_choice(drop, -1); + }, + search_for_unplaced_drop_choice : function (drop, direction) { + var next; + var current = this.current_drag_in_drop(drop); + if ('' === current) { + if (direction == 1) { + next = 1; + } else { + next = 1; + var groupno = drop.getData('group'); + this.doc.drag_items_in_group(groupno).each(function(drag) { + next = Math.max(next, drag.getData('choice')); + }, this); + } + } else { + next = + current + direction; + } + var drag; + do { + if (this.get_choices_for_drop(next, drop).size() === 0){ + this.remove_drag_from_drop(drop); + return; + } else { + drag = this.get_unplaced_choice_for_drop(next, drop); + } + next = next + direction; + } while (drag === null); + this.place_drag_in_drop(drag, drop); + }, + current_drag_in_drop : function (drop) { + var inputid = drop.getData('inputid'); + var inputnode = Y.one('input#'+inputid); + return inputnode.get('value'); + }, + remove_drag_from_drop : function (drop) { + this.place_drag_in_drop(null, drop); + }, + place_drag_in_drop : function (drag, drop) { + var inputid = drop.getData('inputid'); + var inputnode = Y.one('input#'+inputid); + if (drag !== null) { + inputnode.set('value', drag.getData('choice')); + } else { + inputnode.set('value', ''); + } + }, + reposition_drags_for_question : function() { + this.doc.drag_items().removeClass('placed'); + this.doc.drag_items().each (function (dragitem) { + if (dragitem.dd !== undefined) { + dragitem.dd.detachAll('drag:start'); + } + }, this); + this.doc.drop_zones().each(function(dropzone) { + var relativexy = dropzone.getData('xy'); + dropzone.setXY(this.convert_to_window_xy(relativexy)); + var inputcss = 'input#' + dropzone.getData('inputid'); + var input = this.doc.top_node().one(inputcss); + var choice = input.get('value'); + if (choice !== "") { + var dragitem = this.get_unplaced_choice_for_drop(choice, dropzone); + if (dragitem !== null) { + dragitem.setXY(dropzone.getXY()); + dragitem.addClass('placed'); + if (dragitem.dd !== undefined) { + dragitem.dd.once('drag:start', function (e, input) { + input.set('value', ''); + e.target.get('node').removeClass('placed'); + },this, input); + } + } + } + }, this); + this.doc.drag_items().each(function(dragitem) { + if (!dragitem.hasClass('placed') && !dragitem.hasClass('yui3-dd-dragging')) { + var dragitemhome = this.doc.drag_item_home(dragitem.getData('dragitemno')); + dragitem.setXY(dragitemhome.getXY()); + } + }, this); + }, + get_choices_for_drop : function(choice, drop) { + var group = drop.getData('group'); + var dragitem = null; + var dragitems = this.doc.top_node() + .all('div.dragitemgroup'+group+' .choice'+choice+'.drag'); + return dragitems; + }, + get_unplaced_choice_for_drop : function(choice, drop) { + var dragitems = this.get_choices_for_drop(choice, drop); + var dragitem = null; + if (dragitems.some(function (d) { + if (!d.hasClass('placed') && !d.hasClass('yui3-dd-dragging')) { + dragitem = d; + return true; + } else { + return false; + } + })); + return dragitem; + }, + init_drops : function () { + var dropareas = this.doc.top_node().one('div.dropzones'); + var groupnodes = {}; + for (var groupno =1; groupno <= 8; groupno++) { + var groupnode = Y.Node.create('
'); + dropareas.append(groupnode); + groupnodes[groupno] = groupnode; + } + for (var dropno in this.get('drops')){ + var drop = this.get('drops')[dropno]; + var nodeclass = 'dropzone group'+drop.group+' place'+dropno; + var title = drop.text.replace('"', '\"'); + var dropnodehtml = '
 
'; + var dropnode = Y.Node.create(dropnodehtml); + groupnodes[drop.group].append(dropnode); + dropnode.setStyles({'opacity': 0.5}); + dropnode.setData('xy', drop.xy); + dropnode.setData('place', dropno); + dropnode.setData('inputid', drop.fieldname.replace(':', '_')); + dropnode.setData('group', drop.group); + var dropdd = new Y.DD.Drop({ + node: dropnode}); + dropdd.on('drop:hit', function(e) { + var drag = e.drag.get('node'); + var drop = e.drop.get('node'); + if (+drop.getData('group') === drag.getData('group')){ + this.place_drag_in_drop(drag, drop); + } + }, this); + }; + } + }, {NAME : ddmarkerQUESTIONNAME, ATTRS : {}}); + + Y.Event.define('dragchange', { + // Webkit and IE repeat keydown when you hold down arrow keys. + // Opera links keypress to page scroll; others keydown. + // Firefox prevents page scroll via preventDefault() on either + // keydown or keypress. + _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress', + + _keys: { + '32': 'next', + '37': 'previous', + '38': 'previous', + '39': 'next', + '40': 'next', + '27': 'remove' + }, + + _keyHandler: function (e, notifier) { + if (this._keys[e.keyCode]) { + e.direction = this._keys[e.keyCode]; + notifier.fire(e); + } + }, + + on: function (node, sub, notifier) { + sub._detacher = node.on(this._event, this._keyHandler, + this, notifier); + } + }); + M.qtype_ddmarker.init_question = function(config) { + return new ddmarker_QUESTION(config); + } +}, '@VERSION@', { + requires:['node', 'dd', 'dd-drop', 'dd-constrain'] +}); diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js new file mode 100644 index 00000000000..387c4cc67c0 --- /dev/null +++ b/question/type/ddmarker/yui/form/form.js @@ -0,0 +1,341 @@ +/** + * This is the question editing form code. + */ +YUI.add('moodle-qtype_ddmarker-form', function(Y) { + var ddmarkerFORMNAME = 'ddmarker_form'; + var ddmarker_FORM = function() { + ddmarker_FORM.superclass.constructor.apply(this, arguments); + }; + Y.extend(ddmarker_FORM, M.qtype_ddmarker.dd_base_class, { + fp : null, + + initializer : function(params) { + this.fp = this.file_pickers(); + Y.one(this.get('topnode')).append('
'+ + '
'+ + '
'); + this.doc = this.doc_structure(this); + this.draw_dd_area(); + }, + + draw_dd_area : function() { + var bgimageurl = this.fp.file('bgimage').href; + this.stop_selector_events(); + this.set_options_for_drag_item_selectors(); + if (bgimageurl !== null) { + this.doc.load_bg_img(bgimageurl); + this.load_drag_homes(); + + var drop = new Y.DD.Drop({ + node: this.doc.bg_img() + }); + //Listen for a drop:hit on the background image + drop.on('drop:hit', function(e) { + e.drag.get('node').setData('gooddrop', true); + }); + + this.afterimageloaddone = false; + this.doc.bg_img().on('load', this.constrain_image_size, this, 'bgimage'); + this.doc.drag_item_homes() + .on('load', this.constrain_image_size, this, 'dragimage'); + this.doc.bg_img().after('load', this.poll_for_image_load, this, + true, 0, this.after_all_images_loaded); + this.doc.drag_item_homes() .after('load', this.poll_for_image_load, this, + true, 0, this.after_all_images_loaded); + } else { + this.setup_form_events(); + } + this.update_visibility_of_file_pickers(); + }, + + after_all_images_loaded : function () { + this.update_padding_sizes_all(); + this.update_drag_instances(); + this.reposition_drags_for_form(); + this.set_options_for_drag_item_selectors(); + this.setup_form_events(); + Y.later(500, this, this.reposition_drags_for_form, [], true); + }, + + constrain_image_size : function (e, imagetype) { + var maxsize = this.get('maxsizes')[imagetype]; + var reduceby = Math.max(e.target.get('width') / maxsize.width, + e.target.get('height') / maxsize.height); + if (reduceby > 1) { + e.target.set('width', Math.floor(e.target.get('width') / reduceby)); + } + e.target.addClass('constrained'); + e.target.detach('load', this.constrain_image_size); + }, + + load_drag_homes : function () { + //set up drag items homes + for (var i=0; i < this.form.get_form_value('noitems', []); i++) { + this.load_drag_home(i); + } + }, + + load_drag_home : function (dragitemno) { + if ('image' === this.form.get_form_value('dragitemtype', [dragitemno])) { + var url = + this.fp.file(this.form.to_name_with_index('dragitem', [dragitemno])).href; + } else { + var url = null; + } + this.doc.add_or_update_drag_item_home(dragitemno, url, + this.form.get_form_value('drags', [dragitemno, 'draglabel']), + this.form.get_form_value('drags', [dragitemno, 'draggroup'])); + }, + + update_drag_instances : function () { + //set up drop zones + for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { + var dragitemno = this.form.get_form_value('drops', [i, 'choice']); + if (dragitemno !== '0' && (this.doc.drag_item(i) === null)) { + var drag = this.doc.clone_new_drag_item(i, dragitemno - 1); + if (drag !== null) { + this.doc.draggable_for_form(drag); + } + } + } + }, + set_options_for_drag_item_selectors : function () { + var dragitemsoptions = {0: ''}; + for (var i=0; i < this.form.get_form_value('noitems', []); i++) { + var label = this.form.get_form_value('drags', [i, 'draglabel']); + var file = this.fp.file(this.form.to_name_with_index('dragitem', [i])); + if ('image' === this.form.get_form_value('dragitemtype', [i]) + && file.name !== null) { + dragitemsoptions[i+1] = (i+1)+'. '+label+' ('+file.name+')'; + } else if (label != '') { + dragitemsoptions[i+1] = (i+1)+'. '+label; + } + } + for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { + var selector = Y.one('#id_drops_'+i+'_choice'); + var selectedvalue = selector.get('value'); + selector.all('option').remove(true); + for (var value in dragitemsoptions) { + value = +value; + var option = ''; + selector.append(option); + var optionnode = selector.one('option[value="' + value + '"]') + if (value === +selectedvalue) { + optionnode.set('selected', true); + } else { + if (value !== 0) { // no item option is always selectable + var cbselector = 'fieldset#draggableitemheader_'+(value-1) + +' input[type="checkbox"]'; + var cbel = Y.one(cbselector); + var infinite = cbel.get('checked'); + if ((!infinite) && + (this.doc.drag_items_cloned_from(value - 1).size() !== 0)) { + optionnode.set('disabled', true); + } + } + } + } + } + }, + + stop_selector_events : function () { + Y.all('fieldset#dropzoneheader select').detachAll(); + }, + + setup_form_events : function () { + //events triggered by changes to form data + + //x and y coordinates + Y.all('fieldset#dropzoneheader input').on('blur', function (e){ + var name = e.target.getAttribute('name'); + var draginstanceno = this.form.from_name_with_index(name).indexes[0]; + var fromform = [this.form.get_form_value('drops', [draginstanceno, 'xleft']), + this.form.get_form_value('drops', [draginstanceno, 'ytop'])]; + var constrainedxy = this.constrain_xy(draginstanceno, fromform); + this.form.set_form_value('drops', [draginstanceno, 'xleft'], constrainedxy[0]); + this.form.set_form_value('drops', [draginstanceno, 'ytop'], constrainedxy[1]); + }, this); + + //change in selected item + Y.all('fieldset#dropzoneheader select').on('change', function (e){ + var name = e.target.getAttribute('name'); + var draginstanceno = this.form.from_name_with_index(name).indexes[0]; + var old = this.doc.drag_item(draginstanceno); + if (old !== null) { + old.remove(true); + } + this.draw_dd_area(); + }, this); + + for (var i=0; i < this.form.get_form_value('noitems', []); i++) { + //change to group selector + Y.all('fieldset#draggableitemheader_'+i+' select.draggroup') + .on('change', function (e){ + this.doc.drag_items().remove(true); + this.draw_dd_area(); + }, this); + Y.all('fieldset#draggableitemheader_'+i+' select.dragitemtype') + .on('change', function (e){ + this.doc.drag_items().remove(true); + this.draw_dd_area(); + }, this); + Y.all('fieldset#draggableitemheader_'+i+' input[type="text"]') + .on('blur', function (e, draginstanceno){ + this.doc.drag_item(draginstanceno).remove(true); + this.draw_dd_area(); + }, this, i); + //change to infinite checkbox + Y.all('fieldset#draggableitemheader_'+i+' input[type="checkbox"]') + .on('change', this.set_options_for_drag_item_selectors, this); + } + //event on file picker new file selection + Y.after(function (e){ + var name = this.fp.name(e.id); + if (name !== 'bgimage') { + this.doc.drag_items().remove(true); + } + this.draw_dd_area(); + }, M.form_filepicker, 'callback', this); + }, + + update_visibility_of_file_pickers : function() { + for (var i=0; i < this.form.get_form_value('noitems', []); i++) { + if ('image' === this.form.get_form_value('dragitemtype', [i])) { + Y.one('input#id_dragitem_'+i).get('parentNode').get('parentNode') + .setStyle('display', 'block'); + } else { + Y.one('input#id_dragitem_'+i).get('parentNode').get('parentNode') + .setStyle('display', 'none'); + } + } + }, + + reposition_drags_for_form : function() { + this.doc.drag_items().each(function (drag) { + var draginstanceno = drag.getData('draginstanceno'); + this.reposition_drag_for_form(draginstanceno); + }, this); + }, + + reposition_drag_for_form : function (draginstanceno) { + var drag = this.doc.drag_item(draginstanceno); + if (null !== drag && !drag.hasClass('yui3-dd-dragging')) { + var fromform = [this.form.get_form_value('drops', [draginstanceno, 'xleft']), + this.form.get_form_value('drops', [draginstanceno, 'ytop'])]; + if (fromform[0] == '' && fromform[1] == '') { + var dragitemno = drag.getData('dragitemno'); + drag.setXY(this.doc.drag_item_home(dragitemno).getXY()); + } else { + drag.setXY(this.convert_to_window_xy(fromform)); + } + } + }, + set_drag_xy : function (draginstanceno, xy) { + xy = this.constrain_xy(draginstanceno, this.convert_to_bg_img_xy(xy)); + this.form.set_form_value('drops', [draginstanceno, 'xleft'], Math.round(xy[0])); + this.form.set_form_value('drops', [draginstanceno, 'ytop'], Math.round(xy[1])); + }, + reset_drag_xy : function (draginstanceno) { + this.form.set_form_value('drops', [draginstanceno, 'xleft'], ''); + this.form.set_form_value('drops', [draginstanceno, 'ytop'], ''); + }, + + //make sure xy value is not out of bounds of bg image + constrain_xy : function (draginstanceno, bgimgxy) { + var drag = this.doc.drag_item(draginstanceno); + var xleftconstrained = + Math.min(bgimgxy[0], this.doc.bg_img().get('width') - drag.get('offsetWidth')); + var ytopconstrained = + Math.min(bgimgxy[1], this.doc.bg_img().get('height') - drag.get('offsetHeight')); + xleftconstrained = Math.max(xleftconstrained, 0); + ytopconstrained = Math.max(ytopconstrained, 0); + return [xleftconstrained, ytopconstrained]; + }, + convert_to_bg_img_xy : function (windowxy) { + return [+windowxy[0] - this.doc.bg_img().getX()-1, + +windowxy[1] - this.doc.bg_img().getY()-1]; + }, + + /** + * Low level operations on form. + */ + form : { + to_name_with_index : function(name, indexes) { + var indexstring = name; + for (var i=0; i < indexes.length; i++) { + indexstring = indexstring + '[' + indexes[i] + ']'; + } + return indexstring; + }, + get_el : function (name, indexes) { + var form = document.getElementById('mform1'); + return form.elements[this.to_name_with_index(name, indexes)] + }, + get_form_value : function(name, indexes) { + var el = this.get_el(name, indexes); + if (el.type === 'checkbox') { + return el.checked; + } else { + return el.value; + } + }, + set_form_value : function(name, indexes, value) { + var el = this.get_el(name, indexes); + if (el.type === 'checkbox') { + el.checked = value; + } else { + el.value = value; + } + }, + from_name_with_index : function(name) { + var toreturn = {}; + toreturn.indexes = []; + var bracket = name.indexOf('['); + toreturn.name = name.substring(0, bracket); + while (bracket !== -1) { + var end = name.indexOf(']', bracket + 1); + toreturn.indexes.push(name.substring(bracket + 1, end)); + bracket = name.indexOf('[', end + 1); + } + return toreturn; + } + }, + + file_pickers : function () { + var draftitemidstoname; + var nametoparentnode; + if (draftitemidstoname === undefined) { + draftitemidstoname = {}; + nametoparentnode = {}; + var filepickers = Y.all('form.mform input.filepickerhidden'); + filepickers.each(function(filepicker, k, items) { + draftitemidstoname[filepicker.get('value')] = filepicker.get('name'); + nametoparentnode[filepicker.get('name')] = filepicker.get('parentNode'); + }, this); + } + var toreturn = { + file : function (name) { + var parentnode = nametoparentnode[name]; + var fileanchor = parentnode.one('div.filepicker-filelist a'); + if (fileanchor) { + return {href : fileanchor.get('href'), name : fileanchor.get('innerHTML')}; + } else { + return {href : null, name : null}; + } + }, + name : function (draftitemid) { + return draftitemidstoname[draftitemid]; + } + } + return toreturn; + } + }, {NAME : ddmarkerFORMNAME, ATTRS : {maxsizes:{value:null}}}); + M.qtype_ddmarker = M.qtype_ddmarker || {}; + M.qtype_ddmarker.init_form = function(config) { + return new ddmarker_FORM(config); + } +}, '@VERSION@', { + requires:['moodle-qtype_ddmarker-dd', 'form_filepicker'] +}); \ No newline at end of file From 8e1d95beb11345eae84c32da09cdf84ff6521f70 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 24 Sep 2011 15:57:31 +0700 Subject: [PATCH 002/178] MDL-47494 ddmarker: NOBUG edited README.txt --- question/type/ddmarker/README.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/README.txt b/question/type/ddmarker/README.txt index 7f37695349c..5376a3f7786 100644 --- a/question/type/ddmarker/README.txt +++ b/question/type/ddmarker/README.txt @@ -1,13 +1,15 @@ A drag-and-drop question type. -You can use text and / or images as drag items onto defined drop zones on a background image. +You can use markers with text labels as drag items onto rectangular, and in Moodle 2.2+ version +circular and polygon defined drop zones on a background image. This question type requires that gapselect question type -https://github.com/timhunt/moodle-qtype_gapselect/ +https://github.com/timhunt/moodle-qtype_ddimageortext/ and +https://github.com/timhunt/moodle-qtype_gapselect/ to be installed in order to work. This question type was written by Jamie Pratt (http://jamiep.org/). -This question type is compatible with Moodle 2.1. +This question type will be compatible with Moodle 2.1+ or 2.2+. To install using git, type this command in the root of your Moodle install : From 878ecb4804e7824967d4ff89c213ad6c31de8240 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 26 Sep 2011 14:29:20 +0700 Subject: [PATCH 003/178] MDL-47494 ddmarker: NOBUG first try at an icon for the question type. --- question/type/ddmarker/pix/icon.gif | Bin 361 -> 370 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/question/type/ddmarker/pix/icon.gif b/question/type/ddmarker/pix/icon.gif index f5705c637b7bd1003995fef64a48e51ae378b29e..26338f3790743c0e2b3064d4e589ae1ad722b58d 100644 GIT binary patch literal 370 zcmZ?wbhEHb6krfwxXQrr|NsAg|Ni~`3q(NT-=DwlzW=)a?c29^pHDx3{psnmmycfW zyYqDC#XB31Ufg)(;`%Lzx2)K4X~p)7%eGxuvSa1K&4=f%KR$Ey$$2Yw%~`&)f7$xp zMTaKOS>8Nr@8lT^`X)|o>|IpUyRD2C_@04WW7^rL($$$hD zf3h&JGyG@J0f~Y9#K6|?FtNZxhl~A$iqL@^Iqz*5B?lgBu4-7UGu3EfNRt+ObxW%W z4@+x+>qJ-Pi06$lEEN_u`rPfg3T&*r%eqS6mz!tO5F{q_hA4 literal 361 zcmV-v0hazpNk%w1VGsZi0M!5hBQ{wtLSi#VYBWe{H%DSPK~XzRY&}qMKT&T(RB}aB zZb(;iNm+AIQBhKKqgQyQU3{rwf2(7Gu4i?RXoRY1hOuplvvG~5a*eiikhge}xq60} zeTkicikg9qqJx^fhLNL)iiL=t!HuHAkEXAarpA@1$CjzcnXAd1r@5W4$DXLVp|iuH zv(BWrsiwBjsJGFmxYD%4w8YV`#nG|R+_Bu{x#H`-?(@s`_V)Ms-~9dV{r&v_00000 z00000A^8Le002DzEC2ui01yBW000Jtz>siAEDCwZq*8g@5T4NJCU-Ertp!prI)Z4IPFA2C1n50Hg({2?+-V0|KO^rydi#yu5}^QNdG3ImO1u#XLsK HL_q*M7u=r% From dda954a26ca040d1dca363b1bbfe5bbb31bcd208 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 26 Sep 2011 14:29:44 +0700 Subject: [PATCH 004/178] MDL-47494 ddmarker: NOBUG extending functionality from ddimageortext question type. --- question/type/ddmarker/edit_ddmarker_form.php | 348 +------------- .../type/ddmarker/lang/en/qtype_ddmarker.php | 4 +- question/type/ddmarker/question.php | 154 +------ question/type/ddmarker/questiontype.php | 424 +----------------- question/type/ddmarker/renderer.php | 144 +----- 5 files changed, 22 insertions(+), 1052 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index cb4fdee4d1b..dcb0bb32128 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +require_once($CFG->dirroot.'/question/type/ddimageortext/edit_ddtoimage_form_base.php'); + /** * Defines the editing form for the drag-and-drop images onto images question type. * @@ -31,351 +33,7 @@ defined('MOODLE_INTERNAL') || die(); * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class qtype_ddmarker_edit_form extends question_edit_form { - const MAX_GROUPS = 8; - const START_NUM_ITEMS = 6; - const ADD_NUM_ITEMS = 3; - - public function qtype() { - return 'ddmarker'; - } - - /** - * - * Options shared by all file pickers in the form. - */ - public static function file_picker_options() { - $filepickeroptions = array(); - $filepickeroptions['accepted_types'] = array('web_image'); - $filepickeroptions['maxbytes'] = 0; - $filepickeroptions['maxfiles'] = 1; - $filepickeroptions['subdirs'] = 0; - return $filepickeroptions; - } - - protected function get_drag_image_repeats() { - $countimages = 0; - if (isset($this->question->id)) { - foreach ($this->question->options->drags as $drag) { - $countimages = max($countimages, $drag->no); - } - } - if ($this->question->formoptions->repeatelements) { - $imagerepeatsatstart = max(self::START_NUM_ITEMS, $countimages + self::ADD_NUM_ITEMS); - } else { - $imagerepeatsatstart = $countimages; - } - $imagerepeats = optional_param('noitems', $imagerepeatsatstart, PARAM_INT); - $addfields = optional_param('additems', '', PARAM_TEXT); - if (!empty($addfields)) { - $imagerepeats += self::ADD_NUM_ITEMS; - } - return array($imagerepeatsatstart, $imagerepeats); - } - - /** - * definition_inner adds all specific fields to the form. - * @param object $mform (the form being built). - */ - protected function definition_inner($mform) { - - $mform->addElement('header', 'previewareaheader', - get_string('previewareaheader', 'qtype_ddmarker')); - $mform->addElement('static', 'previewarea', - get_string('previewarea', 'qtype_ddmarker'), - get_string('previewareamessage', 'qtype_ddmarker')); - - $mform->registerNoSubmitButton('refresh'); - $mform->addElement('submit', 'refresh', get_string('refresh', 'qtype_ddmarker')); - $mform->closeHeaderBefore('refresh'); - - list($imagerepeatsatstart, $imagerepeats) = $this->get_drag_image_repeats(); - $this->definition_drop_zones($mform, $imagerepeats); - $mform->addElement('advcheckbox', 'shuffleanswers', ' ', - get_string('shuffleimages', 'qtype_ddmarker')); - $mform->setDefault('shuffleanswers', 0); - $mform->closeHeaderBefore('shuffleanswers'); - //add the draggable image fields to the form - $this->definition_draggable_images($mform, $imagerepeatsatstart); - - $this->add_combined_feedback_fields(true); - $this->add_interactive_settings(true, true); - } - - protected function definition_drop_zones($mform, $imagerepeats) { - $mform->addElement('header', 'dropzoneheader', - get_string('dropzoneheader', 'qtype_ddmarker')); - - $mform->addElement('filepicker', 'bgimage', get_string('bgimage', 'qtype_ddmarker'), - null, self::file_picker_options()); - - $countdropzones = 0; - if (isset($this->question->id)) { - foreach ($this->question->options->drops as $drop) { - $countdropzones = max($countdropzones, $drop->no); - } - } - if ($this->question->formoptions->repeatelements) { - $dropzonerepeatsatstart = max(self::START_NUM_ITEMS, - $countdropzones + self::ADD_NUM_ITEMS); - } else { - $dropzonerepeatsatstart = $countdropzones; - } - - $this->repeat_elements($this->drop_zone($mform, $imagerepeats), $dropzonerepeatsatstart, - $this->drop_zones_repeated_options(), - 'nodropzone', 'adddropzone', self::ADD_NUM_ITEMS, - get_string('addmoredropzones', 'qtype_ddmarker')); - } - - protected function drop_zone($mform, $imagerepeats) { - $dropzoneitem = array(); - - $grouparray = array(); - $grouparray[] = $mform->createElement('static', 'xleftlabel', '', - ' '.get_string('xleft', 'qtype_ddmarker').' '); - $grouparray[] = $mform->createElement('text', 'xleft', - get_string('xleft', 'qtype_ddmarker'), - array('size'=>5, 'class'=>'tweakcss')); - $mform->setType('xleft', PARAM_NOTAGS); - $grouparray[] = $mform->createElement('static', 'ytoplabel', '', - ' '.get_string('ytop', 'qtype_ddmarker').' '); - $grouparray[] = $mform->createElement('text', 'ytop', - get_string('ytop', 'qtype_ddmarker'), - array('size'=>5, 'class'=>'tweakcss')); - $mform->setType('ytop', PARAM_NOTAGS); - $options = array(); - - $options[0] = ''; - for ($i = 1; $i <= $imagerepeats; $i += 1) { - $options[$i] = $i; - } - $grouparray[] = $mform->createElement('static', '', '', ' ' . - get_string('draggableitem', 'qtype_ddmarker').' '); - $grouparray[] = $mform->createElement('select', 'choice', - get_string('draggableitem', 'qtype_ddmarker'), $options); - $grouparray[] = $mform->createElement('static', '', '', ' ' . - get_string('label', 'qtype_ddmarker').' '); - $grouparray[] = $mform->createElement('text', 'droplabel', - get_string('label', 'qtype_ddmarker'), - array('size'=>10, 'class'=>'tweakcss')); - $mform->setType('droplabel', PARAM_NOTAGS); - $dropzone = $mform->createElement('group', 'drops', - get_string('dropzone', 'qtype_ddmarker', '{no}'), $grouparray); - return array($dropzone); - } - - protected function drop_zones_repeated_options() { - $repeatedoptions = array(); - $repeatedoptions['choice']['default'] = '0'; - return $repeatedoptions; - } - - protected function definition_draggable_images($mform, $imagerepeatsatstart) { - - $this->repeat_elements($this->draggable_image($mform), $imagerepeatsatstart, - $this->draggable_images_repeated_options(), - 'noitems', 'additems', self::ADD_NUM_ITEMS, - get_string('addmoreimages', 'qtype_ddmarker')); - } - - protected function draggable_image($mform) { - $draggableimageitem = array(); - - $draggableimageitem[] = $mform->createElement('header', 'draggableitemheader', - get_string('draggableitemheader', 'qtype_ddmarker', '{no}')); - $dragitemtypes = array('image' => get_string('draggableimage', 'qtype_ddmarker'), - 'word' => get_string('draggableword', 'qtype_ddmarker')); - $draggableimageitem[] = $mform->createElement('select', 'dragitemtype', - get_string('draggableitemtype', 'qtype_ddmarker'), - $dragitemtypes, - array('class' => 'dragitemtype')); - $draggableimageitem[] = $mform->createElement('filepicker', 'dragitem', '', null, - self::file_picker_options()); - - $grouparray = array(); - $grouparray[] = $mform->createElement('text', 'draglabel', - get_string('label', 'qtype_ddmarker'), - array('size'=>30, 'class'=>'tweakcss')); - $mform->setType('draglabel', PARAM_NOTAGS); - $options = array(); - for ($i = 1; $i <= self::MAX_GROUPS; $i += 1) { - $options[$i] = $i; - } - $grouparray[] = $mform->createElement('static', '', '', ' ' . - get_string('group', 'qtype_gapselect').' '); - $grouparray[] = $mform->createElement('select', 'draggroup', - get_string('group', 'qtype_gapselect'), - $options, - array('class' => 'draggroup')); - $grouparray[] = $mform->createElement('advcheckbox', 'infinite', ' ', - get_string('infinite', 'qtype_ddmarker')); - $draggableimageitem[] = $mform->createElement('group', 'drags', - get_string('label', 'qtype_ddmarker'), $grouparray); - return $draggableimageitem; - } - - protected function draggable_images_repeated_options() { - $repeatedoptions = array(); - $repeatedoptions['draggroup']['default'] = '1'; - return $repeatedoptions; - } - - public function data_preprocessing($question) { - global $PAGE; - - $question = parent::data_preprocessing($question); - $question = $this->data_preprocessing_combined_feedback($question, true); - $question = $this->data_preprocessing_hints($question, true, true); - - $dragids = array(); // drag no -> dragid - if (!empty($question->options)) { - $question->shuffleanswers = $question->options->shuffleanswers; - $question->drags = array(); - foreach ($question->options->drags as $drag) { - $dragindex = $drag->no -1; - $question->drags[$dragindex] = array(); - $question->drags[$dragindex]['draglabel'] = $drag->label; - $question->drags[$dragindex]['infinite'] = $drag->infinite; - $question->drags[$dragindex]['draggroup'] = $drag->draggroup; - $dragids[$dragindex] = $drag->id; - } - $question->drops = array(); - foreach ($question->options->drops as $drop) { - $question->drops[$drop->no -1] = array(); - $question->drops[$drop->no -1]['choice'] = $drop->choice; - $question->drops[$drop->no -1]['droplabel'] = $drop->label; - $question->drops[$drop->no -1]['xleft'] = $drop->xleft; - $question->drops[$drop->no -1]['ytop'] = $drop->ytop; - } - } - //initialise file picker for bgimage - $draftitemid = file_get_submitted_draft_itemid('bgimage'); - - file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddmarker', - 'bgimage', !empty($question->id) ? (int) $question->id : null, - self::file_picker_options()); - $question->bgimage = $draftitemid; - - //initialise file picker for dragimages - list(, $imagerepeats) = $this->get_drag_image_repeats(); - $draftitemids = optional_param_array('dragitem', array(), PARAM_INT); - for ($imageindex = 0; $imageindex < $imagerepeats; $imageindex++) { - $draftitemid = isset($draftitemids[$imageindex]) ? $draftitemids[$imageindex] :0; - //numbers not allowed in filearea name - $itemid = isset($dragids[$imageindex]) ? $dragids[$imageindex] : null; - file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddmarker', - 'dragimage', $itemid, self::file_picker_options()); - $question->dragitem[$imageindex] = $draftitemid; - } - if (!empty($question->options)) { - foreach ($question->options->drags as $drag) { - $dragindex = $drag->no -1; - if (!isset($question->dragitem[$dragindex])) { - $fileexists = false; - } else { - $fileexists = self::file_uploaded($question->dragitem[$dragindex]); - } - $labelexists = $question->drags[$dragindex]['draglabel']; - if ($labelexists && !$fileexists) { - $question->dragitemtype[$dragindex] = 'word'; - } else { - $question->dragitemtype[$dragindex] = 'image'; - } - } - } - $maxsizes =new stdClass(); - $maxsizes->bgimage = new stdClass(); - $maxsizes->bgimage->width = QTYPE_ddmarker_BGIMAGE_MAXWIDTH; - $maxsizes->bgimage->height = QTYPE_ddmarker_BGIMAGE_MAXHEIGHT; - $maxsizes->dragimage = new stdClass(); - $maxsizes->dragimage->width = QTYPE_ddmarker_DRAGIMAGE_MAXWIDTH; - $maxsizes->dragimage->height = QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT; - - $params = array('maxsizes' => $maxsizes, - 'topnode' => 'fieldset#previewareaheader'); - - $PAGE->requires->yui_module('moodle-qtype_ddmarker-form', - 'M.qtype_ddmarker.init_form', - array($params)); - return $question; - } - - - public static function file_uploaded($draftitemid) { - $draftareafiles = file_get_drafarea_files($draftitemid); - do { - $draftareafile = array_shift($draftareafiles->list); - } while ($draftareafile !== null && $draftareafile->filename == '.'); - if ($draftareafile === null) { - return false; - } - return true; - } - - public function validation($data, $files) { - $errors = parent::validation($data, $files); - if (!self::file_uploaded($data['bgimage'])) { - $errors["bgimage"] = get_string('formerror_nobgimage', 'qtype_ddmarker'); - } - - $allchoices = array(); - for ($i=0; $i < $data['nodropzone']; $i++) { - $ytoppresent = (trim($data['drops'][$i]['ytop']) !== ''); - $xleftpresent = (trim($data['drops'][$i]['ytop']) !== ''); - $labelpresent = (trim($data['drops'][$i]['droplabel']) !== ''); - $choice = $data['drops'][$i]['choice']; - $imagechoicepresent = ($choice !== '0'); - - if ($imagechoicepresent) { - if (!$ytoppresent) { - $errors["drops[$i]"] = - get_string('formerror_noytop', 'qtype_ddmarker'); - } - if (!$xleftpresent) { - $errors["drops[$i]"] = - get_string('formerror_noxleft', 'qtype_ddmarker'); - } - - if ($data['dragitemtype'][$choice - 1] != 'word' && - !self::file_uploaded($data['dragitem'][$choice - 1])) { - $errors['dragitem['.($choice - 1).']'] = - get_string('formerror_nofile', 'qtype_ddmarker', $i); - } - - if (isset($allchoices[$choice]) && !$data['drags'][$choice-1]['infinite']) { - $errors["drops[$i]"] = - get_string('formerror_multipledraginstance', 'qtype_ddmarker', $choice); - $errors['drops['.($allchoices[$choice]).']'] = - get_string('formerror_multipledraginstance', 'qtype_ddmarker', $choice); - $errors['drags['.($choice-1).']'] = - get_string('formerror_multipledraginstance2', 'qtype_ddmarker', $choice); - } - $allchoices[$choice] = $i; - } else { - if ($ytoppresent || $xleftpresent || $labelpresent) { - $errors["drops[$i]"] = - get_string('formerror_noimageselected', 'qtype_ddmarker'); - } - } - } - for ($dragindex=0; $dragindex < $data['noitems']; $dragindex++) { - $label = $data['drags'][$dragindex]['draglabel']; - if ($data['dragitemtype'][$dragindex] == 'word') { - $allowedtags = '
'; - $errormessage = get_string('formerror_disallowedtags', 'qtype_ddmarker'); - } else { - $allowedtags = ''; - $errormessage = get_string('formerror_noallowedtags', 'qtype_ddmarker'); - } - if ($label != strip_tags($label, $allowedtags)) { - $errors["drags[{$dragindex}]"] = $errormessage; - } - - } - return $errors; - } - +class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { } diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 6695efdaac6..666ff349590 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -28,7 +28,7 @@ $string['addmoreimages'] = 'Blanks for {no} More Draggable Items'; $string['answer'] = 'Answer'; $string['bgimage'] = 'Background Image'; $string['correctansweris'] = 'The correct answer is: {$a}'; -$string['ddmarker'] = 'Drag and drop: images or text onto image'; +$string['ddmarker'] = 'Drag and drop: markers onto image'; $string['ddmarker_help'] = 'Select a background image file, select draggable images or enter text and define the drop zones on the background image to which they must be dragged.'; $string['ddmarkersummary'] = 'Images or text labels are dragged and dropped into drop zones on a background image.'; $string['draggableimage'] = 'Draggable image'; @@ -58,9 +58,7 @@ $string['previewareaheader'] = 'Preview'; $string['previewareamessage'] = 'Select a background image file and select draggable images or just enter text that will be made draggable. Then choose a drag item for each \'drop zone\', and drag the drag item to where the student should drag it to.'; $string['refresh'] = 'Refresh Preview'; $string['shuffleimages'] = 'Shuffle Drag Items Each Time Question Is Attempted'; -$string['summarisechoice'] = '{$a->no}. {$a->text}'; $string['summariseplace'] = '{$a->no}. {$a->text}'; -$string['summarisechoiceno'] = 'Item {$a}'; $string['summariseplaceno'] = 'Drop zone {$a}'; $string['xleft'] = 'Left'; $string['ytop'] = 'Top'; \ No newline at end of file diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 54ca57b1af1..018815c5ece 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); -require_once($CFG->dirroot . '/question/type/gapselect/questionbase.php'); +require_once($CFG->dirroot . '/question/type/ddimageortext/questionbase.php'); /** @@ -35,133 +35,13 @@ require_once($CFG->dirroot . '/question/type/gapselect/questionbase.php'); * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class qtype_ddmarker_question extends qtype_gapselect_question_base { - public function clear_wrong_from_response(array $response) { - foreach ($this->places as $place => $notused) { - if (array_key_exists($this->field($place), $response) && - $response[$this->field($place)] != $this->get_right_choice_for($place)) { - $response[$this->field($place)] = ''; - } - } - return $response; - } +class qtype_ddmarker_question extends qtype_ddtoimage_question_base { - public function get_right_choice_for($placeno) { - $place = $this->places[$placeno]; - foreach ($this->choiceorder[$place->group] as $choicekey => $choiceid) { - if ($this->rightchoices[$placeno] == $choiceid) { - return $choicekey; - } - } - } - public function summarise_response(array $response) { - $allblank = true; - foreach ($this->places as $placeno => $place) { - $summariseplace = $place->summarise(); - if (array_key_exists($this->field($placeno), $response) && - $response[$this->field($placeno)]) { - $selected = $this->get_selected_choice($place->group, - $response[$this->field($placeno)]); - $summarisechoice = $selected->summarise(); - $allblank = false; - } else { - $summarisechoice = ''; - } - $choices[] = "$summariseplace -> {{$summarisechoice}}"; - } - if ($allblank) { - return null; - } - return implode(' ', $choices); - } - - public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { - if ($filearea == 'bgimage' || $filearea == 'dragimage') { - $validfilearea = true; - } else { - $validfilearea = false; - } - if ($component == 'qtype_ddmarker' && $validfilearea) { - $question = $qa->get_question(); - $itemid = reset($args); - if ($filearea == 'bgimage') { - return $itemid == $question->id; - } else if ($filearea == 'dragimage') { - foreach ($question->choices as $group) { - foreach ($group as $drag) { - if ($drag->id == $itemid) { - return true; - } - } - } - return false; - } - } else { - return parent::check_file_access($qa, $options, $component, - $filearea, $args, $forcedownload); - } - } - public function get_validation_error(array $response) { - if ($this->is_complete_response($response)) { - return ''; - } - return get_string('pleasedraganimagetoeachdropregion', 'qtype_ddmarker'); - } - - public function classify_response(array $response) { - $parts = array(); - foreach ($this->places as $placeno => $place) { - $group = $place->group; - if (!array_key_exists($this->field($placeno), $response) || - !$response[$this->field($placeno)]) { - $parts[$placeno] = question_classified_response::no_response(); - continue; - } - - $fieldname = $this->field($placeno); - $choiceno = $this->choiceorder[$group][$response[$fieldname]]; - $choice = $this->choices[$group][$choiceno]; - - $correct = $this->get_right_choice_for($placeno) == $response[$fieldname]; - $parts[$placeno] = new question_classified_response( - $choiceno, $choice->summarise(), $correct?1:0); - } - return $parts; - } - - public function get_random_guess_score() { - $accum = 0; - - foreach ($this->places as $place) { - $accum += 1 / count($this->choices[$place->group]); - } - - return $accum / count($this->places); - } - - - public function get_question_summary() { - $summary = ''; - if (!html_is_blank($this->questiontext)) { - $question = $this->html_to_text($this->questiontext, $this->questiontextformat); - $summary .= $question . '; '; - } - $places = array(); - foreach ($this->places as $place) { - $cs = array(); - foreach ($this->choices[$place->group] as $choice) { - $cs[] = $choice->summarise(); - } - $places[] = '[[' . $place->summarise() . ']] -> {' . implode(' / ', $cs) . '}'; - } - $summary .= implode('; ', $places); - return $summary; - } } /** - * Represents one of the choices (draggable images). + * Represents one of the choices (draggable markers). * * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -170,26 +50,18 @@ class qtype_ddmarker_drag_item { public $id; public $text; public $no; - public $group; - public $isinfinite; - public function __construct($alttextlabel, $no, $group = 1, $isinfinite = false, $id = 0) { + public function __construct($label, $no, $id = 0) { $this->id = $id; - $this->text = $alttextlabel; + $this->text = $label; $this->no = $no; - $this->group = $group; - $this->isinfinite = $isinfinite; } public function choice_group() { - return $this->group; + return 1; } public function summarise() { - if (trim($this->text) != '') { - return get_string('summarisechoice', 'qtype_ddmarker', $this); - } else { - return get_string('summarisechoiceno', 'qtype_ddmarker', $this->no); - } + return $this->text; } } /** @@ -201,14 +73,14 @@ class qtype_ddmarker_drag_item { class qtype_ddmarker_drop_zone { public $no; public $text; - public $group; - public $xy; + public $shape; + public $coords; - public function __construct($alttextlabel, $no, $group = 1, $x = '', $y = '') { + public function __construct($label, $no, $shape, $coords) { $this->no = $no; - $this->text = $alttextlabel; - $this->group = $group; - $this->xy = array($x, $y); + $this->text = $label; + $this->shape = $shape; + $this->coords = $coords; } public function summarise() { diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 106ca0f1b27..034ee131bd8 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -26,15 +26,10 @@ defined('MOODLE_INTERNAL') || die(); -require_once($CFG->libdir . '/questionlib.php'); -require_once($CFG->dirroot . '/question/engine/lib.php'); -require_once($CFG->dirroot . '/question/format/xml/format.php'); -require_once($CFG->dirroot . '/question/type/gapselect/questiontypebase.php'); +require_once($CFG->dirroot . '/question/type/ddimageortext/questiontypebase.php'); define('QTYPE_ddmarker_BGIMAGE_MAXWIDTH', 600); define('QTYPE_ddmarker_BGIMAGE_MAXHEIGHT', 400); -define('QTYPE_ddmarker_DRAGIMAGE_MAXWIDTH', 150); -define('QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT', 100); /** * The drag-and-drop words into sentences question type class. @@ -42,423 +37,10 @@ define('QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT', 100); * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class qtype_ddmarker extends question_type { - protected function choice_group_key() { - return 'draggroup'; - } +class qtype_ddmarker extends qtype_ddtoimage_base { public function requires_qtypes() { - return array('gapselect'); - } - - public function get_question_options($question) { - global $DB; - $question->options = $DB->get_record('qtype_ddmarker', - array('questionid' => $question->id), '*', MUST_EXIST); - $question->options->drags = $DB->get_records('qtype_ddmarker_drags', - array('questionid' => $question->id), 'no ASC', '*'); - $question->options->drops = $DB->get_records('qtype_ddmarker_drops', - array('questionid' => $question->id), 'no ASC', '*'); - parent::get_question_options($question); - } - - protected function make_choice($dragdata) { - return new qtype_ddmarker_drag_item($dragdata->label, $dragdata->no, - $dragdata->draggroup, $dragdata->infinite, $dragdata->id); - } - - protected function make_place($dropzonedata) { - return new qtype_ddmarker_drop_zone($dropzonedata->label, $dropzonedata->no, - $dropzonedata->group, - $dropzonedata->xleft, $dropzonedata->ytop); - } - - protected function make_hint($hint) { - return question_hint_with_parts::load_from_record($hint); - } - - protected function initialise_question_instance(question_definition $question, $questiondata) { - parent::initialise_question_instance($question, $questiondata); - $question->shufflechoices = $questiondata->options->shuffleanswers; - - $this->initialise_combined_feedback($question, $questiondata, true); - - $question->choices = array(); - $choiceindexmap= array(); - - // Store the choices in arrays by group. - foreach ($questiondata->options->drags as $dragdata) { - - $choice = $this->make_choice($dragdata); - - if (array_key_exists($choice->choice_group(), $question->choices)) { - $question->choices[$choice->choice_group()][$dragdata->no] = $choice; - } else { - $question->choices[$choice->choice_group()][1] = $choice; - } - - end($question->choices[$choice->choice_group()]); - $choiceindexmap[$dragdata->no] = array($choice->choice_group(), - key($question->choices[$choice->choice_group()])); - } - - $question->places = array(); - $question->rightchoices = array(); - - $i = 1; - - foreach ($questiondata->options->drops as $dropdata) { - list($group, $choiceindex) = $choiceindexmap[$dropdata->choice]; - $dropdata->group = $group; - $question->places[$dropdata->no] = $this->make_place($dropdata); - $question->rightchoices[$dropdata->no] = $choiceindex; - } - } - - public function save_question_options($formdata) { - global $DB, $USER; - $context = $formdata->context; - - $options = $DB->get_record('qtype_ddmarker', array('questionid' => $formdata->id)); - if (!$options) { - $options = new stdClass(); - $options->questionid = $formdata->id; - $options->correctfeedback = ''; - $options->partiallycorrectfeedback = ''; - $options->incorrectfeedback = ''; - $options->id = $DB->insert_record('qtype_ddmarker', $options); - } - - $options->shuffleanswers = !empty($formdata->shuffleanswers); - $options = $this->save_combined_feedback_helper($options, $formdata, $context, true); - $this->save_hints($formdata, true); - $DB->update_record('qtype_ddmarker', $options); - $DB->delete_records('qtype_ddmarker_drops', array('questionid' => $formdata->id)); - foreach (array_keys($formdata->drops) as $dropno) { - if ($formdata->drops[$dropno]['choice'] == 0) { - continue; - } - $drop = new stdClass(); - $drop->questionid = $formdata->id; - $drop->no = $dropno + 1; - $drop->xleft = $formdata->drops[$dropno]['xleft']; - $drop->ytop = $formdata->drops[$dropno]['ytop']; - $drop->choice = $formdata->drops[$dropno]['choice']; - $drop->label = $formdata->drops[$dropno]['droplabel']; - - $DB->insert_record('qtype_ddmarker_drops', $drop); - } - - //an array of drag no -> drag id - $olddragids = $DB->get_records_menu('qtype_ddmarker_drags', - array('questionid' => $formdata->id), - '', 'no, id'); - foreach (array_keys($formdata->drags) as $dragno) { - $info = file_get_draft_area_info($formdata->dragitem[$dragno]); - if ($info['filecount'] > 0 || !empty($formdata->drags[$dragno]['draglabel'])) { - $draftitemid = $formdata->dragitem[$dragno]; - - $drag = new stdClass(); - $drag->questionid = $formdata->id; - $drag->no = $dragno + 1; - $drag->draggroup = $formdata->drags[$dragno]['draggroup']; - $drag->infinite = empty($formdata->drags[$dragno]['infinite'])? 0 : 1; - $drag->label = $formdata->drags[$dragno]['draglabel']; - - if (isset($olddragids[$dragno +1])) { - $drag->id = $olddragids[$dragno +1]; - unset($olddragids[$dragno +1]); - $DB->update_record('qtype_ddmarker_drags', $drag); - } else { - $drag->id = $DB->insert_record('qtype_ddmarker_drags', $drag); - } - - if ($formdata->dragitemtype[$dragno] == 'image') { - self::constrain_image_size_in_draft_area($draftitemid, - QTYPE_ddmarker_DRAGIMAGE_MAXWIDTH, - QTYPE_ddmarker_DRAGIMAGE_MAXHEIGHT); - file_save_draft_area_files($draftitemid, $formdata->context->id, - 'qtype_ddmarker', 'dragimage', $drag->id, - array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 1)); - } else { - //delete any existing files for draggable text item type - $fs = get_file_storage(); - $fs->delete_area_files($formdata->context->id, 'qtype_ddmarker', - 'dragimage', $drag->id); - } - - } - - } - if (!empty($olddragids)) { - list($sql, $params) = $DB->get_in_or_equal(array_values($olddragids)); - $DB->delete_records_select('qtype_ddmarker_drags', "id $sql", $params); - } - - self::constrain_image_size_in_draft_area($formdata->bgimage, - QTYPE_ddmarker_BGIMAGE_MAXWIDTH, - QTYPE_ddmarker_BGIMAGE_MAXHEIGHT); - file_save_draft_area_files($formdata->bgimage, $formdata->context->id, - 'qtype_ddmarker', 'bgimage', $formdata->id, - array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 1)); - } - - - public static function constrain_image_size_in_draft_area($draftitemid, $maxwidth, $maxheight) { - global $USER; - $usercontext = get_context_instance(CONTEXT_USER, $USER->id); - $fs = get_file_storage(); - $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id'); - if ($draftfiles) { - foreach ($draftfiles as $file) { - if ($file->is_directory()) { - continue; - } - $imageinfo = $file->get_imageinfo(); - $width = $imageinfo['width']; - $height = $imageinfo['height']; - $mimetype = $imageinfo['mimetype']; - switch ($mimetype) { - case 'image/jpeg' : - $quality = 80; - break; - case 'image/png' : - $quality = 8; - break; - default : - $quality = null; - } - $newwidth = min($maxwidth, $width); - $newheight = min($maxheight, $height); - if ($newwidth != $width || $newheight != $height) { - $newimagefilename = $file->get_filename(); - $newimagefilename = - preg_replace('!\.!', "_{$newwidth}x{$newheight}.", $newimagefilename, 1); - $newrecord = new stdClass(); - $newrecord->contextid = $usercontext->id; - $newrecord->component = 'user'; - $newrecord->filearea = 'draft'; - $newrecord->itemid = $draftitemid; - $newrecord->filepath = '/'; - $newrecord->filename = $newimagefilename; - $fs->convert_image($newrecord, $file, $newwidth, $newheight, true, $quality); - $file->delete(); - } - } - } - } - public function move_files($questionid, $oldcontextid, $newcontextid) { - global $DB; - $fs = get_file_storage(); - - parent::move_files($questionid, $oldcontextid, $newcontextid); - $fs->move_area_files_to_new_context($oldcontextid, - $newcontextid, 'qtype_ddmarker', 'bgimage', $questionid); - $dragids = $DB->get_records_menu('qtype_ddmarker_drags', - array('questionid' => $questionid), 'id', 'id,1'); - foreach ($dragids as $dragid => $notused) { - $fs->move_area_files_to_new_context($oldcontextid, - $newcontextid, 'qtype_ddmarker', 'dragimage', $dragid); - } - - $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); - } - - /** - * Delete all the files belonging to this question. - * @param int $questionid the question being deleted. - * @param int $contextid the context the question is in. - */ - - protected function delete_files($questionid, $contextid) { - global $DB; - $fs = get_file_storage(); - - parent::delete_files($questionid, $contextid); - - $dragids = $DB->get_records_menu('qtype_ddmarker_drags', - array('questionid' => $questionid), 'id', 'id,1'); - foreach ($dragids as $dragid => $notused) { - $fs->delete_area_files($contextid, 'qtype_ddmarker', 'dragimage', $dragid); - } - - $this->delete_files_in_combined_feedback($questionid, $contextid); - } - - public function export_to_xml($question, $format, $extra = null) { - $fs = get_file_storage(); - $contextid = $question->contextid; - $output = ''; - - if ($question->options->shuffleanswers) { - $output .= " \n"; - } - $output .= $format->write_combined_feedback($question->options); - $output .= $format->write_hints($question); - $files = $fs->get_area_files($contextid, 'qtype_ddmarker', 'bgimage', $question->id); - $output .= " ".$this->write_files($files, 2)."\n";; - - foreach ($question->options->drags as $drag) { - $files = - $fs->get_area_files($contextid, 'qtype_ddmarker', 'dragimage', $drag->id); - $output .= " \n"; - $output .= " {$drag->no}\n"; - $output .= $format->writetext($drag->label, 3)."\n"; - $output .= " {$drag->draggroup}\n"; - if ($drag->infinite) { - $output .= " \n"; - } - $output .= $this->write_files($files, 3); - $output .= " \n"; - } - foreach ($question->options->drops as $drop) { - $output .= " \n"; - $output .= $format->writetext($drop->label, 3); - $output .= " {$drop->no}\n"; - $output .= " {$drop->choice}\n"; - $output .= " {$drop->xleft}\n"; - $output .= " {$drop->ytop}\n"; - $output .= " \n"; - } - - return $output; - } - - public function import_from_xml($data, $question, $format, $extra=null) { - if (!isset($data['@']['type']) || $data['@']['type'] != 'ddmarker') { - return false; - } - - $question = $format->import_headers($data); - $question->qtype = 'ddmarker'; - - $question->shuffleanswers = array_key_exists('shuffleanswers', - $format->getpath($data, array('#'), array())); - - $filexml = $format->getpath($data, array('#', 'file'), array()); - $question->bgimage = $this->import_files_to_draft_file_area($format, $filexml); - $drags = $data['#']['drag']; - $question->drags = array(); - - foreach ($drags as $dragxml) { - $dragno = $format->getpath($dragxml, array('#', 'no', 0, '#'), 0); - $dragindex = $dragno -1; - $question->drags[$dragindex] = array(); - $question->drags[$dragindex]['draglabel'] = - $format->getpath($dragxml, array('#', 'text', 0, '#'), '', true); - $question->drags[$dragindex]['infinite'] = array_key_exists('infinite', $dragxml['#']); - $question->drags[$dragindex]['draggroup'] = - $format->getpath($dragxml, array('#', 'draggroup', 0, '#'), 1); - $filexml = $format->getpath($dragxml, array('#', 'file'), array()); - $question->dragitem[$dragindex] = - $this->import_files_to_draft_file_area($format, $filexml); - if (count($filexml)) { - $question->dragitemtype[$dragindex] = 'image'; - } else { - $question->dragitemtype[$dragindex] = 'word'; - } - } - - $drops = $data['#']['drop']; - $question->drops = array(); - foreach ($drops as $dropxml) { - $dropno = $format->getpath($dropxml, array('#', 'no', 0, '#'), 0); - $dropindex = $dropno -1; - $question->drops[$dropindex] = array(); - $question->drops[$dropindex]['choice'] = - $format->getpath($dropxml, array('#', 'choice', 0, '#'), 0); - $question->drops[$dropindex]['droplabel'] = - $format->getpath($dropxml, array('#', 'text', 0, '#'), '', true); - $question->drops[$dropindex]['xleft'] = - $format->getpath($dropxml, array('#', 'xleft', 0, '#'), ''); - $question->drops[$dropindex]['ytop'] = - $format->getpath($dropxml, array('#', 'ytop', 0, '#'), ''); - } - - $format->import_combined_feedback($question, $data, true); - $format->import_hints($question, $data, true); - - return $question; - } - - - /** - * Create a draft files area, import files into it and return the draft item id. - * @param qformat_xml $format - * @param array $xml an array of nodes from the the parsed XML. - * @return integer draftitemid - */ - public function import_files_to_draft_file_area($format, $xml) { - global $USER; - $fs = get_file_storage(); - $files = $format->import_files($xml); - $usercontext = get_context_instance(CONTEXT_USER, $USER->id); - $draftitemid = file_get_unused_draft_itemid(); - foreach ($files as $file) { - $record = new stdClass(); - $record->contextid = $usercontext->id; - $record->component = 'user'; - $record->filearea = 'draft'; - $record->itemid = $draftitemid; - $record->filename = $file->name; - $record->filepath = '/'; - $fs->create_file_from_string($record, $this->decode_file($file)); - } - return $draftitemid; - } - - /** - * Convert files into text output in the given format. - * This method is copied from qformat_default as a quick fix, as the method there is - * protected. - * @param array - * @param string encoding method - * @return string $string - */ - public function write_files($files, $indent) { - if (empty($files)) { - return ''; - } - $string = ''; - foreach ($files as $file) { - if ($file->is_directory()) { - continue; - } - $string .= str_repeat(' ', $indent); - $string .= ''; - $string .= base64_encode($file->get_content()); - $string .= "\n"; - } - return $string; - } - - public function get_possible_responses($questiondata) { - $question = $this->make_question($questiondata); - - $parts = array(); - foreach ($question->places as $placeno => $place) { - $group = $place->group; - $choices = array(); - - foreach ($question->choices[$group] as $i => $choice) { - $summarisechoice = $choice->summarise(); - - $correct = $question->rightchoices[$placeno] == $i; - $choices[$choice->no] = new question_possible_response( - $summarisechoice, - $correct?1:0); - } - $choices[null] = question_possible_response::no_response(); - - $parts[$placeno] = $choices; - } - - return $parts; - } - - public function get_random_guess_score($questiondata) { - $question = $this->make_question($questiondata); - return $question->get_random_guess_score(); + return array_merge(parent::requires_qtypes(), array('ddimageortext')); } } diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index da83b871c60..f7cf56007f2 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die(); -require_once($CFG->dirroot . '/question/type/gapselect/rendererbase.php'); +require_once($CFG->dirroot . '/question/type/ddimageortext/rendererbase.php'); /** @@ -35,146 +35,6 @@ require_once($CFG->dirroot . '/question/type/gapselect/rendererbase.php'); * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class qtype_ddmarker_renderer extends qtype_with_combined_feedback_renderer { +class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { - public function head_code(question_attempt $qa) { - return parent::head_code($qa); - } - public function clear_wrong(question_attempt $qa) { - $question = $qa->get_question(); - $response = $qa->get_last_qt_data(); - - if (!empty($response)) { - $cleanresponse = $question->clear_wrong_from_response($response); - } else { - $cleanresponse = $response; - } - $cleanresponsehtml = ''; - foreach ($cleanresponse as $fieldname => $value) { - list (, $html) = $this->hidden_field_for_qt_var($qa, $fieldname, $value); - $cleanresponsehtml .= $html; - } - return $cleanresponsehtml; - } - - public function formulation_and_controls(question_attempt $qa, - question_display_options $options) { - global $PAGE; - - $question = $qa->get_question(); - $response = $qa->get_last_qt_data(); - - $questiontext = $question->format_questiontext($qa); - - $output = html_writer::tag('div', $questiontext, array('class' => 'qtext')); - - $bgimage = self::get_url_for_image($qa, 'bgimage'); - - $img = html_writer::empty_tag('img', array('src'=>$bgimage, 'class'=>'dropbackground')); - $droparea = html_writer::tag('div', $img, array('class'=>'droparea')); - - $dragimagehomes = ''; - foreach ($question->choices as $groupno => $group) { - $dragimagehomesgroup = ''; - $orderedgroup = $question->get_ordered_choices($groupno); - foreach ($orderedgroup as $choiceno => $dragimage) { - $dragimageurl = self::get_url_for_image($qa, 'dragimage', $dragimage->id); - $classes = array("group{$groupno}", - 'draghome', - "dragitemhomes{$dragimage->no}", - "choice{$choiceno}"); - if ($dragimage->isinfinite) { - $classes[] = 'infinite'; - } - if ($dragimageurl === null) { - $classes[] = 'yui3-cssfonts'; - $dragimagehomesgroup .= html_writer::tag('div', - $dragimage->text, - array('src'=>$dragimageurl, - 'class'=>join(' ', $classes))); - } else { - $dragimagehomesgroup .= html_writer::empty_tag('img', - array('src'=>$dragimageurl, - 'alt' => $dragimage->text, - 'class'=>join(' ', $classes))); - } - } - $dragimagehomes .= html_writer::tag('div', $dragimagehomesgroup, - array('class'=>'dragitemgroup'.$groupno)); - } - - $dragitemsclass = 'dragitems'; - if ($options->readonly) { - $dragitemsclass .= ' readonly'; - } - $dragitems = html_writer::tag('div', $dragimagehomes, array('class'=> $dragitemsclass)); - $dropzones = html_writer::empty_tag('div', array('class'=>'dropzones')); - $output .= html_writer::tag('div', $droparea.$dragitems.$dropzones, - array('class'=>'ddarea')); - foreach ($question->places as $placeno => $place) { - $varname = $question->field($placeno); - list($fieldname, $html) = $this->hidden_field_for_qt_var($qa, $varname); - $output .= $html; - $question->places[$placeno]->fieldname = $fieldname; - } - $topnode = 'div#q'.$qa->get_slot().' div.ddarea'; - $params = array('drops' => $question->places, - 'topnode' => $topnode, - 'readonly' => $options->readonly); - - $PAGE->requires->yui_module('moodle-qtype_ddmarker-dd', - 'M.qtype_ddmarker.init_question', - array($params)); - - if ($qa->get_state() == question_state::$invalid) { - $output .= html_writer::nonempty_tag('div', - $question->get_validation_error($qa->get_last_qt_data()), - array('class' => 'validationerror')); - } - return $output; - } - - protected static function get_url_for_image(question_attempt $qa, $filearea, $itemid = 0) { - $question = $qa->get_question(); - $qubaid = $qa->get_usage_id(); - $slot = $qa->get_slot(); - $fs = get_file_storage(); - if ($filearea == 'bgimage') { - $itemid = $question->id; - } - $draftfiles = $fs->get_area_files($question->contextid, 'qtype_ddmarker', - $filearea, $itemid, 'id'); - if ($draftfiles) { - foreach ($draftfiles as $file) { - if ($file->is_directory()) { - continue; - } - $url = moodle_url::make_pluginfile_url($question->contextid, 'qtype_ddmarker', - $filearea, "$qubaid/$slot/{$itemid}", '/', - $file->get_filename()); - return $url->out(); - } - } - return null; - } - - protected function hidden_field_for_qt_var(question_attempt $qa, $varname, $value = null) { - if ($value === null) { - $value = $qa->get_last_qt_var($varname); - } - $fieldname = $qa->get_qt_field_name($varname); - $attributes = array('type' => 'hidden', - 'id' => str_replace(':', '_', $fieldname), - 'name' => $fieldname, - 'value' => $value); - return array($fieldname, html_writer::empty_tag('input', $attributes)."\n"); - } - - public function specific_feedback(question_attempt $qa) { - return $this->combined_feedback($qa); - } - - public function correct_response(question_attempt $qa) { - return ''; - } } From eb4b065adeffa39218a2dd8bccb1c3f63408fa89 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 27 Sep 2011 17:40:54 +0700 Subject: [PATCH 005/178] MDL-47494 ddmarker: NOBUG fixing case of ddmarker where it should be upper case. --- question/type/ddmarker/yui/dd/dd.js | 24 ++++++++++++------------ question/type/ddmarker/yui/form/form.js | 12 ++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 6e37bb1832b..5cdfb28c603 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -1,12 +1,12 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { - var ddmarkerDDNAME = 'ddmarker_dd'; - var ddmarker_DD = function() { - ddmarker_DD.superclass.constructor.apply(this, arguments); + var DDMARKERDDNAME = 'ddmarker_dd'; + var DDMARKER_DD = function() { + DDMARKER_DD.superclass.constructor.apply(this, arguments); } /** * This is the base class for the question rendering and question editing form code. */ - Y.extend(ddmarker_DD, Y.Base, { + Y.extend(DDMARKER_DD, Y.Base, { doc : null, polltimer : null, afterimageloaddone : false, @@ -215,7 +215,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { +bgimgxy[1] + this.doc.bg_img().getY() + 1]; } }, { - NAME : ddmarkerDDNAME, + NAME : DDMARKERDDNAME, ATTRS : { drops : {value : null}, readonly : {value : false}, @@ -223,16 +223,16 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }); M.qtype_ddmarker = M.qtype_ddmarker || {}; - M.qtype_ddmarker.dd_base_class = ddmarker_DD; + M.qtype_ddmarker.dd_base_class = DDMARKER_DD; - var ddmarkerQUESTIONNAME = 'ddmarker_question'; - var ddmarker_QUESTION = function() { - ddmarker_QUESTION.superclass.constructor.apply(this, arguments); + var DDMARKERQUESTIONNAME = 'ddmarker_question'; + var DDMARKER_QUESTION = function() { + DDMARKER_QUESTION.superclass.constructor.apply(this, arguments); }; /** * This is the code for question rendering. */ - Y.extend(ddmarker_QUESTION, M.qtype_ddmarker.dd_base_class, { + Y.extend(DDMARKER_QUESTION, M.qtype_ddmarker.dd_base_class, { initializer : function(params) { this.doc = this.doc_structure(this); this.poll_for_image_load(null, false, 0, this.create_all_drag_and_drops); @@ -430,7 +430,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, this); }; } - }, {NAME : ddmarkerQUESTIONNAME, ATTRS : {}}); + }, {NAME : DDMARKERQUESTIONNAME, ATTRS : {}}); Y.Event.define('dragchange', { // Webkit and IE repeat keydown when you hold down arrow keys. @@ -461,7 +461,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }); M.qtype_ddmarker.init_question = function(config) { - return new ddmarker_QUESTION(config); + return new DDMARKER_QUESTION(config); } }, '@VERSION@', { requires:['node', 'dd', 'dd-drop', 'dd-constrain'] diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 387c4cc67c0..de0c1a48e53 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -2,11 +2,11 @@ * This is the question editing form code. */ YUI.add('moodle-qtype_ddmarker-form', function(Y) { - var ddmarkerFORMNAME = 'ddmarker_form'; - var ddmarker_FORM = function() { - ddmarker_FORM.superclass.constructor.apply(this, arguments); + var DDMARKERFORMNAME = 'ddmarker_form'; + var DDMARKER_FORM = function() { + DDMARKER_FORM.superclass.constructor.apply(this, arguments); }; - Y.extend(ddmarker_FORM, M.qtype_ddmarker.dd_base_class, { + Y.extend(DDMARKER_FORM, M.qtype_ddmarker.dd_base_class, { fp : null, initializer : function(params) { @@ -331,10 +331,10 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } return toreturn; } - }, {NAME : ddmarkerFORMNAME, ATTRS : {maxsizes:{value:null}}}); + }, {NAME : DDMARKERFORMNAME, ATTRS : {maxsizes:{value:null}}}); M.qtype_ddmarker = M.qtype_ddmarker || {}; M.qtype_ddmarker.init_form = function(config) { - return new ddmarker_FORM(config); + return new DDMARKER_FORM(config); } }, '@VERSION@', { requires:['moodle-qtype_ddmarker-dd', 'form_filepicker'] From d2ac6cea2a7d6d21edeb062dcd6c17193b54b8b9 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 28 Sep 2011 19:31:48 +0700 Subject: [PATCH 006/178] MDL-47494 ddmarker: NOBUG question editing form is now working correctly. --- question/type/ddmarker/db/install.xml | 5 +- question/type/ddmarker/edit_ddmarker_form.php | 208 ++++++++++++++++++ .../type/ddmarker/lang/en/qtype_ddmarker.php | 46 ++-- question/type/ddmarker/questiontype.php | 193 +++++++++++++++- 4 files changed, 428 insertions(+), 24 deletions(-) diff --git a/question/type/ddmarker/db/install.xml b/question/type/ddmarker/db/install.xml index 22d6c462261..a251d0c436f 100644 --- a/question/type/ddmarker/db/install.xml +++ b/question/type/ddmarker/db/install.xml @@ -1,5 +1,5 @@ - @@ -43,7 +43,8 @@ - + + diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index dcb0bb32128..e8b841edbd7 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -36,4 +36,212 @@ defined('MOODLE_INTERNAL') || die(); class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { + public function qtype() { + return 'ddmarker'; + } + + protected function definition_inner($mform) { + $mform->addElement('advcheckbox', 'showmisplaced', ' ', + get_string('showmisplaced', 'qtype_ddmarker')); + parent::definition_inner($mform); + } + + public function js_call() { + global $PAGE; + $maxsizes =new stdClass(); + $maxsizes->bgimage = new stdClass(); + $maxsizes->bgimage->width = QTYPE_DDMARKER_BGIMAGE_MAXWIDTH; + $maxsizes->bgimage->height = QTYPE_DDMARKER_BGIMAGE_MAXHEIGHT; + + $params = array('maxsizes' => $maxsizes, + 'topnode' => 'fieldset#previewareaheader'); + + $PAGE->requires->yui_module('moodle-qtype_ddmarker-form', + 'M.qtype_ddmarker.init_form', + array($params)); + } + + protected function definition_draggable_items($mform, $itemrepeatsatstart) { + + $mform->addElement('header', 'draggableitemheader', + get_string('markers', 'qtype_ddmarker')); + $this->repeat_elements($this->draggable_item($mform), $itemrepeatsatstart, + $this->draggable_items_repeated_options(), + 'noitems', 'additems', self::ADD_NUM_ITEMS, + get_string('addmoreitems', 'qtype_ddmarker')); + } + + protected function draggable_item($mform) { + $draggableimageitem = array(); + + $grouparray= array(); + $grouparray[] = $mform->createElement('text', 'label', + get_string('marker_n', 'qtype_ddmarker'), + array('size'=>30, 'class'=>'tweakcss')); + $mform->setType('text', PARAM_RAW_TRIMMED); + + $grouparray[] = $mform->createElement('advcheckbox', 'infinite', ' ', + get_string('infinite', 'qtype_ddmarker')); + $draggableimageitem[] = $mform->createElement('group', 'drags', + get_string('marker_n', 'qtype_ddmarker'), $grouparray); + return $draggableimageitem; + } + + protected function draggable_items_repeated_options() { + return array(); + } + + + + + protected function drop_zone($mform, $imagerepeats) { + $dropzoneitem = array(); + + $grouparray = array(); + foreach (array('circle', 'rectangle', 'polygon') as $shape) { + $shapearray[$shape] = get_string('shape_'.$shape, 'qtype_ddmarker'); + } + $grouparray[] = $mform->createElement('select', 'shape', + get_string('marker', 'qtype_ddmarker'), $shapearray); + $grouparray[] = $mform->createElement('text', 'coords', + get_string('coords', 'qtype_ddmarker'), + array('size'=>20, 'class'=>'tweakcss')); + $mform->setType('coords', PARAM_NOTAGS); + $markernos = array(); + $markernos[0] = ''; + for ($i = 1; $i <= $imagerepeats; $i += 1) { + $markernos[$i] = $i; + } + $grouparray[] = $mform->createElement('static', '', '', ' ' . + get_string('marker', 'qtype_ddmarker').' '); + $grouparray[] = $mform->createElement('select', 'choice', + get_string('marker', 'qtype_ddmarker'), $markernos); + $grouparray[] = $mform->createElement('static', '', '', ' ' . + get_string('alttext', 'qtype_ddmarker').' '); + $grouparray[] = $mform->createElement('text', 'label', + get_string('alttext', 'qtype_ddmarker'), + array('size'=>10, 'class'=>'tweakcss')); + $dropzone = $mform->createElement('group', 'drops', + get_string('dropzone', 'qtype_ddmarker', '{no}'), $grouparray); + return array($dropzone); + } + + protected function drop_zones_repeated_options() { + $repeatedoptions = array(); + return $repeatedoptions; + } + + protected function get_hint_fields($withclearwrong = false, $withshownumpartscorrect = false) { + $mform = $this->_form; + + $repeated = array(); + $repeated[] = $mform->createElement('header', 'hinthdr', get_string('hintn', 'question')); + $repeated[] = $mform->createElement('editor', 'hint', get_string('hinttext', 'question'), + array('rows' => 5), $this->editoroptions); + $repeatedoptions['hint']['type'] = PARAM_RAW; + + $repeated[] = $mform->createElement('checkbox', 'hintshownumcorrect', + get_string('options', 'question'), + get_string('shownumpartscorrect', 'question')); + $repeated[] = $mform->createElement('checkbox', 'hintstatewhichincorrect', + '', + get_string('stateincorrectlyplaced', 'qtype_ddmarker')); + $repeated[] = $mform->createElement('checkbox', 'hintclearwrong', + '', + get_string('clearwrongparts', 'qtype_ddmarker')); + + return array($repeated, $repeatedoptions); + } + + public function data_preprocessing($question) { + + $question = parent::data_preprocessing($question); + $question = $this->data_preprocessing_combined_feedback($question, true); + $question = $this->data_preprocessing_hints($question); + + $dragids = array(); // drag no -> dragid + if (!empty($question->options)) { + $question->shuffleanswers = $question->options->shuffleanswers; + $question->showmisplaced = $question->options->showmisplaced; + $question->drags = array(); + foreach ($question->options->drags as $drag) { + $dragindex = $drag->no -1; + $question->drags[$dragindex] = array(); + $question->drags[$dragindex]['label'] = $drag->label; + $question->drags[$dragindex]['infinite'] = $drag->infinite; + $dragids[$dragindex] = $drag->id; + } + $question->drops = array(); + foreach ($question->options->drops as $drop) { + $droparray = (array)$drop; + unset($droparray['id']); + unset($droparray['no']); + unset($droparray['questionid']); + $question->drops[$drop->no -1] = $droparray; + } + } + //initialise file picker for bgimage + $draftitemid = file_get_submitted_draft_itemid('bgimage'); + + file_prepare_draft_area($draftitemid, $this->context->id, 'qtype_ddmarker', + 'bgimage', !empty($question->id) ? (int) $question->id : null, + self::file_picker_options()); + $question->bgimage = $draftitemid; + + //$this->js_call(); + + return $question; + } + /** + * Perform the necessary preprocessing for the hint fields. + * @param object $question the data being passed to the form. + * @return object $question the modified data. + */ + protected function data_preprocessing_hints($question) { + if (empty($question->hints)) { + return $question; + } + parent::data_preprocessing_hints($question, true, true); + + $question->hintoptions = array(); + foreach ($question->hints as $hint) { + $question->hintstatewhichincorrect[] = $hint->options; + } + + return $question; + } + + public function validation($data, $files) { + $errors = parent::validation($data, $files); + if (!self::file_uploaded($data['bgimage'])) { + $errors["bgimage"] = get_string('formerror_nobgimage', 'qtype_ddmarker'); + } + + $allchoices = array(); + for ($i=0; $i < $data['nodropzone']; $i++) { + $labelpresent = (trim($data['drops'][$i]['label']) !== ''); + $choice = $data['drops'][$i]['choice']; + $choicepresent = ($choice !== '0'); + + if ($choicepresent) { + //test coords here + + + } else { + if (trim($data['drops'][$i]['coords']) !== '') { + $errors["drops[{$i}]"] + = get_string('formerror_noitemselected', 'qtype_ddmarker'); + } + } + } + for ($dragindex=0; $dragindex < $data['noitems']; $dragindex++) { + $label = $data['drags'][$dragindex]['label']; + if ($label != strip_tags($label)) { + $errors["drags[{$dragindex}]"] + = get_string('formerror_notagsallowed', 'qtype_ddmarker'); + } + + } + return $errors; + } } diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 666ff349590..2f14a1cf0df 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -22,15 +22,17 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -$string['addingddmarker'] = 'Adding drag and drop: images or text onto image'; -$string['addmoredropzones'] = 'Blanks for {no} More Drop Zones'; -$string['addmoreimages'] = 'Blanks for {no} More Draggable Items'; +$string['addingddmarker'] = 'Adding drag and drop markers'; +$string['addmoredropzones'] = 'Blanks for {no} more drop zones'; +$string['addmoreitems'] = 'Blanks for {no} more markers'; +$string['alttext'] = 'Alt text'; $string['answer'] = 'Answer'; -$string['bgimage'] = 'Background Image'; +$string['bgimage'] = 'Background image'; +$string['coords'] = 'Coords'; $string['correctansweris'] = 'The correct answer is: {$a}'; -$string['ddmarker'] = 'Drag and drop: markers onto image'; -$string['ddmarker_help'] = 'Select a background image file, select draggable images or enter text and define the drop zones on the background image to which they must be dragged.'; -$string['ddmarkersummary'] = 'Images or text labels are dragged and dropped into drop zones on a background image.'; +$string['ddmarker'] = 'Drag and drop markers'; +$string['ddmarker_help'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; +$string['ddmarkersummary'] = 'Markers are dragged and dropped onto a background image.'; $string['draggableimage'] = 'Draggable image'; $string['draggableitem'] = 'Draggable item'; $string['draggableitemheader'] = 'Draggable item {$a}'; @@ -38,27 +40,31 @@ $string['draggableitemtype'] = 'Type'; $string['draggableword'] = 'Draggable text'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; -$string['editingddmarker'] = 'Editing drag and drop: images or text onto image'; -$string['formerror_disallowedtags'] = 'You have used html tags here that are not allowed in a draggable text drag item type.'; -$string['formerror_noallowedtags'] = 'No html tags are allowed in this text which is the alt text for a draggable image'; -$string['formerror_noytop'] = 'You must provide a value for the y coords for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.'; -$string['formerror_noxleft'] = 'You must provide a value for the y coords for the top left corner of this drop area. You can drag and drop the drop area above to set the coordinates or enter them manually here.'; -$string['formerror_nofile'] = 'You need to upload or select a file to use here.'; -$string['formerror_nofile3'] = 'You need to select an image file here, or delete the associated label and uncheck the infinite checkbox.'; -$string['formerror_multipledraginstance'] = 'You have selected this image {$a} more than once as the correct choice for a drop zone but it is not marked as being an infinite drag item.'; -$string['formerror_multipledraginstance2'] = 'You have selected this image more than once as the correct choice for a drop zone but it is not marked as being an infinite drag item.'; -$string['formerror_noimageselected'] = 'You need to select a drag item to be the correct choice for this drop zone.'; +$string['editingddmarker'] = 'Editing drag and drop markers'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; +$string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; +$string['formerror_notagsallowed'] = 'No html tags are allowed in the label for a marker'; $string['infinite'] = 'Infinite'; -$string['label'] = 'Text'; +$string['marker'] = 'Marker'; +$string['marker_n'] = 'Marker {no}'; +$string['markers'] = 'Markers'; $string['nolabel'] = 'No label text'; $string['pleasedraganimagetoeachdropregion'] = 'Your answer is not complete, please drag an item to each drop region.'; $string['previewarea'] = 'Preview area -'; $string['previewareaheader'] = 'Preview'; -$string['previewareamessage'] = 'Select a background image file and select draggable images or just enter text that will be made draggable. Then choose a drag item for each \'drop zone\', and drag the drag item to where the student should drag it to.'; +$string['previewareamessage'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; $string['refresh'] = 'Refresh Preview'; +$string['clearwrongparts'] = 'Move incorrectly placed markers back to default start position below image'; +$string['shape'] = 'Shape'; +$string['shape_circle'] = 'Circle'; +$string['shape_circle_coords'] = 'centerx, centery; radius'; +$string['shape_rectangle'] = 'Rectangle'; +$string['shape_rectangle_coords'] = 'leftx, topy; width, height'; +$string['shape_polygon'] = 'Polygon'; +$string['shape_polygon_coords'] = 'x1,y1;x2,y2;x3,y3;x4,y4....'; +$string['showmisplaced'] = 'Highlight drop zones which have not had the correct drag item dropped on them'; $string['shuffleimages'] = 'Shuffle Drag Items Each Time Question Is Attempted'; +$string['stateincorrectlyplaced'] = 'State which markers are incorrectly placed'; $string['summariseplace'] = '{$a->no}. {$a->text}'; $string['summariseplaceno'] = 'Drop zone {$a}'; -$string['xleft'] = 'Left'; $string['ytop'] = 'Top'; \ No newline at end of file diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 034ee131bd8..1104d4a7a54 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -28,8 +28,52 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/question/type/ddimageortext/questiontypebase.php'); -define('QTYPE_ddmarker_BGIMAGE_MAXWIDTH', 600); -define('QTYPE_ddmarker_BGIMAGE_MAXHEIGHT', 400); +define('QTYPE_DDMARKER_BGIMAGE_MAXWIDTH', 600); +define('QTYPE_DDMARKER_BGIMAGE_MAXHEIGHT', 400); + +/** + * 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 + * number of parts right at each stage, and to reset the wrong parts. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class question_hint_ddmarker extends question_hint_with_parts { + + public $statewhichincorrect; + + /** + * Constructor. + * @param int the hint id from the database. + * @param string $hint The hint text + * @param int 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. + */ + public function __construct($id, $hint, $hintformat, $shownumcorrect, + $clearwrong, $statewhichincorrect) { + parent::__construct($id, $hint, $hintformat, $shownumcorrect, $clearwrong); + $this->statewhichincorrect = $statewhichincorrect; + } + + /** + * Create a basic hint from a row loaded from the question_hints table in the database. + * @param object $row with property options as well as hint, shownumcorrect and clearwrong set. + * @return question_hint_ddmarker + */ + public static function load_from_record($row) { + return new question_hint_ddmarker($row->id, $row->hint, $row->hintformat, + $row->shownumcorrect, $row->clearwrong, $row->options); + } + + public function adjust_display_options(question_display_options $options) { + parent::adjust_display_options($options); + $options->statewhichincorrect = $this->statewhichincorrect; + } +} + + /** * The drag-and-drop words into sentences question type class. @@ -42,5 +86,150 @@ class qtype_ddmarker extends qtype_ddtoimage_base { public function requires_qtypes() { return array_merge(parent::requires_qtypes(), array('ddimageortext')); } + public function save_question_options($formdata) { + global $DB, $USER; + $context = $formdata->context; + + $options = $DB->get_record('qtype_ddmarker', array('questionid' => $formdata->id)); + if (!$options) { + $options = new stdClass(); + $options->questionid = $formdata->id; + $options->correctfeedback = ''; + $options->partiallycorrectfeedback = ''; + $options->incorrectfeedback = ''; + $options->id = $DB->insert_record('qtype_ddmarker', $options); + } + + $options->shuffleanswers = !empty($formdata->shuffleanswers); + $options->showmisplaced = !empty($formdata->showmisplaced); + $options = $this->save_combined_feedback_helper($options, $formdata, $context, true); + $this->save_hints($formdata, true); + $DB->update_record('qtype_ddmarker', $options); + $DB->delete_records('qtype_ddmarker_drops', array('questionid' => $formdata->id)); + foreach (array_keys($formdata->drops) as $dropno) { + if ($formdata->drops[$dropno]['choice'] == 0) { + continue; + } + $drop = new stdClass(); + $drop->questionid = $formdata->id; + $drop->no = $dropno + 1; + $drop->shape = $formdata->drops[$dropno]['shape']; + $drop->coords = $formdata->drops[$dropno]['coords']; + $drop->choice = $formdata->drops[$dropno]['choice']; + $drop->label = $formdata->drops[$dropno]['label']; + + $DB->insert_record('qtype_ddmarker_drops', $drop); + } + + //an array of drag no -> drag id + $olddragids = $DB->get_records_menu('qtype_ddmarker_drags', + array('questionid' => $formdata->id), + '', 'no, id'); + foreach (array_keys($formdata->drags) as $dragno) { + if (!empty($formdata->drags[$dragno]['label'])) { + $drag = new stdClass(); + $drag->questionid = $formdata->id; + $drag->no = $dragno + 1; + $drag->infinite = empty($formdata->drags[$dragno]['infinite'])? 0 : 1; + $drag->label = $formdata->drags[$dragno]['label']; + + if (isset($olddragids[$dragno +1])) { + $drag->id = $olddragids[$dragno +1]; + unset($olddragids[$dragno +1]); + $DB->update_record('qtype_ddmarker_drags', $drag); + } else { + $drag->id = $DB->insert_record('qtype_ddmarker_drags', $drag); + } + + } + + } + if (!empty($olddragids)) { + list($sql, $params) = $DB->get_in_or_equal(array_values($olddragids)); + $DB->delete_records_select('qtype_ddmarker_drags', "id $sql", $params); + } + + self::constrain_image_size_in_draft_area($formdata->bgimage, + QTYPE_DDMARKER_BGIMAGE_MAXWIDTH, + QTYPE_DDMARKER_BGIMAGE_MAXHEIGHT); + file_save_draft_area_files($formdata->bgimage, $formdata->context->id, + 'qtype_ddmarker', 'bgimage', $formdata->id, + array('subdirs' => 0, 'maxbytes' => 0, 'maxfiles' => 1)); + } + + public function save_hints($formdata, $withparts = false) { + global $DB; + $context = $formdata->context; + + $oldhints = $DB->get_records('question_hints', + array('questionid' => $formdata->id), 'id ASC'); + + if (!empty($formdata->hint)) { + $numhints = max(array_keys($formdata->hint)) + 1; + } else { + $numhints = 0; + } + + if ($withparts) { + if (!empty($formdata->hintclearwrong)) { + $numclears = max(array_keys($formdata->hintclearwrong)) + 1; + } else { + $numclears = 0; + } + if (!empty($formdata->hintshownumcorrect)) { + $numshows = max(array_keys($formdata->hintshownumcorrect)) + 1; + } else { + $numshows = 0; + } + $numhints = max($numhints, $numclears, $numshows); + } + + for ($i = 0; $i < $numhints; $i += 1) { + if (html_is_blank($formdata->hint[$i]['text'])) { + $formdata->hint[$i]['text'] = ''; + } + + if ($withparts) { + $clearwrong = !empty($formdata->hintclearwrong[$i]); + $shownumcorrect = !empty($formdata->hintshownumcorrect[$i]); + $statewhichincorrect = !empty($formdata->hintstatewhichincorrect[$i]); + } + + if (empty($formdata->hint[$i]['text']) && empty($clearwrong) && + empty($shownumcorrect) && empty($statewhichincorrect)) { + continue; + } + + // Update an existing hint if possible. + $hint = array_shift($oldhints); + if (!$hint) { + $hint = new stdClass(); + $hint->questionid = $formdata->id; + $hint->hint = ''; + $hint->id = $DB->insert_record('question_hints', $hint); + } + + $hint->hint = $this->import_or_save_files($formdata->hint[$i], + $context, 'question', 'hint', $hint->id); + $hint->hintformat = $formdata->hint[$i]['format']; + if ($withparts) { + $hint->clearwrong = $clearwrong; + $hint->shownumcorrect = $shownumcorrect; + $hint->options = $statewhichincorrect; + } + $DB->update_record('question_hints', $hint); + } + + // Delete any remaining old hints. + $fs = get_file_storage(); + foreach ($oldhints as $oldhint) { + $fs->delete_area_files($context->id, 'question', 'hint', $oldhint->id); + $DB->delete_records('question_hints', array('id' => $oldhint->id)); + } + } + + protected function make_hint($hint) { + return question_hint_ddmarker::load_from_record($hint); + } } From 6a69507ba8f5ecd88788c51192fb0dec22590e28 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 1 Oct 2011 18:50:04 +0700 Subject: [PATCH 007/178] MDL-47494 ddmarker: NOBUG changed to using checkbox instead of advcheckbox --- question/type/ddmarker/edit_ddmarker_form.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index e8b841edbd7..e7ec5bb583e 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -80,7 +80,7 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { array('size'=>30, 'class'=>'tweakcss')); $mform->setType('text', PARAM_RAW_TRIMMED); - $grouparray[] = $mform->createElement('advcheckbox', 'infinite', ' ', + $grouparray[] = $mform->createElement('checkbox', 'infinite', ' ', get_string('infinite', 'qtype_ddmarker')); $draggableimageitem[] = $mform->createElement('group', 'drags', get_string('marker_n', 'qtype_ddmarker'), $grouparray); @@ -188,7 +188,7 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { self::file_picker_options()); $question->bgimage = $draftitemid; - //$this->js_call(); + $this->js_call(); return $question; } From bf442b625ad86b93573722d45f8b019e2baff019 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 1 Oct 2011 18:51:35 +0700 Subject: [PATCH 008/178] MDL-47494 ddmarker: NOBUG removed drop->label property. --- question/type/ddmarker/db/install.xml | 5 ++--- question/type/ddmarker/edit_ddmarker_form.php | 7 +------ question/type/ddmarker/questiontype.php | 1 - 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/question/type/ddmarker/db/install.xml b/question/type/ddmarker/db/install.xml index a251d0c436f..2946281de32 100644 --- a/question/type/ddmarker/db/install.xml +++ b/question/type/ddmarker/db/install.xml @@ -1,5 +1,5 @@ - @@ -30,8 +30,7 @@ - - + diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index e7ec5bb583e..0d3608f2194 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -105,7 +105,7 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { get_string('marker', 'qtype_ddmarker'), $shapearray); $grouparray[] = $mform->createElement('text', 'coords', get_string('coords', 'qtype_ddmarker'), - array('size'=>20, 'class'=>'tweakcss')); + array('size'=>50, 'class'=>'tweakcss')); $mform->setType('coords', PARAM_NOTAGS); $markernos = array(); $markernos[0] = ''; @@ -116,11 +116,6 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { get_string('marker', 'qtype_ddmarker').' '); $grouparray[] = $mform->createElement('select', 'choice', get_string('marker', 'qtype_ddmarker'), $markernos); - $grouparray[] = $mform->createElement('static', '', '', ' ' . - get_string('alttext', 'qtype_ddmarker').' '); - $grouparray[] = $mform->createElement('text', 'label', - get_string('alttext', 'qtype_ddmarker'), - array('size'=>10, 'class'=>'tweakcss')); $dropzone = $mform->createElement('group', 'drops', get_string('dropzone', 'qtype_ddmarker', '{no}'), $grouparray); return array($dropzone); diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 1104d4a7a54..2fe96899764 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -116,7 +116,6 @@ class qtype_ddmarker extends qtype_ddtoimage_base { $drop->shape = $formdata->drops[$dropno]['shape']; $drop->coords = $formdata->drops[$dropno]['coords']; $drop->choice = $formdata->drops[$dropno]['choice']; - $drop->label = $formdata->drops[$dropno]['label']; $DB->insert_record('qtype_ddmarker_drops', $drop); } From c2682aa64b3a273a0bc468987ca1dbd0840b2d23 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 1 Oct 2011 18:52:37 +0700 Subject: [PATCH 009/178] MDL-47494 ddmarker: NOBUG displaying drop zones. But polygon drop zone is not in the right place! --- question/type/ddmarker/yui/dd/dd.js | 15 +- question/type/ddmarker/yui/form/form.js | 188 +++++++++++++++++------- 2 files changed, 133 insertions(+), 70 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 5cdfb28c603..cec79e79218 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -18,24 +18,11 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { if (waitforimageconstrain) { bgdone = bgdone && this.doc.bg_img().hasClass('constrained'); } - var alldragsloaded = !this.doc.drag_item_homes().some(function(dragitemhome){ - //in 'some' loop returning true breaks the loop and is passed as return value from - //'some' else returns false. Can be though of as equivalent to ||. - if (dragitemhome.get('tagName') !== 'IMG'){ - return false; - } - var done = (dragitemhome.get('complete')); - if (waitforimageconstrain) { - done = done && dragitemhome.hasClass('constrained'); - } - return !done; - }); - if (bgdone && alldragsloaded) { + if (bgdone) { if (this.polltimer !== null) { this.polltimer.cancel(); this.polltimer = null; } - this.doc.drag_item_homes().detach('load', this.poll_for_image_load); this.doc.bg_img().detach('load', this.poll_for_image_load); if (pause !== 0) { Y.later(pause, this, doafterwords); diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index de0c1a48e53..5cf78a44c65 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -11,9 +11,8 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { initializer : function(params) { this.fp = this.file_pickers(); - Y.one(this.get('topnode')).append('
'+ - '
'+ - '
'); + Y.one(this.get('topnode')).append('
'+ + '
'); this.doc = this.doc_structure(this); this.draw_dd_area(); }, @@ -24,7 +23,6 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { this.set_options_for_drag_item_selectors(); if (bgimageurl !== null) { this.doc.load_bg_img(bgimageurl); - this.load_drag_homes(); var drop = new Y.DD.Drop({ node: this.doc.bg_img() @@ -35,26 +33,20 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { }); this.afterimageloaddone = false; - this.doc.bg_img().on('load', this.constrain_image_size, this, 'bgimage'); - this.doc.drag_item_homes() - .on('load', this.constrain_image_size, this, 'dragimage'); - this.doc.bg_img().after('load', this.poll_for_image_load, this, - true, 0, this.after_all_images_loaded); - this.doc.drag_item_homes() .after('load', this.poll_for_image_load, this, - true, 0, this.after_all_images_loaded); + this.attach_on_load_handlers(this.doc.bg_img(), 'bgimage'); } else { this.setup_form_events(); } - this.update_visibility_of_file_pickers(); + }, + + attach_on_load_handlers : function (imgorimgs, imgtype) { + imgorimgs.on('load', this.constrain_image_size, this, imgtype); + imgorimgs.after('load', this.poll_for_image_load, this, + true, 0, this.after_all_images_loaded); }, after_all_images_loaded : function () { - this.update_padding_sizes_all(); - this.update_drag_instances(); - this.reposition_drags_for_form(); - this.set_options_for_drag_item_selectors(); - this.setup_form_events(); - Y.later(500, this, this.reposition_drags_for_form, [], true); + Y.later(500, this, this.update_drop_zones, [], true); }, constrain_image_size : function (e, imagetype) { @@ -67,48 +59,135 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { e.target.addClass('constrained'); e.target.detach('load', this.constrain_image_size); }, + + graphics : null, - load_drag_homes : function () { - //set up drag items homes - for (var i=0; i < this.form.get_form_value('noitems', []); i++) { - this.load_drag_home(i); - } - }, - load_drag_home : function (dragitemno) { - if ('image' === this.form.get_form_value('dragitemtype', [dragitemno])) { - var url = - this.fp.file(this.form.to_name_with_index('dragitem', [dragitemno])).href; - } else { - var url = null; - } - this.doc.add_or_update_drag_item_home(dragitemno, url, - this.form.get_form_value('drags', [dragitemno, 'draglabel']), - this.form.get_form_value('drags', [dragitemno, 'draggroup'])); - }, - - update_drag_instances : function () { + update_drop_zones : function () { //set up drop zones + if (this.graphics !== null) { + this.graphics.destroy(); + } + this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"}); for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { - var dragitemno = this.form.get_form_value('drops', [i, 'choice']); - if (dragitemno !== '0' && (this.doc.drag_item(i) === null)) { - var drag = this.doc.clone_new_drag_item(i, dragitemno - 1); - if (drag !== null) { - this.doc.draggable_for_form(drag); - } + this.update_drop_zone(i); + } + }, + update_drop_zone : function (dropzoneno) { + var markertext; + var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']); + if (+dragitemno !== 0){ + markertext = this.get_marker_text(dragitemno); + } else { + markertext = ''; + } + var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']); + var drawfunc = 'draw_shape_'+shape; + if (this[drawfunc] instanceof Function){ + this[drawfunc](dropzoneno, markertext); + } + }, + draw_shape_circle : function (dropzoneno, markertext) { + var coords = this.get_coords(dropzoneno); + var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); + if (coordsparts && coordsparts.length === 4) { + var xy = [coordsparts[1] - coordsparts[3], coordsparts[2] - coordsparts[3]]; + var widthheight = [coordsparts[3]*2, coordsparts[3]*2]; + console.log('circle', dropzoneno, markertext, xy, widthheight); + var shape = this.graphics.addShape({ + type: 'circle', + width: widthheight[0], + height: widthheight[1], + fill: { + color: "#0000ff", + opacity : "0.5" + }, + stroke: { + weight:1, + color: "black" + } + }); + shape.setXY(this.convert_to_window_xy(xy)); + console.log(this.convert_to_window_xy(xy)); + } + }, + draw_shape_rectangle : function (dropzoneno, markertext) { + var coords = this.get_coords(dropzoneno); + var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); + if (coordsparts && coordsparts.length === 5) { + var xy = [coordsparts[1], coordsparts[2]]; + var widthheight = [coordsparts[3], coordsparts[4]]; + console.log('rectangle', dropzoneno, markertext, xy, widthheight); + var shape = this.graphics.addShape({ + type: 'rect', + width: widthheight[0], + height: widthheight[1], + fill: { + color: "#0000ff", + opacity : "0.5" + }, + stroke: { + weight:1, + color: "black" + } + }); + shape.setXY(this.convert_to_window_xy(xy)); + console.log(this.convert_to_window_xy(xy)); + } + }, + draw_shape_polygon : function (dropzoneno, markertext) { + var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); + var coordsparts = coords.split(';'); + var xy = []; + for (var i in coordsparts) { + var parts = coordsparts[i].match(/^(\d+),(\d+)$/); + if (parts !== null) { + xy[xy.length] = [parts[1], parts[2]]; } } + if (xy.length > 2) { + console.log('polygon', dropzoneno, markertext, xy); + var polygon = this.graphics.addShape({ + type: "path", + stroke: { + weight: 1, + color: "#000" + }, + fill: { + color: "#f00", + opacity : "0.5" + } + }); + for (var i in xy) { + var windowxy = this.convert_to_window_xy(xy[i]); + console.log(i, this.convert_to_window_xy(xy[i])); + if (i == 0) { + polygon.moveTo(windowxy[0], windowxy[1]); + } else { + polygon.lineTo(windowxy[0], windowxy[1]); + } + } + if (+xy[0][0] !== +xy[xy.length-1][0] || +xy[0][1] !== +xy[xy.length-1][1]) { + var windowxy = this.convert_to_window_xy(xy[0]); + polygon.lineTo(windowxy[0], windowxy[1]); //close polygon if not already closed + } + polygon.end(); + } + }, + get_coords : function (dropzoneno) { + var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); + return coords.replace(new RegExp("\\s*", 'g'), ''); + }, + get_marker_text : function (markerno) { + var label = this.form.get_form_value('drags', [markerno, 'label']); + return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1"); }, set_options_for_drag_item_selectors : function () { var dragitemsoptions = {0: ''}; for (var i=0; i < this.form.get_form_value('noitems', []); i++) { - var label = this.form.get_form_value('drags', [i, 'draglabel']); - var file = this.fp.file(this.form.to_name_with_index('dragitem', [i])); - if ('image' === this.form.get_form_value('dragitemtype', [i]) - && file.name !== null) { - dragitemsoptions[i+1] = (i+1)+'. '+label+' ('+file.name+')'; - } else if (label != '') { - dragitemsoptions[i+1] = (i+1)+'. '+label; + var label = this.get_marker_text(i); + if (label !== "") { + dragitemsoptions[i+1] = label; } } for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { @@ -126,10 +205,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { optionnode.set('selected', true); } else { if (value !== 0) { // no item option is always selectable - var cbselector = 'fieldset#draggableitemheader_'+(value-1) - +' input[type="checkbox"]'; - var cbel = Y.one(cbselector); - var infinite = cbel.get('checked'); + var infinite = this.form.get_form_value('drags', [value-1, 'infinite']); if ((!infinite) && (this.doc.drag_items_cloned_from(value - 1).size() !== 0)) { optionnode.set('disabled', true); @@ -271,7 +347,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { }, get_el : function (name, indexes) { var form = document.getElementById('mform1'); - return form.elements[this.to_name_with_index(name, indexes)] + return form.elements[this.to_name_with_index(name, indexes)]; }, get_form_value : function(name, indexes) { var el = this.get_el(name, indexes); @@ -337,5 +413,5 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { return new DDMARKER_FORM(config); } }, '@VERSION@', { - requires:['moodle-qtype_ddmarker-dd', 'form_filepicker'] + requires:['moodle-qtype_ddmarker-dd', 'form_filepicker', 'graphics', 'resize'] }); \ No newline at end of file From 57e3c7cfc320407e19b167e88bc5c72bd46a2ea0 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 1 Oct 2011 21:23:36 +0700 Subject: [PATCH 010/178] MDL-47494 ddmarker: NOBUG drop zones now appearing where they are supposed to. --- question/type/ddmarker/yui/form/form.js | 99 ++++++++++++++----------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 5cf78a44c65..5cf7cb00b54 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -64,6 +64,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { update_drop_zones : function () { + this.set_options_for_drag_item_selectors(); //set up drop zones if (this.graphics !== null) { this.graphics.destroy(); @@ -92,23 +93,23 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); if (coordsparts && coordsparts.length === 4) { var xy = [coordsparts[1] - coordsparts[3], coordsparts[2] - coordsparts[3]]; - var widthheight = [coordsparts[3]*2, coordsparts[3]*2]; - console.log('circle', dropzoneno, markertext, xy, widthheight); - var shape = this.graphics.addShape({ - type: 'circle', - width: widthheight[0], - height: widthheight[1], - fill: { - color: "#0000ff", - opacity : "0.5" - }, - stroke: { - weight:1, - color: "black" - } - }); - shape.setXY(this.convert_to_window_xy(xy)); - console.log(this.convert_to_window_xy(xy)); + if (this.coords_in_img(xy)) { + var widthheight = [coordsparts[3]*2, coordsparts[3]*2]; + var shape = this.graphics.addShape({ + type: 'circle', + width: widthheight[0], + height: widthheight[1], + fill: { + color: "#0000ff", + opacity : "0.5" + }, + stroke: { + weight:1, + color: "black" + } + }); + shape.setXY(this.convert_to_window_xy(xy)); + } } }, draw_shape_rectangle : function (dropzoneno, markertext) { @@ -117,22 +118,22 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { if (coordsparts && coordsparts.length === 5) { var xy = [coordsparts[1], coordsparts[2]]; var widthheight = [coordsparts[3], coordsparts[4]]; - console.log('rectangle', dropzoneno, markertext, xy, widthheight); - var shape = this.graphics.addShape({ - type: 'rect', - width: widthheight[0], - height: widthheight[1], - fill: { - color: "#0000ff", - opacity : "0.5" - }, - stroke: { - weight:1, - color: "black" - } - }); - shape.setXY(this.convert_to_window_xy(xy)); - console.log(this.convert_to_window_xy(xy)); + if (this.coords_in_img([xy[0]+widthheight[0], xy[1]+widthheight[1]])) { + var shape = this.graphics.addShape({ + type: 'rect', + width: widthheight[0], + height: widthheight[1], + fill: { + color: "#0000ff", + opacity : "0.5" + }, + stroke: { + weight:1, + color: "black" + } + }); + shape.setXY(this.convert_to_window_xy(xy)); + } } }, draw_shape_polygon : function (dropzoneno, markertext) { @@ -141,12 +142,11 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { var xy = []; for (var i in coordsparts) { var parts = coordsparts[i].match(/^(\d+),(\d+)$/); - if (parts !== null) { + if (parts !== null && this.coords_in_img([parts[1], parts[2]])) { xy[xy.length] = [parts[1], parts[2]]; } } if (xy.length > 2) { - console.log('polygon', dropzoneno, markertext, xy); var polygon = this.graphics.addShape({ type: "path", stroke: { @@ -160,20 +160,24 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { }); for (var i in xy) { var windowxy = this.convert_to_window_xy(xy[i]); - console.log(i, this.convert_to_window_xy(xy[i])); if (i == 0) { - polygon.moveTo(windowxy[0], windowxy[1]); + polygon.moveTo(xy[i][0], xy[i][1]); } else { - polygon.lineTo(windowxy[0], windowxy[1]); + polygon.lineTo(xy[i][0], xy[i][1]); } } if (+xy[0][0] !== +xy[xy.length-1][0] || +xy[0][1] !== +xy[xy.length-1][1]) { var windowxy = this.convert_to_window_xy(xy[0]); - polygon.lineTo(windowxy[0], windowxy[1]); //close polygon if not already closed + polygon.lineTo(xy[0][0], xy[0][1]); //close polygon if not already closed } polygon.end(); + polygon.setXY(this.doc.bg_img().getXY()); } }, + coords_in_img : function (coords) { + return (coords[0] <= this.doc.bg_img().get('width') && + coords[1] <= this.doc.bg_img().get('height')); + }, get_coords : function (dropzoneno) { var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); return coords.replace(new RegExp("\\s*", 'g'), ''); @@ -190,9 +194,13 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { dragitemsoptions[i+1] = label; } } + var selectedvalues = []; + for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { + var selector = Y.one('#id_drops_'+i+'_choice'); + selectedvalues[i] = +selector.get('value'); + } for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { var selector = Y.one('#id_drops_'+i+'_choice'); - var selectedvalue = selector.get('value'); selector.all('option').remove(true); for (var value in dragitemsoptions) { value = +value; @@ -201,14 +209,17 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { ''; selector.append(option); var optionnode = selector.one('option[value="' + value + '"]') - if (value === +selectedvalue) { + if (value === selectedvalues[i]) { optionnode.set('selected', true); } else { if (value !== 0) { // no item option is always selectable var infinite = this.form.get_form_value('drags', [value-1, 'infinite']); - if ((!infinite) && - (this.doc.drag_items_cloned_from(value - 1).size() !== 0)) { - optionnode.set('disabled', true); + if (!infinite) { + for (var k in selectedvalues) { + if (+selectedvalues[k] === value) { + optionnode.set('disabled', true); + } + } } } } From f23600db4478ef0a4ddc79fd0bd747780df53592 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 2 Oct 2011 13:19:40 +0700 Subject: [PATCH 011/178] MDL-47494 ddmarker: NOBUG changed to use change events on input elements to trigger repopulation of Marker select box. --- question/type/ddmarker/yui/form/form.js | 51 +++---------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 5cf7cb00b54..29466ced9eb 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -47,6 +47,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { after_all_images_loaded : function () { Y.later(500, this, this.update_drop_zones, [], true); + this.setup_form_events(); }, constrain_image_size : function (e, imagetype) { @@ -64,7 +65,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { update_drop_zones : function () { - this.set_options_for_drag_item_selectors(); + //set up drop zones if (this.graphics !== null) { this.graphics.destroy(); @@ -235,56 +236,14 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { //events triggered by changes to form data //x and y coordinates - Y.all('fieldset#dropzoneheader input').on('blur', function (e){ - var name = e.target.getAttribute('name'); - var draginstanceno = this.form.from_name_with_index(name).indexes[0]; - var fromform = [this.form.get_form_value('drops', [draginstanceno, 'xleft']), - this.form.get_form_value('drops', [draginstanceno, 'ytop'])]; - var constrainedxy = this.constrain_xy(draginstanceno, fromform); - this.form.set_form_value('drops', [draginstanceno, 'xleft'], constrainedxy[0]); - this.form.set_form_value('drops', [draginstanceno, 'ytop'], constrainedxy[1]); + Y.all('fieldset#draggableitemheader input').on('change', function (e){ + this.set_options_for_drag_item_selectors(); }, this); //change in selected item Y.all('fieldset#dropzoneheader select').on('change', function (e){ - var name = e.target.getAttribute('name'); - var draginstanceno = this.form.from_name_with_index(name).indexes[0]; - var old = this.doc.drag_item(draginstanceno); - if (old !== null) { - old.remove(true); - } - this.draw_dd_area(); + this.set_options_for_drag_item_selectors(); }, this); - - for (var i=0; i < this.form.get_form_value('noitems', []); i++) { - //change to group selector - Y.all('fieldset#draggableitemheader_'+i+' select.draggroup') - .on('change', function (e){ - this.doc.drag_items().remove(true); - this.draw_dd_area(); - }, this); - Y.all('fieldset#draggableitemheader_'+i+' select.dragitemtype') - .on('change', function (e){ - this.doc.drag_items().remove(true); - this.draw_dd_area(); - }, this); - Y.all('fieldset#draggableitemheader_'+i+' input[type="text"]') - .on('blur', function (e, draginstanceno){ - this.doc.drag_item(draginstanceno).remove(true); - this.draw_dd_area(); - }, this, i); - //change to infinite checkbox - Y.all('fieldset#draggableitemheader_'+i+' input[type="checkbox"]') - .on('change', this.set_options_for_drag_item_selectors, this); - } - //event on file picker new file selection - Y.after(function (e){ - var name = this.fp.name(e.id); - if (name !== 'bgimage') { - this.doc.drag_items().remove(true); - } - this.draw_dd_area(); - }, M.form_filepicker, 'callback', this); }, update_visibility_of_file_pickers : function() { From 81fa308cda6ff76bf7cf1c05d77f751922b651a4 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 2 Oct 2011 13:21:20 +0700 Subject: [PATCH 012/178] MDL-47494 ddmarker: NOBUG Removed redundant code and reference to unused module. --- question/type/ddmarker/yui/form/form.js | 59 +------------------------ 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 29466ced9eb..d75c538b46b 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -246,63 +246,6 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { }, this); }, - update_visibility_of_file_pickers : function() { - for (var i=0; i < this.form.get_form_value('noitems', []); i++) { - if ('image' === this.form.get_form_value('dragitemtype', [i])) { - Y.one('input#id_dragitem_'+i).get('parentNode').get('parentNode') - .setStyle('display', 'block'); - } else { - Y.one('input#id_dragitem_'+i).get('parentNode').get('parentNode') - .setStyle('display', 'none'); - } - } - }, - - reposition_drags_for_form : function() { - this.doc.drag_items().each(function (drag) { - var draginstanceno = drag.getData('draginstanceno'); - this.reposition_drag_for_form(draginstanceno); - }, this); - }, - - reposition_drag_for_form : function (draginstanceno) { - var drag = this.doc.drag_item(draginstanceno); - if (null !== drag && !drag.hasClass('yui3-dd-dragging')) { - var fromform = [this.form.get_form_value('drops', [draginstanceno, 'xleft']), - this.form.get_form_value('drops', [draginstanceno, 'ytop'])]; - if (fromform[0] == '' && fromform[1] == '') { - var dragitemno = drag.getData('dragitemno'); - drag.setXY(this.doc.drag_item_home(dragitemno).getXY()); - } else { - drag.setXY(this.convert_to_window_xy(fromform)); - } - } - }, - set_drag_xy : function (draginstanceno, xy) { - xy = this.constrain_xy(draginstanceno, this.convert_to_bg_img_xy(xy)); - this.form.set_form_value('drops', [draginstanceno, 'xleft'], Math.round(xy[0])); - this.form.set_form_value('drops', [draginstanceno, 'ytop'], Math.round(xy[1])); - }, - reset_drag_xy : function (draginstanceno) { - this.form.set_form_value('drops', [draginstanceno, 'xleft'], ''); - this.form.set_form_value('drops', [draginstanceno, 'ytop'], ''); - }, - - //make sure xy value is not out of bounds of bg image - constrain_xy : function (draginstanceno, bgimgxy) { - var drag = this.doc.drag_item(draginstanceno); - var xleftconstrained = - Math.min(bgimgxy[0], this.doc.bg_img().get('width') - drag.get('offsetWidth')); - var ytopconstrained = - Math.min(bgimgxy[1], this.doc.bg_img().get('height') - drag.get('offsetHeight')); - xleftconstrained = Math.max(xleftconstrained, 0); - ytopconstrained = Math.max(ytopconstrained, 0); - return [xleftconstrained, ytopconstrained]; - }, - convert_to_bg_img_xy : function (windowxy) { - return [+windowxy[0] - this.doc.bg_img().getX()-1, - +windowxy[1] - this.doc.bg_img().getY()-1]; - }, /** * Low level operations on form. @@ -383,5 +326,5 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { return new DDMARKER_FORM(config); } }, '@VERSION@', { - requires:['moodle-qtype_ddmarker-dd', 'form_filepicker', 'graphics', 'resize'] + requires:['moodle-qtype_ddmarker-dd', 'form_filepicker', 'graphics'] }); \ No newline at end of file From ae674bd250ffd873eae0170b3f63b0a442797a28 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 5 Oct 2011 20:08:52 +0700 Subject: [PATCH 013/178] MDL-47494 ddmarker: NOBUG added display of marker text at center of shape. --- question/type/ddmarker/styles.css | 6 +++ question/type/ddmarker/yui/form/form.js | 53 ++++++++++++++++++------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 30ee6a32c20..d907a5572f5 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -74,3 +74,9 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa *font-size:small; /* for IE */ *font:x-small; /* for IE in quirks mode */ } +.que.ddmarker div.markertexts span.markertext, +form.mform fieldset#previewareaheader div.markertexts span.markertext { + border: 1px solid black; + background-color: white; + padding: 5px; +} \ No newline at end of file diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index d75c538b46b..e24fb57a38f 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -12,7 +12,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { initializer : function(params) { this.fp = this.file_pickers(); Y.one(this.get('topnode')).append('
'+ - '
'); + '
'); this.doc = this.doc_structure(this); this.draw_dd_area(); }, @@ -76,20 +76,28 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } }, update_drop_zone : function (dropzoneno) { - var markertext; var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']); - if (+dragitemno !== 0){ - markertext = this.get_marker_text(dragitemno); - } else { - markertext = ''; - } + var markertext = this.get_marker_text(dragitemno); var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']); var drawfunc = 'draw_shape_'+shape; if (this[drawfunc] instanceof Function){ - this[drawfunc](dropzoneno, markertext); + var xyfortext = this[drawfunc](dropzoneno); + if (xyfortext !== null) { + var classnames = 'markertext markertext' + dropzoneno; + var existingdiv = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); + if (existingdiv) { + existingdiv.setContent(markertext); + } else { + Y.one('div.ddarea div.markertexts').append('' + + markertext+''); + } + var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); + markerspan.setStyle('opacity', '0.6'); + markerspan.setXY(this.convert_to_window_xy(xyfortext)); + } } }, - draw_shape_circle : function (dropzoneno, markertext) { + draw_shape_circle : function (dropzoneno) { var coords = this.get_coords(dropzoneno); var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); if (coordsparts && coordsparts.length === 4) { @@ -110,10 +118,12 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } }); shape.setXY(this.convert_to_window_xy(xy)); + return [+coordsparts[1], +coordsparts[2]]; } } + return null; }, - draw_shape_rectangle : function (dropzoneno, markertext) { + draw_shape_rectangle : function (dropzoneno) { var coords = this.get_coords(dropzoneno); var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); if (coordsparts && coordsparts.length === 5) { @@ -134,10 +144,13 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } }); shape.setXY(this.convert_to_window_xy(xy)); + return [+xy[0]+widthheight[0]/2, +xy[1]+widthheight[1]/2]; } } + return null; + }, - draw_shape_polygon : function (dropzoneno, markertext) { + draw_shape_polygon : function (dropzoneno) { var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); var coordsparts = coords.split(';'); var xy = []; @@ -159,8 +172,14 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { opacity : "0.5" } }); + var maxxy = [0,0]; + var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')]; for (var i in xy) { - var windowxy = this.convert_to_window_xy(xy[i]); + //calculate min and max points to find center to show marker on + minxy[0] = Math.min(xy[i][0], minxy[0]); + minxy[1] = Math.min(xy[i][1], minxy[1]); + maxxy[0] = Math.max(xy[i][0], maxxy[0]); + maxxy[1] = Math.max(xy[i][1], maxxy[1]); if (i == 0) { polygon.moveTo(xy[i][0], xy[i][1]); } else { @@ -173,7 +192,9 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } polygon.end(); polygon.setXY(this.doc.bg_img().getXY()); + return [Math.round((minxy[0] + maxxy[0])/2), Math.round((minxy[1] + maxxy[1])/2)]; } + return null; }, coords_in_img : function (coords) { return (coords[0] <= this.doc.bg_img().get('width') && @@ -184,8 +205,12 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { return coords.replace(new RegExp("\\s*", 'g'), ''); }, get_marker_text : function (markerno) { - var label = this.form.get_form_value('drags', [markerno, 'label']); - return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1"); + if (+markerno !== 0){ + var label = this.form.get_form_value('drags', [markerno-1, 'label']); + return label.replace(new RegExp("^\\s*(.*)\\s*$"), "$1"); + } else { + return ''; + } }, set_options_for_drag_item_selectors : function () { var dragitemsoptions = {0: ''}; From 8aae74a457672ca14dcf1e4dabac08d3ed514328 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 5 Oct 2011 20:11:54 +0700 Subject: [PATCH 014/178] MDL-47494 ddmarker: NOBUG made a start to validation of shape coords saved and also at the same time a start on code to interpret the coords for use in hit detection. --- question/type/ddmarker/edit_ddmarker_form.php | 34 +++++++++++++++++-- question/type/ddmarker/question.php | 3 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index 0d3608f2194..9cd1a2e043f 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -91,14 +91,16 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { return array(); } - + protected function shapes() { + return array('circle', 'rectangle', 'polygon'); + } protected function drop_zone($mform, $imagerepeats) { $dropzoneitem = array(); $grouparray = array(); - foreach (array('circle', 'rectangle', 'polygon') as $shape) { + foreach ($this->shapes as $shape) { $shapearray[$shape] = get_string('shape_'.$shape, 'qtype_ddmarker'); } $grouparray[] = $mform->createElement('select', 'shape', @@ -208,7 +210,8 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { public function validation($data, $files) { $errors = parent::validation($data, $files); - if (!self::file_uploaded($data['bgimage'])) { + $bgimagesize = $this->get_image_size_in_draft_area($data['bgimage']); + if ($bgimagesize === null) { $errors["bgimage"] = get_string('formerror_nobgimage', 'qtype_ddmarker'); } @@ -220,6 +223,11 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { if ($choicepresent) { //test coords here + if ($bgimagesize !== null) { + if (in_array($data['drops'][$i]['shape'], $this->shapes())) { + + } + } } else { @@ -239,4 +247,24 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { } return $errors; } + + public function get_image_size_in_draft_area($draftitemid) { + global $USER; + $usercontext = get_context_instance(CONTEXT_USER, $USER->id); + $fs = get_file_storage(); + $draftfiles = $fs->get_area_files($usercontext->id, 'user', 'draft', $draftitemid, 'id'); + if ($draftfiles) { + foreach ($draftfiles as $file) { + if ($file->is_directory()) { + continue; + } + //just return the data for the first good file, there should only be one. + $imageinfo = $file->get_imageinfo(); + $width = $imageinfo['width']; + $height = $imageinfo['height']; + return array($width, $height); + } + } + return null; + } } diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 018815c5ece..83fe2622296 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -27,6 +27,7 @@ defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/question/type/ddimageortext/questionbase.php'); +require_once($CFG->dirroot . '/question/type/ddmarker/shapes.php'); /** @@ -93,4 +94,4 @@ class qtype_ddmarker_drop_zone { } return $summariseplace; } -} \ No newline at end of file +} From 60c9d277daf58e892a73be62b87e1140dd014956 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 22 Oct 2011 20:29:36 +0700 Subject: [PATCH 015/178] MDL-47494 ddmarker: NOBUG added class for hit testing in various shapes and unit tests for polygons --- question/type/ddmarker/shapes.php | 357 ++++++++++++++++++ .../type/ddmarker/simpletest/testshapes.php | 80 ++++ question/type/ddmarker/yui/dd/dd.js | 1 - 3 files changed, 437 insertions(+), 1 deletion(-) create mode 100644 question/type/ddmarker/shapes.php create mode 100644 question/type/ddmarker/simpletest/testshapes.php diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php new file mode 100644 index 00000000000..e15519fe872 --- /dev/null +++ b/question/type/ddmarker/shapes.php @@ -0,0 +1,357 @@ +. + +/** + * Drag-and-drop words into sentences classes for dealing with shapes on the server side. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */abstract class qtype_ddmarker_shape { + public function __construct($coordsstring) { + + } + public function inside_width_height($widthheight) { + foreach ($this->outlying_coords_to_test() as $coordsxy) { + if ($coordsxy[0] > $widthheight[0] || $coordsxy[1] > $widthheight[1]) { + return false; + } + } + return true; + } + abstract protected function outlying_coords_to_test(); + + protected function is_only_numbers() { + $args = func_get_args(); + foreach ($args as $arg) { + if (0 === preg_match('!^[0-9]+$!', $arg)){ + return false; + } + } + return true; + } + + protected function is_point_in_bounding_box($pointxy, $xleftytop, $xrightybottom) { + if ($pointxy[0] < $xleftytop[0]) { + return false; + } else if ($pointxy[0] > $xrightybottom[0]) { + return false; + } else if ($pointxy[1] < $xleftytop[1]) { + return false; + } else if ($pointxy[1] > $xrightybottom[1]) { + return false; + } + return true; + } + + /** + * @param array $xy $xy[0] is x, $xy[1] is y + * @return boolean is point inside shape + */ + abstract public function is_point_in_shape($xy); +} +class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { + protected $width; + protected $height; + protected $xleft; + protected $ytop; + public function __construct($coordsstring) { + $coordstring = preg_replace('!\s!', '', $coordsstring); + $coordsstringparts = explode(';', $coordsstring); + if (count($coordsstringparts) > 2) { + $this->error = 'toomanysemicolons'; + } else if (count($coordsstringparts) < 2) { + $this->error = 'nosemicolons'; + } else { + $xy = explode(',', $coordsstringparts[0]); + $widthheightparts = explode(',', $coordsstringparts[1]); + if (count($xy) !== 2) { + $this->error = 'unrecognisedxypart'; + } else if (count($widthheightparts) !== 2) { + $this->error = 'unrecognisedwidthheightpart'; + } else { + list($this->width, $this->height) = $widthheightparts; + list($this->xleft, $this->ytop) = $xy; + } + if (!$this->is_only_numbers($this->width, $this->height, $this->ytop, $this->xleft)) { + $this->error = 'onlyusewholepositivenumbers'; + } + $this->width = (int) $this->width; + $this->height = (int) $this->height; + $this->xleft = (int) $this->xleft; + $this->ytop = (int) $this->ytop; + } + + } + protected function outlying_coords_to_test() { + return array($this->xleft + $this->width, $this->ytop + $this->height); + } + public function is_point_in_shape($xy) { + $this->is_point_in_bounding_box($xy, array($this->xleft, $this->ytop), + array($this->ytop + $this->height, $this->xleft + $this->width)); + } +} +class qtype_ddmarker_shape_circle extends qtype_ddmarker_shape { + + protected $xcentre; + protected $ycentre; + protected $radius; + + public function __construct($coordsstring) { + $coordstring = preg_replace('!\s!', '', $coordsstring); + $coordsstringparts = explode(';', $coordsstring); + if (count($coordsstringparts) > 2) { + $this->error = 'toomanysemicolons'; + } else if (count($coordsstringparts) < 2) { + $this->error = 'nosemicolons'; + } else { + $xy = explode(',', $coordsstringparts[0]); + if (count($xy) !== 2) { + $this->error = 'unrecognisedxypart'; + } else { + $this->radius = $coordsstringparts[1]; + list($this->xcentre, $this->ycentre) = $xy; + } + if (!$this->is_only_numbers($this->xcentre, $this->ycentre, $this->radius)) { + $this->error = 'onlyusewholepositivenumbers'; + } + $this->xcentre = (int) $this->xcentre; + $this->ycentre = (int) $this->ycentre; + $this->radius = (int) $this->radius; + } + } + protected function outlying_coords_to_test() { + return array($this->xcentre + $this->radius, $this->ycentre + $this->radius); + } + public function is_point_in_shape($xy) { + $distancefromcentre = sqrt(pow(($xy[0] - $this->xcentre), 2) + + pow(($xy[1] - $this->ycentre), 2)); + return $distancefromcentre < $this->radius; + } +} +class qtype_ddmarker_shape_polygon extends qtype_ddmarker_shape { + /** + * @var array Arrary of xy coords where xy coords are also in a two element array [x,y]. + */ + public $coords; + /** + * @var array min x and y coords in a two element array [x,y]. + */ + protected $minxy; + /** + * @var array max x and y coords in a two element array [x,y]. + */ + protected $maxxy; + + public function __construct($coordsstring) { + $this->coords = array(); + $coordstring = preg_replace('!\s!', '', $coordsstring); + $coordsstringparts = explode(';', $coordsstring); + if (count($coordsstringparts) < 3) { + $this->error = 'polygonmusthaveatleastthreepoints'; + } else { + foreach ($coordsstringparts as $coordsstringpart) { + $xy = explode(',', $coordsstringpart); + if (count($xy) !== 2) { + $this->error = 'unrecognisedxypart'; + } + if (!$this->is_only_numbers($xy[0], $xy[1])) { + $this->error = 'onlyusewholepositivenumbers'; + } + $xy[0] = (int) $xy[0]; + $xy[1] = (int) $xy[1]; + $this->coords[] = $xy; + if (isset($this->minxy)) { + $this->minxy[0] = min($this->minxy[0], $xy[0]); + $this->minxy[1] = min($this->minxy[1], $xy[1]); + } else { + $this->minxy[0] = $xy[0]; + $this->minxy[1] = $xy[1]; + } + if (isset($this->maxxy)) { + $this->maxxy[0] = max($this->maxxy[0], $xy[0]); + $this->maxxy[1] = max($this->maxxy[1], $xy[1]); + } else { + $this->maxxy[0] = $xy[0]; + $this->maxxy[1] = $xy[1]; + } + } + //make sure polygon is not closed + if ($this->coords[count($this->coords)-1][0] == $this->coords[0][0] && + $this->coords[count($this->coords)-1][1] == $this->coords[0][1]) { + unset($this->coords[count($this->coords)-1]); + } + } + } + protected function outlying_coords_to_test() { + return array($this->minxy, $this->maxxy); + } + public function is_point_in_shape($xy) { + $pointatinfinity = new qtype_ddmarker_point(-10000, $xy[1]); + $pointtotest = new qtype_ddmarker_point($xy[0], $xy[1]); + $testsegment = new qtype_ddmarker_segment($pointatinfinity, $pointtotest); + $windingnumber = 0; + foreach ($this->coords as $index => $coord) { + if ($index != 0) { + $a = new qtype_ddmarker_point($this->coords[$index -1][0], + $this->coords[$index -1][1]); + } else { + $a = new qtype_ddmarker_point($this->coords[count($this->coords)-1][0], + $this->coords[count($this->coords)-1][1]); + } + $b = new qtype_ddmarker_point($this->coords[$index][0], + $this->coords[$index][1]); + $segment = new qtype_ddmarker_segment($a, $b); + $intersects = $segment->intersects($testsegment); + if ($intersects === null) { + list($perturbedsegment, $testsegment) = $this->perturb($segment, $testsegment); + if ($index !== 0) { + $this->coords[$index-1][0] = $perturbedsegment->a->x; + $this->coords[$index-1][1] = $perturbedsegment->a->y; + } else { + $this->coords[count($this->coords)-1][0] = $perturbedsegment->a->x; + $this->coords[count($this->coords)-1][1] = $perturbedsegment->a->y; + } + $this->coords[$index][0] = $perturbedsegment->b->x; + $this->coords[$index][1] = $perturbedsegment->b->y; + $intersects = $perturbedsegment->intersects($testsegment); + if ($intersects === null) { + throw new coding_exception('Polygon hit test code failed '. + '- Still touching end point after perturbation'); + } else if ($intersects) { + $windingnumber++; + } + } else if ($intersects) { + $windingnumber++; + } + } + return ($windingnumber%2)?true:false; + } + /** + * $v segment and this touch, move one of them slightly. + * @param qtype_ddmarker_segment $v + * @param int $ua + * @param int $ub + */ + function perturb($p, $q) { + list(,$ua, $ub) = $p->intersection_point($q); + $pt = 0.00001; // Perturbation factor + $h = $p->a->dist($p->b); + if ($ua == 0) { + // q1, q2 intersects p1 exactly, move vertex p1 closer to p2 + $a = ($pt * $p->a->dist(new qtype_ddmarker_point($p->b->x, $p->a->y)))/$h; + $b = ($pt * $p->b->dist(new qtype_ddmarker_point($p->b->x, $p->a->y)))/$h; + $p->a->x = $p->a->x + $a; + $p->a->y = $p->a->y + $b; + } elseif ($ua == 1) { + // q1, q2 intersects p2 exactly, move vertex p2 closer to p1 + $a = ($pt * $p->a->dist(new qtype_ddmarker_point($p->b->x, $p->a->y)))/$h; + $b = ($pt * $p->b->dist(new qtype_ddmarker_point($p->b->x, $p->a->y)))/$h; + $p->b->x = $p->b->x - $a; + $p->b->y = $p->b->y - $b; + } elseif ($ub == 0){ + // p1, p2 intersects q1 exactly, move vertex q1 closer to q2 + $a = ($pt * $q->a->dist(new qtype_ddmarker_point($q->b->x, $q->a->y)))/$h; + $b = ($pt * $q->b->dist(new qtype_ddmarker_point($q->b->x, $q->a->y)))/$h; + $q->a->x = $q->a->x + $a; + $q->a->y = $q->a->y + $b; + } elseif ($ub == 1) { + // p1, p2 intersects q2 exactly, move vertex q2 closer to q1 + $a = ($pt * $q->a->dist(new qtype_ddmarker_point($q->b->x, $q->a->y)))/$h; + $b = ($pt * $q->b->dist(new qtype_ddmarker_point($q->b->x, $q->a->y)))/$h; + $q->b->x = $q->b->x - $a; + $q->b->y = $q->b->y - $b; + } + return array($p, $q); + } +} +class qtype_ddmarker_point { + public $x; + public $y; + public function __construct($x, $y) { + $this->x = $x; + $this->y = $y; + } + /** + ** Return the distance between this point and another + */ + function dist($other) { + return sqrt(pow($this->x - $other->x, 2) + pow($this->y - $other->y, 2)); + } +} + + +/** + * Defines a segment between two end points a and b. + */ +class qtype_ddmarker_segment { + public $a; + public $b; + + public function __construct(qtype_ddmarker_point $a, qtype_ddmarker_point $b) { + $this->a = $a; + $this->b = $b; + } + /** + * Find if this segment intersects another segment $v. + * @param segment $v + * @return boolean does it intersect? + */ + public function intersects(qtype_ddmarker_segment $v){ + //Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + // $this is P1 to P2 and $v is P3 to P4 + list($d, $ua, $ub) = $this->intersection_point($v); + if ($d !== 0) { // The lines intersect at a point somewhere + // The values of $ua and $ub tell us where the intersection occurred. + if ( (($ua == 0 || $ua == 1 )&&($ub >= 0 && $ub <= 1)) + || (($ub == 0 || $ub == 1) && ($ua >= 0 && $ua <= 1))){ + // A value of exactly 0 or 1 means the intersection occurred right at the + // start or end of the line segment. For our purposes we will consider this + // NOT to be an intersection + // away from the intersecting line. + // Degenerate case - segment exactly touches a line + return null; + } elseif (($ua > 0 && $ua < 1) && ($ub > 0 && $ub < 1)) { + // A value between 0 and 1 means the intersection occurred within the + // line segment. + // Intersection occurs on both line segments + return true; + } else { + // The lines do not intersect within the line segments + return false; + } + } else { // The lines do not intersect + return false; + } + } + + public function intersection_point(qtype_ddmarker_segment $v) { + $d = (($v->b->y - $v->a->y) * ($this->b->x - $this->a->x)) + -(($v->b->x - $v->a->x) * ($this->b->y - $this->a->y)); + if ($d !== 0) { // The lines intersect at a point somewhere + $ua = (($v->b->x-$v->a->x)*($this->a->y-$v->a->y) + -($v->b->y-$v->a->y)*($this->a->x-$v->a->x))/$d; + $ub = (($this->b->x-$this->a->x)*($this->a->y-$v->a->y) + -($this->b->y-$this->a->y)*($this->a->x-$v->a->x))/$d; + } else { + $ua = null; + $ub = null; + } + return array($d, $ua, $ub); + } + +} diff --git a/question/type/ddmarker/simpletest/testshapes.php b/question/type/ddmarker/simpletest/testshapes.php new file mode 100644 index 00000000000..c1de565145c --- /dev/null +++ b/question/type/ddmarker/simpletest/testshapes.php @@ -0,0 +1,80 @@ +. + +/** + * Unit tests for the drag-and-drop words shape code. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/type/ddmarker/shapes.php'); + + +/** + * Unit tests for shape code + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_shapes_test extends UnitTestCase { + + public function test_polygon_hit_test() { + $shape = new qtype_ddmarker_shape_polygon('10,10;20,10;20,20;10,20'); + $this->assertTrue($shape->is_point_in_shape(array(15,15))); + $this->assertFalse($shape->is_point_in_shape(array(5,5))); + $this->assertFalse($shape->is_point_in_shape(array(5,15))); + $this->assertFalse($shape->is_point_in_shape(array(15,25))); + $this->assertFalse($shape->is_point_in_shape(array(25,15))); + $this->assertTrue($shape->is_point_in_shape(array(11,11))); + $this->assertTrue($shape->is_point_in_shape(array(19,19))); + + //should accept closed polygon coords or unclosed and it will model a closed polygon + $shape = new qtype_ddmarker_shape_polygon('10,10;20,10;20,20;10,20;10,10'); + $this->assertTrue($shape->is_point_in_shape(array(15,15))); + $this->assertFalse($shape->is_point_in_shape(array(5,5))); + $this->assertFalse($shape->is_point_in_shape(array(5,15))); + $this->assertFalse($shape->is_point_in_shape(array(15,25))); + $this->assertFalse($shape->is_point_in_shape(array(25,15))); + $this->assertTrue($shape->is_point_in_shape(array(11,11))); + $this->assertTrue($shape->is_point_in_shape(array(19,19))); + + $shape = new qtype_ddmarker_shape_polygon('10,10;15,5;20,10;20,20;10,20'); + $this->assertTrue($shape->is_point_in_shape(array(15,15))); + $this->assertFalse($shape->is_point_in_shape(array(5,5))); + $this->assertFalse($shape->is_point_in_shape(array(5,15))); + $this->assertFalse($shape->is_point_in_shape(array(15,25))); + $this->assertFalse($shape->is_point_in_shape(array(25,15))); + $this->assertTrue($shape->is_point_in_shape(array(11,11))); + $this->assertTrue($shape->is_point_in_shape(array(19,19))); + $this->assertTrue($shape->is_point_in_shape(array(15,9))); + $this->assertTrue($shape->is_point_in_shape(array(15,10))); + + $shape = new qtype_ddmarker_shape_polygon('15,5;20,10;20,20;10,20;10,10'); + $this->assertTrue($shape->is_point_in_shape(array(15,10))); + + $shape = new qtype_ddmarker_shape_polygon('15,5;20,10;20,20;10,20;10,10'); + $this->assertFalse($shape->is_point_in_shape(array(25,10))); + + $shape = new qtype_ddmarker_shape_polygon('0,0;500,0;600,1000;0,1200;10,10'); + $this->assertTrue($shape->is_point_in_shape(array(25,10))); + } +} diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index cec79e79218..67e407be496 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -144,7 +144,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { drag.setData('group', group); drag.setData('choice', choice); - }, draggable_for_form : function (drag) { var dd = new Y.DD.Drag({ From 99c6577a94b21240556f8f014439ef2b14b27294 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 24 Oct 2011 11:43:55 +0700 Subject: [PATCH 016/178] MDL-47494 ddmarker: NOBUG added some unit tests for shape hit checks and fixed bugs I found. --- question/type/ddmarker/shapes.php | 15 ++++++------ .../type/ddmarker/simpletest/testshapes.php | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index e15519fe872..9b38c8f04fd 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -21,7 +21,8 @@ * @subpackage ddmarker * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */abstract class qtype_ddmarker_shape { + */ +abstract class qtype_ddmarker_shape { public function __construct($coordsstring) { } @@ -46,13 +47,13 @@ } protected function is_point_in_bounding_box($pointxy, $xleftytop, $xrightybottom) { - if ($pointxy[0] < $xleftytop[0]) { + if ($pointxy[0] <= $xleftytop[0]) { return false; - } else if ($pointxy[0] > $xrightybottom[0]) { + } else if ($pointxy[0] >= $xrightybottom[0]) { return false; - } else if ($pointxy[1] < $xleftytop[1]) { + } else if ($pointxy[1] <= $xleftytop[1]) { return false; - } else if ($pointxy[1] > $xrightybottom[1]) { + } else if ($pointxy[1] >= $xrightybottom[1]) { return false; } return true; @@ -101,8 +102,8 @@ class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { return array($this->xleft + $this->width, $this->ytop + $this->height); } public function is_point_in_shape($xy) { - $this->is_point_in_bounding_box($xy, array($this->xleft, $this->ytop), - array($this->ytop + $this->height, $this->xleft + $this->width)); + return $this->is_point_in_bounding_box($xy, array($this->xleft, $this->ytop), + array($this->xleft + $this->width, $this->ytop + $this->height)); } } class qtype_ddmarker_shape_circle extends qtype_ddmarker_shape { diff --git a/question/type/ddmarker/simpletest/testshapes.php b/question/type/ddmarker/simpletest/testshapes.php index c1de565145c..42f8a5bfa0b 100644 --- a/question/type/ddmarker/simpletest/testshapes.php +++ b/question/type/ddmarker/simpletest/testshapes.php @@ -77,4 +77,28 @@ class qtype_ddmarker_shapes_test extends UnitTestCase { $shape = new qtype_ddmarker_shape_polygon('0,0;500,0;600,1000;0,1200;10,10'); $this->assertTrue($shape->is_point_in_shape(array(25,10))); } + + public function test_circle_hit_test() { + $shape = new qtype_ddmarker_shape_circle('10,10;10'); + $this->assertTrue($shape->is_point_in_shape(array(19,10))); + $this->assertFalse($shape->is_point_in_shape(array(20,10))); + $this->assertTrue($shape->is_point_in_shape(array(10,1))); + $this->assertFalse($shape->is_point_in_shape(array(15,25))); + $this->assertFalse($shape->is_point_in_shape(array(25,15))); + $this->assertTrue($shape->is_point_in_shape(array(11,11))); + $this->assertTrue($shape->is_point_in_shape(array(1,10))); + $this->assertTrue($shape->is_point_in_shape(array(17,17))); + $this->assertTrue($shape->is_point_in_shape(array(3,3))); + $this->assertFalse($shape->is_point_in_shape(array(2,2))); + } + public function test_rectangle_hit_test() { + $shape = new qtype_ddmarker_shape_rectangle('1000,4000;500,400'); + $this->assertTrue($shape->is_point_in_shape(array(1001,4001))); + $this->assertFalse($shape->is_point_in_shape(array(1000,4000))); + $this->assertFalse($shape->is_point_in_shape(array(501,3601))); + $this->assertTrue($shape->is_point_in_shape(array(1499,4399))); + $this->assertFalse($shape->is_point_in_shape(array(25,15))); + $this->assertTrue($shape->is_point_in_shape(array(1001,4399))); + $this->assertTrue($shape->is_point_in_shape(array(1499,4001))); + } } From 64b3d6e833c744a99a3d8d8d21fc3656157b67df Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 29 Oct 2011 12:19:21 +0700 Subject: [PATCH 017/178] MDL-47494 ddmarker: NOBUG further developments --- question/type/ddmarker/edit_ddmarker_form.php | 11 ++-- question/type/ddmarker/shapes.php | 19 +++++-- question/type/ddmarker/styles.css | 8 +++ question/type/ddmarker/yui/form/form.js | 50 ++++++++----------- 4 files changed, 52 insertions(+), 36 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index 9cd1a2e043f..7d9ab7963af 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -15,6 +15,7 @@ // along with Moodle. If not, see . require_once($CFG->dirroot.'/question/type/ddimageortext/edit_ddtoimage_form_base.php'); +require_once($CFG->dirroot.'/question/type/ddmarker/shapes.php'); /** * Defines the editing form for the drag-and-drop images onto images question type. @@ -92,7 +93,10 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { } protected function shapes() { - return array('circle', 'rectangle', 'polygon'); + return preg_grep('!^qtype_ddmarker_shape_!', get_declared_classes()); + return array('qtype_ddmarker_shape_circle', + 'qtype_ddmarker_shape_rectangle', + 'qtype_ddmarker_shape_polygon'); } @@ -100,8 +104,9 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { $dropzoneitem = array(); $grouparray = array(); - foreach ($this->shapes as $shape) { - $shapearray[$shape] = get_string('shape_'.$shape, 'qtype_ddmarker'); + $shapearray = array(); + foreach ($this->shapes() as $shape) { + $shapearray[$shape::name()] = $shape::human_readable_name(); } $grouparray[] = $mform->createElement('select', 'shape', get_string('marker', 'qtype_ddmarker'), $shapearray); diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index 9b38c8f04fd..97c6faf0483 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -64,6 +64,14 @@ abstract class qtype_ddmarker_shape { * @return boolean is point inside shape */ abstract public function is_point_in_shape($xy); + + public static function name() { + return substr(get_called_class(), 21); + } + + public static function human_readable_name() { + return get_string('shape_'.self::name(), 'qtype_ddmarker'); + } } class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { protected $width; @@ -71,15 +79,16 @@ class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { protected $xleft; protected $ytop; public function __construct($coordsstring) { - $coordstring = preg_replace('!\s!', '', $coordsstring); - $coordsstringparts = explode(';', $coordsstring); + $coordstring = preg_replace('!^\s*!', '', $coordsstring); + $coordstring = preg_replace('!\s*$!', '', $coordsstring); + $coordsstringparts = preg_split('!\s*!', $coordsstring); if (count($coordsstringparts) > 2) { - $this->error = 'toomanysemicolons'; + $this->error = 'rectangletoomanyspaces'; } else if (count($coordsstringparts) < 2) { - $this->error = 'nosemicolons'; + $this->error = 'rectanglenospaces'; } else { $xy = explode(',', $coordsstringparts[0]); - $widthheightparts = explode(',', $coordsstringparts[1]); + $widthheightparts = explode('x', $coordsstringparts[1]); if (count($xy) !== 2) { $this->error = 'unrecognisedxypart'; } else if (count($widthheightparts) !== 2) { diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index d907a5572f5..895858e32af 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -76,7 +76,15 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa } .que.ddmarker div.markertexts span.markertext, form.mform fieldset#previewareaheader div.markertexts span.markertext { + z-index: 2; border: 1px solid black; background-color: white; padding: 5px; + position:absolute; + display: -moz-inline-stack; + display:inline-block; + height: auto; + width: auto; + zoom: 1; + *display: inline; } \ No newline at end of file diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index e24fb57a38f..88ffd6dd186 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -11,16 +11,23 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { initializer : function(params) { this.fp = this.file_pickers(); - Y.one(this.get('topnode')).append('
'+ - '
'); + Y.one(this.get('topnode')).append( + '
'+ + '
'+ + '
'+ + '
'+ + '
'); this.doc = this.doc_structure(this); - this.draw_dd_area(); - }, - - draw_dd_area : function() { - var bgimageurl = this.fp.file('bgimage').href; this.stop_selector_events(); this.set_options_for_drag_item_selectors(); + this.setup_form_events(); + Y.later(500, this, this.update_drop_zones, [], true); + Y.after(this.load_bg_image, M.form_filepicker, 'callback', this); + this.load_bg_image(); + }, + + load_bg_image : function() { + var bgimageurl = this.fp.file('bgimage').href; if (bgimageurl !== null) { this.doc.load_bg_img(bgimageurl); @@ -33,25 +40,12 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { }); this.afterimageloaddone = false; - this.attach_on_load_handlers(this.doc.bg_img(), 'bgimage'); - } else { - this.setup_form_events(); + this.doc.bg_img().on('load', this.constrain_image_size, this); } }, - - attach_on_load_handlers : function (imgorimgs, imgtype) { - imgorimgs.on('load', this.constrain_image_size, this, imgtype); - imgorimgs.after('load', this.poll_for_image_load, this, - true, 0, this.after_all_images_loaded); - }, - after_all_images_loaded : function () { - Y.later(500, this, this.update_drop_zones, [], true); - this.setup_form_events(); - }, - - constrain_image_size : function (e, imagetype) { - var maxsize = this.get('maxsizes')[imagetype]; + constrain_image_size : function (e) { + var maxsize = this.get('maxsizes')['bgimage']; var reduceby = Math.max(e.target.get('width') / maxsize.width, e.target.get('height') / maxsize.height); if (reduceby > 1) { @@ -101,9 +95,9 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { var coords = this.get_coords(dropzoneno); var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); if (coordsparts && coordsparts.length === 4) { - var xy = [coordsparts[1] - coordsparts[3], coordsparts[2] - coordsparts[3]]; + var xy = [+coordsparts[1] - coordsparts[3], +coordsparts[2] - coordsparts[3]]; if (this.coords_in_img(xy)) { - var widthheight = [coordsparts[3]*2, coordsparts[3]*2]; + var widthheight = [+coordsparts[3]*2, +coordsparts[3]*2]; var shape = this.graphics.addShape({ type: 'circle', width: widthheight[0], @@ -127,8 +121,8 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { var coords = this.get_coords(dropzoneno); var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); if (coordsparts && coordsparts.length === 5) { - var xy = [coordsparts[1], coordsparts[2]]; - var widthheight = [coordsparts[3], coordsparts[4]]; + var xy = [+coordsparts[1], +coordsparts[2]]; + var widthheight = [+coordsparts[3], +coordsparts[4]]; if (this.coords_in_img([xy[0]+widthheight[0], xy[1]+widthheight[1]])) { var shape = this.graphics.addShape({ type: 'rect', @@ -217,7 +211,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { for (var i=0; i < this.form.get_form_value('noitems', []); i++) { var label = this.get_marker_text(i); if (label !== "") { - dragitemsoptions[i+1] = label; + dragitemsoptions[i] = label; } } var selectedvalues = []; From 4b549f07a5cd9a711618aedfac458e9ed83cdc47 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 29 Oct 2011 12:33:49 +0700 Subject: [PATCH 018/178] MDL-47494 ddmarker: NOBUG centering marker text. --- question/type/ddmarker/yui/form/form.js | 1 + 1 file changed, 1 insertion(+) diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 88ffd6dd186..2705dc70cb3 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -87,6 +87,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); markerspan.setStyle('opacity', '0.6'); + xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); markerspan.setXY(this.convert_to_window_xy(xyfortext)); } } From 3c023b916fa7147131feddb72d672a8b279623fa Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 31 Oct 2011 12:24:37 +0700 Subject: [PATCH 019/178] MDL-47494 ddmarker: NOBUG some styling of marker text spans --- question/type/ddmarker/styles.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 895858e32af..8bc409b3248 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -14,7 +14,7 @@ } .que.ddmarker div.draghome, form.mform fieldset#previewareaheader div.draghome { - border: 1px solid black; + border: 1px dashed black; cursor: move; background-color: #B0C4DE; display: -moz-inline-stack; @@ -77,7 +77,7 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa .que.ddmarker div.markertexts span.markertext, form.mform fieldset#previewareaheader div.markertexts span.markertext { z-index: 2; - border: 1px solid black; + border: 1px dashed black; background-color: white; padding: 5px; position:absolute; @@ -87,4 +87,8 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { width: auto; zoom: 1; *display: inline; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + -khtml-border-radius: 10px; + border-radius: 10px; } \ No newline at end of file From def5ae8de429448a986a3a16fc57c1a8a0b0be78 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 31 Oct 2011 12:31:07 +0700 Subject: [PATCH 020/178] MDL-47494 ddmarker: NOBUG further styling adjustments --- question/type/ddmarker/styles.css | 9 ++++----- question/type/ddmarker/yui/form/form.js | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 8bc409b3248..6ed073a8e69 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -77,7 +77,6 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa .que.ddmarker div.markertexts span.markertext, form.mform fieldset#previewareaheader div.markertexts span.markertext { z-index: 2; - border: 1px dashed black; background-color: white; padding: 5px; position:absolute; @@ -87,8 +86,8 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { width: auto; zoom: 1; *display: inline; - -moz-border-radius: 10px; - -webkit-border-radius: 10px; - -khtml-border-radius: 10px; - border-radius: 10px; + -moz-border-radius: 20px; + -webkit-border-radius: 20px; + -khtml-border-radius: 20px; + border-radius: 20px; } \ No newline at end of file diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 2705dc70cb3..306b8ebf5de 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -86,7 +86,8 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { markertext+''); } var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); - markerspan.setStyle('opacity', '0.6'); + markerspan.setStyle('opacity', '0.4'); + xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); markerspan.setXY(this.convert_to_window_xy(xyfortext)); } From b34bc9970563133202346004edd1cde6431c6411 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 31 Oct 2011 16:14:52 +0700 Subject: [PATCH 021/178] MDL-47494 ddmarker: NOBUG moved colours out of css style sheet and into js Need to be able to assign a colour to the fill of shapes displayed and this cannot be done with css. --- question/type/ddmarker/styles.css | 32 +++------------------- question/type/ddmarker/yui/dd/dd.js | 15 +++++++++++ question/type/ddmarker/yui/form/form.js | 36 ++++++++++++++++--------- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 6ed073a8e69..8e0a57b9888 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -25,30 +25,6 @@ *display: inline; } -.que.ddmarker .group1, form.mform fieldset#previewareaheader .group1 { - background-color: #FFFFFF; -} -.que.ddmarker .group2, form.mform fieldset#previewareaheader .group2 { - background-color: #B0C4DE; -} -.que.ddmarker .group3, form.mform fieldset#previewareaheader .group3 { - background-color: #DCDCDC; -} -.que.ddmarker .group4, form.mform fieldset#previewareaheader .group4 { - background-color: #D8BFD8; -} -.que.ddmarker .group5, form.mform fieldset#previewareaheader .group5 { - background-color: #87CEFA; -} -.que.ddmarker .group6, form.mform fieldset#previewareaheader .group6 { - background-color: #DAA520; -} -.que.ddmarker .group7, form.mform fieldset#previewareaheader .group7 { - background-color: #FFD700; -} -.que.ddmarker .group8, form.mform fieldset#previewareaheader .group8 { - background-color: #F0E68C; -} .que.ddmarker .drag, form.mform fieldset#previewareaheader .drag { border: 1px solid black; cursor: move; @@ -86,8 +62,8 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { width: auto; zoom: 1; *display: inline; - -moz-border-radius: 20px; - -webkit-border-radius: 20px; - -khtml-border-radius: 20px; - border-radius: 20px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + -khtml-border-radius: 10px; + border-radius: 10px; } \ No newline at end of file diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 67e407be496..3c07b901f86 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -171,6 +171,21 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }, + + colours : ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8', + '#87CEFA','#DAA520', '#FFD700', '#F0E68C'], + nextcolourindex : 0, + restart_colours : function () { + this.nextcolourindex = 0; + }, + get_next_colour : function () { + var colour = this.colours[this.nextcolourindex]; + this.nextcolourindex++; + if (this.nextcolourindex === this.colours.length) { + this.nextcolourindex = 0; + } + return colour; + }, update_padding_sizes_all : function () { for (var groupno = 1; groupno <= 8; groupno++) { diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 306b8ebf5de..78f8d7ed596 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -64,6 +64,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { if (this.graphics !== null) { this.graphics.destroy(); } + this.restart_colours(); this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"}); for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { this.update_drop_zone(i); @@ -74,26 +75,35 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { var markertext = this.get_marker_text(dragitemno); var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']); var drawfunc = 'draw_shape_'+shape; + var colourfordropzone = this.get_next_colour(); + Y.one('input#id_drops_'+dropzoneno+'_coords') + .setStyle('background-color', colourfordropzone); if (this[drawfunc] instanceof Function){ - var xyfortext = this[drawfunc](dropzoneno); + var xyfortext = this[drawfunc](dropzoneno, colourfordropzone); if (xyfortext !== null) { var classnames = 'markertext markertext' + dropzoneno; var existingdiv = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); if (existingdiv) { - existingdiv.setContent(markertext); + if (markertext !== '') { + existingdiv.setContent(markertext); + } else { + existingdiv.remove(true); + } } else { Y.one('div.ddarea div.markertexts').append('' + markertext+''); } var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); - markerspan.setStyle('opacity', '0.4'); - xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); - xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); - markerspan.setXY(this.convert_to_window_xy(xyfortext)); + if (markerspan !== null) { + markerspan.setStyle('opacity', '0.4'); + xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); + xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); + markerspan.setXY(this.convert_to_window_xy(xyfortext)); + } } } }, - draw_shape_circle : function (dropzoneno) { + draw_shape_circle : function (dropzoneno, colourfordropzone) { var coords = this.get_coords(dropzoneno); var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); if (coordsparts && coordsparts.length === 4) { @@ -105,7 +115,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { width: widthheight[0], height: widthheight[1], fill: { - color: "#0000ff", + color: colourfordropzone, opacity : "0.5" }, stroke: { @@ -119,7 +129,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } return null; }, - draw_shape_rectangle : function (dropzoneno) { + draw_shape_rectangle : function (dropzoneno, colourfordropzone) { var coords = this.get_coords(dropzoneno); var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); if (coordsparts && coordsparts.length === 5) { @@ -131,7 +141,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { width: widthheight[0], height: widthheight[1], fill: { - color: "#0000ff", + color: colourfordropzone, opacity : "0.5" }, stroke: { @@ -146,7 +156,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { return null; }, - draw_shape_polygon : function (dropzoneno) { + draw_shape_polygon : function (dropzoneno, colourfordropzone) { var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); var coordsparts = coords.split(';'); var xy = []; @@ -161,10 +171,10 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { type: "path", stroke: { weight: 1, - color: "#000" + color: "black" }, fill: { - color: "#f00", + color: colourfordropzone, opacity : "0.5" } }); From ddbea1ae6b67493d5171a34c7edf0f4940330c9f Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 2 Nov 2011 13:49:54 +0700 Subject: [PATCH 022/178] MDL-47494 ddmarker: NOBUG have renderer output correct html and fix file access to bgimage --- question/type/ddmarker/lib.php | 3 +- question/type/ddmarker/question.php | 37 +++++++++-------- question/type/ddmarker/questiontype.php | 7 ++++ question/type/ddmarker/renderer.php | 54 +++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/question/type/ddmarker/lib.php b/question/type/ddmarker/lib.php index d648284fb54..7d2cb6d9bc0 100644 --- a/question/type/ddmarker/lib.php +++ b/question/type/ddmarker/lib.php @@ -34,6 +34,5 @@ defined('MOODLE_INTERNAL') || die(); function qtype_ddmarker_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) { global $CFG; require_once($CFG->libdir . '/questionlib.php'); - question_pluginfile($course, $context, 'qtype_ddmarker', - $filearea, $args, $forcedownload); + question_pluginfile($course, $context, 'qtype_ddmarker', $filearea, $args, $forcedownload); } diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 83fe2622296..37891593a69 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -37,10 +37,23 @@ require_once($CFG->dirroot . '/question/type/ddmarker/shapes.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { - + public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { + if ($filearea == 'bgimage') { + $validfilearea = true; + } else { + $validfilearea = false; + } + if ($component == 'qtype_ddmarker' && $validfilearea) { + $question = $qa->get_question(); + $itemid = reset($args); + return $itemid == $question->id; + } else { + return parent::check_file_access($qa, $options, $component, + $filearea, $args, $forcedownload); + } + } } - /** * Represents one of the choices (draggable markers). * @@ -48,13 +61,13 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_ddmarker_drag_item { - public $id; public $text; public $no; + public $infinite; - public function __construct($label, $no, $id = 0) { - $this->id = $id; + public function __construct($label, $no, $infinite) { $this->text = $label; + $this->infinite = $infinite; $this->no = $no; } public function choice_group() { @@ -72,26 +85,18 @@ class qtype_ddmarker_drag_item { * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_ddmarker_drop_zone { + public $group = 1; public $no; - public $text; public $shape; public $coords; - public function __construct($label, $no, $shape, $coords) { + public function __construct($no, $shape, $coords) { $this->no = $no; - $this->text = $label; $this->shape = $shape; $this->coords = $coords; } public function summarise() { - if (trim($this->text) != '') { - $summariseplace = - get_string('summariseplace', 'qtype_ddmarker', $this); - } else { - $summariseplace = - get_string('summariseplaceno', 'qtype_ddmarker', $this->no); - } - return $summariseplace; + return get_string('summariseplaceno', 'qtype_ddmarker', $this->no); } } diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 2fe96899764..4abd57186b4 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -86,6 +86,7 @@ class qtype_ddmarker extends qtype_ddtoimage_base { public function requires_qtypes() { return array_merge(parent::requires_qtypes(), array('ddimageortext')); } + public function save_question_options($formdata) { global $DB, $USER; $context = $formdata->context; @@ -230,5 +231,11 @@ class qtype_ddmarker extends qtype_ddtoimage_base { protected function make_hint($hint) { return question_hint_ddmarker::load_from_record($hint); } + protected function make_choice($dragdata) { + return new qtype_ddmarker_drag_item($dragdata->label, $dragdata->no, $dragdata->infinite); + } + protected function make_place($dropdata) { + return new qtype_ddmarker_drop_zone($dropdata->no, $dropdata->shape, $dropdata->coords); + } } diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index f7cf56007f2..8e7f5c29d4f 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -36,5 +36,59 @@ require_once($CFG->dirroot . '/question/type/ddimageortext/rendererbase.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { + public function formulation_and_controls(question_attempt $qa, + question_display_options $options) { + global $PAGE; + $question = $qa->get_question(); + $response = $qa->get_last_qt_data(); + + $questiontext = $question->format_questiontext($qa); + + $output = html_writer::tag('div', $questiontext, array('class' => 'qtext')); + + $bgimage = self::get_url_for_image($qa, 'bgimage'); + + $img = html_writer::empty_tag('img', array('src'=>$bgimage, 'class'=>'dropbackground')); + $droparea = html_writer::tag('div', $img, array('class'=>'droparea')); + + $dragimagehomes = ''; + $orderedgroup = $question->get_ordered_choices(1); + foreach ($orderedgroup as $choiceno => $dragimage) { + $classes = array('draghome', + "dragitemhomes{$dragimage->no}", + "choice{$choiceno}"); + if ($dragimage->infinite) { + $classes[] = 'infinite'; + } + $dragimagehomes .= html_writer::tag('div', + $dragimage->text, + array('class'=>join(' ', $classes))); + } + $dragimagehomesdiv = html_writer::tag('div', $dragimagehomes); + + $dragitemsclass = 'dragitems'; + if ($options->readonly) { + $dragitemsclass .= ' readonly'; + } + $dragitems = html_writer::tag('div', $dragimagehomesdiv, array('class'=> $dragitemsclass)); + $dropzones = html_writer::empty_tag('div', array('class'=>'dropzones')); + $output .= html_writer::tag('div', $droparea.$dragitems.$dropzones, + array('class'=>'ddarea')); + $topnode = 'div#q'.$qa->get_slot().' div.ddarea'; + $params = array('drops' => $question->places, + 'topnode' => $topnode, + 'readonly' => $options->readonly); + + $PAGE->requires->yui_module('moodle-qtype_ddmarker-dd', + 'M.qtype_ddmarker.init_question', + array($params)); + + if ($qa->get_state() == question_state::$invalid) { + $output .= html_writer::nonempty_tag('div', + $question->get_validation_error($qa->get_last_qt_data()), + array('class' => 'validationerror')); + } + return $output; + } } From e354ce47c25c668ce5427972b5bc7a1317cae2d8 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 2 Nov 2011 15:56:46 +0700 Subject: [PATCH 023/178] MDL-47494 ddmarker: NOBUG adding target icon to drag items and some improvements to styles --- question/type/ddmarker/renderer.php | 16 +++++++++------- question/type/ddmarker/styles.css | 14 +++++++------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 8e7f5c29d4f..65649dd1c5d 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -38,7 +38,7 @@ require_once($CFG->dirroot . '/question/type/ddimageortext/rendererbase.php'); class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { public function formulation_and_controls(question_attempt $qa, question_display_options $options) { - global $PAGE; + global $PAGE, $OUTPUT; $question = $qa->get_question(); $response = $qa->get_last_qt_data(); @@ -54,6 +54,7 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $dragimagehomes = ''; $orderedgroup = $question->get_ordered_choices(1); + $componentname = $question->qtype->plugin_name(); foreach ($orderedgroup as $choiceno => $dragimage) { $classes = array('draghome', "dragitemhomes{$dragimage->no}", @@ -61,9 +62,12 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { if ($dragimage->infinite) { $classes[] = 'infinite'; } - $dragimagehomes .= html_writer::tag('div', - $dragimage->text, - array('class'=>join(' ', $classes))); + $targeticonhtml = + $OUTPUT->pix_icon('crosshairs', '', $componentname, array('class'=> 'target')); + + $dragimagehomes .= html_writer::tag('span', + $targeticonhtml.$dragimage->text, + array('class'=>join(' ', $classes))); } $dragimagehomesdiv = html_writer::tag('div', $dragimagehomes); @@ -72,9 +76,7 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $dragitemsclass .= ' readonly'; } $dragitems = html_writer::tag('div', $dragimagehomesdiv, array('class'=> $dragitemsclass)); - $dropzones = html_writer::empty_tag('div', array('class'=>'dropzones')); - $output .= html_writer::tag('div', $droparea.$dragitems.$dropzones, - array('class'=>'ddarea')); + $output .= html_writer::tag('div', $droparea.$dragitems, array('class'=>'ddarea')); $topnode = 'div#q'.$qa->get_slot().' div.ddarea'; $params = array('drops' => $question->places, 'topnode' => $topnode, diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 8e0a57b9888..903dc178c00 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -39,23 +39,19 @@ .que.ddmarker .dropbackground, form.mform fieldset#previewareaheader .dropbackground { margin:0 auto; } -.que.ddmarker .dropzone { - border: 1px solid black; - position: absolute; - z-index: 1; -} + .que.ddmarker div.dragitems div.draghome, .que.ddmarker div.dragitems div.drag, form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewareaheader div.drag { font:13px/1.231 arial,helvetica,clean,sans-serif; *font-size:small; /* for IE */ *font:x-small; /* for IE in quirks mode */ } -.que.ddmarker div.markertexts span.markertext, +.que.ddmarker span.draghome, +.que.ddmarker div.markertexts span.drag, form.mform fieldset#previewareaheader div.markertexts span.markertext { z-index: 2; background-color: white; padding: 5px; - position:absolute; display: -moz-inline-stack; display:inline-block; height: auto; @@ -66,4 +62,8 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -webkit-border-radius: 10px; -khtml-border-radius: 10px; border-radius: 10px; +} +.que.ddmarker div.dragitems span.drag, +form.mform fieldset#previewareaheader div.markertexts span.markertext { + position:absolute; } \ No newline at end of file From a98db2a9299b482bed686b637f129e44eb84a069 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 3 Nov 2011 16:22:56 +0700 Subject: [PATCH 024/178] MDL-47494 ddmarker: NOBUG js for question renderer - cloning drags and storing drop coords --- question/type/ddmarker/question.php | 15 ++ question/type/ddmarker/renderer.php | 28 ++- question/type/ddmarker/styles.css | 6 +- question/type/ddmarker/yui/dd/dd.js | 323 ++++++++++------------------ 4 files changed, 149 insertions(+), 223 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 37891593a69..790d8f3b5ef 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -52,6 +52,21 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { $filearea, $args, $forcedownload); } } + /** + * @param int $key stem number + * @return string the question-type variable name. + */ + public function choice($choice) { + return 'c' . $choice; + } + + public function get_expected_data() { + $vars = array(); + foreach ($this->choices as $choice => $notused) { + $vars[$this->choice($choice)] = PARAM_INTEGER; + } + return $vars; + } } /** diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 65649dd1c5d..12049a2666c 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -52,33 +52,32 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $img = html_writer::empty_tag('img', array('src'=>$bgimage, 'class'=>'dropbackground')); $droparea = html_writer::tag('div', $img, array('class'=>'droparea')); - $dragimagehomes = ''; + $draghomes = ''; $orderedgroup = $question->get_ordered_choices(1); $componentname = $question->qtype->plugin_name(); - foreach ($orderedgroup as $choiceno => $dragimage) { + $hiddenfields = ''; + foreach ($orderedgroup as $choiceno => $drag) { $classes = array('draghome', - "dragitemhomes{$dragimage->no}", "choice{$choiceno}"); - if ($dragimage->infinite) { + if ($drag->infinite) { $classes[] = 'infinite'; } $targeticonhtml = $OUTPUT->pix_icon('crosshairs', '', $componentname, array('class'=> 'target')); - $dragimagehomes .= html_writer::tag('span', - $targeticonhtml.$dragimage->text, - array('class'=>join(' ', $classes))); + $draghomesattrs = array('class'=>join(' ', $classes)); + $draghomes .= html_writer::tag('span', $targeticonhtml.$drag->text, $draghomesattrs); + $hiddenfields .= $this->hidden_field_choice($qa, $choiceno); } - $dragimagehomesdiv = html_writer::tag('div', $dragimagehomes); $dragitemsclass = 'dragitems'; if ($options->readonly) { $dragitemsclass .= ' readonly'; } - $dragitems = html_writer::tag('div', $dragimagehomesdiv, array('class'=> $dragitemsclass)); + $dragitems = html_writer::tag('div', $draghomes, array('class'=> $dragitemsclass)); $output .= html_writer::tag('div', $droparea.$dragitems, array('class'=>'ddarea')); - $topnode = 'div#q'.$qa->get_slot().' div.ddarea'; - $params = array('drops' => $question->places, + $topnode = 'div#q'.$qa->get_slot(); + $params = array('inputids' => $question->places, 'topnode' => $topnode, 'readonly' => $options->readonly); @@ -91,6 +90,13 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $question->get_validation_error($qa->get_last_qt_data()), array('class' => 'validationerror')); } + $output .= html_writer::tag('div', $hiddenfields, array('class'=>'ddform')); return $output; } + protected function hidden_field_choice(question_attempt $qa, $choiceno, $value = null) { + $varname = 'c'.$choiceno; + $classes = array('choices', 'choice'.$choiceno); + list(,$html) = $this->hidden_field_for_qt_var($qa, $varname, null, $classes); + return $html; + } } diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 903dc178c00..f73344ffab6 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -25,8 +25,7 @@ *display: inline; } -.que.ddmarker .drag, form.mform fieldset#previewareaheader .drag { - border: 1px solid black; +.que.ddmarker .dragitems .drag { cursor: move; z-index: 2; } @@ -47,10 +46,11 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa *font:x-small; /* for IE in quirks mode */ } .que.ddmarker span.draghome, -.que.ddmarker div.markertexts span.drag, +.que.ddmarker div.dragitems span.drag, form.mform fieldset#previewareaheader div.markertexts span.markertext { z-index: 2; background-color: white; + border-style : none; padding: 5px; display: -moz-inline-stack; display:inline-block; diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 3c07b901f86..803bb34d68a 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -47,27 +47,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { top_node : function() { return topnode; }, - drag_items : function() { - return dragitemsarea.all('.drag'); - }, - drop_zones : function() { - return topnode.all('div.dropzones div.dropzone'); - }, - drop_zone_group : function(groupno) { - return topnode.all('div.dropzones div.group' + groupno); - }, - drag_items_cloned_from : function(dragitemno) { - return dragitemsarea.all('.dragitems'+dragitemno); - }, - drag_item : function(draginstanceno) { - return dragitemsarea.one('.draginstance' + draginstanceno); - }, - drag_items_in_group : function(groupno) { - return dragitemsarea.all('.drag.group' + groupno); - }, - drag_item_homes : function() { - return dragitemsarea.all('.draghome'); - }, bg_img : function() { return topnode.one('.dropbackground'); }, @@ -75,34 +54,17 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { dropbgarea.setContent(''); this.bg_img().on('load', this.on_image_load, this, 'bg_image'); }, - add_or_update_drag_item_home : function (dragitemno, url, alt, group) { - var oldhome = this.drag_item_home(dragitemno); - var classes = 'draghome dragitemhomes'+dragitemno+' group'+group; - var imghtml = ''+alt+''; - var divhtml = '
'+alt+'
'; - if (oldhome === null) { - if (url) { - dragitemsarea.append(imghtml); - } else if (alt !== '') { - dragitemsarea.append(divhtml); - } - } else { - if (url) { - dragitemsarea.insert(imghtml, oldhome); - } else if (alt !== '') { - dragitemsarea.insert(divhtml, oldhome); - } - oldhome.remove(true); - } - var newlycreated = dragitemsarea.one('.dragitemhomes'+dragitemno); - if (newlycreated !== null) { - newlycreated.setData('groupno', group); - newlycreated.setData('dragitemno', dragitemno); - } + drag_items : function() { + return dragitemsarea.all('.drag'); }, - drag_item_home : function (dragitemno) { - return dragitemsarea.one('.dragitemhomes' + dragitemno); + drag_item : function(draginstanceno) { + return dragitemsarea.one('.draginstance' + draginstanceno); + }, + drag_item_home : function (choiceno) { + return dragitemsarea.one('span.draghome.choice' + choiceno); + }, + drag_item_homes : function() { + return dragitemsarea.all('span.draghome'); }, get_classname_numeric_suffix : function(node, prefix) { var classes = node.getAttribute('class'); @@ -119,54 +81,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } throw 'Prefix "'+prefix+'" not found in class names.'; }, - clone_new_drag_item : function (draginstanceno, dragitemno) { - var draghome = this.drag_item_home(dragitemno); - if (draghome === null) { - return null; - } - var drag = draghome.cloneNode(true); - drag.removeClass('dragitemhomes' + dragitemno); - drag.addClass('dragitems' + dragitemno); - drag.addClass('draginstance' + draginstanceno); - drag.removeClass('draghome'); - drag.addClass('drag'); - drag.setStyles({'visibility': 'visible', 'position' : 'absolute'}); - drag.setData('draginstanceno', draginstanceno); - drag.setData('dragitemno', dragitemno); - draghome.get('parentNode').appendChild(drag); - return drag; - }, - draggable_for_question : function (drag, group, choice) { - var dd = new Y.DD.Drag({ - node: drag, - dragMode: 'intersect' - }).plug(Y.Plugin.DDConstrained, {constrain2node: topnode}); - - drag.setData('group', group); - drag.setData('choice', choice); - }, - draggable_for_form : function (drag) { - var dd = new Y.DD.Drag({ - node: drag, - dragMode: 'intersect' - }).plug(Y.Plugin.DDConstrained, {constrain2node: topnode}); - dd.on('drag:end', function(e) { - var dragnode = e.target.get('node'); - var draginstanceno = dragnode.getData('draginstanceno'); - var gooddrop = dragnode.getData('gooddrop'); - var endxy; - - if (!gooddrop) { - mainobj.reset_drag_xy(draginstanceno); - } else { - mainobj.set_drag_xy(draginstanceno, [e.pageX, e.pageY]); - } - }, this); - dd.on('drag:start', function(e) { - var drag = e.target; - drag.get('node').setData('gooddrop', false); - }, this); - + input_for_choice : function (choiceno) { + return topnode.one('input.choice'+choiceno); } } @@ -186,31 +102,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } return colour; }, - - update_padding_sizes_all : function () { - for (var groupno = 1; groupno <= 8; groupno++) { - this.update_padding_size_for_group(groupno); - } - }, - update_padding_size_for_group : function (groupno) { - var groupitems = this.doc.top_node().all('.draghome.group'+groupno); - if (groupitems.size() !== 0) { - var maxwidth = 0; - var maxheight = 0; - groupitems.each(function(item){ - maxwidth = Math.max(maxwidth, item.get('clientWidth')); - maxheight = Math.max(maxheight, item.get('clientHeight')); - }, this); - groupitems.each(function(item) { - var margintopbottom = Math.round((10 + maxheight - item.get('clientHeight')) / 2); - var marginleftright = Math.round((10 + maxwidth - item.get('clientWidth')) / 2); - item.setStyle('padding', margintopbottom+'px '+marginleftright+'px ' - +margintopbottom+'px '+marginleftright+'px'); - }, this); - this.doc.drop_zone_group(groupno).setStyles({'width': maxwidth + 10, - 'height': maxheight + 10}); - } - }, convert_to_window_xy : function (bgimgxy) { return [+bgimgxy[0] + this.doc.bg_img().getX() + 1, +bgimgxy[1] + this.doc.bg_img().getY() + 1]; @@ -241,44 +132,84 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { false, 0, this.create_all_drag_and_drops); this.doc.drag_item_homes().after('load', this.poll_for_image_load, this, false, 0, this.create_all_drag_and_drops); - Y.later(500, this, this.reposition_drags_for_question, [], true); }, create_all_drag_and_drops : function () { - this.init_drops(); - this.update_padding_sizes_all(); - var i = 0; - this.doc.drag_item_homes().each(function(dragitemhome){ - var dragitemno = - +this.doc.get_classname_numeric_suffix(dragitemhome, 'dragitemhomes'); - var choice = +this.doc.get_classname_numeric_suffix(dragitemhome, 'choice'); - var group = +this.doc.get_classname_numeric_suffix(dragitemhome, 'group') - var groupsize = this.doc.drop_zone_group(group).size(); - var dragnode = this.doc.clone_new_drag_item(i, dragitemno); - i++; - if (!this.get('readonly')) { - this.doc.draggable_for_question(dragnode, group, choice); - } - if (dragnode.hasClass('infinite')) { - var dragstocreate = groupsize - 1; - while (dragstocreate > 0) { - dragnode = this.doc.clone_new_drag_item(i, dragitemno); - i++; - if (!this.get('readonly')) { - this.doc.draggable_for_question(dragnode, group, choice); - } - dragstocreate--; - } + var drop = new Y.DD.Drop({ + node: this.doc.bg_img() + }); + //Listen for a drop:hit on the background image + drop.on('drop:hit', function(e) { + e.drag.get('node').setData('droppedonbgimg', true); + }); + this.doc.drag_item_homes().each(this.clone_new_drag_item, this); + this.reposition_drags(); + Y.later(500, this, this.reposition_drags, [], true); +// if (!this.get('readonly')) { +// this.doc.drags().set('tabIndex', 0); +// this.doc.drags().each( +// function(v){ +// v.on('dragchange', this.drop_zone_key_press, this); +// } +// , this); +// } + }, + clone_new_drag_item : function (draghome) { + var drag = draghome.cloneNode(true); + drag.removeClass('draghome'); + drag.addClass('drag'); + drag.setStyles({'visibility': 'visible', 'position': 'absolute', 'opacity': 0.5}); + drag.one('img.target').setStyle('opacity', 1); + draghome.get('parentNode').appendChild(drag); + if (!this.get('readonly')) { + this.draggable(drag); + } + }, + draggable : function (drag) { + var dd = new Y.DD.Drag({ + node: drag, + dragMode: 'intersect' + }).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()}); + dd.on('drag:end', function(e) { + var dragnode = e.target.get('node'); + var gooddrop = dragnode.getData('droppedonbgimg'); + var choiceno = this.get_choiceno_for_drag(drag); + + if (!gooddrop) { + this.reset_drag_xy(choiceno); + } else { + var xy = this.constrain_xy(drag, this.convert_to_bg_img_xy([e.pageX, e.pageY])); + this.set_drag_xy(choiceno, xy); } }, this); - this.reposition_drags_for_question(); - if (!this.get('readonly')) { - this.doc.drop_zones().set('tabIndex', 0); - this.doc.drop_zones().each( - function(v){ - v.on('dragchange', this.drop_zone_key_press, this); - } - , this); - } + dd.on('drag:start', function(e) { + var drag = e.target; + drag.get('node').setData('droppedonbgimg', false); + }, this); + + }, + set_drag_xy : function (choiceno, xy) { + this.set_form_value(choiceno, Math.round(xy[0])+','+Math.round(xy[1])); + }, + reset_drag_xy : function (choiceno) { + var choiceno = this.get_choiceno_for_drag(drag); + this.set_form_value(choiceno, ''); + }, + set_form_value : function (choiceno, value) { + this.doc.input_for_choice(choiceno).set('value', value); + }, + //make sure xy value is not out of bounds of bg image + constrain_xy : function (drag, bgimgxy) { + var xleftconstrained = + Math.min(bgimgxy[0], this.doc.bg_img().get('width') - drag.get('offsetWidth')); + var ytopconstrained = + Math.min(bgimgxy[1], this.doc.bg_img().get('height') - drag.get('offsetHeight')); + xleftconstrained = Math.max(xleftconstrained, 0); + ytopconstrained = Math.max(ytopconstrained, 0); + return [xleftconstrained, ytopconstrained]; + }, + convert_to_bg_img_xy : function (windowxy) { + return [+windowxy[0] - this.doc.bg_img().getX()-1, + +windowxy[1] - this.doc.bg_img().getY()-1]; }, drop_zone_key_press : function (e) { switch (e.direction) { @@ -293,7 +224,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { break; } e.preventDefault(); - this.reposition_drags_for_question(); + this.reposition_drags(); }, place_next_drag_in : function (drop) { this.search_for_unplaced_drop_choice(drop, 1); @@ -346,40 +277,44 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { inputnode.set('value', ''); } }, - reposition_drags_for_question : function() { + reposition_drags : function() { this.doc.drag_items().removeClass('placed'); this.doc.drag_items().each (function (dragitem) { if (dragitem.dd !== undefined) { dragitem.dd.detachAll('drag:start'); } }, this); - this.doc.drop_zones().each(function(dropzone) { - var relativexy = dropzone.getData('xy'); - dropzone.setXY(this.convert_to_window_xy(relativexy)); - var inputcss = 'input#' + dropzone.getData('inputid'); - var input = this.doc.top_node().one(inputcss); - var choice = input.get('value'); - if (choice !== "") { - var dragitem = this.get_unplaced_choice_for_drop(choice, dropzone); - if (dragitem !== null) { - dragitem.setXY(dropzone.getXY()); - dragitem.addClass('placed'); - if (dragitem.dd !== undefined) { - dragitem.dd.once('drag:start', function (e, input) { - input.set('value', ''); - e.target.get('node').removeClass('placed'); - },this, input); - } - } - } - }, this); +// this.doc.drop_zones().each(function(dropzone) { +// var relativexy = dropzone.getData('xy'); +// dropzone.setXY(this.convert_to_window_xy(relativexy)); +// var inputcss = 'input#' + dropzone.getData('inputid'); +// var input = this.doc.top_node().one(inputcss); +// var choice = input.get('value'); +// if (choice !== "") { +// var dragitem = this.get_unplaced_choice_for_drop(choice, dropzone); +// if (dragitem !== null) { +// dragitem.setXY(dropzone.getXY()); +// dragitem.addClass('placed'); +// if (dragitem.dd !== undefined) { +// dragitem.dd.once('drag:start', function (e, input) { +// input.set('value', ''); +// e.target.get('node').removeClass('placed'); +// },this, input); +// } +// } +// } +// }, this); this.doc.drag_items().each(function(dragitem) { if (!dragitem.hasClass('placed') && !dragitem.hasClass('yui3-dd-dragging')) { - var dragitemhome = this.doc.drag_item_home(dragitem.getData('dragitemno')); + var choiceno = this.get_choiceno_for_drag(dragitem); + var dragitemhome = this.doc.drag_item_home(choiceno); dragitem.setXY(dragitemhome.getXY()); } }, this); }, + get_choiceno_for_drag : function(drag) { + return +this.doc.get_classname_numeric_suffix(drag, 'choice'); + }, get_choices_for_drop : function(choice, drop) { var group = drop.getData('group'); var dragitem = null; @@ -399,38 +334,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } })); return dragitem; - }, - init_drops : function () { - var dropareas = this.doc.top_node().one('div.dropzones'); - var groupnodes = {}; - for (var groupno =1; groupno <= 8; groupno++) { - var groupnode = Y.Node.create('
'); - dropareas.append(groupnode); - groupnodes[groupno] = groupnode; - } - for (var dropno in this.get('drops')){ - var drop = this.get('drops')[dropno]; - var nodeclass = 'dropzone group'+drop.group+' place'+dropno; - var title = drop.text.replace('"', '\"'); - var dropnodehtml = '
 
'; - var dropnode = Y.Node.create(dropnodehtml); - groupnodes[drop.group].append(dropnode); - dropnode.setStyles({'opacity': 0.5}); - dropnode.setData('xy', drop.xy); - dropnode.setData('place', dropno); - dropnode.setData('inputid', drop.fieldname.replace(':', '_')); - dropnode.setData('group', drop.group); - var dropdd = new Y.DD.Drop({ - node: dropnode}); - dropdd.on('drop:hit', function(e) { - var drag = e.drag.get('node'); - var drop = e.drop.get('node'); - if (+drop.getData('group') === drag.getData('group')){ - this.place_drag_in_drop(drag, drop); - } - }, this); - }; } + }, {NAME : DDMARKERQUESTIONNAME, ATTRS : {}}); Y.Event.define('dragchange', { From b1330fe88757ba9352a688ee0fef983e71d0c784 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 3 Nov 2011 17:54:40 +0700 Subject: [PATCH 025/178] MDL-47494 ddmarker: NOBUG some style improvements --- question/type/ddmarker/renderer.php | 4 +++- question/type/ddmarker/styles.css | 24 +++++++++++------------- question/type/ddmarker/yui/dd/dd.js | 10 +++------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 12049a2666c..c9f1dff875f 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -65,8 +65,10 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $targeticonhtml = $OUTPUT->pix_icon('crosshairs', '', $componentname, array('class'=> 'target')); + $markertextattrs = array('class'=> 'markertext'); + $markertext = html_writer::tag('span', $drag->text, $markertextattrs); $draghomesattrs = array('class'=>join(' ', $classes)); - $draghomes .= html_writer::tag('span', $targeticonhtml.$drag->text, $draghomesattrs); + $draghomes .= html_writer::tag('span', $targeticonhtml.$markertext, $draghomesattrs); $hiddenfields .= $this->hidden_field_choice($qa, $choiceno); } diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index f73344ffab6..8b91e28962b 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -8,8 +8,6 @@ } .que.ddmarker .draghome, form.mform fieldset#previewareaheader .draghome { - vertical-align: top; - margin: 5px; visibility : hidden; } @@ -24,12 +22,16 @@ zoom: 1; *display: inline; } - -.que.ddmarker .dragitems .drag { +.que.ddmarker .dragitems .dragitem, +.que.ddmarker .dragitems .draghome { + vertical-align: top; +} +.que.ddmarker .dragitems .dragitem { cursor: move; + position: absolute; z-index: 2; } -.que.ddmarker .dragitems.readonly .drag { +.que.ddmarker .dragitems.readonly .dragitem { cursor: auto; } .que.ddmarker div.ddarea, form.mform fieldset#previewareaheader div.ddarea { @@ -39,16 +41,16 @@ margin:0 auto; } -.que.ddmarker div.dragitems div.draghome, .que.ddmarker div.dragitems div.drag, +.que.ddmarker div.dragitems div.draghome, .que.ddmarker div.dragitems div.dragitem, form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewareaheader div.drag { font:13px/1.231 arial,helvetica,clean,sans-serif; *font-size:small; /* for IE */ *font:x-small; /* for IE in quirks mode */ } -.que.ddmarker span.draghome, -.que.ddmarker div.dragitems span.drag, +.que.ddmarker div.dragitems span.markertext, form.mform fieldset#previewareaheader div.markertexts span.markertext { - z-index: 2; + margin: 5px; + z-index: 2; background-color: white; border-style : none; padding: 5px; @@ -63,7 +65,3 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -khtml-border-radius: 10px; border-radius: 10px; } -.que.ddmarker div.dragitems span.drag, -form.mform fieldset#previewareaheader div.markertexts span.markertext { - position:absolute; -} \ No newline at end of file diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 803bb34d68a..840936acacd 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -55,10 +55,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { this.bg_img().on('load', this.on_image_load, this, 'bg_image'); }, drag_items : function() { - return dragitemsarea.all('.drag'); - }, - drag_item : function(draginstanceno) { - return dragitemsarea.one('.draginstance' + draginstanceno); + return dragitemsarea.all('.dragitem'); }, drag_item_home : function (choiceno) { return dragitemsarea.one('span.draghome.choice' + choiceno); @@ -156,9 +153,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { clone_new_drag_item : function (draghome) { var drag = draghome.cloneNode(true); drag.removeClass('draghome'); - drag.addClass('drag'); - drag.setStyles({'visibility': 'visible', 'position': 'absolute', 'opacity': 0.5}); - drag.one('img.target').setStyle('opacity', 1); + drag.addClass('dragitem'); + drag.one('span.markertext').setStyle('opacity', 0.5); draghome.get('parentNode').appendChild(drag); if (!this.get('readonly')) { this.draggable(drag); From 477179666f3e99a475b58a8ad9d1b1c782910356 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 3 Nov 2011 19:45:31 +0700 Subject: [PATCH 026/178] MDL-47494 ddmarker: NOBUG dragging and dropping of non infinite choices is now working. --- question/type/ddmarker/yui/dd/dd.js | 128 ++++++++++++---------------- 1 file changed, 56 insertions(+), 72 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 840936acacd..c16b14877b7 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -57,6 +57,9 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { drag_items : function() { return dragitemsarea.all('.dragitem'); }, + drag_item : function(choiceno) { + return dragitemsarea.one('span.dragitem.choice' + choiceno); + }, drag_item_home : function (choiceno) { return dragitemsarea.one('span.draghome.choice' + choiceno); }, @@ -131,14 +134,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { false, 0, this.create_all_drag_and_drops); }, create_all_drag_and_drops : function () { - var drop = new Y.DD.Drop({ - node: this.doc.bg_img() - }); - //Listen for a drop:hit on the background image - drop.on('drop:hit', function(e) { - e.drag.get('node').setData('droppedonbgimg', true); - }); - this.doc.drag_item_homes().each(this.clone_new_drag_item, this); this.reposition_drags(); Y.later(500, this, this.reposition_drags, [], true); // if (!this.get('readonly')) { @@ -159,6 +154,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { if (!this.get('readonly')) { this.draggable(drag); } + return drag; }, draggable : function (drag) { var dd = new Y.DD.Drag({ @@ -167,46 +163,81 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()}); dd.on('drag:end', function(e) { var dragnode = e.target.get('node'); - var gooddrop = dragnode.getData('droppedonbgimg'); var choiceno = this.get_choiceno_for_drag(drag); - if (!gooddrop) { + var xy = this.convert_to_bg_img_xy([e.pageX, e.pageY]); + if (!this.xy_in_bgimg(drag, xy)) { this.reset_drag_xy(choiceno); } else { - var xy = this.constrain_xy(drag, this.convert_to_bg_img_xy([e.pageX, e.pageY])); this.set_drag_xy(choiceno, xy); } }, this); - dd.on('drag:start', function(e) { - var drag = e.target; - drag.get('node').setData('droppedonbgimg', false); - }, this); - }, set_drag_xy : function (choiceno, xy) { this.set_form_value(choiceno, Math.round(xy[0])+','+Math.round(xy[1])); }, reset_drag_xy : function (choiceno) { - var choiceno = this.get_choiceno_for_drag(drag); this.set_form_value(choiceno, ''); }, set_form_value : function (choiceno, value) { this.doc.input_for_choice(choiceno).set('value', value); }, + get_form_value : function (choiceno) { + return this.doc.input_for_choice(choiceno).get('value'); + }, + get_coords : function (choiceno) { + var fv = this.get_form_value(choiceno); + if (fv === '') { + var dragitemhome = this.doc.drag_item_home(choiceno); + return dragitemhome.getXY(); + } else { + return this.convert_to_window_xy(fv.split(',')); + } + }, //make sure xy value is not out of bounds of bg image - constrain_xy : function (drag, bgimgxy) { - var xleftconstrained = - Math.min(bgimgxy[0], this.doc.bg_img().get('width') - drag.get('offsetWidth')); - var ytopconstrained = - Math.min(bgimgxy[1], this.doc.bg_img().get('height') - drag.get('offsetHeight')); - xleftconstrained = Math.max(xleftconstrained, 0); - ytopconstrained = Math.max(ytopconstrained, 0); - return [xleftconstrained, ytopconstrained]; + xy_in_bgimg : function (drag, bgimgxy) { + if ((bgimgxy[0] < 0) || + (bgimgxy[1] < 0) || + (bgimgxy[0] > this.doc.bg_img().get('width')) || + (bgimgxy[1] > this.doc.bg_img().get('height'))){ + return false; + } else { + return true; + } }, convert_to_bg_img_xy : function (windowxy) { return [+windowxy[0] - this.doc.bg_img().getX()-1, +windowxy[1] - this.doc.bg_img().getY()-1]; }, + reposition_drags : function() { + this.doc.drag_items().each(function(item) { + if (!item.hasClass('yui3-dd-dragging')){ + item.remove(true); + } + }, this); + this.doc.drag_item_homes().each(function (dragitemhome) { + var choiceno = this.get_choiceno_for_drag(dragitemhome); + if (!this.doc.drag_item(choiceno)) { + var dragitem = this.clone_new_drag_item(dragitemhome); + var coords = this.get_coords(choiceno); + dragitem.setXY(coords); + } else { + //item is being dragged + } + }, this); + this.doc.drag_items().removeClass('placed'); + this.doc.drag_items().each (function (dragitem) { + if (dragitem.dd !== undefined) { + dragitem.dd.detachAll('drag:start'); + } + }, this); + + }, + get_choiceno_for_drag : function(drag) { + return +this.doc.get_classname_numeric_suffix(drag, 'choice'); + }, + + //----------- keyboard accessibility stuff below line --------------------- drop_zone_key_press : function (e) { switch (e.direction) { case 'next' : @@ -264,53 +295,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { remove_drag_from_drop : function (drop) { this.place_drag_in_drop(null, drop); }, - place_drag_in_drop : function (drag, drop) { - var inputid = drop.getData('inputid'); - var inputnode = Y.one('input#'+inputid); - if (drag !== null) { - inputnode.set('value', drag.getData('choice')); - } else { - inputnode.set('value', ''); - } - }, - reposition_drags : function() { - this.doc.drag_items().removeClass('placed'); - this.doc.drag_items().each (function (dragitem) { - if (dragitem.dd !== undefined) { - dragitem.dd.detachAll('drag:start'); - } - }, this); -// this.doc.drop_zones().each(function(dropzone) { -// var relativexy = dropzone.getData('xy'); -// dropzone.setXY(this.convert_to_window_xy(relativexy)); -// var inputcss = 'input#' + dropzone.getData('inputid'); -// var input = this.doc.top_node().one(inputcss); -// var choice = input.get('value'); -// if (choice !== "") { -// var dragitem = this.get_unplaced_choice_for_drop(choice, dropzone); -// if (dragitem !== null) { -// dragitem.setXY(dropzone.getXY()); -// dragitem.addClass('placed'); -// if (dragitem.dd !== undefined) { -// dragitem.dd.once('drag:start', function (e, input) { -// input.set('value', ''); -// e.target.get('node').removeClass('placed'); -// },this, input); -// } -// } -// } -// }, this); - this.doc.drag_items().each(function(dragitem) { - if (!dragitem.hasClass('placed') && !dragitem.hasClass('yui3-dd-dragging')) { - var choiceno = this.get_choiceno_for_drag(dragitem); - var dragitemhome = this.doc.drag_item_home(choiceno); - dragitem.setXY(dragitemhome.getXY()); - } - }, this); - }, - get_choiceno_for_drag : function(drag) { - return +this.doc.get_classname_numeric_suffix(drag, 'choice'); - }, get_choices_for_drop : function(choice, drop) { var group = drop.getData('group'); var dragitem = null; From bf2892c4fd0d6d727f26c226b271393945312c2a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 4 Nov 2011 10:16:25 +0700 Subject: [PATCH 027/178] MDL-47494 ddmarker: NOBUG oops. Just committing crosshairs.gif now --- question/type/ddmarker/pix/crosshairs.gif | Bin 0 -> 73 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 question/type/ddmarker/pix/crosshairs.gif diff --git a/question/type/ddmarker/pix/crosshairs.gif b/question/type/ddmarker/pix/crosshairs.gif new file mode 100644 index 0000000000000000000000000000000000000000..e0d22cee0f97dc66e87b2e1cd2e8f075e6aed68f GIT binary patch literal 73 zcmZ?wbhEHb Date: Fri, 4 Nov 2011 17:06:51 +0700 Subject: [PATCH 028/178] MDL-47494 ddmarker: NOBUG added js functionality for 'infinite' drags. --- question/type/ddmarker/renderer.php | 10 +-- question/type/ddmarker/yui/dd/dd.js | 108 ++++++++++++++++------------ 2 files changed, 70 insertions(+), 48 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index c9f1dff875f..b74d1f2ac98 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -69,7 +69,7 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $markertext = html_writer::tag('span', $drag->text, $markertextattrs); $draghomesattrs = array('class'=>join(' ', $classes)); $draghomes .= html_writer::tag('span', $targeticonhtml.$markertext, $draghomesattrs); - $hiddenfields .= $this->hidden_field_choice($qa, $choiceno); + $hiddenfields .= $this->hidden_field_choice($qa, $choiceno, $drag->infinite); } $dragitemsclass = 'dragitems'; @@ -79,8 +79,7 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $dragitems = html_writer::tag('div', $draghomes, array('class'=> $dragitemsclass)); $output .= html_writer::tag('div', $droparea.$dragitems, array('class'=>'ddarea')); $topnode = 'div#q'.$qa->get_slot(); - $params = array('inputids' => $question->places, - 'topnode' => $topnode, + $params = array('topnode' => $topnode, 'readonly' => $options->readonly); $PAGE->requires->yui_module('moodle-qtype_ddmarker-dd', @@ -95,9 +94,12 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $output .= html_writer::tag('div', $hiddenfields, array('class'=>'ddform')); return $output; } - protected function hidden_field_choice(question_attempt $qa, $choiceno, $value = null) { + protected function hidden_field_choice(question_attempt $qa, $choiceno, $infinite, $value = null) { $varname = 'c'.$choiceno; $classes = array('choices', 'choice'.$choiceno); + if ($infinite) { + $classes[] = 'infinite'; + } list(,$html) = $this->hidden_field_for_qt_var($qa, $varname, null, $classes); return $html; } diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index c16b14877b7..ea626d228a8 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -57,8 +57,11 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { drag_items : function() { return dragitemsarea.all('.dragitem'); }, - drag_item : function(choiceno) { - return dragitemsarea.one('span.dragitem.choice' + choiceno); + drag_items_for_choice : function(choiceno) { + return dragitemsarea.all('span.dragitem.choice' + choiceno); + }, + drag_item_being_dragged : function(choiceno) { + return dragitemsarea.one('span.dragitem.yui3-dd-dragging.choice' + choiceno); }, drag_item_home : function (choiceno) { return dragitemsarea.one('span.draghome.choice' + choiceno); @@ -81,6 +84,9 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } throw 'Prefix "'+prefix+'" not found in class names.'; }, + inputs_for_choices : function () { + return topnode.all('input.choices'); + }, input_for_choice : function (choiceno) { return topnode.one('input.choice'+choiceno); } @@ -135,7 +141,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, create_all_drag_and_drops : function () { this.reposition_drags(); - Y.later(500, this, this.reposition_drags, [], true); + Y.on('windowresize', this.reposition_drags, this); // if (!this.get('readonly')) { // this.doc.drags().set('tabIndex', 0); // this.doc.drags().each( @@ -161,19 +167,30 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { node: drag, dragMode: 'intersect' }).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()}); - dd.on('drag:end', function(e) { + dd.after('drag:start', function(e){ var dragnode = e.target.get('node'); - var choiceno = this.get_choiceno_for_drag(drag); - - var xy = this.convert_to_bg_img_xy([e.pageX, e.pageY]); - if (!this.xy_in_bgimg(drag, xy)) { - this.reset_drag_xy(choiceno); - } else { - this.set_drag_xy(choiceno, xy); - } + var choiceno = this.get_choiceno_for_node(dragnode); + this.save_all_xy_for_choice(choiceno); + }, this); + dd.after('drag:end', function(e) { + var dragnode = e.target.get('node'); + var choiceno = this.get_choiceno_for_node(dragnode); + Y.later(100, this, this.save_all_xy_for_choice, choiceno); }, this); }, - set_drag_xy : function (choiceno, xy) { + save_all_xy_for_choice: function (choiceno) { + var dragitems = this.doc.drag_items_for_choice(choiceno); + var coords = []; + dragitems.each(function(dragitem){ + var bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY()); + if (this.xy_in_bgimg(bgimgxy) && !dragitem.hasClass('yui3-dd-dragging')) { + coords[coords.length] = this.convert_to_bg_img_xy(dragitem.getXY()); + } + }, this); + this.set_form_value(choiceno, coords.join(';')); + this.reposition_drags(); + }, + add_drag_xy : function (choiceno, xy) { this.set_form_value(choiceno, Math.round(xy[0])+','+Math.round(xy[1])); }, reset_drag_xy : function (choiceno) { @@ -182,20 +199,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { set_form_value : function (choiceno, value) { this.doc.input_for_choice(choiceno).set('value', value); }, - get_form_value : function (choiceno) { - return this.doc.input_for_choice(choiceno).get('value'); - }, - get_coords : function (choiceno) { - var fv = this.get_form_value(choiceno); - if (fv === '') { - var dragitemhome = this.doc.drag_item_home(choiceno); - return dragitemhome.getXY(); - } else { - return this.convert_to_window_xy(fv.split(',')); - } - }, //make sure xy value is not out of bounds of bg image - xy_in_bgimg : function (drag, bgimgxy) { + xy_in_bgimg : function (bgimgxy) { if ((bgimgxy[0] < 0) || (bgimgxy[1] < 0) || (bgimgxy[0] > this.doc.bg_img().get('width')) || @@ -206,8 +211,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }, convert_to_bg_img_xy : function (windowxy) { - return [+windowxy[0] - this.doc.bg_img().getX()-1, - +windowxy[1] - this.doc.bg_img().getY()-1]; + return [Math.round(+windowxy[0] - this.doc.bg_img().getX()-1), + Math.round(+windowxy[1] - this.doc.bg_img().getY()-1)]; }, reposition_drags : function() { this.doc.drag_items().each(function(item) { @@ -215,26 +220,41 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { item.remove(true); } }, this); - this.doc.drag_item_homes().each(function (dragitemhome) { - var choiceno = this.get_choiceno_for_drag(dragitemhome); - if (!this.doc.drag_item(choiceno)) { + this.doc.inputs_for_choices().each(function (input) { + var choiceno = this.get_choiceno_for_node(input); + var coords = this.get_coords(input); + var dragitemhome = this.doc.drag_item_home(choiceno); + for (var i=0; i < coords.length; i++) { var dragitem = this.clone_new_drag_item(dragitemhome); - var coords = this.get_coords(choiceno); - dragitem.setXY(coords); - } else { - //item is being dragged + dragitem.setXY(coords[i]); } }, this); - this.doc.drag_items().removeClass('placed'); - this.doc.drag_items().each (function (dragitem) { - if (dragitem.dd !== undefined) { - dragitem.dd.detachAll('drag:start'); - } - }, this); - }, - get_choiceno_for_drag : function(drag) { - return +this.doc.get_classname_numeric_suffix(drag, 'choice'); + /** + * Return coords of all drag items except any that are currently being dragged + * based on contents of hidden inputs and whether drags are 'infinite' + */ + get_coords : function (input) { + var choiceno = this.get_choiceno_for_node(input); + var fv = input.get('value'); + var infinite = input.hasClass('infinite'); + var dragging = (null !== this.doc.drag_item_being_dragged(choiceno)); + var coords = []; + var dragitemhome = this.doc.drag_item_home(choiceno); + if (infinite || (!dragging && fv === '')) { + coords[coords.length] = dragitemhome.getXY(); + } + if (fv !== '') { + var coordsstrings = fv.split(';'); + for (var i=0; i Date: Fri, 4 Nov 2011 17:23:58 +0700 Subject: [PATCH 029/178] MDL-47494 ddmarker: NOBUG added drop shadow on marker text being dragged --- question/type/ddmarker/styles.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 8b91e28962b..9c28527da1e 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -65,3 +65,13 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -khtml-border-radius: 10px; border-radius: 10px; } +.que.ddmarker .dragitem.yui3-dd-dragging span.markertext { + z-index: 3; + -moz-box-shadow: 3px 3px 4px #000; + -webkit-box-shadow: 3px 3px 4px #000; + box-shadow: 3px 3px 4px #000; + /* For IE 8 */ + -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000')"; + /* For IE 5.5 - 7 */ + filter: progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000'); +} \ No newline at end of file From 640947153813b570452462c12b951bf73d69310d Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 8 Nov 2011 12:59:04 +0700 Subject: [PATCH 030/178] MDL-47494 ddmarker: NOBUG rewrote code to save target center xy in hidden input tags also bases position of drags on contents of hidden inputs ie. on target center xy. --- question/type/ddmarker/yui/dd/dd.js | 36 +++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index ea626d228a8..befc2707f54 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -133,13 +133,13 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { Y.extend(DDMARKER_QUESTION, M.qtype_ddmarker.dd_base_class, { initializer : function(params) { this.doc = this.doc_structure(this); - this.poll_for_image_load(null, false, 0, this.create_all_drag_and_drops); + this.poll_for_image_load(null, false, 0, this.after_image_load); this.doc.bg_img().after('load', this.poll_for_image_load, this, - false, 0, this.create_all_drag_and_drops); + false, 0, this.after_image_load); this.doc.drag_item_homes().after('load', this.poll_for_image_load, this, - false, 0, this.create_all_drag_and_drops); + false, 0, this.after_image_load); }, - create_all_drag_and_drops : function () { + after_image_load : function () { this.reposition_drags(); Y.on('windowresize', this.reposition_drags, this); // if (!this.get('readonly')) { @@ -182,16 +182,30 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { var dragitems = this.doc.drag_items_for_choice(choiceno); var coords = []; dragitems.each(function(dragitem){ - var bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY()); + var bgimgxy = this.convert_to_bg_img_xy(this.get_target_center_xy(dragitem)); if (this.xy_in_bgimg(bgimgxy) && !dragitem.hasClass('yui3-dd-dragging')) { - coords[coords.length] = this.convert_to_bg_img_xy(dragitem.getXY()); + coords[coords.length] = bgimgxy; } }, this); + console.log({'coords read from display':coords}); this.set_form_value(choiceno, coords.join(';')); this.reposition_drags(); }, - add_drag_xy : function (choiceno, xy) { - this.set_form_value(choiceno, Math.round(xy[0])+','+Math.round(xy[1])); + get_target_center_xy : function (dragitem) { + console.log('dragitem.getXY()', dragitem.getXY()); + var targetnode = dragitem.one('img.target'); + return [targetnode.getX() + (targetnode.get('offsetWidth') / 2), + targetnode.getY() + (targetnode.get('offsetHeight') / 2)]; + }, + convert_target_center_xy_to_drag_xy : function (dragitem, targetcenterxy) { + var currenttargetxy = this.get_target_center_xy(dragitem); + var currentdragitemxy = dragitem.getXY(); + var difference = [currenttargetxy[0] - currentdragitemxy[0], + currenttargetxy[1] - currentdragitemxy[1]]; + console.log({'targetcenterxy':targetcenterxy}); + console.log({'difference':difference}); + return [targetcenterxy[0] - difference[0], + targetcenterxy[1] - difference[1]-1]; }, reset_drag_xy : function (choiceno) { this.set_form_value(choiceno, ''); @@ -247,8 +261,10 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { if (fv !== '') { var coordsstrings = fv.split(';'); for (var i=0; i Date: Tue, 8 Nov 2011 14:42:23 +0700 Subject: [PATCH 031/178] MDL-47494 ddmarker: NOBUG use CSS to move target center to point at 0,0 of dragitem div changed from calculating difference being drag item xy and target center xy which was error prone. --- question/type/ddmarker/styles.css | 8 ++++++++ question/type/ddmarker/yui/dd/dd.js | 23 ++--------------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 9c28527da1e..1b419033bd3 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -65,6 +65,14 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -khtml-border-radius: 10px; border-radius: 10px; } +.que.ddmarker div.dragitems img.target { + position:absolute; + left:-7px; + top :-7px; +} +.que.ddmarker div.dragitems div.draghome img.target { + display:none; +} .que.ddmarker .dragitem.yui3-dd-dragging span.markertext { z-index: 3; -moz-box-shadow: 3px 3px 4px #000; diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index befc2707f54..64a69565964 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -182,7 +182,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { var dragitems = this.doc.drag_items_for_choice(choiceno); var coords = []; dragitems.each(function(dragitem){ - var bgimgxy = this.convert_to_bg_img_xy(this.get_target_center_xy(dragitem)); + var bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY()); if (this.xy_in_bgimg(bgimgxy) && !dragitem.hasClass('yui3-dd-dragging')) { coords[coords.length] = bgimgxy; } @@ -191,22 +191,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { this.set_form_value(choiceno, coords.join(';')); this.reposition_drags(); }, - get_target_center_xy : function (dragitem) { - console.log('dragitem.getXY()', dragitem.getXY()); - var targetnode = dragitem.one('img.target'); - return [targetnode.getX() + (targetnode.get('offsetWidth') / 2), - targetnode.getY() + (targetnode.get('offsetHeight') / 2)]; - }, - convert_target_center_xy_to_drag_xy : function (dragitem, targetcenterxy) { - var currenttargetxy = this.get_target_center_xy(dragitem); - var currentdragitemxy = dragitem.getXY(); - var difference = [currenttargetxy[0] - currentdragitemxy[0], - currenttargetxy[1] - currentdragitemxy[1]]; - console.log({'targetcenterxy':targetcenterxy}); - console.log({'difference':difference}); - return [targetcenterxy[0] - difference[0], - targetcenterxy[1] - difference[1]-1]; - }, reset_drag_xy : function (choiceno) { this.set_form_value(choiceno, ''); }, @@ -261,10 +245,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { if (fv !== '') { var coordsstrings = fv.split(';'); for (var i=0; i Date: Thu, 10 Nov 2011 12:35:40 +0700 Subject: [PATCH 032/178] MDL-47494 ddmarker: NOBUG changed code for positioning drag items Now it does not delete all drag items each time and reclone them. It repositions existing items and deletes and creates new drag items as needed. --- question/type/ddmarker/yui/dd/dd.js | 70 ++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 21 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 64a69565964..02715302ee2 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -60,6 +60,10 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { drag_items_for_choice : function(choiceno) { return dragitemsarea.all('span.dragitem.choice' + choiceno); }, + drag_item_for_choice : function(choiceno, itemno) { + return dragitemsarea.one('span.dragitem.choice'+ choiceno + + '.item' + itemno); + }, drag_item_being_dragged : function(choiceno) { return dragitemsarea.one('span.dragitem.yui3-dd-dragging.choice' + choiceno); }, @@ -136,8 +140,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { this.poll_for_image_load(null, false, 0, this.after_image_load); this.doc.bg_img().after('load', this.poll_for_image_load, this, false, 0, this.after_image_load); - this.doc.drag_item_homes().after('load', this.poll_for_image_load, this, - false, 0, this.after_image_load); }, after_image_load : function () { this.reposition_drags(); @@ -151,10 +153,11 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { // , this); // } }, - clone_new_drag_item : function (draghome) { + clone_new_drag_item : function (draghome, itemno) { var drag = draghome.cloneNode(true); drag.removeClass('draghome'); drag.addClass('dragitem'); + drag.addClass('item'+ itemno); drag.one('span.markertext').setStyle('opacity', 0.5); draghome.get('parentNode').appendChild(drag); if (!this.get('readonly')) { @@ -170,23 +173,37 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { dd.after('drag:start', function(e){ var dragnode = e.target.get('node'); var choiceno = this.get_choiceno_for_node(dragnode); - this.save_all_xy_for_choice(choiceno); + this.save_all_xy_for_choice(choiceno, null); }, this); dd.after('drag:end', function(e) { var dragnode = e.target.get('node'); var choiceno = this.get_choiceno_for_node(dragnode); - Y.later(100, this, this.save_all_xy_for_choice, choiceno); + Y.later(100, this, this.save_all_xy_for_choice, [choiceno, dragnode]); }, this); }, - save_all_xy_for_choice: function (choiceno) { - var dragitems = this.doc.drag_items_for_choice(choiceno); + save_all_xy_for_choice: function (choiceno, dropped) { var coords = []; - dragitems.each(function(dragitem){ - var bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY()); - if (this.xy_in_bgimg(bgimgxy) && !dragitem.hasClass('yui3-dd-dragging')) { + for (var i=0; i <= this.doc.drag_items_for_choice(choiceno).size(); i++) { + var dragitem = this.doc.drag_item_for_choice(choiceno, i); + if (dragitem) { + dragitem.removeClass('item'+i); + if (!dragitem.hasClass('yui3-dd-dragging')) { + var bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY()); + if (this.xy_in_bgimg(bgimgxy)) { + dragitem.removeClass('item'+i); + dragitem.addClass('item'+coords.length); + coords[coords.length] = bgimgxy; + } + } + } + } + if (dropped !== null){ + var bgimgxy = this.convert_to_bg_img_xy(dropped.getXY()); + if (this.xy_in_bgimg(bgimgxy)) { coords[coords.length] = bgimgxy; } - }, this); + dropped.remove(true); + } console.log({'coords read from display':coords}); this.set_form_value(choiceno, coords.join(';')); this.reposition_drags(); @@ -214,19 +231,30 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, reposition_drags : function() { this.doc.drag_items().each(function(item) { - if (!item.hasClass('yui3-dd-dragging')){ - item.remove(true); - } + //if (!item.hasClass('yui3-dd-dragging')){ + item.addClass('unneeded'); + //} }, this); this.doc.inputs_for_choices().each(function (input) { var choiceno = this.get_choiceno_for_node(input); var coords = this.get_coords(input); var dragitemhome = this.doc.drag_item_home(choiceno); for (var i=0; i < coords.length; i++) { - var dragitem = this.clone_new_drag_item(dragitemhome); + var dragitem; + dragitem = this.doc.drag_item_for_choice(choiceno, i); + if (!dragitem || dragitem.hasClass('yui3-dd-dragging')) { + dragitem = this.clone_new_drag_item(dragitemhome, i); + } else { + dragitem.removeClass('unneeded'); + } dragitem.setXY(coords[i]); } }, this); + this.doc.drag_items().each(function(item) { + if (item.hasClass('unneeded') && !item.hasClass('yui3-dd-dragging')) { + item.remove(true); + } + }, this); }, /** * Return coords of all drag items except any that are currently being dragged @@ -239,15 +267,15 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { var dragging = (null !== this.doc.drag_item_being_dragged(choiceno)); var coords = []; var dragitemhome = this.doc.drag_item_home(choiceno); - if (infinite || (!dragging && fv === '')) { - coords[coords.length] = dragitemhome.getXY(); - } if (fv !== '') { var coordsstrings = fv.split(';'); for (var i=0; i Date: Thu, 10 Nov 2011 12:41:23 +0700 Subject: [PATCH 033/178] MDL-47494 ddmarker: NOBUG added code to reposition drags every 500ms --- question/type/ddmarker/yui/dd/dd.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 02715302ee2..62b7d932789 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -143,7 +143,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, after_image_load : function () { this.reposition_drags(); - Y.on('windowresize', this.reposition_drags, this); + Y.later(500, this, this.reposition_drags, [], true); // if (!this.get('readonly')) { // this.doc.drags().set('tabIndex', 0); // this.doc.drags().each( From b88753e448276ff9ad5b69fdccc459e8c68393b9 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 10 Nov 2011 13:04:13 +0700 Subject: [PATCH 034/178] MDL-47494 ddmarker: NOBUG no longer deleting dragged node --- question/type/ddmarker/yui/dd/dd.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 62b7d932789..374bbcc615e 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -86,7 +86,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } } } - throw 'Prefix "'+prefix+'" not found in class names.'; + return null; }, inputs_for_choices : function () { return topnode.all('input.choices'); @@ -173,12 +173,16 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { dd.after('drag:start', function(e){ var dragnode = e.target.get('node'); var choiceno = this.get_choiceno_for_node(dragnode); + var itemno = this.get_itemno_for_node(dragnode); + if (itemno !== null) { + dragnode.removeClass('item'+dragnode); + } this.save_all_xy_for_choice(choiceno, null); }, this); dd.after('drag:end', function(e) { var dragnode = e.target.get('node'); var choiceno = this.get_choiceno_for_node(dragnode); - Y.later(100, this, this.save_all_xy_for_choice, [choiceno, dragnode]); + this.save_all_xy_for_choice(choiceno, dragnode); }, this); }, save_all_xy_for_choice: function (choiceno, dropped) { @@ -199,10 +203,10 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } if (dropped !== null){ var bgimgxy = this.convert_to_bg_img_xy(dropped.getXY()); + dropped.addClass('item'+coords.length); if (this.xy_in_bgimg(bgimgxy)) { coords[coords.length] = bgimgxy; } - dropped.remove(true); } console.log({'coords read from display':coords}); this.set_form_value(choiceno, coords.join(';')); @@ -281,6 +285,9 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { get_choiceno_for_node : function(node) { return +this.doc.get_classname_numeric_suffix(node, 'choice'); }, + get_itemno_for_node : function(node) { + return +this.doc.get_classname_numeric_suffix(node, 'item'); + }, //----------- keyboard accessibility stuff below line --------------------- drop_zone_key_press : function (e) { From 197acdf79be17df4ed3761050d22d13dde8ff953 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 10 Nov 2011 13:06:02 +0700 Subject: [PATCH 035/178] MDL-47494 ddmarker: NOBUG removing console.log --- question/type/ddmarker/yui/dd/dd.js | 1 - 1 file changed, 1 deletion(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 374bbcc615e..13e1208f428 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -208,7 +208,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { coords[coords.length] = bgimgxy; } } - console.log({'coords read from display':coords}); this.set_form_value(choiceno, coords.join(';')); this.reposition_drags(); }, From c27804887329ec63ef8836dfaad10d3c989e7311 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 10 Nov 2011 13:48:00 +0700 Subject: [PATCH 036/178] MDL-47494 ddmarker: NOBUG adding some space between drag items so they can be easily distinguished --- question/type/ddmarker/styles.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 1b419033bd3..f2aeea5e5f2 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -31,6 +31,9 @@ position: absolute; z-index: 2; } +.que.ddmarker .dragitems .draghome { + margin : 10px; +} .que.ddmarker .dragitems.readonly .dragitem { cursor: auto; } From f881a720ef8b3934e545610a062d46ce2ac7e409 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 12 Nov 2011 15:21:50 +0700 Subject: [PATCH 037/178] MDL-47494 ddmarker: NOBUG implemented keyboard control of drag items for accessibility. --- question/type/ddmarker/yui/dd/dd.js | 129 ++++++++++------------------ 1 file changed, 44 insertions(+), 85 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 13e1208f428..af699fbf9df 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -144,14 +144,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { after_image_load : function () { this.reposition_drags(); Y.later(500, this, this.reposition_drags, [], true); -// if (!this.get('readonly')) { -// this.doc.drags().set('tabIndex', 0); -// this.doc.drags().each( -// function(v){ -// v.on('dragchange', this.drop_zone_key_press, this); -// } -// , this); -// } }, clone_new_drag_item : function (draghome, itemno) { var drag = draghome.cloneNode(true); @@ -159,7 +151,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { drag.addClass('dragitem'); drag.addClass('item'+ itemno); drag.one('span.markertext').setStyle('opacity', 0.5); - draghome.get('parentNode').appendChild(drag); + draghome.insert(drag, 'after'); if (!this.get('readonly')) { this.draggable(drag); } @@ -178,12 +170,17 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { dragnode.removeClass('item'+dragnode); } this.save_all_xy_for_choice(choiceno, null); + this.reposition_drags(); }, this); dd.after('drag:end', function(e) { var dragnode = e.target.get('node'); var choiceno = this.get_choiceno_for_node(dragnode); this.save_all_xy_for_choice(choiceno, dragnode); + this.reposition_drags(); }, this); + //--- keyboard accessibility + drag.set('tabIndex', 0); + drag.on('dragchange', this.drop_zone_key_press, this); }, save_all_xy_for_choice: function (choiceno, dropped) { var coords = []; @@ -209,7 +206,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } } this.set_form_value(choiceno, coords.join(';')); - this.reposition_drags(); }, reset_drag_xy : function (choiceno) { this.set_form_value(choiceno, ''); @@ -228,6 +224,14 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { return true; } }, + constrain_to_bgimg : function (windowxy) { + var bgimgxy = this.convert_to_bg_img_xy(windowxy); + bgimgxy[0] = Math.max(0, bgimgxy[0]); + bgimgxy[1] = Math.max(0, bgimgxy[1]); + bgimgxy[0] = Math.min(this.doc.bg_img().get('width'), bgimgxy[0]); + bgimgxy[1] = Math.min(this.doc.bg_img().get('height'), bgimgxy[1]); + return this.convert_to_window_xy(bgimgxy); + }, convert_to_bg_img_xy : function (windowxy) { return [Math.round(+windowxy[0] - this.doc.bg_img().getX()-1), Math.round(+windowxy[1] - this.doc.bg_img().getY()-1)]; @@ -290,86 +294,41 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { //----------- keyboard accessibility stuff below line --------------------- drop_zone_key_press : function (e) { + var dragitem = e.target; + var xy = dragitem.getXY(); switch (e.direction) { - case 'next' : - this.place_next_drag_in(e.target); + case 'left' : + xy[0] -= 1; break; - case 'previous' : - this.place_previous_drag_in(e.target); + case 'right' : + xy[0] += 1; + break; + case 'down' : + xy[1] += 1; + break; + case 'up' : + xy[1] -= 1; break; case 'remove' : - this.remove_drag_from_drop(e.target); + xy = null; break; } - e.preventDefault(); - this.reposition_drags(); - }, - place_next_drag_in : function (drop) { - this.search_for_unplaced_drop_choice(drop, 1); - }, - place_previous_drag_in : function (drop) { - this.search_for_unplaced_drop_choice(drop, -1); - }, - search_for_unplaced_drop_choice : function (drop, direction) { - var next; - var current = this.current_drag_in_drop(drop); - if ('' === current) { - if (direction == 1) { - next = 1; - } else { - next = 1; - var groupno = drop.getData('group'); - this.doc.drag_items_in_group(groupno).each(function(drag) { - next = Math.max(next, drag.getData('choice')); - }, this); - } + var choiceno = this.get_choiceno_for_node(dragitem); + if (xy !== null) { + xy = this.constrain_to_bgimg(xy); } else { - next = + current + direction; + xy = this.doc.drag_item_home(choiceno).getXY(); } - var drag; - do { - if (this.get_choices_for_drop(next, drop).size() === 0){ - this.remove_drag_from_drop(drop); - return; - } else { - drag = this.get_unplaced_choice_for_drop(next, drop); - } - next = next + direction; - } while (drag === null); - this.place_drag_in_drop(drag, drop); - }, - current_drag_in_drop : function (drop) { - var inputid = drop.getData('inputid'); - var inputnode = Y.one('input#'+inputid); - return inputnode.get('value'); - }, - remove_drag_from_drop : function (drop) { - this.place_drag_in_drop(null, drop); - }, - get_choices_for_drop : function(choice, drop) { - var group = drop.getData('group'); - var dragitem = null; - var dragitems = this.doc.top_node() - .all('div.dragitemgroup'+group+' .choice'+choice+'.drag'); - return dragitems; - }, - get_unplaced_choice_for_drop : function(choice, drop) { - var dragitems = this.get_choices_for_drop(choice, drop); - var dragitem = null; - if (dragitems.some(function (d) { - if (!d.hasClass('placed') && !d.hasClass('yui3-dd-dragging')) { - dragitem = d; - return true; - } else { - return false; - } - })); - return dragitem; + console.log(e.direction, xy); + e.preventDefault(); + dragitem.setXY(xy); + this.save_all_xy_for_choice(choiceno, null); } + }, {NAME : DDMARKERQUESTIONNAME, ATTRS : {}}); -/* Y.Event.define('dragchange', { + Y.Event.define('dragchange', { // Webkit and IE repeat keydown when you hold down arrow keys. // Opera links keypress to page scroll; others keydown. // Firefox prevents page scroll via preventDefault() on either @@ -377,12 +336,12 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { _event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress', _keys: { - '32': 'next', - '37': 'previous', - '38': 'previous', - '39': 'next', - '40': 'next', - '27': 'remove' + '32': 'remove', // Space + '37': 'left', // Left arrow + '38': 'up', // Up arrow + '39': 'right', // Right arrow + '40': 'down', // Down arrow + '27': 'remove' // Escape }, _keyHandler: function (e, notifier) { @@ -396,7 +355,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { sub._detacher = node.on(this._event, this._keyHandler, this, notifier); } - });*/ + }); M.qtype_ddmarker.init_question = function(config) { return new DDMARKER_QUESTION(config); } From 6f789d3dbd7f6cdaa5e773a25f7e1fc36af318de Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 14 Nov 2011 15:39:44 +0700 Subject: [PATCH 038/178] MDL-47494 ddmarker: NOBUG more work on code to validate coordinates entered when editing question --- question/type/ddmarker/edit_ddmarker_form.php | 30 ++++++------ .../type/ddmarker/lang/en/qtype_ddmarker.php | 9 +++- question/type/ddmarker/shapes.php | 49 +++++++++++++++++-- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index 7d9ab7963af..ebc9a2b4177 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -92,22 +92,13 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { return array(); } - protected function shapes() { - return preg_grep('!^qtype_ddmarker_shape_!', get_declared_classes()); - return array('qtype_ddmarker_shape_circle', - 'qtype_ddmarker_shape_rectangle', - 'qtype_ddmarker_shape_polygon'); - } protected function drop_zone($mform, $imagerepeats) { $dropzoneitem = array(); $grouparray = array(); - $shapearray = array(); - foreach ($this->shapes() as $shape) { - $shapearray[$shape::name()] = $shape::human_readable_name(); - } + $shapearray = qtype_ddmarker_shape::shape_options(); $grouparray[] = $mform->createElement('select', 'shape', get_string('marker', 'qtype_ddmarker'), $shapearray); $grouparray[] = $mform->createElement('text', 'coords', @@ -229,18 +220,25 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { if ($choicepresent) { //test coords here if ($bgimagesize !== null) { - if (in_array($data['drops'][$i]['shape'], $this->shapes())) { - + $shape = $data['drops'][$i]['shape']; + $coordsstring = $data['drops'][$i]['coords']; + $shapeobj = qtype_ddmarker_shape::create($shape, $coordsstring); + $interpretererror = $shapeobj->get_coords_interpreter_error(); + if ($interpretererror) { + $errors["drops[{$i}]"] = $interpretererror; + } else if (!$shapeobj->inside_width_height($bgimagesize)) { + $errorcode = 'shapeoutsideboundsofbgimage'; + $errors["drops[{$i}]"] = + get_string('formerror_'.$errorcode, 'qtype_ddmarker'); } } - - } else { if (trim($data['drops'][$i]['coords']) !== '') { - $errors["drops[{$i}]"] - = get_string('formerror_noitemselected', 'qtype_ddmarker'); + $errorcode = 'noitemselected'; + $errors["drops[{$i}]"] = get_string('formerror_'.$errorcode, 'qtype_ddmarker'); } } + } for ($dragindex=0; $dragindex < $data['noitems']; $dragindex++) { $label = $data['drags'][$dragindex]['label']; diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 2f14a1cf0df..4e92ba4efdb 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -43,13 +43,20 @@ $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; +$string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['formerror_notagsallowed'] = 'No html tags are allowed in the label for a marker'; +$string['formerror_onlyusewholepositivenumbers'] = 'Please use only whole positive numbers to specify x,y coords and/or width and height of shapes. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; +$string['formerror_polygonmusthaveatleastthreepoints'] = 'For a polygon shape you need to specify at least 3 points. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; +$string['formerror_shapeoutsideboundsofbgimage'] = 'The shape you have defined goes out of the bounds of the background image'; +$string['formerror_toomanysemicolons'] = 'There are too many semi colon separated parts to the coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; +$string['formerror_unrecognisedwidthheightpart'] = 'We do not recognise the width and height you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; +$string['formerror_unrecognisedxypart'] = 'We do not recognise the x,y coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['infinite'] = 'Infinite'; $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; $string['nolabel'] = 'No label text'; -$string['pleasedraganimagetoeachdropregion'] = 'Your answer is not complete, please drag an item to each drop region.'; +$string['pleasedragatleastonemarker'] = 'Your answer is not complete, please drag at least one marker onto the image.'; $string['previewarea'] = 'Preview area -'; $string['previewareaheader'] = 'Preview'; $string['previewareamessage'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index 97c6faf0483..34e6ca025e0 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -23,6 +23,9 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class qtype_ddmarker_shape { + + protected $error = false; + public function __construct($coordsstring) { } @@ -59,6 +62,17 @@ abstract class qtype_ddmarker_shape { return true; } + public function get_coords_interpreter_error() { + if ($this->error) { + $a = new stdClass(); + $a->shape = self::human_readable_name(); + $a->coordsstring = self::human_readable_coords_format(); + return get_string('formerror_'.$this->error, 'qtype_ddmarker', $a); + } else { + return false; + } + } + /** * @param array $xy $xy[0] is x, $xy[1] is y * @return boolean is point inside shape @@ -66,12 +80,37 @@ abstract class qtype_ddmarker_shape { abstract public function is_point_in_shape($xy); public static function name() { - return substr(get_called_class(), 21); + return substr(get_called_class(), strlen(self::$classnameprefix)); } + protected static $classnameprefix = 'qtype_ddmarker_shape_'; + public static function human_readable_name() { return get_string('shape_'.self::name(), 'qtype_ddmarker'); } + + public static function human_readable_coords_format() { + return get_string('shape_'.self::name().'_coords', 'qtype_ddmarker'); + } + + + public static function shape_options() { + $grepexpression = '!^'.preg_quote(self::$classnameprefix, '!').'!'; + $shapes = preg_grep($grepexpression, get_declared_classes()); + $shapearray = array(); + foreach ($shapes as $shape) { + $shapearray[$shape::name()] = $shape::human_readable_name(); + } + asort($shapearray); + return $shapearray; + } + public static function exists($shape) { + return class_exists((self::$classnameprefix).$shape); + } + public static function create($shape, $coordsstring) { + $classname = (self::$classnameprefix).$shape; + return new $classname($coordsstring); + } } class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { protected $width; @@ -81,14 +120,14 @@ class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { public function __construct($coordsstring) { $coordstring = preg_replace('!^\s*!', '', $coordsstring); $coordstring = preg_replace('!\s*$!', '', $coordsstring); - $coordsstringparts = preg_split('!\s*!', $coordsstring); + $coordsstringparts = preg_split('!;!', $coordsstring); if (count($coordsstringparts) > 2) { - $this->error = 'rectangletoomanyspaces'; + $this->error = 'toomanysemicolons'; } else if (count($coordsstringparts) < 2) { - $this->error = 'rectanglenospaces'; + $this->error = 'nosemicolons'; } else { $xy = explode(',', $coordsstringparts[0]); - $widthheightparts = explode('x', $coordsstringparts[1]); + $widthheightparts = explode(',', $coordsstringparts[1]); if (count($xy) !== 2) { $this->error = 'unrecognisedxypart'; } else if (count($widthheightparts) !== 2) { From 33164c042446319a6d20b58f6fd15c5128d16a4f Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 15 Nov 2011 17:09:09 +0700 Subject: [PATCH 039/178] MDL-47494 ddmarker: NOBUG coded question grading. --- question/type/ddmarker/edit_ddmarker_form.php | 3 +- question/type/ddmarker/question.php | 207 +++++++++++++++++- 2 files changed, 205 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index ebc9a2b4177..a96abc1fd88 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -224,7 +224,7 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { $coordsstring = $data['drops'][$i]['coords']; $shapeobj = qtype_ddmarker_shape::create($shape, $coordsstring); $interpretererror = $shapeobj->get_coords_interpreter_error(); - if ($interpretererror) { + if ($interpretererror !== false) { $errors["drops[{$i}]"] = $interpretererror; } else if (!$shapeobj->inside_width_height($bgimagesize)) { $errorcode = 'shapeoutsideboundsofbgimage'; @@ -246,7 +246,6 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { $errors["drags[{$dragindex}]"] = get_string('formerror_notagsallowed', 'qtype_ddmarker'); } - } return $errors; } diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 790d8f3b5ef..8fd8010b612 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -62,11 +62,208 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { public function get_expected_data() { $vars = array(); - foreach ($this->choices as $choice => $notused) { - $vars[$this->choice($choice)] = PARAM_INTEGER; + foreach ($this->choices[1] as $choice => $notused) { + $vars[$this->choice($choice)] = PARAM_NOTAGS; } return $vars; } + public function is_complete_response(array $response) { + foreach ($this->choices[1] as $choice => $notused) { + if ('' != trim($response[$this->choice($choice)])) { + return true; + } + } + return false; + } + public function is_gradable_response(array $response) { + return $this->is_complete_response($response); + } + public function is_same_response(array $prevresponse, array $newresponse) { + foreach ($this->choices[1] as $choice => $notused) { + $fieldname = $this->choice($choice); + if (!$this->arrays_same_at_key_integer( + $prevresponse, $newresponse, $fieldname)) { + return false; + } + } + return true; + } + /** + * Tests to see whether two arrays have the same set of coords at a particular key. Coords + * can be in any order. + * @param array $array1 the first array. + * @param array $array2 the second array. + * @param string $key an array key. + * @return bool whether the two arrays have the same set of coords (or lack of them) + * for a given key. + */ + public function arrays_same_at_key_integer( + array $array1, array $array2, $key) { + if (array_key_exists($key, $array1)) { + $value1 = $array1[$key]; + } else { + $value1 = ''; + } + if (array_key_exists($key, $array2)) { + $value2 = $array2[$key]; + } else { + $value2 = ''; + } + $coords1 = explode(';', $value1); + $coords2 = explode(';', $value2); + if (count($coords1) !== count($coords1)) { + return false; + } else if (count($coords1) === 0) { + return true; + } else { + $valuesinbotharrays = array_intersect($coords1, $coords2); + return (count($valuesinbotharrays) == count($coords1) && + count($valuesinbotharrays) == count($coords2)); + } + } + public function get_validation_error(array $response) { + if ($this->is_complete_response($response)) { + return ''; + } + return get_string('pleasedragatleastonemarker', 'qtype_ddmarker'); + } + public function get_num_parts_right(array $response) { + $chosenhits = $this->choose_hits($response); + $divisor = max(count($this->rightchoices), $this->total_number_of_items_dragged($response)); + return array(count($chosenhits), $divisor); + } + /** + * Choose hits to maximize grade where drop targets may have more than one hit and drop targets + * can overlap. + * @param array $response + * @return array chosen hits + */ + protected function choose_hits(array $response) { + $allhits = $this->get_all_hits($response); + $chosenhits = array(); + foreach ($allhits as $placeno => $hits) { + foreach ($hits as $itemno => $hit) { + $choice = $this->get_right_choice_for($placeno); + $choiceitem = "$choice $itemno"; + if (!in_array($choiceitem, $chosenhits)) { + $chosenhits[$placeno] = $choiceitem; + break; + } + } + } + return $chosenhits; + } + public function total_number_of_items_dragged(array $response) { + $total = 0; + foreach ($this->choiceorder[1] as $choice) { + $choicekey = $this->choice($choice); + if (array_key_exists($choicekey, $response) && trim($response[$choicekey] !== '')) { + $total += count(explode(';', $response[$choicekey])); + } + } + return $total; + } + + /** + * Get's an array of all hits on drop targets. Needs further processing to find which hits + * to select in the general case that drop targets may have more than one hit and drop targets + * can overlap. + * @param array $response + * @return array all hits + */ + protected function get_all_hits(array $response) { + $hits = array(); + foreach ($this->places as $placeno => $place) { + $rightchoice = $this->get_right_choice_for($placeno); + $rightchoicekey = $this->choice($rightchoice); + if (!array_key_exists($rightchoicekey, $response)) { + continue; + } + $choicecoords = $response[$rightchoicekey]; + $coords = explode(';', $choicecoords); + foreach ($coords as $itemno => $coord) { + if (trim($coord) === '') { + continue; + } + $pointxy = explode(',', $coord); + if ($place->drop_hit($pointxy)) { + if (!isset($hits[$placeno])) { + $hits[$placeno] = array(); + } + $hits[$placeno][$itemno] = $coord; + } + } + } + //reverse sort in order of number of hits per place + //(if two or more hits per place then we want to make sure hits do not hit elsewhere) + $sortcomparison = function ($a1, $a2){ + return (count($a1) - count($a2)); + }; + uasort($hits, $sortcomparison); + return $hits; + } + + public function get_right_choice_for($place) { + $group = $this->places[$place]->group; + foreach ($this->choiceorder[$group] as $choicekey => $choiceid) { + if ($this->rightchoices[$place] == $choiceid) { + return $choicekey; + } + } + } + public function grade_response(array $response) { + list($right, $total) = $this->get_num_parts_right($response); + $fraction = $right / $total; + return array($fraction, question_state::graded_state_for_fraction($fraction)); + } + + public function compute_final_grade($responses, $totaltries) { + $maxitemsdragged = 0; + $rightsince = array(); + foreach ($responses as $i => $response) { + $maxitemsdragged = max($maxitemsdragged, + $this->total_number_of_items_dragged($response)); + $hits = $this->choose_hits($response); + dbg(compact('hits', 'maxitemsdragged')); + foreach ($hits as $place => $choiceitem) { + if (!isset($rightsince[$place])) { + $rightsince[$place] = $i; + } + } + foreach ($rightsince as $place => $notused) { + if (!isset($hits[$place])) { + unset($rightsince[$place]); + } + } + } + $numtries = count($responses); + $numright = count($rightsince); + dbg("($numright * $numtries) - ".array_sum($rightsince).") + / ($numright * max($maxitemsdragged, ".count($this->places).")"); + $grade = (($numright * $numtries) - array_sum($rightsince)) + / ($numtries * max($maxitemsdragged, count($this->places))); + return $grade; + } + + public function classify_response(array $response) { + $parts = array(); + foreach ($this->places as $place => $group) { + if (!array_key_exists($this->field($place), $response) || + !$response[$this->field($place)]) { + $parts[$place] = question_classified_response::no_response(); + continue; + } + + $fieldname = $this->field($place); + $choiceno = $this->choiceorder[$group][$response[$fieldname]]; + $choice = $this->choices[$group][$choiceno]; + $parts[$place] = new question_classified_response( + $choiceno, html_to_text($choice->text, 0, false), + ($this->get_right_choice_for($place) == $response[$fieldname]) + / count($this->places)); + } + return $parts; + } } /** @@ -107,11 +304,15 @@ class qtype_ddmarker_drop_zone { public function __construct($no, $shape, $coords) { $this->no = $no; - $this->shape = $shape; + $this->shape = qtype_ddmarker_shape::create($shape, $coords); $this->coords = $coords; } public function summarise() { return get_string('summariseplaceno', 'qtype_ddmarker', $this->no); } + + public function drop_hit($xy) { + return $this->shape->is_point_in_shape($xy); + } } From b1d1ae5095d64b6f1448556452ae5881f114252d Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 15 Nov 2011 17:40:33 +0700 Subject: [PATCH 040/178] MDL-47494 ddmarker: NOBUG coded clear wrong from response --- question/type/ddmarker/question.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 8fd8010b612..64f753d699e 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -244,6 +244,23 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { / ($numtries * max($maxitemsdragged, count($this->places))); return $grade; } + public function clear_wrong_from_response(array $response) { + $hits = $this->choose_hits($response); + + $cleanedresponse = array(); + foreach ($response as $choicekey => $coords) { + $choice = (int)substr($choicekey, 1); + $choiceresponse = array(); + $coordparts = explode(';', $coords); + foreach ($coordparts as $itemno => $coord) { + if (in_array("$choice $itemno", $hits)) { + $choiceresponse[] = $coord; + } + } + $cleanedresponse[$choicekey] = join(';', $choiceresponse); + } + return $cleanedresponse; + } public function classify_response(array $response) { $parts = array(); From 7792acc0a867ca375ebc83c6e7cec87b12765673 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 16 Nov 2011 13:00:16 +0700 Subject: [PATCH 041/178] MDL-47494 ddmarker: NOBUG corrected grading equation to include penalty and clarified code also removed debug code --- question/type/ddmarker/question.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 64f753d699e..ee000e7de30 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -219,29 +219,26 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { public function compute_final_grade($responses, $totaltries) { $maxitemsdragged = 0; - $rightsince = array(); + $wrongtries = array(); foreach ($responses as $i => $response) { $maxitemsdragged = max($maxitemsdragged, $this->total_number_of_items_dragged($response)); $hits = $this->choose_hits($response); - dbg(compact('hits', 'maxitemsdragged')); foreach ($hits as $place => $choiceitem) { - if (!isset($rightsince[$place])) { - $rightsince[$place] = $i; + if (!isset($wrongtries[$place])) { + $wrongtries[$place] = $i; } } - foreach ($rightsince as $place => $notused) { + foreach ($wrongtries as $place => $notused) { if (!isset($hits[$place])) { - unset($rightsince[$place]); + unset($wrongtries[$place]); } } } $numtries = count($responses); - $numright = count($rightsince); - dbg("($numright * $numtries) - ".array_sum($rightsince).") - / ($numright * max($maxitemsdragged, ".count($this->places).")"); - $grade = (($numright * $numtries) - array_sum($rightsince)) - / ($numtries * max($maxitemsdragged, count($this->places))); + $numright = count($wrongtries); + $penalty = array_sum($wrongtries) * $this->penalty; + $grade = ($numright - $penalty) / (max($maxitemsdragged, count($this->places))); return $grade; } public function clear_wrong_from_response(array $response) { From 5d76e3940066929c00ded24e97fec13459bcde39 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 16 Nov 2011 13:07:52 +0700 Subject: [PATCH 042/178] MDL-47494 ddmarker: NOBUG fixing PHP notice about missing label property. --- question/type/ddmarker/edit_ddmarker_form.php | 1 - 1 file changed, 1 deletion(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index a96abc1fd88..a020313ccef 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -213,7 +213,6 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { $allchoices = array(); for ($i=0; $i < $data['nodropzone']; $i++) { - $labelpresent = (trim($data['drops'][$i]['label']) !== ''); $choice = $data['drops'][$i]['choice']; $choicepresent = ($choice !== '0'); From a365808af1707e95d7847339c03a97cd1726231a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 16 Nov 2011 14:32:23 +0700 Subject: [PATCH 043/178] MDL-47494 ddmarker: NOBUG fixed a couple of bugs displaying marker text --- question/type/ddmarker/yui/form/form.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 78f8d7ed596..feda93f0551 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -73,6 +73,18 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { update_drop_zone : function (dropzoneno) { var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']); var markertext = this.get_marker_text(dragitemno); + var existingmarkertext = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); + if (existingmarkertext) { + if (markertext !== '') { + existingmarkertext.setContent(markertext); + } else { + existingmarkertext.remove(true); + } + } else if (markertext !== '') { + var classnames = 'markertext markertext' + dropzoneno; + Y.one('div.ddarea div.markertexts').append('' + + markertext+''); + } var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']); var drawfunc = 'draw_shape_'+shape; var colourfordropzone = this.get_next_colour(); @@ -81,18 +93,6 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { if (this[drawfunc] instanceof Function){ var xyfortext = this[drawfunc](dropzoneno, colourfordropzone); if (xyfortext !== null) { - var classnames = 'markertext markertext' + dropzoneno; - var existingdiv = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); - if (existingdiv) { - if (markertext !== '') { - existingdiv.setContent(markertext); - } else { - existingdiv.remove(true); - } - } else { - Y.one('div.ddarea div.markertexts').append('' + - markertext+''); - } var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); if (markerspan !== null) { markerspan.setStyle('opacity', '0.4'); From 457c35b3dc25c265c35f5efc01ca4473562eab82 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 16 Nov 2011 14:37:09 +0700 Subject: [PATCH 044/178] MDL-47494 ddmarker: NOBUG increase opacity of marker text to 0.6 to make them more readable --- question/type/ddmarker/yui/dd/dd.js | 2 +- question/type/ddmarker/yui/form/form.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index af699fbf9df..98a5ac60834 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -150,7 +150,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { drag.removeClass('draghome'); drag.addClass('dragitem'); drag.addClass('item'+ itemno); - drag.one('span.markertext').setStyle('opacity', 0.5); + drag.one('span.markertext').setStyle('opacity', 0.6); draghome.insert(drag, 'after'); if (!this.get('readonly')) { this.draggable(drag); diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index feda93f0551..d7bf466efd8 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -95,7 +95,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { if (xyfortext !== null) { var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); if (markerspan !== null) { - markerspan.setStyle('opacity', '0.4'); + markerspan.setStyle('opacity', '0.6'); xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); markerspan.setXY(this.convert_to_window_xy(xyfortext)); From bcbe321a878d18a9e8136b5fc8be74d165badad5 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 16 Nov 2011 16:01:47 +0700 Subject: [PATCH 045/178] MDL-47494 ddmarker: NOBUG implemented 'Fill in correct responses' --- question/type/ddmarker/question.php | 27 +++++++++++++++++++++++++++ question/type/ddmarker/shapes.php | 19 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index ee000e7de30..104429ec36d 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -210,6 +210,7 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { return $choicekey; } } + return null; } public function grade_response(array $response) { list($right, $total) = $this->get_num_parts_right($response); @@ -278,6 +279,28 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return $parts; } + + public function get_correct_response() { + $responsecoords = array(); + foreach ($this->places as $placeno => $place) { + $rightchoice = $this->get_right_choice_for($placeno); + if ($rightchoice !== null) { + $rightchoicekey = $this->choice($rightchoice); + $correctcoords = $place->correct_coords(); + if ($correctcoords !== null) { + if (!isset($responsecoords[$rightchoicekey])) { + $responsecoords[$rightchoicekey] = array(); + } + $responsecoords[$rightchoicekey][] = join(',', $correctcoords); + } + } + } + $response = array(); + foreach ($responsecoords as $choicekey => $coords) { + $response[$choicekey] = join(';', $coords); + } + return $response; + } } /** @@ -329,4 +352,8 @@ class qtype_ddmarker_drop_zone { public function drop_hit($xy) { return $this->shape->is_point_in_shape($xy); } + + public function correct_coords() { + return $this->shape->center_point(); + } } diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index 34e6ca025e0..bde344c2469 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -39,6 +39,8 @@ abstract class qtype_ddmarker_shape { } abstract protected function outlying_coords_to_test(); + abstract public function center_point(); + protected function is_only_numbers() { $args = func_get_args(); foreach ($args as $arg) { @@ -153,6 +155,10 @@ class qtype_ddmarker_shape_rectangle extends qtype_ddmarker_shape { return $this->is_point_in_bounding_box($xy, array($this->xleft, $this->ytop), array($this->xleft + $this->width, $this->ytop + $this->height)); } + public function center_point() { + return array($this->xleft + round($this->width / 2), + $this->ytop + round($this->height / 2)); + } } class qtype_ddmarker_shape_circle extends qtype_ddmarker_shape { @@ -191,6 +197,9 @@ class qtype_ddmarker_shape_circle extends qtype_ddmarker_shape { + pow(($xy[1] - $this->ycentre), 2)); return $distancefromcentre < $this->radius; } + public function center_point() { + return array($this->xcentre, $this->ycentre); + } } class qtype_ddmarker_shape_polygon extends qtype_ddmarker_shape { /** @@ -327,6 +336,16 @@ class qtype_ddmarker_shape_polygon extends qtype_ddmarker_shape { } return array($p, $q); } + public function center_point() { + $center = array(round(($this->minxy[0] + $this->maxxy[0])/2), + round(($this->minxy[1] + $this->maxxy[1])/2)); + if ($this->is_point_in_shape($center)) { + return $center; + } else { + return null; + } + + } } class qtype_ddmarker_point { public $x; From e47c7eaec2bdeea05f918e63e130c76906c20d1a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 17 Nov 2011 13:20:43 +0700 Subject: [PATCH 046/178] MDL-47494 ddmarker: NOBUG implemented the highlighting of drop zones with no drag hit --- .../type/ddmarker/lang/en/qtype_ddmarker.php | 1 + question/type/ddmarker/question.php | 27 +++ question/type/ddmarker/renderer.php | 28 ++- question/type/ddmarker/styles.css | 27 ++- question/type/ddmarker/yui/dd/dd.js | 172 +++++++++++++++++- question/type/ddmarker/yui/form/form.js | 146 +-------------- 6 files changed, 254 insertions(+), 147 deletions(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 4e92ba4efdb..821416ba40f 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -41,6 +41,7 @@ $string['draggableword'] = 'Draggable text'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; +$string['followingarewrong'] = 'The following markers have not been dragged to the correct drop zone : {$a}. The drop zones they should have been dragged to are shown above.
Click on the markers above to see the drop zones they should have been dropped in highlighted.'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; $string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 104429ec36d..6611979d783 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -259,7 +259,34 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return $cleanedresponse; } + public function get_drop_zones_without_hit(array $response) { + $hits = $this->choose_hits($response); + $nohits = array(); + foreach ($this->places as $placeno => $place) { + $choice = $this->get_right_choice_for($placeno); + if (!isset($hits[$placeno])) { + $nohit = new stdClass(); + $nohit->coords = $place->coords; + $nohit->shape = $place->shape->name(); + $nohit->markertext = $this->choices[1][$choice]->text; + $nohits[] = $nohit; + } + } + return $nohits; + } + public function wrong_parts(array $response) { + $hits = $this->choose_hits($response); + + $wrong = array(); + foreach ($this->places as $placeno => $place) { + $choice = $this->get_right_choice_for($placeno); + if (!isset($hits[$placeno])) { + $wrong[] = $this->choices[1][$choice]->text; + } + } + return $wrong; + } public function classify_response(array $response) { $parts = array(); foreach ($this->places as $place => $group) { diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index b74d1f2ac98..2352d7090b9 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -76,10 +76,23 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { if ($options->readonly) { $dragitemsclass .= ' readonly'; } + $dragitems = html_writer::tag('div', $draghomes, array('class'=> $dragitemsclass)); - $output .= html_writer::tag('div', $droparea.$dragitems, array('class'=>'ddarea')); + $dropzones = html_writer::empty_tag('div', array('class'=> 'dropzones')); + $texts = html_writer::empty_tag('div', array('class'=> 'markertexts')); + $output .= html_writer::tag('div', + $droparea.$dragitems.$dropzones.$texts, + array('class'=>'ddarea')); + + if ($qa->get_state()->is_finished()) { + $visibledropzones = $question->get_drop_zones_without_hit($response); + } else { + $visibledropzones = array(); + } + $topnode = 'div#q'.$qa->get_slot(); - $params = array('topnode' => $topnode, + $params = array('dropzones' => $visibledropzones, + 'topnode' => $topnode, 'readonly' => $options->readonly); $PAGE->requires->yui_module('moodle-qtype_ddmarker-dd', @@ -91,6 +104,17 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $question->get_validation_error($qa->get_last_qt_data()), array('class' => 'validationerror')); } + if ($qa->get_state()->is_finished()) { + $wrongparts = $question->wrong_parts($response); + $wrongpartsstring = ''; + foreach($wrongparts as $wrongpart) { + $wrongpartsstring .= html_writer::nonempty_tag('span', + $wrongpart, array('class' => 'wrongpart')); + } + $output .= html_writer::nonempty_tag('span', + get_string('followingarewrong', 'qtype_ddmarker', $wrongpartsstring), + array('class' => 'wrongparts')); + } $output .= html_writer::tag('div', $hiddenfields, array('class'=>'ddform')); return $output; } diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index f2aeea5e5f2..a19547e4265 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -51,9 +51,10 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa *font:x-small; /* for IE in quirks mode */ } .que.ddmarker div.dragitems span.markertext, +.que.ddmarker div.dropzones span.markertext, form.mform fieldset#previewareaheader div.markertexts span.markertext { margin: 5px; - z-index: 2; + z-index: 3; background-color: white; border-style : none; padding: 5px; @@ -68,6 +69,30 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -khtml-border-radius: 10px; border-radius: 10px; } +.que.ddmarker div.dropzones span.markertext { + z-index: 2; + background-color: yellow; + border-style : solid; + border-width : 2px; + border-colour : khaki; +} +.que.ddmarker span.wrongparts span.wrongpart { + background-color: yellow; + border-style : solid; + border-width : 2px; + border-colour : khaki; + padding: 5px; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + -khtml-border-radius: 10px; + border-radius: 10px; + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; + filter: alpha(opacity=60); + -moz-opacity:0.6; + -khtml-opacity: 0.6; + opacity: 0.6; + margin: 5px; +} .que.ddmarker div.dragitems img.target { position:absolute; left:-7px; diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 98a5ac60834..09157d1007c 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -10,6 +10,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { doc : null, polltimer : null, afterimageloaddone : false, + graphics : null, poll_for_image_load : function (e, waitforimageconstrain, pause, doafterwords) { if (this.afterimageloaddone) { return; @@ -93,6 +94,9 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, input_for_choice : function (choiceno) { return topnode.one('input.choice'+choiceno); + }, + marker_texts : function () { + return topnode.one('div.markertexts'); } } @@ -115,6 +119,148 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { convert_to_window_xy : function (bgimgxy) { return [+bgimgxy[0] + this.doc.bg_img().getX() + 1, +bgimgxy[1] + this.doc.bg_img().getY() + 1]; + }, + shapes : [], + draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour) { + var existingmarkertext = this.doc.marker_texts().one('span.markertext'+dropzoneno+' a'); + if (existingmarkertext) { + if (markertext !== '') { + existingmarkertext.setContent(markertext); + } else { + existingmarkertext.remove(true); + } + } else if (markertext !== '') { + var classnames = 'markertext markertext' + dropzoneno; + this.doc.marker_texts().append('' + + markertext+''); + } + var drawfunc = 'draw_shape_'+shape; + if (this[drawfunc] instanceof Function){ + var xyfortext = this[drawfunc](dropzoneno, coords, colour); + if (xyfortext !== null) { + var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); + if (markerspan !== null) { + markerspan.setStyle('opacity', '0.6'); + xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); + xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); + markerspan.setXY(this.convert_to_window_xy(xyfortext)); + markerspan.one('a').once('click', + function (e, dropzoneno) { + var fill = this.shapes[dropzoneno].get('fill'); + fill.opacity = 1; + this.shapes[dropzoneno].set('fill', fill); + }, + this, + dropzoneno + ); + markerspan.one('a').set('tabIndex', 0); + + } + } + } + }, + draw_shape_circle : function (dropzoneno, coords, colour) { + var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); + if (coordsparts && coordsparts.length === 4) { + var xy = [+coordsparts[1] - coordsparts[3], +coordsparts[2] - coordsparts[3]]; + if (this.coords_in_img(xy)) { + var widthheight = [+coordsparts[3]*2, +coordsparts[3]*2]; + var shape = this.graphics.addShape({ + type: 'circle', + width: widthheight[0], + height: widthheight[1], + fill: { + color: colour, + opacity : "0.5" + }, + stroke: { + weight:1, + color: "black" + } + }); + shape.setXY(this.convert_to_window_xy(xy)); + this.shapes[dropzoneno] = shape; + return [+coordsparts[1], +coordsparts[2]]; + } + } + return null; + }, + draw_shape_rectangle : function (dropzoneno, coords, colour) { + var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); + if (coordsparts && coordsparts.length === 5) { + var xy = [+coordsparts[1], +coordsparts[2]]; + var widthheight = [+coordsparts[3], +coordsparts[4]]; + if (this.coords_in_img([xy[0]+widthheight[0], xy[1]+widthheight[1]])) { + var shape = this.graphics.addShape({ + type: 'rect', + width: widthheight[0], + height: widthheight[1], + fill: { + color: colour, + opacity : "0.5" + }, + stroke: { + weight:1, + color: "black" + } + }); + shape.setXY(this.convert_to_window_xy(xy)); + this.shapes[dropzoneno] = shape; + return [+xy[0]+widthheight[0]/2, +xy[1]+widthheight[1]/2]; + } + } + return null; + + }, + draw_shape_polygon : function (dropzoneno, coords, colour) { + var coordsparts = coords.split(';'); + var xy = []; + for (var i in coordsparts) { + var parts = coordsparts[i].match(/^(\d+),(\d+)$/); + if (parts !== null && this.coords_in_img([parts[1], parts[2]])) { + xy[xy.length] = [parts[1], parts[2]]; + } + } + if (xy.length > 2) { + var polygon = this.graphics.addShape({ + type: "path", + stroke: { + weight: 1, + color: "black" + }, + fill: { + color: colour, + opacity : "0.5" + } + }); + var maxxy = [0,0]; + var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')]; + for (var i in xy) { + //calculate min and max points to find center to show marker on + minxy[0] = Math.min(xy[i][0], minxy[0]); + minxy[1] = Math.min(xy[i][1], minxy[1]); + maxxy[0] = Math.max(xy[i][0], maxxy[0]); + maxxy[1] = Math.max(xy[i][1], maxxy[1]); + if (i == 0) { + polygon.moveTo(xy[i][0], xy[i][1]); + } else { + polygon.lineTo(xy[i][0], xy[i][1]); + } + } + if (+xy[0][0] !== +xy[xy.length-1][0] || +xy[0][1] !== +xy[xy.length-1][1]) { + var windowxy = this.convert_to_window_xy(xy[0]); + polygon.lineTo(xy[0][0], xy[0][1]); //close polygon if not already closed + } + polygon.end(); + polygon.setXY(this.doc.bg_img().getXY()); + this.shapes[dropzoneno] = polygon; + return [Math.round((minxy[0] + maxxy[0])/2), Math.round((minxy[1] + maxxy[1])/2)]; + } + return null; + }, + coords_in_img : function (coords) { + return (coords[0] <= this.doc.bg_img().get('width') && + coords[1] <= this.doc.bg_img().get('height')); } }, { NAME : DDMARKERDDNAME, @@ -142,8 +288,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { false, 0, this.after_image_load); }, after_image_load : function () { - this.reposition_drags(); - Y.later(500, this, this.reposition_drags, [], true); + this.redraw_drags_and_drops(); + Y.later(2000, this, this.redraw_drags_and_drops, [], true); }, clone_new_drag_item : function (draghome, itemno) { var drag = draghome.cloneNode(true); @@ -170,13 +316,13 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { dragnode.removeClass('item'+dragnode); } this.save_all_xy_for_choice(choiceno, null); - this.reposition_drags(); + this.redraw_drags_and_drops(); }, this); dd.after('drag:end', function(e) { var dragnode = e.target.get('node'); var choiceno = this.get_choiceno_for_node(dragnode); this.save_all_xy_for_choice(choiceno, dragnode); - this.reposition_drags(); + this.redraw_drags_and_drops(); }, this); //--- keyboard accessibility drag.set('tabIndex', 0); @@ -236,7 +382,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { return [Math.round(+windowxy[0] - this.doc.bg_img().getX()-1), Math.round(+windowxy[1] - this.doc.bg_img().getY()-1)]; }, - reposition_drags : function() { + redraw_drags_and_drops : function() { this.doc.drag_items().each(function(item) { //if (!item.hasClass('yui3-dd-dragging')){ item.addClass('unneeded'); @@ -262,6 +408,18 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { item.remove(true); } }, this); + if (this.graphics !== null) { + this.graphics.destroy(); + } + this.restart_colours(); + this.graphics = new Y.Graphic( + {render:this.doc.top_node().one("div.ddarea div.dropzones")} + ); + for (var dropzoneno in this.get('dropzones')) { + var colourfordropzone = this.get_next_colour(); + var d = this.get('dropzones')[dropzoneno]; + this.draw_drop_zone(dropzoneno, d.markertext, d.shape, d.coords, colourfordropzone); + } }, /** * Return coords of all drag items except any that are currently being dragged @@ -326,7 +484,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } - }, {NAME : DDMARKERQUESTIONNAME, ATTRS : {}}); + }, {NAME : DDMARKERQUESTIONNAME, ATTRS : {dropzones:{value:[]}}}); Y.Event.define('dragchange', { // Webkit and IE repeat keydown when you hold down arrow keys. @@ -360,5 +518,5 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { return new DDMARKER_QUESTION(config); } }, '@VERSION@', { - requires:['node', 'event-resize', 'dd', 'dd-drop', 'dd-constrain'] + requires:['node', 'event-resize', 'dd', 'dd-drop', 'dd-constrain', 'graphics'] }); diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index d7bf466efd8..7023e17b45b 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -55,7 +55,6 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { e.target.detach('load', this.constrain_image_size); }, - graphics : null, update_drop_zones : function () { @@ -66,146 +65,19 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { } this.restart_colours(); this.graphics = new Y.Graphic({render:"div.ddarea div.dropzones"}); - for (var i=0; i < this.form.get_form_value('nodropzone', []); i++) { - this.update_drop_zone(i); - } - }, - update_drop_zone : function (dropzoneno) { - var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']); - var markertext = this.get_marker_text(dragitemno); - var existingmarkertext = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); - if (existingmarkertext) { - if (markertext !== '') { - existingmarkertext.setContent(markertext); - } else { - existingmarkertext.remove(true); - } - } else if (markertext !== '') { - var classnames = 'markertext markertext' + dropzoneno; - Y.one('div.ddarea div.markertexts').append('' + - markertext+''); - } - var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']); - var drawfunc = 'draw_shape_'+shape; - var colourfordropzone = this.get_next_colour(); - Y.one('input#id_drops_'+dropzoneno+'_coords') + var noofdropzones = this.form.get_form_value('nodropzone', []); + for (var dropzoneno=0; dropzoneno < noofdropzones; dropzoneno++) { + var dragitemno = this.form.get_form_value('drops', [dropzoneno, 'choice']); + var markertext = this.get_marker_text(dragitemno); + var shape = this.form.get_form_value('drops', [dropzoneno, 'shape']); + var coords = this.get_coords(dropzoneno); + var colourfordropzone = this.get_next_colour(); + Y.one('input#id_drops_'+dropzoneno+'_coords') .setStyle('background-color', colourfordropzone); - if (this[drawfunc] instanceof Function){ - var xyfortext = this[drawfunc](dropzoneno, colourfordropzone); - if (xyfortext !== null) { - var markerspan = Y.one('div.ddarea div.markertexts span.markertext'+dropzoneno); - if (markerspan !== null) { - markerspan.setStyle('opacity', '0.6'); - xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); - xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); - markerspan.setXY(this.convert_to_window_xy(xyfortext)); - } - } + this.draw_drop_zone(dropzoneno, markertext, shape, coords, colourfordropzone); } }, - draw_shape_circle : function (dropzoneno, colourfordropzone) { - var coords = this.get_coords(dropzoneno); - var coordsparts = coords.match(/(\d+),(\d+);(\d+)/); - if (coordsparts && coordsparts.length === 4) { - var xy = [+coordsparts[1] - coordsparts[3], +coordsparts[2] - coordsparts[3]]; - if (this.coords_in_img(xy)) { - var widthheight = [+coordsparts[3]*2, +coordsparts[3]*2]; - var shape = this.graphics.addShape({ - type: 'circle', - width: widthheight[0], - height: widthheight[1], - fill: { - color: colourfordropzone, - opacity : "0.5" - }, - stroke: { - weight:1, - color: "black" - } - }); - shape.setXY(this.convert_to_window_xy(xy)); - return [+coordsparts[1], +coordsparts[2]]; - } - } - return null; - }, - draw_shape_rectangle : function (dropzoneno, colourfordropzone) { - var coords = this.get_coords(dropzoneno); - var coordsparts = coords.match(/(\d+),(\d+);(\d+),(\d+)/); - if (coordsparts && coordsparts.length === 5) { - var xy = [+coordsparts[1], +coordsparts[2]]; - var widthheight = [+coordsparts[3], +coordsparts[4]]; - if (this.coords_in_img([xy[0]+widthheight[0], xy[1]+widthheight[1]])) { - var shape = this.graphics.addShape({ - type: 'rect', - width: widthheight[0], - height: widthheight[1], - fill: { - color: colourfordropzone, - opacity : "0.5" - }, - stroke: { - weight:1, - color: "black" - } - }); - shape.setXY(this.convert_to_window_xy(xy)); - return [+xy[0]+widthheight[0]/2, +xy[1]+widthheight[1]/2]; - } - } - return null; - }, - draw_shape_polygon : function (dropzoneno, colourfordropzone) { - var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); - var coordsparts = coords.split(';'); - var xy = []; - for (var i in coordsparts) { - var parts = coordsparts[i].match(/^(\d+),(\d+)$/); - if (parts !== null && this.coords_in_img([parts[1], parts[2]])) { - xy[xy.length] = [parts[1], parts[2]]; - } - } - if (xy.length > 2) { - var polygon = this.graphics.addShape({ - type: "path", - stroke: { - weight: 1, - color: "black" - }, - fill: { - color: colourfordropzone, - opacity : "0.5" - } - }); - var maxxy = [0,0]; - var minxy = [this.doc.bg_img().get('width'), this.doc.bg_img().get('height')]; - for (var i in xy) { - //calculate min and max points to find center to show marker on - minxy[0] = Math.min(xy[i][0], minxy[0]); - minxy[1] = Math.min(xy[i][1], minxy[1]); - maxxy[0] = Math.max(xy[i][0], maxxy[0]); - maxxy[1] = Math.max(xy[i][1], maxxy[1]); - if (i == 0) { - polygon.moveTo(xy[i][0], xy[i][1]); - } else { - polygon.lineTo(xy[i][0], xy[i][1]); - } - } - if (+xy[0][0] !== +xy[xy.length-1][0] || +xy[0][1] !== +xy[xy.length-1][1]) { - var windowxy = this.convert_to_window_xy(xy[0]); - polygon.lineTo(xy[0][0], xy[0][1]); //close polygon if not already closed - } - polygon.end(); - polygon.setXY(this.doc.bg_img().getXY()); - return [Math.round((minxy[0] + maxxy[0])/2), Math.round((minxy[1] + maxxy[1])/2)]; - } - return null; - }, - coords_in_img : function (coords) { - return (coords[0] <= this.doc.bg_img().get('width') && - coords[1] <= this.doc.bg_img().get('height')); - }, get_coords : function (dropzoneno) { var coords = this.form.get_form_value('drops', [dropzoneno, 'coords']); return coords.replace(new RegExp("\\s*", 'g'), ''); From 43964253f36b09cc28060304378f9a09df3430c0 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 17 Nov 2011 13:37:33 +0700 Subject: [PATCH 047/178] MDL-47494 ddmarker: NOBUG control highlighting drops without hit by showmisplaced flag from form --- question/type/ddmarker/question.php | 3 +++ question/type/ddmarker/questiontype.php | 7 +++++++ question/type/ddmarker/renderer.php | 5 +++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 6611979d783..2542dfeb495 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -37,6 +37,9 @@ require_once($CFG->dirroot . '/question/type/ddmarker/shapes.php'); * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { + + public $showmisplaced; + public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) { if ($filearea == 'bgimage') { $validfilearea = true; diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 4abd57186b4..932ad490d74 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -238,4 +238,11 @@ class qtype_ddmarker extends qtype_ddtoimage_base { protected function make_place($dropdata) { return new qtype_ddmarker_drop_zone($dropdata->no, $dropdata->shape, $dropdata->coords); } + + protected function initialise_combined_feedback(question_definition $question, + $questiondata, $withparts = false) { + parent::initialise_combined_feedback($question, $questiondata, $withparts); + $question->showmisplaced = $questiondata->options->showmisplaced; + } + } diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 2352d7090b9..2bca3de2b15 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -84,7 +84,7 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $droparea.$dragitems.$dropzones.$texts, array('class'=>'ddarea')); - if ($qa->get_state()->is_finished()) { + if ($question->showmisplaced && $qa->get_state()->is_finished()) { $visibledropzones = $question->get_drop_zones_without_hit($response); } else { $visibledropzones = array(); @@ -104,7 +104,8 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $question->get_validation_error($qa->get_last_qt_data()), array('class' => 'validationerror')); } - if ($qa->get_state()->is_finished()) { + + if ($question->showmisplaced && $qa->get_state()->is_finished()) { $wrongparts = $question->wrong_parts($response); $wrongpartsstring = ''; foreach($wrongparts as $wrongpart) { From 70c434ee5b82f77dbf5e740c081d4517757b02f8 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 17 Nov 2011 16:08:02 +0700 Subject: [PATCH 048/178] MDL-47494 ddmarker: NOBUG implemented highlighting of incorrectly dragged drag items. --- .../type/ddmarker/lang/en/qtype_ddmarker.php | 3 +- question/type/ddmarker/question.php | 29 ++++++++++++------- question/type/ddmarker/renderer.php | 26 ++++++++++++++--- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 821416ba40f..abcd11ce583 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -41,7 +41,8 @@ $string['draggableword'] = 'Draggable text'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; -$string['followingarewrong'] = 'The following markers have not been dragged to the correct drop zone : {$a}. The drop zones they should have been dragged to are shown above.
Click on the markers above to see the drop zones they should have been dropped in highlighted.'; +$string['followingarewrong'] = 'The following markers have been dragged to the wrong position : {$a}.'; +$string['followingarewrongandhighlighted'] = 'The following markers need to be dragged to the correct drop zone : {$a}. The drop zones they should have been dragged to are shown above.
Click on the markers above to see the drop zones they should have been dropped in highlighted.'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; $string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 2542dfeb495..139ec5b3a42 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -262,6 +262,24 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return $cleanedresponse; } + public function get_wrong_drags(array $response) { + $hits = $this->choose_hits($response); + $wrong = array(); + foreach ($response as $choicekey => $coords) { + $choice = (int)substr($choicekey, 1); + if ($coords != '') { + $coordparts = explode(';', $coords); + foreach ($coordparts as $itemno => $coord) { + if (!in_array("$choice $itemno", $hits)) { + $wrong[] = $this->get_selected_choice(1, $choice)->text; + } + } + } + } + return $wrong; + } + + public function get_drop_zones_without_hit(array $response) { $hits = $this->choose_hits($response); @@ -278,18 +296,7 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return $nohits; } - public function wrong_parts(array $response) { - $hits = $this->choose_hits($response); - $wrong = array(); - foreach ($this->places as $placeno => $place) { - $choice = $this->get_right_choice_for($placeno); - if (!isset($hits[$placeno])) { - $wrong[] = $this->choices[1][$choice]->text; - } - } - return $wrong; - } public function classify_response(array $response) { $parts = array(); foreach ($this->places as $place => $group) { diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 2bca3de2b15..56e35804426 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -106,16 +106,17 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { } if ($question->showmisplaced && $qa->get_state()->is_finished()) { - $wrongparts = $question->wrong_parts($response); + $wrongparts = $question->get_drop_zones_without_hit($response); $wrongpartsstring = ''; foreach($wrongparts as $wrongpart) { $wrongpartsstring .= html_writer::nonempty_tag('span', - $wrongpart, array('class' => 'wrongpart')); + $wrongpart->markertext, array('class' => 'wrongpart')); } $output .= html_writer::nonempty_tag('span', - get_string('followingarewrong', 'qtype_ddmarker', $wrongpartsstring), - array('class' => 'wrongparts')); + get_string('followingarewrongandhighlighted', 'qtype_ddmarker', $wrongpartsstring), + array('class' => 'wrongparts')); } + $output .= html_writer::tag('div', $hiddenfields, array('class'=>'ddform')); return $output; } @@ -128,4 +129,21 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { list(,$html) = $this->hidden_field_for_qt_var($qa, $varname, null, $classes); return $html; } + + protected function hint(question_attempt $qa, question_hint $hint) { + $question = $qa->get_question(); + $response = $qa->get_last_qt_data(); + if ($hint->statewhichincorrect) { + $wrongparts = $question->get_wrong_drags($response); + $wrongpartsstring = ''; + foreach($wrongparts as $wrongpart) { + $wrongpartsstring .= html_writer::nonempty_tag('span', + $wrongpart, array('class' => 'wrongpart')); + } + $output .= html_writer::nonempty_tag('span', + get_string('followingarewrong', 'qtype_ddmarker', $wrongpartsstring), + array('class' => 'wrongparts')); + } + return $output . parent::hint($qa, $hint); + } } From 67a2afa43e3f07eb8818a3fc7ed70d9bf54b4524 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 17 Nov 2011 16:13:10 +0700 Subject: [PATCH 049/178] MDL-47494 ddmarker: NOBUG hopefully made some of the student facing messages more intelligible --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index abcd11ce583..4ed2e888a61 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -41,8 +41,8 @@ $string['draggableword'] = 'Draggable text'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; -$string['followingarewrong'] = 'The following markers have been dragged to the wrong position : {$a}.'; -$string['followingarewrongandhighlighted'] = 'The following markers need to be dragged to the correct drop zone : {$a}. The drop zones they should have been dragged to are shown above.
Click on the markers above to see the drop zones they should have been dropped in highlighted.'; +$string['followingarewrong'] = 'The following markers have been placed in the wrong area : {$a}.'; +$string['followingarewrongandhighlighted'] = 'The following markers need to be placed in the correct area : {$a}. The areas they should have been placed in are shown above.
Click on the markers above to see the areas they should have been placed in highlighted.'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; $string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; @@ -58,7 +58,7 @@ $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; $string['nolabel'] = 'No label text'; -$string['pleasedragatleastonemarker'] = 'Your answer is not complete, please drag at least one marker onto the image.'; +$string['pleasedragatleastonemarker'] = 'Your answer is not complete, you must place at least one marker on the image.'; $string['previewarea'] = 'Preview area -'; $string['previewareaheader'] = 'Preview'; $string['previewareamessage'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; From 374c250b5133ea7b5977888a07f36467a904045d Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 17 Nov 2011 16:17:06 +0700 Subject: [PATCH 050/178] MDL-47494 ddmarker: NOBUG added a border to draggable items --- question/type/ddmarker/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index a19547e4265..40ae654f66e 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -56,7 +56,7 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { margin: 5px; z-index: 3; background-color: white; - border-style : none; + border: 2px solid black; padding: 5px; display: -moz-inline-stack; display:inline-block; From cb88dc139bef9a041ff61d3c9a1d91aac9dcd0c8 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 12:34:00 +0700 Subject: [PATCH 051/178] MDL-47494 ddmarker: NOBUG fixed some problems with response summaries --- question/type/ddmarker/question.php | 49 ++++++++++++++++++------- question/type/ddmarker/questiontype.php | 33 +++++++++++++++++ 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 139ec5b3a42..20cca84915e 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -299,20 +299,18 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { public function classify_response(array $response) { $parts = array(); - foreach ($this->places as $place => $group) { - if (!array_key_exists($this->field($place), $response) || - !$response[$this->field($place)]) { - $parts[$place] = question_classified_response::no_response(); - continue; + $hits = $this->choose_hits($response); + foreach ($this->places as $placeno => $place) { + if (isset($hits[$placeno])) { + $shuffledchoiceno = $this->get_right_choice_for($placeno); + $choiceno = $this->get_selected_choice(1, $shuffledchoiceno); + $parts[$placeno] = new question_classified_response( + $choiceno, + $this->choices[$choiceno]->summarise(), + 1 / count($this->places)); + } else { + $parts[$placeno] = question_classified_response::no_response(); } - - $fieldname = $this->field($place); - $choiceno = $this->choiceorder[$group][$response[$fieldname]]; - $choice = $this->choices[$group][$choiceno]; - $parts[$place] = new question_classified_response( - $choiceno, html_to_text($choice->text, 0, false), - ($this->get_right_choice_for($place) == $response[$fieldname]) - / count($this->places)); } return $parts; } @@ -338,6 +336,31 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return $response; } + + public function get_right_answer_summary() { + $placesummaries = array(); + foreach ($this->places as $placeno => $place) { + $shuffledchoiceno = $this->get_right_choice_for($placeno); + $choice = $this->get_selected_choice(1, $shuffledchoiceno); + $placesummaries[] = '{'.$place->summarise().' -> '.$choice->summarise().'}'; + } + return join(', ', $placesummaries); + } + + public function summarise_response(array $response) { + $hits = $this->choose_hits($response); + $goodhits = array(); + foreach ($this->places as $placeno => $place) { + if (isset($hits[$placeno])) { + $choice = $this->choices[$this->get_right_choice_for($placeno)]; + $goodhits[] = "{".$place->summarise()." -> ". $choice->summarise(). "}"; + } + } + if (count($goodhits)) { + return null; + } + return implode(', ', $goodhits); + } } /** diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 932ad490d74..baabc417938 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -245,4 +245,37 @@ class qtype_ddmarker extends qtype_ddtoimage_base { $question->showmisplaced = $questiondata->options->showmisplaced; } + public function move_files($questionid, $oldcontextid, $newcontextid) { + global $DB; + $fs = get_file_storage(); + + parent::move_files($questionid, $oldcontextid, $newcontextid); + $fs->move_area_files_to_new_context($oldcontextid, + $newcontextid, 'qtype_ddmarker', 'bgimage', $questionid); + + + $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); + } + + /** + * Delete all the files belonging to this question. + * @param int $questionid the question being deleted. + * @param int $contextid the context the question is in. + */ + + protected function delete_files($questionid, $contextid) { + global $DB; + $fs = get_file_storage(); + + parent::delete_files($questionid, $contextid); + + $dragids = $DB->get_records_menu('qtype_ddimageortext_drags', + array('questionid' => $questionid), 'id', 'id,1'); + foreach ($dragids as $dragid => $notused) { + $fs->delete_area_files($contextid, 'qtype_ddimageortext', 'dragimage', $dragid); + } + + $this->delete_files_in_combined_feedback($questionid, $contextid); + } + } From 597a696a4a351029b86e292b7f137d8c71d40c8f Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 12:34:00 +0700 Subject: [PATCH 052/178] MDL-47494 ddmarker: NOBUG fixed some problems with response summaries --- question/type/ddmarker/question.php | 49 ++++++++++++++++++------- question/type/ddmarker/questiontype.php | 33 +++++++++++++++++ 2 files changed, 69 insertions(+), 13 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 139ec5b3a42..20cca84915e 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -299,20 +299,18 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { public function classify_response(array $response) { $parts = array(); - foreach ($this->places as $place => $group) { - if (!array_key_exists($this->field($place), $response) || - !$response[$this->field($place)]) { - $parts[$place] = question_classified_response::no_response(); - continue; + $hits = $this->choose_hits($response); + foreach ($this->places as $placeno => $place) { + if (isset($hits[$placeno])) { + $shuffledchoiceno = $this->get_right_choice_for($placeno); + $choiceno = $this->get_selected_choice(1, $shuffledchoiceno); + $parts[$placeno] = new question_classified_response( + $choiceno, + $this->choices[$choiceno]->summarise(), + 1 / count($this->places)); + } else { + $parts[$placeno] = question_classified_response::no_response(); } - - $fieldname = $this->field($place); - $choiceno = $this->choiceorder[$group][$response[$fieldname]]; - $choice = $this->choices[$group][$choiceno]; - $parts[$place] = new question_classified_response( - $choiceno, html_to_text($choice->text, 0, false), - ($this->get_right_choice_for($place) == $response[$fieldname]) - / count($this->places)); } return $parts; } @@ -338,6 +336,31 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return $response; } + + public function get_right_answer_summary() { + $placesummaries = array(); + foreach ($this->places as $placeno => $place) { + $shuffledchoiceno = $this->get_right_choice_for($placeno); + $choice = $this->get_selected_choice(1, $shuffledchoiceno); + $placesummaries[] = '{'.$place->summarise().' -> '.$choice->summarise().'}'; + } + return join(', ', $placesummaries); + } + + public function summarise_response(array $response) { + $hits = $this->choose_hits($response); + $goodhits = array(); + foreach ($this->places as $placeno => $place) { + if (isset($hits[$placeno])) { + $choice = $this->choices[$this->get_right_choice_for($placeno)]; + $goodhits[] = "{".$place->summarise()." -> ". $choice->summarise(). "}"; + } + } + if (count($goodhits)) { + return null; + } + return implode(', ', $goodhits); + } } /** diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 932ad490d74..baabc417938 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -245,4 +245,37 @@ class qtype_ddmarker extends qtype_ddtoimage_base { $question->showmisplaced = $questiondata->options->showmisplaced; } + public function move_files($questionid, $oldcontextid, $newcontextid) { + global $DB; + $fs = get_file_storage(); + + parent::move_files($questionid, $oldcontextid, $newcontextid); + $fs->move_area_files_to_new_context($oldcontextid, + $newcontextid, 'qtype_ddmarker', 'bgimage', $questionid); + + + $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); + } + + /** + * Delete all the files belonging to this question. + * @param int $questionid the question being deleted. + * @param int $contextid the context the question is in. + */ + + protected function delete_files($questionid, $contextid) { + global $DB; + $fs = get_file_storage(); + + parent::delete_files($questionid, $contextid); + + $dragids = $DB->get_records_menu('qtype_ddimageortext_drags', + array('questionid' => $questionid), 'id', 'id,1'); + foreach ($dragids as $dragid => $notused) { + $fs->delete_area_files($contextid, 'qtype_ddimageortext', 'dragimage', $dragid); + } + + $this->delete_files_in_combined_feedback($questionid, $contextid); + } + } From 6d6cda8902692b8e064315b5e6e34ab6b3d21384 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 13:12:48 +0700 Subject: [PATCH 053/178] MDL-47494 ddmarker: NOBUG fix for notices seen when pressing 'fill in correct response' in preview --- question/type/ddmarker/question.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 20cca84915e..0b6eb5fc82f 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -71,8 +71,9 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { return $vars; } public function is_complete_response(array $response) { - foreach ($this->choices[1] as $choice => $notused) { - if ('' != trim($response[$this->choice($choice)])) { + foreach ($this->choices[1] as $choiceno => $notused) { + if (isset($response[$this->choice($choiceno)]) + && '' != trim($response[$this->choice($choiceno)])) { return true; } } @@ -352,7 +353,8 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { $goodhits = array(); foreach ($this->places as $placeno => $place) { if (isset($hits[$placeno])) { - $choice = $this->choices[$this->get_right_choice_for($placeno)]; + $shuffledchoiceno = $this->get_right_choice_for($placeno); + $choice = $this->get_selected_choice(1, $shuffledchoiceno); $goodhits[] = "{".$place->summarise()." -> ". $choice->summarise(). "}"; } } From 6f6d6e2fbed319a8ba1ee5d894158cffb9d74561 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 13:17:27 +0700 Subject: [PATCH 054/178] MDL-47494 ddmarker: NOBUG fixed notice when hints are displayed --- question/type/ddmarker/renderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 56e35804426..d2b3d4e9413 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -131,6 +131,7 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { } protected function hint(question_attempt $qa, question_hint $hint) { + $output = ''; $question = $qa->get_question(); $response = $qa->get_last_qt_data(); if ($hint->statewhichincorrect) { From 8ed8434c6364fa8ab6ffe94f80a27d86ec35360f Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 13:19:33 +0700 Subject: [PATCH 055/178] MDL-47494 ddmarker: NOBUG string change --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 4ed2e888a61..8bf6b42f6da 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -42,7 +42,7 @@ $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; $string['followingarewrong'] = 'The following markers have been placed in the wrong area : {$a}.'; -$string['followingarewrongandhighlighted'] = 'The following markers need to be placed in the correct area : {$a}. The areas they should have been placed in are shown above.
Click on the markers above to see the areas they should have been placed in highlighted.'; +$string['followingarewrongandhighlighted'] = 'The following markers were not placed in the correct area : {$a}. The areas they should have been placed in are shown above.
Click on the markers above to see the areas they should have been placed in highlighted.'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; $string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; From c28563d2f0e0c489650b1b5dd18c4e1a0341580c Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 18:40:50 +0700 Subject: [PATCH 056/178] MDL-47494 ddmarker: NOBUG suppress message about wrongly dragged question parts when there are none. --- question/type/ddmarker/renderer.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index d2b3d4e9413..b8682946104 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -107,14 +107,16 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { if ($question->showmisplaced && $qa->get_state()->is_finished()) { $wrongparts = $question->get_drop_zones_without_hit($response); - $wrongpartsstring = ''; - foreach($wrongparts as $wrongpart) { - $wrongpartsstring .= html_writer::nonempty_tag('span', - $wrongpart->markertext, array('class' => 'wrongpart')); + if (count($wrongparts) !== 0) { + $wrongpartsstring = ''; + foreach($wrongparts as $wrongpart) { + $wrongpartsstring .= html_writer::nonempty_tag('span', + $wrongpart->markertext, array('class' => 'wrongpart')); + } + $output .= html_writer::nonempty_tag('span', + get_string('followingarewrongandhighlighted', 'qtype_ddmarker', $wrongpartsstring), + array('class' => 'wrongparts')); } - $output .= html_writer::nonempty_tag('span', - get_string('followingarewrongandhighlighted', 'qtype_ddmarker', $wrongpartsstring), - array('class' => 'wrongparts')); } $output .= html_writer::tag('div', $hiddenfields, array('class'=>'ddform')); From 14fe3addd8f84149cb769795b0a07d7f637bd552 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 18 Nov 2011 18:41:42 +0700 Subject: [PATCH 057/178] MDL-47494 ddmarker: NOBUG suppressing link in drag when using question editing form --- question/type/ddmarker/yui/dd/dd.js | 32 +++++++++++++++++-------- question/type/ddmarker/yui/form/form.js | 3 ++- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 09157d1007c..74b4f0f80bd 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -62,7 +62,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { return dragitemsarea.all('span.dragitem.choice' + choiceno); }, drag_item_for_choice : function(choiceno, itemno) { - return dragitemsarea.one('span.dragitem.choice'+ choiceno + + return dragitemsarea.one('span.dragitem.choice'+ choiceno + '.item' + itemno); }, drag_item_being_dragged : function(choiceno) { @@ -101,7 +101,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }, - + colours : ['#FFFFFF', '#B0C4DE', '#DCDCDC', '#D8BFD8', '#87CEFA','#DAA520', '#FFD700', '#F0E68C'], nextcolourindex : 0, @@ -121,8 +121,14 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { +bgimgxy[1] + this.doc.bg_img().getY() + 1]; }, shapes : [], - draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour) { - var existingmarkertext = this.doc.marker_texts().one('span.markertext'+dropzoneno+' a'); + draw_drop_zone : function (dropzoneno, markertext, shape, coords, colour, link) { + if (link) { + var existingmarkertext + = this.doc.marker_texts().one('span.markertext'+dropzoneno+' a'); + } else { + var existingmarkertext = this.doc.marker_texts().one('span.markertext'+dropzoneno); + } + if (existingmarkertext) { if (markertext !== '') { existingmarkertext.setContent(markertext); @@ -131,8 +137,13 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } } else if (markertext !== '') { var classnames = 'markertext markertext' + dropzoneno; - this.doc.marker_texts().append('' + - markertext+''); + if (link) { + this.doc.marker_texts().append('' + + markertext+''); + } else { + this.doc.marker_texts().append('' + + markertext+''); + } } var drawfunc = 'draw_shape_'+shape; if (this[drawfunc] instanceof Function){ @@ -259,7 +270,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { return null; }, coords_in_img : function (coords) { - return (coords[0] <= this.doc.bg_img().get('width') && + return (coords[0] <= this.doc.bg_img().get('width') && coords[1] <= this.doc.bg_img().get('height')); } }, { @@ -363,7 +374,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { xy_in_bgimg : function (bgimgxy) { if ((bgimgxy[0] < 0) || (bgimgxy[1] < 0) || - (bgimgxy[0] > this.doc.bg_img().get('width')) || + (bgimgxy[0] > this.doc.bg_img().get('width')) || (bgimgxy[1] > this.doc.bg_img().get('height'))){ return false; } else { @@ -418,7 +429,8 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { for (var dropzoneno in this.get('dropzones')) { var colourfordropzone = this.get_next_colour(); var d = this.get('dropzones')[dropzoneno]; - this.draw_drop_zone(dropzoneno, d.markertext, d.shape, d.coords, colourfordropzone); + this.draw_drop_zone(dropzoneno, d.markertext, + d.shape, d.coords, colourfordropzone, true); } }, /** @@ -449,7 +461,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { get_itemno_for_node : function(node) { return +this.doc.get_classname_numeric_suffix(node, 'item'); }, - + //----------- keyboard accessibility stuff below line --------------------- drop_zone_key_press : function (e) { var dragitem = e.target; diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index 7023e17b45b..a6021186c7b 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -74,7 +74,8 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { var colourfordropzone = this.get_next_colour(); Y.one('input#id_drops_'+dropzoneno+'_coords') .setStyle('background-color', colourfordropzone); - this.draw_drop_zone(dropzoneno, markertext, shape, coords, colourfordropzone); + this.draw_drop_zone(dropzoneno, markertext, + shape, coords, colourfordropzone, false); } }, From c9cf5af4e58173454af8e644b8829bd8034493ca Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 19 Nov 2011 13:37:59 +0700 Subject: [PATCH 058/178] MDL-47494 ddmarker: NOBUG fixed issue with occurances of html_writer::empty_tag('div'..... Divs were being generated as
which is probably illegal html and was not getting correctly selected with YUI to add children. --- question/type/ddmarker/renderer.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index b8682946104..6d676fc8226 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -78,8 +78,8 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { } $dragitems = html_writer::tag('div', $draghomes, array('class'=> $dragitemsclass)); - $dropzones = html_writer::empty_tag('div', array('class'=> 'dropzones')); - $texts = html_writer::empty_tag('div', array('class'=> 'markertexts')); + $dropzones = html_writer::tag('div', '', array('class'=> 'dropzones')); + $texts = html_writer::tag('div', '', array('class'=> 'markertexts')); $output .= html_writer::tag('div', $droparea.$dragitems.$dropzones.$texts, array('class'=>'ddarea')); @@ -143,10 +143,11 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $wrongpartsstring .= html_writer::nonempty_tag('span', $wrongpart, array('class' => 'wrongpart')); } - $output .= html_writer::nonempty_tag('span', + $output .= html_writer::nonempty_tag('div', get_string('followingarewrong', 'qtype_ddmarker', $wrongpartsstring), array('class' => 'wrongparts')); } - return $output . parent::hint($qa, $hint); + $output .= parent::hint($qa, $hint); + return $output; } } From 5356733eff709cee608048a14de98cda96cd509e Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 19 Nov 2011 15:15:01 +0700 Subject: [PATCH 059/178] MDL-47494 ddmarker: NOBUG suppress link in drag items in question editing form --- question/type/ddmarker/yui/dd/dd.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 74b4f0f80bd..9244fb8f121 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -155,17 +155,18 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { xyfortext[0] -= Math.round(markerspan.get('offsetWidth') / 2); xyfortext[1] -= Math.round(markerspan.get('offsetHeight') / 2); markerspan.setXY(this.convert_to_window_xy(xyfortext)); - markerspan.one('a').once('click', - function (e, dropzoneno) { + var markerspananchor = markerspan.one('a'); + if (markerspananchor !== null) { + markerspananchor.once('click', function (e, dropzoneno) { var fill = this.shapes[dropzoneno].get('fill'); fill.opacity = 1; this.shapes[dropzoneno].set('fill', fill); - }, - this, - dropzoneno - ); - markerspan.one('a').set('tabIndex', 0); - + }, + this, + dropzoneno + ); + markerspananchor.set('tabIndex', 0); + } } } } From 267ccc734717651ce640758bf47053af5e8764aa Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 19 Nov 2011 15:15:29 +0700 Subject: [PATCH 060/178] MDL-47494 ddmarker: NOBUG oops. Finish code for moving and deleting files related to question --- question/type/ddmarker/questiontype.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index baabc417938..e11938e8924 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -253,7 +253,6 @@ class qtype_ddmarker extends qtype_ddtoimage_base { $fs->move_area_files_to_new_context($oldcontextid, $newcontextid, 'qtype_ddmarker', 'bgimage', $questionid); - $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); } @@ -269,12 +268,6 @@ class qtype_ddmarker extends qtype_ddtoimage_base { parent::delete_files($questionid, $contextid); - $dragids = $DB->get_records_menu('qtype_ddimageortext_drags', - array('questionid' => $questionid), 'id', 'id,1'); - foreach ($dragids as $dragid => $notused) { - $fs->delete_area_files($contextid, 'qtype_ddimageortext', 'dragimage', $dragid); - } - $this->delete_files_in_combined_feedback($questionid, $contextid); } From fd1be85cbe021d5a30221c5ff9ebe0f670ce2d29 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 19 Nov 2011 17:59:56 +0700 Subject: [PATCH 061/178] MDL-47494 ddmarker: NOBUG implement import / export of question type --- question/type/ddmarker/edit_ddmarker_form.php | 4 +- question/type/ddmarker/questiontype.php | 90 ++++++++++++++++++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index a020313ccef..bbc4845b0ee 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -136,7 +136,7 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { $repeated[] = $mform->createElement('checkbox', 'hintshownumcorrect', get_string('options', 'question'), get_string('shownumpartscorrect', 'question')); - $repeated[] = $mform->createElement('checkbox', 'hintstatewhichincorrect', + $repeated[] = $mform->createElement('checkbox', 'hintoptions', '', get_string('stateincorrectlyplaced', 'qtype_ddmarker')); $repeated[] = $mform->createElement('checkbox', 'hintclearwrong', @@ -198,7 +198,7 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { $question->hintoptions = array(); foreach ($question->hints as $hint) { - $question->hintstatewhichincorrect[] = $hint->options; + $question->hintoptions[] = $hint->options; } return $question; diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index e11938e8924..7d50dfe38b5 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -192,7 +192,7 @@ class qtype_ddmarker extends qtype_ddtoimage_base { if ($withparts) { $clearwrong = !empty($formdata->hintclearwrong[$i]); $shownumcorrect = !empty($formdata->hintshownumcorrect[$i]); - $statewhichincorrect = !empty($formdata->hintstatewhichincorrect[$i]); + $statewhichincorrect = !empty($formdata->hintoptions[$i]); } if (empty($formdata->hint[$i]['text']) && empty($clearwrong) && @@ -271,4 +271,92 @@ class qtype_ddmarker extends qtype_ddtoimage_base { $this->delete_files_in_combined_feedback($questionid, $contextid); } + public function export_to_xml($question, qformat_xml $format, $extra = null) { + $fs = get_file_storage(); + $contextid = $question->contextid; + $output = ''; + + if ($question->options->shuffleanswers) { + $output .= " \n"; + } + if ($question->options->showmisplaced) { + $output .= " \n"; + } + $output .= $format->write_combined_feedback($question->options, + $question->id, + $question->contextid); + $files = $fs->get_area_files($contextid, 'qtype_ddmarker', 'bgimage', $question->id); + $output .= " ".$this->write_files($files, 2)."\n";; + + foreach ($question->options->drags as $drag) { + $files = + $fs->get_area_files($contextid, 'qtype_ddmarker', 'dragimage', $drag->id); + $output .= " \n"; + $output .= " {$drag->no}\n"; + $output .= $format->writetext($drag->label, 3)."\n"; + if ($drag->infinite) { + $output .= " \n"; + } + $output .= " \n"; + } + foreach ($question->options->drops as $drop) { + $output .= " \n"; + $output .= " {$drop->no}\n"; + $output .= " {$drop->shape}\n"; + $output .= " {$drop->coords}\n"; + $output .= " {$drop->choice}\n"; + $output .= " \n"; + } + + return $output; + } + + public function import_from_xml($data, $question, qformat_xml $format, $extra=null) { + if (!isset($data['@']['type']) || $data['@']['type'] != 'ddmarker') { + return false; + } + + $question = $format->import_headers($data); + $question->qtype = 'ddmarker'; + + $question->shuffleanswers = array_key_exists('shuffleanswers', + $format->getpath($data, array('#'), array())); + $question->showmisplaced = array_key_exists('showmisplaced', + $format->getpath($data, array('#'), array())); + + $filexml = $format->getpath($data, array('#', 'file'), array()); + $question->bgimage = $this->import_files_to_draft_file_area($format, $filexml); + $drags = $data['#']['drag']; + $question->drags = array(); + + foreach ($drags as $dragxml) { + $dragno = $format->getpath($dragxml, array('#', 'no', 0, '#'), 0); + $dragindex = $dragno -1; + $question->drags[$dragindex] = array(); + $question->drags[$dragindex]['label'] = + $format->getpath($dragxml, array('#', 'text', 0, '#'), '', true); + $question->drags[$dragindex]['infinite'] = array_key_exists('infinite', $dragxml['#']); + + } + + $drops = $data['#']['drop']; + $question->drops = array(); + foreach ($drops as $dropxml) { + $dropno = $format->getpath($dropxml, array('#', 'no', 0, '#'), 0); + $dropindex = $dropno -1; + $question->drops[$dropindex] = array(); + $question->drops[$dropindex]['choice'] = + $format->getpath($dropxml, array('#', 'choice', 0, '#'), 0); + $question->drops[$dropindex]['shape'] = + $format->getpath($dropxml, array('#', 'shape', 0, '#'), ''); + $question->drops[$dropindex]['coords'] = + $format->getpath($dropxml, array('#', 'coords', 0, '#'), ''); + } + + $format->import_combined_feedback($question, $data, true); + $format->import_hints($question, $data, true, true); + + return $question; + } + } From fb151998650cec05ffe8cb810778d6ee94e4b520 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 19 Nov 2011 19:30:34 +0700 Subject: [PATCH 062/178] MDL-47494 ddmarker: NOBUG implemented backup and restore. --- .../moodle2/backup_qtype_ddmarker_plugin.class.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php b/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php index 5ddaaea2137..d304edb9886 100644 --- a/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php +++ b/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php @@ -49,17 +49,19 @@ class backup_qtype_ddmarker_plugin extends backup_qtype_plugin { $dds = new backup_nested_element($qtype, array('id'), array( 'shuffleanswers', 'correctfeedback', 'correctfeedbackformat', 'partiallycorrectfeedback', 'partiallycorrectfeedbackformat', - 'incorrectfeedback', 'incorrectfeedbackformat', 'shownumcorrect')); + 'incorrectfeedback', 'incorrectfeedbackformat', 'shownumcorrect', + 'showmisplaced') + ); $pluginwrapper->add_child($dds); $drags = new backup_nested_element('drags'); $drag = new backup_nested_element('drag', array('id'), - array('no', 'draggroup', 'infinite', 'label')); + array('no', 'infinite', 'label')); $drops = new backup_nested_element('drops'); $drop = new backup_nested_element('drop', array('id'), - array('no', 'xleft', 'ytop', 'choice', 'label')); + array('no', 'shape', 'coords', 'choice')); $dds->set_source_table("qtype_{$qtype}", array('questionid' => backup::VAR_PARENTID)); @@ -91,7 +93,6 @@ class backup_qtype_ddmarker_plugin extends backup_qtype_plugin { 'partiallycorrectfeedback' => 'question_created', 'incorrectfeedback' => 'question_created', - 'bgimage' => 'question_created', - 'dragimage' => "qtype_{$qtype}_drags"); + 'bgimage' => 'question_created'); } } From 5e0f5f52de4c5c2078ea64daa21f38ecd54994d6 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 23 Nov 2011 11:51:21 +0700 Subject: [PATCH 063/178] MDL-47494 ddmarker: NOBUG fixing CSS selectors so that they apply style to highlighted marker text --- question/type/ddmarker/styles.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 40ae654f66e..c1239c77c67 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -51,7 +51,7 @@ form.mform fieldset#previewareaheader div.draghome, form.mform fieldset#previewa *font:x-small; /* for IE in quirks mode */ } .que.ddmarker div.dragitems span.markertext, -.que.ddmarker div.dropzones span.markertext, +.que.ddmarker div.markertexts span.markertext, form.mform fieldset#previewareaheader div.markertexts span.markertext { margin: 5px; z-index: 3; @@ -69,7 +69,7 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -khtml-border-radius: 10px; border-radius: 10px; } -.que.ddmarker div.dropzones span.markertext { +.que.ddmarker div.markertexts span.markertext { z-index: 2; background-color: yellow; border-style : solid; From 14230fd6dc5935ffb5cfef167ae288c1c5284e01 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 23 Nov 2011 12:31:24 +0700 Subject: [PATCH 064/178] MDL-47494 ddmarker: NOBUG fix question statistics in quiz statistics report --- question/type/ddmarker/question.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 0b6eb5fc82f..f75e0576a7a 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -304,11 +304,11 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { foreach ($this->places as $placeno => $place) { if (isset($hits[$placeno])) { $shuffledchoiceno = $this->get_right_choice_for($placeno); - $choiceno = $this->get_selected_choice(1, $shuffledchoiceno); + $choice = $this->get_selected_choice(1, $shuffledchoiceno); $parts[$placeno] = new question_classified_response( - $choiceno, - $this->choices[$choiceno]->summarise(), - 1 / count($this->places)); + $choice->no, + $choice->summarise(), + 1 / count($this->places)); } else { $parts[$placeno] = question_classified_response::no_response(); } From bd3406adf9d3301cc6bbf8fd9953a5462bfc85b4 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 23 Nov 2011 20:16:39 +0700 Subject: [PATCH 065/178] MDL-47494 ddmarker: NOBUG fix to silly typo that was causing response summary not to be shown --- question/type/ddmarker/question.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index f75e0576a7a..3ede3e015d0 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -358,7 +358,7 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { $goodhits[] = "{".$place->summarise()." -> ". $choice->summarise(). "}"; } } - if (count($goodhits)) { + if (count($goodhits) == 0) { return null; } return implode(', ', $goodhits); From a709dca58b415195aee6fb6bee18482a45c9ac14 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 25 Nov 2011 17:47:42 +0700 Subject: [PATCH 066/178] MDL-47494 ddmarker: NOBUG adding gridline overlay to drop background image in ddmarker. --- question/type/ddmarker/pix/grid.png | Bin 0 -> 295 bytes question/type/ddmarker/pix/grid.xcf | Bin 0 -> 15160 bytes question/type/ddmarker/styles.css | 6 +++++- question/type/ddmarker/yui/form/form.js | 6 +++++- 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 question/type/ddmarker/pix/grid.png create mode 100644 question/type/ddmarker/pix/grid.xcf diff --git a/question/type/ddmarker/pix/grid.png b/question/type/ddmarker/pix/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..233f84c42294830cf47d7cd03402e0c98023938a GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^DImn~MjINslNL_*o%P{grT?Cmw5a)#k*|F|?b6;C&HB=K`tD!5 zm2YV&IIysEG%yHGkiYWl%;YEEG|f&1ZNI__786ilN?IBHYT7RA{W|#|ML+>2mub6n zO1)$6f!PiX934R3-MLHO@j_%BSdIiuUz)e_v^0|oNVb7tB9OOx^Oe1dK)qngB3^B} zqFY+&43-7z0`bbH?J|eR0&Ve*owjSK9>PY^S`VY=Hf=TQS%IEq@O1TaS?83{1ONv; BXvY8m literal 0 HcmV?d00001 diff --git a/question/type/ddmarker/pix/grid.xcf b/question/type/ddmarker/pix/grid.xcf new file mode 100644 index 0000000000000000000000000000000000000000..f93874773dd4708cd0e853dad75702387173dbf3 GIT binary patch literal 15160 zcmeHO&2G~`5FUq?q%EOA{9QO0l|w2K)Ix<&Dxm5aBm@#iB-eF})x@siRBBE+5+M#q zJOj_cg~#9lcnZun&Ti{CPEe#Ka_Foy>)9D^y#98^vp-qaYaN@XcEfCVO^1*U6)}GU zWEjY9f=c^eiI|XXftEnGL3b3-_n~Y&#kK6>XKQxQYB_!fWnv{0Rbq|TvRtR;wmp6t zD-5cJoo>@H!=ULMZmw1vt{pUkwpooYwYGa;nOl#x;*`N`E7c~d9%NM2dUHI(RZqiX z%l3TN+}6*6u$v`Rvf7F7bxhB6|r?YmbTslHsb1gG$DCq9@-5g?d{F zb+<5x%T7tip;L8bq9X$V*PDL>M z3{Kh%Sf*0py<&8Oac{xL3w;`v7d{i+-6OjHlc@HEbu09zPsMTB+BijA@DWR z(xr>D`czQ)&rvBZvk?051Rvg5+qdi^w;i1LEG2!$;|KV8p7GFUOz_y9 zIyVGKH**QHw?y^EK&<6EIQQ|)1mQ?fJw6w**MGsE;OCM}i@{z5o82f6)ZJ z=`cGtvWl7zUB72ek0IZemzc!6)ew!$Nh z9tb6I(qXQL(zq~p9Xh_Bqa>W1u7HsAsYDj5RR0Y|M=Qv8QQUo~luEvP4>A+<090ii Rz8n#HU_i^22%@vM_zOwa6{!FK literal 0 HcmV?d00001 diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index c1239c77c67..0da4f62dbf5 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -110,4 +110,8 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -ms-filter: "progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000')"; /* For IE 5.5 - 7 */ filter: progid:DXImageTransform.Microsoft.Shadow(Strength=4, Direction=135, Color='#000000'); -} \ No newline at end of file +} +#page-question-type-ddmarker .ddarea .grid { + position: absolute; + background: url([[pix:qtype_ddmarker|grid]]) repeat scroll 0 0; +} diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index a6021186c7b..eea64823c2a 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -16,6 +16,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { '
'+ '
'+ '
'+ + '
'+ '
'); this.doc = this.doc_structure(this); this.stop_selector_events(); @@ -54,7 +55,6 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { e.target.addClass('constrained'); e.target.detach('load', this.constrain_image_size); }, - update_drop_zones : function () { @@ -77,6 +77,10 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { this.draw_drop_zone(dropzoneno, markertext, shape, coords, colourfordropzone, false); } + Y.one('div.ddarea .grid') + .setXY(this.doc.bg_img().getXY()) + .setStyle('width', this.doc.bg_img().get('width')) + .setStyle('height', this.doc.bg_img().get('height')); }, get_coords : function (dropzoneno) { From 3ed6823de47d7849e68a0493b8eafae36e50b65f Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 29 Nov 2011 07:05:28 +0700 Subject: [PATCH 067/178] MDL-47494 ddmarker: NOBUG some lang string changes --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 8bf6b42f6da..93bed5e2bae 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -23,7 +23,6 @@ */ $string['addingddmarker'] = 'Adding drag and drop markers'; -$string['addmoredropzones'] = 'Blanks for {no} more drop zones'; $string['addmoreitems'] = 'Blanks for {no} more markers'; $string['alttext'] = 'Alt text'; $string['answer'] = 'Answer'; @@ -31,7 +30,7 @@ $string['bgimage'] = 'Background image'; $string['coords'] = 'Coords'; $string['correctansweris'] = 'The correct answer is: {$a}'; $string['ddmarker'] = 'Drag and drop markers'; -$string['ddmarker_help'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; +$string['ddmarker_help'] = 'select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; $string['ddmarkersummary'] = 'Markers are dragged and dropped onto a background image.'; $string['draggableimage'] = 'Draggable image'; $string['draggableitem'] = 'Draggable item'; @@ -42,7 +41,7 @@ $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; $string['followingarewrong'] = 'The following markers have been placed in the wrong area : {$a}.'; -$string['followingarewrongandhighlighted'] = 'The following markers were not placed in the correct area : {$a}. The areas they should have been placed in are shown above.
Click on the markers above to see the areas they should have been placed in highlighted.'; +$string['followingarewrongandhighlighted'] = 'The following markers were incorrectly placed : {$a}. Highlighted marker(s) are now shown with the correct placement(s).
Click on the marker to highlight the allowed area.'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; $string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; @@ -62,7 +61,7 @@ $string['pleasedragatleastonemarker'] = 'Your answer is not complete, you must p $string['previewarea'] = 'Preview area -'; $string['previewareaheader'] = 'Preview'; $string['previewareamessage'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; -$string['refresh'] = 'Refresh Preview'; +$string['refresh'] = 'Refresh preview'; $string['clearwrongparts'] = 'Move incorrectly placed markers back to default start position below image'; $string['shape'] = 'Shape'; $string['shape_circle'] = 'Circle'; @@ -71,8 +70,8 @@ $string['shape_rectangle'] = 'Rectangle'; $string['shape_rectangle_coords'] = 'leftx, topy; width, height'; $string['shape_polygon'] = 'Polygon'; $string['shape_polygon_coords'] = 'x1,y1;x2,y2;x3,y3;x4,y4....'; -$string['showmisplaced'] = 'Highlight drop zones which have not had the correct drag item dropped on them'; -$string['shuffleimages'] = 'Shuffle Drag Items Each Time Question Is Attempted'; +$string['showmisplaced'] = 'Highlight drop zones which have not had the correct marker dropped on them'; +$string['shuffleimages'] = 'Shuffle drag items each time question is attempted'; $string['stateincorrectlyplaced'] = 'State which markers are incorrectly placed'; $string['summariseplace'] = '{$a->no}. {$a->text}'; $string['summariseplaceno'] = 'Drop zone {$a}'; From 7401a9146b421ed641e540de9c28ef1e5b1f33e2 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 29 Nov 2011 14:28:35 +0700 Subject: [PATCH 068/178] MDL-47494 ddmarker: OU-2409 adding a comma between misplaced markers in feedback --- question/type/ddmarker/renderer.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 6d676fc8226..2300694f174 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -108,14 +108,17 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { if ($question->showmisplaced && $qa->get_state()->is_finished()) { $wrongparts = $question->get_drop_zones_without_hit($response); if (count($wrongparts) !== 0) { - $wrongpartsstring = ''; + $wrongpartsstringspans = array(); foreach($wrongparts as $wrongpart) { - $wrongpartsstring .= html_writer::nonempty_tag('span', + $wrongpartsstringspans[] = html_writer::nonempty_tag('span', $wrongpart->markertext, array('class' => 'wrongpart')); } + $wrongpartsstring = join(', ', $wrongpartsstringspans); $output .= html_writer::nonempty_tag('span', - get_string('followingarewrongandhighlighted', 'qtype_ddmarker', $wrongpartsstring), - array('class' => 'wrongparts')); + get_string('followingarewrongandhighlighted', + 'qtype_ddmarker', + $wrongpartsstring), + array('class' => 'wrongparts')); } } From 5a34e0b0374125de7292f8be1ea56b2237bbec3d Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 29 Nov 2011 14:29:34 +0700 Subject: [PATCH 069/178] MDL-47494 ddmarker: OU-2407 allow tags in markers relaxed form validation and hardened js --- question/type/ddmarker/edit_ddmarker_form.php | 7 +++++-- question/type/ddmarker/lang/en/qtype_ddmarker.php | 2 +- question/type/ddmarker/yui/form/form.js | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php index bbc4845b0ee..9fc2d8efe76 100644 --- a/question/type/ddmarker/edit_ddmarker_form.php +++ b/question/type/ddmarker/edit_ddmarker_form.php @@ -17,6 +17,8 @@ require_once($CFG->dirroot.'/question/type/ddimageortext/edit_ddtoimage_form_base.php'); require_once($CFG->dirroot.'/question/type/ddmarker/shapes.php'); +define('QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER', '
'); + /** * Defines the editing form for the drag-and-drop images onto images question type. * @@ -241,9 +243,10 @@ class qtype_ddmarker_edit_form extends qtype_ddtoimage_edit_form_base { } for ($dragindex=0; $dragindex < $data['noitems']; $dragindex++) { $label = $data['drags'][$dragindex]['label']; - if ($label != strip_tags($label)) { + if ($label != strip_tags($label, QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER)) { $errors["drags[{$dragindex}]"] - = get_string('formerror_notagsallowed', 'qtype_ddmarker'); + = get_string('formerror_onlysometagsallowed', 'qtype_ddmarker', + QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER); } } return $errors; diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 93bed5e2bae..615355ecd8d 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -45,7 +45,7 @@ $string['followingarewrongandhighlighted'] = 'The following markers were incorre $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; $string['formerror_noitemselected'] = 'You have specified a drop zone but not chosen a marker that must be dragged to the zone'; $string['formerror_nosemicolons'] = 'There are no semicolons in your coordinates string. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; -$string['formerror_notagsallowed'] = 'No html tags are allowed in the label for a marker'; +$string['formerror_onlysometagsallowed'] = 'Only "{$a}" tags are allowed in the label for a marker'; $string['formerror_onlyusewholepositivenumbers'] = 'Please use only whole positive numbers to specify x,y coords and/or width and height of shapes. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['formerror_polygonmusthaveatleastthreepoints'] = 'For a polygon shape you need to specify at least 3 points. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['formerror_shapeoutsideboundsofbgimage'] = 'The shape you have defined goes out of the bounds of the background image'; diff --git a/question/type/ddmarker/yui/form/form.js b/question/type/ddmarker/yui/form/form.js index eea64823c2a..cec4478964f 100644 --- a/question/type/ddmarker/yui/form/form.js +++ b/question/type/ddmarker/yui/form/form.js @@ -100,7 +100,7 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { for (var i=0; i < this.form.get_form_value('noitems', []); i++) { var label = this.get_marker_text(i); if (label !== "") { - dragitemsoptions[i] = label; + dragitemsoptions[i] = Y.Escape.html(label); } } var selectedvalues = []; @@ -234,5 +234,5 @@ YUI.add('moodle-qtype_ddmarker-form', function(Y) { return new DDMARKER_FORM(config); } }, '@VERSION@', { - requires:['moodle-qtype_ddmarker-dd', 'form_filepicker', 'graphics'] + requires:['moodle-qtype_ddmarker-dd', 'form_filepicker', 'graphics', 'escape'] }); \ No newline at end of file From d89a34852406ef5f7aba112cab085787b6d985d3 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 29 Nov 2011 14:44:06 +0700 Subject: [PATCH 070/178] MDL-47494 ddmarker: OU-2422 Added 20px margin between drag items and image above --- question/type/ddmarker/styles.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 0da4f62dbf5..a0790ac2be6 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -34,6 +34,9 @@ .que.ddmarker .dragitems .draghome { margin : 10px; } +.que.ddmarker .dragitems { + margin-top : 20px; +} .que.ddmarker .dragitems.readonly .dragitem { cursor: auto; } From 9a0723c5d6639982a27d839c9d6debf1879d3d66 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 29 Nov 2011 19:10:09 +0700 Subject: [PATCH 071/178] MDL-47494 ddmarker: OU-2426 Work around bug seen in IE7 Graphics.destroy function was throwing an error. Seems there is a bug in the YUI library affecting IE 7 and 8. --- question/type/ddmarker/yui/dd/dd.js | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 9244fb8f121..3506ab1371e 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -421,17 +421,22 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }, this); if (this.graphics !== null) { - this.graphics.destroy(); + var oldshapes = this.graphics.get('shapes'); + for (var id in oldshapes) { + oldshapes[id].destroy(); + } } - this.restart_colours(); - this.graphics = new Y.Graphic( - {render:this.doc.top_node().one("div.ddarea div.dropzones")} - ); - for (var dropzoneno in this.get('dropzones')) { - var colourfordropzone = this.get_next_colour(); - var d = this.get('dropzones')[dropzoneno]; - this.draw_drop_zone(dropzoneno, d.markertext, - d.shape, d.coords, colourfordropzone, true); + if (this.get('dropzones').length !== 0) { + this.restart_colours(); + this.graphics = new Y.Graphic( + {render:this.doc.top_node().one("div.ddarea div.dropzones")} + ); + for (var dropzoneno in this.get('dropzones')) { + var colourfordropzone = this.get_next_colour(); + var d = this.get('dropzones')[dropzoneno]; + this.draw_drop_zone(dropzoneno, d.markertext, + d.shape, d.coords, colourfordropzone, true); + } } }, /** From 8d5d2155e4e51f704575c259257796a85d3f17db Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 30 Nov 2011 08:09:19 +0700 Subject: [PATCH 072/178] MDL-47494 ddmarker: OU-2454 Fix for text boxes in feedback in IE7 being broken in half by br --- question/type/ddmarker/styles.css | 1 + 1 file changed, 1 insertion(+) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index a0790ac2be6..4755192d14a 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -95,6 +95,7 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { -khtml-opacity: 0.6; opacity: 0.6; margin: 5px; + display : inline-block; } .que.ddmarker div.dragitems img.target { position:absolute; From 6e96abb029d103e7c0159403c191fd99aaaa9d81 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 30 Nov 2011 08:21:52 +0700 Subject: [PATCH 073/178] MDL-47494 ddmarker: OU-2453 Fixed drag redraw so now there are no duplicate drag items --- question/type/ddmarker/yui/dd/dd.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 3506ab1371e..57ddc20a446 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -66,7 +66,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { '.item' + itemno); }, drag_item_being_dragged : function(choiceno) { - return dragitemsarea.one('span.dragitem.yui3-dd-dragging.choice' + choiceno); + return dragitemsarea.one('span.dragitem.beingdragged.choice' + choiceno); }, drag_item_home : function (choiceno) { return dragitemsarea.one('span.draghome.choice' + choiceno); @@ -322,6 +322,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }).plug(Y.Plugin.DDConstrained, {constrain2node: this.doc.top_node()}); dd.after('drag:start', function(e){ var dragnode = e.target.get('node'); + dragnode.addClass('beingdragged'); var choiceno = this.get_choiceno_for_node(dragnode); var itemno = this.get_itemno_for_node(dragnode); if (itemno !== null) { @@ -332,6 +333,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, this); dd.after('drag:end', function(e) { var dragnode = e.target.get('node'); + dragnode.removeClass('beingdragged'); var choiceno = this.get_choiceno_for_node(dragnode); this.save_all_xy_for_choice(choiceno, dragnode); this.redraw_drags_and_drops(); @@ -346,7 +348,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { var dragitem = this.doc.drag_item_for_choice(choiceno, i); if (dragitem) { dragitem.removeClass('item'+i); - if (!dragitem.hasClass('yui3-dd-dragging')) { + if (!dragitem.hasClass('beingdragged')) { var bgimgxy = this.convert_to_bg_img_xy(dragitem.getXY()); if (this.xy_in_bgimg(bgimgxy)) { dragitem.removeClass('item'+i); @@ -396,7 +398,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { }, redraw_drags_and_drops : function() { this.doc.drag_items().each(function(item) { - //if (!item.hasClass('yui3-dd-dragging')){ + //if (!item.hasClass('beingdragged')){ item.addClass('unneeded'); //} }, this); @@ -407,7 +409,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { for (var i=0; i < coords.length; i++) { var dragitem; dragitem = this.doc.drag_item_for_choice(choiceno, i); - if (!dragitem || dragitem.hasClass('yui3-dd-dragging')) { + if (!dragitem || dragitem.hasClass('beingdragged')) { dragitem = this.clone_new_drag_item(dragitemhome, i); } else { dragitem.removeClass('unneeded'); @@ -416,7 +418,7 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { } }, this); this.doc.drag_items().each(function(item) { - if (item.hasClass('unneeded') && !item.hasClass('yui3-dd-dragging')) { + if (item.hasClass('unneeded') && !item.hasClass('beingdragged')) { item.remove(true); } }, this); From f76c454b049063250b4768a58a832633a3a2d0ca Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 2 Dec 2011 16:07:31 +0700 Subject: [PATCH 074/178] MDL-47494 ddmarker: OU-2409 fixed punctuation and style in auto generated hints for wrongly placed drags --- question/type/ddmarker/renderer.php | 12 ++++++------ question/type/ddmarker/styles.css | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 2300694f174..b146969f5a3 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -140,14 +140,14 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $question = $qa->get_question(); $response = $qa->get_last_qt_data(); if ($hint->statewhichincorrect) { - $wrongparts = $question->get_wrong_drags($response); - $wrongpartsstring = ''; - foreach($wrongparts as $wrongpart) { - $wrongpartsstring .= html_writer::nonempty_tag('span', - $wrongpart, array('class' => 'wrongpart')); + $wrongdrags = $question->get_wrong_drags($response); + $wrongparts = array(); + foreach($wrongdrags as $wrongdrag) { + $wrongparts[] = html_writer::nonempty_tag('span', + $wrongdrag, array('class' => 'wrongpart')); } $output .= html_writer::nonempty_tag('div', - get_string('followingarewrong', 'qtype_ddmarker', $wrongpartsstring), + get_string('followingarewrong', 'qtype_ddmarker', join(', ', $wrongparts)), array('class' => 'wrongparts')); } $output .= parent::hint($qa, $hint); diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 4755192d14a..50c43356edc 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -79,7 +79,7 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { border-width : 2px; border-colour : khaki; } -.que.ddmarker span.wrongparts span.wrongpart { +.que.ddmarker span.wrongpart { background-color: yellow; border-style : solid; border-width : 2px; From 6daf12fe1468a125598d2ed752ccf94e5b24344d Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 2 Dec 2011 16:44:39 +0700 Subject: [PATCH 075/178] MDL-47494 ddmarker: OU-2488 Improved English in coordinate validation error messages. --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 9 ++++++--- question/type/ddmarker/shapes.php | 10 +++++++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 615355ecd8d..24c2891ce5e 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -65,11 +65,14 @@ $string['refresh'] = 'Refresh preview'; $string['clearwrongparts'] = 'Move incorrectly placed markers back to default start position below image'; $string['shape'] = 'Shape'; $string['shape_circle'] = 'Circle'; -$string['shape_circle_coords'] = 'centerx, centery; radius'; +$string['shape_circle_lowercase'] = 'circle'; +$string['shape_circle_coords'] = 'x,y;r (where x,y are the xy coordinates of the center of the circle and r is the radius)'; $string['shape_rectangle'] = 'Rectangle'; -$string['shape_rectangle_coords'] = 'leftx, topy; width, height'; +$string['shape_rectangle_lowercase'] = 'rectangle'; +$string['shape_rectangle_coords'] = 'x,y;w,h (where x,y are the xy coordinates of the top left corner of the rectangle and w and h are the width and height of the rectangle)'; $string['shape_polygon'] = 'Polygon'; -$string['shape_polygon_coords'] = 'x1,y1;x2,y2;x3,y3;x4,y4....'; +$string['shape_polygon_lowercase'] = 'polygon'; +$string['shape_polygon_coords'] = 'x1,y1;x2,y2;x3,y3;x4,y4....(where x1, y1 are the x,y coordinates of the first vertex, x2, y2 are the x,y coordinates of the second, etc. You do not need to repeat the coordinates for the first vertex to close the polygon)'; $string['showmisplaced'] = 'Highlight drop zones which have not had the correct marker dropped on them'; $string['shuffleimages'] = 'Shuffle drag items each time question is attempted'; $string['stateincorrectlyplaced'] = 'State which markers are incorrectly placed'; diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index bde344c2469..b6018bf406d 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -67,7 +67,7 @@ abstract class qtype_ddmarker_shape { public function get_coords_interpreter_error() { if ($this->error) { $a = new stdClass(); - $a->shape = self::human_readable_name(); + $a->shape = self::human_readable_name(true); $a->coordsstring = self::human_readable_coords_format(); return get_string('formerror_'.$this->error, 'qtype_ddmarker', $a); } else { @@ -87,8 +87,12 @@ abstract class qtype_ddmarker_shape { protected static $classnameprefix = 'qtype_ddmarker_shape_'; - public static function human_readable_name() { - return get_string('shape_'.self::name(), 'qtype_ddmarker'); + public static function human_readable_name($lowercase = false) { + $stringid = 'shape_'.self::name(); + if ($lowercase) { + $stringid .= '_lowercase'; + } + return get_string($stringid, 'qtype_ddmarker'); } public static function human_readable_coords_format() { From 53c8848761c2baf5123d879e754ea326b734da99 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 2 Dec 2011 20:19:30 +0700 Subject: [PATCH 076/178] MDL-47494 ddmarker: OU-2493 Assigned some extra keys to move markers about the screen --- question/type/ddmarker/yui/dd/dd.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index 57ddc20a446..f9e53042bd3 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -519,6 +519,10 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { '38': 'up', // Up arrow '39': 'right', // Right arrow '40': 'down', // Down arrow + '65': 'left', // a + '87': 'up', // w + '68': 'right', // d + '83': 'down', // s '27': 'remove' // Escape }, From ac9c8df673b452282f93b374c074165c3dc9ec5a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 2 Dec 2011 20:20:19 +0700 Subject: [PATCH 077/178] MDL-47494 ddmarker: OU-2489 Using js instead of styling to position markers correctly on home --- question/type/ddmarker/styles.css | 21 ++------------------- question/type/ddmarker/yui/dd/dd.js | 10 ++++++---- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/question/type/ddmarker/styles.css b/question/type/ddmarker/styles.css index 50c43356edc..53b12e5b775 100644 --- a/question/type/ddmarker/styles.css +++ b/question/type/ddmarker/styles.css @@ -7,25 +7,10 @@ border: 1px solid #000000; } -.que.ddmarker .draghome, form.mform fieldset#previewareaheader .draghome { +.que.ddmarker .draghome img, .que.ddmarker .draghome span { visibility : hidden; } -.que.ddmarker div.draghome, form.mform fieldset#previewareaheader div.draghome { - border: 1px dashed black; - cursor: move; - background-color: #B0C4DE; - display: -moz-inline-stack; - display:inline-block; - height: auto; - width: auto; - zoom: 1; - *display: inline; -} -.que.ddmarker .dragitems .dragitem, -.que.ddmarker .dragitems .draghome { - vertical-align: top; -} .que.ddmarker .dragitems .dragitem { cursor: move; position: absolute; @@ -35,7 +20,7 @@ margin : 10px; } .que.ddmarker .dragitems { - margin-top : 20px; + margin-top : 10px; } .que.ddmarker .dragitems.readonly .dragitem { cursor: auto; @@ -63,8 +48,6 @@ form.mform fieldset#previewareaheader div.markertexts span.markertext { padding: 5px; display: -moz-inline-stack; display:inline-block; - height: auto; - width: auto; zoom: 1; *display: inline; -moz-border-radius: 10px; diff --git a/question/type/ddmarker/yui/dd/dd.js b/question/type/ddmarker/yui/dd/dd.js index f9e53042bd3..1d93d478ba9 100644 --- a/question/type/ddmarker/yui/dd/dd.js +++ b/question/type/ddmarker/yui/dd/dd.js @@ -451,7 +451,6 @@ YUI.add('moodle-qtype_ddmarker-dd', function(Y) { var infinite = input.hasClass('infinite'); var dragging = (null !== this.doc.drag_item_being_dragged(choiceno)); var coords = []; - var dragitemhome = this.doc.drag_item_home(choiceno); if (fv !== '') { var coordsstrings = fv.split(';'); for (var i=0; i Date: Fri, 2 Dec 2011 22:59:15 +0700 Subject: [PATCH 078/178] MDL-47494 ddmarker: OU-2488 Proper British English --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 24c2891ce5e..3dd5b31313f 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -66,7 +66,7 @@ $string['clearwrongparts'] = 'Move incorrectly placed markers back to default st $string['shape'] = 'Shape'; $string['shape_circle'] = 'Circle'; $string['shape_circle_lowercase'] = 'circle'; -$string['shape_circle_coords'] = 'x,y;r (where x,y are the xy coordinates of the center of the circle and r is the radius)'; +$string['shape_circle_coords'] = 'x,y;r (where x,y are the xy coordinates of the centre of the circle and r is the radius)'; $string['shape_rectangle'] = 'Rectangle'; $string['shape_rectangle_lowercase'] = 'rectangle'; $string['shape_rectangle_coords'] = 'x,y;w,h (where x,y are the xy coordinates of the top left corner of the rectangle and w and h are the width and height of the rectangle)'; From 0aa089c778edd036eadd94b2e02fa644c2570f6a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 26 Jan 2012 16:56:36 +0700 Subject: [PATCH 079/178] MDL-47494 ddmarker: NOBUG return null for random_guess_score as it is incalculable there are no shown drop zones and we don't know how many drag items have to be dragged to the image. We could do the complicated calculation to find the area of a polygon and compare it to the area of the image but it is an overly complex a calculation I feel outside the scope of the current project. --- question/type/ddmarker/question.php | 4 ++++ question/type/ddmarker/questiontype.php | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 3ede3e015d0..d6872963aee 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -363,6 +363,10 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } return implode(', ', $goodhits); } + + public function get_random_guess_score() { + return null; + } } /** diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 7d50dfe38b5..a9ca0ae3355 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -359,4 +359,8 @@ class qtype_ddmarker extends qtype_ddtoimage_base { return $question; } + public function get_random_guess_score($questiondata) { + return null; + } + } From 1cae63adf739b024376c318ee9e90dbd077ac1db Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 26 Jan 2012 18:00:45 +0700 Subject: [PATCH 080/178] MDL-47494 ddmarker: NOBUG making polygon::is_point_in_shape more robust --- question/type/ddmarker/shapes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index b6018bf406d..36cfb5f36cf 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -263,7 +263,7 @@ class qtype_ddmarker_shape_polygon extends qtype_ddmarker_shape { return array($this->minxy, $this->maxxy); } public function is_point_in_shape($xy) { - $pointatinfinity = new qtype_ddmarker_point(-10000, $xy[1]); + $pointatinfinity = new qtype_ddmarker_point(-1000000, $xy[1]+1); $pointtotest = new qtype_ddmarker_point($xy[0], $xy[1]); $testsegment = new qtype_ddmarker_segment($pointatinfinity, $pointtotest); $windingnumber = 0; @@ -414,7 +414,7 @@ class qtype_ddmarker_segment { public function intersection_point(qtype_ddmarker_segment $v) { $d = (($v->b->y - $v->a->y) * ($this->b->x - $this->a->x)) -(($v->b->x - $v->a->x) * ($this->b->y - $this->a->y)); - if ($d !== 0) { // The lines intersect at a point somewhere + if ($d != 0) { // The lines intersect at a point somewhere $ua = (($v->b->x-$v->a->x)*($this->a->y-$v->a->y) -($v->b->y-$v->a->y)*($this->a->x-$v->a->x))/$d; $ub = (($this->b->x-$this->a->x)*($this->a->y-$v->a->y) From cf25df7a3056b9f9c7097584ffd5247c5822c2a8 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 29 Jan 2012 14:56:09 +0700 Subject: [PATCH 081/178] MDL-47494 ddmarker: NOBUG fixing obvious typo and redundant logic test. --- question/type/ddmarker/question.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index d6872963aee..9b145ed90af 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -115,14 +115,13 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } $coords1 = explode(';', $value1); $coords2 = explode(';', $value2); - if (count($coords1) !== count($coords1)) { + if (count($coords1) !== count($coords2)) { return false; } else if (count($coords1) === 0) { return true; } else { $valuesinbotharrays = array_intersect($coords1, $coords2); - return (count($valuesinbotharrays) == count($coords1) && - count($valuesinbotharrays) == count($coords2)); + return (count($valuesinbotharrays) == count($coords1)); } } public function get_validation_error(array $response) { From 317caf1150e679cdd1c5d97db29508cc6a7bc7d2 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 29 Jan 2012 15:11:57 +0700 Subject: [PATCH 082/178] MDL-47494 ddmarker: NOBUG fixing potential issue with use of core php array_intersect func --- question/type/ddmarker/question.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 9b145ed90af..3e051986887 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -120,10 +120,32 @@ class qtype_ddmarker_question extends qtype_ddtoimage_question_base { } else if (count($coords1) === 0) { return true; } else { - $valuesinbotharrays = array_intersect($coords1, $coords2); + $valuesinbotharrays = $this->array_intersect_fixed($coords1, $coords2); return (count($valuesinbotharrays) == count($coords1)); } } + + /** + * + * This function is a variation of array_intersect that checks for the existence of duplicate + * array values too. + * @author dml at nm dot ru (taken from comments on php manual) + * @param array $array1 + * @param array $array2 + * @return bool whether array1 and array2 contain the same values including duplicate values + */ + protected function array_intersect_fixed($array1, $array2) { + $result = array(); + foreach ($array1 as $val) { + if (($key = array_search($val, $array2, TRUE))!==false) { + $result[] = $val; + unset($array2[$key]); + } + } + return $result; + } + + public function get_validation_error(array $response) { if ($this->is_complete_response($response)) { return ''; From 389c6f5589a69a9864abdca3e1081367fda568ef Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 29 Jan 2012 17:20:00 +0700 Subject: [PATCH 083/178] MDL-47494 ddmarker: NOBUG fixing testquestion.php unit tests --- question/type/ddmarker/simpletest/helper.php | 44 ++--- .../type/ddmarker/simpletest/testquestion.php | 174 +++++++++++------- 2 files changed, 126 insertions(+), 92 deletions(-) diff --git a/question/type/ddmarker/simpletest/helper.php b/question/type/ddmarker/simpletest/helper.php index e0b2b16b50a..b52c82fb108 100644 --- a/question/type/ddmarker/simpletest/helper.php +++ b/question/type/ddmarker/simpletest/helper.php @@ -57,20 +57,18 @@ class qtype_ddmarker_test_helper extends question_test_helper { test_question_maker::set_standard_combined_feedback_fields($dd); $dd->choices = $this->make_choice_structure(array( - new qtype_ddmarker_drag_item('quick', 1, 1), - new qtype_ddmarker_drag_item('fox', 2, 1), - new qtype_ddmarker_drag_item('lazy', 1, 2), - new qtype_ddmarker_drag_item('dog', 2, 2) + new qtype_ddmarker_drag_item('quick', 1, 0), + new qtype_ddmarker_drag_item('fox', 2, 0), + new qtype_ddmarker_drag_item('lazy', 3, 0) )); $dd->places = $this->make_place_structure(array( - new qtype_ddmarker_drop_zone('', 1, 1), - new qtype_ddmarker_drop_zone('', 2, 1), - new qtype_ddmarker_drop_zone('', 3, 2), - new qtype_ddmarker_drop_zone('', 4, 2) + new qtype_ddmarker_drop_zone(1, 'circle', '50,50;50'), + new qtype_ddmarker_drop_zone(2, 'rectangle', '100,0;100,100'), + new qtype_ddmarker_drop_zone(3, 'polygon', '0,100;200,100;200,200;0,200') )); - $dd->rightchoices = array(1 => 1, 2 => 2, 3 => 1, 4 => 2); + $dd->rightchoices = array(1 => 1, 2 => 2, 3 => 3); return $dd; } @@ -78,10 +76,11 @@ class qtype_ddmarker_test_helper extends question_test_helper { protected function make_choice_structure($choices) { $choicestructure = array(); foreach ($choices as $choice) { - if (!isset($choicestructure[$choice->group])) { - $choicestructure[$choice->group] = array(); + $group = $choice->choice_group(); + if (!isset($choicestructure[$group])) { + $choicestructure[$group] = array(); } - $choicestructure[$choice->group][$choice->no] = $choice; + $choicestructure[$group][$choice->no] = $choice; } return $choicestructure; } @@ -104,9 +103,8 @@ class qtype_ddmarker_test_helper extends question_test_helper { test_question_maker::initialise_a_question($dd); $dd->name = 'Drag-and-drop words into sentences question'; - $dd->questiontext = 'Fill in the operators to make this equation work: ' . - '7 [[1]] 11 [[2]] 13 [[1]] 17 [[2]] 19 = 3'; - $dd->generalfeedback = 'This sentence uses each letter of the alphabet.'; + $dd->questiontext = 'Fill in the operators to make this equation work: '; + $dd->generalfeedback = 'Hmmmm...'; $dd->qtype = question_bank::get_qtype('ddmarker'); $dd->shufflechoices = true; @@ -114,17 +112,19 @@ class qtype_ddmarker_test_helper extends question_test_helper { test_question_maker::set_standard_combined_feedback_fields($dd); $dd->choices = $this->make_choice_structure(array( - new qtype_ddmarker_drag_item('+', 1, 1), - new qtype_ddmarker_drag_item('-', 2, 1) + new qtype_ddmarker_drag_item('+', 1, 1), + new qtype_ddmarker_drag_item('-', 2, 1), + new qtype_ddmarker_drag_item('*', 3, 1), + new qtype_ddmarker_drag_item('/', 4, 1) + )); $dd->places = $this->make_place_structure(array( - new qtype_ddmarker_drop_zone('', 1, 1), - new qtype_ddmarker_drop_zone('', 2, 1), - new qtype_ddmarker_drop_zone('', 3, 1), - new qtype_ddmarker_drop_zone('', 4, 1) + new qtype_ddmarker_drop_zone(1, 'circle', '50,50;50'), + new qtype_ddmarker_drop_zone(2, 'rectangle', '100,0;100,100'), + new qtype_ddmarker_drop_zone(3, 'polygon', '0,100;100,100;100,200;0,200') )); - $dd->rightchoices = array(1 => 1, 2 => 2, 3 => 1, 4 => 2); + $dd->rightchoices = array(1 => 1, 2 => 1, 3 => 1); return $dd; } diff --git a/question/type/ddmarker/simpletest/testquestion.php b/question/type/ddmarker/simpletest/testquestion.php index 3ab04998412..4616e14f9b3 100644 --- a/question/type/ddmarker/simpletest/testquestion.php +++ b/question/type/ddmarker/simpletest/testquestion.php @@ -41,21 +41,18 @@ class qtype_ddmarker_question_test extends UnitTestCase { public function test_get_question_summary() { $dd = test_question_maker::make_question('ddmarker'); $this->assertEqual('The quick brown fox jumped over the lazy dog.; '. - '[[Drop zone 1]] -> {1. quick / 2. fox}; '. - '[[Drop zone 2]] -> {1. quick / 2. fox}; '. - '[[Drop zone 3]] -> {1. lazy / 2. dog}; '. - '[[Drop zone 4]] -> {1. lazy / 2. dog}', + '[[Drop zone 1]] -> {quick / fox / lazy}; '. + '[[Drop zone 2]] -> {quick / fox / lazy}; '. + '[[Drop zone 3]] -> {quick / fox / lazy}', $dd->get_question_summary()); } public function test_get_question_summary_maths() { $dd = test_question_maker::make_question('ddmarker', 'maths'); - $this->assertEqual('Fill in the operators to make this equation work: '. - '7 [[1]] 11 [[2]] 13 [[1]] 17 [[2]] 19 = 3; '. - '[[Drop zone 1]] -> {1. + / 2. -}; '. - '[[Drop zone 2]] -> {1. + / 2. -}; '. - '[[Drop zone 3]] -> {1. + / 2. -}; '. - '[[Drop zone 4]] -> {1. + / 2. -}', + $this->assertEqual('Fill in the operators to make this equation work:; '. + '[[Drop zone 1]] -> {+ / - / * / /}; '. + '[[Drop zone 2]] -> {+ / - / * / /}; '. + '[[Drop zone 3]] -> {+ / - / * / /}', $dd->get_question_summary()); } @@ -64,11 +61,12 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $this->assertEqual('Drop zone 1 -> {1. quick} '. - 'Drop zone 2 -> {1. quick} '. - 'Drop zone 3 -> {1. lazy} '. - 'Drop zone 4 -> {1. lazy}', - $dd->summarise_response(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + $this->assertEqual('{Drop zone 1 -> quick}, '. + '{Drop zone 2 -> fox}, '. + '{Drop zone 3 -> lazy}', + $dd->summarise_response(array('c1' => '50,50', + 'c2' => '150,50', + 'c3' => '50,150'))); } public function test_summarise_response_maths() { @@ -76,21 +74,22 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $this->assertEqual('Drop zone 1 -> {1. +} '. - 'Drop zone 2 -> {2. -} '. - 'Drop zone 3 -> {1. +} '. - 'Drop zone 4 -> {2. -}', - $dd->summarise_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + $this->assertEqual('{Drop zone 1 -> +}, '. + '{Drop zone 2 -> +}, '. + '{Drop zone 3 -> +}', + $dd->summarise_response(array('c1' => '50,50;150,50;50,150', + 'c2' => '', + 'c3' => ''))); } public function test_get_random_guess_score() { $dd = test_question_maker::make_question('ddmarker'); - $this->assertEqual(0.5, $dd->get_random_guess_score()); + $this->assertEqual(null, $dd->get_random_guess_score()); } public function test_get_random_guess_score_maths() { $dd = test_question_maker::make_question('ddmarker', 'maths'); - $this->assertEqual(0.5, $dd->get_random_guess_score()); + $this->assertEqual(null, $dd->get_random_guess_score()); } public function test_get_right_choice_for() { @@ -100,6 +99,7 @@ class qtype_ddmarker_question_test extends UnitTestCase { $this->assertEqual(1, $dd->get_right_choice_for(1)); $this->assertEqual(2, $dd->get_right_choice_for(2)); + $this->assertEqual(3, $dd->get_right_choice_for(3)); } public function test_get_right_choice_for_maths() { @@ -108,9 +108,8 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->start_attempt(new question_attempt_step(), 1); $this->assertEqual(1, $dd->get_right_choice_for(1)); - $this->assertEqual(2, $dd->get_right_choice_for(2)); + $this->assertEqual(1, $dd->get_right_choice_for(2)); $this->assertEqual(1, $dd->get_right_choice_for(3)); - $this->assertEqual(2, $dd->get_right_choice_for(4)); } public function test_clear_wrong_from_response() { @@ -118,8 +117,8 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $initialresponse = array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'); - $this->assertEqual(array('p1' => '1', 'p2' => '', 'p3' => '1', 'p4' => ''), + $initialresponse = array('c1' => '50,50', 'c2' => '100,100', 'c3' => '100,100;200,200'); + $this->assertEqual(array('c1' => '50,50', 'c2' => '', 'c3' => ''), $dd->clear_wrong_from_response($initialresponse)); } @@ -128,10 +127,18 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $this->assertEqual(array(2, 4), - $dd->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '2'))); - $this->assertEqual(array(4, 4), - $dd->get_num_parts_right(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + //second returned param in array is the max of correct choices or the actual number of + //items dragged. + $response1 = array('c1' => '50,50', 'c2' => '100,100', 'c3' => '100,100;200,200'); + $this->assertEqual(array(1, 4), $dd->get_num_parts_right($response1)); + $response2 = array('c1' => '50,50;150,50;50,150', + 'c2' => '100,100', + 'c3' => '100,100;200,200'); + $this->assertEqual(array(1, 6), $dd->get_num_parts_right($response2)); + $response3 = array('c1' => '50,50;150,50;50,150', + 'c2' => '', + 'c3' => ''); + $this->assertEqual(array(1, 3), $dd->get_num_parts_right($response3)); } public function test_get_num_parts_right_maths() { @@ -139,9 +146,9 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $this->assertEqual(array(2, 4), + $this->assertEqual(array(3, 3), $dd->get_num_parts_right(array( - 'p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + 'c1' => '50,50;150,50;50,150', 'c2' => '', 'c3' => ''))); } public function test_get_expected_data() { @@ -149,7 +156,7 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->start_attempt(new question_attempt_step(), 1); $this->assertEqual( - array('p1' => PARAM_INT, 'p2' => PARAM_INT, 'p3' => PARAM_INT, 'p4' => PARAM_INT), + array('c1' => PARAM_NOTAGS, 'c2' => PARAM_NOTAGS, 'c3' => PARAM_NOTAGS), $dd->get_expected_data() ); } @@ -159,8 +166,8 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'), - $dd->get_correct_response()); + $this->assertEqual(array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150'), + $dd->get_correct_response()); } public function test_get_correct_response_maths() { @@ -168,8 +175,7 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->shufflechoices = false; $dd->start_attempt(new question_attempt_step(), 1); - $this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'), - $dd->get_correct_response()); + $this->assertEqual(array('c1' => '50,50;150,50;50,150'), $dd->get_correct_response()); } public function test_is_same_response() { @@ -178,23 +184,47 @@ class qtype_ddmarker_question_test extends UnitTestCase { $this->assertTrue($dd->is_same_response( array(), - array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => ''))); + array('c1' => '', 'c2' => '', 'c3' => '', 'c4' => ''))); $this->assertFalse($dd->is_same_response( array(), - array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => ''))); + array('c1' => '100,100', 'c2' => '', 'c3' => '', 'c4' => ''))); $this->assertFalse($dd->is_same_response( - array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => ''), - array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => ''))); + array('c1' => '', 'c2' => '', 'c3' => '', 'c4' => ''), + array('c1' => '100,100', 'c2' => '', 'c3' => '', 'c4' => ''))); $this->assertTrue($dd->is_same_response( - array('p1' => '1', 'p2' => '2', 'p3' => '3', 'p4' => '4'), - array('p1' => '1', 'p2' => '2', 'p3' => '3', 'p4' => '4'))); + array('c1' => '100,100', 'c2' => '2', 'c3' => '3', 'c4' => '400,400'), + array('c1' => '100,100', 'c2' => '2', 'c3' => '3', 'c4' => '400,400'))); $this->assertFalse($dd->is_same_response( - array('p1' => '1', 'p2' => '2', 'p3' => '3', 'p4' => '4'), - array('p1' => '1', 'p2' => '2', 'p3' => '2', 'p4' => '4'))); + array('c1' => '100,100', 'c2' => '200,200', 'c3' => '300,300', 'c4' => '400,400'), + array('c1' => '100,100', 'c2' => '200,200', 'c3' => '200,200', 'c4' => '400,400'))); + + $this->assertTrue($dd->is_same_response( + array('c1' => '100,100;200,200', 'c2' => '', + 'c3' => '100,100;300,300', 'c4' => '400,400'), + array('c1' => '200,200;100,100', 'c2' => '', + 'c3' => '300,300;100,100', 'c4' => '400,400'))); + + $this->assertFalse($dd->is_same_response( + array('c1' => '100,100;200,200', 'c2' => '', + 'c3' => '100,100;400,300', 'c4' => '400,400'), + array('c1' => '200,200;100,100', 'c2' => '', + 'c3' => '300,300;100,100', 'c4' => '400,400'))); + + $this->assertTrue($dd->is_same_response( + array('c1' => '100,100;100,100;200,200', 'c2' => '', + 'c3' => '100,100;300,300', 'c4' => '400,400'), + array('c1' => '200,200;100,100;100,100', 'c2' => '', + 'c3' => '300,300;100,100', 'c4' => '400,400'))); + + $this->assertFalse($dd->is_same_response( + array('c1' => '100,100;100,100;200,200', 'c2' => '', + 'c3' => '100,100;300,300', 'c4' => '400,400'), + array('c1' => '200,200;100,100', 'c2' => '', + 'c3' => '300,300;100,100', 'c4' => '400,400'))); } public function test_is_complete_response() { $dd = test_question_maker::make_question('ddmarker'); @@ -202,10 +232,10 @@ class qtype_ddmarker_question_test extends UnitTestCase { $this->assertFalse($dd->is_complete_response(array())); $this->assertFalse($dd->is_complete_response( - array('p1' => '1', 'p2' => '1', 'p3' => '', 'p4' => ''))); - $this->assertFalse($dd->is_complete_response(array('p1' => '1'))); + array('c1' => '', 'c2' => '', 'c3' => ''))); + $this->assertFalse($dd->is_complete_response(array('c1' => ''))); $this->assertTrue($dd->is_complete_response( - array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + array('c1' => '300,300', 'c2' => '300,300', 'c3' => '300,300'))); } public function test_is_gradable_response() { @@ -214,12 +244,12 @@ class qtype_ddmarker_question_test extends UnitTestCase { $this->assertFalse($dd->is_gradable_response(array())); $this->assertFalse($dd->is_gradable_response( - array('p1' => '', 'p2' => '', 'p3' => '', 'p3' => ''))); + array('c1' => '', 'c2' => '', 'c3' => '', 'c3' => ''))); $this->assertTrue($dd->is_gradable_response( - array('p1' => '1', 'p2' => '1', 'p3' => ''))); - $this->assertTrue($dd->is_gradable_response(array('p1' => '1'))); + array('c1' => '300,300', 'c2' => '300,300', 'c3' => ''))); + $this->assertTrue($dd->is_gradable_response(array('c1' => '300,300'))); $this->assertTrue($dd->is_gradable_response( - array('p1' => '1', 'p2' => '1', 'p3' => '1'))); + array('c1' => '300,300', 'c2' => '300,300', 'c3' => '300,300'))); } public function test_grading() { @@ -228,11 +258,11 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->start_attempt(new question_attempt_step(), 1); $this->assertEqual(array(1, question_state::$gradedright), - $dd->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); - $this->assertEqual(array(0.25, question_state::$gradedpartial), - $dd->grade_response(array('p1' => '1'))); + $dd->grade_response(array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150'))); + $this->assertEqual(array(2/3, question_state::$gradedpartial), + $dd->grade_response(array('c1' => '50,50', 'c2' => '50,50', 'c3' => '100,150'))); $this->assertEqual(array(0, question_state::$gradedwrong), - $dd->grade_response(array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1'))); + $dd->grade_response(array('c1' => '150,50', 'c2' => '50,50', 'c3' => '100,50'))); } public function test_grading_maths() { @@ -241,11 +271,16 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->start_attempt(new question_attempt_step(), 1); $this->assertEqual(array(1, question_state::$gradedright), - $dd->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); - $this->assertEqual(array(0.5, question_state::$gradedpartial), - $dd->grade_response(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1'))); + $dd->grade_response(array('c1' => '50,50;150,50;50,150', 'c2' => '', 'c3' => ''))); + $this->assertEqual(array(0.75, question_state::$gradedpartial), + $dd->grade_response(array('c1' => '50,50;150,50;50,150', + 'c2' => '', 'c3' => '50,150'))); $this->assertEqual(array(0, question_state::$gradedwrong), - $dd->grade_response(array('p1' => '', 'p2' => '1', 'p3' => '2', 'p4' => '1'))); + $dd->grade_response(array('c1' => '', 'c2' => '50,50;150,50', 'c3' => '100,50'))); + $this->assertEqual(array(0, question_state::$gradedwrong), + $dd->grade_response(array('c1' => '300,300', + 'c2' => '50,50;150,50', + 'c3' => '100,50'))); } public function test_classify_response() { @@ -254,16 +289,15 @@ class qtype_ddmarker_question_test extends UnitTestCase { $dd->start_attempt(new question_attempt_step(), 1); $this->assertEqual(array( - 1 => new question_classified_response(1, '1. quick', 1), - 2 => new question_classified_response(2, '2. fox', 1), - 3 => new question_classified_response(1, '1. lazy', 1), - 4 => new question_classified_response(2, '2. dog', 1) - ), $dd->classify_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'))); + 1 => new question_classified_response(1, 'quick', 1/3), + 2 => new question_classified_response(2, 'fox', 1/3), + 3 => new question_classified_response(3, 'lazy', 1/3)), + $dd->classify_response(array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150'))); + $this->assertEqual(array( - 1 => question_classified_response::no_response(), - 2 => new question_classified_response(1, '1. quick', 0), - 3 => new question_classified_response(2, '2. dog', 0), - 4 => new question_classified_response(2, '2. dog', 1) - ), $dd->classify_response(array('p1' => '', 'p2' => '1', 'p3' => '2', 'p4' => '2'))); + 1 => new question_classified_response(1, 'quick', 1/3), + 2 => question_classified_response::no_response(), + 3 => question_classified_response::no_response()), + $dd->classify_response(array('c1' => '50,50', 'c2' => '100,150', 'c3' => '150,50'))); } } From 551fdcc830976609b569892d11c90cb10e975a9c Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 30 Jan 2012 21:00:04 +0700 Subject: [PATCH 084/178] MDL-47494 ddmarker: NOBUG fixing unit tests in testwalkthrough.php --- .../ddmarker/simpletest/testwalkthrough.php | 602 +++++++----------- 1 file changed, 231 insertions(+), 371 deletions(-) diff --git a/question/type/ddmarker/simpletest/testwalkthrough.php b/question/type/ddmarker/simpletest/testwalkthrough.php index a5049119605..eb6d493f083 100644 --- a/question/type/ddmarker/simpletest/testwalkthrough.php +++ b/question/type/ddmarker/simpletest/testwalkthrough.php @@ -38,14 +38,26 @@ require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); */ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { - protected function get_contains_drag_image_home_expectation($dragitemno, $choice, $group) { - $class = 'group' . $group; - $class .= ' draghome dragitemhomes' . $dragitemno. ' choice'.$choice.' yui3-cssfonts'; + protected function get_contains_draggable_marker_home_expectation($choice, $infinite) { + $class = 'draghome choice'.$choice; + if ($infinite) { + $class .= ' infinite'; + } $expectedattrs = array(); $expectedattrs['class'] = $class; - return new ContainsTagWithAttributes('div', $expectedattrs); + return new ContainsTagWithAttributes('span', $expectedattrs); + } + + protected function get_contains_hidden_expectation($choiceno, $value = null) { + $name = $this->quba->get_field_prefix($this->slot) .'c'. $choiceno; + $expectedattributes = array('type' => 'hidden', 'name' => s($name)); + $expectedattributes['class'] = "choices choice{$choiceno}"; + if (!is_null($value)) { + $expectedattributes['value'] = s($value); + } + return new ContainsTagWithAttributes('input', $expectedattributes); } public function test_interactive_behaviour() { @@ -53,8 +65,10 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Create a drag-and-drop question. $dd = test_question_maker::make_question('ddmarker'); $dd->hints = array( - new question_hint_with_parts(13, 'This is the first hint.', FORMAT_HTML, false, false), - new question_hint_with_parts(14, 'This is the second hint.', FORMAT_HTML, true, true), + new question_hint_ddmarker(13, 'This is the first hint.', + FORMAT_HTML, false, false, false), + new question_hint_ddmarker(14, 'This is the second hint.', + FORMAT_HTML, true, true, false), ); $dd->shufflechoices = false; $this->start_attempt_at_question($dd, 'interactive', 12); @@ -64,66 +78,48 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(3), $this->get_no_hint_visible_expectation()); + $completelywrong = array('c1' => '0,250', 'c2' => '100,250', 'c3' => '150,250'); // Save the wrong answer. - $this->process_submission(array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1')); + $this->process_submission($completelywrong); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 1), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '0,250'), + $this->get_contains_hidden_expectation(2, '100,250'), + $this->get_contains_hidden_expectation(3, '150,250'), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(3), $this->get_no_hint_visible_expectation()); // Submit the wrong answer. - $this->process_submission( - array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1', '-submit' => 1)); + $this->process_submission($completelywrong + array('-submit' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 1), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '0,250'), + $this->get_contains_hidden_expectation(2, '100,250'), + $this->get_contains_hidden_expectation(3, '150,250'), $this->get_contains_try_again_button_expectation(true), new PatternExpectation('/' . preg_quote(get_string('notcomplete', 'qbehaviour_interactive')) . '/'), @@ -137,18 +133,12 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', '2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', '1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', '2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', '1'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '0,250'), + $this->get_contains_hidden_expectation(2, '100,250'), + $this->get_contains_hidden_expectation(3, '150,250'), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation(), @@ -157,24 +147,18 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Submit the right answer. $this->process_submission( - array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2', '-submit' => 1)); + array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150', '-submit' => 1)); // Verify. $this->check_current_state(question_state::$gradedright); $this->check_current_mark(8); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', '1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', '2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', '1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', '2'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, '100,150'), $this->get_contains_submit_button_expectation(false), $this->get_contains_correct_expectation(), $this->get_no_hint_visible_expectation()); @@ -199,61 +183,43 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_does_not_contain_feedback_expectation()); // Save a partial answer. - $this->process_submission(array('p1' => '2', 'p2' => '1')); + $this->process_submission(array('c1' => '150,50', 'c2' => '50,50')); // Verify. - $this->check_current_state(question_state::$invalid); + $this->check_current_state(question_state::$complete); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '150,50'), + $this->get_contains_hidden_expectation(2, '50,50'), + $this->get_contains_hidden_expectation(3, ''), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); // Save the right answer. $this->process_submission( - array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')); + array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150')); // Verify. $this->check_current_state(question_state::$complete); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, '100,150'), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); @@ -265,18 +231,12 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_mark(12); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, '100,150'), $this->get_contains_correct_expectation()); // Change the right answer a bit. @@ -287,7 +247,7 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Verify. $this->check_current_state(question_state::$gradedpartial); - $this->check_current_mark(9); + $this->check_current_mark(8); } public function test_deferred_feedback_unanswered() { @@ -301,41 +261,29 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); $this->check_step_count(1); // Save a blank response. - $this->process_submission(array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => '')); + $this->process_submission(array('c1' => '', 'c2' => '', 'c3' => '')); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, ''), + $this->get_contains_hidden_expectation(2, ''), + $this->get_contains_hidden_expectation(3, ''), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); $this->check_step_count(1); @@ -347,10 +295,9 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_state(question_state::$gaveup); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2)); + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false)); } public function test_deferred_feedback_partial_answer() { @@ -364,40 +311,27 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); - // Save a partial response. - $this->process_submission(array('p1' => '1', 'p2' => '2', 'p3' => '', 'p4' => '')); + $this->process_submission(array('c1' => '50,50', 'c2' => '150,50', 'c3' => '')); // Verify. - $this->check_current_state(question_state::$invalid); + $this->check_current_state(question_state::$complete); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, ''), $this->get_does_not_contain_correctness_expectation(), $this->get_does_not_contain_feedback_expectation()); @@ -406,12 +340,11 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Verify. $this->check_current_state(question_state::$gradedpartial); - $this->check_current_mark(1.5); + $this->check_current_mark(2); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), $this->get_contains_partcorrect_expectation()); } @@ -420,10 +353,10 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Create a drag-and-drop question. $dd = test_question_maker::make_question('ddmarker'); $dd->hints = array( - new question_hint_with_parts(1, 'This is the first hint.', - FORMAT_MOODLE, true, true), - new question_hint_with_parts(2, 'This is the second hint.', - FORMAT_MOODLE, true, true), + new question_hint_ddmarker(1, 'This is the first hint.', + FORMAT_MOODLE, true, true, false), + new question_hint_ddmarker(2, 'This is the second hint.', + FORMAT_MOODLE, true, true, false), ); $dd->shufflechoices = false; $this->start_attempt_at_question($dd, 'interactive', 12); @@ -434,18 +367,12 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->assertEqual('interactivecountback', $this->quba->get_question_attempt($this->slot)->get_behaviour_name()); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(3), @@ -454,16 +381,15 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Submit an response with the first two parts right. $this->process_submission( - array('p1' => '1', 'p2' => '2', 'p3' => '2', 'p4' => '1', '-submit' => 1)); + array('c1' => '50,50', 'c2' => '150,50', 'c3' => '150,50', '-submit' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), $this->get_contains_submit_button_expectation(false), $this->get_contains_try_again_button_expectation(true), $this->get_does_not_contain_correctness_expectation(), @@ -472,42 +398,31 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->get_contains_hint_expectation('This is the first hint'), $this->get_contains_num_parts_correct(2), $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 1)); + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, '150,50')); // Check that extract responses will return the reset data. $prefix = $this->quba->get_field_prefix($this->slot); - $this->assertEqual(array('p1' => '1', 'p2' => '2'), + $this->assertEqual(array('c1' => '50,50', 'c2' => '150,50'), $this->quba->extract_responses($this->slot, - array($prefix . 'p1' => '1', $prefix . 'p2' => '2', '-tryagain' => 1))); + array($prefix . 'c1' => '50,50', $prefix . 'c2' => '150,50', '-tryagain' => 1))); // Do try again. - // keys p3 and p4 are extra hidden fields to clear data. + // keys c3 is an extra hidden fields to clear data. $this->process_submission( - array('p1' => '1', 'p2' => '2', 'p3' => '', 'p4' => '', '-tryagain' => 1)); + array('c1' => '50,50', 'c2' => '150,50', 'c3' => '', '-tryagain' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, ''), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_try_again_button_expectation(), $this->get_does_not_contain_correctness_expectation(), @@ -517,16 +432,15 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Submit an response with the first and last parts right. $this->process_submission( - array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '2', '-submit' => 1)); + array('c1' => '50,50', 'c2' => '150,150', 'c3' => '100,150', '-submit' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), $this->get_contains_submit_button_expectation(false), $this->get_contains_try_again_button_expectation(true), $this->get_does_not_contain_correctness_expectation(), @@ -535,35 +449,24 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->get_contains_hint_expectation('This is the second hint'), $this->get_contains_num_parts_correct(2), $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 2)); + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,150'), + $this->get_contains_hidden_expectation(3, '100,150')); // Do try again. $this->process_submission( - array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => '2', '-tryagain' => 1)); + array('c1' => '50,50', 'c2' => '', 'c3' => '', '-tryagain' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, ''), + $this->get_contains_hidden_expectation(3, ''), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_try_again_button_expectation(), $this->get_does_not_contain_correctness_expectation(), @@ -573,24 +476,18 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Submit the right answer. $this->process_submission( - array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2', '-submit' => 1)); + array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150', '-submit' => 1)); // Verify. $this->check_current_state(question_state::$gradedright); - $this->check_current_mark(7); + $this->check_current_mark(8); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', 1), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, '50,50'), + $this->get_contains_hidden_expectation(2, '150,50'), + $this->get_contains_hidden_expectation(3, '100,150'), $this->get_contains_submit_button_expectation(false), $this->get_does_not_contain_try_again_button_expectation(), $this->get_contains_correct_expectation(), @@ -604,10 +501,10 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Create a drag-and-drop question. $dd = test_question_maker::make_question('ddmarker'); $dd->hints = array( - new question_hint_with_parts(23, 'This is the first hint.', - FORMAT_MOODLE, false, false), - new question_hint_with_parts(24, 'This is the second hint.', - FORMAT_MOODLE, true, true), + new question_hint_ddmarker(23, 'This is the first hint.', + FORMAT_MOODLE, false, false, false), + new question_hint_ddmarker(24, 'This is the second hint.', + FORMAT_MOODLE, true, true, false), ); $dd->shufflechoices = false; $this->start_attempt_at_question($dd, 'interactive', 3); @@ -616,25 +513,19 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(3), $this->get_no_hint_visible_expectation()); // Save the right answer. - $this->process_submission(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')); + $this->process_submission(array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150')); // Finish the attempt without clicking check. $this->quba->finish_all_questions(); @@ -643,10 +534,9 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_state(question_state::$gradedright); $this->check_current_mark(3); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), $this->get_contains_submit_button_expectation(false), $this->get_contains_correct_expectation(), $this->get_no_hint_visible_expectation()); @@ -664,51 +554,44 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Create a drag-and-drop question. $dd = test_question_maker::make_question('ddmarker'); $dd->hints = array( - new question_hint_with_parts(23, 'This is the first hint.', - FORMAT_MOODLE, false, false), - new question_hint_with_parts(24, 'This is the second hint.', - FORMAT_MOODLE, true, true), + new question_hint_ddmarker(23, 'This is the first hint.', + FORMAT_MOODLE, false, false, false), + new question_hint_ddmarker(24, 'This is the second hint.', + FORMAT_MOODLE, true, true, false), ); $dd->shufflechoices = false; - $this->start_attempt_at_question($dd, 'interactive', 4); + $this->start_attempt_at_question($dd, 'interactive', 3); // Check the initial state. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(3), $this->get_no_hint_visible_expectation()); // Save the a partially right answer. - $this->process_submission(array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '1')); + $this->process_submission(array('c1' => '50,50', 'c2' => '50,50', 'c3' => '100,150')); // Finish the attempt without clicking check. $this->quba->finish_all_questions(); // Verify. $this->check_current_state(question_state::$gradedpartial); - $this->check_current_mark(1); + $this->check_current_mark(2); $this->check_current_output( - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), $this->get_contains_submit_button_expectation(false), $this->get_contains_partcorrect_expectation(), $this->get_no_hint_visible_expectation()); @@ -718,7 +601,7 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Verify. $this->check_current_state(question_state::$gradedpartial); - $this->check_current_mark(1); + $this->check_current_mark(2); } public function test_interactive_no_right_clears() { @@ -726,8 +609,10 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Create a drag-and-drop question. $dd = test_question_maker::make_question('ddmarker'); $dd->hints = array( - new question_hint_with_parts(23, 'This is the first hint.', FORMAT_MOODLE, false, true), - new question_hint_with_parts(24, 'This is the second hint.', FORMAT_MOODLE, true, true), + new question_hint_ddmarker(23, 'This is the first hint.', + FORMAT_MOODLE, false, true, false), + new question_hint_ddmarker(24, 'This is the second hint.', + FORMAT_MOODLE, true, true, false), ); $dd->shufflechoices = false; $this->start_attempt_at_question($dd, 'interactive', 3); @@ -738,18 +623,12 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_output( $this->get_contains_marked_out_of_summary(), - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(3), @@ -757,41 +636,34 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Save the a completely wrong answer. $this->process_submission( - array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1', '-submit' => 1)); + array('c1' => '100,150', 'c2' => '100,150', 'c3' => '50,50', '-submit' => 1)); // Verify. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( $this->get_contains_marked_out_of_summary(), - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), $this->get_contains_submit_button_expectation(false), $this->get_contains_hint_expectation('This is the first hint')); // Do try again. $this->process_submission( - array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => '', '-tryagain' => 1)); + array('c1' => '', 'c2' => '', 'c3' => '', '-tryagain' => 1)); // Check that all the wrong answers have been cleared. $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_current_output( $this->get_contains_marked_out_of_summary(), - $this->get_contains_drag_image_home_expectation(1, 1, 1), - $this->get_contains_drag_image_home_expectation(2, 2, 1), - $this->get_contains_drag_image_home_expectation(1, 1, 2), - $this->get_contains_drag_image_home_expectation(2, 2, 2), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', ''), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', ''), + $this->get_contains_draggable_marker_home_expectation(1, false), + $this->get_contains_draggable_marker_home_expectation(2, false), + $this->get_contains_draggable_marker_home_expectation(3, false), + $this->get_contains_hidden_expectation(1, ''), + $this->get_contains_hidden_expectation(2, ''), + $this->get_contains_hidden_expectation(3, ''), $this->get_contains_submit_button_expectation(true), $this->get_does_not_contain_feedback_expectation(), $this->get_tries_remaining_expectation(2), @@ -809,14 +681,9 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { $this->check_current_mark(null); $this->check_current_output( - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3'), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4'), + $this->get_contains_hidden_expectation(1), + $this->get_contains_hidden_expectation(2), + $this->get_contains_hidden_expectation(3), $this->get_does_not_contain_feedback_expectation()); // Save a partial answer. @@ -825,31 +692,24 @@ class qtype_ddmarker_walkthrough_test extends qbehaviour_walkthrough_test_base { // Verify. $this->check_current_state(question_state::$complete); $this->check_current_mark(null); + $rightanswer = array($dd->get_right_choice_for(1)=>'50,50', + $dd->get_right_choice_for(2) =>'150,50', + $dd->get_right_choice_for(3) => '100,150'); $this->check_current_output( - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p1', - $dd->get_right_choice_for(1)), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p2', - $dd->get_right_choice_for(2)), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p3', - $dd->get_right_choice_for(3)), - $this->get_contains_hidden_expectation( - $this->quba->get_field_prefix($this->slot) . 'p4', - $dd->get_right_choice_for(4)), - $this->get_does_not_contain_correctness_expectation(), - $this->get_does_not_contain_feedback_expectation()); + $this->get_contains_hidden_expectation(1, $rightanswer[1]), + $this->get_contains_hidden_expectation(2, $rightanswer[2]), + $this->get_contains_hidden_expectation(3, $rightanswer[3]), + $this->get_does_not_contain_correctness_expectation(), + $this->get_does_not_contain_feedback_expectation()); // Finish the attempt. $this->quba->finish_all_questions(); // Verify. $this->displayoptions->rightanswer = question_display_options::VISIBLE; - $this->assertEqual('Drop zone 1 -> {1. quick} '. - 'Drop zone 2 -> {2. fox} '. - 'Drop zone 3 -> {1. lazy} '. - 'Drop zone 4 -> {2. dog}', + $this->assertEqual('{Drop zone 1 -> quick}, '. + '{Drop zone 2 -> fox}, '. + '{Drop zone 3 -> lazy}', $dd->get_right_answer_summary()); $this->check_current_state(question_state::$gradedright); $this->check_current_mark(3); From 4dafd7aa56a9ec57496b843a49d21722ed533bce Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 31 Jan 2012 16:43:00 +0700 Subject: [PATCH 085/178] MDL-47494 ddmarker: NOBUG change in dependency code for qtypes --- question/type/ddmarker/questiontype.php | 4 ---- question/type/ddmarker/version.php | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index a9ca0ae3355..6eb7487dd63 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -83,10 +83,6 @@ class question_hint_ddmarker extends question_hint_with_parts { */ class qtype_ddmarker extends qtype_ddtoimage_base { - public function requires_qtypes() { - return array_merge(parent::requires_qtypes(), array('ddimageortext')); - } - public function save_question_options($formdata) { global $DB, $USER; $context = $formdata->context; diff --git a/question/type/ddmarker/version.php b/question/type/ddmarker/version.php index fc9ce78417d..8200bac782b 100644 --- a/question/type/ddmarker/version.php +++ b/question/type/ddmarker/version.php @@ -27,3 +27,7 @@ defined('MOODLE_INTERNAL') || die(); $plugin->version = 2011051200; $plugin->requires = 2011051212; +$plugin->dependencies = array( + 'qtype_gapselect' => 2011020100, + 'qtype_ddimageortext' => 2011020100, +); \ No newline at end of file From 9c6e2570beb473bf886789a052f7a23d2348f911 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 31 Jan 2012 18:00:59 +0700 Subject: [PATCH 086/178] MDL-47494 ddmarker: NOBUG change to string identifiers --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 3dd5b31313f..97310f29567 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -22,16 +22,12 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -$string['addingddmarker'] = 'Adding drag and drop markers'; $string['addmoreitems'] = 'Blanks for {no} more markers'; $string['alttext'] = 'Alt text'; $string['answer'] = 'Answer'; $string['bgimage'] = 'Background image'; $string['coords'] = 'Coords'; $string['correctansweris'] = 'The correct answer is: {$a}'; -$string['ddmarker'] = 'Drag and drop markers'; -$string['ddmarker_help'] = 'select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; -$string['ddmarkersummary'] = 'Markers are dragged and dropped onto a background image.'; $string['draggableimage'] = 'Draggable image'; $string['draggableitem'] = 'Draggable item'; $string['draggableitemheader'] = 'Draggable item {$a}'; @@ -39,7 +35,6 @@ $string['draggableitemtype'] = 'Type'; $string['draggableword'] = 'Draggable text'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; -$string['editingddmarker'] = 'Editing drag and drop markers'; $string['followingarewrong'] = 'The following markers have been placed in the wrong area : {$a}.'; $string['followingarewrongandhighlighted'] = 'The following markers were incorrectly placed : {$a}. Highlighted marker(s) are now shown with the correct placement(s).
Click on the marker to highlight the allowed area.'; $string['formerror_nobgimage'] = 'You need to select an image to use as the background for the drag and drop area.'; @@ -58,6 +53,12 @@ $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; $string['nolabel'] = 'No label text'; $string['pleasedragatleastonemarker'] = 'Your answer is not complete, you must place at least one marker on the image.'; +$string['pluginname'] = 'Drag and drop markers'; +$string['pluginname_help'] = 'select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; +$string['pluginname_link'] = 'question/type/ddmarker'; +$string['pluginnameadding'] = 'Adding drag and drop markers'; +$string['pluginnameediting'] = 'Editing drag and drop markers'; +$string['pluginnamesummary'] = 'Markers are dragged and dropped onto a background image.'; $string['previewarea'] = 'Preview area -'; $string['previewareaheader'] = 'Preview'; $string['previewareamessage'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; From bfd69fe65966e3f07c5f78f41649b9e3f4677a62 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Fri, 3 Feb 2012 12:54:22 +0700 Subject: [PATCH 087/178] MDL-47494 ddmarker: NOBUG updating README.txt installation instructions to include 2.1 branch --- question/type/ddmarker/README.txt | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/README.txt b/question/type/ddmarker/README.txt index 5376a3f7786..ac6cb89b5dd 100644 --- a/question/type/ddmarker/README.txt +++ b/question/type/ddmarker/README.txt @@ -1,20 +1,26 @@ A drag-and-drop question type. -You can use markers with text labels as drag items onto rectangular, and in Moodle 2.2+ version +You can use markers with text labels as drag items onto rectangular, and in Moodle 2.2+ version circular and polygon defined drop zones on a background image. This question type requires that gapselect question type -https://github.com/timhunt/moodle-qtype_ddimageortext/ and -https://github.com/timhunt/moodle-qtype_gapselect/ +https://github.com/timhunt/moodle-qtype_ddimageortext/ and +https://github.com/timhunt/moodle-qtype_gapselect/ to be installed in order to work. This question type was written by Jamie Pratt (http://jamiep.org/). -This question type will be compatible with Moodle 2.1+ or 2.2+. +This question type is compatible with Moodle 2.1+ (MOODLE_21_STABLE branch) or 2.2+ (master branch). -To install using git, type this command in the root of your Moodle install : +To install using git for a 2.2+ Moodle installation, type this command in the root of your Moodle +install : git clone git://github.com/jamiepratt/moodle-qtype_ddmarker.git question/type/ddmarker +To install using git for a 2.1+ Moodle installation, type this command in the root of your Moodle +install : + +git clone -b MOODLE_21_STABLE git://github.com/jamiepratt/moodle-qtype_ddmarker.git question/type/ddmarker + Then add question/type/ddmarker to your git ignore. Alternatively, download the zip from From 445143ef480873944062b98a2ef5a15e70f5b066 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Tue, 6 Dec 2011 14:43:19 +0000 Subject: [PATCH 088/178] MDL-47494 ddmarker: Fix default text format when importing hints. --- question/type/ddmarker/questiontype.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 6eb7487dd63..a1a10d58334 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -350,7 +350,8 @@ class qtype_ddmarker extends qtype_ddtoimage_base { } $format->import_combined_feedback($question, $data, true); - $format->import_hints($question, $data, true, true); + $format->import_hints($question, $data, true, true, + $format->get_format($question->questiontextformat)); return $question; } From 7421dafa8f591eb08100d04610ecd008e0029866 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Tue, 6 Dec 2011 14:43:19 +0000 Subject: [PATCH 089/178] MDL-47494 ddmarker: Fix default text format when importing hints. --- question/type/ddmarker/questiontype.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index a9ca0ae3355..6646195bf9d 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -354,7 +354,8 @@ class qtype_ddmarker extends qtype_ddtoimage_base { } $format->import_combined_feedback($question, $data, true); - $format->import_hints($question, $data, true, true); + $format->import_hints($question, $data, true, true, + $format->get_format($question->questiontextformat)); return $question; } From c710a1ebdb7260bd77e0a685b8ba65a7c08cf01a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 5 Feb 2012 12:51:40 +0700 Subject: [PATCH 090/178] MDL-47494 ddmarker: NOBUG updating repo url in installation instructions --- question/type/ddmarker/README.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/README.txt b/question/type/ddmarker/README.txt index ac6cb89b5dd..15c5b81bb8f 100644 --- a/question/type/ddmarker/README.txt +++ b/question/type/ddmarker/README.txt @@ -3,8 +3,8 @@ You can use markers with text labels as drag items onto rectangular, and in Mood circular and polygon defined drop zones on a background image. This question type requires that gapselect question type -https://github.com/timhunt/moodle-qtype_ddimageortext/ and -https://github.com/timhunt/moodle-qtype_gapselect/ +https://github.com/moodleou/moodle-qtype_ddimageortext/ and +https://github.com/moodleou/moodle-qtype_gapselect/ to be installed in order to work. This question type was written by Jamie Pratt (http://jamiep.org/). @@ -14,15 +14,15 @@ This question type is compatible with Moodle 2.1+ (MOODLE_21_STABLE branch) or 2 To install using git for a 2.2+ Moodle installation, type this command in the root of your Moodle install : -git clone git://github.com/jamiepratt/moodle-qtype_ddmarker.git question/type/ddmarker +git clone git://github.com/moodleou/moodle-qtype_ddmarker.git question/type/ddmarker To install using git for a 2.1+ Moodle installation, type this command in the root of your Moodle install : -git clone -b MOODLE_21_STABLE git://github.com/jamiepratt/moodle-qtype_ddmarker.git question/type/ddmarker +git clone -b MOODLE_21_STABLE git://github.com/moodleou/moodle-qtype_ddmarker.git question/type/ddmarker Then add question/type/ddmarker to your git ignore. Alternatively, download the zip from - https://github.com/jamiepratt/moodle-qtype_ddmarker/zipball/master + https://github.com/moodleou/moodle-qtype_ddmarker/zipball/master unzip it into the question/type folder, and then rename the new folder to ddmarker. From 612607d66c97acf53fd9f2e85dcce356259b4634 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 5 Feb 2012 18:36:29 +0700 Subject: [PATCH 091/178] MDL-47494 ddmarker: NOBUG adding an alt tag to drag and drop background image --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 1 + question/type/ddmarker/renderer.php | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 3dd5b31313f..d2abf973ee6 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -37,6 +37,7 @@ $string['draggableitem'] = 'Draggable item'; $string['draggableitemheader'] = 'Draggable item {$a}'; $string['draggableitemtype'] = 'Type'; $string['draggableword'] = 'Draggable text'; +$string['dropbackground'] = 'Background image for dragging markers onto'; $string['dropzone'] = 'Drop zone {$a}'; $string['dropzoneheader'] = 'Drop zones'; $string['editingddmarker'] = 'Editing drag and drop markers'; diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index b146969f5a3..458f640ea64 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -49,7 +49,11 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base { $bgimage = self::get_url_for_image($qa, 'bgimage'); - $img = html_writer::empty_tag('img', array('src'=>$bgimage, 'class'=>'dropbackground')); + $img = html_writer::empty_tag('img', + array('src'=>$bgimage, + 'class'=>'dropbackground', + 'alt' => get_string('dropbackground', 'qtype_ddmarker'))); + $droparea = html_writer::tag('div', $img, array('class'=>'droparea')); $draghomes = ''; From 2a71bf53d2cb658969670f1e0d8a83451ac5c399 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 10 Feb 2012 16:20:14 +0000 Subject: [PATCH 092/178] MDL-47494 ddmarker: Clean up version.php and comments. --- question/type/ddmarker/README.txt | 23 +++++++++++-------- question/type/ddmarker/question.php | 7 +++--- question/type/ddmarker/questiontype.php | 2 +- question/type/ddmarker/renderer.php | 7 +++--- question/type/ddmarker/shapes.php | 5 ++-- question/type/ddmarker/simpletest/helper.php | 11 ++++----- .../type/ddmarker/simpletest/testquestion.php | 7 +++--- .../ddmarker/simpletest/testquestiontype.php | 7 +++--- .../ddmarker/simpletest/testwalkthrough.php | 7 +++--- question/type/ddmarker/version.php | 13 +++++++---- 10 files changed, 44 insertions(+), 45 deletions(-) diff --git a/question/type/ddmarker/README.txt b/question/type/ddmarker/README.txt index 15c5b81bb8f..f096c4f1606 100644 --- a/question/type/ddmarker/README.txt +++ b/question/type/ddmarker/README.txt @@ -1,6 +1,7 @@ -A drag-and-drop question type. -You can use markers with text labels as drag items onto rectangular, and in Moodle 2.2+ version -circular and polygon defined drop zones on a background image. +Drag-and-drop markers question type + +You can use markers with text labels as drag items onto rectangular, and in +Moodle 2.2+ version circular and polygon defined drop zones on a background image. This question type requires that gapselect question type https://github.com/moodleou/moodle-qtype_ddimageortext/ and @@ -9,20 +10,22 @@ to be installed in order to work. This question type was written by Jamie Pratt (http://jamiep.org/). -This question type is compatible with Moodle 2.1+ (MOODLE_21_STABLE branch) or 2.2+ (master branch). +This question type is compatible with Moodle 2.1+ (MOODLE_21_STABLE branch) or +2.2+ (master branch). -To install using git for a 2.2+ Moodle installation, type this command in the root of your Moodle -install : +To install using git for a 2.2+ Moodle installation, type this command in the +root of your Moodle install: git clone git://github.com/moodleou/moodle-qtype_ddmarker.git question/type/ddmarker -To install using git for a 2.1+ Moodle installation, type this command in the root of your Moodle -install : +To install using git for a 2.1+ Moodle installation, type this command in the +root of your Moodle install: -git clone -b MOODLE_21_STABLE git://github.com/moodleou/moodle-qtype_ddmarker.git question/type/ddmarker +git clone -b MOODLE_21_STABLE git://github.com/moodleou/moodle-qtype_ddmarker.git question/type/ddmarker Then add question/type/ddmarker to your git ignore. Alternatively, download the zip from - https://github.com/moodleou/moodle-qtype_ddmarker/zipball/master + Moodle 2.2+ - https://github.com/moodleou/moodle-qtype_ddmarker/zipball/master + Moodle 2.1+ - https://github.com/moodleou/moodle-qtype_ddmarker/zipball/MOODLE_21_STABLE unzip it into the question/type folder, and then rename the new folder to ddmarker. diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php index 3e051986887..22cb99e408d 100644 --- a/question/type/ddmarker/question.php +++ b/question/type/ddmarker/question.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Drag-and-drop words into sentences question definition class. + * Drag-and-drop markers question definition class. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -31,7 +30,7 @@ require_once($CFG->dirroot . '/question/type/ddmarker/shapes.php'); /** - * Represents a drag-and-drop images to image question. + * Represents a drag-and-drop markers question. * * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 6646195bf9d..66ffaf7cf61 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -76,7 +76,7 @@ class question_hint_ddmarker extends question_hint_with_parts { /** - * The drag-and-drop words into sentences question type class. + * The drag-and-drop markers question type class. * * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/question/type/ddmarker/renderer.php b/question/type/ddmarker/renderer.php index 458f640ea64..0c047631d22 100644 --- a/question/type/ddmarker/renderer.php +++ b/question/type/ddmarker/renderer.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Drag-and-drop words into sentences question renderer class. + * Drag-and-drop markers question renderer class. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -30,7 +29,7 @@ require_once($CFG->dirroot . '/question/type/ddimageortext/rendererbase.php'); /** - * Generates the output for drag-and-drop words into sentences questions. + * Generates the output for drag-and-drop markers questions. * * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php index 36cfb5f36cf..eae350ced22 100644 --- a/question/type/ddmarker/shapes.php +++ b/question/type/ddmarker/shapes.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Drag-and-drop words into sentences classes for dealing with shapes on the server side. + * Drag-and-drop markers classes for dealing with shapes on the server side. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/question/type/ddmarker/simpletest/helper.php b/question/type/ddmarker/simpletest/helper.php index b52c82fb108..1c773da62d3 100644 --- a/question/type/ddmarker/simpletest/helper.php +++ b/question/type/ddmarker/simpletest/helper.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Test helpers for the drag-and-drop words into sentences question type. + * Test helpers for the drag-and-drop markers question type. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -28,7 +27,7 @@ defined('MOODLE_INTERNAL') || die(); /** - * Test helper class for the drag-and-drop words into sentences question type. + * Test helper class for the drag-and-drop markers question type. * * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later @@ -47,7 +46,7 @@ class qtype_ddmarker_test_helper extends question_test_helper { test_question_maker::initialise_a_question($dd); - $dd->name = 'Drag-and-drop words into sentences question'; + $dd->name = 'Drag-and-drop markers question'; $dd->questiontext = 'The quick brown fox jumped over the lazy dog.'; $dd->generalfeedback = 'This sentence uses each letter of the alphabet.'; $dd->qtype = question_bank::get_qtype('ddmarker'); @@ -102,7 +101,7 @@ class qtype_ddmarker_test_helper extends question_test_helper { test_question_maker::initialise_a_question($dd); - $dd->name = 'Drag-and-drop words into sentences question'; + $dd->name = 'Drag-and-drop markers question'; $dd->questiontext = 'Fill in the operators to make this equation work: '; $dd->generalfeedback = 'Hmmmm...'; $dd->qtype = question_bank::get_qtype('ddmarker'); diff --git a/question/type/ddmarker/simpletest/testquestion.php b/question/type/ddmarker/simpletest/testquestion.php index 4616e14f9b3..705f95a49d0 100644 --- a/question/type/ddmarker/simpletest/testquestion.php +++ b/question/type/ddmarker/simpletest/testquestion.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Unit tests for the drag-and-drop words into sentences question definition class. + * Unit tests for the drag-and-drop markers question definition class. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -31,7 +30,7 @@ require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); /** - * Unit tests for the matching question definition class. + * Unit tests for the drag-and-drop markers question definition class. * * @copyright 2009 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/question/type/ddmarker/simpletest/testquestiontype.php b/question/type/ddmarker/simpletest/testquestiontype.php index 4e2a35a84a0..63d0cdd1e7d 100644 --- a/question/type/ddmarker/simpletest/testquestiontype.php +++ b/question/type/ddmarker/simpletest/testquestiontype.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Unit tests for the drag-and-drop words into sentences question definition class. + * Unit tests for the drag-and-drop markers question definition class. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -31,7 +30,7 @@ require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); /** - * Unit tests for the drag-and-drop words into sentences question definition class. + * Unit tests for the drag-and-drop markers question definition class. * * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/question/type/ddmarker/simpletest/testwalkthrough.php b/question/type/ddmarker/simpletest/testwalkthrough.php index eb6d493f083..62411531aa9 100644 --- a/question/type/ddmarker/simpletest/testwalkthrough.php +++ b/question/type/ddmarker/simpletest/testwalkthrough.php @@ -15,10 +15,9 @@ // along with Moodle. If not, see . /** - * Unit tests for the drag-and-drop words into sentences question type. + * Unit tests for the drag-and-drop markers question type. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -31,7 +30,7 @@ require_once($CFG->dirroot . '/question/type/ddmarker/simpletest/helper.php'); /** - * Unit tests for the drag-and-drop words into sentences question type. + * Unit tests for the drag-and-drop markers question type. * * @copyright 2010 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later diff --git a/question/type/ddmarker/version.php b/question/type/ddmarker/version.php index fc9ce78417d..d62ee1c6466 100644 --- a/question/type/ddmarker/version.php +++ b/question/type/ddmarker/version.php @@ -15,15 +15,18 @@ // along with Moodle. If not, see . /** - * Version information for the drag-and-drop words into sentences question type. + * Version information for the drag-and-drop markers question type. * - * @package qtype - * @subpackage ddmarker + * @package qtype_ddmarker * @copyright 2011 The Open University * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2011051200; -$plugin->requires = 2011051212; +$plugin->version = 2011051200; +$plugin->requires = 2011070100; +$plugin->cron = 0; +$plugin->component = 'qtype_ddmarker'; +$plugin->maturity = MATURITY_STABLE; +$plugin->release = '1.0'; From 03337d8bab805b31485df3d428eebb1310dcc0f5 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 10 Feb 2012 16:25:23 +0000 Subject: [PATCH 093/178] MDL-47494 ddmarker: Add extra pluginname string to appease the plugins DB --- question/type/ddmarker/lang/en/qtype_ddmarker.php | 1 + 1 file changed, 1 insertion(+) diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index d2abf973ee6..ec9afc7ca3a 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -59,6 +59,7 @@ $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; $string['nolabel'] = 'No label text'; $string['pleasedragatleastonemarker'] = 'Your answer is not complete, you must place at least one marker on the image.'; +$string['pluginname'] = 'Drag and drop markers'; $string['previewarea'] = 'Preview area -'; $string['previewareaheader'] = 'Preview'; $string['previewareamessage'] = 'Select a background image file, enter text labels for markers and define the drop zones on the background image to which they must be dragged.'; From 6bdb7323ae9cf0d849cb56c3dece55915a03aa4a Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 28 Feb 2012 13:05:45 +0700 Subject: [PATCH 094/178] MDL-47494 ddmarker: NOBUG admin page that lists contexts and categories and the number of questions contained in them. --- .../type/ddmarker/imagetargetconverter.php | 224 ++++++++++++++++++ .../type/ddmarker/lang/en/qtype_ddmarker.php | 3 + question/type/ddmarker/settings.php | 32 +++ 3 files changed, 259 insertions(+) create mode 100644 question/type/ddmarker/imagetargetconverter.php create mode 100644 question/type/ddmarker/settings.php diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php new file mode 100644 index 00000000000..155ddcb2084 --- /dev/null +++ b/question/type/ddmarker/imagetargetconverter.php @@ -0,0 +1,224 @@ +. + +/** + * This page lets admins check the environment requirements for this question type. + * + * @package qtype + * @subpackage pmatch + * @copyright 2012 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +require_once(dirname(__FILE__) . '/../../../config.php'); +require_once($CFG->libdir . '/adminlib.php'); + +abstract class qtype_ddmarker_list_item { + /** + * @var count of questions contained in this item and sub items. + */ + protected $qcount = 0; + /** + * @var children array of pointers to list_items either category_list_items or context_list_items + */ + protected $children = array(); + + protected $record; + + + public function add_child($child) { + $this->children[] = $child; + $this->children = array_unique($this->children); + } + + abstract protected function parent_node (); + + abstract public function render (); + + public function leaf_to_root($qcount) { + $this->qcount += $qcount; + $parent = $this->parent_node(); + if ($parent !== null) { + $parent->add_child($this); + $parent->leaf_to_root($qcount); + } + } + + protected function render_children() { + $children = array(); + foreach ($this->children as $child) { + $children[] = $child->render(); + } + return html_writer::alist($children); + } + + public function __toString() { + return get_class($this).' '.$this->record->id; + } + +} +class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { + + function __construct($record, $list, $parentlist) { + $this->record = $record; + $this->list = $list; + $this->parentlist = $parentlist; + } + + public function parent_node() { + if ($this->record->parent == 0) { + return $this->parentlist->get_instance($this->record->contextid); + } else { + return $this->list->get_instance($this->record->parent); + } + } + + public function render () { + $a = new stdClass(); + $a->qcount = $this->qcount; + $a->name = $this->record->name; + return get_string('categorylistitem', 'qtype_ddmarker', $a).$this->render_children(); + } +} +class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { + + protected $list; + protected $parentlist = null; + + function __construct($record, $list) { + $this->record = $record; + $this->list = $list; + } + + public function parent_node() { + $pathids = explode('/', $this->record->path); + if (count($pathids) >= 3) { + return $this->list->get_instance($pathids[count($pathids)-2]); + } else { + return null; + } + } + + public function render () { + $a = new stdClass(); + $a->qcount = $this->qcount; + $a->name = $this->record->get_context_name(); + return get_string('contextlistitem', 'qtype_ddmarker', $a).$this->render_children(); + } +} +/** + * Describes a nested list of listitems. This class and sub classes contain the functionality to build the nested list. + **/ +abstract class qtype_ddmarker_list { + abstract protected function new_list_item($record); + protected function make_list_item_instances_from_records ($contextids) { + if (!empty($this->records)) { + foreach ($this->records as $id => $record) { + $this->instances[$id] = $this->new_list_item($record); + } + } + } + public function get_instance($id) { + return $this->instances[$id]; + } + + public function leaf_node ($id, $qcount) { + $instance = $this->get_instance($id); + $instance->leaf_to_root($qcount); + } + +} + +class qtype_ddmarker_context_list extends qtype_ddmarker_list { + protected $records = array(); + protected $instances = array(); + + protected function new_list_item($record) { + return new qtype_ddmarker_context_list_item($record, $this); + } + + public function __construct($contextids) { + global $DB; + $this->records = array(); + foreach ($contextids as $contextid) { + if (!isset($records[$contextid])) { + $this->records[$contextid] = context::instance_by_id($contextid); + } + $this->records += $this->records[$contextid]->get_parent_contexts(); + } + parent::make_list_item_instances_from_records ($contextids); + } + public function render() { + $rootitem = html_writer::tag('li', $this->root_node()->render()); + return html_writer::tag('ul', $rootitem); + } + public function root_node () { + return $this->get_instance(get_context_instance(CONTEXT_SYSTEM)->id); + } +} + +class qtype_ddmarker_category_list extends qtype_ddmarker_list { + protected $records = array(); + protected $instances = array(); + protected $contextlist; + protected function new_list_item($record) { + return new qtype_ddmarker_category_list_item($record, $this, $this->contextlist); + } + public function __construct($contextids, $contextlist) { + global $DB; + $this->contextlist = $contextlist; + //probably most efficient way to reconstruct question category tree is to load all q cats in relevant contexts + list($sql, $params) = $DB->get_in_or_equal($contextids); + $this->records = $DB->get_records_select('question_categories', "contextid ".$sql, $params); + parent::make_list_item_instances_from_records ($contextids); + } +} +// Check the user is logged in. +require_login(); +$context = get_context_instance(CONTEXT_SYSTEM); +require_capability('moodle/question:config', $context); + +admin_externalpage_setup('qtypeddmarkerfromimagetarget'); + +// Header. +echo $OUTPUT->header(); +echo $OUTPUT->heading_with_help(get_string('imagetargetconverter', 'qtype_ddmarker'), '', 'qtype_ddmarker'); + +$catswithquestions = $DB->get_records_sql('SELECT cat.id, cat.contextid, COUNT(1) AS qcount '. + 'FROM {question_categories} cat, {question} q '. + 'WHERE q.category = cat.id '. + 'GROUP BY cat.id, cat.contextid'); +//print_object($catswithquestions); + +$contextids = array(); +foreach ($catswithquestions as $catwithquestions) { + $contextids[] = $catwithquestions->contextid; +} + +$contexts = new qtype_ddmarker_context_list($contextids); +$categories = new qtype_ddmarker_category_list($contextids, $contexts); + +foreach ($catswithquestions as $catwithquestions) { + $categories->leaf_node($catwithquestions->id, $catwithquestions->qcount); +} +//print_object($contexts->root_node()); + +echo $contexts->render(); + + +// Footer. +echo $OUTPUT->footer(); diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index ec9afc7ca3a..581fd492cc0 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -27,6 +27,8 @@ $string['addmoreitems'] = 'Blanks for {no} more markers'; $string['alttext'] = 'Alt text'; $string['answer'] = 'Answer'; $string['bgimage'] = 'Background image'; +$string['categorylistitem'] = 'Category "{$a->name}" (contains {$a->qcount} questions)'; +$string['contextlistitem'] = 'Context "{$a->name}" (contains {$a->qcount} questions)'; $string['coords'] = 'Coords'; $string['correctansweris'] = 'The correct answer is: {$a}'; $string['ddmarker'] = 'Drag and drop markers'; @@ -53,6 +55,7 @@ $string['formerror_shapeoutsideboundsofbgimage'] = 'The shape you have defined g $string['formerror_toomanysemicolons'] = 'There are too many semi colon separated parts to the coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['formerror_unrecognisedwidthheightpart'] = 'We do not recognise the width and height you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['formerror_unrecognisedxypart'] = 'We do not recognise the x,y coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; +$string['imagetargetconverter'] = 'Convert image target questions to drag and drop marker'; $string['infinite'] = 'Infinite'; $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; diff --git a/question/type/ddmarker/settings.php b/question/type/ddmarker/settings.php new file mode 100644 index 00000000000..a523f7128df --- /dev/null +++ b/question/type/ddmarker/settings.php @@ -0,0 +1,32 @@ +. + +/** + * Admin settings for the Opaque question type. + * + * @package qtype + * @subpackage opaque + * @copyright 2011 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +$settings = new admin_externalpage('qtypeddmarkerfromimagetarget', + get_string('imagetargetconverter', 'qtype_ddmarker'), + new moodle_url('/question/type/ddmarker/imagetargetconverter.php'), + 'moodle/question:config'); From 5e37b1f0f743a4423ca50779501eba0c50e21361 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 22 Mar 2012 09:52:56 +0700 Subject: [PATCH 095/178] MDL-47494 ddmarker: NOBUG further work on adminpage --- .../type/ddmarker/imagetargetconverter.php | 88 +++++++++++++++---- .../type/ddmarker/lang/en/qtype_ddmarker.php | 7 +- 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 155ddcb2084..7eb3f997920 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -42,12 +42,14 @@ abstract class qtype_ddmarker_list_item { public function add_child($child) { $this->children[] = $child; + //array_unique relies on __toString() returning a unique string to determine if objects in array + //are the same or not $this->children = array_unique($this->children); } abstract protected function parent_node (); - abstract public function render (); + abstract public function render($stringidentifier, $link); public function leaf_to_root($qcount) { $this->qcount += $qcount; @@ -58,10 +60,10 @@ abstract class qtype_ddmarker_list_item { } } - protected function render_children() { + protected function render_children($stringidentifier, $link) { $children = array(); foreach ($this->children as $child) { - $children[] = $child->render(); + $children[] = $child->render($stringidentifier, $link); } return html_writer::alist($children); } @@ -87,11 +89,18 @@ class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { } } - public function render () { + public function render ($stringidentifier, $link) { + global $PAGE; $a = new stdClass(); $a->qcount = $this->qcount; $a->name = $this->record->name; - return get_string('categorylistitem', 'qtype_ddmarker', $a).$this->render_children(); + $thisitem = get_string($stringidentifier.'category', 'qtype_ddmarker', $a); + if ($link) { + $actionurl = new moodle_url($PAGE->url, array('categoryid'=> $this->record->id)); + $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); + } + + return $thisitem.$this->render_children($stringidentifier, $link); } } class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { @@ -113,11 +122,17 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { } } - public function render () { + public function render ($stringidentifier, $link) { + global $PAGE; $a = new stdClass(); $a->qcount = $this->qcount; $a->name = $this->record->get_context_name(); - return get_string('contextlistitem', 'qtype_ddmarker', $a).$this->render_children(); + $thisitem = get_string($stringidentifier.'context', 'qtype_ddmarker', $a); + if ($link) { + $actionurl = new moodle_url($PAGE->url, array('contextid'=> $this->record->id)); + $thisitem = html_writer::tag('a', $thisitem,array('href' => $actionurl)); + } + return $thisitem.$this->render_children($stringidentifier, $link); } } /** @@ -162,8 +177,11 @@ class qtype_ddmarker_context_list extends qtype_ddmarker_list { } parent::make_list_item_instances_from_records ($contextids); } - public function render() { - $rootitem = html_writer::tag('li', $this->root_node()->render()); + public function render($stringidentifier, $link, $roottorender = null) { + if ($roottorender === null) { + $roottorender = $this->root_node(); + } + $rootitem = html_writer::tag('li', $roottorender->render($stringidentifier, $link)); return html_writer::tag('ul', $rootitem); } public function root_node () { @@ -187,6 +205,10 @@ class qtype_ddmarker_category_list extends qtype_ddmarker_list { parent::make_list_item_instances_from_records ($contextids); } } + +$categoryid = optional_param('categoryid', 0, PARAM_INT); +$qcontextid = optional_param('contextid', 0, PARAM_INT); +$confirm = optional_param('confirm', 0, PARAM_INT); // Check the user is logged in. require_login(); $context = get_context_instance(CONTEXT_SYSTEM); @@ -198,10 +220,29 @@ admin_externalpage_setup('qtypeddmarkerfromimagetarget'); echo $OUTPUT->header(); echo $OUTPUT->heading_with_help(get_string('imagetargetconverter', 'qtype_ddmarker'), '', 'qtype_ddmarker'); -$catswithquestions = $DB->get_records_sql('SELECT cat.id, cat.contextid, COUNT(1) AS qcount '. - 'FROM {question_categories} cat, {question} q '. - 'WHERE q.category = cat.id '. - 'GROUP BY cat.id, cat.contextid'); + +$params = array(); +$from = 'FROM {question_categories} cat, {question} q'; +$where = ' WHERE q.category = cat.id '; + +if ($qcontextid) { + $qcontext = get_context_instance_by_id($qcontextid, MUST_EXIST); + $from .= ', {context} context'; + $where .= 'AND cat.contextid = context.id AND (context.path LIKE :path OR context.id = :id) '; + $params['path'] = $qcontext->path.'/%'; + $params['id'] = $qcontext->id; +} else if ($categoryid) { + $from .= ', {context} context, {question_categories} cat2'; + $where .= 'AND cat.contextid = cat2.contextid AND cat2.id = :categoryid '; + $params['categoryid'] = $categoryid; +} +$sql = 'SELECT cat.id, cat.contextid, COUNT(1) AS qcount '. + $from. + $where. + 'GROUP BY cat.id, cat.contextid'; + + +$catswithquestions = $DB->get_records_sql($sql, $params); //print_object($catswithquestions); $contextids = array(); @@ -216,8 +257,25 @@ foreach ($catswithquestions as $catwithquestions) { $categories->leaf_node($catwithquestions->id, $catwithquestions->qcount); } //print_object($contexts->root_node()); - -echo $contexts->render(); +if ($categoryid || $qcontextid) { + if (!$confirm) { + $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid')+array('confirm'=>1)); + $cancelurl = new moodle_url($PAGE->url); + if ($categoryid) { + $torender = $categories->get_instance($categoryid); + } else if ($qcontextid) { + $torender = $contexts->get_instance($qcontextid); + } else { + $torender = $contexts->root_node(); + } + echo $contexts->render('listitem', false, $torender); + echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); + } else if (confirm_sesskey()) { + + } +} else { + echo $contexts->render('listitemaction', true, $contexts->root_node()); +} // Footer. diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 581fd492cc0..b5e481d5546 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -27,8 +27,7 @@ $string['addmoreitems'] = 'Blanks for {no} more markers'; $string['alttext'] = 'Alt text'; $string['answer'] = 'Answer'; $string['bgimage'] = 'Background image'; -$string['categorylistitem'] = 'Category "{$a->name}" (contains {$a->qcount} questions)'; -$string['contextlistitem'] = 'Context "{$a->name}" (contains {$a->qcount} questions)'; +$string['confirmimagetargetconversion'] = 'You are about to convert the above image target questions to the drag and drop markers question type.'; $string['coords'] = 'Coords'; $string['correctansweris'] = 'The correct answer is: {$a}'; $string['ddmarker'] = 'Drag and drop markers'; @@ -57,6 +56,10 @@ $string['formerror_unrecognisedwidthheightpart'] = 'We do not recognise the widt $string['formerror_unrecognisedxypart'] = 'We do not recognise the x,y coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['imagetargetconverter'] = 'Convert image target questions to drag and drop marker'; $string['infinite'] = 'Infinite'; +$string['listitemactioncategory'] = 'Convert all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemactioncontext'] = 'Convert all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemcategory'] = 'Questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemcontext'] = 'Questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; From 2379439f63af89fa968c6e636fb45dd830ca349c Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 27 Mar 2012 14:41:58 +0700 Subject: [PATCH 096/178] MDL-47494 ddmarker: NOBUG change API calls that only work post 2.1 This need to be merged into master branch because we want to keep the 2.1 branch mergeable but then we will merge with 'ours' strategy, effectively discarding changes. --- .../type/ddmarker/imagetargetconverter.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 7eb3f997920..be2d62f1148 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -99,7 +99,7 @@ class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { $actionurl = new moodle_url($PAGE->url, array('categoryid'=> $this->record->id)); $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); } - + return $thisitem.$this->render_children($stringidentifier, $link); } } @@ -126,7 +126,7 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { global $PAGE; $a = new stdClass(); $a->qcount = $this->qcount; - $a->name = $this->record->get_context_name(); + $a->name = print_context_name($this->record); $thisitem = get_string($stringidentifier.'context', 'qtype_ddmarker', $a); if ($link) { $actionurl = new moodle_url($PAGE->url, array('contextid'=> $this->record->id)); @@ -170,10 +170,16 @@ class qtype_ddmarker_context_list extends qtype_ddmarker_list { global $DB; $this->records = array(); foreach ($contextids as $contextid) { - if (!isset($records[$contextid])) { - $this->records[$contextid] = context::instance_by_id($contextid); + if (!isset($this->records[$contextid])) { + $this->records[$contextid] = get_context_instance_by_id($contextid, MUST_EXIST); + } + $parents = get_parent_contexts($this->records[$contextid]); + foreach ($parents as $parentcontextid) { + if (!isset($this->records[$parentcontextid])) { + $this->records[$parentcontextid] = + get_context_instance_by_id($parentcontextid, MUST_EXIST); + } } - $this->records += $this->records[$contextid]->get_parent_contexts(); } parent::make_list_item_instances_from_records ($contextids); } @@ -271,7 +277,7 @@ if ($categoryid || $qcontextid) { echo $contexts->render('listitem', false, $torender); echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); } else if (confirm_sesskey()) { - + } } else { echo $contexts->render('listitemaction', true, $contexts->root_node()); From e1e835e7272658a36605d302eba37fa378f6ea68 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 27 Mar 2012 18:20:14 +0700 Subject: [PATCH 097/178] MDL-47494 ddmarker: NOBUG fixing coding style deviations found by code checker. --- question/type/ddmarker/imagetargetconverter.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index be2d62f1148..917aa48d341 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -75,7 +75,7 @@ abstract class qtype_ddmarker_list_item { } class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { - function __construct($record, $list, $parentlist) { + public function __construct($record, $list, $parentlist) { $this->record = $record; $this->list = $list; $this->parentlist = $parentlist; @@ -108,7 +108,7 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { protected $list; protected $parentlist = null; - function __construct($record, $list) { + public function __construct($record, $list) { $this->record = $record; $this->list = $list; } @@ -130,7 +130,7 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { $thisitem = get_string($stringidentifier.'context', 'qtype_ddmarker', $a); if ($link) { $actionurl = new moodle_url($PAGE->url, array('contextid'=> $this->record->id)); - $thisitem = html_writer::tag('a', $thisitem,array('href' => $actionurl)); + $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); } return $thisitem.$this->render_children($stringidentifier, $link); } @@ -249,7 +249,6 @@ $sql = 'SELECT cat.id, cat.contextid, COUNT(1) AS qcount '. $catswithquestions = $DB->get_records_sql($sql, $params); -//print_object($catswithquestions); $contextids = array(); foreach ($catswithquestions as $catwithquestions) { @@ -262,7 +261,6 @@ $categories = new qtype_ddmarker_category_list($contextids, $contexts); foreach ($catswithquestions as $catwithquestions) { $categories->leaf_node($catwithquestions->id, $catwithquestions->qcount); } -//print_object($contexts->root_node()); if ($categoryid || $qcontextid) { if (!$confirm) { $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid')+array('confirm'=>1)); From a83fc4678471516a93c91f78fd5d9d24d2cebadd Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Tue, 27 Mar 2012 18:22:49 +0700 Subject: [PATCH 098/178] MDL-47494 ddmarker: NOBUG fixing white space issue --- question/type/ddmarker/imagetargetconverter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index a98346f3e56..e2a36853671 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -99,7 +99,7 @@ class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { $actionurl = new moodle_url($PAGE->url, array('categoryid'=> $this->record->id)); $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); } - + return $thisitem.$this->render_children($stringidentifier, $link); } } @@ -269,7 +269,7 @@ if ($categoryid || $qcontextid) { echo $contexts->render('listitem', false, $torender); echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); } else if (confirm_sesskey()) { - + } } else { echo $contexts->render('listitemaction', true, $contexts->root_node()); From af9d63a8dc662b9b015ecdbcbed5aa3ce84f1f1c Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 29 Mar 2012 14:05:57 +0700 Subject: [PATCH 099/178] MDL-47494 ddmarker: NOBUG list questions in categories in category / context selection UI --- .../type/ddmarker/imagetargetconverter.php | 74 ++++++++++++++----- .../type/ddmarker/lang/en/qtype_ddmarker.php | 1 + 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 917aa48d341..6b8ea1a5d42 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -103,6 +103,26 @@ class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { return $thisitem.$this->render_children($stringidentifier, $link); } } +class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { + + public function __construct($record, $list, $parentlist) { + $this->record = $record; + $this->list = $list; + $this->parentlist = $parentlist; + } + + public function parent_node() { + return $this->parentlist->get_instance($this->record->cat_id); + } + + public function render ($stringidentifier, $link) { + global $PAGE; + $a = new stdClass(); + $a->name = $this->record->name; + $thisitem = get_string('listitemquestion', 'qtype_ddmarker', $a); + return $thisitem; + } +} class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { protected $list; @@ -139,8 +159,10 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { * Describes a nested list of listitems. This class and sub classes contain the functionality to build the nested list. **/ abstract class qtype_ddmarker_list { + protected $records = array(); + protected $instances = array(); abstract protected function new_list_item($record); - protected function make_list_item_instances_from_records ($contextids) { + protected function make_list_item_instances_from_records ($contextids = null) { if (!empty($this->records)) { foreach ($this->records as $id => $record) { $this->instances[$id] = $this->new_list_item($record); @@ -154,13 +176,12 @@ abstract class qtype_ddmarker_list { public function leaf_node ($id, $qcount) { $instance = $this->get_instance($id); $instance->leaf_to_root($qcount); + //print_object(array('list' => $this)+ compact('id', 'qcount')); } } class qtype_ddmarker_context_list extends qtype_ddmarker_list { - protected $records = array(); - protected $instances = array(); protected function new_list_item($record) { return new qtype_ddmarker_context_list_item($record, $this); @@ -195,9 +216,9 @@ class qtype_ddmarker_context_list extends qtype_ddmarker_list { } } + + class qtype_ddmarker_category_list extends qtype_ddmarker_list { - protected $records = array(); - protected $instances = array(); protected $contextlist; protected function new_list_item($record) { return new qtype_ddmarker_category_list_item($record, $this, $this->contextlist); @@ -212,6 +233,19 @@ class qtype_ddmarker_category_list extends qtype_ddmarker_list { } } +class qtype_ddmarker_question_list extends qtype_ddmarker_list { + protected $categorylist; + protected function new_list_item($record) { + return new qtype_ddmarker_question_list_item($record, $this, $this->categorylist); + } + public function __construct($questions, $categorylist) { + global $DB; + $this->categorylist = $categorylist; + $this->records = $questions; + parent::make_list_item_instances_from_records (); + } +} + $categoryid = optional_param('categoryid', 0, PARAM_INT); $qcontextid = optional_param('contextid', 0, PARAM_INT); $confirm = optional_param('confirm', 0, PARAM_INT); @@ -242,45 +276,45 @@ if ($qcontextid) { $where .= 'AND cat.contextid = cat2.contextid AND cat2.id = :categoryid '; $params['categoryid'] = $categoryid; } -$sql = 'SELECT cat.id, cat.contextid, COUNT(1) AS qcount '. +$sql = 'SELECT q.id, cat.id AS cat_id, cat.contextid, q.name '. $from. $where. - 'GROUP BY cat.id, cat.contextid'; + 'ORDER BY cat.id'; -$catswithquestions = $DB->get_records_sql($sql, $params); +$questions = $DB->get_records_sql($sql, $params); $contextids = array(); -foreach ($catswithquestions as $catwithquestions) { - $contextids[] = $catwithquestions->contextid; +foreach ($questions as $question) { + $contextids[] = $question->contextid; } -$contexts = new qtype_ddmarker_context_list($contextids); -$categories = new qtype_ddmarker_category_list($contextids, $contexts); +$contextlist = new qtype_ddmarker_context_list(array_unique($contextids)); +$categorylist = new qtype_ddmarker_category_list($contextids, $contextlist); +$questionlist = new qtype_ddmarker_question_list($questions, $categorylist); -foreach ($catswithquestions as $catwithquestions) { - $categories->leaf_node($catwithquestions->id, $catwithquestions->qcount); +foreach ($questions as $question) { + $questionlist->leaf_node($question->id, 1); } if ($categoryid || $qcontextid) { if (!$confirm) { $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid')+array('confirm'=>1)); $cancelurl = new moodle_url($PAGE->url); if ($categoryid) { - $torender = $categories->get_instance($categoryid); + $torender = $categorylist->get_instance($categoryid); } else if ($qcontextid) { - $torender = $contexts->get_instance($qcontextid); + $torender = $contextlist->get_instance($qcontextid); } else { - $torender = $contexts->root_node(); + $torender = $contextlist->root_node(); } - echo $contexts->render('listitem', false, $torender); + echo $contextlist->render('listitem', false, $torender); echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); } else if (confirm_sesskey()) { } } else { - echo $contexts->render('listitemaction', true, $contexts->root_node()); + echo $contextlist->render('listitemaction', true, $contextlist->root_node()); } - // Footer. echo $OUTPUT->footer(); diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index b5e481d5546..1503967ed10 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -60,6 +60,7 @@ $string['listitemactioncategory'] = 'Convert all imagetarget questions in catego $string['listitemactioncontext'] = 'Convert all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; $string['listitemcategory'] = 'Questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; $string['listitemcontext'] = 'Questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemquestion'] = 'Question "{$a->name}"'; $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; From 2de9e082eeb3256b36e7d5811b31f188dcd98a81 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 29 Mar 2012 15:29:06 +0700 Subject: [PATCH 100/178] MDL-47494 ddmarker: NOBUG fixing error in sql to fetch relevant questions --- question/type/ddmarker/imagetargetconverter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 6b8ea1a5d42..3398da0777e 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -272,7 +272,7 @@ if ($qcontextid) { $params['path'] = $qcontext->path.'/%'; $params['id'] = $qcontext->id; } else if ($categoryid) { - $from .= ', {context} context, {question_categories} cat2'; + $from .= ', {question_categories} cat2'; $where .= 'AND cat.contextid = cat2.contextid AND cat2.id = :categoryid '; $params['categoryid'] = $categoryid; } From 9437265e8b761f41545bbef2050e37224158abcc Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Thu, 29 Mar 2012 15:30:55 +0700 Subject: [PATCH 101/178] MDL-47494 ddmarker: NOBUG restructuring if conditions to interpret page params --- .../type/ddmarker/imagetargetconverter.php | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 3398da0777e..9e15df3beab 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -176,7 +176,6 @@ abstract class qtype_ddmarker_list { public function leaf_node ($id, $qcount) { $instance = $this->get_instance($id); $instance->leaf_to_root($qcount); - //print_object(array('list' => $this)+ compact('id', 'qcount')); } } @@ -272,6 +271,7 @@ if ($qcontextid) { $params['path'] = $qcontext->path.'/%'; $params['id'] = $qcontext->id; } else if ($categoryid) { + //fetch all questions from this cats context $from .= ', {question_categories} cat2'; $where .= 'AND cat.contextid = cat2.contextid AND cat2.id = :categoryid '; $params['categoryid'] = $categoryid; @@ -279,8 +279,7 @@ if ($qcontextid) { $sql = 'SELECT q.id, cat.id AS cat_id, cat.contextid, q.name '. $from. $where. - 'ORDER BY cat.id'; - + 'ORDER BY cat.id, q.name'; $questions = $DB->get_records_sql($sql, $params); @@ -296,24 +295,25 @@ $questionlist = new qtype_ddmarker_question_list($questions, $categorylist); foreach ($questions as $question) { $questionlist->leaf_node($question->id, 1); } -if ($categoryid || $qcontextid) { - if (!$confirm) { +$questionsselected = (bool) ($categoryid || $qcontextid); +if ($categoryid) { + $top = $categorylist->get_instance($categoryid); +} else if ($qcontextid) { + $top = $contextlist->get_instance($qcontextid); +} else { + $top = $contextlist->root_node(); +} +if (!$confirm) { + if ($questionsselected) { + echo $contextlist->render('listitemaction', false, $top); $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid')+array('confirm'=>1)); $cancelurl = new moodle_url($PAGE->url); - if ($categoryid) { - $torender = $categorylist->get_instance($categoryid); - } else if ($qcontextid) { - $torender = $contextlist->get_instance($qcontextid); - } else { - $torender = $contextlist->root_node(); - } - echo $contextlist->render('listitem', false, $torender); echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); - } else if (confirm_sesskey()) { - + } else { + echo $contextlist->render('listitem', true, $top); } -} else { - echo $contextlist->render('listitemaction', true, $contextlist->root_node()); +} else if (confirm_sesskey()) { + } // Footer. From 5110ed613441c1898785b5563a3241d52798242e Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Fri, 30 Mar 2012 10:30:52 +0100 Subject: [PATCH 102/178] MDL-47494 ddmarker: Files in hints were not moved or deleted properly for OU qtypes. #3478 --- question/type/ddmarker/questiontype.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php index 3f580a12ee9..ee03154bc5b 100644 --- a/question/type/ddmarker/questiontype.php +++ b/question/type/ddmarker/questiontype.php @@ -250,6 +250,7 @@ class qtype_ddmarker extends qtype_ddtoimage_base { $newcontextid, 'qtype_ddmarker', 'bgimage', $questionid); $this->move_files_in_combined_feedback($questionid, $oldcontextid, $newcontextid); + $this->move_files_in_hints($questionid, $oldcontextid, $newcontextid); } /** @@ -265,6 +266,7 @@ class qtype_ddmarker extends qtype_ddtoimage_base { parent::delete_files($questionid, $contextid); $this->delete_files_in_combined_feedback($questionid, $contextid); + $this->delete_files_in_hints($questionid, $contextid); } public function export_to_xml($question, qformat_xml $format, $extra = null) { From d0c22457577a4f8e11f34e2ade1b6c0bb6bca563 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 09:46:23 +0700 Subject: [PATCH 103/178] MDL-47494 ddmarker: NOBUG adding method to process questions. --- question/type/ddmarker/imagetargetconverter.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 9e15df3beab..5e5c7a44df2 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -60,6 +60,21 @@ abstract class qtype_ddmarker_list_item { } } + public function process($progresstrace = null, $depth = 0) { + if (null === $progresstrace) { + $progresstrace = new html_list_progress_trace(); + } + $progresstrace->output((string)$this, $depth); + $this->process_children($progresstrace, $depth); + } + + protected function process_children($progresstrace, $depth) { + $children = array(); + foreach ($this->children as $child) { + $child->process($progresstrace, $depth + 1); + } + } + protected function render_children($stringidentifier, $link) { $children = array(); foreach ($this->children as $child) { @@ -313,7 +328,7 @@ if (!$confirm) { echo $contextlist->render('listitem', true, $top); } } else if (confirm_sesskey()) { - + $top->process(); } // Footer. From d4e4c3131dbcc4ead925e934c197b815acd51e10 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 10:04:15 +0700 Subject: [PATCH 104/178] MDL-47494 ddmarker: NOBUG moved list classes to new file --- .../type/ddmarker/imagetargetconverter.php | 234 +--------------- question/type/ddmarker/questionlists.php | 257 ++++++++++++++++++ 2 files changed, 258 insertions(+), 233 deletions(-) create mode 100644 question/type/ddmarker/questionlists.php diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 5e5c7a44df2..cbbe57f0b94 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -26,239 +26,7 @@ require_once(dirname(__FILE__) . '/../../../config.php'); require_once($CFG->libdir . '/adminlib.php'); - -abstract class qtype_ddmarker_list_item { - /** - * @var count of questions contained in this item and sub items. - */ - protected $qcount = 0; - /** - * @var children array of pointers to list_items either category_list_items or context_list_items - */ - protected $children = array(); - - protected $record; - - - public function add_child($child) { - $this->children[] = $child; - //array_unique relies on __toString() returning a unique string to determine if objects in array - //are the same or not - $this->children = array_unique($this->children); - } - - abstract protected function parent_node (); - - abstract public function render($stringidentifier, $link); - - public function leaf_to_root($qcount) { - $this->qcount += $qcount; - $parent = $this->parent_node(); - if ($parent !== null) { - $parent->add_child($this); - $parent->leaf_to_root($qcount); - } - } - - public function process($progresstrace = null, $depth = 0) { - if (null === $progresstrace) { - $progresstrace = new html_list_progress_trace(); - } - $progresstrace->output((string)$this, $depth); - $this->process_children($progresstrace, $depth); - } - - protected function process_children($progresstrace, $depth) { - $children = array(); - foreach ($this->children as $child) { - $child->process($progresstrace, $depth + 1); - } - } - - protected function render_children($stringidentifier, $link) { - $children = array(); - foreach ($this->children as $child) { - $children[] = $child->render($stringidentifier, $link); - } - return html_writer::alist($children); - } - - public function __toString() { - return get_class($this).' '.$this->record->id; - } - -} -class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { - - public function __construct($record, $list, $parentlist) { - $this->record = $record; - $this->list = $list; - $this->parentlist = $parentlist; - } - - public function parent_node() { - if ($this->record->parent == 0) { - return $this->parentlist->get_instance($this->record->contextid); - } else { - return $this->list->get_instance($this->record->parent); - } - } - - public function render ($stringidentifier, $link) { - global $PAGE; - $a = new stdClass(); - $a->qcount = $this->qcount; - $a->name = $this->record->name; - $thisitem = get_string($stringidentifier.'category', 'qtype_ddmarker', $a); - if ($link) { - $actionurl = new moodle_url($PAGE->url, array('categoryid'=> $this->record->id)); - $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); - } - - return $thisitem.$this->render_children($stringidentifier, $link); - } -} -class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { - - public function __construct($record, $list, $parentlist) { - $this->record = $record; - $this->list = $list; - $this->parentlist = $parentlist; - } - - public function parent_node() { - return $this->parentlist->get_instance($this->record->cat_id); - } - - public function render ($stringidentifier, $link) { - global $PAGE; - $a = new stdClass(); - $a->name = $this->record->name; - $thisitem = get_string('listitemquestion', 'qtype_ddmarker', $a); - return $thisitem; - } -} -class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { - - protected $list; - protected $parentlist = null; - - public function __construct($record, $list) { - $this->record = $record; - $this->list = $list; - } - - public function parent_node() { - $pathids = explode('/', $this->record->path); - if (count($pathids) >= 3) { - return $this->list->get_instance($pathids[count($pathids)-2]); - } else { - return null; - } - } - - public function render ($stringidentifier, $link) { - global $PAGE; - $a = new stdClass(); - $a->qcount = $this->qcount; - $a->name = print_context_name($this->record); - $thisitem = get_string($stringidentifier.'context', 'qtype_ddmarker', $a); - if ($link) { - $actionurl = new moodle_url($PAGE->url, array('contextid'=> $this->record->id)); - $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); - } - return $thisitem.$this->render_children($stringidentifier, $link); - } -} -/** - * Describes a nested list of listitems. This class and sub classes contain the functionality to build the nested list. - **/ -abstract class qtype_ddmarker_list { - protected $records = array(); - protected $instances = array(); - abstract protected function new_list_item($record); - protected function make_list_item_instances_from_records ($contextids = null) { - if (!empty($this->records)) { - foreach ($this->records as $id => $record) { - $this->instances[$id] = $this->new_list_item($record); - } - } - } - public function get_instance($id) { - return $this->instances[$id]; - } - - public function leaf_node ($id, $qcount) { - $instance = $this->get_instance($id); - $instance->leaf_to_root($qcount); - } - -} - -class qtype_ddmarker_context_list extends qtype_ddmarker_list { - - protected function new_list_item($record) { - return new qtype_ddmarker_context_list_item($record, $this); - } - - public function __construct($contextids) { - global $DB; - $this->records = array(); - foreach ($contextids as $contextid) { - if (!isset($this->records[$contextid])) { - $this->records[$contextid] = get_context_instance_by_id($contextid, MUST_EXIST); - } - $parents = get_parent_contexts($this->records[$contextid]); - foreach ($parents as $parentcontextid) { - if (!isset($this->records[$parentcontextid])) { - $this->records[$parentcontextid] = - get_context_instance_by_id($parentcontextid, MUST_EXIST); - } - } - } - parent::make_list_item_instances_from_records ($contextids); - } - public function render($stringidentifier, $link, $roottorender = null) { - if ($roottorender === null) { - $roottorender = $this->root_node(); - } - $rootitem = html_writer::tag('li', $roottorender->render($stringidentifier, $link)); - return html_writer::tag('ul', $rootitem); - } - public function root_node () { - return $this->get_instance(get_context_instance(CONTEXT_SYSTEM)->id); - } -} - - - -class qtype_ddmarker_category_list extends qtype_ddmarker_list { - protected $contextlist; - protected function new_list_item($record) { - return new qtype_ddmarker_category_list_item($record, $this, $this->contextlist); - } - public function __construct($contextids, $contextlist) { - global $DB; - $this->contextlist = $contextlist; - //probably most efficient way to reconstruct question category tree is to load all q cats in relevant contexts - list($sql, $params) = $DB->get_in_or_equal($contextids); - $this->records = $DB->get_records_select('question_categories', "contextid ".$sql, $params); - parent::make_list_item_instances_from_records ($contextids); - } -} - -class qtype_ddmarker_question_list extends qtype_ddmarker_list { - protected $categorylist; - protected function new_list_item($record) { - return new qtype_ddmarker_question_list_item($record, $this, $this->categorylist); - } - public function __construct($questions, $categorylist) { - global $DB; - $this->categorylist = $categorylist; - $this->records = $questions; - parent::make_list_item_instances_from_records (); - } -} +require_once($CFG->dirroot.'/question/type/ddmarker/questionlists.php'); $categoryid = optional_param('categoryid', 0, PARAM_INT); $qcontextid = optional_param('contextid', 0, PARAM_INT); diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php new file mode 100644 index 00000000000..b7edebee3e8 --- /dev/null +++ b/question/type/ddmarker/questionlists.php @@ -0,0 +1,257 @@ +. + +/** + * These classes handle transforming arrays of records into a linked tree of contexts, categories and questions. + * + * @package qtype + * @subpackage pmatch + * @copyright 2012 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +abstract class qtype_ddmarker_list_item { + /** + * @var count of questions contained in this item and sub items. + */ + protected $qcount = 0; + /** + * @var children array of pointers to list_items either category_list_items or context_list_items + */ + protected $children = array(); + + protected $record; + + + public function add_child($child) { + $this->children[] = $child; + //array_unique relies on __toString() returning a unique string to determine if objects in array + //are the same or not + $this->children = array_unique($this->children); + } + + abstract protected function parent_node (); + + abstract public function render($stringidentifier, $link); + + public function leaf_to_root($qcount) { + $this->qcount += $qcount; + $parent = $this->parent_node(); + if ($parent !== null) { + $parent->add_child($this); + $parent->leaf_to_root($qcount); + } + } + + public function process($progresstrace = null, $depth = 0) { + if (null === $progresstrace) { + $progresstrace = new html_list_progress_trace(); + } + $progresstrace->output((string)$this, $depth); + $this->process_children($progresstrace, $depth); + } + + protected function process_children($progresstrace, $depth) { + $children = array(); + foreach ($this->children as $child) { + $child->process($progresstrace, $depth + 1); + } + } + + protected function render_children($stringidentifier, $link) { + $children = array(); + foreach ($this->children as $child) { + $children[] = $child->render($stringidentifier, $link); + } + return html_writer::alist($children); + } + + public function __toString() { + return get_class($this).' '.$this->record->id; + } + +} +class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { + + public function __construct($record, $list, $parentlist) { + $this->record = $record; + $this->list = $list; + $this->parentlist = $parentlist; + } + + public function parent_node() { + if ($this->record->parent == 0) { + return $this->parentlist->get_instance($this->record->contextid); + } else { + return $this->list->get_instance($this->record->parent); + } + } + + public function render ($stringidentifier, $link) { + global $PAGE; + $a = new stdClass(); + $a->qcount = $this->qcount; + $a->name = $this->record->name; + $thisitem = get_string($stringidentifier.'category', 'qtype_ddmarker', $a); + if ($link) { + $actionurl = new moodle_url($PAGE->url, array('categoryid'=> $this->record->id)); + $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); + } + + return $thisitem.$this->render_children($stringidentifier, $link); + } +} +class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { + + public function __construct($record, $list, $parentlist) { + $this->record = $record; + $this->list = $list; + $this->parentlist = $parentlist; + } + + public function parent_node() { + return $this->parentlist->get_instance($this->record->cat_id); + } + + public function render ($stringidentifier, $link) { + global $PAGE; + $a = new stdClass(); + $a->name = $this->record->name; + $thisitem = get_string('listitemquestion', 'qtype_ddmarker', $a); + return $thisitem; + } +} +class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { + + protected $list; + protected $parentlist = null; + + public function __construct($record, $list) { + $this->record = $record; + $this->list = $list; + } + + public function parent_node() { + $pathids = explode('/', $this->record->path); + if (count($pathids) >= 3) { + return $this->list->get_instance($pathids[count($pathids)-2]); + } else { + return null; + } + } + + public function render ($stringidentifier, $link) { + global $PAGE; + $a = new stdClass(); + $a->qcount = $this->qcount; + $a->name = print_context_name($this->record); + $thisitem = get_string($stringidentifier.'context', 'qtype_ddmarker', $a); + if ($link) { + $actionurl = new moodle_url($PAGE->url, array('contextid'=> $this->record->id)); + $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); + } + return $thisitem.$this->render_children($stringidentifier, $link); + } +} +/** + * Describes a nested list of listitems. This class and sub classes contain the functionality to build the nested list. + **/ +abstract class qtype_ddmarker_list { + protected $records = array(); + protected $instances = array(); + abstract protected function new_list_item($record); + protected function make_list_item_instances_from_records ($contextids = null) { + if (!empty($this->records)) { + foreach ($this->records as $id => $record) { + $this->instances[$id] = $this->new_list_item($record); + } + } + } + public function get_instance($id) { + return $this->instances[$id]; + } + + public function leaf_node ($id, $qcount) { + $instance = $this->get_instance($id); + $instance->leaf_to_root($qcount); + } + +} + +class qtype_ddmarker_context_list extends qtype_ddmarker_list { + + protected function new_list_item($record) { + return new qtype_ddmarker_context_list_item($record, $this); + } + + public function __construct($contextids) { + global $DB; + $this->records = array(); + foreach ($contextids as $contextid) { + if (!isset($this->records[$contextid])) { + $this->records[$contextid] = get_context_instance_by_id($contextid, MUST_EXIST); + } + $parents = get_parent_contexts($this->records[$contextid]); + foreach ($parents as $parentcontextid) { + if (!isset($this->records[$parentcontextid])) { + $this->records[$parentcontextid] = + get_context_instance_by_id($parentcontextid, MUST_EXIST); + } + } + } + parent::make_list_item_instances_from_records ($contextids); + } + public function render($stringidentifier, $link, $roottorender = null) { + if ($roottorender === null) { + $roottorender = $this->root_node(); + } + $rootitem = html_writer::tag('li', $roottorender->render($stringidentifier, $link)); + return html_writer::tag('ul', $rootitem); + } + public function root_node () { + return $this->get_instance(get_context_instance(CONTEXT_SYSTEM)->id); + } +} + + + +class qtype_ddmarker_category_list extends qtype_ddmarker_list { + protected $contextlist; + protected function new_list_item($record) { + return new qtype_ddmarker_category_list_item($record, $this, $this->contextlist); + } + public function __construct($contextids, $contextlist) { + global $DB; + $this->contextlist = $contextlist; + //probably most efficient way to reconstruct question category tree is to load all q cats in relevant contexts + list($sql, $params) = $DB->get_in_or_equal($contextids); + $this->records = $DB->get_records_select('question_categories', "contextid ".$sql, $params); + parent::make_list_item_instances_from_records ($contextids); + } +} + +class qtype_ddmarker_question_list extends qtype_ddmarker_list { + protected $categorylist; + protected function new_list_item($record) { + return new qtype_ddmarker_question_list_item($record, $this, $this->categorylist); + } + public function __construct($questions, $categorylist) { + global $DB; + $this->categorylist = $categorylist; + $this->records = $questions; + parent::make_list_item_instances_from_records (); + } +} \ No newline at end of file From 44f16ef16d2b227dfc4dff36016fbec8e13c7b60 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 10:25:13 +0700 Subject: [PATCH 105/178] MDL-47494 ddmarker: NOBUG some fixes to php doc comments --- question/type/ddmarker/imagetargetconverter.php | 6 +++--- question/type/ddmarker/questionlists.php | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index cbbe57f0b94..55496749fb6 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -15,11 +15,11 @@ // along with Moodle. If not, see . /** - * This page lets admins check the environment requirements for this question type. + * This page lets admin convert imagetarget questions to the ddmarker question type. * * @package qtype - * @subpackage pmatch - * @copyright 2012 The Open University + * @subpackage ddmarker + * @copyright 2012 Jamie Pratt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index b7edebee3e8..a92a1bb7ff8 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -1,5 +1,4 @@ Date: Sat, 31 Mar 2012 10:52:28 +0700 Subject: [PATCH 106/178] MDL-47494 ddmarker: NOBUG subclass list and list item classes to add specific processing --- .../type/ddmarker/imagetargetconverter.php | 19 ++++++++++++++++++- question/type/ddmarker/questionlists.php | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 55496749fb6..2a40c465419 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -28,6 +28,23 @@ require_once(dirname(__FILE__) . '/../../../config.php'); require_once($CFG->libdir . '/adminlib.php'); require_once($CFG->dirroot.'/question/type/ddmarker/questionlists.php'); +class qtype_ddmarker_question_converter_list extends qtype_ddmarker_question_list { + protected function new_list_item($record) { + return new qtype_ddmarker_question_converter_list_item($record, $this, $this->categorylist); + } +} +class qtype_ddmarker_question_converter_list_item extends qtype_ddmarker_question_list_item { + public function process($progresstrace = null, $depth = 0) { + $this->convert_question(); + parent::process($progresstrace, $depth);//outputs progress message + } + + protected function convert_question() { + $questionrec = $this->record; + + } +} + $categoryid = optional_param('categoryid', 0, PARAM_INT); $qcontextid = optional_param('contextid', 0, PARAM_INT); $confirm = optional_param('confirm', 0, PARAM_INT); @@ -73,7 +90,7 @@ foreach ($questions as $question) { $contextlist = new qtype_ddmarker_context_list(array_unique($contextids)); $categorylist = new qtype_ddmarker_category_list($contextids, $contextlist); -$questionlist = new qtype_ddmarker_question_list($questions, $categorylist); +$questionlist = new qtype_ddmarker_question_converter_list($questions, $categorylist); foreach ($questions as $question) { $questionlist->leaf_node($question->id, 1); diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index a92a1bb7ff8..6c40685693d 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Moodle. If not, see . +defined('MOODLE_INTERNAL') || die(); + /** * These classes handle transforming arrays of records into a linked tree of contexts, categories and questions. * From e1bd1cf6e309a1283a9c8f03d395c1ef65e6af6b Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 14:09:31 +0700 Subject: [PATCH 107/178] MDL-47494 ddmarker: NOBUG loading imagetarget and answers records all before processing --- .../type/ddmarker/imagetargetconverter.php | 24 ++++++++++++++----- question/type/ddmarker/questionlists.php | 19 ++++++++++++++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 2a40c465419..a0a9bb79c43 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -32,16 +32,30 @@ class qtype_ddmarker_question_converter_list extends qtype_ddmarker_question_lis protected function new_list_item($record) { return new qtype_ddmarker_question_converter_list_item($record, $this, $this->categorylist); } + public function prepare_for_processing($top) { + global $DB; + $questionids = $top->question_ids(); + list($inorequalsql, $inorequalparams) = $DB->get_in_or_equal($questionids); + $imagetargetrecords = $DB->get_records_select('question_imagetarget', 'question '.$inorequalsql, $inorequalparams); + $answers = array(); + foreach ($imagetargetrecords as $imagetargetrecord) { + $this->get_instance($imagetargetrecord->question)->imagetargetrecord = $imagetargetrecord; + } + $answerrecords = $DB->get_records_select('question_answers', 'question '.$inorequalsql, $inorequalparams); + foreach ($answerrecords as $answerrecord) { + $this->get_instance($answerrecord->question)->answers[] = $answerrecord; + } + } } class qtype_ddmarker_question_converter_list_item extends qtype_ddmarker_question_list_item { + public $imagetargetrecord = null; + public $answers = array(); public function process($progresstrace = null, $depth = 0) { $this->convert_question(); parent::process($progresstrace, $depth);//outputs progress message } - protected function convert_question() { $questionrec = $this->record; - } } @@ -76,10 +90,7 @@ if ($qcontextid) { $where .= 'AND cat.contextid = cat2.contextid AND cat2.id = :categoryid '; $params['categoryid'] = $categoryid; } -$sql = 'SELECT q.id, cat.id AS cat_id, cat.contextid, q.name '. - $from. - $where. - 'ORDER BY cat.id, q.name'; +$sql = 'SELECT q.*, cat.contextid '.$from.$where.'ORDER BY cat.id, q.name'; $questions = $DB->get_records_sql($sql, $params); @@ -113,6 +124,7 @@ if (!$confirm) { echo $contextlist->render('listitem', true, $top); } } else if (confirm_sesskey()) { + $questionlist->prepare_for_processing($top); $top->process(); } diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index 6c40685693d..23cd17ab540 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -72,6 +72,18 @@ abstract class qtype_ddmarker_list_item { } } + public function question_ids() { + return $this->child_question_ids(); + } + + protected function child_question_ids() { + $ids = array(); + foreach ($this->children as $child) { + $ids = array_merge($ids, $child->question_ids()); + } + return $ids; + } + protected function render_children($stringidentifier, $link) { $children = array(); foreach ($this->children as $child) { @@ -124,7 +136,7 @@ class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { } public function parent_node() { - return $this->parentlist->get_instance($this->record->cat_id); + return $this->parentlist->get_instance($this->record->category); } public function render ($stringidentifier, $link) { @@ -134,6 +146,9 @@ class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { $thisitem = get_string('listitemquestion', 'qtype_ddmarker', $a); return $thisitem; } + public function question_ids() { + return array($this->record->id); + } } class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { @@ -255,4 +270,6 @@ class qtype_ddmarker_question_list extends qtype_ddmarker_list { $this->records = $questions; parent::make_list_item_instances_from_records (); } + public function prepare_for_processing($top) { + } } \ No newline at end of file From 864e78601941ec3aaeb98b5937fa35bb950e0549 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 14:23:25 +0700 Subject: [PATCH 108/178] MDL-47494 ddmarker: NOBUG make it possible to select just one question for processing --- question/type/ddmarker/imagetargetconverter.php | 13 ++++++++++--- question/type/ddmarker/questionlists.php | 4 ++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index a0a9bb79c43..93423dd73e5 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -61,6 +61,7 @@ class qtype_ddmarker_question_converter_list_item extends qtype_ddmarker_questio $categoryid = optional_param('categoryid', 0, PARAM_INT); $qcontextid = optional_param('contextid', 0, PARAM_INT); +$questionid = optional_param('questionid', 0, PARAM_INT); $confirm = optional_param('confirm', 0, PARAM_INT); // Check the user is logged in. require_login(); @@ -89,6 +90,10 @@ if ($qcontextid) { $from .= ', {question_categories} cat2'; $where .= 'AND cat.contextid = cat2.contextid AND cat2.id = :categoryid '; $params['categoryid'] = $categoryid; +} else if ($questionid) { + //fetch all questions from this cats context + $where .= 'AND q.id = :questionid '; + $params['questionid'] = $questionid; } $sql = 'SELECT q.*, cat.contextid '.$from.$where.'ORDER BY cat.id, q.name'; @@ -106,8 +111,10 @@ $questionlist = new qtype_ddmarker_question_converter_list($questions, $category foreach ($questions as $question) { $questionlist->leaf_node($question->id, 1); } -$questionsselected = (bool) ($categoryid || $qcontextid); -if ($categoryid) { +$questionsselected = (bool) ($categoryid || $qcontextid || $questionid); +if ($questionid) { + $top = $questionlist->get_instance($questionid); +} else if ($categoryid) { $top = $categorylist->get_instance($categoryid); } else if ($qcontextid) { $top = $contextlist->get_instance($qcontextid); @@ -117,7 +124,7 @@ if ($categoryid) { if (!$confirm) { if ($questionsselected) { echo $contextlist->render('listitemaction', false, $top); - $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid')+array('confirm'=>1)); + $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid', 'questionid') + array('confirm'=>1)); $cancelurl = new moodle_url($PAGE->url); echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); } else { diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index 23cd17ab540..ecb5f4f93f2 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -144,6 +144,10 @@ class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { $a = new stdClass(); $a->name = $this->record->name; $thisitem = get_string('listitemquestion', 'qtype_ddmarker', $a); + if ($link) { + $actionurl = new moodle_url($PAGE->url, array('questionid'=> $this->record->id)); + $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); + } return $thisitem; } public function question_ids() { From c3efffdb72aa7221f84c7a6cd79fed642aa78550 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 14:42:37 +0700 Subject: [PATCH 109/178] MDL-47494 ddmarker: NOBUG improvement in wording of messages for the user --- question/type/ddmarker/imagetargetconverter.php | 2 +- question/type/ddmarker/lang/en/qtype_ddmarker.php | 14 +++++++++----- question/type/ddmarker/questionlists.php | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 93423dd73e5..08c8090a16a 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -128,7 +128,7 @@ if (!$confirm) { $cancelurl = new moodle_url($PAGE->url); echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); } else { - echo $contextlist->render('listitem', true, $top); + echo $contextlist->render('listitemlist', true, $top); } } else if (confirm_sesskey()) { $questionlist->prepare_for_processing($top); diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 1503967ed10..41211b816c1 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -56,11 +56,15 @@ $string['formerror_unrecognisedwidthheightpart'] = 'We do not recognise the widt $string['formerror_unrecognisedxypart'] = 'We do not recognise the x,y coordinates you have specified. Your coordinates for a {$a->shape} should be expressed as - {$a->coordsstring}.'; $string['imagetargetconverter'] = 'Convert image target questions to drag and drop marker'; $string['infinite'] = 'Infinite'; -$string['listitemactioncategory'] = 'Convert all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; -$string['listitemactioncontext'] = 'Convert all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; -$string['listitemcategory'] = 'Questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; -$string['listitemcontext'] = 'Questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; -$string['listitemquestion'] = 'Question "{$a->name}"'; +$string['listitemactioncategory'] = 'About to convert all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemactioncontext'] = 'About to convert all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemactionquestion'] = 'About to convert question "{$a->name}"'; +$string['listitemlistcategory'] = 'Select all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemlistcontext'] = 'Select all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemlistquestion'] = 'Select question "{$a->name}"'; +$string['listitemprocessingcategory'] = 'Converting all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemprocessingcontext'] = 'Converting all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)'; +$string['listitemprocessingquestion'] = 'Converted question "{$a->name}"'; $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index ecb5f4f93f2..5bbdbcb42e1 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -61,7 +61,7 @@ abstract class qtype_ddmarker_list_item { if (null === $progresstrace) { $progresstrace = new html_list_progress_trace(); } - $progresstrace->output((string)$this, $depth); + $progresstrace->output($this->render('listitemprocessing', false), $depth); $this->process_children($progresstrace, $depth); } @@ -143,7 +143,7 @@ class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { global $PAGE; $a = new stdClass(); $a->name = $this->record->name; - $thisitem = get_string('listitemquestion', 'qtype_ddmarker', $a); + $thisitem = get_string($stringidentifier.'question', 'qtype_ddmarker', $a); if ($link) { $actionurl = new moodle_url($PAGE->url, array('questionid'=> $this->record->id)); $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); From 45766c517adebce30ae4d05712fbe5c8250de879 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sat, 31 Mar 2012 18:13:39 +0700 Subject: [PATCH 110/178] MDL-47494 ddmarker: NOBUG implemented conversion of question itself --- .../type/ddmarker/imagetargetconverter.php | 67 ++++++++++++++++++- question/type/ddmarker/questionlists.php | 15 +++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 08c8090a16a..19667a43c34 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -55,7 +55,70 @@ class qtype_ddmarker_question_converter_list_item extends qtype_ddmarker_questio parent::process($progresstrace, $depth);//outputs progress message } protected function convert_question() { - $questionrec = $this->record; + global $DB; + foreach ($this->answers as $answer) { + $no = 1; + if ('*' !== $answer->answer) { + $drop = new stdClass(); + $drop->questionid = $this->record->id; + $drop->shape = 'rectangle'; + $drop->no = $no; + list($x1, $y1, $x2, $y2) = explode(',', $answer->answer); + $width = $x2 - $x1; + $height = $y2 - $y1; + $drop->coords = "{$x1},{$y1};{$width},{$height}"; + $drop->choice = 1; + $DB->insert_record('qtype_ddmarker_drops', $drop); + $no++; + $correctfeedback = $answer->feedback; + $correctfeedbackformat = $answer->feedbackformat; + } else { + $incorrectfeedback = $answer->feedback; + $incorrectfeedbackformat = $answer->feedbackformat; + } + } + $drag = new stdClass(); + $drag->questionid = $this->record->id; + $drag->no = 1; + $drag->label = "X"; + $drag->infinite = 0; + $DB->insert_record('qtype_ddmarker_drags', $drag); + + $ddmarker = new stdClass(); + $ddmarker->questionid = $this->record->id; + $ddmarker->shuffleanswers = 0; + $ddmarker->correctfeedback = $correctfeedback; + $ddmarker->correctfeedbackformat = $correctfeedbackformat; + $ddmarker->partiallycorrectfeedback = ''; + $ddmarker->partiallycorrectfeedbackformat = 1; + $ddmarker->incorrectfeedback = $incorrectfeedback; + $ddmarker->incorrectfeedbackformat = $incorrectfeedbackformat; + $ddmarker->shownumcorrect = 0; + $ddmarker->showmisplaced = 0; + $DB->insert_record('qtype_ddmarker', $ddmarker); + + $newrec = clone($this->record); + unset($newrec->contextid); + $newrec->qtype = 'ddmarker'; + $newrec->timemodified = time(); + $DB->update_record('question', $newrec); + + $fs = get_file_storage(); + $bgimagefile = $fs->get_file($this->course_context_id(), + 'course', + 'legacy', + '0', + '/'.dirname($this->imagetargetrecord->qimage).'/', + basename($this->imagetargetrecord->qimage)); + $newbgimagefile = new stdClass(); + $newbgimagefile->component = 'qtype_ddmarker'; + $newbgimagefile->filearea = 'bgimage'; + $newbgimagefile->filepath = '/'; + $newbgimagefile->itemid = $this->record->id; + $fs->create_file_from_storedfile($newbgimagefile, $bgimagefile); + + $DB->delete_records('question_imagetarget', array('question' => $this->record->id)); + $DB->delete_records('question_answers', array('question' => $this->record->id)); } } @@ -77,7 +140,7 @@ echo $OUTPUT->heading_with_help(get_string('imagetargetconverter', 'qtype_ddmark $params = array(); $from = 'FROM {question_categories} cat, {question} q'; -$where = ' WHERE q.category = cat.id '; +$where = ' WHERE q.qtype = \'imagetarget\' AND q.category = cat.id '; if ($qcontextid) { $qcontext = get_context_instance_by_id($qcontextid, MUST_EXIST); diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index 5bbdbcb42e1..af4668d8a3b 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -96,6 +96,13 @@ abstract class qtype_ddmarker_list_item { return get_class($this).' '.$this->record->id; } + /** + * @return the course id in which this item is contained or the id of the front page course + */ + public function course_context_id() { + return $this->parent_node()->course_context_id(); + } + } class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { @@ -185,6 +192,14 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { } return $thisitem.$this->render_children($stringidentifier, $link); } + + public function course_context_id() { + if ((int)$this->record->contextlevel === CONTEXT_COURSE) { + return $this->record->id; + } else { + return parent::course_context_id(); + } + } } /** * Describes a nested list of listitems. This class and sub classes contain the functionality to build the nested list. From a79d43fe9c21d7a3f1a4589627c91c27a53a3118 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Sun, 1 Apr 2012 17:30:42 +0700 Subject: [PATCH 111/178] MDL-47494 ddmarker: NOBUG adding a condition to catch if there are no questions to convert --- .../type/ddmarker/imagetargetconverter.php | 69 ++++++++++--------- .../type/ddmarker/lang/en/qtype_ddmarker.php | 1 + 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 19667a43c34..364d6451381 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -162,41 +162,44 @@ $sql = 'SELECT q.*, cat.contextid '.$from.$where.'ORDER BY cat.id, q.name'; $questions = $DB->get_records_sql($sql, $params); -$contextids = array(); -foreach ($questions as $question) { - $contextids[] = $question->contextid; -} - -$contextlist = new qtype_ddmarker_context_list(array_unique($contextids)); -$categorylist = new qtype_ddmarker_category_list($contextids, $contextlist); -$questionlist = new qtype_ddmarker_question_converter_list($questions, $categorylist); - -foreach ($questions as $question) { - $questionlist->leaf_node($question->id, 1); -} -$questionsselected = (bool) ($categoryid || $qcontextid || $questionid); -if ($questionid) { - $top = $questionlist->get_instance($questionid); -} else if ($categoryid) { - $top = $categorylist->get_instance($categoryid); -} else if ($qcontextid) { - $top = $contextlist->get_instance($qcontextid); +if (!count($questions)) { + echo html_writer::tag('div', get_string('noquestionsfound', 'qtype_ddmarker')); } else { - $top = $contextlist->root_node(); -} -if (!$confirm) { - if ($questionsselected) { - echo $contextlist->render('listitemaction', false, $top); - $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid', 'questionid') + array('confirm'=>1)); - $cancelurl = new moodle_url($PAGE->url); - echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); - } else { - echo $contextlist->render('listitemlist', true, $top); + $contextids = array(); + foreach ($questions as $question) { + $contextids[] = $question->contextid; } -} else if (confirm_sesskey()) { - $questionlist->prepare_for_processing($top); - $top->process(); -} + $contextlist = new qtype_ddmarker_context_list(array_unique($contextids)); + $categorylist = new qtype_ddmarker_category_list($contextids, $contextlist); + $questionlist = new qtype_ddmarker_question_converter_list($questions, $categorylist); + + foreach ($questions as $question) { + $questionlist->leaf_node($question->id, 1); + } + $questionsselected = (bool) ($categoryid || $qcontextid || $questionid); + if ($questionid) { + $top = $questionlist->get_instance($questionid); + } else if ($categoryid) { + $top = $categorylist->get_instance($categoryid); + } else if ($qcontextid) { + $top = $contextlist->get_instance($qcontextid); + } else { + $top = $contextlist->root_node(); + } + if (!$confirm) { + if ($questionsselected) { + echo $contextlist->render('listitemaction', false, $top); + $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid', 'questionid') + array('confirm'=>1)); + $cancelurl = new moodle_url($PAGE->url); + echo $OUTPUT->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl); + } else { + echo $contextlist->render('listitemlist', true, $top); + } + } else if (confirm_sesskey()) { + $questionlist->prepare_for_processing($top); + $top->process(); + } +} // Footer. echo $OUTPUT->footer(); diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php index 41211b816c1..aa4cc9263b9 100644 --- a/question/type/ddmarker/lang/en/qtype_ddmarker.php +++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php @@ -69,6 +69,7 @@ $string['marker'] = 'Marker'; $string['marker_n'] = 'Marker {no}'; $string['markers'] = 'Markers'; $string['nolabel'] = 'No label text'; +$string['noquestionsfound'] = 'No questions found to convert here.'; $string['pleasedragatleastonemarker'] = 'Your answer is not complete, you must place at least one marker on the image.'; $string['pluginname'] = 'Drag and drop markers'; $string['previewarea'] = 'Preview area -'; From 35c7dea6d201de9fd72ba5920119ffe09257e3af Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 2 Apr 2012 16:33:09 +0700 Subject: [PATCH 112/178] MDL-47494 ddmarker: NOBUG added script needed for upgrading attempt data from imagetarget qs --- question/type/ddmarker/db/upgradelib.php | 85 ++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 question/type/ddmarker/db/upgradelib.php diff --git a/question/type/ddmarker/db/upgradelib.php b/question/type/ddmarker/db/upgradelib.php new file mode 100644 index 00000000000..2ab9118ddb7 --- /dev/null +++ b/question/type/ddmarker/db/upgradelib.php @@ -0,0 +1,85 @@ +. + +/** + * Upgrade library code for the ddmarker question type. This will only get triggered by the code + * to convert imagetarget questions to ddmarker. + * + * @package qtype + * @subpackage ddmarker + * @copyright 2012 Jamie Pratt + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +/** + * Class for converting attempt data from imagetarget questions when converting + * attempts to the new question engine. + * + * This class is used by the code in question/engine/upgrade/upgradelib.php. + * + * @copyright 2010 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_ddmarker_qe2_attempt_updater extends question_qtype_attempt_updater { + public function right_answer() { + $drag = reset($this->question->options->drags); + return '{'.get_string('dropzone', 'qtype_ddmarker', '1')." -> ".$drag->label.'}'; + } + + public function was_answered($state) { + return !empty($state->answer); + } + + public function response_summary($state) { + if (!empty($state->answer)) { + $drag = reset($this->question->options->drags); + foreach ($this->question->options->drops as $drop) { + list($xy, $wh) = explode(';', $drop->coords); + list($x, $y) = explode(',', $xy); + list($w, $h) = explode(',', $wh); + list($answerx, $answery) = explode(',', $state->answer); + if (($answerx >= $x && $answerx <= ($x + $w)) && ($answery >= $y && $answery <= ($y + $h))) { + return '{'.get_string('dropzone', 'qtype_ddmarker', $drop->no)." -> ".$drag->label.'}'; + } + } + return ''; + } else { + return null; + } + } + + public function question_summary() { + $drag = reset($this->question->options->drags); + return parent::question_summary().'[['.get_string('dropzone', 'qtype_ddmarker', '1')."]] -> {".$drag->label.'}'; + } + + public function set_first_step_data_elements($state, &$data) { + $data['_choiceorder1'] = '1'; + } + + public function supply_missing_first_step_data(&$data) { + } + + public function set_data_elements_for_step($state, &$data) { + if (!empty($state->answer)) { + $data['c1'] = $state->answer; + } + } +} From 5153be2b26f32dfd245e71ecbda2360e39dc80f3 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Mon, 2 Apr 2012 17:20:12 +0700 Subject: [PATCH 113/178] MDL-47494 ddmarker: NOBUG not using list progress trace any more as it is has a bug --- .../type/ddmarker/imagetargetconverter.php | 2 ++ question/type/ddmarker/questionlists.php | 35 +++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 364d6451381..6533917d38b 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -198,7 +198,9 @@ if (!count($questions)) { } } else if (confirm_sesskey()) { $questionlist->prepare_for_processing($top); + echo '
    '; $top->process(); + echo '
'; } } // Footer. diff --git a/question/type/ddmarker/questionlists.php b/question/type/ddmarker/questionlists.php index af4668d8a3b..9f8a0ba9f4d 100644 --- a/question/type/ddmarker/questionlists.php +++ b/question/type/ddmarker/questionlists.php @@ -46,7 +46,11 @@ abstract class qtype_ddmarker_list_item { abstract protected function parent_node (); - abstract public function render($stringidentifier, $link); + public function render($stringidentifier, $link) { + return $this->render_item($stringidentifier, $link).$this->render_children($stringidentifier, $link); + } + + abstract protected function render_item($stringidentifier, $link); public function leaf_to_root($qcount) { $this->qcount += $qcount; @@ -57,19 +61,20 @@ abstract class qtype_ddmarker_list_item { } } - public function process($progresstrace = null, $depth = 0) { - if (null === $progresstrace) { - $progresstrace = new html_list_progress_trace(); - } - $progresstrace->output($this->render('listitemprocessing', false), $depth); - $this->process_children($progresstrace, $depth); + public function process() { + echo '
  • '; + echo $this->render_item('listitemprocessing', false); + $this->process_children(); + echo '
  • '; + flush(); } - protected function process_children($progresstrace, $depth) { - $children = array(); + protected function process_children() { + echo '
      '; foreach ($this->children as $child) { - $child->process($progresstrace, $depth + 1); + $child->process(); } + echo '
    '; } public function question_ids() { @@ -120,7 +125,7 @@ class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { } } - public function render ($stringidentifier, $link) { + protected function render_item ($stringidentifier, $link) { global $PAGE; $a = new stdClass(); $a->qcount = $this->qcount; @@ -131,7 +136,7 @@ class qtype_ddmarker_category_list_item extends qtype_ddmarker_list_item { $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); } - return $thisitem.$this->render_children($stringidentifier, $link); + return $thisitem; } } class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { @@ -146,7 +151,7 @@ class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item { return $this->parentlist->get_instance($this->record->category); } - public function render ($stringidentifier, $link) { + protected function render_item ($stringidentifier, $link) { global $PAGE; $a = new stdClass(); $a->name = $this->record->name; @@ -180,7 +185,7 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { } } - public function render ($stringidentifier, $link) { + protected function render_item ($stringidentifier, $link) { global $PAGE; $a = new stdClass(); $a->qcount = $this->qcount; @@ -190,7 +195,7 @@ class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item { $actionurl = new moodle_url($PAGE->url, array('contextid'=> $this->record->id)); $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl)); } - return $thisitem.$this->render_children($stringidentifier, $link); + return $thisitem; } public function course_context_id() { From d544fb24ce8e95e69b0f4957e989d8d42fe57eb0 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 4 Apr 2012 12:58:02 +0700 Subject: [PATCH 114/178] MDL-47494 ddmarker: NOBUG added script to do upgrade automatically during 2.1 site upgrade --- question/type/ddmarker/db/install.php | 166 ++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 question/type/ddmarker/db/install.php diff --git a/question/type/ddmarker/db/install.php b/question/type/ddmarker/db/install.php new file mode 100644 index 00000000000..59a39eb8cb2 --- /dev/null +++ b/question/type/ddmarker/db/install.php @@ -0,0 +1,166 @@ +. + +/** + * Matching question type upgrade code. + * + * @package qtype + * @subpackage match + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + + +function index_array_of_records_by_key($key, $recs) { + $out = array(); + foreach ($recs as $id => $rec) { + if (!isset($out[$rec->{$key}])) { + $out[$rec->{$key}] = array(); + } + $out[$rec->{$key}][$id] = $rec; + } + return $out; +} + +function course_context_id($catcontextid) { + $context = get_context_instance_by_id($catcontextid); + while ($context->contextlevel != CONTEXT_COURSE) { + $context = get_context_instance_by_id(get_parent_contextid($context)); + } + return $context->id; +} +/** + * Upgrade code for the matching question type. + * @param int $oldversion the version we are upgrading from. + */ +function xmldb_qtype_ddmarker_install() { + global $DB, $OUTPUT; + + $from = 'FROM {question_categories} cat, {question} q'; + $where = ' WHERE q.qtype = \'imagetarget\' AND q.category = cat.id '; + + $sql = 'SELECT q.*, cat.contextid '.$from.$where.'ORDER BY cat.id, q.name'; + + $questions = $DB->get_records_sql($sql); + + if (!empty($questions)) { + foreach ($questions as $question) { + $dragssql = 'SELECT drag.* '.$from.', {qtype_ddmarker_drags} drag'.$where.' AND drag.questionid = q.id'; + $drags = index_array_of_records_by_key('questionid', $DB->get_records_sql($dragssql)); + + $dropssql = 'SELECT drop.* '.$from.', {qtype_ddmarker_drops} drop'.$where.' AND drop.questionid = q.id'; + $drops = index_array_of_records_by_key('questionid', $DB->get_records_sql($dropssql)); + + $answerssql = 'SELECT answer.* '.$from.', {question_answers} answer'.$where.' AND answer.question = q.id'; + $answers = index_array_of_records_by_key('question', $DB->get_records_sql($answerssql)); + + $imgfiles = $DB->get_records_sql_menu('SELECT question, qimage FROM {question_imagetarget}'); + + $correctfeedback = ''; + $correctfeedbackformat = 1; + $incorrectfeedback = ''; + $incorrectfeedbackformat = 1; + $foundincorrectanswer = false; + foreach ($answers[$question->id] as $answer) { + $no = 1; + if ('*' !== $answer->answer) { + $drop = new stdClass(); + $drop->questionid = $question->id; + $drop->shape = 'rectangle'; + $drop->no = $no; + list($x1, $y1, $x2, $y2) = explode(',', $answer->answer); + $width = $x2 - $x1; + $height = $y2 - $y1; + $drop->coords = "{$x1},{$y1};{$width},{$height}"; + $drop->choice = 1; + $DB->insert_record('qtype_ddmarker_drops', $drop); + $no++; + $correctfeedback = $answer->feedback; + $correctfeedbackformat = $answer->feedbackformat; + } else { + $foundincorrectanswer = false; + $incorrectfeedback = $answer->feedback; + $incorrectfeedbackformat = $answer->feedbackformat; + } + } + if (count($answers[$question->id]) < 2) { + echo $OUTPUT->notification('There are less than 2 answers. '. + '(Normally we expect at least a correct and incorrect answer). '. + 'For question id '.$question->id.' "'.$question->name.'".', + 'notifyproblem'); + } + if (!$foundincorrectanswer) { + echo $OUTPUT->notification('No incorrect answer found for question id '.$question->id.' "'.$question->name.'".', + 'notifyproblem'); + } + $drag = new stdClass(); + $drag->questionid = $question->id; + $drag->no = 1; + $drag->label = "X"; + $drag->infinite = 0; + $DB->insert_record('qtype_ddmarker_drags', $drag); + + $ddmarker = new stdClass(); + $ddmarker->questionid = $question->id; + $ddmarker->shuffleanswers = 0; + $ddmarker->correctfeedback = $correctfeedback; + $ddmarker->correctfeedbackformat = $correctfeedbackformat; + $ddmarker->partiallycorrectfeedback = ''; + $ddmarker->partiallycorrectfeedbackformat = 1; + $ddmarker->incorrectfeedback = $incorrectfeedback; + $ddmarker->incorrectfeedbackformat = $incorrectfeedbackformat; + $ddmarker->shownumcorrect = 0; + $ddmarker->showmisplaced = 0; + $DB->insert_record('qtype_ddmarker', $ddmarker); + + $newrec = clone($question); + unset($newrec->contextid); + $newrec->qtype = 'ddmarker'; + $newrec->timemodified = time(); + $DB->update_record('question', $newrec); + + $fs = get_file_storage(); + //we need to look in the course legacy files area for file + $bgimagefile = $fs->get_file(course_context_id($question->contextid), + 'course', + 'legacy', + '0', + '/'.dirname($imgfiles[$question->id]).'/', + basename($imgfiles[$question->id])); + if ($bgimagefile === false) { + echo $OUTPUT->notification('File "'.$imgfiles[$question->id].'" not found in legacy course files area. '. + 'For question id '.$question->id.' "'.$question->name.'".', + 'notifyproblem'); + } else { + $newbgimagefile = new stdClass(); + $newbgimagefile->component = 'qtype_ddmarker'; + $newbgimagefile->filearea = 'bgimage'; + $newbgimagefile->filepath = '/'; + $newbgimagefile->itemid = $question->id; + $newbgimagefile->contextid = $question->contextid; + $fs->create_file_from_storedfile($newbgimagefile, $bgimagefile); + } + } + + list($qsql, $qparams) = $DB->get_in_or_equal(array_keys($questions)); + $DB->delete_records_select('question_answers', 'question '.$qsql, $qparams); + $dbman = $DB->get_manager(); + $dbman->drop_table(new xmldb_table('question_imagetarget')); + } +} From 86529cb3abd489f79bb88070ee0cd6738a87c155 Mon Sep 17 00:00:00 2001 From: Jamie Pratt Date: Wed, 4 Apr 2012 14:42:40 +0700 Subject: [PATCH 115/178] MDL-47494 ddmarker: NOBUG refactoring so that automatic and manual conversion code is shared --- question/type/ddmarker/db/install.php | 141 +++--------------- question/type/ddmarker/db/upgradelib.php | 1 - .../type/ddmarker/imagetargetconverter.php | 69 +-------- question/type/ddmarker/lib.php | 98 ++++++++++++ 4 files changed, 124 insertions(+), 185 deletions(-) diff --git a/question/type/ddmarker/db/install.php b/question/type/ddmarker/db/install.php index 59a39eb8cb2..dfa98f087af 100644 --- a/question/type/ddmarker/db/install.php +++ b/question/type/ddmarker/db/install.php @@ -15,7 +15,7 @@ // along with Moodle. If not, see . /** - * Matching question type upgrade code. + * ddmarker question type installation code. * * @package qtype * @subpackage match @@ -27,27 +27,8 @@ defined('MOODLE_INTERNAL') || die(); -function index_array_of_records_by_key($key, $recs) { - $out = array(); - foreach ($recs as $id => $rec) { - if (!isset($out[$rec->{$key}])) { - $out[$rec->{$key}] = array(); - } - $out[$rec->{$key}][$id] = $rec; - } - return $out; -} - -function course_context_id($catcontextid) { - $context = get_context_instance_by_id($catcontextid); - while ($context->contextlevel != CONTEXT_COURSE) { - $context = get_context_instance_by_id(get_parent_contextid($context)); - } - return $context->id; -} /** - * Upgrade code for the matching question type. - * @param int $oldversion the version we are upgrading from. + * Installation code for the ddmarker question type. It converts all existing imagetarget questions to ddmarker */ function xmldb_qtype_ddmarker_install() { global $DB, $OUTPUT; @@ -60,107 +41,33 @@ function xmldb_qtype_ddmarker_install() { $questions = $DB->get_records_sql($sql); if (!empty($questions)) { + require_once(dirname(__FILE__).'/../lib.php'); + $dragssql = 'SELECT drag.* '.$from.', {qtype_ddmarker_drags} drag'.$where.' AND drag.questionid = q.id'; + $drags = xmldb_qtype_ddmarker_index_array_of_records_by_key('questionid', $DB->get_records_sql($dragssql)); + + $dropssql = 'SELECT drop.* '.$from.', {qtype_ddmarker_drops} drop'.$where.' AND drop.questionid = q.id'; + $drops = xmldb_qtype_ddmarker_index_array_of_records_by_key('questionid', $DB->get_records_sql($dropssql)); + + $answerssql = 'SELECT answer.* '.$from.', {question_answers} answer'.$where.' AND answer.question = q.id'; + $answers = xmldb_qtype_ddmarker_index_array_of_records_by_key('question', $DB->get_records_sql($answerssql)); + + $imgfiles = $DB->get_records_sql_menu('SELECT question, qimage FROM {question_imagetarget}'); foreach ($questions as $question) { - $dragssql = 'SELECT drag.* '.$from.', {qtype_ddmarker_drags} drag'.$where.' AND drag.questionid = q.id'; - $drags = index_array_of_records_by_key('questionid', $DB->get_records_sql($dragssql)); - - $dropssql = 'SELECT drop.* '.$from.', {qtype_ddmarker_drops} drop'.$where.' AND drop.questionid = q.id'; - $drops = index_array_of_records_by_key('questionid', $DB->get_records_sql($dropssql)); - - $answerssql = 'SELECT answer.* '.$from.', {question_answers} answer'.$where.' AND answer.question = q.id'; - $answers = index_array_of_records_by_key('question', $DB->get_records_sql($answerssql)); - - $imgfiles = $DB->get_records_sql_menu('SELECT question, qimage FROM {question_imagetarget}'); - - $correctfeedback = ''; - $correctfeedbackformat = 1; - $incorrectfeedback = ''; - $incorrectfeedbackformat = 1; - $foundincorrectanswer = false; - foreach ($answers[$question->id] as $answer) { - $no = 1; - if ('*' !== $answer->answer) { - $drop = new stdClass(); - $drop->questionid = $question->id; - $drop->shape = 'rectangle'; - $drop->no = $no; - list($x1, $y1, $x2, $y2) = explode(',', $answer->answer); - $width = $x2 - $x1; - $height = $y2 - $y1; - $drop->coords = "{$x1},{$y1};{$width},{$height}"; - $drop->choice = 1; - $DB->insert_record('qtype_ddmarker_drops', $drop); - $no++; - $correctfeedback = $answer->feedback; - $correctfeedbackformat = $answer->feedbackformat; - } else { - $foundincorrectanswer = false; - $incorrectfeedback = $answer->feedback; - $incorrectfeedbackformat = $answer->feedbackformat; - } - } - if (count($answers[$question->id]) < 2) { - echo $OUTPUT->notification('There are less than 2 answers. '. - '(Normally we expect at least a correct and incorrect answer). '. - 'For question id '.$question->id.' "'.$question->name.'".', - 'notifyproblem'); - } - if (!$foundincorrectanswer) { - echo $OUTPUT->notification('No incorrect answer found for question id '.$question->id.' "'.$question->name.'".', - 'notifyproblem'); - } - $drag = new stdClass(); - $drag->questionid = $question->id; - $drag->no = 1; - $drag->label = "X"; - $drag->infinite = 0; - $DB->insert_record('qtype_ddmarker_drags', $drag); - - $ddmarker = new stdClass(); - $ddmarker->questionid = $question->id; - $ddmarker->shuffleanswers = 0; - $ddmarker->correctfeedback = $correctfeedback; - $ddmarker->correctfeedbackformat = $correctfeedbackformat; - $ddmarker->partiallycorrectfeedback = ''; - $ddmarker->partiallycorrectfeedbackformat = 1; - $ddmarker->incorrectfeedback = $incorrectfeedback; - $ddmarker->incorrectfeedbackformat = $incorrectfeedbackformat; - $ddmarker->shownumcorrect = 0; - $ddmarker->showmisplaced = 0; - $DB->insert_record('qtype_ddmarker', $ddmarker); - - $newrec = clone($question); - unset($newrec->contextid); - $newrec->qtype = 'ddmarker'; - $newrec->timemodified = time(); - $DB->update_record('question', $newrec); - - $fs = get_file_storage(); - //we need to look in the course legacy files area for file - $bgimagefile = $fs->get_file(course_context_id($question->contextid), - 'course', - 'legacy', - '0', - '/'.dirname($imgfiles[$question->id]).'/', - basename($imgfiles[$question->id])); - if ($bgimagefile === false) { - echo $OUTPUT->notification('File "'.$imgfiles[$question->id].'" not found in legacy course files area. '. - 'For question id '.$question->id.' "'.$question->name.'".', - 'notifyproblem'); - } else { - $newbgimagefile = new stdClass(); - $newbgimagefile->component = 'qtype_ddmarker'; - $newbgimagefile->filearea = 'bgimage'; - $newbgimagefile->filepath = '/'; - $newbgimagefile->itemid = $question->id; - $newbgimagefile->contextid = $question->contextid; - $fs->create_file_from_storedfile($newbgimagefile, $bgimagefile); - } + qtype_ddmarker_convert_image_target_question($question, $imgfiles[$question->id], $answers[$question->id]); } - list($qsql, $qparams) = $DB->get_in_or_equal(array_keys($questions)); $DB->delete_records_select('question_answers', 'question '.$qsql, $qparams); $dbman = $DB->get_manager(); $dbman->drop_table(new xmldb_table('question_imagetarget')); } } +function xmldb_qtype_ddmarker_index_array_of_records_by_key($key, $recs) { + $out = array(); + foreach ($recs as $id => $rec) { + if (!isset($out[$rec->{$key}])) { + $out[$rec->{$key}] = array(); + } + $out[$rec->{$key}][$id] = $rec; + } + return $out; +} diff --git a/question/type/ddmarker/db/upgradelib.php b/question/type/ddmarker/db/upgradelib.php index 2ab9118ddb7..7c854a4c5c9 100644 --- a/question/type/ddmarker/db/upgradelib.php +++ b/question/type/ddmarker/db/upgradelib.php @@ -27,7 +27,6 @@ defined('MOODLE_INTERNAL') || die(); - /** * Class for converting attempt data from imagetarget questions when converting * attempts to the new question engine. diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php index 6533917d38b..ebb8dece159 100644 --- a/question/type/ddmarker/imagetargetconverter.php +++ b/question/type/ddmarker/imagetargetconverter.php @@ -51,75 +51,9 @@ class qtype_ddmarker_question_converter_list_item extends qtype_ddmarker_questio public $imagetargetrecord = null; public $answers = array(); public function process($progresstrace = null, $depth = 0) { - $this->convert_question(); + qtype_ddmarker_convert_image_target_question($this->record, $this->imagetargetrecord->qimage, $this->answers); parent::process($progresstrace, $depth);//outputs progress message } - protected function convert_question() { - global $DB; - foreach ($this->answers as $answer) { - $no = 1; - if ('*' !== $answer->answer) { - $drop = new stdClass(); - $drop->questionid = $this->record->id; - $drop->shape = 'rectangle'; - $drop->no = $no; - list($x1, $y1, $x2, $y2) = explode(',', $answer->answer); - $width = $x2 - $x1; - $height = $y2 - $y1; - $drop->coords = "{$x1},{$y1};{$width},{$height}"; - $drop->choice = 1; - $DB->insert_record('qtype_ddmarker_drops', $drop); - $no++; - $correctfeedback = $answer->feedback; - $correctfeedbackformat = $answer->feedbackformat; - } else { - $incorrectfeedback = $answer->feedback; - $incorrectfeedbackformat = $answer->feedbackformat; - } - } - $drag = new stdClass(); - $drag->questionid = $this->record->id; - $drag->no = 1; - $drag->label = "X"; - $drag->infinite = 0; - $DB->insert_record('qtype_ddmarker_drags', $drag); - - $ddmarker = new stdClass(); - $ddmarker->questionid = $this->record->id; - $ddmarker->shuffleanswers = 0; - $ddmarker->correctfeedback = $correctfeedback; - $ddmarker->correctfeedbackformat = $correctfeedbackformat; - $ddmarker->partiallycorrectfeedback = ''; - $ddmarker->partiallycorrectfeedbackformat = 1; - $ddmarker->incorrectfeedback = $incorrectfeedback; - $ddmarker->incorrectfeedbackformat = $incorrectfeedbackformat; - $ddmarker->shownumcorrect = 0; - $ddmarker->showmisplaced = 0; - $DB->insert_record('qtype_ddmarker', $ddmarker); - - $newrec = clone($this->record); - unset($newrec->contextid); - $newrec->qtype = 'ddmarker'; - $newrec->timemodified = time(); - $DB->update_record('question', $newrec); - - $fs = get_file_storage(); - $bgimagefile = $fs->get_file($this->course_context_id(), - 'course', - 'legacy', - '0', - '/'.dirname($this->imagetargetrecord->qimage).'/', - basename($this->imagetargetrecord->qimage)); - $newbgimagefile = new stdClass(); - $newbgimagefile->component = 'qtype_ddmarker'; - $newbgimagefile->filearea = 'bgimage'; - $newbgimagefile->filepath = '/'; - $newbgimagefile->itemid = $this->record->id; - $fs->create_file_from_storedfile($newbgimagefile, $bgimagefile); - - $DB->delete_records('question_imagetarget', array('question' => $this->record->id)); - $DB->delete_records('question_answers', array('question' => $this->record->id)); - } } $categoryid = optional_param('categoryid', 0, PARAM_INT); @@ -197,6 +131,7 @@ if (!count($questions)) { echo $contextlist->render('listitemlist', true, $top); } } else if (confirm_sesskey()) { + require_once(dirname(__FILE__).'/lib.php'); $questionlist->prepare_for_processing($top); echo '