MDL-47494 ddmarker: NOBUG extending functionality from ddimageortext question type.

This commit is contained in:
Jamie Pratt
2011-09-26 14:29:44 +07:00
parent 878ecb4804
commit dda954a26c
5 changed files with 22 additions and 1052 deletions
+3 -345
View File
@@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
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 = '<br><sub><sup><b><i><strong><em>';
$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 {
}
@@ -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';
+13 -141
View File
@@ -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() {
+3 -421
View File
@@ -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 .= " <shuffleanswers/>\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 .= " <drag>\n";
$output .= " <no>{$drag->no}</no>\n";
$output .= $format->writetext($drag->label, 3)."\n";
$output .= " <draggroup>{$drag->draggroup}</draggroup>\n";
if ($drag->infinite) {
$output .= " <infinite/>\n";
}
$output .= $this->write_files($files, 3);
$output .= " </drag>\n";
}
foreach ($question->options->drops as $drop) {
$output .= " <drop>\n";
$output .= $format->writetext($drop->label, 3);
$output .= " <no>{$drop->no}</no>\n";
$output .= " <choice>{$drop->choice}</choice>\n";
$output .= " <xleft>{$drop->xleft}</xleft>\n";
$output .= " <ytop>{$drop->ytop}</ytop>\n";
$output .= " </drop>\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 <file> 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 .= '<file name="' . $file->get_filename() . '" encoding="base64">';
$string .= base64_encode($file->get_content());
$string .= "</file>\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'));
}
}
+2 -142
View File
@@ -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 '';
}
}