MDL-74054 core_question: Add additional plugin points
This refactors the question bank view class to support new plugin points required for the question bank UI customisation features.
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
define("qbank_columnsortorder/sort_columns",["exports","core/ajax","core/notification","core/sortable_list","jquery"],(function(_exports,_ajax,_notification,_sortable_list,_jquery){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Javascript for sorting columns in question bank view.
|
||||
*
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
* @author Ghaly Marc-Alexandre <marc-alexandreghaly@catalyst-ca.net>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_sortable_list=_interopRequireDefault(_sortable_list),_jquery=_interopRequireDefault(_jquery);const setOrder=columns=>(0,_ajax.call)([{methodname:"qbank_columnsortorder_set_columnbank_order",args:{columns:columns}}])[0],getColumnOrder=listRoot=>{const columns=Array.from(listRoot.querySelectorAll("[data-pluginname]")).map((column=>column.dataset.pluginname));return columns.filter(((value,index)=>columns.indexOf(value)===index))};_exports.init=id=>{(listRoot=>{new _sortable_list.default(".list",{moveHandlerSelector:".item"}),(0,_jquery.default)(".item").on(_sortable_list.default.EVENTS.DROP,(()=>{const columns=getColumnOrder(listRoot);setOrder(columns).catch(_notification.exception),listRoot.querySelectorAll(".item").forEach((item=>item.classList.remove("active")))})),(0,_jquery.default)(".item").on(_sortable_list.default.EVENTS.DRAGSTART,(event=>{event.currentTarget.classList.add("active")}))})(document.querySelector("#".concat(id)))}}));
|
||||
|
||||
//# sourceMappingURL=sort_columns.min.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"sort_columns.min.js","sources":["../src/sort_columns.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Javascript for sorting columns in question bank view.\n *\n * @copyright 2021 Catalyst IT Australia Pty Ltd\n * @author Ghaly Marc-Alexandre <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {call as fetchMany} from 'core/ajax';\nimport {exception as displayException} from 'core/notification';\nimport SortableList from 'core/sortable_list';\nimport jQuery from 'jquery';\n\n/**\n * Sets up sortable list in the column sort order page.\n * @param {Element} listRoot\n */\nconst setupSortableLists = (listRoot) => {\n new SortableList('.list', {\n moveHandlerSelector: '.item',\n });\n\n jQuery('.item').on(SortableList.EVENTS.DROP, () => {\n const columns = getColumnOrder(listRoot);\n setOrder(columns).catch(displayException);\n listRoot.querySelectorAll('.item').forEach(item => item.classList.remove('active'));\n });\n\n jQuery('.item').on(SortableList.EVENTS.DRAGSTART, (event) => {\n event.currentTarget.classList.add('active');\n });\n};\n\n/**\n * Call external function set_order - inserts the updated column in the config_plugins table.\n *\n * @param {String} columns String that contains column order.\n * @returns {Promise}\n */\nconst setOrder = columns => fetchMany([{\n methodname: 'qbank_columnsortorder_set_columnbank_order',\n args: {columns},\n}])[0];\n\n/**\n * Gets the newly reordered columns to display in the question bank view.\n * @param {Element} listRoot\n * @returns {Array}\n */\nconst getColumnOrder = listRoot => {\n const columns = Array.from(listRoot.querySelectorAll('[data-pluginname]'))\n .map(column => column.dataset.pluginname);\n\n return columns.filter((value, index) => columns.indexOf(value) === index);\n};\n\n/**\n * Initialize module\n * @param {String} id unique id for columns.\n */\nexport const init = id => {\n const listRoot = document.querySelector(`#${id}`);\n setupSortableLists(listRoot);\n};\n"],"names":["setOrder","columns","methodname","args","getColumnOrder","listRoot","Array","from","querySelectorAll","map","column","dataset","pluginname","filter","value","index","indexOf","id","SortableList","moveHandlerSelector","on","EVENTS","DROP","catch","displayException","forEach","item","classList","remove","DRAGSTART","event","currentTarget","add","setupSortableLists","document","querySelector"],"mappings":";;;;;;;sLAsDMA,SAAWC,UAAW,cAAU,CAAC,CACnCC,WAAY,6CACZC,KAAM,CAACF,QAAAA,YACP,GAOEG,eAAiBC,iBACbJ,QAAUK,MAAMC,KAAKF,SAASG,iBAAiB,sBAChDC,KAAIC,QAAUA,OAAOC,QAAQC,oBAE3BX,QAAQY,QAAO,CAACC,MAAOC,QAAUd,QAAQe,QAAQF,SAAWC,uBAOnDE,KA3CQZ,CAAAA,eACpBa,uBAAa,QAAS,CACtBC,oBAAqB,8BAGlB,SAASC,GAAGF,uBAAaG,OAAOC,MAAM,WACnCrB,QAAUG,eAAeC,UAC/BL,SAASC,SAASsB,MAAMC,yBACxBnB,SAASG,iBAAiB,SAASiB,SAAQC,MAAQA,KAAKC,UAAUC,OAAO,mCAGtE,SAASR,GAAGF,uBAAaG,OAAOQ,WAAYC,QAC/CA,MAAMC,cAAcJ,UAAUK,IAAI,cAiCtCC,CADiBC,SAASC,yBAAkBlB"}
|
||||
@@ -1,79 +0,0 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Javascript for sorting columns in question bank view.
|
||||
*
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
* @author Ghaly Marc-Alexandre <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import {call as fetchMany} from 'core/ajax';
|
||||
import {exception as displayException} from 'core/notification';
|
||||
import SortableList from 'core/sortable_list';
|
||||
import jQuery from 'jquery';
|
||||
|
||||
/**
|
||||
* Sets up sortable list in the column sort order page.
|
||||
* @param {Element} listRoot
|
||||
*/
|
||||
const setupSortableLists = (listRoot) => {
|
||||
new SortableList('.list', {
|
||||
moveHandlerSelector: '.item',
|
||||
});
|
||||
|
||||
jQuery('.item').on(SortableList.EVENTS.DROP, () => {
|
||||
const columns = getColumnOrder(listRoot);
|
||||
setOrder(columns).catch(displayException);
|
||||
listRoot.querySelectorAll('.item').forEach(item => item.classList.remove('active'));
|
||||
});
|
||||
|
||||
jQuery('.item').on(SortableList.EVENTS.DRAGSTART, (event) => {
|
||||
event.currentTarget.classList.add('active');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Call external function set_order - inserts the updated column in the config_plugins table.
|
||||
*
|
||||
* @param {String} columns String that contains column order.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
const setOrder = columns => fetchMany([{
|
||||
methodname: 'qbank_columnsortorder_set_columnbank_order',
|
||||
args: {columns},
|
||||
}])[0];
|
||||
|
||||
/**
|
||||
* Gets the newly reordered columns to display in the question bank view.
|
||||
* @param {Element} listRoot
|
||||
* @returns {Array}
|
||||
*/
|
||||
const getColumnOrder = listRoot => {
|
||||
const columns = Array.from(listRoot.querySelectorAll('[data-pluginname]'))
|
||||
.map(column => column.dataset.pluginname);
|
||||
|
||||
return columns.filter((value, index) => columns.indexOf(value) === index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize module
|
||||
* @param {String} id unique id for columns.
|
||||
*/
|
||||
export const init = id => {
|
||||
const listRoot = document.querySelector(`#${id}`);
|
||||
setupSortableLists(listRoot);
|
||||
};
|
||||
@@ -21,8 +21,14 @@ defined('MOODLE_INTERNAL') || die();
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
|
||||
use context_system;
|
||||
use core_question\local\bank\column_action_base;
|
||||
use core_question\local\bank\column_base;
|
||||
use core_question\local\bank\column_manager_base;
|
||||
use core_question\local\bank\question_edit_contexts;
|
||||
use core_question\local\bank\view;
|
||||
use qbank_columnsortorder\local\bank\column_action_move;
|
||||
use qbank_columnsortorder\local\bank\column_action_remove;
|
||||
use qbank_columnsortorder\local\bank\column_action_resize;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
@@ -33,40 +39,113 @@ use moodle_url;
|
||||
* @author Ghaly Marc-Alexandre <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class column_manager {
|
||||
class column_manager extends column_manager_base {
|
||||
/**
|
||||
* @var array|bool Column order as set in config_plugins 'class' => 'position', ie: question_type_column => 3.
|
||||
* @var array Column order as set in config_plugins 'class' => 'position', ie: question_type_column => 3.
|
||||
*/
|
||||
public $columnorder;
|
||||
|
||||
/**
|
||||
* @var array|bool Disabled columns in config_plugins table.
|
||||
* @var array hidden columns.
|
||||
*/
|
||||
public $hiddencolumns;
|
||||
|
||||
/**
|
||||
* @var array columns with size.
|
||||
*/
|
||||
public $colsize;
|
||||
|
||||
/**
|
||||
* @var array Disabled columns in config_plugins table.
|
||||
*/
|
||||
public $disabledcolumns;
|
||||
|
||||
/**
|
||||
* Constructor for column_manager class.
|
||||
*
|
||||
* @param bool $globalsettings Only use the global default settings, ignoring user preferences?
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->columnorder = get_config('qbank_columnsortorder', 'enabledcol');
|
||||
$this->disabledcolumns = get_config('qbank_columnsortorder', 'disabledcol');
|
||||
public function __construct(bool $globalsettings = false) {
|
||||
$this->columnorder = $this->setup_property('enabledcol', $globalsettings);
|
||||
$this->hiddencolumns = $this->setup_property('hiddencols', $globalsettings);
|
||||
$this->colsize = $this->setup_property('colsize', $globalsettings, 'json');
|
||||
$this->disabledcolumns = $this->setup_property('disabledcol', $globalsettings);
|
||||
|
||||
if ($this->columnorder) {
|
||||
$this->columnorder = array_flip(explode(',', $this->columnorder));
|
||||
$this->columnorder = array_flip($this->columnorder);
|
||||
}
|
||||
if ($this->disabledcolumns) {
|
||||
$this->disabledcolumns = array_flip(explode(',', $this->disabledcolumns));
|
||||
$this->disabledcolumns = array_flip($this->disabledcolumns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value for the given property, based the saved user preference or config setting.
|
||||
*
|
||||
* If no value is currently stored, returns an empty array.
|
||||
*
|
||||
* @param string $setting The identifier used for the saved config and user preference settings.
|
||||
* @param bool $global Only get the global default, ignoring the user preference?
|
||||
* @param string $encoding The encoding used to store the property - csv or json
|
||||
* @return array
|
||||
*/
|
||||
private function setup_property(string $setting, bool $global = false, $encoding = 'csv'): array {
|
||||
$value = get_config('qbank_columnsortorder', $setting);
|
||||
if (!$global) {
|
||||
$value = get_user_preferences("qbank_columnsortorder_{$setting}", $value);
|
||||
}
|
||||
if (empty($value)) {
|
||||
return [];
|
||||
}
|
||||
return $encoding == 'csv' ? explode(',', $value) : json_decode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets column order in the qbank_columnsortorder plugin config.
|
||||
*
|
||||
* @param array $columns Column order to set.
|
||||
* @param bool $global save this as a global default, rather than a user preference?
|
||||
*/
|
||||
public static function set_column_order(array $columns) : void {
|
||||
public static function set_column_order(array $columns, bool $global = false) : void {
|
||||
$columns = implode(',', $columns);
|
||||
set_config('enabledcol', $columns, 'qbank_columnsortorder');
|
||||
self::save_preference('enabledcol', $columns, $global);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hidden Columns.
|
||||
*
|
||||
* @param array $columns hidden columns
|
||||
* @param bool $global save this as a global default, rather than a user preference?
|
||||
*/
|
||||
public static function set_hidden_columns(array $columns, bool $global = false) : void {
|
||||
$columns = implode(',', $columns);
|
||||
self::save_preference('hiddencols', $columns, $global);
|
||||
}
|
||||
|
||||
/**
|
||||
* Column size.
|
||||
*
|
||||
* @param string $sizes columns with width
|
||||
* @param bool $global save this as a global default, rather than a user preference?
|
||||
*/
|
||||
public static function set_column_size(string $sizes, bool $global = false) : void {
|
||||
self::save_preference('colsize', $sizes, $global);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Preferences.
|
||||
*
|
||||
* @param string $name name of a configuration
|
||||
* @param string $value value of a configuration
|
||||
* @param bool $global save this as a global default, rather than a user preference?
|
||||
*/
|
||||
private static function save_preference(string $name, string $value, bool $global = false): void {
|
||||
if ($global) {
|
||||
require_capability('moodle/site:config', context_system::instance());
|
||||
set_config($name, $value, 'qbank_columnsortorder');
|
||||
} else {
|
||||
set_user_preference("qbank_columnsortorder_{$name}", $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,12 +153,12 @@ class column_manager {
|
||||
*
|
||||
* @return view
|
||||
*/
|
||||
protected function get_questionbank(): view {
|
||||
public function get_questionbank(): view {
|
||||
$course = (object) ['id' => 0];
|
||||
$context = context_system::instance();
|
||||
$contexts = new question_edit_contexts($context);
|
||||
// Dummy call to get the objects without error.
|
||||
$questionbank = new view($contexts, new moodle_url('/question/dummyurl.php'), $course, null);
|
||||
$questionbank = new view($contexts, new moodle_url('/question/bank/columnsortorder/sortcolumns.php'), $course, null);
|
||||
return $questionbank;
|
||||
}
|
||||
|
||||
@@ -250,4 +329,62 @@ class column_manager {
|
||||
}
|
||||
return $ordertosort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of columns, set the isvisible attribute according to $this->hiddencolumns.
|
||||
*
|
||||
* @param column_base[] $columns
|
||||
* @return array
|
||||
*/
|
||||
public function set_columns_visibility(array $columns): array {
|
||||
foreach ($columns as $column) {
|
||||
if (!is_object($column)) {
|
||||
continue;
|
||||
}
|
||||
$column->isvisible = !in_array(get_class($column), $this->hiddencolumns);
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return $this->colsize mapped as an array of column name => width, excluding empty sizes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_colsize_map(): array {
|
||||
$sizes = array_reduce($this->colsize, function($result, $colsize) {
|
||||
$result[$colsize->column] = $colsize->width;
|
||||
return $result;
|
||||
}, []);
|
||||
return array_filter($sizes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of hidden columns as an array of class => column name
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_hidden_columns(): array {
|
||||
return array_reduce($this->hiddencolumns, function($result, $hiddencolumn) {
|
||||
$result[$hiddencolumn] = (new $hiddencolumn($this->get_questionbank()))->get_title();
|
||||
return $result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
public function get_column_width(column_base $column): string {
|
||||
$colsizemap = $this->get_colsize_map();
|
||||
$columnclass = get_class($column);
|
||||
if (array_key_exists($columnclass, $colsizemap)) {
|
||||
return $colsizemap[$columnclass];
|
||||
}
|
||||
return parent::get_column_width($column);
|
||||
}
|
||||
|
||||
public function get_column_actions(view $qbank): array {
|
||||
return [
|
||||
new column_action_move($qbank),
|
||||
new column_action_remove($qbank),
|
||||
new column_action_resize($qbank),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ class set_columnbank_order extends external_api {
|
||||
return new external_function_parameters([
|
||||
'columns' => new external_multiple_structure(
|
||||
new external_value(PARAM_TEXT, 'Plugin name for the column', VALUE_REQUIRED)
|
||||
)
|
||||
),
|
||||
'global' => new external_value(PARAM_BOOL, 'Set global config setting, rather than user preference',
|
||||
VALUE_DEFAULT, false),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -54,16 +56,28 @@ class set_columnbank_order extends external_api {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the columns plugin order.
|
||||
* Set columns order.
|
||||
*
|
||||
* @param array $columns json string representing new column order.
|
||||
* @param array $columns List of column names in the desired order.
|
||||
* @param bool $global Set global config setting, rather than user preference
|
||||
*/
|
||||
public static function execute(array $columns): void {
|
||||
['columns' => $columns] = self::validate_parameters(self::execute_parameters(), ['columns' => $columns]);
|
||||
public static function execute(array $columns, bool $global = false): void {
|
||||
[
|
||||
'columns' => $columns,
|
||||
'global' => $global,
|
||||
]
|
||||
= self::validate_parameters(self::execute_parameters(),
|
||||
[
|
||||
'columns' => $columns,
|
||||
'global' => $global,
|
||||
]);
|
||||
|
||||
$context = context_system::instance();
|
||||
self::validate_context($context);
|
||||
require_capability('moodle/category:manage', $context);
|
||||
if ($global) {
|
||||
require_capability('moodle/site:config', $context);
|
||||
}
|
||||
|
||||
column_manager::set_column_order($columns);
|
||||
column_manager::set_column_order($columns, $global);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace qbank_columnsortorder\output;
|
||||
|
||||
use moodle_url;
|
||||
use plugin_renderer_base;
|
||||
use qbank_columnsortorder\column_manager;
|
||||
|
||||
/**
|
||||
* Class renderer.
|
||||
* @package qbank_columnsortorder
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
* @author Ghaly Marc-Alexandre <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class renderer extends plugin_renderer_base {
|
||||
/**
|
||||
* Render list of question bank plugin columns.
|
||||
*
|
||||
* @return string The rendered HTML.
|
||||
*/
|
||||
public function render_column_sort_ui() {
|
||||
$columnsortorder = new column_manager();
|
||||
$enabledcolumns = $columnsortorder->get_columns();
|
||||
$disabledcolumns = $columnsortorder->get_disabled_columns();
|
||||
$params = [];
|
||||
foreach ($enabledcolumns as $columnname) {
|
||||
$name = $columnname->name;
|
||||
$colname = get_string('qbankcolumnname', 'qbank_columnsortorder', $columnname->colname);
|
||||
if ($columnname->class === 'qbank_customfields\custom_field_column') {
|
||||
$columnname->class .= "\\$columnname->colname";
|
||||
}
|
||||
$params['names'][] = ['name' => $name, 'colname' => $colname, 'class' => $columnname->class];
|
||||
}
|
||||
$params['disabled'] = $disabledcolumns;
|
||||
$params['columnsdisabled'] = (!empty($params['disabled'])) ? true : false;
|
||||
$urltoredirect = new moodle_url('/admin/settings.php', ['section' => 'manageqbanks']);
|
||||
|
||||
$params['urltomanageqbanks'] = get_string('qbankgotomanageqbanks', 'qbank_columnsortorder', $urltoredirect->out());
|
||||
|
||||
return $this->render_from_template('qbank_columnsortorder/columnsortorder', $params);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
namespace qbank_columnsortorder\privacy;
|
||||
|
||||
use \core_privacy\local\metadata\null_provider;
|
||||
use core_privacy\local\metadata\collection;
|
||||
use core_privacy\local\request\writer;
|
||||
|
||||
/**
|
||||
* Privacy provider for columnsortorder.
|
||||
@@ -26,14 +27,32 @@ use \core_privacy\local\metadata\null_provider;
|
||||
* @author Ghaly Marc-Alexandre <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements null_provider {
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason() : string {
|
||||
return 'privacy:metadata';
|
||||
class provider implements
|
||||
// The forms subsystem does not store any data itself, it has no database tables.
|
||||
\core_privacy\local\metadata\provider,
|
||||
|
||||
// The forms subsystem has user preferences.
|
||||
\core_privacy\local\request\user_preference_provider {
|
||||
|
||||
public static function get_metadata(collection $collection): collection {
|
||||
$collection->add_user_preference('enabledcol', 'privacy:metadata:preference:enabledcol');
|
||||
$collection->add_user_preference('hiddencols', 'privacy:metadata:preference:hiddencols');
|
||||
$collection->add_user_preference('colsize', 'privacy:metadata:preference:colsize');
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
public static function export_user_preferences(int $userid) {
|
||||
$components = ['core_question', "qbank_history"];
|
||||
foreach ($components as $component) {
|
||||
$prefnames = ['enabledcol', 'hiddencols', 'colsize'];
|
||||
foreach ($prefnames as $name) {
|
||||
$preference = get_user_preferences("{$component}_{$name}", null, $userid);
|
||||
if ($preference !== null) {
|
||||
$desc = get_string('enabledcol', 'privacy:metadata:preference:enabledcol');
|
||||
writer::export_user_preference('qbank_columnsortorder', "{$component}_{$name}", $preference, $desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
/**
|
||||
* qbank_columnsortorder external functions and service definitions.
|
||||
*
|
||||
* @package qbank_columnsortorder
|
||||
* @category webservice
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
@@ -28,9 +28,21 @@ defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$functions = [
|
||||
'qbank_columnsortorder_set_columnbank_order' => [
|
||||
'classname' => 'qbank_columnsortorder\external\set_columnbank_order',
|
||||
'classname' => 'qbank_columnsortorder\external\set_columnbank_order',
|
||||
'description' => 'Sets question columns order in database',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
'qbank_columnsortorder_set_hidden_columns' => [
|
||||
'classname' => 'qbank_columnsortorder\external\set_hidden_columns',
|
||||
'description' => 'Hidden Columns',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
'qbank_columnsortorder_set_column_size' => [
|
||||
'classname' => 'qbank_columnsortorder\external\set_column_size',
|
||||
'description' => 'Column size',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -23,11 +23,25 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['addcolumn'] = 'Add column \'{$a}\'';
|
||||
$string['addcolumns'] = 'Add columns';
|
||||
$string['auto'] = 'Auto';
|
||||
$string['columnwidth'] = 'Column width (pixels)';
|
||||
$string['invalidwidth'] = 'Width must be at least 10.';
|
||||
$string['movecolumn'] = 'Move column \'{$a}\'';
|
||||
$string['pluginname'] = 'Column sort order';
|
||||
$string['privacy:metadata'] = 'The Column sort order question bank plugin does not store any personal data.';
|
||||
$string['privacy:metadata:preference:enabledcol'] = 'The plugin saves user preference of column orders.';
|
||||
$string['privacy:metadata:preference:hiddencols'] = 'The plugin saves user preference of hidden columns.';
|
||||
$string['privacy:metadata:preference:colsize'] = 'The plugin saves user preference of column sizes.';
|
||||
$string['qbankcolumnsortorder'] = 'Column sort order';
|
||||
$string['qbankgotocolumnsort'] = 'You can change the order of the columns in the question bank on the page {$a}.';
|
||||
$string['qbankcolumnsdisabled'] = 'Currently disabled question bank plugins:';
|
||||
$string['qbankgotomanageqbanks'] = 'You can remove a column by disabling the plugin in <a href=\'{$a}\'>Manage question bank plugins</a>.';
|
||||
$string['qbankcolumnname'] = '({$a})';
|
||||
$string['qbankcolumnname'] = '{$a}';
|
||||
$string['qbanksortdescription'] = 'The order in which plugins are listed below determines the order of the columns in the question bank.';
|
||||
$string['resetcolumns'] = 'Reset columns';
|
||||
$string['resize'] = 'Resize';
|
||||
$string['resizecolumn'] = 'Resize column {$a}';
|
||||
$string['removecolumn'] = 'Remove column \'{$a}\'';
|
||||
$string['showhidecolumn'] = 'Show/Hide column';
|
||||
$string['width'] = 'Width of \'{$a}\' in pixels';
|
||||
|
||||
@@ -29,9 +29,16 @@ require_once($CFG->libdir.'/adminlib.php');
|
||||
admin_externalpage_setup('qbank_columnsortorder', '', ['section' => 'columnsortorder'],
|
||||
'/question/bank/columnsortorder/sortcolumns.php');
|
||||
|
||||
$renderer = $PAGE->get_renderer('qbank_columnsortorder');
|
||||
$preview = optional_param('preview', false, PARAM_BOOL);
|
||||
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(new lang_string('qbankcolumnsortorder', 'qbank_columnsortorder'));
|
||||
echo $renderer->render_column_sort_ui();
|
||||
if ($preview) {
|
||||
$columnmanager = new \qbank_columnsortorder\column_manager(true);
|
||||
$preview = $columnmanager->get_questionbank()->get_preview();
|
||||
echo $OUTPUT->render(new \qbank_columnsortorder\output\column_sort_preview($preview));
|
||||
|
||||
} else {
|
||||
echo $OUTPUT->render(new \qbank_columnsortorder\output\column_sort_ui());
|
||||
}
|
||||
echo $OUTPUT->footer();
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
{{!
|
||||
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 qbank_columnsortorder/columnsortorder.mustache
|
||||
|
||||
Admin question bank setting template.
|
||||
|
||||
Context variables required for this template:
|
||||
* name - plugin name
|
||||
* hiddenname - class/columname
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "Creatorname (creator_name_column)",
|
||||
"hiddenname": "creator_name_column"
|
||||
}
|
||||
}}
|
||||
|
||||
<div>
|
||||
{{#str}}qbanksortdescription, qbank_columnsortorder{{/str}}
|
||||
</div>
|
||||
<br>
|
||||
<div class="list list-group" id="qbank_columnsortorder-{{uniqid}}">
|
||||
{{#names}}
|
||||
<div class="column item list-group-item list-group-item-action" data-pluginname={{class}}>
|
||||
{{>core/drag_handle}}
|
||||
{{name}} {{colname}}
|
||||
</div>
|
||||
{{/names}}
|
||||
</div>
|
||||
<br>
|
||||
<div>
|
||||
{{#columnsdisabled}}
|
||||
{{#str}}qbankcolumnsdisabled, qbank_columnsortorder{{/str}}
|
||||
<br>
|
||||
{{/columnsdisabled}}
|
||||
{{#disabled}}
|
||||
<div class="list-group-item disabled">
|
||||
{{disabledname}}
|
||||
</div>
|
||||
{{/disabled}}
|
||||
</div>
|
||||
{{#js}}
|
||||
require(['qbank_columnsortorder/sort_columns'], function(SortColumns) {
|
||||
SortColumns.init("qbank_columnsortorder-{{uniqid}}");
|
||||
});
|
||||
{{/js}}
|
||||
<br>
|
||||
<div>
|
||||
{{{urltomanageqbanks}}}
|
||||
</div>
|
||||
@@ -23,7 +23,6 @@ use context_course;
|
||||
use core_question\local\bank\question_edit_contexts;
|
||||
use core_question\local\bank\view;
|
||||
use moodle_url;
|
||||
use qbank_columnsortorder\external\set_columnbank_order;
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/question/tests/fixtures/testable_core_question_column.php');
|
||||
@@ -36,11 +35,10 @@ require_once($CFG->dirroot . '/question/classes/external.php');
|
||||
* @copyright 2021 Catalyst IT Australia Pty Ltd
|
||||
* @author Ghaly Marc-Alexandre <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
* @coversDefaultClass \qbank_columnsortorder\column_manager
|
||||
* @covers \qbank_columnsortorder\column_manager
|
||||
*/
|
||||
class column_manager_test extends advanced_testcase {
|
||||
|
||||
|
||||
/** @var \stdClass course record. */
|
||||
protected $course;
|
||||
|
||||
@@ -53,6 +51,9 @@ class column_manager_test extends advanced_testcase {
|
||||
/** @var \qbank_columnsortorder\column_manager */
|
||||
protected $columnmanager;
|
||||
|
||||
/** @var string */
|
||||
protected $randomstring;
|
||||
|
||||
/**
|
||||
* Setup testcase.
|
||||
*/
|
||||
@@ -69,10 +70,74 @@ class column_manager_test extends advanced_testcase {
|
||||
|
||||
// Get current view columns.
|
||||
$this->columns = [];
|
||||
foreach ($this->questionbank->get_visiblecolumns() as $columnn) {
|
||||
$this->columns[] = get_class($columnn);
|
||||
foreach ($this->questionbank->get_visiblecolumns() as $column) {
|
||||
$this->columns[] = get_class($column);
|
||||
}
|
||||
$this->columnmanager = new column_manager();
|
||||
$this->randomstring = random_string();
|
||||
}
|
||||
|
||||
public function test_settings_provider(): array {
|
||||
return [
|
||||
'Test set_column_order' => [
|
||||
'setting' => 'enabledcol',
|
||||
'function' => 'set_column_order',
|
||||
'dataproperty' => 'columns',
|
||||
'csv' => true
|
||||
],
|
||||
'Test set_hidden_columns' => [
|
||||
'setting' => 'hiddencols',
|
||||
'function' => 'set_hidden_columns',
|
||||
'dataproperty' => 'columns',
|
||||
'csv' => true
|
||||
],
|
||||
'Test set_column_size' => [
|
||||
'setting' => 'colsize',
|
||||
'function' => 'set_column_size',
|
||||
'dataproperty' => 'randomstring',
|
||||
'csv' => false
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting config settings
|
||||
*
|
||||
* @dataProvider test_settings_provider
|
||||
* @param string $setting The name of the setting being saved
|
||||
* @param string $function The name of the function being called
|
||||
* @param string $dataproperty The property of the test class to pass to the function.
|
||||
* @param bool $csv True of the data is stored as a comma-separated list.
|
||||
* @return void
|
||||
*/
|
||||
public function test_settings(string $setting, string $function, string $dataproperty, bool $csv): void {
|
||||
$data = $this->{$dataproperty};
|
||||
$this->assertFalse(get_config('qbank_columnsortorder', $setting));
|
||||
$this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
|
||||
column_manager::{$function}($data);
|
||||
$expected = $csv ? implode(',', $data) : $data;
|
||||
$this->assertEquals($expected, get_config('qbank_columnsortorder', $setting));
|
||||
$this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting user preferences
|
||||
*
|
||||
* @dataProvider test_settings_provider
|
||||
* @param string $setting The name of the setting being saved
|
||||
* @param string $function The name of the function being called
|
||||
* @param string $dataproperty The property of the test class to pass to the function.
|
||||
* @param bool $csv True of the data is stored as a comma-separated list.
|
||||
* @return void
|
||||
*/
|
||||
public function test_settings_user(string $setting, string $function, string $dataproperty, bool $csv): void {
|
||||
$data = $this->{$dataproperty};
|
||||
$this->assertFalse(get_config('qbank_columnsortorder', $setting));
|
||||
$this->assertEmpty(get_user_preferences('qbank_columnsortorder_' . $setting));
|
||||
column_manager::{$function}($data, 'qbank_columnsortorder');
|
||||
$expected = $csv ? implode(',', $data) : $data;
|
||||
$this->assertFalse(get_config('qbank_columnsortorder', $setting));
|
||||
$this->assertEquals($expected, get_user_preferences('qbank_columnsortorder_' . $setting));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,45 +156,59 @@ class column_manager_test extends advanced_testcase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test function sort columns method.
|
||||
* The get_sorted_columns method should return the provided columns sorted according to enabledcol setting.
|
||||
*
|
||||
* @covers ::get_sorted_columns
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_sorted_columns(): void {
|
||||
$neworder = $this->columnmanager->get_sorted_columns($this->columns);
|
||||
$neworder = $this->columns;
|
||||
shuffle($neworder);
|
||||
set_columnbank_order::execute($neworder);
|
||||
$currentconfig = get_config('qbank_columnsortorder', 'enabledcol');
|
||||
$currentconfig = explode(',', $currentconfig);
|
||||
ksort($currentconfig);
|
||||
$this->assertSame($neworder, $currentconfig);
|
||||
set_config('enabledcol', implode(',', $neworder), 'qbank_columnsortorder');
|
||||
|
||||
$this->columnmanager = new column_manager();
|
||||
$columnstosort = [];
|
||||
foreach ($this->columns as $key => $column) {
|
||||
$colname = explode('\\', $column);
|
||||
$columnstosort[end($colname)] = $column;
|
||||
}
|
||||
|
||||
$sortedcolumns = $this->columnmanager->get_sorted_columns($columnstosort);
|
||||
|
||||
$expectedorder = ['checkbox_column' => 0];
|
||||
foreach ($neworder as $key => $column) {
|
||||
$colname = explode('\\', $column);
|
||||
$expectedorder[end($colname)] = $column;
|
||||
}
|
||||
$this->assertSame($expectedorder, $sortedcolumns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test function enabing and disablingcolumns.
|
||||
* Test disabled columns are removed from enabledcol setting and added to disabledcol setting.
|
||||
*
|
||||
* @covers ::enable_columns
|
||||
* @covers ::disable_columns
|
||||
* @return void
|
||||
*/
|
||||
public function test_enable_disable_columns(): void {
|
||||
$neworder = $this->columnmanager->get_sorted_columns($this->columns);
|
||||
shuffle($neworder);
|
||||
set_columnbank_order::execute($neworder);
|
||||
$currentconfig = get_config('qbank_columnsortorder', 'enabledcol');
|
||||
$currentconfig = explode(',', $currentconfig);
|
||||
$class = $currentconfig[array_rand($currentconfig, 1)];
|
||||
$randomplugintodisable = explode('\\', $class)[0];
|
||||
$olddisabledconfig = get_config('qbank_columnsortorder', 'disabledcol');
|
||||
$this->columnmanager->disable_columns($randomplugintodisable);
|
||||
$newdisabledconfig = get_config('qbank_columnsortorder', 'disabledcol');
|
||||
$this->assertNotEquals($olddisabledconfig, $newdisabledconfig);
|
||||
$this->columnmanager->enable_columns($randomplugintodisable);
|
||||
$newdisabledconfig = get_config('qbank_columnsortorder', 'disabledcol');
|
||||
$this->assertEmpty($newdisabledconfig);
|
||||
$enabledconfig = get_config('qbank_columnsortorder', 'enabledcol');
|
||||
$contains = strpos($enabledconfig, $randomplugintodisable);
|
||||
$this->assertNotFalse($contains);
|
||||
$this->assertIsInt($contains);
|
||||
public function test_disable_columns(): void {
|
||||
// Set up enabledcol with all plugins.
|
||||
set_config('enabledcol', implode(',', $this->columns), 'qbank_columnsortorder');
|
||||
$this->columnmanager = new column_manager();
|
||||
$this->assertFalse(get_config('qbank_columnsortorder', 'disabledcol'));
|
||||
|
||||
// Disable a random plugin.
|
||||
$plugincolumns = array_filter($this->columns, fn($column) => str_starts_with($column, 'qbank_'));
|
||||
$randomcolumn = $plugincolumns[array_rand($plugincolumns, 1)];
|
||||
$randomplugin = explode('\\', $randomcolumn)[0];
|
||||
$this->columnmanager->disable_columns($randomplugin);
|
||||
|
||||
// The enabledcol setting should now contain all columns except the disabled plugin.
|
||||
$expectedconfig = array_filter($this->columns, fn($column) => !str_starts_with($column, $randomplugin));
|
||||
sort($expectedconfig);
|
||||
$newconfig = explode(',', get_config('qbank_columnsortorder', 'enabledcol'));
|
||||
sort($newconfig);
|
||||
$this->assertEquals($expectedconfig, $newconfig);
|
||||
$this->assertNotContains($randomcolumn, $newconfig);
|
||||
// The disabledcol setting should only contain columns from the disabled plugin.
|
||||
$disabledconfig = explode(',', get_config('qbank_columnsortorder', 'disabledcol'));
|
||||
array_walk($disabledconfig, fn($column) => $this->assertStringStartsWith($randomplugin, $column));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -159,4 +238,68 @@ class column_manager_test extends advanced_testcase {
|
||||
$this->assertNotFalse($contains);
|
||||
$this->assertIsInt($contains);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test enabled columns are removed from disabledcol setting and added to enabledcol setting.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_enable_columns() {
|
||||
// Set up disablecol with columns from 2 random plugins, and enabledcol with all other columns.
|
||||
$plugincolumns = array_filter($this->columns, fn($column) => str_starts_with($column, 'qbank_'));
|
||||
$plugins = array_unique(array_map(fn($column) => explode('\\', $column)[0], $plugincolumns));
|
||||
$randomplugins = array_rand($plugins, 2);
|
||||
$randomplugin1 = $plugins[$randomplugins[0]];
|
||||
$randomplugin2 = $plugins[$randomplugins[1]];
|
||||
|
||||
$disabledcols = array_filter($this->columns,
|
||||
fn($column) => str_starts_with($column, $randomplugin1) || str_starts_with($column, $randomplugin2));
|
||||
$enabledcols = array_diff($this->columns, $disabledcols);
|
||||
|
||||
set_config('enabledcol', implode(',', $enabledcols), 'qbank_columnsortorder');
|
||||
set_config('disabledcol', implode(',', $disabledcols), 'qbank_columnsortorder');
|
||||
|
||||
// Enable one of the disabled plugins.
|
||||
$this->columnmanager = new column_manager();
|
||||
$this->columnmanager->enable_columns($randomplugin1);
|
||||
// The enabledcol setting should now contain all columns except the remaining disabled plugin.
|
||||
$expectedenabled = array_filter($this->columns, fn($column) => !str_starts_with($column, $randomplugin2));
|
||||
$expecteddisabled = array_filter($disabledcols, fn($column) => str_starts_with($column, $randomplugin2));
|
||||
sort($expectedenabled);
|
||||
sort($expecteddisabled);
|
||||
$newenabled = explode(',', get_config('qbank_columnsortorder', 'enabledcol'));
|
||||
sort($newenabled);
|
||||
$this->assertEquals($expectedenabled, $newenabled);
|
||||
$this->assertNotContains(reset($expecteddisabled), $newenabled);
|
||||
// The disabledcol setting should only contain columns from the remaining disabled plugin.
|
||||
$newdisabled = explode(',', get_config('qbank_columnsortorder', 'disabledcol'));
|
||||
array_walk($newdisabled, fn($column) => $this->assertStringStartsWith($randomplugin2, $column));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that get_disabled_columns returns names of all the columns in the disabledcol setting
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function test_get_disabled_columns(): void {
|
||||
// Set up disablecol with columns from 2 random plugins, and enabledcol with all other columns.
|
||||
$plugincolumns = array_filter($this->columns, fn($column) => str_starts_with($column, 'qbank_'));
|
||||
$randomcolumn = $plugincolumns[array_rand($plugincolumns, 1)];
|
||||
$randomplugin = explode('\\', $randomcolumn)[0];
|
||||
|
||||
$disabledcols = array_filter($this->columns, fn($column) => str_starts_with($column, $randomplugin));
|
||||
|
||||
set_config('disabledcol', implode(',', $disabledcols), 'qbank_columnsortorder');
|
||||
|
||||
$this->columnmanager = new column_manager();
|
||||
$expecteddisablednames = [];
|
||||
foreach ($disabledcols as $disabledcol) {
|
||||
$columnobject = new $disabledcol($this->questionbank);
|
||||
$expecteddisablednames[] = (object) [
|
||||
'disabledname' => $columnobject->get_title(),
|
||||
];
|
||||
}
|
||||
$disablednames = $this->columnmanager->get_disabled_columns();
|
||||
$this->assertEquals($expecteddisablednames, $disablednames);
|
||||
}
|
||||
}
|
||||
|
||||
-60
@@ -1,60 +0,0 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace qbank_columnsortorder;
|
||||
|
||||
use advanced_testcase;
|
||||
use qbank_columnsortorder\column_manager;
|
||||
use qbank_columnsortorder\external\set_columnbank_order;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
|
||||
require_once($CFG->dirroot . '/question/classes/external.php');
|
||||
|
||||
/**
|
||||
* Unit tests for qbank_columnsortorder external API.
|
||||
*
|
||||
* @package qbank_columnsortorder
|
||||
* @author 2021, Ghaly Marc-Alexandre <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class columnsortorder_external_test extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test that external call core_question_external::set_columnbank_order($oldorder) sets proper
|
||||
* data in config_plugins table.
|
||||
*/
|
||||
public function test_columnorder_external(): void {
|
||||
$this->resetAfterTest(true);
|
||||
$this->setAdminUser();
|
||||
|
||||
$columnsortorder = new column_manager();
|
||||
$questionlistcolumns = $columnsortorder->get_columns();
|
||||
$columnclasses = [];
|
||||
foreach ($questionlistcolumns as $columnnobject) {
|
||||
$columnclasses[] = $columnnobject->class;
|
||||
}
|
||||
shuffle($columnclasses);
|
||||
set_columnbank_order::execute($columnclasses);
|
||||
|
||||
$currentconfig = (array)get_config('qbank_columnsortorder', 'enabledcol');
|
||||
$currentconfig = explode(',', $currentconfig[0]);
|
||||
|
||||
$this->assertSame($columnclasses, $currentconfig);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,6 @@
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->component = 'qbank_columnsortorder';
|
||||
$plugin->version = 2023042401;
|
||||
$plugin->version = 2023052200;
|
||||
$plugin->requires = 2023041800;
|
||||
$plugin->maturity = MATURITY_STABLE;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
namespace qbank_editquestion;
|
||||
|
||||
use core_question\local\bank\question_version_status;
|
||||
use qbank_editquestion\output\add_new_question;
|
||||
|
||||
/**
|
||||
* Class editquestion_helper for methods related to add/edit/copy
|
||||
@@ -69,25 +70,13 @@ class editquestion_helper {
|
||||
* @param bool $canadd the text to display on the button.
|
||||
* @param string $tooltip a tooltip to add to the button (optional).
|
||||
* @param bool $disabled if true, the button will be disabled.
|
||||
* @deprecated since Moodle 4.3. Use {@see add_new_question} renderable instead
|
||||
* @todo Final deprecation in Moodle 4.7
|
||||
*/
|
||||
public static function create_new_question_button($categoryid, $params, $canadd, $tooltip = '', $disabled = false) {
|
||||
global $PAGE, $OUTPUT;
|
||||
|
||||
$addquestiondisplay = array();
|
||||
$addquestiondisplay['canadd'] = $canadd;
|
||||
if ($canadd) {
|
||||
$params['category'] = $categoryid;
|
||||
$url = new \moodle_url('/question/bank/editquestion/addquestion.php', $params);
|
||||
$buttonparams = ['disabled' => $disabled];
|
||||
if (!empty($tooltip)) {
|
||||
$buttonparams['title'] = $tooltip;
|
||||
}
|
||||
$addquestiondisplay['buttonhtml'] = $OUTPUT->single_button($url,
|
||||
get_string('createnewquestion', 'question'),
|
||||
'get', $buttonparams);
|
||||
$addquestiondisplay['qtypeform'] = self::print_choose_qtype_to_add_form(array());
|
||||
}
|
||||
return $PAGE->get_renderer('qbank_editquestion')->render_create_new_question_button($addquestiondisplay);
|
||||
global $OUTPUT;
|
||||
debugging('create_new_question_button() is deprecated. Use the add_new_question renderable instead.');
|
||||
return $OUTPUT->render(new add_new_question($categoryid, $params, $canadd));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace qbank_editquestion\output;
|
||||
|
||||
use qbank_editquestion\editquestion_helper;
|
||||
use renderer_base;
|
||||
|
||||
/**
|
||||
* Create new question button
|
||||
*
|
||||
* @package qbank_editquestion
|
||||
* @copyright 2023 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class add_new_question implements \templatable, \renderable {
|
||||
|
||||
/** @var int $categoryid The ID of the category the quesiton will be added to. */
|
||||
protected int $categoryid;
|
||||
|
||||
/** @var array $params URL parameters to pass to the add question form. */
|
||||
protected array $params;
|
||||
|
||||
/** @var bool $canadd True if the add question button should be displayed. If false, a placeholder will be shown.*/
|
||||
protected bool $canadd;
|
||||
|
||||
/**
|
||||
* Store data for building the template context.
|
||||
*
|
||||
* @param int $categoryid
|
||||
* @param array $params
|
||||
* @param bool $canadd
|
||||
*/
|
||||
public function __construct(int $categoryid, array $params, bool $canadd) {
|
||||
$this->categoryid = $categoryid;
|
||||
$this->params = $params;
|
||||
$this->canadd = $canadd;
|
||||
}
|
||||
|
||||
public function export_for_template(renderer_base $output): array {
|
||||
$addquestiondisplay = [];
|
||||
$addquestiondisplay['canadd'] = $this->canadd;
|
||||
if ($this->canadd) {
|
||||
$this->params['category'] = $this->categoryid;
|
||||
$url = new \moodle_url('/question/bank/editquestion/addquestion.php', $this->params);
|
||||
$addquestiondisplay['buttonhtml'] = $output->single_button($url,
|
||||
get_string('createnewquestion', 'question'),
|
||||
'get');
|
||||
$addquestiondisplay['qtypeform'] = editquestion_helper::print_choose_qtype_to_add_form(array());
|
||||
}
|
||||
return $addquestiondisplay;
|
||||
}
|
||||
}
|
||||
@@ -52,8 +52,12 @@ class renderer extends \plugin_renderer_base {
|
||||
*
|
||||
* @param array $addquestiondata
|
||||
* @return bool|string
|
||||
* @deprecated since Moodle 4.3. Use {@see add_new_question} renderable instead
|
||||
* @todo Final deprecation in Moodle 4.7
|
||||
*/
|
||||
public function render_create_new_question_button ($addquestiondata) {
|
||||
debugging('render_create_new_question_button() is deprecated. '
|
||||
. 'Pass the add_new_question renderable to render() instead.');
|
||||
return $this->render_from_template('qbank_editquestion/add_new_question', $addquestiondata);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
|
||||
namespace qbank_editquestion;
|
||||
|
||||
use core\context;
|
||||
use core_question\local\bank\view;
|
||||
use qbank_editquestion\output\add_new_question;
|
||||
|
||||
/**
|
||||
* Class columns is the entrypoint for the columns.
|
||||
@@ -35,7 +37,7 @@ use core_question\local\bank\view;
|
||||
* @author Safat Shahin <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class plugin_feature extends \core_question\local\bank\plugin_features_base{
|
||||
class plugin_feature extends \core_question\local\bank\plugin_features_base {
|
||||
|
||||
public function get_question_columns($qbank): array {
|
||||
return [
|
||||
@@ -50,4 +52,23 @@ class plugin_feature extends \core_question\local\bank\plugin_features_base{
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return "Add new question" control.
|
||||
*
|
||||
* @param view $qbank The question bank view.
|
||||
* @param context $context The current context, for permission checks.
|
||||
* @param int $categoryid The current question category ID.
|
||||
* @return \renderable[]
|
||||
*/
|
||||
public function get_question_bank_controls(view $qbank, context $context, int $categoryid): array {
|
||||
if (!$qbank->allow_add_questions()) {
|
||||
return [];
|
||||
}
|
||||
$canadd = has_capability('moodle/question:add', $context);
|
||||
$urlparams = (new edit_action($qbank))->editquestionurl->params();
|
||||
return [
|
||||
100 => new add_new_question($categoryid, $urlparams, $canadd),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -53,6 +53,14 @@ class discrimination_index extends column_base {
|
||||
echo $PAGE->get_renderer('qbank_statistics')->render_discrimination_index($discriminationindex);
|
||||
}
|
||||
|
||||
public function display_preview(\stdClass $question, string $rowclasses): void {
|
||||
global $PAGE;
|
||||
|
||||
$this->display_start($question, $rowclasses);
|
||||
echo $PAGE->get_renderer('qbank_statistics')->render_discrimination_index(50.00);
|
||||
$this->display_end($question, $rowclasses);;
|
||||
}
|
||||
|
||||
public function get_extra_classes(): array {
|
||||
return ['pr-3'];
|
||||
}
|
||||
|
||||
@@ -51,6 +51,14 @@ class discriminative_efficiency extends column_base {
|
||||
echo $PAGE->get_renderer('qbank_statistics')->render_discriminative_efficiency($discriminativeefficiency);
|
||||
}
|
||||
|
||||
public function display_preview(\stdClass $question, string $rowclasses): void {
|
||||
global $PAGE;
|
||||
|
||||
$this->display_start($question, $rowclasses);
|
||||
echo $PAGE->get_renderer('qbank_statistics')->render_discriminative_efficiency(25);
|
||||
$this->display_end($question, $rowclasses);;
|
||||
}
|
||||
|
||||
public function get_extra_classes(): array {
|
||||
return ['pr-3'];
|
||||
}
|
||||
|
||||
@@ -52,6 +52,14 @@ class facility_index extends column_base {
|
||||
echo $PAGE->get_renderer('qbank_statistics')->render_facility_index($facility);
|
||||
}
|
||||
|
||||
public function display_preview(\stdClass $question, string $rowclasses): void {
|
||||
global $PAGE;
|
||||
|
||||
$this->display_start($question, $rowclasses);
|
||||
echo $PAGE->get_renderer('qbank_statistics')->render_facility_index(0.75);
|
||||
$this->display_end($question, $rowclasses);;
|
||||
}
|
||||
|
||||
public function get_extra_classes(): array {
|
||||
return ['pr-3'];
|
||||
}
|
||||
|
||||
@@ -57,6 +57,26 @@ class checkbox_column extends column_base {
|
||||
return get_string('selectquestionsforbulk', 'question');
|
||||
}
|
||||
|
||||
public function display_header(array $columnactions = [], string $width = ''): void {
|
||||
global $PAGE;
|
||||
$renderer = $PAGE->get_renderer('core_question', 'bank');
|
||||
|
||||
$data = [];
|
||||
$data['sortable'] = false;
|
||||
$data['extraclasses'] = $this->get_classes();
|
||||
$name = get_class($this);
|
||||
$data['sorttip'] = true;
|
||||
$data['tiptitle'] = $this->get_title();
|
||||
$data['tip'] = $this->get_title_tip();
|
||||
|
||||
$data['colname'] = $this->get_column_name();
|
||||
$data['name'] = get_string('selectall');
|
||||
$data['class'] = $name;
|
||||
$data['width'] = $width;
|
||||
|
||||
echo $renderer->render_column_header($data);
|
||||
}
|
||||
|
||||
protected function display_content($question, $rowclasses): void {
|
||||
global $OUTPUT;
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Base class to implement actions that can be performed on any column.
|
||||
*
|
||||
* A plugin should define subclasses of this for each action it provides, and return an instance of each from
|
||||
* plugin_feature::get_column_actions(). The action returned from {@see get_action_menu_link()} will be displayed in each column
|
||||
* header.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2023 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class column_action_base extends view_component {
|
||||
/**
|
||||
* A chance for subclasses to initialise themselves, for example to load lang strings,
|
||||
* without having to override the constructor.
|
||||
*/
|
||||
protected function init(): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the action menu link for this action on the supplied column.
|
||||
*
|
||||
* @param column_base $column The column we are providing the action for.
|
||||
* @return ?\action_menu_link The action to display in the column header.
|
||||
*/
|
||||
abstract public function get_action_menu_link(column_base $column): ?\action_menu_link;
|
||||
}
|
||||
@@ -41,6 +41,9 @@ abstract class column_base extends view_component {
|
||||
/** @var bool determine whether the column is td or th. */
|
||||
protected $isheading = false;
|
||||
|
||||
/** @var bool determine whether the column is visible */
|
||||
public $isvisible = true;
|
||||
|
||||
/**
|
||||
* Set the column as heading
|
||||
*/
|
||||
@@ -78,8 +81,10 @@ abstract class column_base extends view_component {
|
||||
|
||||
/**
|
||||
* Output the column header cell.
|
||||
*
|
||||
* @params column_action_base[] A list of column actions to include in the header.
|
||||
*/
|
||||
public function display_header(): void {
|
||||
public function display_header(array $columnactions = [], string $width = ''): void {
|
||||
global $PAGE;
|
||||
$renderer = $PAGE->get_renderer('core_question', 'bank');
|
||||
|
||||
@@ -115,6 +120,16 @@ abstract class column_base extends view_component {
|
||||
$data['help'] = $help->export_for_template($renderer);
|
||||
}
|
||||
|
||||
$data['colname'] = $this->get_column_name();
|
||||
$data['name'] = $title;
|
||||
$data['class'] = $name;
|
||||
$data['width'] = $width;
|
||||
if (!empty($columnactions)) {
|
||||
$actions = array_map(fn($columnaction) => $columnaction->get_action_menu_link($this), $columnactions);
|
||||
$actionmenu = new \action_menu($actions);
|
||||
$data['actionmenu'] = $actionmenu->export_for_template($renderer);
|
||||
}
|
||||
|
||||
echo $renderer->render_column_header($data);
|
||||
}
|
||||
|
||||
@@ -216,7 +231,10 @@ abstract class column_base extends view_component {
|
||||
*/
|
||||
protected function display_start($question, $rowclasses): void {
|
||||
$tag = 'td';
|
||||
$attr = ['class' => $this->get_classes()];
|
||||
$attr = [
|
||||
'class' => $this->get_classes(),
|
||||
'data-pluginname' => get_class($this),
|
||||
];
|
||||
if ($this->isheading) {
|
||||
$tag = 'th';
|
||||
$attr['scope'] = 'row';
|
||||
@@ -395,4 +413,16 @@ abstract class column_base extends view_component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the column with an example value.
|
||||
*
|
||||
* By default, this will call $this->display() using whatever dummy data is passed in. Columns can override this
|
||||
* to provide example output without requiring valid data.
|
||||
*
|
||||
* @param \stdClass $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_preview(\stdClass $question, string $rowclasses): void {
|
||||
$this->display($question, $rowclasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
/**
|
||||
* Default column manager class
|
||||
*
|
||||
* This class defines stub methods that can be overridden by a plugin defining its own column manager.
|
||||
*
|
||||
* @package core_question
|
||||
* @copyright 2023 onwards Catalyst IT EU {@link https://catalyst-eu.net}
|
||||
* @author Mark Johnson <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class column_manager_base {
|
||||
|
||||
/**
|
||||
* Sort the list of columns
|
||||
*
|
||||
* Sort the provided list of columns into the order implemented in this column manager.
|
||||
*
|
||||
* @param array $unsortedcolumns Unordered array of columns
|
||||
* @return array Columns in the desired order.
|
||||
*/
|
||||
public function get_sorted_columns(array $unsortedcolumns): array {
|
||||
return $unsortedcolumns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of columns, set the isvisible attribute.
|
||||
*
|
||||
* This base class leave all columns visible.
|
||||
*
|
||||
* @param column_base[] $columns
|
||||
* @return array
|
||||
*/
|
||||
public function set_columns_visibility(array $columns): array {
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of actions to display in an action menu for each column.
|
||||
*
|
||||
* @param view $qbank Question bank view.
|
||||
* @return column_action_base[] A list of column actions.
|
||||
*/
|
||||
public function get_column_actions(view $qbank): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a column, return a value for its width CSS property.
|
||||
*
|
||||
* @param column_base $column
|
||||
* @return string CSS width property value.
|
||||
*/
|
||||
public function get_column_width(column_base $column): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,11 @@
|
||||
|
||||
namespace core_question\local\bank;
|
||||
|
||||
use core\context;
|
||||
use qbank_columnsortorder\local\qbank\column_action_move;
|
||||
use qbank_columnsortorder\local\qbank\column_action_remove;
|
||||
use qbank_columnsortorder\local\qbank\column_action_resize;
|
||||
|
||||
/**
|
||||
* Class plugin_features_base is the base class for qbank plugins.
|
||||
*
|
||||
@@ -75,6 +80,31 @@ class plugin_features_base {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return a column manager object, if this plugin provides one.
|
||||
*
|
||||
* @return ?column_manager_base
|
||||
*/
|
||||
public function get_column_manager(): ?column_manager_base {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return an array of renderable objects, for adding additional controls to the question bank screen.
|
||||
*
|
||||
* The array returned can include a numeric index for each object, to indicate the position in which it should be displayed
|
||||
* relative to other controls. If two plugins return controls with the same position, they will be displayed after one another,
|
||||
* based on the alphabetical order of the plugin component names.
|
||||
*
|
||||
* @param view $qbank The question bank view.
|
||||
* @param context $context The current context, for permission checks.
|
||||
* @param int $categoryid The current question category ID.
|
||||
* @return \renderable[]
|
||||
*/
|
||||
public function get_question_bank_controls(view $qbank, context $context, int $categoryid): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return search conditions for the plugin.
|
||||
*
|
||||
|
||||
@@ -34,7 +34,7 @@ use core_plugin_manager;
|
||||
use core_question\local\bank\condition;
|
||||
use core_question\local\statistics\statistics_bulk_loader;
|
||||
use core_question\output\question_bank_filter_ui;
|
||||
use qbank_columnsortorder\column_manager;
|
||||
use core_question\local\bank\column_manager_base;
|
||||
use qbank_deletequestion\hidden_condition;
|
||||
use qbank_editquestion\editquestion_helper;
|
||||
use qbank_managecategories\category_condition;
|
||||
@@ -193,7 +193,7 @@ class view {
|
||||
protected $pagevars = [];
|
||||
|
||||
/**
|
||||
* @var array $plugins all the qbank plugin objects.
|
||||
* @var plugin_features_base[] $plugins Plugin feature objects for all enabled qbank plugins.
|
||||
*/
|
||||
protected $plugins = [];
|
||||
|
||||
@@ -212,6 +212,11 @@ class view {
|
||||
*/
|
||||
public $extraparams = [];
|
||||
|
||||
/**
|
||||
* @var column_manager_base $columnmanager The column manager, can be overridden by plugins.
|
||||
*/
|
||||
protected $columnmanager;
|
||||
|
||||
/**
|
||||
* Constructor for view.
|
||||
*
|
||||
@@ -266,8 +271,9 @@ class view {
|
||||
|
||||
$this->lastchangedid = clean_param($pageurl->param('lastchanged'), PARAM_INT);
|
||||
|
||||
$this->init_plugins();
|
||||
$this->init_column_manager();
|
||||
// Possibly the heading part can be removed.
|
||||
$this->plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
|
||||
$this->set_pagevars($params);
|
||||
$this->init_columns($this->wanted_columns(), $this->heading_column());
|
||||
$this->init_question_actions();
|
||||
@@ -276,17 +282,45 @@ class view {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize bulk actions.
|
||||
* Get an array of plugin features objects for all enabled qbank plugins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init_bulk_actions(): void {
|
||||
protected function init_plugins(): void {
|
||||
$plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
|
||||
foreach ($plugins as $componentname => $plugin) {
|
||||
foreach ($plugins as $componentname => $pluginclass) {
|
||||
if (!\core\plugininfo\qbank::is_plugin_enabled($componentname)) {
|
||||
continue;
|
||||
}
|
||||
$this->plugins[$componentname] = new $pluginclass();
|
||||
}
|
||||
// Sort plugin list by component name.
|
||||
ksort($this->plugins);
|
||||
}
|
||||
|
||||
$pluginentrypoint = new $plugin();
|
||||
$bulkactions = $pluginentrypoint->get_bulk_actions();
|
||||
/**
|
||||
* Allow qbank plugins to override the column manager.
|
||||
*
|
||||
* If multiple qbank plugins define a column manager, this will pick the first one sorted alphabetically.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function init_column_manager(): void {
|
||||
$this->columnmanager = new column_manager_base();
|
||||
foreach ($this->plugins as $plugin) {
|
||||
if ($columnmanager = $plugin->get_column_manager()) {
|
||||
$this->columnmanager = $columnmanager;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize bulk actions.
|
||||
*/
|
||||
protected function init_bulk_actions(): void {
|
||||
foreach ($this->plugins as $componentname => $plugin) {
|
||||
$bulkactions = $plugin->get_bulk_actions();
|
||||
if (!is_array($bulkactions)) {
|
||||
debugging("The method {$componentname}::get_bulk_actions() must return an " .
|
||||
"array of bulk actions instead of a single bulk action. " .
|
||||
@@ -302,7 +336,6 @@ class view {
|
||||
'capabilities' => $bulkactionobject->get_bulk_action_capabilities()
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,14 +369,9 @@ class view {
|
||||
* @return void
|
||||
*/
|
||||
protected function init_question_actions(): void {
|
||||
$plugins = \core_component::get_plugin_list_with_class('qbank', 'plugin_feature', 'plugin_feature.php');
|
||||
$this->questionactions = [];
|
||||
foreach ($plugins as $component => $plugin) {
|
||||
if (!qbank::is_plugin_enabled($component)) {
|
||||
continue;
|
||||
}
|
||||
$pluginentrypointobject = new $plugin();
|
||||
$menuactions = $pluginentrypointobject->get_question_actions($this);
|
||||
foreach ($this->plugins as $plugin) {
|
||||
$menuactions = $plugin->get_question_actions($this);
|
||||
foreach ($menuactions as $menuaction) {
|
||||
$this->questionactions[$menuaction::class] = $menuaction;
|
||||
}
|
||||
@@ -395,14 +423,8 @@ class view {
|
||||
$questionbankclasscolumns = $this->get_class_for_columns();
|
||||
|
||||
$plugins = $this->plugins;
|
||||
foreach ($plugins as $componentname => $plugin) {
|
||||
$pluginentrypointobject = new $plugin();
|
||||
$plugincolumnobjects = $pluginentrypointobject->get_question_columns($this);
|
||||
// Don't need the plugins without column objects.
|
||||
if (empty($plugincolumnobjects)) {
|
||||
unset($plugins[$componentname]);
|
||||
continue;
|
||||
}
|
||||
foreach ($this->plugins as $componentname => $plugin) {
|
||||
$plugincolumnobjects = $plugin->get_question_columns($this);
|
||||
foreach ($plugincolumnobjects as $columnobject) {
|
||||
$columnname = $columnobject->get_column_name();
|
||||
foreach ($this->corequestionbankcolumns as $key => $corequestionbankcolumn) {
|
||||
@@ -431,15 +453,12 @@ class view {
|
||||
$questionbankclasscolumns[$key] = $newpluginclasscolumn;
|
||||
}
|
||||
|
||||
// Check if qbank_columnsortorder is enabled.
|
||||
if (array_key_exists('columnsortorder', core_plugin_manager::instance()->get_enabled_plugins('qbank'))) {
|
||||
$columnorder = new column_manager();
|
||||
$questionbankclasscolumns = $columnorder->get_sorted_columns($questionbankclasscolumns);
|
||||
}
|
||||
$questionbankclasscolumns = $this->columnmanager->get_sorted_columns($questionbankclasscolumns);
|
||||
$questionbankclasscolumns = $this->columnmanager->set_columns_visibility($questionbankclasscolumns);
|
||||
|
||||
// Mitigate the error in case of any regression.
|
||||
foreach ($questionbankclasscolumns as $shortname => $questionbankclasscolumn) {
|
||||
if (!is_object($questionbankclasscolumn)) {
|
||||
if (!is_object($questionbankclasscolumn) || !$questionbankclasscolumn->isvisible) {
|
||||
unset($questionbankclasscolumns[$shortname]);
|
||||
}
|
||||
}
|
||||
@@ -456,7 +475,7 @@ class view {
|
||||
$this->requiredcolumns = [];
|
||||
$questionbankcolumns = $this->get_question_bank_plugins();
|
||||
foreach ($questionbankcolumns as $classobject) {
|
||||
if (empty($classobject)) {
|
||||
if (empty($classobject) || !($classobject instanceof \core_question\local\bank\column_base)) {
|
||||
continue;
|
||||
}
|
||||
$this->requiredcolumns[$classobject->get_column_name()] = $classobject;
|
||||
@@ -500,7 +519,10 @@ class view {
|
||||
if ($column->is_extra_row()) {
|
||||
$this->extrarows[$column->get_column_name()] = $column;
|
||||
} else {
|
||||
$this->visiblecolumns[$column->get_column_name()] = $column;
|
||||
// Only add columns which are visible.
|
||||
if ($column->isvisible) {
|
||||
$this->visiblecolumns[$column->get_column_name()] = $column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -905,7 +927,14 @@ class view {
|
||||
* Shows the question bank interface.
|
||||
*/
|
||||
public function display(): void {
|
||||
echo \html_writer::start_div('questionbankwindow boxwidthwide boxaligncenter');
|
||||
$editcontexts = $this->contexts->having_one_edit_tab_cap('questions');
|
||||
|
||||
echo \html_writer::start_div('questionbankwindow boxwidthwide boxaligncenter', [
|
||||
'data-component' => 'core_question',
|
||||
'data-callback' => 'display_question_bank',
|
||||
'data-contextid' => $editcontexts[array_key_last($editcontexts)]->id,
|
||||
]);
|
||||
|
||||
// Show the filters and search options.
|
||||
$this->wanted_filters();
|
||||
// Continues with list of questions.
|
||||
@@ -1087,22 +1116,50 @@ class view {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new question form.
|
||||
* Does the current view allow adding new questions?
|
||||
*
|
||||
* @param false|mixed|\stdClass $category
|
||||
* @param bool $canadd
|
||||
* @return bool True if the view supports adding new questions.
|
||||
*/
|
||||
protected function create_new_question_form($category, $canadd): void {
|
||||
if (\core\plugininfo\qbank::is_plugin_enabled('qbank_editquestion')) {
|
||||
echo editquestion_helper::create_new_question_button($category->id,
|
||||
$this->questionactions['qbank_editquestion\edit_action']->editquestionurl->params(), $canadd);
|
||||
public function allow_add_questions(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the question bank controls for each plugin.
|
||||
*
|
||||
* Controls will be output in the order defined by the array keys returned from
|
||||
* {@see plugin_features_base::get_question_bank_controls}. If more than one plugin defines a control in the same position,
|
||||
* they will placed after one another based on the alphabetical order of the plugins.
|
||||
*
|
||||
* @param \core\context $context The current context, for permissions checks.
|
||||
* @param int $categoryid The current question category.
|
||||
*/
|
||||
protected function get_plugin_controls(\core\context $context, int $categoryid): string {
|
||||
global $OUTPUT;
|
||||
$orderedcontrols = [];
|
||||
foreach ($this->plugins as $plugin) {
|
||||
$plugincontrols = $plugin->get_question_bank_controls($this, $context, $categoryid);
|
||||
foreach ($plugincontrols as $position => $plugincontrol) {
|
||||
if (!array_key_exists($position, $orderedcontrols)) {
|
||||
$orderedcontrols[$position] = [];
|
||||
}
|
||||
$orderedcontrols[$position][] = $plugincontrol;
|
||||
}
|
||||
}
|
||||
ksort($orderedcontrols);
|
||||
$output = '';
|
||||
foreach ($orderedcontrols as $controls) {
|
||||
foreach ($controls as $control) {
|
||||
$output .= $OUTPUT->render($control);
|
||||
}
|
||||
}
|
||||
return $OUTPUT->render_from_template('core_question/question_bank_controls', ['controls' => $output]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the table of questions in a category with interactions
|
||||
*/
|
||||
protected function display_question_list(): void {
|
||||
public function display_question_list(): void {
|
||||
// This function can be moderately slow with large question counts and may time out.
|
||||
// We probably do not want to raise it to unlimited, so randomly picking 5 minutes.
|
||||
// Note: We do not call this in the loop because quiz ob_ captures this function (see raise() PHP doc).
|
||||
@@ -1120,6 +1177,7 @@ class view {
|
||||
'data-contextid' => $this->get_most_specific_context()->id,
|
||||
]
|
||||
);
|
||||
echo $this->get_plugin_controls($catcontext, $categoryid);
|
||||
|
||||
$this->build_query();
|
||||
$questionsrs = $this->load_page_questions();
|
||||
@@ -1376,13 +1434,13 @@ class view {
|
||||
// Start of the table.
|
||||
echo \html_writer::start_tag('table', [
|
||||
'id' => 'categoryquestions',
|
||||
'class' => 'table-responsive',
|
||||
'data-defaultsort' => json_encode($this->sort)
|
||||
'class' => 'question-bank-table generaltable table-responsive',
|
||||
'data-defaultsort' => json_encode($this->sort),
|
||||
]);
|
||||
|
||||
// Prints the table header.
|
||||
echo \html_writer::start_tag('thead');
|
||||
echo \html_writer::start_tag('tr');
|
||||
echo \html_writer::start_tag('tr', ['class' => 'qbank-column-list']);
|
||||
$this->print_table_headers();
|
||||
echo \html_writer::end_tag('tr');
|
||||
echo \html_writer::end_tag('thead');
|
||||
@@ -1432,9 +1490,11 @@ class view {
|
||||
/**
|
||||
* Print table headers from child classes.
|
||||
*/
|
||||
public function print_table_headers(): void {
|
||||
protected function print_table_headers(): void {
|
||||
$columnactions = $this->columnmanager->get_column_actions($this);
|
||||
foreach ($this->visiblecolumns as $column) {
|
||||
$column->display_header();
|
||||
$width = $this->columnmanager->get_column_width($column);
|
||||
$column->display_header($columnactions, $width);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,31 +43,37 @@
|
||||
Example context (json):
|
||||
{
|
||||
"sortable": false,
|
||||
"extraclasses": "checkbox pr-3",
|
||||
"tiptitle": "<input id=\"qbheadercheckbox\" name=\"qbheadercheckbox\" type=\"checkbox\" value=\"1\"\n data-action=\"toggle\"\n data-toggle=\"master\"\n data-togglegroup=\"qbank\"\n data-toggle-selectall=\"Select all\"\n data-toggle-deselectall=\"Deselect all\"\n \n\/>\n <label for=\"qbheadercheckbox\" class=\"accesshide\">Select all<\/label>",
|
||||
"extraclasses": "checkbox pr-1",
|
||||
"tiptitle": "<input id=\"qbheadercheckbox\" name=\"qbheadercheckbox\" type=\"checkbox\" value=\"1\" data-action=\"toggle\" data-toggle=\"master\" data-togglegroup=\"qbank\" data-toggle-selectall=\"Select all\" data-toggle-deselectall=\"Deselect all\"><label for=\"qbheadercheckbox\" class=\"accesshide\">Select all<\/label>",
|
||||
"sorttip": true,
|
||||
"tip": "Select questions for bulk actions"
|
||||
"tip": "Select questions for bulk actions",
|
||||
"class": "plugin_class",
|
||||
"name": "plugin_name"
|
||||
}
|
||||
}}
|
||||
<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}}
|
||||
<th class="header align-top {{extraclasses}}" scope="col" data-pluginname="{{class}}" data-name="{{name}}" {{#width}}style="width: {{width}};"{{/width}}>
|
||||
<div class="header-container">
|
||||
<div class="header-text">
|
||||
{{#title}}
|
||||
<div class="title mr-1">{{title}}</div>
|
||||
{{/title}}
|
||||
{{^sortable}}
|
||||
{{^sorttip}}
|
||||
<span class="mr-1">{{tiptitle}}</span>
|
||||
{{/sorttip}}
|
||||
{{#sorttip}}
|
||||
<span class="mr-1" title="{{tip}}">{{{tiptitle}}}</span>
|
||||
{{/sorttip}}
|
||||
{{/sortable}}
|
||||
{{#help}}
|
||||
{{>core/help_icon}}
|
||||
{{/help}}
|
||||
{{#actionmenu}}{{>core/action_menu}}{{/actionmenu}}
|
||||
</div>
|
||||
</div>
|
||||
{{#sortable}}
|
||||
<div class="sorters">
|
||||
{{{sortlinks}}}
|
||||
</div>
|
||||
{{/sortable}}
|
||||
{{#help}}
|
||||
{{>core/help_icon}}
|
||||
{{/help}}
|
||||
</th>
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{{!
|
||||
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_bank_controls
|
||||
|
||||
Flex container for displaying plugin controls at the top of the question bank table.
|
||||
|
||||
Context variables required for this template:
|
||||
* controls - Rendered HTML for controls.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"controls": "<div><button class=\"btn btn-secondary\">Control 1</button><button class=\"btn btn-secondary\">Control 2</button></div>"
|
||||
}
|
||||
}}
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-start mb-2">
|
||||
{{{controls}}}
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
@core @core_question
|
||||
@core @core_question @javascript
|
||||
Feature: A teacher can edit questions in the question bank
|
||||
In order to improve my questions
|
||||
As a teacher
|
||||
@@ -22,6 +22,7 @@ Feature: A teacher can edit questions in the question bank
|
||||
| Test questions | essay | Test question to be edited | Write about whatever you want |
|
||||
And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1"
|
||||
|
||||
@javascript
|
||||
Scenario: Edit a previously created question
|
||||
When I am on the "Test question to be edited" "core_question > edit" page logged in as "teacher1"
|
||||
And I set the following fields to these values:
|
||||
@@ -30,7 +31,7 @@ Feature: A teacher can edit questions in the question bank
|
||||
And I press "id_submitbutton"
|
||||
Then I should see "Edited question name"
|
||||
And I should not see "Test question to be edited"
|
||||
And "Edited question name" row "Created by" column of "categoryquestions" table should contain "Teacher 1"
|
||||
And I should see "Teacher 1"
|
||||
|
||||
Scenario: Edit a previously created question without permission 'moodle/question:moveall' and 'moodle/question:movemine'
|
||||
Given I log in as "admin"
|
||||
@@ -45,7 +46,7 @@ Feature: A teacher can edit questions in the question bank
|
||||
And I press "id_submitbutton"
|
||||
Then I should see "Edited question name"
|
||||
And I should not see "Test question to be edited"
|
||||
And "Edited question name" row "Created by" column of "categoryquestions" table should contain "Teacher 1"
|
||||
And I should see "Teacher 1"
|
||||
|
||||
Scenario: Edit a previously created question without permission 'moodle/question:editall' and 'moodle/question:editmine'
|
||||
Given I log in as "admin"
|
||||
@@ -65,7 +66,7 @@ Feature: A teacher can edit questions in the question bank
|
||||
And I set the field "Question name" to "Edited question name"
|
||||
And I press "Cancel"
|
||||
Then I should see "Test question to be edited"
|
||||
And "Test question to be edited" row "Created by" column of "categoryquestions" table should contain "Admin User"
|
||||
And I should see "Admin User"
|
||||
|
||||
Scenario: A question can have its idnumber removed
|
||||
Given the following "questions" exist:
|
||||
|
||||
@@ -33,6 +33,15 @@ This files describes API changes for code that uses the question API.
|
||||
`set_action_button()` on the `qbank_action_menu` object. This means plugin pages can include their own button like the
|
||||
category management page has "Add category".
|
||||
|
||||
8) The question bank view now has additional hooks for qbank plugins to modify how the question bank is displayed.
|
||||
`\core_question\local\bank\column_manager_base` defines methods for setting the order, visibility and width of columns, and
|
||||
for providing a list of actions to be displayed in each column's header. Any qbank plugin can return a subclass of this from
|
||||
its `plugin_features::get_column_manager()` method to implement its own logic for these methods. Only one column manager can be
|
||||
active at a time, so in the case of multiple enabled plugins providing a column manager, the first one will be used by component
|
||||
name sorted alphabetically.
|
||||
Plugins can also define `plugin_features::get_question_bank_controls()` returning a list of renderables that will be displayed
|
||||
above the question bank table, to provide additional functionality on the question bank page.
|
||||
|
||||
=== 4.2 ===
|
||||
|
||||
1) The question/qengine.js has been deprecated. We create core_question/question_engine
|
||||
|
||||
Reference in New Issue
Block a user