mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 04:30:15 +01:00
Merge branch 'master_MDL-71516-qbank' of https://github.com/catalyst/moodle-MDL-70329
This commit is contained in:
commit
c0fc0747a8
63
admin/qbankplugins.php
Normal file
63
admin/qbankplugins.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Question bank plugin settings.
|
||||
*
|
||||
* @package core
|
||||
* @subpackage questionbank
|
||||
* @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
|
||||
*/
|
||||
|
||||
require_once('../config.php');
|
||||
require_once($CFG->libdir.'/adminlib.php');
|
||||
|
||||
$action = required_param('action', PARAM_ALPHANUMEXT);
|
||||
$name = required_param('name', PARAM_PLUGIN);
|
||||
|
||||
$syscontext = context_system::instance();
|
||||
$PAGE->set_url('/admin/qbankplugins.php');
|
||||
$PAGE->set_context($syscontext);
|
||||
|
||||
require_admin();
|
||||
|
||||
$return = new moodle_url('/admin/settings.php', ['section' => 'manageqbanks']);
|
||||
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('qbank');
|
||||
$sortorder = array_flip(array_keys($plugins));
|
||||
|
||||
if (!isset($plugins[$name])) {
|
||||
throw new moodle_exception('qbanknotfound', 'question', $return, $name);
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'disable':
|
||||
if ($plugins[$name]->is_enabled()) {
|
||||
set_config('disabled', 1, 'qbank_'. $name);
|
||||
}
|
||||
break;
|
||||
case 'enable':
|
||||
if (!$plugins[$name]->is_enabled()) {
|
||||
unset_config('disabled', 'qbank_'. $name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
core_plugin_manager::reset_caches();
|
||||
|
||||
redirect($return);
|
@ -403,6 +403,20 @@ if ($hassiteconfig) {
|
||||
}
|
||||
}
|
||||
|
||||
// Question bank settings.
|
||||
if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext)) {
|
||||
$ADMIN->add('modules', new admin_category('qbanksettings',
|
||||
new lang_string('type_qbank_plural', 'plugin')));
|
||||
$temp = new admin_settingpage('manageqbanks', new lang_string('manageqbanks', 'admin'));
|
||||
$temp->add(new \core_question\admin\manage_qbank_plugins_page());
|
||||
$ADMIN->add('qbanksettings', $temp);
|
||||
$plugins = core_plugin_manager::instance()->get_plugins_of_type('qbank');
|
||||
foreach ($plugins as $plugin) {
|
||||
/** @var \core\plugininfo\qbank $plugin */
|
||||
$plugin->load_settings($ADMIN, 'qbanksettings', $hassiteconfig);
|
||||
}
|
||||
}
|
||||
|
||||
// Question type settings
|
||||
if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext)) {
|
||||
|
||||
|
@ -1515,6 +1515,7 @@ $string['webproxyinfo'] = 'Fill in the following options if your Moodle server c
|
||||
$string['xmlrpcrecommended'] = 'The XMLRPC extension is useful for web services and Moodle networking.';
|
||||
$string['yuicomboloading'] = 'YUI combo loading';
|
||||
$string['ziprequired'] = 'The Zip PHP extension is now required by Moodle, info-ZIP binaries or PclZip library are not used anymore.';
|
||||
$string['manageqbanks'] = 'Manage question bank plugins';
|
||||
|
||||
|
||||
$string['caching'] = 'Caching';
|
||||
|
@ -192,6 +192,8 @@ $string['type_tool'] = 'Admin tool';
|
||||
$string['type_tool_plural'] = 'Admin tools';
|
||||
$string['type_webservice'] = 'Webservice protocol';
|
||||
$string['type_webservice_plural'] = 'Webservice protocols';
|
||||
$string['type_qbank'] = 'Question bank plugin';
|
||||
$string['type_qbank_plural'] = 'Question bank plugins';
|
||||
$string['updateavailable'] = 'There is a new version {$a} available!';
|
||||
$string['updateavailable_moreinfo'] = 'More info...';
|
||||
$string['updateavailable_release'] = 'Release {$a}';
|
||||
|
@ -494,3 +494,5 @@ $string['whichtries'] = 'Which tries';
|
||||
$string['withselected'] = 'With selected';
|
||||
$string['xoutofmax'] = '{$a->mark} out of {$a->max}';
|
||||
$string['yougotnright'] = 'You have correctly selected {$a->num}.';
|
||||
$string['qbanknotfound'] = 'The \'{$a}\' question bank plugin doesn\'t exist or is not recognised.';
|
||||
$string['noquestionbanks'] = 'No question bank plugin found.';
|
||||
|
@ -1938,6 +1938,10 @@ class core_plugin_manager {
|
||||
'checkbox', 'datetime', 'menu', 'social', 'text', 'textarea'
|
||||
),
|
||||
|
||||
'qbank' => [
|
||||
''
|
||||
],
|
||||
|
||||
'qbehaviour' => array(
|
||||
'adaptive', 'adaptivenopenalty', 'deferredcbm',
|
||||
'deferredfeedback', 'immediatecbm', 'immediatefeedback',
|
||||
@ -2241,6 +2245,7 @@ class core_plugin_manager {
|
||||
$fix['mod'] = $types['mod'];
|
||||
$fix['block'] = $types['block'];
|
||||
$fix['qtype'] = $types['qtype'];
|
||||
$fix['qbank'] = $types['qbank'];
|
||||
$fix['qbehaviour'] = $types['qbehaviour'];
|
||||
$fix['qformat'] = $types['qformat'];
|
||||
$fix['filter'] = $types['filter'];
|
||||
|
133
lib/classes/plugininfo/qbank.php
Normal file
133
lib/classes/plugininfo/qbank.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?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 classes used for plugin info.
|
||||
*
|
||||
* @package core
|
||||
* @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
|
||||
*/
|
||||
|
||||
namespace core\plugininfo;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Base class for qbank plugins.
|
||||
*
|
||||
* @package core
|
||||
* @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 extends base {
|
||||
|
||||
public function is_uninstall_allowed(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function get_manage_url(): \moodle_url {
|
||||
return new \moodle_url('/admin/settings.php', ['section' => 'manageqbanks']);
|
||||
}
|
||||
|
||||
public static function get_plugins($type, $typerootdir, $typeclass, $pluginman): array {
|
||||
global $CFG;
|
||||
|
||||
$qbank = parent::get_plugins($type, $typerootdir, $typeclass, $pluginman);
|
||||
$order = array_keys($qbank);
|
||||
$sortedqbanks = [];
|
||||
foreach ($order as $qbankname) {
|
||||
$sortedqbanks[$qbankname] = $qbank[$qbankname];
|
||||
}
|
||||
return $sortedqbanks;
|
||||
}
|
||||
|
||||
public static function get_enabled_plugins(): ?array {
|
||||
global $CFG;
|
||||
$pluginmanager = \core_plugin_manager::instance();
|
||||
$plugins = $pluginmanager->get_installed_plugins('qbank');
|
||||
|
||||
if (!$plugins) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$plugins = array_keys($plugins);
|
||||
|
||||
// Filter to return only enabled plugins.
|
||||
$enabled = [];
|
||||
foreach ($plugins as $plugin) {
|
||||
$qbankinfo = $pluginmanager->get_plugin_info('qbank_'.$plugin);
|
||||
$qbankavailable = $qbankinfo->get_status();
|
||||
if ($qbankavailable === \core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
continue;
|
||||
}
|
||||
$disabled = get_config('qbank_' . $plugin, 'disabled');
|
||||
if (empty($disabled)) {
|
||||
$enabled[$plugin] = $plugin;
|
||||
}
|
||||
}
|
||||
return $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a qbank plugin is ready to be used.
|
||||
* It checks the plugin status as well as the plugin is missing or not.
|
||||
*
|
||||
* @param string $fullpluginname the name of the plugin
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_plugin_enabled($fullpluginname): bool {
|
||||
$pluginmanager = \core_plugin_manager::instance();
|
||||
$qbankinfo = $pluginmanager->get_plugin_info($fullpluginname);
|
||||
if (empty($qbankinfo)) {
|
||||
return false;
|
||||
}
|
||||
$qbankavailable = $qbankinfo->get_status();
|
||||
if ($qbankavailable === \core_plugin_manager::PLUGIN_STATUS_MISSING ||
|
||||
!empty(get_config($fullpluginname, 'disabled'))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function load_settings(\part_of_admin_tree $adminroot, $parentnodename, $hassiteconfig): void {
|
||||
global $CFG, $USER, $DB, $OUTPUT, $PAGE; // In case settings.php wants to refer to them.
|
||||
$ADMIN = $adminroot; // May be used in settings.php.
|
||||
$plugininfo = $this; // Also can be used inside settings.php.
|
||||
|
||||
if (!$this->is_installed_and_upgraded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$hassiteconfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
$section = $this->get_settings_section_name();
|
||||
|
||||
$settings = null;
|
||||
if (file_exists($this->full_path('settings.php'))) {
|
||||
$settings = new \admin_settingpage($section, $this->displayname,
|
||||
'moodle/site:config', $this->is_enabled() === false);
|
||||
include($this->full_path('settings.php')); // This may also set $settings to null.
|
||||
}
|
||||
if ($settings) {
|
||||
$ADMIN->add($parentnodename, $settings);
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
"repository": "repository",
|
||||
"portfolio": "portfolio",
|
||||
"search": "search\/engine",
|
||||
"qbank": "question\/bank",
|
||||
"qbehaviour": "question\/behaviour",
|
||||
"qformat": "question\/format",
|
||||
"plagiarism": "plagiarism",
|
||||
|
@ -1756,19 +1756,23 @@ function question_edit_url($context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds question bank setting links to the given navigation node if caps are met.
|
||||
* Adds question bank setting links to the given navigation node if caps are met
|
||||
* and loads the navigation from the plugins.
|
||||
* Qbank plugins can extend the navigation_plugin_base and add their own navigation node,
|
||||
* this method will help to autoload those nodes in the question bank navigation.
|
||||
*
|
||||
* @param navigation_node $navigationnode The navigation node to add the question branch to
|
||||
* @param object $context
|
||||
* @param string $baseurl the url of the base where the api is implemented from
|
||||
* @return navigation_node Returns the question branch that was added
|
||||
*/
|
||||
function question_extend_settings_navigation(navigation_node $navigationnode, $context) {
|
||||
function question_extend_settings_navigation(navigation_node $navigationnode, $context, $baseurl = '/question/edit.php') {
|
||||
global $PAGE;
|
||||
|
||||
if ($context->contextlevel == CONTEXT_COURSE) {
|
||||
$params = array('courseid'=>$context->instanceid);
|
||||
$params = ['courseid' => $context->instanceid];
|
||||
} else if ($context->contextlevel == CONTEXT_MODULE) {
|
||||
$params = array('cmid'=>$context->instanceid);
|
||||
$params = ['cmid' => $context->instanceid];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@ -1778,24 +1782,84 @@ function question_extend_settings_navigation(navigation_node $navigationnode, $c
|
||||
}
|
||||
|
||||
$questionnode = $navigationnode->add(get_string('questionbank', 'question'),
|
||||
new moodle_url('/question/edit.php', $params), navigation_node::TYPE_CONTAINER, null, 'questionbank');
|
||||
new moodle_url($baseurl, $params), navigation_node::TYPE_CONTAINER, null, 'questionbank');
|
||||
|
||||
$corenavigations = [
|
||||
'questions' => [
|
||||
'title' => get_string('questions', 'question'),
|
||||
'url' => new moodle_url($baseurl)
|
||||
],
|
||||
'categories' => [
|
||||
'title' => get_string('categories', 'question'),
|
||||
'url' => new moodle_url('/question/category.php')
|
||||
],
|
||||
'import' => [
|
||||
'title' => get_string('import', 'question'),
|
||||
'url' => new moodle_url('/question/import.php')
|
||||
],
|
||||
'export' => [
|
||||
'title' => get_string('export', 'question'),
|
||||
'url' => new moodle_url('/question/export.php')
|
||||
]
|
||||
];
|
||||
|
||||
$plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
|
||||
foreach ($plugins as $componentname => $plugin) {
|
||||
$pluginentrypoint = new $plugin();
|
||||
$pluginentrypointobject = $pluginentrypoint->get_navigation_node();
|
||||
// Don't need the plugins without navigation node.
|
||||
if ($pluginentrypointobject === null) {
|
||||
unset($plugins[$componentname]);
|
||||
continue;
|
||||
}
|
||||
foreach ($corenavigations as $key => $corenavigation) {
|
||||
if ($pluginentrypointobject->get_navigation_key() === $key) {
|
||||
unset($plugins[$componentname]);
|
||||
if (!\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
|
||||
unset($corenavigations[$key]);
|
||||
break;
|
||||
}
|
||||
$corenavigations[$key] = [
|
||||
'title' => $pluginentrypointobject->get_navigation_title(),
|
||||
'url' => $pluginentrypointobject->get_navigation_url()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Community/additional plugins have navigation node.
|
||||
$pluginnavigations = [];
|
||||
foreach ($plugins as $componentname => $plugin) {
|
||||
$pluginentrypoin = new $plugin();
|
||||
$pluginentrypointobject = $pluginentrypoin->get_navigation_node();
|
||||
if (!\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
|
||||
unset($corenavigations[$key]);
|
||||
continue;
|
||||
}
|
||||
$pluginnavigations[$pluginentrypointobject->get_navigation_key()] = [
|
||||
'title' => $pluginentrypointobject->get_navigation_title(),
|
||||
'url' => $pluginentrypointobject->get_navigation_url(),
|
||||
'capabilities' => $pluginentrypointobject->get_navigation_capabilities()
|
||||
];
|
||||
}
|
||||
|
||||
$contexts = new question_edit_contexts($context);
|
||||
if ($contexts->have_one_edit_tab_cap('questions')) {
|
||||
$questionnode->add(get_string('questions', 'question'), new moodle_url(
|
||||
'/question/edit.php', $params), navigation_node::TYPE_SETTING, null, 'questions');
|
||||
foreach ($corenavigations as $key => $corenavigation) {
|
||||
if ($contexts->have_one_edit_tab_cap($key)) {
|
||||
$questionnode->add($corenavigation['title'], new moodle_url(
|
||||
$corenavigation['url'], $params), navigation_node::TYPE_SETTING, null, $key);
|
||||
}
|
||||
}
|
||||
if ($contexts->have_one_edit_tab_cap('categories')) {
|
||||
$questionnode->add(get_string('categories', 'question'), new moodle_url(
|
||||
'/question/category.php', $params), navigation_node::TYPE_SETTING, null, 'categories');
|
||||
}
|
||||
if ($contexts->have_one_edit_tab_cap('import')) {
|
||||
$questionnode->add(get_string('import', 'question'), new moodle_url(
|
||||
'/question/import.php', $params), navigation_node::TYPE_SETTING, null, 'import');
|
||||
}
|
||||
if ($contexts->have_one_edit_tab_cap('export')) {
|
||||
$questionnode->add(get_string('export', 'question'), new moodle_url(
|
||||
'/question/export.php', $params), navigation_node::TYPE_SETTING, null, 'export');
|
||||
|
||||
foreach ($pluginnavigations as $key => $pluginnavigation) {
|
||||
if (is_array($pluginnavigation['capabilities'])) {
|
||||
if (!$contexts->have_one_cap($pluginnavigation['capabilities'])) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$questionnode->add($pluginnavigation['title'], new moodle_url(
|
||||
$pluginnavigation['url'], $params), navigation_node::TYPE_SETTING, null, $key);
|
||||
}
|
||||
|
||||
return $questionnode;
|
||||
|
76
mod/quiz/classes/question/bank/question_name_column.php
Normal file
76
mod/quiz/classes/question/bank/question_name_column.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A column type for the name of the question name.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace mod_quiz\question\bank;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* A column type for the name of the question name.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class question_name_column extends \core_question\bank\column_base {
|
||||
protected $checkboxespresent = null;
|
||||
|
||||
public function get_name() {
|
||||
return 'questionname';
|
||||
}
|
||||
|
||||
protected function get_title() {
|
||||
return get_string('question');
|
||||
}
|
||||
|
||||
protected function label_for($question) {
|
||||
if (is_null($this->checkboxespresent)) {
|
||||
$this->checkboxespresent = $this->qbank->has_column('core_question\bank\checkbox_column');
|
||||
}
|
||||
if ($this->checkboxespresent) {
|
||||
return 'checkq' . $question->id;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected function display_content($question, $rowclasses) {
|
||||
$labelfor = $this->label_for($question);
|
||||
if ($labelfor) {
|
||||
echo '<label for="' . $labelfor . '">';
|
||||
}
|
||||
echo format_string($question->name);
|
||||
if ($labelfor) {
|
||||
echo '</label>';
|
||||
}
|
||||
}
|
||||
|
||||
public function get_required_fields() {
|
||||
return array('q.id', 'q.name');
|
||||
}
|
||||
|
||||
public function is_sortable() {
|
||||
return 'q.name';
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class question_name_text_column extends \core_question\bank\question_name_column {
|
||||
class question_name_text_column extends question_name_column {
|
||||
public function get_name() {
|
||||
return 'questionnametext';
|
||||
}
|
||||
|
2
question/bank/upgrade.txt
Normal file
2
question/bank/upgrade.txt
Normal file
@ -0,0 +1,2 @@
|
||||
This file describes core qbank plugin changes in /question/bank/*,
|
||||
information provided here is intended especially for developers.
|
@ -133,14 +133,15 @@ class question_category_list_item extends list_item {
|
||||
}
|
||||
|
||||
public function item_html($extraargs = array()){
|
||||
global $CFG, $OUTPUT;
|
||||
global $CFG, $PAGE, $OUTPUT;
|
||||
$str = $extraargs['str'];
|
||||
$category = $this->item;
|
||||
|
||||
$editqestions = get_string('editquestions', 'question');
|
||||
|
||||
// Each section adds html to be displayed as part of this list item.
|
||||
$questionbankurl = new moodle_url('/question/edit.php', $this->parentlist->pageurl->params());
|
||||
$nodeparent = $PAGE->settingsnav->find('questionbank', \navigation_node::TYPE_CONTAINER);
|
||||
$questionbankurl = new moodle_url($nodeparent->action->get_path(), $this->parentlist->pageurl->params());
|
||||
$questionbankurl->param('cat', $category->id . ',' . $category->contextid);
|
||||
$item = '';
|
||||
$text = format_string($category->name, true, ['context' => $this->parentlist->context]);
|
||||
|
140
question/classes/admin/manage_qbank_plugins_page.php
Normal file
140
question/classes/admin/manage_qbank_plugins_page.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Manage question banks page.
|
||||
*
|
||||
* @package core_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
|
||||
*/
|
||||
|
||||
namespace core_question\admin;
|
||||
|
||||
/**
|
||||
* Class manage_qbank_plugins_page.
|
||||
*
|
||||
* @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 manage_qbank_plugins_page extends \admin_setting {
|
||||
|
||||
/**
|
||||
* Class admin_page_manageqbanks constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->nosave = true;
|
||||
parent::__construct('manageqbanks',
|
||||
new \lang_string('manageqbanks', 'admin'), '', '');
|
||||
}
|
||||
|
||||
public function get_setting(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_defaultsetting(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function write_setting($data): string {
|
||||
// Do not write any setting.
|
||||
return '';
|
||||
}
|
||||
|
||||
public function is_related($query): bool {
|
||||
if (parent::is_related($query)) {
|
||||
return true;
|
||||
}
|
||||
$types = \core_plugin_manager::instance()->get_plugins_of_type('qbank');
|
||||
foreach ($types as $type) {
|
||||
if (strpos($type->component, $query) !== false ||
|
||||
strpos(\core_text::strtolower($type->displayname), $query) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function output_html($data, $query = ''): string {
|
||||
global $CFG, $OUTPUT;
|
||||
$return = '';
|
||||
|
||||
$pluginmanager = \core_plugin_manager::instance();
|
||||
$types = $pluginmanager->get_plugins_of_type('qbank');
|
||||
if (empty($types)) {
|
||||
return get_string('noquestionbanks', 'question');
|
||||
}
|
||||
$txt = get_strings(['settings', 'name', 'enable', 'disable', 'default']);
|
||||
$txt->uninstall = get_string('uninstallplugin', 'core_admin');
|
||||
|
||||
$table = new \html_table();
|
||||
$table->head = [$txt->name, $txt->enable, $txt->settings, $txt->uninstall];
|
||||
$table->align = ['left', 'center', 'center', 'center', 'center'];
|
||||
$table->attributes['class'] = 'manageqbanktable generaltable admintable';
|
||||
$table->data = [];
|
||||
|
||||
$totalenabled = 0;
|
||||
$count = 0;
|
||||
foreach ($types as $type) {
|
||||
if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
|
||||
$totalenabled++;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($types as $type) {
|
||||
$url = new \moodle_url('/admin/qbankplugins.php', ['sesskey' => sesskey(), 'name' => $type->name]);
|
||||
|
||||
$class = '';
|
||||
if ($pluginmanager->get_plugin_info('qbank_'.$type->name)->get_status() ===
|
||||
\core_plugin_manager::PLUGIN_STATUS_MISSING) {
|
||||
$strtypename = $type->displayname.' ('.get_string('missingfromdisk').')';
|
||||
} else {
|
||||
$strtypename = $type->displayname;
|
||||
}
|
||||
|
||||
if ($type->is_enabled()) {
|
||||
$hideshow = \html_writer::link($url->out(false, ['action' => 'disable']),
|
||||
$OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', ['class' => 'iconsmall']));
|
||||
} else {
|
||||
$class = 'dimmed_text';
|
||||
$hideshow = \html_writer::link($url->out(false, ['action' => 'enable']),
|
||||
$OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', ['class' => 'iconsmall']));
|
||||
}
|
||||
|
||||
$settings = '';
|
||||
if ($type->get_settings_url()) {
|
||||
$settings = \html_writer::link($type->get_settings_url(), $txt->settings);
|
||||
}
|
||||
|
||||
$uninstall = '';
|
||||
if ($uninstallurl = \core_plugin_manager::instance()->get_uninstall_url(
|
||||
'qbank_'.$type->name, 'manage')) {
|
||||
$uninstall = \html_writer::link($uninstallurl, $txt->uninstall);
|
||||
}
|
||||
|
||||
$row = new \html_table_row([$strtypename, $hideshow, $settings, $uninstall]);
|
||||
if ($class) {
|
||||
$row->attributes['class'] = $class;
|
||||
}
|
||||
$table->data[] = $row;
|
||||
$count++;
|
||||
}
|
||||
$return .= \html_writer::table($table);
|
||||
return highlight($query, $return);
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
abstract class action_column_base extends column_base {
|
||||
|
||||
|
@ -32,6 +32,7 @@ use core\output\checkbox_toggleall;
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
class checkbox_column extends column_base {
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Base class for representing a column in a {@link question_bank_view}.
|
||||
* Base class for representing a column in a {@see question_bank_view}.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
@ -27,12 +27,14 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Base class for representing a column in a {@link question_bank_view}.
|
||||
* Base class for representing a column in a {@see question_bank_view}.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
abstract class column_base {
|
||||
|
||||
/**
|
||||
* @var view $qbank the question bank view we are helping to render.
|
||||
*/
|
||||
@ -42,10 +44,10 @@ abstract class column_base {
|
||||
protected $isheading = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param view $qbank the question bank view we are helping to render.
|
||||
* Constructor for the question bank view we are helping to render.
|
||||
* @param view $qbank
|
||||
*/
|
||||
public function __construct(view $qbank) {
|
||||
public function __construct($qbank) {
|
||||
$this->qbank = $qbank;
|
||||
$this->init();
|
||||
}
|
||||
@ -68,6 +70,27 @@ abstract class column_base {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the row has an extra preference to view/hide.
|
||||
*/
|
||||
public function has_preference(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the preference key of the row.
|
||||
*/
|
||||
public function get_preference_key(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the preference of the row.
|
||||
*/
|
||||
public function get_preference(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the column header cell.
|
||||
*/
|
||||
@ -272,7 +295,7 @@ abstract class column_base {
|
||||
/**
|
||||
* Load the tags for each question.
|
||||
*
|
||||
* Helper that can be used from {@link load_additional_data()};
|
||||
* Helper that can be used from {@see load_additional_data()};
|
||||
*
|
||||
* @param array $questions
|
||||
*/
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2013 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class copy_action_column extends menu_action_column_base {
|
||||
/** @var string avoids repeated calls to get_string('duplicate'). */
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class creator_name_column extends column_base {
|
||||
public function get_name() {
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class delete_action_column extends menu_action_column_base {
|
||||
protected $strdelete;
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class edit_action_column extends menu_action_column_base {
|
||||
protected $stredit;
|
||||
|
@ -36,6 +36,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2019 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
class edit_menu_column extends column_base {
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2019 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class export_xml_action_column extends menu_action_column_base {
|
||||
/** @var string avoids repeated calls to get_string('duplicate'). */
|
||||
|
@ -36,6 +36,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2019 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
abstract class menu_action_column_base extends action_column_base implements menuable_action {
|
||||
|
||||
|
@ -43,6 +43,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2019 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
interface menuable_action {
|
||||
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class modifier_name_column extends column_base {
|
||||
public function get_name() {
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class preview_action_column extends action_column_base implements menuable_action {
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class question_name_column extends column_base {
|
||||
protected $checkboxespresent = null;
|
||||
@ -45,7 +46,7 @@ class question_name_column extends column_base {
|
||||
|
||||
protected function label_for($question) {
|
||||
if (is_null($this->checkboxespresent)) {
|
||||
$this->checkboxespresent = $this->qbank->has_column('core_question\bank\checkbox_column');
|
||||
$this->checkboxespresent = $this->qbank->has_column('core_question\local\bank\checkbox_column');
|
||||
}
|
||||
if ($this->checkboxespresent) {
|
||||
return 'checkq' . $question->id;
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2019 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class question_name_idnumber_tags_column extends question_name_column {
|
||||
public function get_name() {
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class question_text_row extends row_base {
|
||||
protected $formatoptions;
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class question_type_column extends column_base {
|
||||
public function get_name() {
|
||||
|
@ -41,6 +41,7 @@ namespace core_question\bank;
|
||||
*
|
||||
* @copyright 2015 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
class random_question_loader {
|
||||
/** @var \qubaid_condition which usages to consider previous attempts from. */
|
||||
|
@ -31,6 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
abstract class row_base extends column_base {
|
||||
public function is_extra_row() {
|
||||
|
@ -24,12 +24,12 @@
|
||||
*/
|
||||
|
||||
namespace core_question\bank\search;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* This class controls from which category questions are listed.
|
||||
*
|
||||
* @copyright 2013 Ray Morris
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class category_condition extends condition {
|
||||
@ -87,16 +87,25 @@ class category_condition extends condition {
|
||||
if ($this->recurse) {
|
||||
$categoryids = question_categorylist($this->category->id);
|
||||
} else {
|
||||
$categoryids = array($this->category->id);
|
||||
$categoryids = [$this->category->id];
|
||||
}
|
||||
list($catidtest, $this->params) = $DB->get_in_or_equal($categoryids, SQL_PARAMS_NAMED, 'cat');
|
||||
$this->where = 'q.category ' . $catidtest;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL fragment to add to the where clause.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function where() {
|
||||
return $this->where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parameters to be bound to the above WHERE clause fragment.
|
||||
* @return array parameter name => value.
|
||||
*/
|
||||
public function params() {
|
||||
return $this->params;
|
||||
}
|
||||
@ -105,8 +114,14 @@ class category_condition extends condition {
|
||||
* Called by question_bank_view to display the GUI for selecting a category
|
||||
*/
|
||||
public function display_options() {
|
||||
$this->display_category_form($this->contexts, $this->baseurl, $this->cat);
|
||||
$this->print_category_info($this->category);
|
||||
global $PAGE;
|
||||
$displaydata = [];
|
||||
$catmenu = question_category_options($this->contexts, true, 0,
|
||||
true, -1, false);
|
||||
$displaydata['categoryselect'] = \html_writer::select($catmenu, 'category', $this->cat, [],
|
||||
array('class' => 'searchoptions custom-select', 'id' => 'id_selectacategory'));
|
||||
$displaydata['categorydesc'] = $this->print_category_info($this->category);
|
||||
return $PAGE->get_renderer('core_question', 'bank')->render_category_condition($displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -114,12 +129,12 @@ class category_condition extends condition {
|
||||
* question_bank_view places this within the section that is hidden by default
|
||||
*/
|
||||
public function display_options_adv() {
|
||||
echo \html_writer::start_div();
|
||||
echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'recurse',
|
||||
'value' => 0, 'id' => 'recurse_off'));
|
||||
echo \html_writer::checkbox('recurse', '1', $this->recurse, get_string('includesubcategories', 'question'),
|
||||
array('id' => 'recurse_on', 'class' => 'searchoptions mr-1'));
|
||||
echo \html_writer::end_div() . "\n";
|
||||
global $PAGE;
|
||||
$displaydata = [];
|
||||
if ($this->recurse) {
|
||||
$displaydata['checked'] = 'checked="true"';
|
||||
}
|
||||
return $PAGE->get_renderer('core_question', 'bank')->render_category_condition_advanced($displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -128,12 +143,15 @@ class category_condition extends condition {
|
||||
* @param array $contexts of contexts that can be accessed from here.
|
||||
* @param \moodle_url $pageurl the URL of this page.
|
||||
* @param string $current 'categoryID,contextID'.
|
||||
* @deprecated since Moodle 4.0
|
||||
*/
|
||||
protected function display_category_form($contexts, $pageurl, $current) {
|
||||
debugging('Function display_category_form() is deprecated,
|
||||
please use the core_question renderer instead.', DEBUG_DEVELOPER);
|
||||
echo \html_writer::start_div('choosecategory');
|
||||
$catmenu = question_category_options($contexts, true, 0, true, -1, false);
|
||||
echo \html_writer::label(get_string('selectacategory', 'question'), 'id_selectacategory', true, array("class" => "mr-1"));
|
||||
echo \html_writer::select($catmenu, 'category', $current, array(),
|
||||
echo \html_writer::label(get_string('selectacategory', 'question'), 'id_selectacategory', true, ["class" => "mr-1"]);
|
||||
echo \html_writer::select($catmenu, 'category', $current, [],
|
||||
array('class' => 'searchoptions custom-select', 'id' => 'id_selectacategory'));
|
||||
echo \html_writer::end_div() . "\n";
|
||||
}
|
||||
@ -151,8 +169,7 @@ class category_condition extends condition {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$category = $DB->get_record('question_categories',
|
||||
array('id' => $categoryid, 'contextid' => $contextid))) {
|
||||
if (!$category = $DB->get_record('question_categories', ['id' => $categoryid, 'contextid' => $contextid])) {
|
||||
echo $OUTPUT->box_start('generalbox questionbank');
|
||||
echo $OUTPUT->notification('Category not found!');
|
||||
echo $OUTPUT->box_end();
|
||||
@ -164,19 +181,17 @@ class category_condition extends condition {
|
||||
|
||||
/**
|
||||
* Print the category description
|
||||
* @param stdClass $category the category information form the database.
|
||||
* @param \stdClass $category the category information form the database.
|
||||
*/
|
||||
protected function print_category_info($category) {
|
||||
protected function print_category_info($category): string {
|
||||
$formatoptions = new \stdClass();
|
||||
$formatoptions->noclean = true;
|
||||
$formatoptions->overflowdiv = true;
|
||||
echo \html_writer::start_div('boxaligncenter categoryinfo pl-0');
|
||||
if (isset($this->maxinfolength)) {
|
||||
echo shorten_text(format_text($category->info, $category->infoformat, $formatoptions, $this->course->id),
|
||||
$this->maxinfolength);
|
||||
return shorten_text(format_text($category->info, $category->infoformat, $formatoptions, $this->course->id),
|
||||
$this->maxinfolength);
|
||||
} else {
|
||||
echo format_text($category->info, $category->infoformat, $formatoptions, $this->course->id);
|
||||
return format_text($category->info, $category->infoformat, $formatoptions, $this->course->id);
|
||||
}
|
||||
echo \html_writer::end_div() . "\n";
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,11 @@
|
||||
*/
|
||||
|
||||
namespace core_question\bank\search;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* An abstract class for filtering/searching questions.
|
||||
*
|
||||
* See also {@link question_bank_view::init_search_conditions()}.
|
||||
* See also {@see question_bank_view::init_search_conditions()}.
|
||||
* @copyright 2013 Ray Morris
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@ -38,33 +37,33 @@ abstract class condition {
|
||||
* Return an SQL fragment to be ANDed into the WHERE clause to filter which questions are shown.
|
||||
* @return string SQL fragment. Must use named parameters.
|
||||
*/
|
||||
public abstract function where();
|
||||
abstract public function where();
|
||||
|
||||
/**
|
||||
* Return parameters to be bound to the above WHERE clause fragment.
|
||||
* @return array parameter name => value.
|
||||
*/
|
||||
public function params() {
|
||||
return array();
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Display GUI for selecting criteria for this condition. Displayed when Show More is open.
|
||||
*
|
||||
* Compare display_options(), which displays always, whether Show More is open or not.
|
||||
* @return string HTML form fragment
|
||||
* @return bool|string HTML form fragment
|
||||
*/
|
||||
public function display_options_adv() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display GUI for selecting criteria for this condition. Displayed always, whether Show More is open or not.
|
||||
*
|
||||
* Compare display_options_adv(), which displays when Show More is open.
|
||||
* @return string HTML form fragment
|
||||
* @return bool|string HTML form fragment
|
||||
*/
|
||||
public function display_options() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -24,12 +24,12 @@
|
||||
*/
|
||||
|
||||
namespace core_question\bank\search;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* This class controls whether hidden / deleted questions are hidden in the list.
|
||||
*
|
||||
* @copyright 2013 Ray Morris
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class hidden_condition extends condition {
|
||||
@ -50,6 +50,11 @@ class hidden_condition extends condition {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL fragment to add to the where clause.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function where() {
|
||||
return $this->where;
|
||||
}
|
||||
@ -58,11 +63,11 @@ class hidden_condition extends condition {
|
||||
* Print HTML to display the "Also show old questions" checkbox
|
||||
*/
|
||||
public function display_options_adv() {
|
||||
echo \html_writer::start_div();
|
||||
echo \html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'showhidden',
|
||||
'value' => '0', 'id' => 'showhidden_off'));
|
||||
echo \html_writer::checkbox('showhidden', '1', (! $this->hide), get_string('showhidden', 'question'),
|
||||
array('id' => 'showhidden_on', 'class' => 'searchoptions mr-1'));
|
||||
echo \html_writer::end_div() . "\n";
|
||||
global $PAGE;
|
||||
$displaydata = [];
|
||||
if (!$this->hide) {
|
||||
$displaydata['checked'] = 'checked="true"';
|
||||
}
|
||||
return $PAGE->get_renderer('core_question', 'bank')->render_hidden_condition_advanced($displaydata);
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@
|
||||
*/
|
||||
|
||||
namespace core_question\bank\search;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Question bank search class to allow searching/filtering by tags on a question.
|
||||
@ -32,17 +31,23 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class tag_condition extends condition {
|
||||
|
||||
/** @var string SQL fragment to add to the where clause. */
|
||||
protected $where;
|
||||
|
||||
/** @var string SQL fragment to add to the where clause. */
|
||||
protected $contexts;
|
||||
|
||||
/** @var array List of IDs for tags that have been selected in the form. */
|
||||
protected $selectedtagids;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param context[] $contexts List of contexts to show tags from
|
||||
*
|
||||
* @param array $contexts List of contexts to show tags from
|
||||
* @param int[] $selectedtagids List of IDs for tags to filter by.
|
||||
* @throws \coding_exception
|
||||
* @throws \dml_exception
|
||||
*/
|
||||
public function __construct(array $contexts, array $selectedtagids = []) {
|
||||
global $DB;
|
||||
@ -118,6 +123,6 @@ class tag_condition extends condition {
|
||||
'tagoptions' => $tagoptions
|
||||
];
|
||||
|
||||
echo $OUTPUT->render_from_template('core_question/tag_condition', $context);
|
||||
return $OUTPUT->render_from_template('core_question/tag_condition', $context);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*
|
||||
* @copyright 2018 Simey Lameze <simey@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to the plugin
|
||||
*/
|
||||
class tags_action_column extends action_column_base implements menuable_action {
|
||||
/**
|
||||
@ -39,6 +40,11 @@ class tags_action_column extends action_column_base implements menuable_action {
|
||||
|
||||
public function init() {
|
||||
parent::init();
|
||||
global $CFG;
|
||||
if ($CFG->usetags) {
|
||||
global $PAGE;
|
||||
$PAGE->requires->js_call_amd('core_question/edit_tags', 'init', ['#questionscontainer']);
|
||||
}
|
||||
$this->managetags = get_string('managetags', 'tag');
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,7 @@ use core_question\bank\search\condition;
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @todo MDL-72004 delete the class and add it to lib/db/renameclasses.php pointing to core_question/local/bank
|
||||
*/
|
||||
class view {
|
||||
const MAX_SORTS = 3;
|
||||
|
@ -285,7 +285,7 @@ class core_question_external extends external_api {
|
||||
// The user must be able to view all questions in the category that they are requesting.
|
||||
$editcontexts->require_cap('moodle/question:viewall');
|
||||
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
// Only load the properties we require from the DB.
|
||||
$properties = \core_question\external\question_summary_exporter::get_mandatory_properties();
|
||||
$questions = $loader->get_questions($categoryid, $includesubcategories, $tagids, $limit, $offset, $properties);
|
||||
|
60
question/classes/local/bank/action_column_base.php
Normal file
60
question/classes/local/bank/action_column_base.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A base class for actions that are an icon that lets you manipulate the question in some way.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* A base class for actions that are an icon that lets you manipulate the question in some way.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class action_column_base extends column_base {
|
||||
|
||||
protected function get_title(): string {
|
||||
return ' ';
|
||||
}
|
||||
|
||||
public function get_extra_classes(): array {
|
||||
return ['iconcol'];
|
||||
}
|
||||
|
||||
protected function print_icon($icon, $title, $url): void {
|
||||
global $OUTPUT;
|
||||
echo \html_writer::tag('a', $OUTPUT->pix_icon($icon, $title), ['title' => $title, 'href' => $url]);
|
||||
}
|
||||
|
||||
public function get_extra_joins(): array {
|
||||
return ['qc' => 'JOIN {question_categories} qc ON qc.id = q.category'];
|
||||
}
|
||||
|
||||
public function get_required_fields(): array {
|
||||
// Createdby is required for permission checks.
|
||||
// Qtype so we can easily avoid applying actions to question types that
|
||||
// are no longer installed.
|
||||
return ['q.id', 'q.qtype', 'q.createdby', 'qc.contextid'];
|
||||
}
|
||||
|
||||
}
|
78
question/classes/local/bank/checkbox_column.php
Normal file
78
question/classes/local/bank/checkbox_column.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A column with a checkbox for each question with name q{questionid}.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
use core\output\checkbox_toggleall;
|
||||
|
||||
/**
|
||||
* A column with a checkbox for each question with name q{questionid}.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class checkbox_column extends column_base {
|
||||
|
||||
public function get_name(): string {
|
||||
return 'checkbox';
|
||||
}
|
||||
|
||||
protected function get_title() {
|
||||
global $OUTPUT;
|
||||
|
||||
$mastercheckbox = new checkbox_toggleall('qbank', true, [
|
||||
'id' => 'qbheadercheckbox',
|
||||
'name' => 'qbheadercheckbox',
|
||||
'value' => '1',
|
||||
'label' => get_string('selectall'),
|
||||
'labelclasses' => 'accesshide',
|
||||
]);
|
||||
|
||||
return $OUTPUT->render($mastercheckbox);
|
||||
}
|
||||
|
||||
protected function get_title_tip() {
|
||||
return get_string('selectquestionsforbulk', 'question');
|
||||
}
|
||||
|
||||
protected function display_content($question, $rowclasses): void {
|
||||
global $OUTPUT;
|
||||
|
||||
$checkbox = new checkbox_toggleall('qbank', false, [
|
||||
'id' => "checkq{$question->id}",
|
||||
'name' => "q{$question->id}",
|
||||
'value' => '1',
|
||||
'label' => get_string('select'),
|
||||
'labelclasses' => 'accesshide',
|
||||
]);
|
||||
|
||||
echo $OUTPUT->render($checkbox);
|
||||
}
|
||||
|
||||
public function get_required_fields(): array {
|
||||
return ['q.id'];
|
||||
}
|
||||
|
||||
}
|
393
question/classes/local/bank/column_base.php
Normal file
393
question/classes/local/bank/column_base.php
Normal file
@ -0,0 +1,393 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Base class for representing a column.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Base class for representing a column.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class column_base {
|
||||
|
||||
/**
|
||||
* @var view $qbank the question bank view we are helping to render.
|
||||
*/
|
||||
protected $qbank;
|
||||
|
||||
/** @var bool determine whether the column is td or th. */
|
||||
protected $isheading = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param view $qbank the question bank view we are helping to render.
|
||||
*/
|
||||
public function __construct(view $qbank) {
|
||||
$this->qbank = $qbank;
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* A chance for subclasses to initialise themselves, for example to load lang strings,
|
||||
* without having to override the constructor.
|
||||
*/
|
||||
protected function init(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the column as heading
|
||||
*/
|
||||
public function set_as_heading(): void {
|
||||
$this->isheading = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the column is an extra row of not.
|
||||
*/
|
||||
public function is_extra_row(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the row has an extra preference to view/hide.
|
||||
*/
|
||||
public function has_preference(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the preference key of the row.
|
||||
*/
|
||||
public function get_preference_key(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the preference of the row.
|
||||
*/
|
||||
public function get_preference(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the column header cell.
|
||||
*/
|
||||
public function display_header(): void {
|
||||
global $PAGE;
|
||||
|
||||
$data = [];
|
||||
$data['sortable'] = true;
|
||||
$data['extraclasses'] = $this->get_classes();
|
||||
$sortable = $this->is_sortable();
|
||||
$name = get_class($this);
|
||||
$title = $this->get_title();
|
||||
$tip = $this->get_title_tip();
|
||||
$links = [];
|
||||
if (is_array($sortable)) {
|
||||
if ($title) {
|
||||
$data['title'] = $title;
|
||||
}
|
||||
foreach ($sortable as $subsort => $details) {
|
||||
$links[] = $this->make_sort_link($name . '-' . $subsort,
|
||||
$details['title'], isset($details['tip']) ? $details['tip'] : '', !empty($details['reverse']));
|
||||
}
|
||||
$data['sortlinks'] = implode(' / ', $links);
|
||||
} else if ($sortable) {
|
||||
$data['sortlinks'] = $this->make_sort_link($name, $title, $tip);
|
||||
} else {
|
||||
$data['sortable'] = false;
|
||||
$data['tiptitle'] = $title;
|
||||
if ($tip) {
|
||||
$data['sorttip'] = true;
|
||||
$data['tip'] = $tip;
|
||||
}
|
||||
}
|
||||
|
||||
$renderer = $PAGE->get_renderer('core_question', 'bank');
|
||||
echo $renderer->render_column_header($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Title for this column. Not used if is_sortable returns an array.
|
||||
*/
|
||||
abstract protected function get_title();
|
||||
|
||||
/**
|
||||
* Use this when get_title() returns
|
||||
* something very short, and you want a longer version as a tool tip.
|
||||
*
|
||||
* @return string a fuller version of the name.
|
||||
*/
|
||||
protected function get_title_tip() {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a link that changes the sort order, and indicates the current sort state.
|
||||
* @param string $sort the column to sort on.
|
||||
* @param string $title the link text.
|
||||
* @param string $tip the link tool-tip text. If empty, defaults to title.
|
||||
* @param bool $defaultreverse whether the default sort order for this column is descending, rather than ascending.
|
||||
* @return string
|
||||
*/
|
||||
protected function make_sort_link($sort, $title, $tip, $defaultreverse = false): string {
|
||||
global $PAGE;
|
||||
$sortdata = [];
|
||||
$currentsort = $this->qbank->get_primary_sort_order($sort);
|
||||
$newsortreverse = $defaultreverse;
|
||||
if ($currentsort) {
|
||||
$newsortreverse = $currentsort > 0;
|
||||
}
|
||||
if (!$tip) {
|
||||
$tip = $title;
|
||||
}
|
||||
if ($newsortreverse) {
|
||||
$tip = get_string('sortbyxreverse', '', $tip);
|
||||
} else {
|
||||
$tip = get_string('sortbyx', '', $tip);
|
||||
}
|
||||
|
||||
$link = $title;
|
||||
if ($currentsort) {
|
||||
$link .= $this->get_sort_icon($currentsort < 0);
|
||||
}
|
||||
|
||||
$sortdata['sorturl'] = $this->qbank->new_sort_url($sort, $newsortreverse);
|
||||
$sortdata['sortcontent'] = $link;
|
||||
$sortdata['sorttip'] = $tip;
|
||||
$renderer = $PAGE->get_renderer('core_question', 'bank');
|
||||
return $renderer->render_column_sort($sortdata);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an icon representing the corrent sort state.
|
||||
* @param bool $reverse sort is descending, not ascending.
|
||||
* @return string HTML image tag.
|
||||
*/
|
||||
protected function get_sort_icon($reverse): string {
|
||||
global $OUTPUT;
|
||||
if ($reverse) {
|
||||
return $OUTPUT->pix_icon('t/sort_desc', get_string('desc'), '', ['class' => 'iconsort']);
|
||||
} else {
|
||||
return $OUTPUT->pix_icon('t/sort_asc', get_string('asc'), '', ['class' => 'iconsort']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output this column.
|
||||
* @param object $question the row from the $question table, augmented with extra information.
|
||||
* @param string $rowclasses CSS class names that should be applied to this row of output.
|
||||
*/
|
||||
public function display($question, $rowclasses): void {
|
||||
$this->display_start($question, $rowclasses);
|
||||
$this->display_content($question, $rowclasses);
|
||||
$this->display_end($question, $rowclasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the opening column tag. If it is set as heading, it will use <th> tag instead of <td>
|
||||
*
|
||||
* @param \stdClass $question
|
||||
* @param string $rowclasses
|
||||
*/
|
||||
protected function display_start($question, $rowclasses): void {
|
||||
$tag = 'td';
|
||||
$attr = ['class' => $this->get_classes()];
|
||||
if ($this->isheading) {
|
||||
$tag = 'th';
|
||||
$attr['scope'] = 'row';
|
||||
}
|
||||
echo \html_writer::start_tag($tag, $attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* The CSS classes to apply to every cell in this column.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_classes(): string {
|
||||
$classes = $this->get_extra_classes();
|
||||
$classes[] = $this->get_name();
|
||||
return implode(' ', $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the internal name for this column. Used as a CSS class name,
|
||||
* and to store information about the current sort. Must match PARAM_ALPHA.
|
||||
*
|
||||
* @return string column name.
|
||||
*/
|
||||
abstract public function get_name();
|
||||
|
||||
/**
|
||||
* Any extra class names you would like applied to every cell in this column.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_extra_classes(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the contents of this column.
|
||||
* @param object $question the row from the $question table, augmented with extra information.
|
||||
* @param string $rowclasses CSS class names that should be applied to this row of output.
|
||||
*/
|
||||
abstract protected function display_content($question, $rowclasses);
|
||||
|
||||
/**
|
||||
* Output the closing column tag
|
||||
*
|
||||
* @param object $question
|
||||
* @param string $rowclasses
|
||||
*/
|
||||
protected function display_end($question, $rowclasses): void {
|
||||
$tag = 'td';
|
||||
if ($this->isheading) {
|
||||
$tag = 'th';
|
||||
}
|
||||
echo \html_writer::end_tag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array 'table_alias' => 'JOIN clause' to bring in any data that
|
||||
* this column required.
|
||||
*
|
||||
* The return values for all the columns will be checked. It is OK if two
|
||||
* columns join in the same table with the same alias and identical JOIN clauses.
|
||||
* If to columns try to use the same alias with different joins, you get an error.
|
||||
* The only table included by default is the question table, which is aliased to 'q'.
|
||||
*
|
||||
* It is importnat that your join simply adds additional data (or NULLs) to the
|
||||
* existing rows of the query. It must not cause additional rows.
|
||||
*
|
||||
* @return array 'table_alias' => 'JOIN clause'
|
||||
*/
|
||||
public function get_extra_joins(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Use table alias 'q' for the question table, or one of the
|
||||
* ones from get_extra_joins. Every field requested must specify a table prefix.
|
||||
*
|
||||
* @return array fields required.
|
||||
*/
|
||||
public function get_required_fields(): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* If this column needs extra data (e.g. tags) then load that here.
|
||||
*
|
||||
* The extra data should be added to the question object in the array.
|
||||
* Probably a good idea to check that another column has not already
|
||||
* loaded the data you want.
|
||||
*
|
||||
* @param \stdClass[] $questions the questions that will be displayed.
|
||||
*/
|
||||
public function load_additional_data(array $questions) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the tags for each question.
|
||||
*
|
||||
* Helper that can be used from {@see load_additional_data()};
|
||||
*
|
||||
* @param array $questions
|
||||
*/
|
||||
public function load_question_tags(array $questions): void {
|
||||
$firstquestion = reset($questions);
|
||||
if (isset($firstquestion->tags)) {
|
||||
// Looks like tags are already loaded, so don't do it again.
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the tags.
|
||||
$tagdata = \core_tag_tag::get_items_tags('core_question', 'question',
|
||||
array_keys($questions));
|
||||
|
||||
// Add them to the question objects.
|
||||
foreach ($tagdata as $questionid => $tags) {
|
||||
$questions[$questionid]->tags = $tags;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this column be sorted on? You can return either:
|
||||
* + false for no (the default),
|
||||
* + a field name, if sorting this column corresponds to sorting on that datbase field.
|
||||
* + an array of subnames to sort on as follows
|
||||
* return [
|
||||
* 'firstname' => ['field' => 'uc.firstname', 'title' => get_string('firstname')],
|
||||
* 'lastname' => ['field' => 'uc.lastname', 'title' => get_string('lastname')],
|
||||
* ];
|
||||
* As well as field, and field, you can also add 'revers' => 1 if you want the default sort
|
||||
* order to be DESC.
|
||||
* @return mixed as above.
|
||||
*/
|
||||
public function is_sortable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for building sort clauses.
|
||||
* @param bool $reverse whether the normal direction should be reversed.
|
||||
* @return string 'ASC' or 'DESC'
|
||||
*/
|
||||
protected function sortorder($reverse): string {
|
||||
if ($reverse) {
|
||||
return ' DESC';
|
||||
} else {
|
||||
return ' ASC';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the expressions.
|
||||
*
|
||||
* @param bool $reverse Whether to sort in the reverse of the default sort order.
|
||||
* @param string $subsort if is_sortable returns an array of subnames, then this will be
|
||||
* one of those. Otherwise will be empty.
|
||||
* @return string some SQL to go in the order by clause.
|
||||
*/
|
||||
public function sort_expression($reverse, $subsort): string {
|
||||
$sortable = $this->is_sortable();
|
||||
if (is_array($sortable)) {
|
||||
if (array_key_exists($subsort, $sortable)) {
|
||||
return $sortable[$subsort]['field'] . $this->sortorder($reverse);
|
||||
} else {
|
||||
throw new \coding_exception('Unexpected $subsort type: ' . $subsort);
|
||||
}
|
||||
} else if ($sortable) {
|
||||
return $sortable . $this->sortorder($reverse);
|
||||
} else {
|
||||
throw new \coding_exception('sort_expression called on a non-sortable column.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
102
question/classes/local/bank/edit_menu_column.php
Normal file
102
question/classes/local/bank/edit_menu_column.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A question bank column which gathers together all the actions into a menu.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2019 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* A question bank column which gathers together all the actions into a menu.
|
||||
*
|
||||
* This question bank column, if added to the question bank, will
|
||||
* replace all of the other columns which implement the
|
||||
* {@see menuable_action} interface and replace them with a single
|
||||
* column containing an Edit menu.
|
||||
*
|
||||
* @copyright 2019 The Open University
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class edit_menu_column extends column_base {
|
||||
/**
|
||||
* @var menuable_action[]
|
||||
*/
|
||||
protected $actions;
|
||||
|
||||
/**
|
||||
* Set up the list of actions that should be shown in the menu.
|
||||
*
|
||||
* This takes a list of column object (the list from a question
|
||||
* bank view). It extracts all the ones that should go in the menu
|
||||
* and stores them for later use. Then it returns the remaining columns.
|
||||
*
|
||||
* @param column_base[] $allcolumns a set of columns.
|
||||
* @return column_base[] the non-action columns from the set.
|
||||
* @todo MDL-72004 changes for class renaming.
|
||||
*/
|
||||
public function claim_menuable_columns($allcolumns): array {
|
||||
$remainingcolumns = [];
|
||||
foreach ($allcolumns as $key => $column) {
|
||||
if ($column instanceof menuable_action || $column instanceof \core_question\bank\menuable_action) {
|
||||
$this->actions[$key] = $column;
|
||||
} else {
|
||||
$remainingcolumns[$key] = $column;
|
||||
}
|
||||
}
|
||||
return $remainingcolumns;
|
||||
}
|
||||
|
||||
protected function get_title() {
|
||||
return get_string('actions');
|
||||
}
|
||||
|
||||
public function get_name(): string {
|
||||
return 'editmenu';
|
||||
}
|
||||
|
||||
protected function display_content($question, $rowclasses): void {
|
||||
global $OUTPUT;
|
||||
|
||||
$menu = new \action_menu();
|
||||
$menu->set_menu_trigger(get_string('edit'));
|
||||
$menu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
foreach ($this->actions as $actioncolumn) {
|
||||
$action = $actioncolumn->get_action_menu_link($question);
|
||||
if ($action) {
|
||||
$menu->add($action);
|
||||
}
|
||||
}
|
||||
|
||||
$qtypeactions = \question_bank::get_qtype($question->qtype, false)
|
||||
->get_extra_question_bank_actions($question);
|
||||
foreach ($qtypeactions as $action) {
|
||||
$menu->add($action);
|
||||
}
|
||||
|
||||
echo $OUTPUT->render($menu);
|
||||
}
|
||||
|
||||
public function get_required_fields():array {
|
||||
return ['q.qtype'];
|
||||
}
|
||||
|
||||
}
|
55
question/classes/local/bank/helper.php
Normal file
55
question/classes/local/bank/helper.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Helper class for question bank and its plugins.
|
||||
*
|
||||
* All the functions which has a potential to be used by different features or
|
||||
* plugins, should go here.
|
||||
*
|
||||
* @package core_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
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Class helper
|
||||
*
|
||||
* @package core_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 helper {
|
||||
|
||||
/**
|
||||
* Check the status of a plugin and throw exception if not enabled and called manually.
|
||||
*
|
||||
* Any action plugin having a php script, should call this function for a safer enable/disable implementation.
|
||||
*
|
||||
* @param string $pluginname
|
||||
* @return void
|
||||
*/
|
||||
public static function require_plugin_enabled(string $pluginname): void {
|
||||
if (!\core\plugininfo\qbank::is_plugin_enabled($pluginname)) {
|
||||
throw new \moodle_exception('The following plugin is either disabled or missing from disk: ' . $pluginname);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
69
question/classes/local/bank/menu_action_column_base.php
Normal file
69
question/classes/local/bank/menu_action_column_base.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Base class to make it easier to implement actions that are menuable_actions.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2019 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Base class to make it easier to implement actions that are menuable_actions.
|
||||
*
|
||||
* Use this class if your action is simple (defined by just a URL, label and icon).
|
||||
* If your action is not simple enough to fit into the pattern that this
|
||||
* class implements, then you will have to implement the menuable_action
|
||||
* interface yourself.
|
||||
*
|
||||
* @copyright 2019 Tim Hunt
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class menu_action_column_base extends action_column_base implements menuable_action {
|
||||
|
||||
/**
|
||||
* Get the information required to display this action either as a menu item or a separate action column.
|
||||
*
|
||||
* If this action cannot apply to this question (e.g. because the user does not have
|
||||
* permission, then return [null, null, null].
|
||||
*
|
||||
* @param \stdClass $question the row from the $question table, augmented with extra information.
|
||||
* @return array with three elements.
|
||||
* $url - the URL to perform the action.
|
||||
* $icon - the icon for this action. E.g. 't/delete'.
|
||||
* $label - text label to display in the UI (either in the menu, or as a tool-tip on the icon)
|
||||
*/
|
||||
abstract protected function get_url_icon_and_label(\stdClass $question);
|
||||
|
||||
protected function display_content($question, $rowclasses): void {
|
||||
[$url, $icon, $label] = $this->get_url_icon_and_label($question);
|
||||
if ($url) {
|
||||
$this->print_icon($icon, $label, $url);
|
||||
}
|
||||
}
|
||||
|
||||
public function get_action_menu_link(\stdClass $question): ?\action_menu_link {
|
||||
[$url, $icon, $label] = $this->get_url_icon_and_label($question);
|
||||
if (!$url) {
|
||||
return null;
|
||||
}
|
||||
return new \action_menu_link_secondary($url, new \pix_icon($icon, ''), $label);
|
||||
}
|
||||
}
|
54
question/classes/local/bank/menuable_action.php
Normal file
54
question/classes/local/bank/menuable_action.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Interface to indicate that a question bank column can go in the action menu.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2019 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Interface to indicate that a question bank column can go in the action menu.
|
||||
*
|
||||
* If a question bank column implements this interface, and if the {@see edit_menu_column}
|
||||
* is present in the question bank view, then the 'column' will be shown as an entry in the
|
||||
* edit menu instead of as a separate column.
|
||||
*
|
||||
* Probably most columns that want to implement this will be subclasses of
|
||||
* {@see action_column_base}, and most such columns should probably implement
|
||||
* this interface.
|
||||
*
|
||||
* If your column is a simple action, you can probably save duplicated code by
|
||||
* using the base class action_column_menuable as an easy way to implement both
|
||||
* action_column_base and this interface.
|
||||
*
|
||||
* @copyright 2019 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
interface menuable_action {
|
||||
|
||||
/**
|
||||
* Return the appropriate action menu link, or null if it does not apply to this question.
|
||||
*
|
||||
* @param \stdClass $question data about the question being displayed in this row.
|
||||
* @return \action_menu_link|null the action, if applicable to this question.
|
||||
*/
|
||||
public function get_action_menu_link(\stdClass $question): ?\action_menu_link;
|
||||
}
|
64
question/classes/local/bank/navigation_node_base.php
Normal file
64
question/classes/local/bank/navigation_node_base.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Base class class for navigation node.
|
||||
*
|
||||
* @package core_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
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Class navigation_node_base is the base class for navigation node.
|
||||
*
|
||||
* @package core_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
|
||||
*/
|
||||
abstract class navigation_node_base {
|
||||
|
||||
/**
|
||||
* Title for this node.
|
||||
*/
|
||||
abstract public function get_navigation_title();
|
||||
|
||||
/**
|
||||
* Key for this node.
|
||||
*/
|
||||
abstract public function get_navigation_key();
|
||||
|
||||
/**
|
||||
* URL for this node
|
||||
*/
|
||||
abstract public function get_navigation_url();
|
||||
|
||||
/**
|
||||
* Tab capabilities.
|
||||
*
|
||||
* If it has capabilities to be checked, it will return the array of capabilities.
|
||||
*
|
||||
* @return null|array
|
||||
*/
|
||||
public function get_navigation_capabilities(): ?array {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
59
question/classes/local/bank/plugin_features_base.php
Normal file
59
question/classes/local/bank/plugin_features_base.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Base class class for qbank plugins.
|
||||
*
|
||||
* Every qbank plugin must extent this class.
|
||||
*
|
||||
* @package core_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
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Class plugin_features_base is the base class for qbank plugins.
|
||||
*
|
||||
* @package core_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 plugin_features_base {
|
||||
|
||||
/**
|
||||
* This method will return the array of objects to be rendered as a prt of question bank columns/actions.
|
||||
*
|
||||
* @param view $qbank
|
||||
* @return array
|
||||
*/
|
||||
public function get_question_columns(view $qbank): ?array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the object for the navigation node.
|
||||
*
|
||||
* @return null|object
|
||||
*/
|
||||
public function get_navigation_node(): ?object {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
310
question/classes/local/bank/random_question_loader.php
Normal file
310
question/classes/local/bank/random_question_loader.php
Normal file
@ -0,0 +1,310 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* A class for efficiently finds questions at random from the question bank.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2015 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* This class efficiently finds questions at random from the question bank.
|
||||
*
|
||||
* You can ask for questions at random one at a time. Each time you ask, you
|
||||
* pass a category id, and whether to pick from that category and all subcategories
|
||||
* or just that category.
|
||||
*
|
||||
* The number of teams each question has been used is tracked, and we will always
|
||||
* return a question from among those elegible that has been used the fewest times.
|
||||
* So, if there are questions that have not been used yet in the category asked for,
|
||||
* one of those will be returned. However, within one instantiation of this class,
|
||||
* we will never return a given question more than once, and we will never return
|
||||
* questions passed into the constructor as $usedquestions.
|
||||
*
|
||||
* @copyright 2015 The Open University
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class random_question_loader {
|
||||
/** @var \qubaid_condition which usages to consider previous attempts from. */
|
||||
protected $qubaids;
|
||||
|
||||
/** @var array qtypes that cannot be used by random questions. */
|
||||
protected $excludedqtypes;
|
||||
|
||||
/** @var array categoryid & include subcategories => num previous uses => questionid => 1. */
|
||||
protected $availablequestionscache = [];
|
||||
|
||||
/**
|
||||
* @var array questionid => num recent uses. Questions that have been used,
|
||||
* but that is not yet recorded in the DB.
|
||||
*/
|
||||
protected $recentlyusedquestions;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \qubaid_condition $qubaids the usages to consider when counting previous uses of each question.
|
||||
* @param array $usedquestions questionid => number of times used count. If we should allow for
|
||||
* further existing uses of a question in addition to the ones in $qubaids.
|
||||
*/
|
||||
public function __construct(\qubaid_condition $qubaids, array $usedquestions = []) {
|
||||
$this->qubaids = $qubaids;
|
||||
$this->recentlyusedquestions = $usedquestions;
|
||||
|
||||
foreach (\question_bank::get_all_qtypes() as $qtype) {
|
||||
if (!$qtype->is_usable_by_random()) {
|
||||
$this->excludedqtypes[] = $qtype->name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a question at random from the given category, from among those with the fewest uses.
|
||||
* If an array of tag ids are specified, then only the questions that are tagged with ALL those tags will be selected.
|
||||
*
|
||||
* It is up the the caller to verify that the cateogry exists. An unknown category
|
||||
* behaves like an empty one.
|
||||
*
|
||||
* @param int $categoryid the id of a category in the question bank.
|
||||
* @param bool $includesubcategories wether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param array $tagids An array of tag ids. A question has to be tagged with all the provided tagids (if any)
|
||||
* in order to be eligible for being picked.
|
||||
* @return int|null the id of the question picked, or null if there aren't any.
|
||||
*/
|
||||
public function get_next_question_id($categoryid, $includesubcategories, $tagids = []): ?int {
|
||||
$this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
|
||||
|
||||
$categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
|
||||
if (empty($this->availablequestionscache[$categorykey])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
reset($this->availablequestionscache[$categorykey]);
|
||||
$lowestcount = key($this->availablequestionscache[$categorykey]);
|
||||
reset($this->availablequestionscache[$categorykey][$lowestcount]);
|
||||
$questionid = key($this->availablequestionscache[$categorykey][$lowestcount]);
|
||||
$this->use_question($questionid);
|
||||
return $questionid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key into {@see $availablequestionscache} for this combination of options.
|
||||
*
|
||||
* @param int $categoryid the id of a category in the question bank.
|
||||
* @param bool $includesubcategories wether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param array $tagids an array of tag ids.
|
||||
* @return string the cache key.
|
||||
*/
|
||||
protected function get_category_key($categoryid, $includesubcategories, $tagids = []): string {
|
||||
if ($includesubcategories) {
|
||||
$key = $categoryid . '|1';
|
||||
} else {
|
||||
$key = $categoryid . '|0';
|
||||
}
|
||||
|
||||
if (!empty($tagids)) {
|
||||
$key .= '|' . implode('|', $tagids);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate {@see $availablequestionscache} for this combination of options.
|
||||
*
|
||||
* @param int $categoryid The id of a category in the question bank.
|
||||
* @param bool $includesubcategories Whether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param array $tagids An array of tag ids. If an array is provided, then
|
||||
* only the questions that are tagged with ALL the provided tagids will be loaded.
|
||||
*/
|
||||
protected function ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids = []): void {
|
||||
global $DB;
|
||||
|
||||
$categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
|
||||
|
||||
if (isset($this->availablequestionscache[$categorykey])) {
|
||||
// Data is already in the cache, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the available questions from the question bank.
|
||||
if ($includesubcategories) {
|
||||
$categoryids = question_categorylist($categoryid);
|
||||
} else {
|
||||
$categoryids = [$categoryid];
|
||||
}
|
||||
|
||||
list($extraconditions, $extraparams) = $DB->get_in_or_equal($this->excludedqtypes,
|
||||
SQL_PARAMS_NAMED, 'excludedqtype', false);
|
||||
|
||||
$questionidsandcounts = \question_bank::get_finder()->get_questions_from_categories_and_tags_with_usage_counts(
|
||||
$categoryids, $this->qubaids, 'q.qtype ' . $extraconditions, $extraparams, $tagids);
|
||||
if (!$questionidsandcounts) {
|
||||
// No questions in this category.
|
||||
$this->availablequestionscache[$categorykey] = [];
|
||||
return;
|
||||
}
|
||||
|
||||
// Put all the questions with each value of $prevusecount in separate arrays.
|
||||
$idsbyusecount = [];
|
||||
foreach ($questionidsandcounts as $questionid => $prevusecount) {
|
||||
if (isset($this->recentlyusedquestions[$questionid])) {
|
||||
// Recently used questions are never returned.
|
||||
continue;
|
||||
}
|
||||
$idsbyusecount[$prevusecount][] = $questionid;
|
||||
}
|
||||
|
||||
// Now put that data into our cache. For each count, we need to shuffle
|
||||
// questionids, and make those the keys of an array.
|
||||
$this->availablequestionscache[$categorykey] = [];
|
||||
foreach ($idsbyusecount as $prevusecount => $questionids) {
|
||||
shuffle($questionids);
|
||||
$this->availablequestionscache[$categorykey][$prevusecount] = array_combine(
|
||||
$questionids, array_fill(0, count($questionids), 1));
|
||||
}
|
||||
ksort($this->availablequestionscache[$categorykey]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the internal data structures to indicate that a given question has
|
||||
* been used one more time.
|
||||
*
|
||||
* @param int $questionid the question that is being used.
|
||||
*/
|
||||
protected function use_question($questionid): void {
|
||||
if (isset($this->recentlyusedquestions[$questionid])) {
|
||||
$this->recentlyusedquestions[$questionid] += 1;
|
||||
} else {
|
||||
$this->recentlyusedquestions[$questionid] = 1;
|
||||
}
|
||||
|
||||
foreach ($this->availablequestionscache as $categorykey => $questionsforcategory) {
|
||||
foreach ($questionsforcategory as $numuses => $questionids) {
|
||||
if (!isset($questionids[$questionid])) {
|
||||
continue;
|
||||
}
|
||||
unset($this->availablequestionscache[$categorykey][$numuses][$questionid]);
|
||||
if (empty($this->availablequestionscache[$categorykey][$numuses])) {
|
||||
unset($this->availablequestionscache[$categorykey][$numuses]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of available question ids for the given criteria.
|
||||
*
|
||||
* @param int $categoryid The id of a category in the question bank.
|
||||
* @param bool $includesubcategories Whether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param array $tagids An array of tag ids. If an array is provided, then
|
||||
* only the questions that are tagged with ALL the provided tagids will be loaded.
|
||||
* @return int[] The list of question ids
|
||||
*/
|
||||
protected function get_question_ids($categoryid, $includesubcategories, $tagids = []): array {
|
||||
$this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
|
||||
$categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
|
||||
$cachedvalues = $this->availablequestionscache[$categorykey];
|
||||
$questionids = [];
|
||||
|
||||
foreach ($cachedvalues as $usecount => $ids) {
|
||||
$questionids = array_merge($questionids, array_keys($ids));
|
||||
}
|
||||
|
||||
return $questionids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given question is available in a given category. If so, mark it used.
|
||||
* If an optional list of tag ids are provided, then the question must be tagged with
|
||||
* ALL of the provided tags to be considered as available.
|
||||
*
|
||||
* @param int $categoryid the id of a category in the question bank.
|
||||
* @param bool $includesubcategories wether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param int $questionid the question that is being used.
|
||||
* @param array $tagids An array of tag ids. Only the questions that are tagged with all the provided tagids can be available.
|
||||
* @return bool whether the question is available in the requested category.
|
||||
*/
|
||||
public function is_question_available($categoryid, $includesubcategories, $questionid, $tagids = []): bool {
|
||||
$this->ensure_questions_for_category_loaded($categoryid, $includesubcategories, $tagids);
|
||||
$categorykey = $this->get_category_key($categoryid, $includesubcategories, $tagids);
|
||||
|
||||
foreach ($this->availablequestionscache[$categorykey] as $questionids) {
|
||||
if (isset($questionids[$questionid])) {
|
||||
$this->use_question($questionid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of available questions for the given criteria.
|
||||
*
|
||||
* @param int $categoryid The id of a category in the question bank.
|
||||
* @param bool $includesubcategories Whether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param array $tagids An array of tag ids. If an array is provided, then
|
||||
* only the questions that are tagged with ALL the provided tagids will be loaded.
|
||||
* @param int $limit Maximum number of results to return.
|
||||
* @param int $offset Number of items to skip from the begging of the result set.
|
||||
* @param string[] $fields The fields to return for each question.
|
||||
* @return \stdClass[] The list of question records
|
||||
*/
|
||||
public function get_questions($categoryid, $includesubcategories, $tagids = [], $limit = 100, $offset = 0, $fields = []) {
|
||||
global $DB;
|
||||
|
||||
$questionids = $this->get_question_ids($categoryid, $includesubcategories, $tagids);
|
||||
if (empty($questionids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (empty($fields)) {
|
||||
// Return all fields.
|
||||
$fieldsstring = '*';
|
||||
} else {
|
||||
$fieldsstring = implode(',', $fields);
|
||||
}
|
||||
|
||||
return $DB->get_records_list('question', 'id', $questionids, 'id', $fieldsstring, $offset, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of available questions for the given criteria.
|
||||
*
|
||||
* @param int $categoryid The id of a category in the question bank.
|
||||
* @param bool $includesubcategories Whether to pick a question from exactly
|
||||
* that category, or that category and subcategories.
|
||||
* @param array $tagids An array of tag ids. If an array is provided, then
|
||||
* only the questions that are tagged with ALL the provided tagids will be loaded.
|
||||
* @return int The number of questions matching the criteria.
|
||||
*/
|
||||
public function count_questions($categoryid, $includesubcategories, $tagids = []): int {
|
||||
$questionids = $this->get_question_ids($categoryid, $includesubcategories, $tagids);
|
||||
return count($questionids);
|
||||
}
|
||||
}
|
70
question/classes/local/bank/row_base.php
Normal file
70
question/classes/local/bank/row_base.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Base class for 'columns' that are actually displayed as a row following the main question row.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Base class for 'columns' that are actually displayed as a row following the main question row.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @author 2021 Safat Shahin <safatshahin@catalyst-au.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class row_base extends column_base {
|
||||
|
||||
/**
|
||||
* Check if the column is an extra row of not.
|
||||
*/
|
||||
public function is_extra_row(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the opening column tag. If it is set as heading, it will use <th> tag instead of <td>
|
||||
*
|
||||
* @param \stdClass $question
|
||||
* @param string $rowclasses
|
||||
*/
|
||||
protected function display_start($question, $rowclasses): void {
|
||||
if ($rowclasses) {
|
||||
echo \html_writer::start_tag('tr', ['class' => $rowclasses]);
|
||||
} else {
|
||||
echo \html_writer::start_tag('tr');
|
||||
}
|
||||
echo \html_writer::start_tag('td',
|
||||
['colspan' => $this->qbank->get_column_count(), 'class' => $this->get_name()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the closing column tag
|
||||
*
|
||||
* @param object $question
|
||||
* @param string $rowclasses
|
||||
*/
|
||||
protected function display_end($question, $rowclasses): void {
|
||||
echo \html_writer::end_tag('td');
|
||||
echo \html_writer::end_tag('tr');
|
||||
}
|
||||
|
||||
}
|
1322
question/classes/local/bank/view.php
Normal file
1322
question/classes/local/bank/view.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,7 +36,9 @@ if (($lastchanged = optional_param('lastchanged', 0, PARAM_INT)) !== 0) {
|
||||
}
|
||||
$PAGE->set_url($url);
|
||||
|
||||
$questionbank = new core_question\bank\view($contexts, $thispageurl, $COURSE, $cm);
|
||||
$questionbank = new core_question\local\bank\view($contexts, $thispageurl, $COURSE, $cm);
|
||||
|
||||
// TODO MDL-72076 - this one will become redundant after implementing bulk actions UI.
|
||||
$questionbank->process_actions();
|
||||
|
||||
$context = $contexts->lowest();
|
||||
@ -49,11 +51,8 @@ echo $OUTPUT->header();
|
||||
$renderer = $PAGE->get_renderer('core_question', 'bank');
|
||||
echo $renderer->extra_horizontal_navigation();
|
||||
|
||||
echo '<div class="questionbankwindow boxwidthwide boxaligncenter">';
|
||||
$questionbank->display('questions', $pagevars['qpage'], $pagevars['qperpage'],
|
||||
$pagevars['cat'], $pagevars['recurse'], $pagevars['showhidden'],
|
||||
$pagevars['qbshowtext'], $pagevars['qtagids']);
|
||||
echo "</div>\n";
|
||||
// Print the question area.
|
||||
$questionbank->display($pagevars, 'questions');
|
||||
|
||||
// Log the view of this category.
|
||||
list($categoryid, $contextid) = explode(',', $pagevars['cat']);
|
||||
|
@ -140,13 +140,14 @@ function question_can_delete_cat($todelete) {
|
||||
|
||||
|
||||
/**
|
||||
* Base class for representing a column in a {@link question_bank_view}.
|
||||
* Base class for representing a column in a {@see question_bank_view}.
|
||||
*
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\column_base', 'question_bank_column_base', true);
|
||||
class_alias('core_question\local\bank\column_base', 'question_bank_column_base', true);
|
||||
|
||||
/**
|
||||
* A column with a checkbox for each question with name q{questionid}.
|
||||
@ -154,8 +155,9 @@ class_alias('core_question\bank\column_base', 'question_bank_column_base', true)
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\checkbox_column', 'question_bank_checkbox_column', true);
|
||||
class_alias('core_question\local\bank\checkbox_column', 'question_bank_checkbox_column', true);
|
||||
|
||||
/**
|
||||
* A column type for the name of the question type.
|
||||
@ -163,6 +165,7 @@ class_alias('core_question\bank\checkbox_column', 'question_bank_checkbox_column
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\question_type_column', 'question_bank_question_type_column', true);
|
||||
|
||||
@ -173,6 +176,7 @@ class_alias('core_question\bank\question_type_column', 'question_bank_question_t
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\question_name_column', 'question_bank_question_name_column', true);
|
||||
|
||||
@ -183,6 +187,7 @@ class_alias('core_question\bank\question_name_column', 'question_bank_question_n
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\creator_name_column', 'question_bank_creator_name_column', true);
|
||||
|
||||
@ -193,6 +198,7 @@ class_alias('core_question\bank\creator_name_column', 'question_bank_creator_nam
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\modifier_name_column', 'question_bank_modifier_name_column', true);
|
||||
|
||||
@ -203,8 +209,9 @@ class_alias('core_question\bank\modifier_name_column', 'question_bank_modifier_n
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\action_column_base', 'question_bank_action_column_base', true);
|
||||
class_alias('core_question\local\bank\action_column_base', 'question_bank_action_column_base', true);
|
||||
|
||||
|
||||
/**
|
||||
@ -213,6 +220,7 @@ class_alias('core_question\bank\action_column_base', 'question_bank_action_colum
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\edit_action_column', 'question_bank_edit_action_column', true);
|
||||
|
||||
@ -222,6 +230,7 @@ class_alias('core_question\bank\edit_action_column', 'question_bank_edit_action_
|
||||
* @copyright 2013 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\copy_action_column', 'question_bank_copy_action_column', true);
|
||||
|
||||
@ -231,6 +240,7 @@ class_alias('core_question\bank\copy_action_column', 'question_bank_copy_action_
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\preview_action_column', 'question_bank_preview_action_column', true);
|
||||
|
||||
@ -241,6 +251,7 @@ class_alias('core_question\bank\preview_action_column', 'question_bank_preview_a
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\delete_action_column', 'question_bank_delete_action_column', true);
|
||||
|
||||
@ -250,8 +261,9 @@ class_alias('core_question\bank\delete_action_column', 'question_bank_delete_act
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\row_base', 'question_bank_row_base', true);
|
||||
class_alias('core_question\local\bank\row_base', 'question_bank_row_base', true);
|
||||
|
||||
/**
|
||||
* A column type for the name of the question name.
|
||||
@ -259,6 +271,7 @@ class_alias('core_question\bank\row_base', 'question_bank_row_base', true);
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\question_text_row', 'question_bank_question_text_row', true);
|
||||
|
||||
@ -266,8 +279,9 @@ class_alias('core_question\bank\question_text_row', 'question_bank_question_text
|
||||
* @copyright 2009 Tim Hunt
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @deprecated since Moodle 2.7 MDL-40457
|
||||
* @todo MDl-72004 delete the class alias, not done in MDL-71516 for any potential error from other plugins.
|
||||
*/
|
||||
class_alias('core_question\bank\view', 'question_bank_view', true);
|
||||
class_alias('core_question\local\bank\view', 'question_bank_view', true);
|
||||
|
||||
/**
|
||||
* Common setup for all pages for editing questions.
|
||||
|
@ -87,7 +87,7 @@ if ($previewid) {
|
||||
// actually from the user point of view, it makes sense.
|
||||
print_error('submissionoutofsequencefriendlymessage', 'question',
|
||||
question_preview_url($question->id, $options->behaviour,
|
||||
$options->maxmark, $options, $options->variant, $context), null, $e);
|
||||
$options->maxmark, $options, $options->variant, $context), null, $e);
|
||||
}
|
||||
|
||||
if ($quba->get_owning_context()->instanceid != $USER->id) {
|
||||
@ -255,15 +255,15 @@ echo $quba->render_question($slot, $options, $displaynumber);
|
||||
// Finish the question form.
|
||||
echo html_writer::start_tag('div', array('id' => 'previewcontrols', 'class' => 'controls'));
|
||||
echo html_writer::empty_tag('input', $restartdisabled + array('type' => 'submit',
|
||||
'name' => 'restart', 'value' => get_string('restart', 'question'), 'class' => 'btn btn-secondary'));
|
||||
'name' => 'restart', 'value' => get_string('restart', 'question'), 'class' => 'btn btn-secondary'));
|
||||
echo html_writer::empty_tag('input', $finishdisabled + array('type' => 'submit',
|
||||
'name' => 'save', 'value' => get_string('save', 'question'), 'class' => 'btn btn-secondary',
|
||||
'id' => 'id_save_question_preview'));
|
||||
'name' => 'save', 'value' => get_string('save', 'question'), 'class' => 'btn btn-secondary',
|
||||
'id' => 'id_save_question_preview'));
|
||||
echo html_writer::empty_tag('input', $filldisabled + array('type' => 'submit',
|
||||
'name' => 'fill', 'value' => get_string('fillincorrect', 'question'), 'class' => 'btn btn-secondary'));
|
||||
'name' => 'fill', 'value' => get_string('fillincorrect', 'question'), 'class' => 'btn btn-secondary'));
|
||||
echo html_writer::empty_tag('input', $finishdisabled + array('type' => 'submit',
|
||||
'name' => 'finish', 'value' => get_string('submitandfinish', 'question'), 'class' => 'btn btn-secondary',
|
||||
'id' => 'id_finish_question_preview'));
|
||||
'name' => 'finish', 'value' => get_string('submitandfinish', 'question'), 'class' => 'btn btn-secondary',
|
||||
'id' => 'id_finish_question_preview'));
|
||||
echo html_writer::end_tag('div');
|
||||
echo html_writer::end_tag('form');
|
||||
|
||||
@ -277,8 +277,17 @@ print_collapsible_region_end();
|
||||
|
||||
// Output a link to export this single question.
|
||||
if (question_has_capability_on($question, 'view')) {
|
||||
echo html_writer::link(question_get_export_single_question_url($question),
|
||||
get_string('exportonequestion', 'question'));
|
||||
if (class_exists('qbank_exporttoxml\\exporttoxml_helper')) {
|
||||
if (\core\plugininfo\qbank::is_plugin_enabled('qbank_exporttoxml')) {
|
||||
$exportfunction = '\\qbank_exporttoxml\\exporttoxml_helper::question_get_export_single_question_url';
|
||||
echo html_writer::link($exportfunction($question),
|
||||
get_string('exportonequestion', 'question'));
|
||||
}
|
||||
} else {
|
||||
$exportfunction = 'question_get_export_single_question_url';
|
||||
echo html_writer::link($exportfunction($question),
|
||||
get_string('exportonequestion', 'question'));
|
||||
}
|
||||
}
|
||||
|
||||
// Log the preview of this question.
|
||||
@ -290,10 +299,9 @@ $optionsform->display();
|
||||
|
||||
$PAGE->requires->js_module('core_question_engine');
|
||||
$PAGE->requires->strings_for_js(array(
|
||||
'closepreview',
|
||||
'closepreview',
|
||||
), 'question');
|
||||
$PAGE->requires->yui_module('moodle-question-preview', 'M.question.preview.init');
|
||||
$PAGE->requires->js_call_amd('core_form/submit', 'init', ['id_save_question_preview']);
|
||||
$PAGE->requires->js_call_amd('core_form/submit', 'init', ['id_finish_question_preview']);
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
|
@ -17,8 +17,7 @@
|
||||
/**
|
||||
* Renderers for outputting parts of the question bank.
|
||||
*
|
||||
* @package moodlecore
|
||||
* @subpackage questionbank
|
||||
* @package core_question
|
||||
* @copyright 2011 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
@ -38,9 +37,10 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
/**
|
||||
* Display additional navigation if needed.
|
||||
*
|
||||
* @param string $active
|
||||
* @return string
|
||||
*/
|
||||
public function extra_horizontal_navigation() {
|
||||
public function extra_horizontal_navigation($active = null) {
|
||||
// Horizontal navigation for question bank.
|
||||
if ($questionnode = $this->page->settingsnav->find("questionbank", \navigation_node::TYPE_CONTAINER)) {
|
||||
if ($children = $questionnode->children) {
|
||||
@ -48,8 +48,11 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
foreach ($children as $key => $node) {
|
||||
$tabs[] = new \tabobject($node->key, $node->action, $node->text);
|
||||
}
|
||||
$active = $questionnode->find_active_node()->key;
|
||||
return \html_writer::div(print_tabs([$tabs], $active, null, null, true), 'questionbank-navigation');
|
||||
if (empty($active)) {
|
||||
$active = $questionnode->find_active_node()->key;
|
||||
}
|
||||
return \html_writer::div(print_tabs([$tabs], $active, null, null, true),
|
||||
'questionbank-navigation');
|
||||
}
|
||||
}
|
||||
return '';
|
||||
@ -68,6 +71,26 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
return $this->image_icon('icon', $namestr, $qtype->plugin_name(), array('title' => $namestr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the column headers.
|
||||
*
|
||||
* @param array $qbankheaderdata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_column_header($qbankheaderdata) {
|
||||
return $this->render_from_template('core_question/column_header', $qbankheaderdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the column sort elements.
|
||||
*
|
||||
* @param array $sortdata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_column_sort($sortdata) {
|
||||
return $this->render_from_template('core_question/column_sort', $sortdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a qbank_chooser.
|
||||
*
|
||||
@ -78,6 +101,56 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
return $this->render_from_template('core_question/qbank_chooser', $qbankchooser->export_for_template($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Render category condition.
|
||||
*
|
||||
* @param array $displaydata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_category_condition($displaydata) {
|
||||
return $this->render_from_template('core_question/category_condition', $displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render category condition advanced.
|
||||
*
|
||||
* @param array $displaydata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_category_condition_advanced($displaydata) {
|
||||
return $this->render_from_template('core_question/category_condition_advanced', $displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render hidden condition advanced.
|
||||
*
|
||||
* @param array $displaydata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_hidden_condition_advanced($displaydata) {
|
||||
return $this->render_from_template('core_question/hidden_condition_advanced', $displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render question pagination.
|
||||
*
|
||||
* @param array $displaydata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_question_pagination($displaydata) {
|
||||
return $this->render_from_template('core_question/question_pagination', $displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render question showtext checkbox.
|
||||
*
|
||||
* @param array $displaydata
|
||||
* @return bool|string
|
||||
*/
|
||||
public function render_showtext_checkbox($displaydata) {
|
||||
return $this->render_from_template('core_question/showtext_checkbox', $displaydata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the HTML for the question chooser javascript popup.
|
||||
*
|
||||
@ -89,7 +162,7 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
public function qbank_chooser($real, $fake, $course, $hiddenparams) {
|
||||
debugging('Method core_question_bank_renderer::qbank_chooser() is deprecated, ' .
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -101,7 +174,7 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
protected function qbank_chooser_types($types) {
|
||||
debugging('Method core_question_bank_renderer::qbank_chooser_types() is deprecated, ' .
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -115,7 +188,7 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
protected function qbank_chooser_qtype($qtype, $classes = array()) {
|
||||
debugging('Method core_question_bank_renderer::qbank_chooser_qtype() is deprecated, ' .
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -128,7 +201,8 @@ class core_question_bank_renderer extends plugin_renderer_base {
|
||||
*/
|
||||
protected function qbank_chooser_title($title, $identifier = null) {
|
||||
debugging('Method core_question_bank_renderer::qbank_chooser_title() is deprecated, ' .
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
'see core_question_bank_renderer::render_qbank_chooser().', DEBUG_DEVELOPER);
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
38
question/templates/category_condition.mustache
Normal file
38
question/templates/category_condition.mustache
Normal file
@ -0,0 +1,38 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/category_condition
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"displaydata": [
|
||||
{
|
||||
"categoryselect": "html string",
|
||||
"categorydesc": "html string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="choosecategory">
|
||||
<label class="mr-1" for="id_selectacategory">
|
||||
{{#str}} selectacategory, question{{/str}}
|
||||
</label>
|
||||
{{{categoryselect}}}
|
||||
</div>
|
||||
<div class="boxaligncenter categoryinfo pl-0">
|
||||
{{{categorydesc}}}
|
||||
</div>
|
33
question/templates/category_condition_advanced.mustache
Normal file
33
question/templates/category_condition_advanced.mustache
Normal file
@ -0,0 +1,33 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/category_condition_advanced
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"displaydata": [
|
||||
{
|
||||
"checked": "checked attribute"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="category_condition_advanced">
|
||||
<input type="hidden" name="recurse" value="0" id="recurse_off">
|
||||
<input id="recurse_on" class="searchoptions mr-1" type="checkbox" value="1" name="recurse" {{{checked}}}>
|
||||
<label for="recurse_on">{{#str}} includesubcategories, question{{/str}}</label>
|
||||
</div>
|
52
question/templates/column_header.mustache
Normal file
52
question/templates/column_header.mustache
Normal file
@ -0,0 +1,52 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/column_header
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"qbankheaderdata": [
|
||||
{
|
||||
"extraclasses": "checkbox",
|
||||
"sortable": false,
|
||||
"tiptitle": "Element title",
|
||||
"sorttip": true,
|
||||
"tip": "Select questions for bulk actions",
|
||||
"sortlinks": "sort / sort"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<th class="header {{extraclasses}}" scope="col">
|
||||
{{#title}}
|
||||
<div class="title">{{title}}</div>
|
||||
{{/title}}
|
||||
{{^sortable}}
|
||||
{{^sorttip}}
|
||||
{{tiptitle}}
|
||||
{{/sorttip}}
|
||||
{{#sorttip}}
|
||||
<span title="{{tip}}">{{{tiptitle}}}</span>
|
||||
{{/sorttip}}
|
||||
{{/sortable}}
|
||||
{{#sortable}}
|
||||
<div class="sorters">
|
||||
{{{sortlinks}}}
|
||||
</div>
|
||||
{{/sortable}}
|
||||
</th>
|
||||
|
34
question/templates/column_sort.mustache
Normal file
34
question/templates/column_sort.mustache
Normal file
@ -0,0 +1,34 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/column_sort
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"sortdata": [
|
||||
{
|
||||
"sorturl": "url",
|
||||
"sorttip": "Sort according to first name",
|
||||
"sortcontent": "Sort title sort icon"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<a href="{{{sorturl}}}" title="{{sorttip}}">
|
||||
{{{sortcontent}}}
|
||||
</a>
|
||||
|
33
question/templates/hidden_condition_advanced.mustache
Normal file
33
question/templates/hidden_condition_advanced.mustache
Normal file
@ -0,0 +1,33 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/hidden_condition_advanced
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"displaydata": [
|
||||
{
|
||||
"checked": "checked attribute"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="hidden_condition_advanced">
|
||||
<input type="hidden" name="showhidden" value="0" id="showhidden_off">
|
||||
<input id="showhidden_on" class="searchoptions mr-1" type="checkbox" value="1" name="showhidden" {{{checked}}}>
|
||||
<label for="showhidden_on">{{#str}} showhidden, question{{/str}}</label>
|
||||
</div>
|
48
question/templates/question_pagination.mustache
Normal file
48
question/templates/question_pagination.mustache
Normal file
@ -0,0 +1,48 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/question_pagination
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"sortdata": [
|
||||
{
|
||||
"extraclasses": "classes",
|
||||
"pagination": "pagination html",
|
||||
"showall": "true/false",
|
||||
"showallurl": "moodle url",
|
||||
"biggertotal": "true/false",
|
||||
"totalnumber": "number of questions"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div class="categorypagingbarcontainer {{extraclasses}}">
|
||||
{{{pagination}}}
|
||||
</div>
|
||||
{{#showall}}
|
||||
<div class="question-showall-text">
|
||||
<a href="{{{showallurl}}}">
|
||||
{{^biggertotal}}
|
||||
{{#str}} showall, moodle, {{totalnumber}} {{/str}}
|
||||
{{/biggertotal}}
|
||||
{{#biggertotal}}
|
||||
{{#str}} showperpage, moodle, {{totalnumber}} {{/str}}
|
||||
{{/biggertotal}}
|
||||
</a>
|
||||
</div>
|
||||
{{/showall}}
|
33
question/templates/showtext_checkbox.mustache
Normal file
33
question/templates/showtext_checkbox.mustache
Normal file
@ -0,0 +1,33 @@
|
||||
{{!
|
||||
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/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_question/showtext_checkbox
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"sortdata": [
|
||||
{
|
||||
"checked": "true/false"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
<div>
|
||||
<input type="hidden" name="qbshowtext" value="0" id="qbshowtext_off">
|
||||
<input id="qbshowtext_on" class="searchoptions mr-1" type="checkbox" value="1" name="qbshowtext" {{#checked}} checked="checked" {{/checked}}>
|
||||
<label for="qbshowtext_on">{{#str}} showquestiontext, question {{/str}}</label>
|
||||
</div>
|
@ -107,16 +107,16 @@ class core_question_backup_testcase extends advanced_testcase {
|
||||
|
||||
// Create a new course category and and a new course in that.
|
||||
$category1 = $this->getDataGenerator()->create_category();
|
||||
$course = $this->getDataGenerator()->create_course(array('category' => $category1->id));
|
||||
$course = $this->getDataGenerator()->create_course(['category' => $category1->id]);
|
||||
$courseshortname = $course->shortname;
|
||||
$coursefullname = $course->fullname;
|
||||
|
||||
// Create 2 questions.
|
||||
$qgen = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
$context = context_coursecat::instance($category1->id);
|
||||
$qcat = $qgen->create_question_category(array('contextid' => $context->id));
|
||||
$question1 = $qgen->create_question('shortanswer', null, array('category' => $qcat->id, 'idnumber' => 'q1'));
|
||||
$question2 = $qgen->create_question('shortanswer', null, array('category' => $qcat->id, 'idnumber' => 'q2'));
|
||||
$qcat = $qgen->create_question_category(['contextid' => $context->id]);
|
||||
$question1 = $qgen->create_question('shortanswer', null, ['category' => $qcat->id, 'idnumber' => 'q1']);
|
||||
$question2 = $qgen->create_question('shortanswer', null, ['category' => $qcat->id, 'idnumber' => 'q2']);
|
||||
|
||||
// Tag the questions with 2 question tags and 2 course level question tags.
|
||||
$qcontext = context::instance_by_id($qcat->contextid);
|
||||
@ -127,7 +127,7 @@ class core_question_backup_testcase extends advanced_testcase {
|
||||
core_tag_tag::set_item_tags('core_question', 'question', $question2->id, $coursecontext, ['ctag3', 'ctag4']);
|
||||
|
||||
// Create a quiz and add one of the questions to that.
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
|
||||
quiz_add_quiz_question($question1->id, $quiz);
|
||||
|
||||
// Backup the course twice for future use.
|
||||
@ -144,7 +144,7 @@ class core_question_backup_testcase extends advanced_testcase {
|
||||
|
||||
// The questions should remain in the question category they were which is
|
||||
// a question category belonging to a course category context.
|
||||
$questions = $DB->get_records('question', array('category' => $qcat->id), 'idnumber');
|
||||
$questions = $DB->get_records('question', ['category' => $qcat->id], 'idnumber');
|
||||
$this->assertCount(2, $questions);
|
||||
|
||||
// Retrieve tags for each question and check if they are assigned at the right context.
|
||||
@ -179,10 +179,10 @@ class core_question_backup_testcase extends advanced_testcase {
|
||||
// Create a new course category to restore the backup file into it.
|
||||
$category2 = $this->getDataGenerator()->create_category();
|
||||
|
||||
$expectedwarnings = array(
|
||||
$expectedwarnings = [
|
||||
get_string('qcategory2coursefallback', 'backup', (object) ['name' => 'top']),
|
||||
get_string('qcategory2coursefallback', 'backup', (object) ['name' => $qcat->name])
|
||||
);
|
||||
];
|
||||
|
||||
// Restore to a new course in the new course category.
|
||||
$courseid3 = $this->restore_course($backupid2, $coursefullname, $courseshortname . '_3', $category2->id, $expectedwarnings);
|
||||
@ -192,7 +192,7 @@ class core_question_backup_testcase extends advanced_testcase {
|
||||
$questions = $DB->get_records_sql("SELECT q.*
|
||||
FROM {question} q
|
||||
JOIN {question_categories} qc ON q.category = qc.id
|
||||
WHERE qc.contextid = ?", array($coursecontext3->id));
|
||||
WHERE qc.contextid = ?", [$coursecontext3->id]);
|
||||
$this->assertCount(2, $questions);
|
||||
|
||||
// Now, retrieve tags for each question and check if they are assigned at the right context.
|
||||
|
@ -59,9 +59,18 @@ class core_question_bank_view_testcase extends advanced_testcase {
|
||||
$cache->delete($questiondata->id);
|
||||
|
||||
// Generate the view.
|
||||
$view = new core_question\bank\view($contexts, new moodle_url('/'), $course);
|
||||
$view = new core_question\local\bank\view($contexts, new moodle_url('/'), $course);
|
||||
ob_start();
|
||||
$view->display('editq', 0, 20, $cat->id . ',' . $context->id, false, false, false);
|
||||
$pagevars = [
|
||||
'qpage' => 0,
|
||||
'qperpage' => 20,
|
||||
'cat' => $cat->id . ',' . $context->id,
|
||||
'recurse' => false,
|
||||
'showhidden' => false,
|
||||
'qbshowtext' => false
|
||||
|
||||
];
|
||||
$view->display($pagevars, 'editq');
|
||||
$html = ob_get_clean();
|
||||
|
||||
// Verify the output includes the expected question.
|
||||
@ -91,9 +100,18 @@ class core_question_bank_view_testcase extends advanced_testcase {
|
||||
$DB->set_field('question', 'qtype', 'unknownqtype', ['id' => $questiondata->id]);
|
||||
|
||||
// Generate the view.
|
||||
$view = new core_question\bank\view($contexts, new moodle_url('/'), $course);
|
||||
$view = new core_question\local\bank\view($contexts, new moodle_url('/'), $course);
|
||||
ob_start();
|
||||
$view->display('editq', 0, 20, $cat->id . ',' . $context->id, false, false, false);
|
||||
$pagevars = [
|
||||
'qpage' => 0,
|
||||
'qperpage' => 20,
|
||||
'cat' => $cat->id . ',' . $context->id,
|
||||
'recurse' => false,
|
||||
'showhidden' => false,
|
||||
'qbshowtext' => false
|
||||
|
||||
];
|
||||
$view->display($pagevars, 'editq');
|
||||
$html = ob_get_clean();
|
||||
|
||||
// Mainly we are verifying that there was no fatal error.
|
||||
|
@ -50,8 +50,8 @@ class behat_question_base extends behat_base {
|
||||
*/
|
||||
protected function finish_adding_question($questiontypename, TableNode $questiondata) {
|
||||
|
||||
$this->execute('behat_forms::i_set_the_field_to', array($this->escape($questiontypename), 1));
|
||||
$this->execute("behat_general::i_click_on", array('.submitbutton', "css_element"));
|
||||
$this->execute('behat_forms::i_set_the_field_to', [$this->escape($questiontypename), 1]);
|
||||
$this->execute("behat_general::i_click_on", ['.submitbutton', "css_element"]);
|
||||
|
||||
$this->execute("behat_forms::i_set_the_following_fields_to_these_values", $questiondata);
|
||||
$this->execute("behat_forms::press_button", 'id_submitbutton');
|
||||
|
@ -44,16 +44,16 @@ class core_question_events_testcase extends advanced_testcase {
|
||||
public function test_question_category_created() {
|
||||
$this->setAdminUser();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course->id));
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course->id]);
|
||||
|
||||
$contexts = new question_edit_contexts(context_module::instance($quiz->cmid));
|
||||
|
||||
$defaultcategoryobj = question_make_default_categories(array($contexts->lowest()));
|
||||
$defaultcategoryobj = question_make_default_categories([$contexts->lowest()]);
|
||||
$defaultcategory = $defaultcategoryobj->id . ',' . $defaultcategoryobj->contextid;
|
||||
|
||||
$qcobject = new question_category_object(
|
||||
1,
|
||||
new moodle_url('/mod/quiz/edit.php', array('cmid' => $quiz->cmid)),
|
||||
new moodle_url('/mod/quiz/edit.php', ['cmid' => $quiz->cmid]),
|
||||
$contexts->having_one_edit_tab_cap('categories'),
|
||||
$defaultcategoryobj->id,
|
||||
$defaultcategory,
|
||||
@ -69,7 +69,7 @@ class core_question_events_testcase extends advanced_testcase {
|
||||
// Check that the event data is valid.
|
||||
$this->assertInstanceOf('\core\event\question_category_created', $event);
|
||||
$this->assertEquals(context_module::instance($quiz->cmid), $event->get_context());
|
||||
$expected = array($course->id, 'quiz', 'addcategory', 'view.php?id=' . $quiz->cmid , $categoryid, $quiz->cmid);
|
||||
$expected = [$course->id, 'quiz', 'addcategory', 'view.php?id=' . $quiz->cmid , $categoryid, $quiz->cmid];
|
||||
$this->assertEventLegacyLogData($expected, $event);
|
||||
$this->assertEventContextNotUsed($event);
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ class core_question_external_testcase extends externallib_advanced_testcase {
|
||||
$this->student = self::getDataGenerator()->create_user();
|
||||
|
||||
// Users enrolments.
|
||||
$this->studentrole = $DB->get_record('role', array('shortname' => 'student'));
|
||||
$this->studentrole = $DB->get_record('role', ['shortname' => 'student']);
|
||||
$this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual');
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ class core_question_external_testcase extends externallib_advanced_testcase {
|
||||
|
||||
$quba = question_engine::make_questions_usage_by_activity('core_question_update_flag', context_system::instance());
|
||||
$quba->set_preferred_behaviour('deferredfeedback');
|
||||
$questiondata = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||
$questiondata = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
|
||||
$question = question_bank::load_question($questiondata->id);
|
||||
$slot = $quba->add_question($question);
|
||||
$qa = $quba->get_question_attempt($slot);
|
||||
|
@ -31,7 +31,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
* @copyright 2018 Huong Nguyen <huongnv13@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class testable_core_question_column extends \core_question\bank\column_base {
|
||||
class testable_core_question_column extends \core_question\local\bank\column_base {
|
||||
|
||||
/** @var array sortable columns. */
|
||||
private $sortable = [];
|
||||
|
@ -45,14 +45,14 @@ class core_question_generator extends component_generator_base {
|
||||
|
||||
$this->categorycount++;
|
||||
|
||||
$defaults = array(
|
||||
$defaults = [
|
||||
'name' => 'Test question category ' . $this->categorycount,
|
||||
'info' => '',
|
||||
'infoformat' => FORMAT_HTML,
|
||||
'stamp' => make_unique_id_code(),
|
||||
'sortorder' => 999,
|
||||
'idnumber' => null
|
||||
);
|
||||
];
|
||||
|
||||
$record = $this->datagenerator->combine_defaults_and_record($defaults, $record);
|
||||
|
||||
@ -175,12 +175,12 @@ class core_question_generator extends component_generator_base {
|
||||
|
||||
$qcat = $this->create_question_category(['contextid' => $context->id]);
|
||||
|
||||
$questions = array(
|
||||
$questions = [
|
||||
$this->create_question('shortanswer', null, ['category' => $qcat->id]),
|
||||
$this->create_question('shortanswer', null, ['category' => $qcat->id]),
|
||||
);
|
||||
];
|
||||
|
||||
return array($category, $course, $qcat, $questions);
|
||||
return [$category, $course, $qcat, $questions];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,8 +46,7 @@ class core_question_generator_testcase extends advanced_testcase {
|
||||
// creates a Top category as well.
|
||||
$this->assertEquals($count, $DB->count_records('question_categories'));
|
||||
|
||||
$cat = $generator->create_question_category(array(
|
||||
'name' => 'My category', 'sortorder' => 1));
|
||||
$cat = $generator->create_question_category(['name' => 'My category', 'sortorder' => 1]);
|
||||
$this->assertSame('My category', $cat->name);
|
||||
$this->assertSame(1, $cat->sortorder);
|
||||
}
|
||||
@ -61,8 +60,8 @@ class core_question_generator_testcase extends advanced_testcase {
|
||||
$this->assertNull($questions[0]->idnumber);
|
||||
$this->assertNull($questions[1]->idnumber);
|
||||
// Check created idnumbers.
|
||||
$qcat1 = $generator->create_question_category(array(
|
||||
'name' => 'My category', 'sortorder' => 1, 'idnumber' => 'myqcat'));
|
||||
$qcat1 = $generator->create_question_category([
|
||||
'name' => 'My category', 'sortorder' => 1, 'idnumber' => 'myqcat']);
|
||||
$this->assertSame('myqcat', $qcat1->idnumber);
|
||||
$quest1 = $generator->update_question($questions[0], null, ['idnumber' => 'myquest']);
|
||||
$this->assertSame('myquest', $quest1->idnumber);
|
||||
|
@ -57,14 +57,14 @@ class testable_qformat extends qformat_default {
|
||||
class qformat_default_test extends advanced_testcase {
|
||||
public function test_assemble_category_path() {
|
||||
$format = new testable_qformat();
|
||||
$pathsections = array(
|
||||
$pathsections = [
|
||||
'$course$',
|
||||
"Tim's questions",
|
||||
"Tricky things like / // and so on",
|
||||
'Category name ending in /',
|
||||
'/ and one that starts with one',
|
||||
'<span lang="en" class="multilang">Matematically</span> <span lang="sv" class="multilang">Matematiskt (svenska)</span>'
|
||||
);
|
||||
];
|
||||
$this->assertEquals('$course$/Tim\'s questions/Tricky things like // //// and so on/Category name ending in // / // and one that starts with one/<span lang="en" class="multilang">Matematically<//span> <span lang="sv" class="multilang">Matematiskt (svenska)<//span>',
|
||||
$format->assemble_category_path($pathsections));
|
||||
}
|
||||
@ -72,20 +72,20 @@ class qformat_default_test extends advanced_testcase {
|
||||
public function test_split_category_path() {
|
||||
$format = new testable_qformat();
|
||||
$path = '$course$/Tim\'s questions/Tricky things like // //// and so on/Category name ending in // / // and one that starts with one/<span lang="en" class="multilang">Matematically<//span> <span lang="sv" class="multilang">Matematiskt (svenska)<//span>';
|
||||
$this->assertEquals(array(
|
||||
$this->assertEquals([
|
||||
'$course$',
|
||||
"Tim's questions",
|
||||
"Tricky things like / // and so on",
|
||||
'Category name ending in /',
|
||||
'/ and one that starts with one',
|
||||
'<span lang="en" class="multilang">Matematically</span> <span lang="sv" class="multilang">Matematiskt (svenska)</span>'
|
||||
), $format->split_category_path($path));
|
||||
], $format->split_category_path($path));
|
||||
}
|
||||
|
||||
public function test_split_category_path_cleans() {
|
||||
$format = new testable_qformat();
|
||||
$path = '<evil>Nasty <virus //> thing<//evil>';
|
||||
$this->assertEquals(array('Nasty thing'), $format->split_category_path($path));
|
||||
$this->assertEquals(['Nasty thing'], $format->split_category_path($path));
|
||||
}
|
||||
|
||||
public function test_clean_question_name() {
|
||||
|
@ -42,7 +42,7 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
$quba->set_preferred_behaviour('deferredfeedback');
|
||||
$slot = $quba->add_question($question);
|
||||
$quba->start_all_questions(new core_question\engine\variants\least_used_strategy(
|
||||
$quba, new qubaid_list(array())));
|
||||
$quba, new qubaid_list([])));
|
||||
$this->assertEquals(1, $quba->get_variant($slot));
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
$slot1 = $quba->add_question($question);
|
||||
$slot2 = $quba->add_question($question);
|
||||
$quba->start_all_questions(new core_question\engine\variants\least_used_strategy(
|
||||
$quba, new qubaid_list(array())));
|
||||
$quba, new qubaid_list([])));
|
||||
$this->assertEquals($quba->get_variant($slot1), $quba->get_variant($slot2));
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$questiondata = $generator->create_question('calculated', null, array('category' => $cat->id));
|
||||
$questiondata = $generator->create_question('calculated', null, ['category' => $cat->id]);
|
||||
|
||||
// Create two dataset items.
|
||||
$adefinitionid = $DB->get_field_sql("
|
||||
@ -72,23 +72,23 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
FROM {question_dataset_definitions} qdd
|
||||
JOIN {question_datasets} qd ON qd.datasetdefinition = qdd.id
|
||||
WHERE qd.question = ?
|
||||
AND qdd.name = ?", array($questiondata->id, 'a'));
|
||||
AND qdd.name = ?", [$questiondata->id, 'a']);
|
||||
$bdefinitionid = $DB->get_field_sql("
|
||||
SELECT qdd.id
|
||||
FROM {question_dataset_definitions} qdd
|
||||
JOIN {question_datasets} qd ON qd.datasetdefinition = qdd.id
|
||||
WHERE qd.question = ?
|
||||
AND qdd.name = ?", array($questiondata->id, 'b'));
|
||||
$DB->set_field('question_dataset_definitions', 'itemcount', 2, array('id' => $adefinitionid));
|
||||
$DB->set_field('question_dataset_definitions', 'itemcount', 2, array('id' => $bdefinitionid));
|
||||
$DB->insert_record('question_dataset_items', array('definition' => $adefinitionid,
|
||||
'itemnumber' => 1, 'value' => 3));
|
||||
$DB->insert_record('question_dataset_items', array('definition' => $bdefinitionid,
|
||||
'itemnumber' => 1, 'value' => 7));
|
||||
$DB->insert_record('question_dataset_items', array('definition' => $adefinitionid,
|
||||
'itemnumber' => 2, 'value' => 6));
|
||||
$DB->insert_record('question_dataset_items', array('definition' => $bdefinitionid,
|
||||
'itemnumber' => 2, 'value' => 4));
|
||||
AND qdd.name = ?", [$questiondata->id, 'b']);
|
||||
$DB->set_field('question_dataset_definitions', 'itemcount', 2, ['id' => $adefinitionid]);
|
||||
$DB->set_field('question_dataset_definitions', 'itemcount', 2, ['id' => $bdefinitionid]);
|
||||
$DB->insert_record('question_dataset_items', ['definition' => $adefinitionid,
|
||||
'itemnumber' => 1, 'value' => 3]);
|
||||
$DB->insert_record('question_dataset_items', ['definition' => $bdefinitionid,
|
||||
'itemnumber' => 1, 'value' => 7]);
|
||||
$DB->insert_record('question_dataset_items', ['definition' => $adefinitionid,
|
||||
'itemnumber' => 2, 'value' => 6]);
|
||||
$DB->insert_record('question_dataset_items', ['definition' => $bdefinitionid,
|
||||
'itemnumber' => 2, 'value' => 4]);
|
||||
|
||||
$question = question_bank::load_question($questiondata->id);
|
||||
|
||||
@ -96,7 +96,7 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
$quba1->set_preferred_behaviour('deferredfeedback');
|
||||
$slot1 = $quba1->add_question($question);
|
||||
$quba1->start_all_questions(new core_question\engine\variants\least_used_strategy(
|
||||
$quba1, new qubaid_list(array())));
|
||||
$quba1, new qubaid_list([])));
|
||||
question_engine::save_questions_usage_by_activity($quba1);
|
||||
$variant1 = $quba1->get_variant($slot1);
|
||||
|
||||
@ -105,7 +105,7 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
$quba2->set_preferred_behaviour('deferredfeedback');
|
||||
$slot2 = $quba2->add_question($question);
|
||||
$quba2->start_all_questions(new core_question\engine\variants\least_used_strategy(
|
||||
$quba1, new qubaid_list(array($quba1->get_id()))));
|
||||
$quba1, new qubaid_list([$quba1->get_id()])));
|
||||
question_engine::save_questions_usage_by_activity($quba2);
|
||||
$variant2 = $quba2->get_variant($slot2);
|
||||
|
||||
@ -116,7 +116,7 @@ class least_used_variant_strategy_testcase extends advanced_testcase {
|
||||
$quba3->set_preferred_behaviour('deferredfeedback');
|
||||
$slot3 = $quba3->add_question($question);
|
||||
$quba3->start_all_questions(new core_question\engine\variants\least_used_strategy(
|
||||
$quba1, new qubaid_list(array($quba1->get_id(), $quba2->get_id()))));
|
||||
$quba1, new qubaid_list([$quba1->get_id(), $quba2->get_id()])));
|
||||
$variant3 = $quba3->get_variant($slot3);
|
||||
|
||||
$this->assertTrue($variant3 == $variant1 || $variant3 == $variant2);
|
||||
|
@ -305,17 +305,17 @@ class core_question_privacy_provider_testcase extends \core_privacy\tests\provid
|
||||
// Q4 - Created by the other user, Modified by the other user.
|
||||
// Q5 - Created by the UUT, Modified by the UUT, but in a different context.
|
||||
$this->setUser($user);
|
||||
$q1 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$q2 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$q1 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$q2 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
|
||||
$this->setUser($otheruser);
|
||||
$questiongenerator->update_question($q2);
|
||||
$q3 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$q4 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$q3 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$q4 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
|
||||
$this->setUser($user);
|
||||
$questiongenerator->update_question($q3);
|
||||
$q5 = $questiongenerator->create_question('shortanswer', null, array('category' => $othercat->id));
|
||||
$q5 = $questiongenerator->create_question('shortanswer', null, ['category' => $othercat->id]);
|
||||
|
||||
$approvedcontextlist = new \core_privacy\tests\request\approved_contextlist(
|
||||
$user,
|
||||
|
@ -44,7 +44,7 @@ class question_bank_column_testcase extends advanced_testcase {
|
||||
public function test_column_header_multi_sort_no_tooltips() {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$questionbank = new core_question\bank\view(
|
||||
$questionbank = new core_question\local\bank\view(
|
||||
new question_edit_contexts(context_course::instance($course->id)),
|
||||
new moodle_url('/'),
|
||||
$course
|
||||
@ -67,8 +67,8 @@ class question_bank_column_testcase extends advanced_testcase {
|
||||
$columnbase->display_header();
|
||||
$output = ob_get_clean();
|
||||
|
||||
$this->assertStringContainsString(' title="Sort by Apple ascending">Apple</a>', $output);
|
||||
$this->assertStringContainsString(' title="Sort by Banana ascending">Banana</a>', $output);
|
||||
$this->assertStringContainsString(' title="Sort by Apple ascending">', $output);
|
||||
$this->assertStringContainsString(' title="Sort by Banana ascending">', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,7 +78,7 @@ class question_bank_column_testcase extends advanced_testcase {
|
||||
public function test_column_header_multi_sort_with_tooltips() {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$questionbank = new core_question\bank\view(
|
||||
$questionbank = new core_question\local\bank\view(
|
||||
new question_edit_contexts(context_course::instance($course->id)),
|
||||
new moodle_url('/'),
|
||||
$course
|
||||
@ -103,7 +103,7 @@ class question_bank_column_testcase extends advanced_testcase {
|
||||
$columnbase->display_header();
|
||||
$output = ob_get_clean();
|
||||
|
||||
$this->assertStringContainsString(' title="Sort by Apple Tooltips ascending">Apple</a>', $output);
|
||||
$this->assertStringContainsString(' title="Sort by Banana Tooltips ascending">Banana</a>', $output);
|
||||
$this->assertStringContainsString(' title="Sort by Apple Tooltips ascending">', $output);
|
||||
$this->assertStringContainsString(' title="Sort by Banana Tooltips ascending">', $output);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Tests for the {@link core_question\bank\random_question_loader} class.
|
||||
* Tests for the {@see core_question\local\bank\random_question_loader} class.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2015 The Open University
|
||||
@ -26,7 +26,7 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
|
||||
/**
|
||||
* Tests for the {@link core_question\bank\random_question_loader} class.
|
||||
* Tests for the {@see core_question\local\bank\random_question_loader} class.
|
||||
*
|
||||
* @copyright 2015 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
@ -38,7 +38,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 0));
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 1));
|
||||
@ -46,7 +46,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
|
||||
public function test_unknown_category_behaves_like_empty() {
|
||||
// It is up the caller to make sure the category id is valid.
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
$this->assertNull($loader->get_next_question_id(-1, 1));
|
||||
}
|
||||
|
||||
@ -55,8 +55,8 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$info = $generator->create_question('description', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$info = $generator->create_question('description', null, ['category' => $cat->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 0));
|
||||
}
|
||||
@ -67,9 +67,9 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$DB->set_field('question', 'hidden', 1, array('id' => $question1->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$question1 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$DB->set_field('question', 'hidden', 1, ['id' => $question1->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 0));
|
||||
}
|
||||
@ -79,8 +79,8 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('multianswer', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$question1 = $generator->create_question('multianswer', null, ['category' => $cat->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0));
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 0));
|
||||
@ -93,9 +93,9 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', array('course' => $course));
|
||||
$quiz = $this->getDataGenerator()->create_module('quiz', ['course' => $course]);
|
||||
quiz_add_random_questions($quiz, 1, $cat->id, 1, false);
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 0));
|
||||
}
|
||||
@ -105,8 +105,8 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$question1 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 1));
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 0));
|
||||
@ -117,15 +117,15 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$question1 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$question2 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$questionids = array();
|
||||
$questionids = [];
|
||||
$questionids[] = $loader->get_next_question_id($cat->id, 0);
|
||||
$questionids[] = $loader->get_next_question_id($cat->id, 0);
|
||||
sort($questionids);
|
||||
$this->assertEquals(array($question1->id, $question2->id), $questionids);
|
||||
$this->assertEquals([$question1->id, $question2->id], $questionids);
|
||||
|
||||
$this->assertNull($loader->get_next_question_id($cat->id, 1));
|
||||
}
|
||||
@ -135,10 +135,10 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat1 = $generator->create_question_category();
|
||||
$cat2 = $generator->create_question_category(array('parent' => $cat1->id));
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat1->id));
|
||||
$question2 = $generator->create_question('shortanswer', null, array('category' => $cat2->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$cat2 = $generator->create_question_category(['parent' => $cat1->id]);
|
||||
$question1 = $generator->create_question('shortanswer', null, ['category' => $cat1->id]);
|
||||
$question2 = $generator->create_question('shortanswer', null, ['category' => $cat2->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
|
||||
$this->assertEquals($question2->id, $loader->get_next_question_id($cat2->id, 1));
|
||||
$this->assertEquals($question1->id, $loader->get_next_question_id($cat1->id, 1));
|
||||
@ -151,9 +151,9 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()),
|
||||
$question1 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$question2 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]),
|
||||
array($question2->id => 2));
|
||||
|
||||
$this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0));
|
||||
@ -165,8 +165,8 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$question2 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$question1 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$question2 = $generator->create_question('shortanswer', null, ['category' => $cat->id]);
|
||||
$quba = question_engine::make_questions_usage_by_activity('test', context_system::instance());
|
||||
$quba->set_preferred_behaviour('deferredfeedback');
|
||||
$question = question_bank::load_question($question2->id);
|
||||
@ -175,7 +175,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$quba->start_all_questions();
|
||||
question_engine::save_questions_usage_by_activity($quba);
|
||||
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array($quba->get_id())));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list(array($quba->get_id())));
|
||||
|
||||
$this->assertEquals($question1->id, $loader->get_next_question_id($cat->id, 0));
|
||||
$this->assertEquals($question2->id, $loader->get_next_question_id($cat->id, 0));
|
||||
@ -187,7 +187,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list(array()));
|
||||
|
||||
$this->assertFalse($loader->is_question_available($cat->id, 0, 1));
|
||||
$this->assertFalse($loader->is_question_available($cat->id, 1, 1));
|
||||
@ -199,7 +199,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$info = $generator->create_question('description', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list(array()));
|
||||
|
||||
$this->assertFalse($loader->is_question_available($cat->id, 0, $info->id));
|
||||
$this->assertFalse($loader->is_question_available($cat->id, 1, $info->id));
|
||||
@ -211,7 +211,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
|
||||
$cat = $generator->create_question_category();
|
||||
$question1 = $generator->create_question('shortanswer', null, array('category' => $cat->id));
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list(array()));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list(array()));
|
||||
|
||||
$this->assertTrue($loader->is_question_available($cat->id, 0, $question1->id));
|
||||
$this->assertFalse($loader->is_question_available($cat->id, 0, $question1->id));
|
||||
@ -224,54 +224,54 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function get_questions_test_cases() {
|
||||
return [
|
||||
'empty category' => [
|
||||
'categoryindex' => 'emptycat',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedquestionindexes' => []
|
||||
],
|
||||
'single category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedquestionindexes' => ['cat1q1', 'cat1q2']
|
||||
],
|
||||
'include sub category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => [],
|
||||
'expectedquestionindexes' => ['cat1q1', 'cat1q2', 'subcatq1', 'subcatq2']
|
||||
],
|
||||
'single category with tags' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedquestionindexes' => ['cat1q1']
|
||||
],
|
||||
'include sub category with tag on parent' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedquestionindexes' => ['cat1q1']
|
||||
],
|
||||
'include sub category with tag on sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['subcat'],
|
||||
'expectedquestionindexes' => ['subcatq1']
|
||||
],
|
||||
'include sub category with same tag on parent and sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['foo'],
|
||||
'expectedquestionindexes' => ['cat1q1', 'subcatq1']
|
||||
],
|
||||
'include sub category with tag not matching' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1', 'cat2'],
|
||||
'expectedquestionindexes' => []
|
||||
]
|
||||
'empty category' => [
|
||||
'categoryindex' => 'emptycat',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedquestionindexes' => []
|
||||
],
|
||||
'single category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedquestionindexes' => ['cat1q1', 'cat1q2']
|
||||
],
|
||||
'include sub category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => [],
|
||||
'expectedquestionindexes' => ['cat1q1', 'cat1q2', 'subcatq1', 'subcatq2']
|
||||
],
|
||||
'single category with tags' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedquestionindexes' => ['cat1q1']
|
||||
],
|
||||
'include sub category with tag on parent' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedquestionindexes' => ['cat1q1']
|
||||
],
|
||||
'include sub category with tag on sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['subcat'],
|
||||
'expectedquestionindexes' => ['subcatq1']
|
||||
],
|
||||
'include sub category with same tag on parent and sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['foo'],
|
||||
'expectedquestionindexes' => ['cat1q1', 'subcatq1']
|
||||
],
|
||||
'include sub category with tag not matching' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1', 'cat2'],
|
||||
'expectedquestionindexes' => []
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@ -301,20 +301,20 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
* @param string[] $expectedquestionindexes The questions expected in the result
|
||||
*/
|
||||
public function test_get_questions_variations(
|
||||
$categoryindex,
|
||||
$includesubcategories,
|
||||
$usetagnames,
|
||||
$expectedquestionindexes
|
||||
$categoryindex,
|
||||
$includesubcategories,
|
||||
$usetagnames,
|
||||
$expectedquestionindexes
|
||||
) {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$categories = [];
|
||||
$questions = [];
|
||||
$tagnames = [
|
||||
'cat1',
|
||||
'cat2',
|
||||
'subcat',
|
||||
'foo'
|
||||
'cat1',
|
||||
'cat2',
|
||||
'subcat',
|
||||
'foo'
|
||||
];
|
||||
$collid = core_tag_collection::get_default();
|
||||
$tags = core_tag_tag::create_if_missing($collid, $tagnames);
|
||||
@ -345,7 +345,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
return $tags[$tagname]->id;
|
||||
}, $usetagnames);
|
||||
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
$result = $loader->get_questions($category->id, $includesubcategories, $tagids);
|
||||
// Generate the expected question set.
|
||||
$expectedquestions = array_map(function($index) use ($questions) {
|
||||
@ -370,7 +370,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$tagids = [];
|
||||
$limit = 1;
|
||||
$offset = 0;
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
list($category, $questions) = $this->create_category_and_questions($numberofquestions);
|
||||
|
||||
// Sort the questions by id to match the ordering of the get_questions
|
||||
@ -387,11 +387,11 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
|
||||
for ($i = 0; $i < $numberofquestions; $i++) {
|
||||
$result = $loader->get_questions(
|
||||
$category->id,
|
||||
$includesubcategories,
|
||||
$tagids,
|
||||
$limit,
|
||||
$offset
|
||||
$category->id,
|
||||
$includesubcategories,
|
||||
$tagids,
|
||||
$limit,
|
||||
$offset
|
||||
);
|
||||
|
||||
$this->assertCount($limit, $result);
|
||||
@ -413,16 +413,16 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
$limit = 10;
|
||||
$offset = 0;
|
||||
$fields = ['id', 'name'];
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
list($category, $questions) = $this->create_category_and_questions(1);
|
||||
|
||||
$result = $loader->get_questions(
|
||||
$category->id,
|
||||
$includesubcategories,
|
||||
$tagids,
|
||||
$limit,
|
||||
$offset,
|
||||
$fields
|
||||
$category->id,
|
||||
$includesubcategories,
|
||||
$tagids,
|
||||
$limit,
|
||||
$offset,
|
||||
$fields
|
||||
);
|
||||
|
||||
$expectedquestion = array_shift($questions);
|
||||
@ -440,54 +440,54 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
*/
|
||||
public function count_questions_test_cases() {
|
||||
return [
|
||||
'empty category' => [
|
||||
'categoryindex' => 'emptycat',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedcount' => 0
|
||||
],
|
||||
'single category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedcount' => 2
|
||||
],
|
||||
'include sub category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => [],
|
||||
'expectedcount' => 4
|
||||
],
|
||||
'single category with tags' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedcount' => 1
|
||||
],
|
||||
'include sub category with tag on parent' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedcount' => 1
|
||||
],
|
||||
'include sub category with tag on sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['subcat'],
|
||||
'expectedcount' => 1
|
||||
],
|
||||
'include sub category with same tag on parent and sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['foo'],
|
||||
'expectedcount' => 2
|
||||
],
|
||||
'include sub category with tag not matching' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1', 'cat2'],
|
||||
'expectedcount' => 0
|
||||
]
|
||||
'empty category' => [
|
||||
'categoryindex' => 'emptycat',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedcount' => 0
|
||||
],
|
||||
'single category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => [],
|
||||
'expectedcount' => 2
|
||||
],
|
||||
'include sub category' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => [],
|
||||
'expectedcount' => 4
|
||||
],
|
||||
'single category with tags' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => false,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedcount' => 1
|
||||
],
|
||||
'include sub category with tag on parent' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1'],
|
||||
'expectedcount' => 1
|
||||
],
|
||||
'include sub category with tag on sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['subcat'],
|
||||
'expectedcount' => 1
|
||||
],
|
||||
'include sub category with same tag on parent and sub' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['foo'],
|
||||
'expectedcount' => 2
|
||||
],
|
||||
'include sub category with tag not matching' => [
|
||||
'categoryindex' => 'cat1',
|
||||
'includesubcategories' => true,
|
||||
'usetagnames' => ['cat1', 'cat2'],
|
||||
'expectedcount' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
@ -517,20 +517,20 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
* @param int $expectedcount The number of questions expected in the result
|
||||
*/
|
||||
public function test_count_questions_variations(
|
||||
$categoryindex,
|
||||
$includesubcategories,
|
||||
$usetagnames,
|
||||
$expectedcount
|
||||
$categoryindex,
|
||||
$includesubcategories,
|
||||
$usetagnames,
|
||||
$expectedcount
|
||||
) {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$categories = [];
|
||||
$questions = [];
|
||||
$tagnames = [
|
||||
'cat1',
|
||||
'cat2',
|
||||
'subcat',
|
||||
'foo'
|
||||
'cat1',
|
||||
'cat2',
|
||||
'subcat',
|
||||
'foo'
|
||||
];
|
||||
$collid = core_tag_collection::get_default();
|
||||
$tags = core_tag_tag::create_if_missing($collid, $tagnames);
|
||||
@ -561,7 +561,7 @@ class random_question_loader_testcase extends advanced_testcase {
|
||||
return $tags[$tagname]->id;
|
||||
}, $usetagnames);
|
||||
|
||||
$loader = new \core_question\bank\random_question_loader(new qubaid_list([]));
|
||||
$loader = new \core_question\local\bank\random_question_loader(new qubaid_list([]));
|
||||
$result = $loader->count_questions($category->id, $includesubcategories, $tagids);
|
||||
|
||||
// Ensure the result matches what was expected.
|
||||
|
@ -334,12 +334,20 @@ class question_dataset_dependent_items_form extends question_wizard_form {
|
||||
|
||||
// Submit buttons.
|
||||
if ($this->noofitems > 0) {
|
||||
$buttonarray = array();
|
||||
$buttonarray = [];
|
||||
$buttonarray[] = $mform->createElement(
|
||||
'submit', 'savechanges', get_string('savechanges'));
|
||||
|
||||
$previewlink = $PAGE->get_renderer('core_question')->question_preview_link(
|
||||
// Todo MDL-72004 changes for class renaming and default sort.
|
||||
if (class_exists('qbank_previewquestion\\preview_action_column')) {
|
||||
if (\core\plugininfo\qbank::is_plugin_enabled('qbank_previewquestion')) {
|
||||
$previewlink = $PAGE->get_renderer('qbank_previewquestion')->question_preview_link(
|
||||
$this->question->id, $this->categorycontext, true);
|
||||
}
|
||||
} else {
|
||||
$previewlink = $PAGE->get_renderer('core_question')->question_preview_link(
|
||||
$this->question->id, $this->categorycontext, true);
|
||||
}
|
||||
$buttonarray[] = $mform->createElement('static', 'previewlink', '', $previewlink);
|
||||
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
|
||||
|
@ -233,7 +233,7 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
$a->user = get_string('unknown', 'question');
|
||||
}
|
||||
$mform->addElement('static', 'created', get_string('created', 'question'),
|
||||
get_string('byandon', 'question', $a));
|
||||
get_string('byandon', 'question', $a));
|
||||
if (!empty($this->question->modifiedby)) {
|
||||
$a = new stdClass();
|
||||
$a->time = userdate($this->question->timemodified);
|
||||
@ -254,10 +254,18 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
|
||||
$buttonarray = array();
|
||||
$buttonarray[] = $mform->createElement('submit', 'updatebutton',
|
||||
get_string('savechangesandcontinueediting', 'question'));
|
||||
get_string('savechangesandcontinueediting', 'question'));
|
||||
if ($this->can_preview()) {
|
||||
$previewlink = $PAGE->get_renderer('core_question')->question_preview_link(
|
||||
$this->question->id, $this->context, true);
|
||||
// Todo MDL-72004 changes for class renaming and default sort.
|
||||
if (class_exists('qbank_previewquestion\\preview_action_column')) {
|
||||
if (\core\plugininfo\qbank::is_plugin_enabled('qbank_previewquestion')) {
|
||||
$previewlink = $PAGE->get_renderer('qbank_previewquestion')->question_preview_link(
|
||||
$this->question->id, $this->context, true);
|
||||
}
|
||||
} else {
|
||||
$previewlink = $PAGE->get_renderer('core_question')->question_preview_link(
|
||||
$this->question->id, $this->context, true);
|
||||
}
|
||||
$buttonarray[] = $mform->createElement('static', 'previewlink', '', $previewlink);
|
||||
}
|
||||
|
||||
@ -267,7 +275,7 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
$this->add_action_buttons(true, get_string('savechanges'));
|
||||
|
||||
if ((!empty($this->question->id)) && (!($this->question->formoptions->canedit ||
|
||||
$this->question->formoptions->cansaveasnew))) {
|
||||
$this->question->formoptions->cansaveasnew))) {
|
||||
$mform->hardFreezeAllVisibleExcept(array('categorymoveto', 'buttonar', 'currentgrp'));
|
||||
}
|
||||
}
|
||||
@ -309,7 +317,7 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
$answeroptions[] = $mform->createElement('select', 'fraction',
|
||||
get_string('gradenoun'), $gradeoptions);
|
||||
$repeated[] = $mform->createElement('group', 'answeroptions',
|
||||
$label, $answeroptions, null, false);
|
||||
$label, $answeroptions, null, false);
|
||||
$repeated[] = $mform->createElement('editor', 'feedback',
|
||||
get_string('feedback', 'question'), array('rows' => 5), $this->editoroptions);
|
||||
$repeatedoptions['answer']['type'] = PARAM_RAW;
|
||||
@ -377,7 +385,7 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
// and the question isn't a course or activity level question then
|
||||
// allow course tags to be added to the course.
|
||||
$coursetagheader = get_string('questionformtagheader', 'core_question',
|
||||
$editingcoursecontext->get_context_name(true));
|
||||
$editingcoursecontext->get_context_name(true));
|
||||
$mform->addElement('header', 'coursetagsheader', $coursetagheader);
|
||||
$mform->addElement('autocomplete', 'coursetags', get_string('tags'), $tagstrings, $options);
|
||||
|
||||
@ -400,7 +408,7 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
protected function add_per_answer_fields(&$mform, $label, $gradeoptions,
|
||||
$minoptions = QUESTION_NUMANS_START, $addoptions = QUESTION_NUMANS_ADD) {
|
||||
$mform->addElement('header', 'answerhdr',
|
||||
get_string('answers', 'question'), '');
|
||||
get_string('answers', 'question'), '');
|
||||
$mform->setExpanded('answerhdr', 1);
|
||||
$answersoption = '';
|
||||
$repeatedoptions = array();
|
||||
@ -434,8 +442,8 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
$fields = array('correctfeedback', 'partiallycorrectfeedback', 'incorrectfeedback');
|
||||
foreach ($fields as $feedbackname) {
|
||||
$element = $mform->addElement('editor', $feedbackname,
|
||||
get_string($feedbackname, 'question'),
|
||||
array('rows' => 5), $this->editoroptions);
|
||||
get_string($feedbackname, 'question'),
|
||||
array('rows' => 5), $this->editoroptions);
|
||||
$mform->setType($feedbackname, PARAM_RAW);
|
||||
// Using setValue() as setDefault() does not work for the editor class.
|
||||
$element->setValue(array('text' => get_string($feedbackname.'default', 'question')));
|
||||
@ -476,7 +484,7 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
|
||||
if (count($optionelements)) {
|
||||
$repeated[] = $mform->createElement('group', 'hintoptions',
|
||||
get_string('hintnoptions', 'question'), $optionelements, null, false);
|
||||
get_string('hintnoptions', 'question'), $optionelements, null, false);
|
||||
}
|
||||
|
||||
return array($repeated, $repeatedoptions);
|
||||
@ -620,13 +628,13 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
// Prepare the feedback editor to display files in draft area.
|
||||
$draftitemid = file_get_submitted_draft_itemid('answer['.$key.']');
|
||||
$question->answer[$key]['text'] = file_prepare_draft_area(
|
||||
$draftitemid, // Draftid
|
||||
$this->context->id, // context
|
||||
'question', // component
|
||||
'answer', // filarea
|
||||
!empty($answer->id) ? (int) $answer->id : null, // itemid
|
||||
$this->fileoptions, // options
|
||||
$answer->answer // text.
|
||||
$draftitemid, // Draftid.
|
||||
$this->context->id, // Context.
|
||||
'question', // Component.
|
||||
'answer', // Filarea.
|
||||
!empty($answer->id) ? (int) $answer->id : null, // Itemid.
|
||||
$this->fileoptions, // Options.
|
||||
$answer->answer // Text.
|
||||
);
|
||||
$question->answer[$key]['itemid'] = $draftitemid;
|
||||
$question->answer[$key]['format'] = $answer->answerformat;
|
||||
@ -651,13 +659,13 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
// Prepare the feedback editor to display files in draft area.
|
||||
$draftitemid = file_get_submitted_draft_itemid('feedback['.$key.']');
|
||||
$question->feedback[$key]['text'] = file_prepare_draft_area(
|
||||
$draftitemid, // Draftid
|
||||
$this->context->id, // context
|
||||
'question', // component
|
||||
'answerfeedback', // filarea
|
||||
!empty($answer->id) ? (int) $answer->id : null, // itemid
|
||||
$this->fileoptions, // options
|
||||
$answer->feedback // text.
|
||||
$draftitemid, // Draftid.
|
||||
$this->context->id, // Context.
|
||||
'question', // Component.
|
||||
'answerfeedback', // Filarea.
|
||||
!empty($answer->id) ? (int) $answer->id : null, // Itemid.
|
||||
$this->fileoptions, // Options.
|
||||
$answer->feedback // Text.
|
||||
);
|
||||
$question->feedback[$key]['itemid'] = $draftitemid;
|
||||
$question->feedback[$key]['format'] = $answer->feedbackformat;
|
||||
@ -742,13 +750,13 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
$draftid = file_get_submitted_draft_itemid($feedbackname);
|
||||
$feedback = array();
|
||||
$feedback['text'] = file_prepare_draft_area(
|
||||
$draftid, // Draftid
|
||||
$this->context->id, // context
|
||||
'question', // component
|
||||
$feedbackname, // filarea
|
||||
!empty($question->id) ? (int) $question->id : null, // itemid
|
||||
$this->fileoptions, // options
|
||||
$question->options->$feedbackname // text.
|
||||
$draftid, // Draftid.
|
||||
$this->context->id, // Context.
|
||||
'question', // Component.
|
||||
$feedbackname, // Filarea.
|
||||
!empty($question->id) ? (int) $question->id : null, // Itemid.
|
||||
$this->fileoptions, // Options.
|
||||
$question->options->$feedbackname // Text.
|
||||
);
|
||||
$feedbackformat = $feedbackname . 'format';
|
||||
$feedback['format'] = $question->options->$feedbackformat;
|
||||
@ -782,13 +790,13 @@ abstract class question_edit_form extends question_wizard_form {
|
||||
// Prepare feedback editor to display files in draft area.
|
||||
$draftitemid = file_get_submitted_draft_itemid('hint['.$key.']');
|
||||
$question->hint[$key]['text'] = file_prepare_draft_area(
|
||||
$draftitemid, // Draftid
|
||||
$this->context->id, // context
|
||||
'question', // component
|
||||
'hint', // filarea
|
||||
!empty($hint->id) ? (int) $hint->id : null, // itemid
|
||||
$this->fileoptions, // options
|
||||
$hint->hint // text.
|
||||
$draftitemid, // Draftid.
|
||||
$this->context->id, // Context.
|
||||
'question', // Component.
|
||||
'hint', // Filarea.
|
||||
!empty($hint->id) ? (int) $hint->id : null, // Itemid.
|
||||
$this->fileoptions, // Options.
|
||||
$hint->hint // Text.
|
||||
);
|
||||
$question->hint[$key]['itemid'] = $draftitemid;
|
||||
$question->hint[$key]['format'] = $hint->hintformat;
|
||||
|
@ -1,5 +1,12 @@
|
||||
This files describes API changes for code that uses the question API.
|
||||
|
||||
=== 4.0 ==
|
||||
|
||||
1) Previously, the questionbank api classes were coupled in one place. Now the classes
|
||||
are divided in two different parts, base classes and feature classes. All the base
|
||||
classes are moved classes/local/bank and all the feature classes will be moved to
|
||||
the plugin for that feature.
|
||||
|
||||
=== 3.9 ==
|
||||
|
||||
1) For years, the ..._questions_in_use callback has been the right way for plugins to
|
||||
|
Loading…
x
Reference in New Issue
Block a user