MDL-79863 qtype_ordering: qtype/ordering fix upgrade script to check for existence of reader_question_instances before attempting to acces sit

This commit is contained in:
Gordon Bateson 2014-12-03 22:02:45 +09:00 committed by Mathew May
parent eea5d67803
commit 61500b65b6
9 changed files with 155 additions and 205 deletions

View File

@ -30,7 +30,7 @@ function xmldb_qtype_ordering_upgrade($oldversion) {
$newversion = 2013062800;
if ($oldversion < $newversion) {
$select = 'qn.*, qo.id AS questionorderingid';
$from = '{question} qn LEFT JOIN {question_ordering} qo ON qn.id=qo.question';
$from = '{question} qn LEFT JOIN {question_ordering} qo ON qn.id = qo.question';
$where = 'qn.qtype = ? AND qo.id IS NULL';
$params = array('ordering');
if ($questions = $DB->get_records_sql("SELECT $select FROM $from WHERE $where", $params)) {
@ -49,8 +49,12 @@ function xmldb_qtype_ordering_upgrade($oldversion) {
} else {
// this is a faulty ordering question - remove it
$DB->delete_records('question', array('id' => $question->id));
$DB->delete_records('quiz_question_instances', array('question' => $question->id));
$DB->delete_records('reader_question_instances', array('question' => $question->id));
if ($dbman->table_exists('quiz_question_instances')) {
$DB->delete_records('quiz_question_instances', array('question' => $question->id));
}
if ($dbman->table_exists('reader_question_instances')) {
$DB->delete_records('reader_question_instances', array('question' => $question->id));
}
}
}
}

View File

@ -704,7 +704,6 @@ class question_type extends default_questiontype {
* be retrieved. Question type specific information is
* available.
*/
// ULPGC ecastro
function get_all_responses(&$question, &$state) {
if (isset($question->options->answers) && is_array($question->options->answers)) {
$answers = array();
@ -759,7 +758,6 @@ class question_type extends default_questiontype {
* for which a correct answer is needed. Question
* type specific information is included.
*/
// ULPGC ecastro
function get_actual_response($question, $state) {
if (!empty($state->responses)) {
$responses[] = $state->responses[''];
@ -796,7 +794,6 @@ class question_type extends default_questiontype {
return array($responsedetail);
}
// ULPGC ecastro
function get_fractional_grade(&$question, &$state) {
$grade = $state->grade;
if ($question->maxgrade > 0) {
@ -819,7 +816,6 @@ class question_type extends default_questiontype {
* for which a correct answer is needed. Question
* type specific information is included.
*/
// ULPGC ecastro
function check_response(&$question, &$state){
return false;
}
@ -1249,19 +1245,19 @@ class question_type extends default_questiontype {
function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
global $CFG, $DB;
static $addStyle = true;
static $addScript = true;
if (empty($question->options)) {
return; // shouldn't happen !!
if (empty($options->correctness)) {
$options->correctness = 0;
}
if (empty($question->options->answers)) {
// shortcut to answers
$answers = $question->options->answers;
if (empty($question->options) || empty($answers)) {
return; // shouldn't happen !!
}
if ($question->options->studentsee==0) { // all items
$question->options->studentsee = count($question->options->answers);
$question->options->studentsee = count($answers);
} else {
// a nasty hack so that "studentsee" is the same
// as what is displayed by edit_ordering_form.php
@ -1271,139 +1267,81 @@ class question_type extends default_questiontype {
switch ($question->options->logical) {
case 0: // all
$answerids = array_keys($question->options->answers);
$answerids = array_keys($answers);
break;
case 1: // random subset
$answerids = array_rand($question->options->answers, $question->options->studentsee);
$answerids = array_rand($answers, $question->options->studentsee);
break;
case 2: // contiguous subset
if (count($question->options->answers) > $question->options->studentsee) {
$offset = mt_rand(0, count($question->options->answers) - $question->options->studentsee);
$question->options->answers = array_slice($question->options->answers, $offset, $question->options->studentsee, true);
if (count($answers) > $question->options->studentsee) {
$offset = mt_rand(0, count($answers) - $question->options->studentsee);
$answers = array_slice($answers, $offset, $question->options->studentsee, true);
}
$answerids = array_keys($question->options->answers);
$answerids = array_keys($answers);
break;
}
shuffle($answerids);
$formatoptions = (object)array('noclean' => true, 'para' => true);
$questiontext = format_text($question->questiontext, $question->questiontextformat, $formatoptions, $cmoptions->course);
$response_name = 'q'.$question->id;
$response_id = 'id_q'.$question->id;
$sortable_id = 'id_sortable'.$question->id;
$output = '';
$output .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery.js'));
$output .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery-ui.js'));
$style = "\n";
$style .= "ul.sortable".$question->id." li {\n";
$style .= " position: relative;\n";
$style .= "}\n";
if ($addStyle) {
$addStyle = false; // only add style once
$style .= "ul.boxy {\n";
$style .= " border: 1px solid #ccc;\n";
$style .= " float: left;\n";
$style .= " font-family: Arial, sans-serif;\n";
$style .= " font-size: 13px;\n";
$style .= " list-style-type: none;\n";
$style .= " margin: 0px;\n";
$style .= " margin-left: 5px;\n";
$style .= " padding: 4px 4px 0 4px;\n";
$style .= " width: 360px;\n";
$style .= "}\n";
$style .= "ul.boxy li {\n";
$style .= " background-color: #eeeeee;\n";
$style .= " border: 1px solid #cccccc;\n";
$style .= " border-image: initial;\n";
$style .= " cursor: move;\n";
$style .= " list-style-type: none;\n";
$style .= " margin-bottom: 1px;\n";
$style .= " min-height: 20px;\n";
$style .= " padding: 8px 2px;\n";
$style .= "}\n";
if (count($answerids)) {
if ($options->readonly || $options->correctness) {
// don't allow items to be dragged and dropped
} else {
$script = "\n";
$script .= "//<![CDATA[\n";
$script .= "$(function() {\n";
$script .= " $('#$sortable_id').sortable({\n";
$script .= " update: function(event, ui) {\n";
$script .= " var ItemsOrder = $(this).sortable('toArray').toString();\n";
$script .= " $('#$response_id').attr('value', ItemsOrder);\n";
$script .= " }\n";
$script .= " });\n";
$script .= " $('#$sortable_id').disableSelection();\n";
$script .= "});\n";
$script .= "$(document).ready(function() {\n";
$script .= " var ItemsOrder = $('#$sortable_id').sortable('toArray').toString();\n";
$script .= " $('#$response_id').attr('value', ItemsOrder);\n";
$script .= "});\n";
$script .= "//]]>\n";
$output .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
}
$questiontext = $this->format_text($question->questiontext, $question->questiontextformat, $cmoptions);
$output .= html_writer::tag('div', $questiontext, array('class' => 'qtext'));
$output .= html_writer::start_tag('div', array('class' => 'ablock'));
$output .= html_writer::start_tag('div', array('class' => 'answer'));
$output .= html_writer::start_tag('ul', array('class' => 'sortablelist', 'id' => $sortable_id));
// generate ordering items
foreach ($answerids as $i => $answerid) {
// the original "id" revealed the correct order of the answers
// because $answer->fraction holds the correct order number
// $id = 'ordering_item_'.$answerid.'_'.intval($answers[$answerid]->fraction);
$id = 'ordering_item_'.md5($CFG->passwordsaltmain.$answers[$answerid]->answer);
$class = 'sortableitem';
$params = array('class' => $class, 'id' => $id);
$output .= html_writer::tag('li', $answers[$answerid]->answer, $params);
}
$output .= html_writer::end_tag('ul');
$output .= html_writer::end_tag('div'); // answer
$output .= html_writer::end_tag('div'); // ablock
$output .= html_writer::empty_tag('input', array('type' => 'hidden',
'name' => $response_name,
'id' => $response_id,
'value' => ''));
$output .= html_writer::tag('div', '', array('style' => 'clear:both;'));
}
$output .= html_writer::tag('style', $style, array('type' => 'text/css'));
$script = "\n";
$script .= "//<![CDATA[\n";
$script .= "$(function() {\n";
$script .= " $('#sortable".$question->id."').sortable({\n";
$script .= " update: function(event, ui) {\n";
$script .= " var ItemsOrder = $(this).sortable('toArray').toString();\n";
$script .= " $('#q".$question->id."').attr('value', ItemsOrder);\n";
$script .= " }\n";
$script .= " });\n";
$script .= " $('#sortable".$question->id."').disableSelection();\n";
$script .= "});\n";
$script .= "$(document).ready(function() {\n";
$script .= " var ItemsOrder = $('#sortable".$question->id."').sortable('toArray').toString();\n";
$script .= " $('#q".$question->id."').attr('value', ItemsOrder);\n";
$script .= "});\n";
$script .= "//]]>\n";
$output .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
$output .= html_writer::tag('div', stripslashes($questiontext), array('class' => 'qtext'));
$output .= html_writer::start_tag('div', array('class' => 'ablock'));
$output .= html_writer::start_tag('div', array('class' => 'answer'));
$output .= html_writer::start_tag('ul', array('class' => 'boxy', 'id' => 'sortable'.$question->id));
// generate ordering items
foreach ($answerids as $i => $answerid) {
// the original "id" revealed the correct order of the answers
// because $answer->fraction holds the correct order number
// $id = 'ordering_item_'.$answerid.'_'.intval($question->options->answers[$answerid]->fraction);
$id = 'ordering_item_'.md5($CFG->passwordsaltmain.$question->options->answers[$answerid]->answer);
$params = array('class' => 'ui-state-default', 'id' => $id);
$output .= html_writer::tag('li', $question->options->answers[$answerid]->answer, $params);
}
$output .= html_writer::end_tag('ul');
$output .= html_writer::end_tag('div'); // answer
$output .= html_writer::end_tag('div'); // ablock
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'q'.$question->id, 'id' => 'q'.$question->id, 'value' => '9'));
$output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'answer', 'value' => ''));
$output .= html_writer::tag('div', '', array('style' => 'clear:both;'));
$script = "\n";
$script .= "//<![CDATA[\n";
if ($addScript) {
$addScript = false; // only add these functions once
$script .= "function orderingTouchHandler(event) {\n";
$script .= " var touch = event.changedTouches[0];\n";
$script .= " switch (event.type) {\n";
$script .= " case 'touchstart': var type = 'mousedown'; break;\n";
$script .= " case 'touchmove': var type = 'mousemove'; event.preventDefault(); break;\n";
$script .= " case 'touchend': var type = 'mouseup'; break;\n";
$script .= " default: return;\n";
$script .= " }\n";
$script .= " var simulatedEvent = document.createEvent('MouseEvent');\n";
$script .= " // initMouseEvent(type, canBubble, cancelable, view, clickCount, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget)\n";
$script .= " simulatedEvent.initMouseEvent(type, true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);\n";
$script .= " touch.target.dispatchEvent(simulatedEvent);\n";
$script .= " event.preventDefault();\n";
$script .= "}\n";
$script .= "function orderingInit(sortableid) {\n";
$script .= " var obj = document.getElementById(sortableid);\n";
$script .= " if (obj) {\n";
$script .= " for (var i=0; i<obj.childNodes.length; i++) {\n";
$script .= " obj.childNodes.item(i).addEventListener('touchstart', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchmove', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchend', orderingTouchHandler, false);\n";
$script .= " obj.childNodes.item(i).addEventListener('touchcancel', orderingTouchHandler, false);\n";
$script .= " }\n";
$script .= " obj = null;\n";
$script .= " } else {\n";
$script .= " // try again in 1/2 a second - shouldn't be necessary !!\n";
$script .= " setTimeout(new Function('orderingInit(".'"'."'+sortableid+'".'"'.")'), 500);\n";
$script .= " }\n";
$script .= "}\n";
}
$script .= "orderingInit('sortable".$question->id."');\n";
$script .= "//]]>\n";
$output .= html_writer::tag('script', $script, array('type' => 'text/javascript'));
echo $output;
}

View File

@ -31,6 +31,6 @@ defined('MOODLE_INTERNAL') || die();
*/
function qtype_ordering_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG;
require_once($CFG->libdir . '/questionlib.php');
require_once($CFG->dirroot.'/lib/questionlib.php');
question_pluginfile($course, $context, 'qtype_ordering', $filearea, $args, $forcedownload);
}

View File

@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Ordering question definition classes.
* ORDERING question definition classes.
*
* @package qtype
* @subpackage ordering
@ -26,7 +26,7 @@
defined('MOODLE_INTERNAL') || die();
/**
* Represents an ordering question.
* Represents an ORDERING question.
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later

View File

@ -34,13 +34,40 @@ if (class_exists('question_type')) {
}
/**
* The ordering question type.
* The ORDERING question type.
*
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_ordering extends question_type {
/**
* Utility method used by {@link qtype_renderer::head_code()}
* It looks for any of the files script.js or script.php that
* exist in the plugin folder and ensures they get included.
* It also includes the jquery files required for this plugin
*/
public function find_standard_scripts() {
global $PAGE;
// include "script.js" and/or "script.php" in the normal way
parent::find_standard_scripts();
// include jquery files
if (method_exists($PAGE->requires, 'jquery')) {
// Moodle >= 2.5
$PAGE->requires->jquery();
$PAGE->requires->jquery_plugin('ui');
$PAGE->requires->jquery_plugin('ui.touch-punch', 'qtype_ordering');
} else {
// Moodle <= 2.4
$jquery = '/question/type/' . $this->name().'/jquery';
$PAGE->requires->js($jquery.'/jquery.js', true);
$PAGE->requires->js($jquery.'/jquery-ui.js', true);
$PAGE->requires->js($jquery.'/jquery-ui.touch-punch.js', true);
}
}
protected function initialise_question_instance(question_definition $question, $questiondata) {
parent::initialise_question_instance($question, $questiondata);
$answers = array_keys($questiondata->options->answers);

View File

@ -1,12 +1,12 @@
======================================
==========================================
The Ordering question type for Moodle 2.x
======================================
==========================================
The ordering question type displays several short sentences in a random order which are to be dragged into the correct sequential order. It was developed for the ordering questions used in the Reader activity module for Moodle 2.x
======================================
==========================================
To INSTALL or UPDATE this plugin
======================================
==========================================
1. get the files for this plugin from any one of the following locations:
@ -23,9 +23,9 @@ To INSTALL or UPDATE this plugin
if install/upgrade does not begin automatically, you can initiate it manually by navigating to the following link:
Settings -> Site administration -> Notifications
======================================
==========================================
Further information
======================================
==========================================
For more information, tutorials and online discussion forums, please visit:
http://moodlereader.org/

View File

@ -26,7 +26,7 @@
defined('MOODLE_INTERNAL') || die();
/**
* Generates the output for true-false questions.
* Generates the output for ORDERING questions
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@ -36,9 +36,6 @@ class qtype_ordering_renderer extends qtype_renderer {
public function formulation_and_controls(question_attempt $qa, question_display_options $options) {
global $CFG, $DB;
static $addStyle = true;
static $addScript = true;
$question = $qa->get_question();
$ordering = $question->get_ordering_options();
$answers = $question->get_ordering_answers();
@ -55,13 +52,6 @@ class qtype_ordering_renderer extends qtype_renderer {
$ordering->studentsee += 2;
}
if ($options->readonly || $options->correctness) {
// don't allow items to be dragged and dropped
$readonly = true;
} else {
$readonly = false;
}
if ($options->correctness) {
list($answerids, $correctorder) = $this->get_response($qa, $question, $answers);
} else {
@ -91,45 +81,10 @@ class qtype_ordering_renderer extends qtype_renderer {
$sortable_id = 'id_sortable_'.$question->id;
$result = '';
if ($readonly==false) {
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery.js'));
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery-ui.js'));
$result .= html_writer::tag('script', '', array('type'=>'text/javascript', 'src'=>$CFG->wwwroot.'/question/type/ordering/js/jquery.ui.touch-punch.js'));
}
$style = "\n";
$style .= "ul#$sortable_id li {\n";
$style .= " position: relative;\n";
$style .= "}\n";
if ($addStyle) {
$addStyle = false; // only add style once
$style .= "ul.boxy {\n";
$style .= " border: 1px solid #ccc;\n";
$style .= " float: left;\n";
$style .= " font-family: Arial, sans-serif;\n";
$style .= " font-size: 13px;\n";
$style .= " list-style-type: none;\n";
$style .= " margin: 0px;\n";
$style .= " margin-left: 5px;\n";
$style .= " padding: 4px 4px 0 4px;\n";
$style .= " width: 360px;\n";
$style .= "}\n";
$style .= "ul.boxy li {\n";
$style .= " background-color: #eeeeee;\n";
$style .= " border: 1px solid #cccccc;\n";
$style .= " border-image: initial;\n";
if ($readonly==false) {
$style .= " cursor: move;\n";
}
$style .= " list-style-type: none;\n";
$style .= " margin-bottom: 1px;\n";
$style .= " min-height: 20px;\n";
$style .= " padding: 8px 2px;\n";
$style .= "}\n";
}
$result .= html_writer::tag('style', $style, array('type' => 'text/css'));
if ($readonly==false) {
if ($options->readonly || $options->correctness) {
// don't allow items to be dragged and dropped
} else {
$script = "\n";
$script .= "//<![CDATA[\n";
$script .= "$(function() {\n";
@ -154,7 +109,7 @@ class qtype_ordering_renderer extends qtype_renderer {
if (count($answerids)) {
$result .= html_writer::start_tag('div', array('class' => 'ablock'));
$result .= html_writer::start_tag('div', array('class' => 'answer'));
$result .= html_writer::start_tag('ul', array('class' => 'boxy', 'id' => $sortable_id));
$result .= html_writer::start_tag('ul', array('class' => 'sortablelist', 'id' => $sortable_id));
// generate ordering items
foreach ($answerids as $position => $answerid) {
@ -169,7 +124,7 @@ class qtype_ordering_renderer extends qtype_renderer {
}
$img = "$img ";
} else {
$class = 'ui-state-default';
$class = 'sortableitem';
$img = '';
}
// the original "id" revealed the correct order of the answers
@ -185,8 +140,10 @@ class qtype_ordering_renderer extends qtype_renderer {
$result .= html_writer::end_tag('div'); // answer
$result .= html_writer::end_tag('div'); // ablock
$params = array('type' => 'hidden', 'name' => $response_name, 'id' => $response_id, 'value' => '');
$result .= html_writer::empty_tag('input', $params);
$result .= html_writer::empty_tag('input', array('type' => 'hidden',
'name' => $response_name,
'id' => $response_id,
'value' => ''));
$result .= html_writer::tag('div', '', array('style' => 'clear:both;'));
}

View File

@ -1,11 +1,35 @@
ul.boxy li.correctposition {
border: 4px solid #99ff66; /* gentle green */
ul.sortablelist {
border : 1px solid #ccc;
float : left;
font-family : Arial, sans-serif;
font-size : 13px;
list-style-type : none;
margin : 0px;
margin-left : 5px;
padding : 4px 4px 0 4px;
width : 360px;
}
ul.boxy li.wrongposition {
border: 4px solid #ff7373; /* gentle red */
ul.sortablelist li {
background-color : #eeeeee;
border : 1px solid #cccccc;
border-image : initial;
list-style-type : none;
margin-bottom : 1px;
min-height : 20px;
padding : 8px 2px;
}
ul.boxy li.correctposition,
ul.boxy li.wrongposition {
margin-bottom: 4px;
margin-top: 4px;
ul.sortablelist li.sortableitem {
position : relative;
cursor : move;
}
ul.sortablelist li.correctposition {
border : 4px solid #99ff66; /* gentle green */
}
ul.sortablelist li.wrongposition {
border : 4px solid #ff7373; /* gentle red */
}
ul.sortablelist li.correctposition,
ul.sortablelist li.wrongposition {
margin-bottom : 4px;
margin-top : 4px;
}

View File

@ -30,6 +30,6 @@ defined('MOODLE_INTERNAL') || die();
$plugin->cron = 0;
$plugin->component = 'qtype_ordering';
$plugin->maturity = MATURITY_STABLE; // ALPHA=50, BETA=100, RC=150, STABLE=200
$plugin->release = '2014-11-30 (03)';
$plugin->version = 2014113003;
$plugin->release = '2014-12-04 (04)';
$plugin->version = 2014120404;
$plugin->requires = 2010112400; // Moodle 2.0