mirror of
https://github.com/moodle/moodle.git
synced 2025-04-20 07:56:06 +02:00
MDL-61380 Questions: Editing configuration of existing random questions
Editing "random" questions follows a different procedure than adding them. This commit takes care of editing a "random" question's configuration. This commit also introduces mod_quiz\form\randomquestion_form, so mod_quiz can edit random questions without having to use the form that is inside qtype_random.
This commit is contained in:
parent
4f964a1ca0
commit
6650c66b59
84
mod/quiz/classes/form/randomquestion_form.php
Normal file
84
mod/quiz/classes/form/randomquestion_form.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Defines the editing form for random questions.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_quiz\form;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->dirroot.'/lib/formslib.php');
|
||||
|
||||
/**
|
||||
* Class randomquestion_form
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class randomquestion_form extends \moodleform {
|
||||
|
||||
/**
|
||||
* Form definiton.
|
||||
*/
|
||||
public function definition() {
|
||||
$mform = $this->_form;
|
||||
|
||||
$contexts = $this->_customdata['contexts'];
|
||||
$usablecontexts = $contexts->having_cap('moodle/question:useall');
|
||||
|
||||
// Standard fields at the start of the form.
|
||||
$mform->addElement('header', 'generalheader', get_string("general", 'form'));
|
||||
|
||||
$mform->addElement('questioncategory', 'category', get_string('category', 'question'),
|
||||
array('contexts' => $usablecontexts, 'top' => true));
|
||||
|
||||
$mform->addElement('advcheckbox', 'includesubcategories', get_string('recurse', 'quiz'), null, null, array(0, 1));
|
||||
|
||||
$tops = question_get_top_categories_for_contexts(array_column($contexts->all(), 'id'));
|
||||
$mform->hideIf('includesubcategories', 'category', 'in', $tops);
|
||||
|
||||
$tags = \core_tag_tag::get_tags_by_area_in_contexts('core_question', 'question', $usablecontexts);
|
||||
$tagstrings = array();
|
||||
foreach ($tags as $tag) {
|
||||
$tagstrings["{$tag->id},{$tag->name}"] = $tag->name;
|
||||
}
|
||||
$options = array(
|
||||
'multiple' => true,
|
||||
'noselectionstring' => get_string('anytags', 'quiz'),
|
||||
);
|
||||
$mform->addElement('autocomplete', 'fromtags', get_string('randomquestiontags', 'mod_quiz'), $tagstrings, $options);
|
||||
$mform->addHelpButton('fromtags', 'randomquestiontags', 'mod_quiz');
|
||||
|
||||
$mform->addElement('hidden', 'slotid');
|
||||
$mform->setType('slotid', PARAM_INT);
|
||||
|
||||
$mform->addElement('hidden', 'returnurl');
|
||||
$mform->setType('returnurl', PARAM_LOCALURL);
|
||||
|
||||
$buttonarray = array();
|
||||
$buttonarray[] = $mform->createElement('submit', 'submitbutton', get_string('savechanges'));
|
||||
$buttonarray[] = $mform->createElement('cancel');
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
}
|
||||
}
|
@ -958,16 +958,16 @@ class edit_renderer extends \plugin_renderer_base {
|
||||
* and also to see that category in the question bank.
|
||||
*
|
||||
* @param structure $structure object containing the structure of the quiz.
|
||||
* @param int $slot which slot we are outputting.
|
||||
* @param int $slotnumber which slot we are outputting.
|
||||
* @param \moodle_url $pageurl the canonical URL of this page.
|
||||
* @return string HTML to output.
|
||||
*/
|
||||
public function random_question(structure $structure, $slot, $pageurl) {
|
||||
public function random_question(structure $structure, $slotnumber, $pageurl) {
|
||||
|
||||
$question = $structure->get_question_in_slot($slot);
|
||||
$editurl = new \moodle_url('/question/question.php', array(
|
||||
'returnurl' => $pageurl->out_as_local_url(),
|
||||
'cmid' => $structure->get_cmid(), 'id' => $question->id));
|
||||
$question = $structure->get_question_in_slot($slotnumber);
|
||||
$slot = $structure->get_slot_by_number($slotnumber);
|
||||
$editurl = new \moodle_url('/mod/quiz/editrandom.php',
|
||||
array('returnurl' => $pageurl->out_as_local_url(), 'slotid' => $slot->id));
|
||||
|
||||
$temp = clone($question);
|
||||
$temp->questiontext = '';
|
||||
|
@ -409,6 +409,23 @@ class structure {
|
||||
return $this->slots[$slotid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a slot by it's slot number. Throws an exception if it is missing.
|
||||
*
|
||||
* @param int $slotnumber The slot number
|
||||
* @return \stdClass
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public function get_slot_by_number($slotnumber) {
|
||||
foreach ($this->slots as $slot) {
|
||||
if ($slot->slot == $slotnumber) {
|
||||
return $slot;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \coding_exception('The \'slotnumber\' could not be found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether adding a section heading is possible
|
||||
* @param int $pagenumber the number of the page.
|
||||
|
152
mod/quiz/editrandom.php
Normal file
152
mod/quiz/editrandom.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Page for editing random questions.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../../config.php');
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
$slotid = required_param('slotid', PARAM_INT);
|
||||
$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
|
||||
|
||||
// Get the quiz slot.
|
||||
$slot = $DB->get_record('quiz_slots', array('id' => $slotid));
|
||||
if (!$slot || empty($slot->questioncategoryid)) {
|
||||
print_error('invalidrandomslot', 'mod_quiz');
|
||||
}
|
||||
|
||||
if (!$quiz = $DB->get_record('quiz', array('id' => $slot->quizid))) {
|
||||
print_error('invalidquizid', 'quiz');
|
||||
}
|
||||
|
||||
$cm = get_coursemodule_from_instance('quiz', $slot->quizid, $quiz->course);
|
||||
|
||||
require_login($cm->course, false, $cm);
|
||||
|
||||
if ($returnurl) {
|
||||
$returnurl = new moodle_url($returnurl);
|
||||
} else {
|
||||
$returnurl = new moodle_url('/mod/quiz/edit.php', array('cmid' => $cm->id));
|
||||
}
|
||||
|
||||
$url = new moodle_url('/mod/quiz/editrandom.php', array('slotid' => $slotid));
|
||||
$PAGE->set_url($url);
|
||||
$PAGE->set_pagelayout('admin');
|
||||
|
||||
if (!$question = $DB->get_record('question', array('id' => $slot->questionid))) {
|
||||
print_error('questiondoesnotexist', 'question', $returnurl);
|
||||
}
|
||||
|
||||
$qtypeobj = question_bank::get_qtype('random');
|
||||
|
||||
// Validate the question category.
|
||||
if (!$category = $DB->get_record('question_categories', array('id' => $question->category))) {
|
||||
print_error('categorydoesnotexist', 'question', $returnurl);
|
||||
}
|
||||
|
||||
// Check permissions.
|
||||
question_require_capability_on($question, 'edit');
|
||||
|
||||
$thiscontext = context_module::instance($cm->id);
|
||||
$contexts = new question_edit_contexts($thiscontext);
|
||||
|
||||
// Create the question editing form.
|
||||
$mform = new mod_quiz\form\randomquestion_form(new moodle_url('/mod/quiz/editrandom.php'),
|
||||
array('contexts' => $contexts));
|
||||
|
||||
// Send the question object and a few more parameters to the form.
|
||||
$toform = fullclone($question);
|
||||
$toform->category = "{$category->id},{$category->contextid}";
|
||||
$toform->includesubcategories = $slot->includingsubcategories;
|
||||
$toform->fromtags = array();
|
||||
if ($slot->tags) {
|
||||
$tags = quiz_extract_random_question_tags($slot->tags);
|
||||
foreach ($tags as $tag) {
|
||||
$toform->fromtags[] = "{$tag->id},{$tag->name}";
|
||||
}
|
||||
}
|
||||
$toform->returnurl = $returnurl;
|
||||
|
||||
if ($cm !== null) {
|
||||
$toform->cmid = $cm->id;
|
||||
$toform->courseid = $cm->course;
|
||||
} else {
|
||||
$toform->courseid = $COURSE->id;
|
||||
}
|
||||
|
||||
$toform->slotid = $slotid;
|
||||
|
||||
$mform->set_data($toform);
|
||||
|
||||
if ($mform->is_cancelled()) {
|
||||
redirect($returnurl);
|
||||
} else if ($fromform = $mform->get_data()) {
|
||||
|
||||
// If we are moving a question, check we have permission to move it from
|
||||
// whence it came. Where we are moving to is validated by the form.
|
||||
list($newcatid, $newcontextid) = explode(',', $fromform->category);
|
||||
if (!empty($question->id) && $newcatid != $question->category) {
|
||||
$contextid = $newcontextid;
|
||||
question_require_capability_on($question, 'move');
|
||||
} else {
|
||||
$contextid = $category->contextid;
|
||||
}
|
||||
|
||||
$question = $qtypeobj->save_question($question, $fromform);
|
||||
|
||||
// We need to save some data into the quiz_slots table.
|
||||
$slot->questioncategoryid = $fromform->category;
|
||||
$slot->includingsubcategories = $fromform->includesubcategories;
|
||||
|
||||
$tags = [];
|
||||
foreach ($fromform->fromtags as $tagstring) {
|
||||
list($tagid, $tagname) = explode(',', $tagstring);
|
||||
$tags[] = (object) [
|
||||
'id' => $tagid,
|
||||
'name' => $tagname
|
||||
];
|
||||
}
|
||||
$slot->tags = quiz_build_random_question_tag_json($tags);
|
||||
|
||||
$DB->update_record('quiz_slots', $slot);
|
||||
|
||||
// Purge this question from the cache.
|
||||
question_bank::notify_question_edited($question->id);
|
||||
|
||||
$returnurl->param('lastchanged', $question->id);
|
||||
redirect($returnurl);
|
||||
}
|
||||
|
||||
$streditingquestion = $qtypeobj->get_heading();
|
||||
$PAGE->set_title($streditingquestion);
|
||||
$PAGE->set_heading($COURSE->fullname);
|
||||
$PAGE->navbar->add($streditingquestion);
|
||||
|
||||
// Display a heading, question editing form and possibly some extra content needed for
|
||||
// for this question type.
|
||||
echo $OUTPUT->header();
|
||||
$heading = get_string('randomediting', 'mod_quiz');
|
||||
echo $OUTPUT->heading_with_help($heading, 'randomquestion', 'mod_quiz');
|
||||
|
||||
$mform->display();
|
||||
|
||||
echo $OUTPUT->footer();
|
@ -439,6 +439,7 @@ $string['invalidcategory'] = 'Category ID is invalid';
|
||||
$string['invalidoverrideid'] = 'Invalid override id';
|
||||
$string['invalidquestionid'] = 'Invalid question id';
|
||||
$string['invalidquizid'] = 'Invalid quiz ID';
|
||||
$string['invalidrandomslot'] = 'Invalid random question slot id.';
|
||||
$string['invalidsource'] = 'The source is not accepted as valid.';
|
||||
$string['invalidsourcetype'] = 'Invalid source type.';
|
||||
$string['invalidstateid'] = 'Invalid state id';
|
||||
@ -696,10 +697,13 @@ $string['quiztimer'] = 'Quiz Timer';
|
||||
$string['quizwillopen'] = 'This quiz will open {$a}';
|
||||
$string['random'] = 'Random question';
|
||||
$string['randomcreate'] = 'Create random questions';
|
||||
$string['randomediting'] = 'Editing a random question';
|
||||
$string['randomfromcategory'] = 'Random question from category:';
|
||||
$string['randomfromexistingcategory'] = 'Random question from an existing category';
|
||||
$string['randomnumber'] = 'Number of random questions';
|
||||
$string['randomnosubcat'] = 'Questions from this category only, not its subcategories.';
|
||||
$string['randomquestion'] = 'Random question';
|
||||
$string['randomquestion_help'] = 'A random question is a way of inserting a randomly-chosen question from a specified category or by a specified tag into an activity.';
|
||||
$string['randomquestiontags'] = 'Tags';
|
||||
$string['randomquestiontags_help'] = 'You can restrict the selection criteria further by specifying some question tags here.
|
||||
|
||||
|
@ -2436,3 +2436,60 @@ function quiz_is_overriden_calendar_event(\calendar_event $event) {
|
||||
|
||||
return $DB->record_exists('quiz_overrides', $overrideparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing a list of tag records, this function validates each pair and builds a json string
|
||||
* that can be stored in the quiz_slots.tags field.
|
||||
*
|
||||
* @param stdClass[] $tagrecords List of tag objects with id and name properties.
|
||||
* @return string
|
||||
*/
|
||||
function quiz_build_random_question_tag_json($tagrecords) {
|
||||
$tags = [];
|
||||
foreach ($tagrecords as $tagrecord) {
|
||||
if ($tag = core_tag_tag::get($tagrecord->id, 'id, name')) {
|
||||
$tags[] = [
|
||||
'id' => (int)$tagrecord->id,
|
||||
'name' => $tag->name
|
||||
];
|
||||
} else if ($tag = core_tag_tag::get_by_name(0, $tagrecord->name, 'id, name')) {
|
||||
$tags[] = [
|
||||
'id' => $tag->id,
|
||||
'name' => $tagrecord->name
|
||||
];
|
||||
} else {
|
||||
$tags[] = [
|
||||
'id' => null,
|
||||
'name' => $tagrecord->name
|
||||
];
|
||||
}
|
||||
}
|
||||
return json_encode($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Providing tags data in the JSON format, this function returns tag records containing the id and name properties.
|
||||
*
|
||||
* @param string $tagsjson The JSON string representing an array of tags in the [{"id":tagid,"name":"tagname"}] format.
|
||||
* E.g. [{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}]
|
||||
* Usually equal to the value of the tags field retrieved from the quiz_slots table.
|
||||
* @return array An array of tags containing the id and name properties, indexed by tag ids.
|
||||
*/
|
||||
function quiz_extract_random_question_tags($tagsjson) {
|
||||
$tagrecords = [];
|
||||
if (!empty($tagsjson)) {
|
||||
$tags = json_decode($tagsjson);
|
||||
// Only work with tags that exist.
|
||||
foreach ($tags as $tagdata) {
|
||||
if (!array_key_exists($tagdata->id, $tagrecords)) {
|
||||
if ($tag = core_tag_tag::get($tagdata->id, 'id, name')) {
|
||||
$tagrecords[$tag->id] = $tag->to_object();
|
||||
} else if ($tag = core_tag_tag::get_by_name(0, $tagdata->name, 'id, name')) {
|
||||
$tagrecords[$tag->id] = $tag->to_object();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tagrecords;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user