diff --git a/question/type/ddmarker/README.md b/question/type/ddmarker/README.md
new file mode 100644
index 00000000000..29cddfa6509
--- /dev/null
+++ b/question/type/ddmarker/README.md
@@ -0,0 +1,131 @@
+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
+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/).
+
+This question type is compatible with Moodle 2.1+ (MOODLE_21_STABLE branch) or
+2.2+ (master branch).
+
+###Installation
+
+####Installation Using Git
+
+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:
+
+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.
+
+####Installation From Downloaded zip file
+
+Alternatively, download the zip from :
+
+* 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.
+
+###Converting 'image target' type questions to this type
+
+The imagetarget question type question type will not be upgraded to use with Moodle beyond version 1.9 it seems.
+
+But you can convert your existing image target questions and question attempt data to be drag and drop marker questions and they
+will work as the image target previously worked.
+
+There are two ways to convert your imagetarget questions to ddmarker question types.
+
+####Automatic Conversion (recommended)
+
+Conversion will happen automatically when you upgrade for Moodle 2.0.
+
+It is recommended you follow the following steps :
+
+Upgrade your site to Moodle 2.0. This involves :
+
+* Upgrade the code base to Moodle 2.0
+* Remove the imagetarget question type code from question/type/imagetarget/
+* Go to your http://{moodleroot}/admin/ to trigger the upgrade of the db
+
+At this point your imagetarget questions won't work and will show up as 'missing type' but once that upgrade is
+done change your Moodle code to Moodle 2.2+
+
+* install the ddmarker question type code in question type ddmarker.
+* then go to your http://{moodleroot}/admin/ to trigger the upgrade of the db
+
+This will convert all your imagetarget questions to ddmarker automatically.
+
+####Manual Conversion
+
+You can also use a manual script to convert imagetarget questions to ddmarker after upgrading to Moodle 2.1 or greater.
+
+* Log in as admin.
+* You will find a script in the admin menu under plugins/question types/ to convert your imagetarget questions to ddmarker.
+(This just converts your questions themselves.)
+* In order to convert your question attempt data to be used with the ddmarker question type and Moodle 2.1 or greater you need to
+find the 'question engine upgrade helper' which will appear at the root of the admin menu. Use this script to :
+* _Reset the upgrade of all attempt data._
+* _Run the attempt data upgrade again._
+
+###Issues with converting image target question type questions
+
+####Background image shrinkage
+
+The ddmarker question type will shrink the background image of your questions to be within a maximum size and width while at the
+time preserving aspect ratio of the image. The default max width is 600 pixels and height 400 pixels.
+
+The ddmarker question type does this whenever you edit a question and save it again.
+
+Unfortunately when you open up a question to edit it this means that if the image is to big it will be shrunk but at present the
+position of your drop zones are not moved to compensate for this shrinkage. There won't be a problem until you try to edit and save
+a question but when you open it in the editor the drop zones will not be in the correct position. If you save the question they
+they will then be saved in the wrong position.
+
+####Work around for background image shrinkage
+
+Either :
+
+* do not edit questions which have sizes above the allowed max.
+* or you can change the allowed maximum size of images there are two constant at the top of question/type/ddmarker/questiontype.php
+that define the size limits.
+
+The limit is just there to shrink outrageously large images down to a reasonable size automatically. You could set the value of the
+constants defining the max size to a very large value to effectively disable the image size constraints all together.
+
+####Lack of drag label in imagetarget questions
+
+Normally each draggable marker in the ddmarker question type has a text label. There are no labels for the imagetarget markers
+though, I needed to pick something that will work in any language as the label for the single imagetarget draggable marker
+so I picked 'X'.
+
+####Work around for lack of drag label in imagetarget questions
+
+Your teachers can edit this label after the question has been created. And they might also like to take advantage of the ability in
+the ddmarker question type to be able to specify the correct drop zones for more than one drag marker onto the same image,
+giving each drag marker a different label.
+
+You can change the default drag marker label which you can find defined in a constant at the top of question/type/ddmarker/lib.php
+
+####No automatic feedback telling the user whether they got the question right, partially right or wrong.
+
+In the imagetarget question you get a text message telling the user whether they got the question correct, partially correct or
+wrong. In many of the new question types in Moodle 2.1 onwards the teacher is expected to enter the message that the user will
+see in the question definition. This is called 'combined feedback' and by default it is blank. So questions that have been
+converted from per 2.1 Moodle, from imagetarget question types that used to give some feedback to the user will no longer do so.
+
+####Work around for lack of automatic feedback telling the user whether they got the question right, partially right or wrong.
+
+You can use this plug in https://github.com/jamiepratt/moodle-admin_tool_questionaddfeedback to bulk add feedback to questions.
diff --git a/question/type/ddmarker/README.txt b/question/type/ddmarker/README.txt
deleted file mode 100644
index f096c4f1606..00000000000
--- a/question/type/ddmarker/README.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-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
-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/).
-
-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:
-
-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/moodleou/moodle-qtype_ddmarker.git question/type/ddmarker
-
-Then add question/type/ddmarker to your git ignore.
-
-Alternatively, download the zip from
- 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/backup/moodle2/backup_qtype_ddmarker_plugin.class.php b/question/type/ddmarker/backup/moodle2/backup_qtype_ddmarker_plugin.class.php
index d304edb9886..fc40930bc67 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
@@ -15,9 +15,10 @@
// along with Moodle. If not, see .
/**
- * @package moodlecore
- * @subpackage backup-moodle2
- * @copyright 2011 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
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
index ea190807504..25e7233c479 100644
--- a/question/type/ddmarker/backup/moodle2/restore_qtype_ddmarker_plugin.class.php
+++ b/question/type/ddmarker/backup/moodle2/restore_qtype_ddmarker_plugin.class.php
@@ -15,9 +15,10 @@
// along with Moodle. If not, see .
/**
- * @package moodlecore
- * @subpackage backup-moodle2
- * @copyright 2011 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
diff --git a/question/type/ddmarker/db/install.php b/question/type/ddmarker/db/install.php
new file mode 100644
index 00000000000..7d46131f560
--- /dev/null
+++ b/question/type/ddmarker/db/install.php
@@ -0,0 +1,79 @@
+.
+
+/**
+ * ddmarker question type installation code.
+ *
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Installation code for the ddmarker question type. It converts all existing imagetarget questions to ddmarker
+ */
+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)) {
+ 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 drp.* '.$from.', {qtype_ddmarker_drops} drp'.$where.' AND drp.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}');
+ $progressbar = new progress_bar('qtype_ddmarker_convert_from_imagetarget');
+ $progressbar->create();
+ $done = 0;
+ foreach ($questions as $question) {
+ qtype_ddmarker_convert_image_target_question($question, $imgfiles[$question->id], $answers[$question->id]);
+ $done++;
+ $progressbar->update($done, count($questions), get_string('convertingimagetargetquestion', 'qtype_ddmarker', $question));
+ }
+ 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
new file mode 100644
index 00000000000..fd2e3810edd
--- /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 The Open University
+ * @author 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;
+ }
+ }
+}
diff --git a/question/type/ddmarker/edit_ddmarker_form.php b/question/type/ddmarker/edit_ddmarker_form.php
index 9fc2d8efe76..5969a3367d8 100644
--- a/question/type/ddmarker/edit_ddmarker_form.php
+++ b/question/type/ddmarker/edit_ddmarker_form.php
@@ -24,7 +24,8 @@ define('QTYPE_DDMARKER_ALLOWED_TAGS_IN_MARKER', '
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
diff --git a/question/type/ddmarker/imagetargetconverter.php b/question/type/ddmarker/imagetargetconverter.php
new file mode 100644
index 00000000000..1ab0f542c35
--- /dev/null
+++ b/question/type/ddmarker/imagetargetconverter.php
@@ -0,0 +1,158 @@
+.
+
+/**
+ * This page lets admin convert imagetarget questions to the ddmarker question type.
+ *
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
+ * @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');
+require_once(dirname(__FILE__).'/questionlists.php');
+require_once(dirname(__FILE__).'/lib.php');
+
+class qtype_ddmarker_question_converter_list extends qtype_ddmarker_question_list {
+ protected function new_list_item($stringidentifier, $link, $record) {
+ return new qtype_ddmarker_question_converter_list_item($stringidentifier, $link, $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($renderer) {
+ qtype_ddmarker_convert_image_target_question($this->record, $this->imagetargetrecord->qimage, $this->answers);
+ parent::process($renderer);//outputs progress message
+ }
+}
+
+$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();
+$context = get_context_instance(CONTEXT_SYSTEM);
+require_capability('moodle/question:config', $context);
+
+admin_externalpage_setup('qtypeddmarkerfromimagetarget');
+
+// Header.
+$renderer = $PAGE->get_renderer('qtype_ddmarker', 'list');
+echo $renderer->header();
+echo $renderer->heading(get_string('imagetargetconverter', 'qtype_ddmarker'), 2);
+
+
+$params = array();
+$from = 'FROM {question_categories} cat, {question} q';
+$where = ' WHERE q.qtype = \'imagetarget\' AND 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) {
+ //fetch all questions from this cats context
+ $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';
+
+$questions = $DB->get_records_sql($sql, $params);
+
+if (!count($questions)) {
+ echo html_writer::tag('div', get_string('noquestionsfound', 'qtype_ddmarker'));
+} else {
+ $contextids = array();
+ foreach ($questions as $question) {
+ $contextids[] = $question->contextid;
+ }
+
+ $questionsselected = (bool) ($categoryid || $qcontextid || $questionid);
+ if (!$confirm) {
+ if (!$questionsselected) {
+ $pagestate = 'listall';
+ } else {
+ $pagestate = 'confirm';
+ }
+ } else if (confirm_sesskey()) {
+ $pagestate = 'processing';
+ }
+ $link = ($pagestate == 'listall');
+ $contextlist = new qtype_ddmarker_context_list($pagestate, $link, array_unique($contextids));
+ $categorylist = new qtype_ddmarker_category_list($pagestate, $link, $contextids, $contextlist);
+ $questionlist = new qtype_ddmarker_question_converter_list($pagestate, $link, $questions, $categorylist);
+
+ foreach ($questions as $question) {
+ $questionlist->leaf_node($question->id, 1);
+ }
+ 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();
+ }
+ switch ($pagestate) {
+ case 'listall' :
+ echo $renderer->render_qtype_ddmarker_list($top);
+ break;
+ case 'confirm' :
+ echo $renderer->render_qtype_ddmarker_list($top);
+ $cofirmedurl = new moodle_url($PAGE->url, compact('categoryid', 'contextid', 'questionid') + array('confirm'=>1));
+ $cancelurl = new moodle_url($PAGE->url);
+ echo $renderer->confirm(get_string('confirmimagetargetconversion', 'qtype_ddmarker'), $cofirmedurl, $cancelurl);
+ break;
+ case 'processing' :
+ $questionlist->prepare_for_processing($top);
+ echo '';
+ $top->process($renderer);
+ echo '
';
+ break;
+ default :
+ break;
+ }
+}
+// Footer.
+echo $renderer->footer();
diff --git a/question/type/ddmarker/lang/en/qtype_ddmarker.php b/question/type/ddmarker/lang/en/qtype_ddmarker.php
index 9e156918d52..d0c0c3fb838 100644
--- a/question/type/ddmarker/lang/en/qtype_ddmarker.php
+++ b/question/type/ddmarker/lang/en/qtype_ddmarker.php
@@ -18,7 +18,8 @@
*
* @package qtype
* @subpackage ddmarker
- * @copyright 2011 The Open University
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
@@ -26,7 +27,9 @@ $string['addmoreitems'] = 'Blanks for {no} more markers';
$string['alttext'] = 'Alt text';
$string['answer'] = 'Answer';
$string['bgimage'] = 'Background image';
+$string['confirmimagetargetconversion'] = 'You are about to convert the above image target questions to the drag and drop markers question type.';
$string['coords'] = 'Coords';
+$string['convertingimagetargetquestion'] = 'Converted question "{$a->name}"';
$string['correctansweris'] = 'The correct answer is: {$a}';
$string['draggableimage'] = 'Draggable image';
$string['draggableitem'] = 'Draggable item';
@@ -48,11 +51,22 @@ $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['listitemconfirmcategory'] = 'About to convert all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)';
+$string['listitemconfirmcontext'] = 'About to convert all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)';
+$string['listitemconfirmquestion'] = 'About to convert question "{$a->name}"';
+$string['listitemlistallcategory'] = 'Select all imagetarget questions in category "{$a->name}" (contains {$a->qcount} imagetarget questions)';
+$string['listitemlistallcontext'] = 'Select all imagetarget questions in context "{$a->name}" (contains {$a->qcount} imagetarget questions)';
+$string['listitemlistallquestion'] = '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';
$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['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.';
diff --git a/question/type/ddmarker/lib.php b/question/type/ddmarker/lib.php
index 8cc24264add..7b93681ad79 100644
--- a/question/type/ddmarker/lib.php
+++ b/question/type/ddmarker/lib.php
@@ -17,16 +17,21 @@
/**
* Serve question type files
*
- * @since 2.0
* @package qtype
- * @subpackage essay
- * @copyright Dongsheng Cai
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+/**
+ *
+ * @var string label to use for drag items when converting image target questions to ddmarker question type
+ */
+define('QTYPE_DDMARKER_LABEL_FOR_MARKER_FOR_IMAGE_TARGET_QS', 'X');
/**
* Checks file access for essay questions.
@@ -36,3 +41,101 @@ function qtype_ddmarker_pluginfile($course, $cm, $context, $filearea, $args, $fo
require_once($CFG->libdir . '/questionlib.php');
question_pluginfile($course, $context, 'qtype_ddmarker', $filearea, $args, $forcedownload, $options);
}
+
+
+
+function qtype_ddmarker_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;
+}
+
+function qtype_ddmarker_convert_image_target_question($question, $imgfilename, $answers) {
+ global $DB, $OUTPUT;
+ $correctfeedback = '';
+ $correctfeedbackformat = 1;
+ $incorrectfeedback = '';
+ $incorrectfeedbackformat = 1;
+ $foundincorrectanswer = false;
+ foreach ($answers 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 = true;
+ $incorrectfeedback = $answer->feedback;
+ $incorrectfeedbackformat = $answer->feedbackformat;
+ }
+ }
+ if (count($answers) < 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 = QTYPE_DDMARKER_LABEL_FOR_MARKER_FOR_IMAGE_TARGET_QS;
+ $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(qtype_ddmarker_course_context_id($question->contextid),
+ 'course',
+ 'legacy',
+ '0',
+ '/'.dirname($imgfilename).'/',
+ basename($imgfilename));
+ if ($bgimagefile === false) {
+ echo $OUTPUT->notification('File "'.$imgfilename.'" 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);
+ }
+}
diff --git a/question/type/ddmarker/question.php b/question/type/ddmarker/question.php
index 22cb99e408d..568313d4b50 100644
--- a/question/type/ddmarker/question.php
+++ b/question/type/ddmarker/question.php
@@ -17,8 +17,10 @@
/**
* Drag-and-drop markers question definition class.
*
- * @package qtype_ddmarker
- * @copyright 2009 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author 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
new file mode 100644
index 00000000000..df17ff52a7c
--- /dev/null
+++ b/question/type/ddmarker/questionlists.php
@@ -0,0 +1,273 @@
+.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * These classes handle transforming arrays of records into a linked tree of contexts, categories and questions.
+ *
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 Jamie Pratt
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+abstract class qtype_ddmarker_list_item implements renderable {
+
+ /**
+ * @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 $stringidentifier;
+ protected $link;
+ protected $record;
+ protected $list;
+ protected $parentlist;
+ protected $listtype = null;
+
+ public function __construct($stringidentifier, $link, $record, $list, $parentlist = null) {
+ $this->stringidentifier = $stringidentifier;
+ $this->link = $link;
+ $this->record = $record;
+ $this->list = $list;
+ $this->parentlist = $parentlist;
+ }
+
+ 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 ();
+
+
+ public function item_name() {
+ return $this->record->name;
+ }
+
+ public function id_param_name() {
+ return $this->listtype.'id';
+ }
+ public function get_id() {
+ return $this->record->id;
+ }
+ public function get_q_count() {
+ return $this->qcount;
+ }
+ public function get_string_identifier() {
+ return $this->stringidentifier;
+ }
+ public function get_linked() {
+ return $this->link;
+ }
+ public function get_list_type() {
+ return $this->listtype;
+ }
+ public function get_children() {
+ return $this->children;
+ }
+ 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($renderer) {
+ echo '';
+ echo $renderer->item($this);
+ $this->process_children($renderer);
+ echo '';
+ flush();
+ }
+
+ protected function process_children($renderer) {
+ echo '';
+ foreach ($this->children as $child) {
+ $child->process($renderer);
+ }
+ echo '
';
+ }
+
+ 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;
+ }
+
+
+ public function __toString() {
+ 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 {
+ protected $listtype = 'category';
+
+ 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);
+ }
+ }
+}
+class qtype_ddmarker_question_list_item extends qtype_ddmarker_list_item {
+ protected $listtype = 'question';
+
+ public function parent_node() {
+ return $this->parentlist->get_instance($this->record->category);
+ }
+
+ public function question_ids() {
+ return array($this->record->id);
+ }
+}
+class qtype_ddmarker_context_list_item extends qtype_ddmarker_list_item {
+ protected $listtype = 'context';
+ 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 item_name() {
+ return print_context_name($this->record);
+ }
+
+ 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.
+ **/
+abstract class qtype_ddmarker_list {
+ protected $records = array();
+ protected $instances = array();
+ abstract protected function new_list_item($stringidentifier, $link, $record);
+ protected function make_list_item_instances_from_records($stringidentifier, $link) {
+ if (!empty($this->records)) {
+ foreach ($this->records as $id => $record) {
+ $this->instances[$id] = $this->new_list_item($stringidentifier, $link, $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($stringidentifier, $link, $record) {
+ return new qtype_ddmarker_context_list_item($stringidentifier, $link, $record, $this);
+ }
+
+ public function __construct($stringidentifier, $link, $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);
+ }
+ }
+ }
+ $this->make_list_item_instances_from_records($stringidentifier, $link);
+ }
+ public function render($roottorender = null) {
+ if ($roottorender === null) {
+ $roottorender = $this->root_node();
+ }
+ $rootitem = html_writer::tag('li', $roottorender->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 $contextlist;
+ protected function new_list_item($stringidentifier, $link, $record) {
+ return new qtype_ddmarker_category_list_item($stringidentifier, $link, $record, $this, $this->contextlist);
+ }
+ public function __construct($stringidentifier, $link, $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);
+ $this->make_list_item_instances_from_records($stringidentifier, $link);
+ }
+}
+
+class qtype_ddmarker_question_list extends qtype_ddmarker_list {
+ protected $categorylist;
+ protected function new_list_item($stringidentifier, $link, $record) {
+ return new qtype_ddmarker_question_list_item($stringidentifier, $link, $record, $this, $this->categorylist);
+ }
+ public function __construct($stringidentifier, $link, $questions, $categorylist) {
+ global $DB;
+ $this->categorylist = $categorylist;
+ $this->records = $questions;
+ $this->make_list_item_instances_from_records($stringidentifier, $link);
+ }
+ public function prepare_for_processing($top) {
+ }
+}
\ No newline at end of file
diff --git a/question/type/ddmarker/questiontype.php b/question/type/ddmarker/questiontype.php
index ee03154bc5b..24f5071df19 100644
--- a/question/type/ddmarker/questiontype.php
+++ b/question/type/ddmarker/questiontype.php
@@ -19,7 +19,8 @@
*
* @package qtype
* @subpackage ddmarker
- * @copyright 2009 The Open University
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @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 0c047631d22..48952905e49 100644
--- a/question/type/ddmarker/renderer.php
+++ b/question/type/ddmarker/renderer.php
@@ -17,14 +17,17 @@
/**
* Drag-and-drop markers question renderer class.
*
- * @package qtype_ddmarker
- * @copyright 2010 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot . '/question/type/rendererbase.php');
require_once($CFG->dirroot . '/question/type/ddimageortext/rendererbase.php');
@@ -156,4 +159,36 @@ class qtype_ddmarker_renderer extends qtype_ddtoimage_renderer_base {
$output .= parent::hint($qa, $hint);
return $output;
}
+
}
+class qtype_ddmarker_list_renderer extends plugin_renderer_base {
+
+ public function render_qtype_ddmarker_list(qtype_ddmarker_list_item $top) {
+ $list = html_writer::tag('ul', html_writer::tag('li', $this->render_qtype_ddmarker_list_item($top)));
+ return $this->output->container($list, 'listofquestions');
+ }
+ public function render_qtype_ddmarker_list_item(qtype_ddmarker_list_item $listitem) {
+ return $this->item($listitem).$this->children($listitem);
+ }
+
+ public function item(qtype_ddmarker_list_item $item) {
+ global $PAGE;
+ $a = new stdClass();
+ $a->qcount = $item->get_q_count();
+ $a->name = $item->item_name();
+ $thisitem = get_string('listitem'.$item->get_string_identifier().$item->get_list_type(), 'qtype_ddmarker', $a);
+ if ($item->get_linked()) {
+ $actionurl = new moodle_url($PAGE->url, array($item->id_param_name() => $item->get_id()));
+ $thisitem = html_writer::tag('a', $thisitem, array('href' => $actionurl));
+ }
+ return $thisitem;
+ }
+
+ protected function children(qtype_ddmarker_list_item $item) {
+ $children = array();
+ foreach ($item->get_children() as $child) {
+ $children[] = $this->render_qtype_ddmarker_list_item($child);
+ }
+ return html_writer::alist($children);
+ }
+}
\ No newline at end of file
diff --git a/question/type/ddmarker/settings.php b/question/type/ddmarker/settings.php
new file mode 100644
index 00000000000..2dd2cc81d6e
--- /dev/null
+++ b/question/type/ddmarker/settings.php
@@ -0,0 +1,32 @@
+.
+
+/**
+ *
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
+ * @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');
diff --git a/question/type/ddmarker/shapes.php b/question/type/ddmarker/shapes.php
index eae350ced22..e645fc76b55 100644
--- a/question/type/ddmarker/shapes.php
+++ b/question/type/ddmarker/shapes.php
@@ -17,8 +17,10 @@
/**
* Drag-and-drop markers classes for dealing with shapes on the server side.
*
- * @package qtype_ddmarker
- * @copyright 2009 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class qtype_ddmarker_shape {
diff --git a/question/type/ddmarker/simpletest/helper.php b/question/type/ddmarker/simpletest/helper.php
index 1c773da62d3..a78d3e08bcf 100644
--- a/question/type/ddmarker/simpletest/helper.php
+++ b/question/type/ddmarker/simpletest/helper.php
@@ -17,8 +17,10 @@
/**
* Test helpers for the drag-and-drop markers question type.
*
- * @package qtype_ddmarker
- * @copyright 2010 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
diff --git a/question/type/ddmarker/simpletest/testquestion.php b/question/type/ddmarker/simpletest/testquestion.php
index 705f95a49d0..7d1cafd86e1 100644
--- a/question/type/ddmarker/simpletest/testquestion.php
+++ b/question/type/ddmarker/simpletest/testquestion.php
@@ -17,8 +17,10 @@
/**
* Unit tests for the drag-and-drop markers question definition class.
*
- * @package qtype_ddmarker
- * @copyright 2010 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @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 63d0cdd1e7d..4c0b632a46f 100644
--- a/question/type/ddmarker/simpletest/testquestiontype.php
+++ b/question/type/ddmarker/simpletest/testquestiontype.php
@@ -17,8 +17,10 @@
/**
* Unit tests for the drag-and-drop markers question definition class.
*
- * @package qtype_ddmarker
- * @copyright 2010 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
diff --git a/question/type/ddmarker/simpletest/testshapes.php b/question/type/ddmarker/simpletest/testshapes.php
index 42f8a5bfa0b..17279fc1036 100644
--- a/question/type/ddmarker/simpletest/testshapes.php
+++ b/question/type/ddmarker/simpletest/testshapes.php
@@ -19,7 +19,8 @@
*
* @package qtype
* @subpackage ddmarker
- * @copyright 2010 The Open University
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @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 62411531aa9..3fc1a9187a5 100644
--- a/question/type/ddmarker/simpletest/testwalkthrough.php
+++ b/question/type/ddmarker/simpletest/testwalkthrough.php
@@ -17,8 +17,10 @@
/**
* Unit tests for the drag-and-drop markers question type.
*
- * @package qtype_ddmarker
- * @copyright 2010 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @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 f5270ffb2fc..398dac632d1 100644
--- a/question/type/ddmarker/version.php
+++ b/question/type/ddmarker/version.php
@@ -17,8 +17,10 @@
/**
* Version information for the drag-and-drop markers question type.
*
- * @package qtype_ddmarker
- * @copyright 2011 The Open University
+ * @package qtype
+ * @subpackage ddmarker
+ * @copyright 2012 The Open University
+ * @author Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/