MDL-47494 ddmarker: NOBUG question editing form is now working correctly.

This commit is contained in:
Jamie Pratt
2011-09-28 19:31:48 +07:00
parent eb4b065ade
commit d2ac6cea2a
4 changed files with 428 additions and 24 deletions
+3 -2
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<XMLDB PATH="question/type/ddmarker/db" VERSION="20110924" COMMENT="XMLDB file for Moodle question/type/ddmarker"
<XMLDB PATH="question/type/ddmarker/db" VERSION="20110927" COMMENT="XMLDB file for Moodle question/type/ddmarker"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
>
@@ -43,7 +43,8 @@
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="questionid"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="no"/>
<FIELD NAME="no" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="drag no" PREVIOUS="questionid" NEXT="label"/>
<FIELD NAME="label" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" COMMENT="Alt text label for drag-able image." PREVIOUS="no"/>
<FIELD NAME="label" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" COMMENT="Alt text label for drag-able image." PREVIOUS="no" NEXT="infinite"/>
<FIELD NAME="infinite" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="label"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="questionid"/>
@@ -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;
}
}
@@ -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';
+191 -2
View File
@@ -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);
}
}