Merge branch 'master_MDL-71696-versioning-integration' of https://github.com/catalyst/moodle-MDL-70329

This commit is contained in:
Sara Arjona 2022-02-03 13:25:27 +01:00
commit b841a811be
223 changed files with 7768 additions and 2899 deletions

View file

@ -0,0 +1,105 @@
<?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/>.
namespace mod_quiz\external;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir . '/externallib.php');
require_once($CFG->dirroot . '/question/engine/lib.php');
require_once($CFG->dirroot . '/question/engine/datalib.php');
require_once($CFG->libdir . '/questionlib.php');
use external_api;
use external_description;
use external_function_parameters;
use external_single_structure;
use external_value;
use stdClass;
/**
* External api for changing the question version in the quiz.
*
* @package mod_quiz
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class submit_question_version extends external_api {
/**
* Parameters for the submit_question_version.
*
* @return \external_function_parameters
*/
public static function execute_parameters(): external_function_parameters {
return new external_function_parameters (
[
'slotid' => new external_value(PARAM_INT, ''),
'newversion' => new external_value(PARAM_INT, '')
]
);
}
/**
* Set the questions slot parameters to display the question template.
*
* @param int $slotid Slot id to display.
* @param int $newversion
* @return array
*/
public static function execute(int $slotid, int $newversion): array {
global $DB;
$params = [
'slotid' => $slotid,
'newversion' => $newversion
];
$params = self::validate_parameters(self::execute_parameters(), $params);
$response = ['result' => false];
// Get the required data.
$referencedata = $DB->get_record('question_references', ['itemid' => $params['slotid']]);
$slotdata = $DB->get_record('quiz_slots', ['id' => $slotid]);
// Capability check.
list($course, $cm) = get_course_and_cm_from_instance($slotdata->quizid, 'quiz');
$context = \context_module::instance($cm->id);
self::validate_context($context);
require_capability('mod/quiz:manage', $context);
$reference = new stdClass();
$reference->id = $referencedata->id;
if ($params['newversion'] === 0) {
$reference->version = null;
} else {
$reference->version = $params['newversion'];
}
$response['result'] = $DB->update_record('question_references', $reference);
return $response;
}
/**
* Define the webservice response.
*
* @return external_description
*/
public static function execute_returns() {
return new external_single_structure(
[
'result' => new external_value(PARAM_BOOL, '')
]
);
}
}

View file

@ -14,23 +14,14 @@
// 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 \mod_quiz\local\structure\slot_random class.
*
* @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\local\structure;
defined('MOODLE_INTERNAL') || die();
/**
* Class slot_random, represents a random question slot type.
*
* @package mod_quiz
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class slot_random {
@ -38,6 +29,11 @@ class slot_random {
/** @var \stdClass Slot's properties. A record retrieved from the quiz_slots table. */
protected $record;
/**
* @var \stdClass set reference record
*/
protected $referencerecord;
/**
* @var \stdClass The quiz this question slot belongs to.
*/
@ -48,6 +44,11 @@ class slot_random {
*/
protected $tags = [];
/**
* @var string filter condition
*/
protected $filtercondition = null;
/**
* slot_random constructor.
*
@ -55,16 +56,22 @@ class slot_random {
*/
public function __construct($slotrecord = null) {
$this->record = new \stdClass();
$this->referencerecord = new \stdClass();
$properties = array(
'id', 'slot', 'quizid', 'page', 'requireprevious', 'questionid',
'questioncategoryid', 'includingsubcategories', 'maxmark');
$slotproperties = ['id', 'slot', 'quizid', 'page', 'requireprevious', 'maxmark'];
$setreferenceproperties = ['usingcontextid', 'questionscontextid'];
foreach ($properties as $property) {
foreach ($slotproperties as $property) {
if (isset($slotrecord->$property)) {
$this->record->$property = $slotrecord->$property;
}
}
foreach ($setreferenceproperties as $referenceproperty) {
if (isset($slotrecord->$referenceproperty)) {
$this->referencerecord->$referenceproperty = $slotrecord->$referenceproperty;
}
}
}
/**
@ -122,6 +129,19 @@ class slot_random {
$this->tags = \core_tag_tag::get_bulk($tagids, 'id, name');
}
/**
* Set filter condition.
*
* @param \stdClass $filters
*/
public function set_filter_condition($filters) {
if (!empty($this->tags)) {
$filters->tags = $this->tags;
}
$this->filtercondition = json_encode($filters);
}
/**
* Inserts the quiz slot at the $page page.
* It is required to call this function if you are building a quiz slot object from scratch.
@ -179,17 +199,11 @@ class slot_random {
$this->record->id = $DB->insert_record('quiz_slots', $this->record);
if (!empty($this->tags)) {
$recordstoinsert = [];
foreach ($this->tags as $tag) {
$recordstoinsert[] = (object)[
'slotid' => $this->record->id,
'tagid' => $tag->id,
'tagname' => $tag->name
];
}
$DB->insert_records('quiz_slot_tags', $recordstoinsert);
}
$this->referencerecord->component = 'mod_quiz';
$this->referencerecord->questionarea = 'slot';
$this->referencerecord->itemid = $this->record->id;
$this->referencerecord->filtercondition = $this->filtercondition;
$DB->insert_record('question_set_references', $this->referencerecord);
$trans->allow_commit();

View file

@ -25,8 +25,10 @@
namespace mod_quiz\output;
defined('MOODLE_INTERNAL') || die();
use mod_quiz\question\bank\qbank_helper;
use \mod_quiz\structure;
use \html_writer;
use \qbank_previewquestion\helper;
use renderable;
/**
@ -46,13 +48,13 @@ class edit_renderer extends \plugin_renderer_base {
*
* @param \quiz $quizobj object containing all the quiz settings information.
* @param structure $structure object containing the structure of the quiz.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param \moodle_url $pageurl the canonical URL of this page.
* @param array $pagevars the variables from {@link question_edit_setup()}.
* @return string HTML to output.
*/
public function edit_page(\quiz $quizobj, structure $structure,
\question_edit_contexts $contexts, \moodle_url $pageurl, array $pagevars) {
\core_question\local\bank\question_edit_contexts $contexts, \moodle_url $pageurl, array $pagevars) {
$output = '';
// Information at the top.
@ -493,7 +495,7 @@ class edit_renderer extends \plugin_renderer_base {
*
* @param structure $structure object containing the structure of the quiz.
* @param \stdClass $section information about the section.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param array $pagevars the variables from {@link \question_edit_setup()}.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML to output.
@ -513,7 +515,7 @@ class edit_renderer extends \plugin_renderer_base {
*
* @param structure $structure object containing the structure of the quiz.
* @param int $slot which slot we are outputting.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param array $pagevars the variables from {@link \question_edit_setup()}.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML to output.
@ -546,7 +548,7 @@ class edit_renderer extends \plugin_renderer_base {
*
* @param structure $structure object containing the structure of the quiz.
* @param int $slot the first slot on the page we are outputting.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param array $pagevars the variables from {@link \question_edit_setup()}.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML to output.
@ -580,12 +582,12 @@ class edit_renderer extends \plugin_renderer_base {
* @param structure $structure object containing the structure of the quiz.
* @param int $page the page number that this menu will add to.
* @param \moodle_url $pageurl the canonical URL of this page.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param array $pagevars the variables from {@link \question_edit_setup()}.
* @return string HTML to output.
*/
public function add_menu_actions(structure $structure, $page, \moodle_url $pageurl,
\question_edit_contexts $contexts, array $pagevars) {
\core_question\local\bank\question_edit_contexts $contexts, array $pagevars) {
$actions = $this->edit_menu_actions($structure, $page, $pageurl, $pagevars);
if (empty($actions)) {
@ -727,7 +729,11 @@ class edit_renderer extends \plugin_renderer_base {
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML to output.
*/
public function question(structure $structure, $slot, \moodle_url $pageurl) {
public function question(structure $structure, int $slot, \moodle_url $pageurl) {
global $DB;
// Get the data required by the question_slot template.
$slotid = $structure->get_slot_id_for_slot($slot);
$output = '';
$output .= html_writer::start_tag('div');
@ -735,50 +741,92 @@ class edit_renderer extends \plugin_renderer_base {
$output .= $this->question_move_icon($structure, $slot);
}
$output .= html_writer::start_div('mod-indent-outer');
$checkbox = new \core\output\checkbox_toggleall($this->togglegroup, false, [
'id' => 'selectquestion-' . $structure->get_displayed_number_for_slot($slot),
'name' => 'selectquestion[]',
'value' => $structure->get_displayed_number_for_slot($slot),
'classes' => 'select-multiple-checkbox',
]);
$output .= $this->render($checkbox);
$output .= $this->question_number($structure->get_displayed_number_for_slot($slot));
$data = [
'slotid' => $slotid,
'canbeedited' => $structure->can_be_edited(),
'checkbox' => $this->get_checkbox_render($structure, $slot),
'questionnumber' => $this->question_number($structure->get_displayed_number_for_slot($slot)),
'questionname' => $this->get_question_name_for_slot($structure, $slot, $pageurl),
'questionicons' => $this->get_action_icon($structure, $slot, $pageurl),
'questiondependencyicon' => ($structure->can_be_edited() ? $this->question_dependency_icon($structure, $slot) : ''),
'versionselection' => false
];
// This div is used to indent the content.
$output .= html_writer::div('', 'mod-indent');
$data['versionoptions'] = [];
if ($structure->get_slot_by_number($slot)->qtype !== 'random') {
$data['versionselection'] = true;
$data['versionoption'] = qbank_helper::get_question_version_info($structure->get_question_in_slot($slot)->id, $slotid);
$this->page->requires->js_call_amd('mod_quiz/question_slot', 'init', [$slotid]);
}
// Render the question slot template.
$output .= $this->render_from_template('mod_quiz/question_slot', $data);
$output .= html_writer::end_tag('div');
return $output;
}
/**
* Get the checkbox render.
*
* @param structure $structure object containing the structure of the quiz.
* @param int $slot the slot on the page we are outputting.
* @return string HTML to output.
*/
public function get_checkbox_render(structure $structure, int $slot) : string {
$checkbox = new \core\output\checkbox_toggleall($this->togglegroup, false,
[
'id' => 'selectquestion-' . $structure->get_displayed_number_for_slot($slot),
'name' => 'selectquestion[]',
'value' => $structure->get_displayed_number_for_slot($slot),
'classes' => 'select-multiple-checkbox',
]);
return $this->render($checkbox);
}
/**
* Get the question name for the slot.
*
* @param structure $structure object containing the structure of the quiz.
* @param int $slot the slot on the page we are outputting.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML to output.
*/
public function get_question_name_for_slot(structure $structure, int $slot, \moodle_url $pageurl) : string {
// Display the link to the question (or do nothing if question has no url).
if ($structure->get_question_type_for_slot($slot) == 'random') {
if ($structure->get_question_type_for_slot($slot) === 'random') {
$questionname = $this->random_question($structure, $slot, $pageurl);
} else {
$questionname = $this->question_name($structure, $slot, $pageurl);
}
// Start the div for the activity title, excluding the edit icons.
$output .= html_writer::start_div('activityinstance');
$output .= $questionname;
// Closing the tag which contains everything but edit icons. Content part of the module should not be part of this.
$output .= html_writer::end_tag('div'); // .activityinstance.
return $questionname;
}
/**
* Get the action icons render.
*
* @param structure $structure object containing the structure of the quiz.
* @param int $slot the slot on the page we are outputting.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML to output.
*/
public function get_action_icon(structure $structure, int $slot, \moodle_url $pageurl) : string {
// Action icons.
$qtype = $structure->get_question_type_for_slot($slot);
$questionicons = '';
$questionicons .= $this->question_preview_icon($structure->get_quiz(), $structure->get_question_in_slot($slot));
if ($qtype !== 'random') {
$questionicons .= $this->question_preview_icon($structure->get_quiz(), $structure->get_question_in_slot($slot),
null, null, $qtype);
}
if ($structure->can_be_edited()) {
$questionicons .= $this->question_remove_icon($structure, $slot, $pageurl);
}
$questionicons .= $this->marked_out_of_field($structure, $slot);
$output .= html_writer::span($questionicons, 'actions'); // Required to add js spinner icon.
if ($structure->can_be_edited()) {
$output .= $this->question_dependency_icon($structure, $slot);
}
// End of indentation div.
$output .= html_writer::end_tag('div');
$output .= html_writer::end_tag('div');
return $output;
return $questionicons;
}
/**
@ -814,9 +862,10 @@ class edit_renderer extends \plugin_renderer_base {
* @param \stdClass $question data from the question and quiz_slots tables.
* @param bool $label if true, show the preview question label after the icon
* @param int $variant which question variant to preview (optional).
* @param string $qtype the type of question
* @return string HTML to output.
*/
public function question_preview_icon($quiz, $question, $label = null, $variant = null) {
public function question_preview_icon($quiz, $question, $label = null, $variant = null, $qtype = null) {
$url = quiz_question_preview_url($quiz, $question, $variant);
// Do we want a label?
@ -975,10 +1024,8 @@ class edit_renderer extends \plugin_renderer_base {
* @return string HTML to output.
*/
public function random_question(structure $structure, $slotnumber, $pageurl) {
$question = $structure->get_question_in_slot($slotnumber);
$slot = $structure->get_slot_by_number($slotnumber);
$slottags = $structure->get_slot_tags_for_slot_id($slot->id);
$editurl = new \moodle_url('/mod/quiz/editrandom.php',
array('returnurl' => $pageurl->out_as_local_url(), 'slotid' => $slot->id));
@ -986,6 +1033,9 @@ class edit_renderer extends \plugin_renderer_base {
$temp->questiontext = '';
$instancename = quiz_question_tostring($temp);
$setreference = qbank_helper::get_random_question_data_from_slot($slot->id);
$filtercondition = json_decode($setreference->filtercondition);
$configuretitle = get_string('configurerandomquestion', 'quiz');
$qtype = \question_bank::get_qtype($question->qtype, false);
$namestr = $qtype->local_name();
@ -994,20 +1044,25 @@ class edit_renderer extends \plugin_renderer_base {
$editicon = $this->pix_icon('t/edit', $configuretitle, 'moodle', array('title' => ''));
$qbankurlparams = array(
'cmid' => $structure->get_cmid(),
'cat' => $question->category . ',' . $question->contextid,
'recurse' => !empty($question->questiontext)
'cmid' => $structure->get_cmid(),
'cat' => $filtercondition->questioncategoryid . ',' . $setreference->questionscontextid,
'recurse' => !empty($setreference->questionscontextid)
);
$slottags = [];
if (isset($filtercondition->tags)) {
$slottags = $filtercondition->tags;
}
foreach ($slottags as $index => $slottag) {
$qbankurlparams["qtagids[{$index}]"] = $slottag->tagid;
$slottag = explode(',', $slottag);
$qbankurlparams["qtagids[{$index}]"] = $slottag[0];
}
// If this is a random question, display a link to show the questions
// selected from in the question bank.
$qbankurl = new \moodle_url('/question/edit.php', $qbankurlparams);
$qbanklink = ' ' . \html_writer::link($qbankurl,
get_string('seequestions', 'quiz'), array('class' => 'mod_quiz_random_qbank_link'));
get_string('seequestions', 'quiz'), array('class' => 'mod_quiz_random_qbank_link'));
return html_writer::link($editurl, $icon . $editicon, array('title' => $configuretitle)) .
' ' . $instancename . ' ' . $qbanklink;
@ -1083,13 +1138,13 @@ class edit_renderer extends \plugin_renderer_base {
* is handled with the specific code for those.)
*
* @param structure $structure object containing the structure of the quiz.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param array $pagevars the variables from {@link \question_edit_setup()}.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return bool Always returns true
*/
protected function initialise_editing_javascript(structure $structure,
\question_edit_contexts $contexts, array $pagevars, \moodle_url $pageurl) {
\core_question\local\bank\question_edit_contexts $contexts, array $pagevars, \moodle_url $pageurl) {
$config = new \stdClass();
$config->resourceurl = '/mod/quiz/edit_rest.php';
@ -1189,13 +1244,13 @@ class edit_renderer extends \plugin_renderer_base {
* HTML for a page, with ids stripped, so it can be used as a javascript template.
*
* @param structure $structure object containing the structure of the quiz.
* @param \question_edit_contexts $contexts the relevant question bank contexts.
* @param \core_question\local\bank\question_edit_contexts $contexts the relevant question bank contexts.
* @param array $pagevars the variables from {@link \question_edit_setup()}.
* @param \moodle_url $pageurl the canonical URL of this page.
* @return string HTML for a new page.
*/
protected function new_page_template(structure $structure,
\question_edit_contexts $contexts, array $pagevars, \moodle_url $pageurl) {
\core_question\local\bank\question_edit_contexts $contexts, array $pagevars, \moodle_url $pageurl) {
if (!$structure->has_questions()) {
return '';
}

View file

@ -25,6 +25,9 @@
namespace mod_quiz\question\bank;
use core_question\local\bank\question_version_status;
use mod_quiz\question\bank\filter\custom_category_condition;
/**
* Subclass to customise the view of the question bank for the quiz editing screen.
*
@ -44,8 +47,8 @@ class custom_view extends \core_question\local\bank\view {
const MAX_TEXT_LENGTH = 200;
/**
* Constructor for custom_view.
* @param \question_edit_contexts $contexts
* Constructor.
* @param \core_question\local\bank\question_edit_contexts $contexts
* @param \moodle_url $pageurl
* @param \stdClass $course course settings
* @param \stdClass $cm activity settings.
@ -251,4 +254,81 @@ class custom_view extends \core_question\local\bank\view {
$this->sort[$sort] = $order;
}
}
protected function build_query(): void {
// Get the required tables and fields.
$joins = [];
$fields = ['qv.status', 'qc.id', 'qv.version', 'qv.id as versionid', 'qbe.id as questionbankentryid'];
if (!empty($this->requiredcolumns)) {
foreach ($this->requiredcolumns as $column) {
$extrajoins = $column->get_extra_joins();
foreach ($extrajoins as $prefix => $join) {
if (isset($joins[$prefix]) && $joins[$prefix] != $join) {
throw new \coding_exception('Join ' . $join . ' conflicts with previous join ' . $joins[$prefix]);
}
$joins[$prefix] = $join;
}
$fields = array_merge($fields, $column->get_required_fields());
}
}
$fields = array_unique($fields);
// Build the order by clause.
$sorts = [];
foreach ($this->sort as $sort => $order) {
list($colname, $subsort) = $this->parse_subsort($sort);
$sorts[] = $this->requiredcolumns[$colname]->sort_expression($order < 0, $subsort);
}
// Build the where clause.
$latestversion = 'qv.version = (SELECT MAX(v.version)
FROM {question_versions} v
JOIN {question_bank_entries} be
ON be.id = v.questionbankentryid
WHERE be.id = qbe.id)';
$readyonly = "qv.status = '" . question_version_status::QUESTION_STATUS_READY . "' ";
$tests = ['q.parent = 0', $latestversion, $readyonly];
$this->sqlparams = [];
foreach ($this->searchconditions as $searchcondition) {
if ($searchcondition->where()) {
$tests[] = '((' . $searchcondition->where() .'))';
}
if ($searchcondition->params()) {
$this->sqlparams = array_merge($this->sqlparams, $searchcondition->params());
}
}
// Build the SQL.
$sql = ' FROM {question} q ' . implode(' ', $joins);
$sql .= ' WHERE ' . implode(' AND ', $tests);
$this->countsql = 'SELECT count(1)' . $sql;
$this->loadsql = 'SELECT ' . implode(', ', $fields) . $sql . ' ORDER BY ' . implode(', ', $sorts);
}
public function wanted_filters($cat, $tagids, $showhidden, $recurse, $editcontexts, $showquestiontext): void {
global $CFG;
list(, $contextid) = explode(',', $cat);
$catcontext = \context::instance_by_id($contextid);
$thiscontext = $this->get_most_specific_context();
// Category selection form.
$this->display_question_bank_header();
// Display tag filter if usetags setting is enabled/enablefilters is true.
if ($this->enablefilters) {
if (is_array($this->customfilterobjects)) {
foreach ($this->customfilterobjects as $filterobjects) {
$this->searchconditions[] = $filterobjects;
}
} else {
if ($CFG->usetags) {
array_unshift($this->searchconditions,
new \core_question\bank\search\tag_condition([$catcontext, $thiscontext], $tagids));
}
array_unshift($this->searchconditions, new \core_question\bank\search\hidden_condition(!$showhidden));
array_unshift($this->searchconditions, new custom_category_condition(
$cat, $recurse, $editcontexts, $this->baseurl, $this->course));
}
}
$this->display_options_form($showquestiontext);
}
}

View file

@ -0,0 +1,45 @@
<?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/>.
namespace mod_quiz\question\bank\filter;
/**
* A custom filter condition for quiz to select question categories.
*
* This is required as quiz will only use ready questions and the count should show according to that.
*
* @package mod_quiz
* @category question
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_category_condition extends \core_question\bank\search\category_condition {
public function display_options() {
global $PAGE;
$displaydata = [];
$catmenu = custom_category_condition_helper::question_category_options($this->contexts, true, 0,
true, -1, false);
$displaydata['categoryselect'] = \html_writer::select($catmenu, 'category', $this->cat, [],
['class' => 'searchoptions custom-select', 'id' => 'id_selectacategory']);
$displaydata['categorydesc'] = '';
if ($this->category) {
$displaydata['categorydesc'] = $this->print_category_info($this->category);
}
return $PAGE->get_renderer('core_question', 'bank')->render_category_condition($displaydata);
}
}

View file

@ -0,0 +1,129 @@
<?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/>.
namespace mod_quiz\question\bank\filter;
use core_question\local\bank\question_version_status;
/**
* A custom filter condition helper for quiz to select question categories.
*
* This is required as quiz will only use ready questions and the count should show according to that.
*
* @package mod_quiz
* @category question
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class custom_category_condition_helper extends \qbank_managecategories\helper {
public static function question_category_options(array $contexts, bool $top = false, int $currentcat = 0,
bool $popupform = false, int $nochildrenof = -1,
bool $escapecontextnames = true): array {
global $CFG;
$pcontexts = [];
foreach ($contexts as $context) {
$pcontexts[] = $context->id;
}
$contextslist = join(', ', $pcontexts);
$categories = self::get_categories_for_contexts($contextslist, 'parent, sortorder, name ASC', $top);
if ($top) {
$categories = self::question_fix_top_names($categories);
}
$categories = self::question_add_context_in_key($categories);
$categories = self::add_indented_names($categories, $nochildrenof);
// Sort cats out into different contexts.
$categoriesarray = [];
foreach ($pcontexts as $contextid) {
$context = \context::instance_by_id($contextid);
$contextstring = $context->get_context_name(true, true, $escapecontextnames);
foreach ($categories as $category) {
if ($category->contextid == $contextid) {
$cid = $category->id;
if ($currentcat != $cid || $currentcat == 0) {
$a = new \stdClass;
$a->name = format_string($category->indentedname, true,
['context' => $context]);
if ($category->idnumber !== null && $category->idnumber !== '') {
$a->idnumber = s($category->idnumber);
}
if (!empty($category->questioncount)) {
$a->questioncount = $category->questioncount;
}
if (isset($a->idnumber) && isset($a->questioncount)) {
$formattedname = get_string('categorynamewithidnumberandcount', 'question', $a);
} else if (isset($a->idnumber)) {
$formattedname = get_string('categorynamewithidnumber', 'question', $a);
} else if (isset($a->questioncount)) {
$formattedname = get_string('categorynamewithcount', 'question', $a);
} else {
$formattedname = $a->name;
}
$categoriesarray[$contextstring][$cid] = $formattedname;
}
}
}
}
if ($popupform) {
$popupcats = [];
foreach ($categoriesarray as $contextstring => $optgroup) {
$group = [];
foreach ($optgroup as $key => $value) {
$key = str_replace($CFG->wwwroot, '', $key);
$group[$key] = $value;
}
$popupcats[] = [$contextstring => $group];
}
return $popupcats;
} else {
return $categoriesarray;
}
}
public static function get_categories_for_contexts($contexts, string $sortorder = 'parent, sortorder, name ASC',
bool $top = false, int $showallversions = 0): array {
global $DB;
$topwhere = $top ? '' : 'AND c.parent <> 0';
$statuscondition = "AND qv.status = '". question_version_status::QUESTION_STATUS_READY . "' ";
$sql = "SELECT c.*,
(SELECT COUNT(1)
FROM {question} q
JOIN {question_versions} qv ON qv.questionid = q.id
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
WHERE q.parent = '0'
$statuscondition
AND c.id = qbe.questioncategoryid
AND ($showallversions = 1
OR (qv.version = (SELECT MAX(v.version)
FROM {question_versions} v
JOIN {question_bank_entries} be ON be.id = v.questionbankentryid
WHERE be.id = qbe.id)
)
)
) AS questioncount
FROM {question_categories} c
WHERE c.contextid IN ($contexts) $topwhere
ORDER BY $sortorder";
return $DB->get_records_sql($sql);
}
}

View file

@ -0,0 +1,427 @@
<?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/>.
namespace mod_quiz\question\bank;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot . '/mod/quiz/accessmanager.php');
require_once($CFG->dirroot . '/mod/quiz/attemptlib.php');
/**
* Helper class for question bank and its associated data.
*
* @package mod_quiz
* @category question
* @copyright 2021 Catalyst IT Australia Pty Ltd
* @author Safat Shahin <safatshahin@catalyst-au.net>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbank_helper {
/**
* Check if the slot is a random question or not.
*
* @param int $slotid
* @return bool
*/
public static function is_random($slotid): bool {
global $DB;
$params = [
'itemid' => $slotid,
'component' => 'mod_quiz',
'questionarea' => 'slot'
];
return $DB->record_exists('question_set_references', $params);
}
/**
* Get the version options for the question.
*
* @param int $questionid
* @return array
*/
public static function get_version_options($questionid): array {
global $DB;
$sql = "SELECT qv.id AS versionid, qv.version
FROM {question_versions} qv
WHERE qv.questionbankentryid = (SELECT DISTINCT qbe.id
FROM {question_bank_entries} qbe
JOIN {question_versions} qv ON qbe.id = qv.questionbankentryid
JOIN {question} q ON qv.questionid = q.id
WHERE q.id = ?)
ORDER BY qv.version DESC";
return $DB->get_records_sql($sql, [$questionid]);
}
/**
* Sort the elements of an array according to a key.
*
* @param array $arrays
* @param string $on
* @param int $order
* @return array
*/
public static function question_array_sort($arrays, $on, $order = SORT_ASC): array {
$element = [];
foreach ($arrays as $array) {
$element[$array->$on] = $array;
}
ksort($element, $order);
return $element;
}
/**
* Get the question id from slot id.
*
* @param int $slotid
* @return mixed
*/
public static function get_question_for_redo($slotid) {
global $DB;
$params = [
'itemid' => $slotid,
'component' => 'mod_quiz',
'questionarea' => 'slot'
];
$referencerecord = $DB->get_record('question_references', $params);
if ($referencerecord->version === null) {
$questionsql = 'SELECT q.id
FROM {question} q
JOIN {question_versions} qv ON qv.questionid = q.id
WHERE qv.version = (SELECT MAX(v.version)
FROM {question_versions} v
JOIN {question_bank_entries} be
ON be.id = v.questionbankentryid
WHERE be.id = qv.questionbankentryid)
AND qv.questionbankentryid = ?';
$questionid = $DB->get_record_sql($questionsql, [$referencerecord->questionbankentryid])->id;
} else {
$questionid = $DB->get_field('question_versions', 'questionid',
['questionbankentryid' => $referencerecord->questionbankentryid,
'version' => $referencerecord->version]);
}
return $questionid;
}
/**
* Get random question object from the slot id.
*
* @param int $slotid
* @return false|mixed|\stdClass
*/
public static function get_random_question_data_from_slot($slotid) {
global $DB;
$params = [
'itemid' => $slotid,
'component' => 'mod_quiz',
'questionarea' => 'slot'
];
return $DB->get_record('question_set_references', $params);
}
/**
* Get the question ids for specific question version.
*
* @param int $quizid
* @return array
*/
public static function get_specific_version_question_ids($quizid) {
global $DB;
$questionids = [];
$sql = 'SELECT qv.questionid
FROM {quiz_slots} qs
JOIN {question_references} qr ON qr.itemid = qs.id
JOIN {question_versions} qv ON qv.questionbankentryid = qr.questionbankentryid
AND qv.version = qr.version
WHERE qr.version IS NOT NULL
AND qs.quizid = ?
AND qr.component = ?
AND qr.questionarea = ?';
$questions = $DB->get_records_sql($sql, [$quizid, 'mod_quiz', 'slot']);
foreach ($questions as $question) {
$questionids [] = $question->questionid;
}
return $questionids;
}
/**
* Get the question ids for always latest options.
*
* @param int $quizid
* @return array
*/
public static function get_always_latest_version_question_ids($quizid) {
global $DB;
$questionids = [];
$sql = 'SELECT qr.questionbankentryid as entry
FROM {quiz_slots} qs
JOIN {question_references} qr ON qr.itemid = qs.id
WHERE qr.version IS NULL
AND qs.quizid = ?
AND qr.component = ?
AND qr.questionarea = ?';
$entryids = $DB->get_records_sql($sql, [$quizid, 'mod_quiz', 'slot']);
$questionentries = [];
foreach ($entryids as $entryid) {
$questionentries [] = $entryid->entry;
}
if (empty($questionentries)) {
return $questionids;
}
list($questionidcondition, $params) = $DB->get_in_or_equal($questionentries);
$extracondition = 'AND qv.questionbankentryid ' . $questionidcondition;
$questionsql = "SELECT q.id
FROM {question} q
JOIN {question_versions} qv ON qv.questionid = q.id
WHERE qv.version = (SELECT MAX(v.version)
FROM {question_versions} v
JOIN {question_bank_entries} be
ON be.id = v.questionbankentryid
WHERE be.id = qv.questionbankentryid)
$extracondition";
$questions = $DB->get_records_sql($questionsql, $params);
foreach ($questions as $question) {
$questionids [] = $question->id;
}
return $questionids;
}
/**
* Get the question structure data for the given quiz or question ids.
*
* @param null $quizid
* @param array $questionids
* @param bool $attempt
* @return array
*/
public static function get_question_structure_data($quizid, $questionids = [], $attempt = false) {
global $DB;
$params = ['quizid' => $quizid];
$condition = '';
$joinon = 'AND qr.version = qv.version';
if (!empty($questionids)) {
list($condition, $param) = $DB->get_in_or_equal($questionids, SQL_PARAMS_NAMED, 'questionid');
$condition = 'AND q.id ' . $condition;
$joinon = '';
$params = array_merge($params, $param);
}
if ($attempt) {
$selectstart = 'q.*, slot.id AS slotid, slot.slot,';
} else {
$selectstart = 'slot.slot, slot.id AS slotid, q.*,';
}
$sql = "SELECT $selectstart
q.id AS questionid,
q.name,
q.qtype,
q.length,
slot.page,
slot.maxmark,
slot.requireprevious,
qc.id as category,
qc.contextid,qv.status,
qv.id as versionid,
qv.version,
qv.questionbankentryid
FROM {quiz_slots} slot
LEFT JOIN {question_references} qr ON qr.itemid = slot.id AND qr.component = 'mod_quiz' AND qr.questionarea = 'slot'
LEFT JOIN {question_bank_entries} qbe ON qbe.id = qr.questionbankentryid
LEFT JOIN {question_versions} qv ON qv.questionbankentryid = qbe.id $joinon
LEFT JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
LEFT JOIN {question} q ON q.id = qv.questionid
WHERE slot.quizid = :quizid
$condition";
$questiondatas = $DB->get_records_sql($sql, $params);
foreach ($questiondatas as $questiondata) {
$questiondata->_partiallyloaded = true;
}
if (!empty($questiondatas)) {
return $questiondatas;
}
return [];
}
/**
* Get question structure.
*
* @param int $quizid
* @return array
*/
public static function get_question_structure($quizid) {
$firstslotsets = self::get_question_structure_data($quizid);
$latestquestionids = self::get_always_latest_version_question_ids($quizid);
$secondslotsets = self::get_question_structure_data($quizid, $latestquestionids);
foreach ($firstslotsets as $key => $firstslotset) {
foreach ($secondslotsets as $secondslotset) {
if ($firstslotset->slotid === $secondslotset->slotid) {
unset($firstslotsets[$key]);
}
}
}
return self::question_array_sort(array_merge($firstslotsets, $secondslotsets), 'slot');
}
/**
* Load random questions.
*
* @param int $quizid
* @param array $questiondata
* @return array
*/
public static function question_load_random_questions($quizid, $questiondata) {
global $DB, $USER;
$sql = 'SELECT slot.id AS slotid,
slot.maxmark,
slot.slot,
slot.page,
qsr.filtercondition
FROM {question_set_references} qsr
JOIN {quiz_slots} slot ON slot.id = qsr.itemid
WHERE slot.quizid = ?
AND qsr.component = ?
AND qsr.questionarea = ?';
$randomquestiondatas = $DB->get_records_sql($sql, [$quizid, 'mod_quiz', 'slot']);
$randomquestions = [];
// Questions already added.
$usedquestionids = [];
foreach ($questiondata as $question) {
if (isset($usedquestions[$question->id])) {
$usedquestionids[$question->id] += 1;
} else {
$usedquestionids[$question->id] = 1;
}
}
// Usages for this user's previous quiz attempts.
$qubaids = new \mod_quiz\question\qubaids_for_users_attempts($quizid, $USER->id);
$randomloader = new \core_question\local\bank\random_question_loader($qubaids, $usedquestionids);
foreach ($randomquestiondatas as $randomquestiondata) {
$filtercondition = json_decode($randomquestiondata->filtercondition);
$tagids = [];
if (isset($filtercondition->tags)) {
foreach ($filtercondition->tags as $tag) {
$tagstring = explode(',', $tag);
$tagids [] = $tagstring[0];
}
}
$randomquestiondata->randomfromcategory = $filtercondition->questioncategoryid;
$randomquestiondata->randomincludingsubcategories = $filtercondition->includingsubcategories;
$randomquestiondata->questionid = $randomloader->get_next_question_id($randomquestiondata->randomfromcategory,
$randomquestiondata->randomincludingsubcategories, $tagids);
$randomquestions [] = $randomquestiondata;
}
foreach ($randomquestions as $randomquestion) {
// Should not add if there is no question found from the ramdom question loader, maybe empty category.
if ($randomquestion->questionid === null) {
continue;
}
$question = new \stdClass();
$question->slotid = $randomquestion->slotid;
$question->maxmark = $randomquestion->maxmark;
$question->slot = $randomquestion->slot;
$question->page = $randomquestion->page;
$qdatas = question_preload_questions($randomquestion->questionid);
$qdatas = reset($qdatas);
foreach ($qdatas as $key => $qdata) {
$question->$key = $qdata;
}
$questiondata[$question->id] = $question;
}
return $questiondata;
}
/**
* Choose question for redo.
*
* @param int $slotid
* @param \qubaid_condition $qubaids
* @return int
*/
public static function choose_question_for_redo($slotid, $qubaids): int {
// Choose the replacement question.
if (!self::is_random($slotid)) {
$newqusetionid = self::get_question_for_redo($slotid);
} else {
$tagids = [];
$randomquestiondata = self::get_random_question_data_from_slot($slotid);
$filtercondition = json_decode($randomquestiondata->filtercondition);
if (isset($filtercondition->tags)) {
foreach ($filtercondition->tags as $tag) {
$tagstring = explode(',', $tag);
$tagids [] = $tagstring[0];
}
}
$randomloader = new \core_question\local\bank\random_question_loader($qubaids, []);
$newqusetionid = $randomloader->get_next_question_id($filtercondition->questioncategoryid,
(bool) $filtercondition->includingsubcategories, $tagids);
if ($newqusetionid === null) {
throw new \moodle_exception('notenoughrandomquestions', 'quiz');
}
}
return $newqusetionid;
}
/**
* Get the version information for a question to show in the version selection dropdown.
*
* @param int $questionid
* @param int $slotid
* @return array
*/
public static function get_question_version_info($questionid, $slotid): array {
global $DB;
$versiondata = [];
$versionsoptions = self::get_version_options($questionid);
$latestversion = reset($versionsoptions);
// Object for using the latest version.
$alwaysuselatest = new \stdClass();
$alwaysuselatest->versionid = 0;
$alwaysuselatest->version = 0;
$alwaysuselatest->versionvalue = get_string('alwayslatest', 'quiz');
array_unshift($versionsoptions, $alwaysuselatest);
$referencedata = $DB->get_record('question_references', ['itemid' => $slotid]);
if (!isset($referencedata->version) || ($referencedata->version === null)) {
$currentversion = 0;
} else {
$currentversion = $referencedata->version;
}
foreach ($versionsoptions as $versionsoption) {
$versionsoption->selected = false;
if ($versionsoption->version === $currentversion) {
$versionsoption->selected = true;
}
if (!isset($versionsoption->versionvalue)) {
if ($versionsoption->version === $latestversion->version) {
$versionsoption->versionvalue = get_string('questionversionlatest', 'quiz', $versionsoption->version);
} else {
$versionsoption->versionvalue = get_string('questionversion', 'quiz', $versionsoption->version);
}
}
$versiondata[] = $versionsoption;
}
return $versiondata;
}
}

View file

@ -48,7 +48,7 @@ class question_name_text_column extends question_name_column {
$fields = parent::get_required_fields();
$fields[] = 'q.questiontext';
$fields[] = 'q.questiontextformat';
$fields[] = 'q.idnumber';
$fields[] = 'qbe.idnumber';
return $fields;
}

View file

@ -24,8 +24,10 @@
*/
namespace mod_quiz\question;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/mod/quiz/attemptlib.php');
/**
* A {@link qubaid_condition} representing all the attempts by one user at a given quiz.

View file

@ -23,6 +23,8 @@
*/
namespace mod_quiz;
use mod_quiz\question\bank\qbank_helper;
defined('MOODLE_INTERNAL') || die();
/**
@ -126,6 +128,15 @@ class structure {
return $this->questions[$this->slotsinorder[$slotnumber]->questionid];
}
/**
* Get the information about the question name in a given slot.
* @param int $slotnumber the index of the slot in question.
* @return \stdClass the data from the questions table, augmented with
*/
public function get_question_name_in_slot($slotnumber) {
return $this->questions[$this->slotsinorder[$slotnumber]->name];
}
/**
* Get the displayed question number (or 'i') for a given slot.
* @param int $slotnumber the index of the slot in question.
@ -606,30 +617,29 @@ class structure {
public function populate_structure($quiz) {
global $DB;
$slots = $DB->get_records_sql("
SELECT slot.id AS slotid, slot.slot, slot.questionid, slot.page, slot.maxmark,
slot.requireprevious, q.*, qc.contextid
FROM {quiz_slots} slot
LEFT JOIN {question} q ON q.id = slot.questionid
LEFT JOIN {question_categories} qc ON qc.id = q.category
WHERE slot.quizid = ?
ORDER BY slot.slot", array($quiz->id));
$slots = qbank_helper::get_question_structure($quiz->id);
$slots = $this->populate_missing_questions($slots);
$this->questions = array();
$this->slotsinorder = array();
$this->questions = [];
$this->slotsinorder = [];
foreach ($slots as $slotdata) {
$this->questions[$slotdata->questionid] = $slotdata;
$slot = new \stdClass();
$slot->id = $slotdata->slotid;
$slot->name = $slotdata->name;
$slot->slot = $slotdata->slot;
$slot->quizid = $quiz->id;
$slot->page = $slotdata->page;
$slot->questionid = $slotdata->questionid;
$slot->maxmark = $slotdata->maxmark;
$slot->requireprevious = $slotdata->requireprevious;
$slot->qtype = $slotdata->qtype;
$slot->length = $slotdata->length;
$slot->category = $slotdata->category;
$slot->questionbankentryid = $slotdata->questionbankentryid ?? null;
$slot->version = $slotdata->version ?? null;
$this->slotsinorder[$slot->slot] = $slot;
}
@ -646,21 +656,30 @@ class structure {
* @return \stdClass[] updated $slots array.
*/
protected function populate_missing_questions($slots) {
// Address missing question types.
global $DB;
// Address missing/random question types.
foreach ($slots as $slot) {
if ($slot->qtype === null) {
// If the questiontype is missing change the question type.
$slot->id = $slot->questionid;
$slot->category = 0;
$slot->qtype = 'missingtype';
$slot->name = get_string('missingquestion', 'quiz');
$slot->slot = $slot->slot;
$slot->maxmark = 0;
$slot->requireprevious = 0;
$slot->questiontext = ' ';
$slot->questiontextformat = FORMAT_HTML;
$slot->length = 1;
// Check if the question is random.
if ($setreference = $DB->get_record('question_set_references', ['itemid' => $slot->slotid])) {
$filtercondition = json_decode($setreference->filtercondition);
$slot->id = $slot->slotid;
$slot->category = $filtercondition->questioncategoryid;
$slot->qtype = 'random';
$slot->name = get_string('random', 'quiz');
$slot->length = 1;
} else {
// If the questiontype is missing change the question type.
$slot->id = $slot->questionid;
$slot->category = 0;
$slot->qtype = 'missingtype';
$slot->name = get_string('missingquestion', 'quiz');
$slot->maxmark = 0;
$slot->requireprevious = 0;
$slot->questiontext = ' ';
$slot->questiontextformat = FORMAT_HTML;
$slot->length = 1;
}
} else if (!\question_bank::qtype_exists($slot->qtype)) {
$slot->qtype = 'missingtype';
}
@ -936,7 +955,18 @@ class structure {
$maxslot = $DB->get_field_sql('SELECT MAX(slot) FROM {quiz_slots} WHERE quizid = ?', array($this->get_quizid()));
$trans = $DB->start_delegated_transaction();
$DB->delete_records('quiz_slot_tags', array('slotid' => $slot->id));
// Delete the reference if its a question.
$questionreference = $DB->get_record('question_references',
['component' => 'mod_quiz', 'questionarea' => 'slot', 'itemid' => $slot->id]);
if ($questionreference) {
$DB->delete_records('question_references', ['id' => $questionreference->id]);
}
// Delete the set reference if its a random question.
$questionsetreference = $DB->get_record('question_set_references',
['component' => 'mod_quiz', 'questionarea' => 'slot', 'itemid' => $slot->id]);
if ($questionsetreference) {
$DB->delete_records('question_set_references', ['id' => $questionsetreference->id]);
}
$DB->delete_records('quiz_slots', array('id' => $slot->id));
for ($i = $slot->slot + 1; $i <= $maxslot; $i++) {
$DB->set_field('quiz_slots', 'slot', $i - 1,
@ -946,12 +976,6 @@ class structure {
unset($this->slotsinorder[$i]);
}
$qtype = $DB->get_field('question', 'qtype', array('id' => $slot->questionid));
if ($qtype === 'random') {
// This function automatically checks if the question is in use, and won't delete if it is.
question_delete_question($slot->questionid);
}
quiz_update_section_firstslots($this->get_quizid(), -1, $slotnumber);
foreach ($this->sections as $key => $section) {
if ($section->firstslot > $slotnumber) {
@ -960,7 +984,7 @@ class structure {
}
$this->populate_slots_with_sections();
$this->populate_question_numbers();
unset($this->questions[$slot->questionid]);
$this->unset_question($slot->id);
$this->refresh_page_numbers_and_update_db();
@ -978,6 +1002,19 @@ class structure {
$event->trigger();
}
/**
* Unset the question object after deletion.
*
* @param int $slotid
*/
public function unset_question($slotid) {
foreach ($this->questions as $key => $question) {
if ($question->slotid === $slotid) {
unset($this->questions[$key]);
}
}
}
/**
* Change the max mark for a slot.
*
@ -1203,30 +1240,6 @@ class structure {
$event->trigger();
}
/**
* Set up this class with the slot tags for each of the slots.
*/
protected function populate_slot_tags() {
$slotids = array_column($this->slotsinorder, 'id');
$this->slottags = quiz_retrieve_tags_for_slot_ids($slotids);
}
/**
* Retrieve the list of slot tags for the given slot id.
*
* @param int $slotid The id for the slot
* @return \stdClass[] The list of slot tag records
*/
public function get_slot_tags_for_slot_id($slotid) {
if (!$this->hasloadedtags) {
// Lazy load the tags just in case they are never required.
$this->populate_slot_tags();
$this->hasloadedtags = true;
}
return isset($this->slottags[$slotid]) ? $this->slottags[$slotid] : [];
}
/**
* Whether the current user can add random questions to the quiz or not.
* It is only possible to add a random question if the user has the moodle/question:useall capability
@ -1237,7 +1250,7 @@ class structure {
public function can_add_random_questions() {
if ($this->canaddrandom === null) {
$quizcontext = $this->quizobj->get_context();
$relatedcontexts = new \question_edit_contexts($quizcontext);
$relatedcontexts = new \core_question\local\bank\question_edit_contexts($quizcontext);
$usablecontexts = $relatedcontexts->having_cap('moodle/question:useall');
$this->canaddrandom = !empty($usablecontexts);
@ -1245,4 +1258,21 @@ class structure {
return $this->canaddrandom;
}
/**
* Retrieve the list of slot tags for the given slot id.
*
* @param int $slotid The id for the slot
* @return \stdClass[] The list of slot tag records
* @deprecated since Moodle 4.0 MDL-71573
* @todo Final deprecation on Moodle 4.4 MDL-72438
*/
public function get_slot_tags_for_slot_id($slotid) {
debugging('Function get_slot_tags_for_slot_id() has been deprecated and the structure
for this method have been moved to filtercondition in question_set_reference table, please
use the new structure instead.', DEBUG_DEVELOPER);
// All the associated code for this method have been removed to get rid of accidental call or errors.
return [];
}
}