Merge branch 'MDL-84987_main' of https://github.com/marxjohnson/moodle
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
define("qbank_managecategories/categorymanager",["exports","core/reactive","core/str","qbank_managecategories/mutations","qbank_managecategories/events","core/ajax","core/notification","core_form/modalform"],(function(_exports,_reactive,_str,_mutations,_events,_ajax,_notification,_modalform){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.categorymanager=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_modalform=_interopRequireDefault(_modalform);const SELECTORS_CATEGORY_LIST=".qbank_managecategories-categorylist",SELECTORS_CONTEXT=".qbank_managecategories-categorylist[data-contextid]",SELECTORS_CATEGORY_ITEM=".qbank_managecategories-item[data-categoryid]",SELECTORS_CATEGORY_ROOT="#categoryroot",SELECTORS_SHOWDESCRIPTIONS_TOGGLE="#showdescriptions-toggle",SELECTORS_ADD_EDIT_BUTTON='[data-action="addeditcategory"]',CLASSES_DRAGHANDLE="draghandle",CLASSES_DANGER="alert-danger";class CategoryManager extends _reactive.Reactive{moveCategory(categoryId,targetParentId){let precedingSiblingId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const call={methodname:"qbank_managecategories_move_category",args:{pagecontextid:this.state.page.contextid,categoryid:categoryId,targetparentid:targetParentId,precedingsiblingid:precedingSiblingId}};_ajax.default.call([call])[0].then((stateUpdates=>(this.stateManager.processUpdates(stateUpdates),stateUpdates))).catch((error=>{var _document$getElements;_notification.default.addNotification({message:error.message,type:"error"}),null===(_document$getElements=document.getElementsByClassName(CLASSES_DANGER)[0])||void 0===_document$getElements||_document$getElements.scrollIntoView()}))}getTitle(isEdit){return(0,_str.get_string)(isEdit?"editcategory":"addcategory","question")}getSave(isEdit){return isEdit?(0,_str.get_string)("savechanges","core"):(0,_str.get_string)("addcategory","question")}showEditModal(e){const addEditButton=e.target.closest(SELECTORS_ADD_EDIT_BUTTON);if(!addEditButton)return;if(!addEditButton.dataset.actiontype)return;e.preventDefault();const title=categorymanager.getTitle("edit"===addEditButton.dataset.actiontype),save=categorymanager.getSave("edit"===addEditButton.dataset.actiontype),cmid=addEditButton.dataset.cmid,courseid=addEditButton.dataset.courseid,questioncount=addEditButton.dataset.questioncount;let contextid=addEditButton.dataset.contextid,categoryid=null,sortorder=null,parent=null;const categoryItem=e.target.closest(SELECTORS_CATEGORY_ITEM);if(categoryItem){contextid=categoryItem.dataset.contextid,categoryid=categoryItem.dataset.categoryid,sortorder=categoryItem.dataset.sortorder;const parentContext=categoryItem.closest(SELECTORS_CONTEXT);parent=categoryItem.dataset.parent+","+parentContext.dataset.contextid}const modalForm=new _modalform.default({formClass:"qbank_managecategories\\form\\question_category_edit_form",args:{cmid:cmid,courseid:courseid,questioncount:questioncount,contextid:contextid,categoryid:categoryid,sortorder:sortorder,parent:parent},modalConfig:{title:title,large:!0},saveButtonText:save,returnFocus:addEditButton});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(response=>{categorymanager.stateManager.processUpdates(response.detail)})),modalForm.show()}}const categorymanager=new CategoryManager({name:"qtype_managecategories_categorymanager",eventName:_events.eventTypes.qbankManagecategoriesStateUpdated,eventDispatch:_events.notifyQbankManagecategoriesStateUpdated,mutations:_mutations.mutations});_exports.categorymanager=categorymanager;_exports.init=()=>{(async reactive=>{const stateData={page:{contextid:document.querySelector(SELECTORS_CATEGORY_ROOT).dataset.contextid,showdescriptions:document.querySelector(SELECTORS_SHOWDESCRIPTIONS_TOGGLE).checked},categories:[],categoryLists:[]};document.querySelectorAll(SELECTORS_CATEGORY_ITEM).forEach((item=>{stateData.categories.push({id:item.dataset.categoryid,name:item.dataset.categoryname,parent:item.dataset.parent,contextid:item.dataset.contextid,sortorder:item.dataset.sortorder,draghandle:item.classList.contains(CLASSES_DRAGHANDLE)})})),document.querySelectorAll(SELECTORS_CATEGORY_LIST).forEach((categoryList=>{stateData.categoryLists.push({id:categoryList.dataset.categoryid,childCount:categoryList.querySelectorAll(SELECTORS_CATEGORY_ITEM).length})})),reactive.setInitialState(stateData)})(categorymanager)}}));
|
||||
define("qbank_managecategories/categorymanager",["exports","core/reactive","core/str","qbank_managecategories/mutations","qbank_managecategories/events","core/ajax","core/notification","core_form/modalform"],(function(_exports,_reactive,_str,_mutations,_events,_ajax,_notification,_modalform){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=_exports.categorymanager=void 0,_ajax=_interopRequireDefault(_ajax),_notification=_interopRequireDefault(_notification),_modalform=_interopRequireDefault(_modalform);const SELECTORS_CATEGORY_LIST=".qbank_managecategories-categorylist",SELECTORS_CONTEXT=".qbank_managecategories-categorylist[data-contextid]",SELECTORS_CATEGORY_ITEM=".qbank_managecategories-item[data-categoryid]",SELECTORS_CATEGORY_ROOT="#categoryroot",SELECTORS_SHOWDESCRIPTIONS_TOGGLE="#showdescriptions-toggle",SELECTORS_ADD_EDIT_BUTTON='[data-action="addeditcategory"]',CLASSES_DRAGHANDLE="draghandle",CLASSES_DANGER="alert-danger";class CategoryManager extends _reactive.Reactive{moveCategory(categoryId,targetParentId){let precedingSiblingId=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const call={methodname:"qbank_managecategories_move_category",args:{pagecontextid:this.state.page.contextid,categoryid:categoryId,targetparentid:targetParentId,precedingsiblingid:precedingSiblingId}};_ajax.default.call([call])[0].then((stateUpdates=>(this.stateManager.processUpdates(stateUpdates),stateUpdates))).catch((error=>{var _document$getElements;_notification.default.addNotification({message:error.message,type:"error"}),null===(_document$getElements=document.getElementsByClassName(CLASSES_DANGER)[0])||void 0===_document$getElements||_document$getElements.scrollIntoView()}))}getTitle(isEdit){return(0,_str.get_string)(isEdit?"editcategory":"addcategory","question")}getSave(isEdit){return isEdit?(0,_str.get_string)("savechanges","core"):(0,_str.get_string)("addcategory","question")}showEditModal(e){const addEditButton=e.target.closest(SELECTORS_ADD_EDIT_BUTTON);if(!addEditButton)return;if(!addEditButton.dataset.actiontype)return;e.preventDefault();const title=categorymanager.getTitle("edit"===addEditButton.dataset.actiontype),save=categorymanager.getSave("edit"===addEditButton.dataset.actiontype),cmid=addEditButton.dataset.cmid,courseid=addEditButton.dataset.courseid,questioncount=addEditButton.dataset.questioncount;let contextid=addEditButton.dataset.contextid,categoryid=null,sortorder=null,parent=null;const categoryItem=e.target.closest(SELECTORS_CATEGORY_ITEM);if(categoryItem){contextid=categoryItem.dataset.contextid,categoryid=categoryItem.dataset.categoryid,sortorder=categoryItem.dataset.sortorder;const parentContext=categoryItem.closest(SELECTORS_CONTEXT);parent=categoryItem.dataset.parent+","+parentContext.dataset.contextid}const modalForm=new _modalform.default({formClass:"qbank_managecategories\\form\\question_category_edit_form",args:{cmid:cmid,courseid:courseid,questioncount:questioncount,contextid:contextid,categoryid:categoryid,sortorder:sortorder,parent:parent},modalConfig:{title:title,large:!0},saveButtonText:save,returnFocus:addEditButton});modalForm.addEventListener(modalForm.events.FORM_SUBMITTED,(response=>{categorymanager.stateManager.processUpdates(response.detail)})),modalForm.show()}updateCategoryName(categoryId,newName){categorymanager.stateManager.setReadOnly(!1),categorymanager.stateManager.processUpdate("categories","put",{id:categoryId,name:newName}),categorymanager.stateManager.setReadOnly(!0)}}const categorymanager=new CategoryManager({name:"qtype_managecategories_categorymanager",eventName:_events.eventTypes.qbankManagecategoriesStateUpdated,eventDispatch:_events.notifyQbankManagecategoriesStateUpdated,mutations:_mutations.mutations});_exports.categorymanager=categorymanager;_exports.init=()=>{(async reactive=>{const stateData={page:{contextid:document.querySelector(SELECTORS_CATEGORY_ROOT).dataset.contextid,showdescriptions:document.querySelector(SELECTORS_SHOWDESCRIPTIONS_TOGGLE).checked},categories:[],categoryLists:[]};document.querySelectorAll(SELECTORS_CATEGORY_ITEM).forEach((item=>{stateData.categories.push({id:item.dataset.categoryid,name:item.dataset.categoryname,parent:item.dataset.parent,contextid:item.dataset.contextid,sortorder:item.dataset.sortorder,draghandle:item.classList.contains(CLASSES_DRAGHANDLE)})})),document.querySelectorAll(SELECTORS_CATEGORY_LIST).forEach((categoryList=>{stateData.categoryLists.push({id:categoryList.dataset.categoryid,childCount:categoryList.querySelectorAll(SELECTORS_CATEGORY_ITEM).length})})),reactive.setInitialState(stateData)})(categorymanager)}}));
|
||||
|
||||
//# sourceMappingURL=categorymanager.min.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -25,6 +25,7 @@ import {categorymanager} from 'qbank_managecategories/categorymanager';
|
||||
import Templates from 'core/templates';
|
||||
import Modal from "core/modal";
|
||||
import {get_string as getString} from "core/str";
|
||||
import {eventTypes as inplaceEditableEventTypes} from 'core/local/inplace_editable/events';
|
||||
|
||||
export default class extends BaseComponent {
|
||||
|
||||
@@ -35,6 +36,7 @@ export default class extends BaseComponent {
|
||||
CATEGORY_ITEM: '.qbank_managecategories-item[data-categoryid]',
|
||||
CATEGORY_CONTENTS: '.qbank_managecategories-item > .container',
|
||||
EDIT_BUTTON: '[data-action="addeditcategory"]',
|
||||
EDITABLE_CATEGORY_NAME: '.inplaceeditable[data-itemtype="categoryname"]',
|
||||
MOVE_BUTTON: '[role="menuitem"][data-actiontype="move"]',
|
||||
CONTEXT: '.qbank_managecategories-categorylist[data-contextid]',
|
||||
MODAL_CATEGORY_ITEM: '.modal_category_item[data-movingcategoryid]',
|
||||
@@ -59,6 +61,10 @@ export default class extends BaseComponent {
|
||||
this.addEventListener(this.getElement(this.selectors.EDIT_BUTTON), 'click', categorymanager.showEditModal);
|
||||
const moveButton = this.getElement(this.selectors.MOVE_BUTTON);
|
||||
this.addEventListener(moveButton, 'click', this.showMoveModal);
|
||||
this.addEventListener(this.getElement(), inplaceEditableEventTypes.elementUpdated, (e) => {
|
||||
const editable = e.target.closest(this.selectors.EDITABLE_CATEGORY_NAME);
|
||||
categorymanager.updateCategoryName(editable.dataset.itemid, editable.dataset.value);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@@ -192,6 +198,8 @@ export default class extends BaseComponent {
|
||||
// When the template context is added or updated, re-render the content.
|
||||
{watch: `categories[${this.element.dataset.categoryid}].templatecontext:created`, handler: this.rerender},
|
||||
{watch: `categories[${this.element.dataset.categoryid}].templatecontext:updated`, handler: this.rerender},
|
||||
// When the name is updated, update it in the element data set.
|
||||
{watch: `categories[${this.element.dataset.categoryid}].name:updated`, handler: this.updateName},
|
||||
// When a new category is created, check whether we need to add a child list to this category.
|
||||
{watch: `categories:created`, handler: this.checkChildList},
|
||||
];
|
||||
@@ -459,4 +467,14 @@ export default class extends BaseComponent {
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the name in the category item element's data, for building move targets.
|
||||
*
|
||||
* @param {Object} args
|
||||
* @param {Object} args.element
|
||||
*/
|
||||
updateName({element}) {
|
||||
this.getElement().dataset.categoryname = element.name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,6 +209,12 @@ class CategoryManager extends Reactive {
|
||||
// Show the form.
|
||||
modalForm.show();
|
||||
}
|
||||
|
||||
updateCategoryName(categoryId, newName) {
|
||||
categorymanager.stateManager.setReadOnly(false);
|
||||
categorymanager.stateManager.processUpdate('categories', 'put', {id: categoryId, name: newName});
|
||||
categorymanager.stateManager.setReadOnly(true);
|
||||
}
|
||||
}
|
||||
|
||||
export const categorymanager = new CategoryManager({
|
||||
|
||||
@@ -91,6 +91,16 @@ class category implements renderable, templatable {
|
||||
$questionbankurl = new moodle_url('/question/edit.php', $params);
|
||||
$questionbankurl->param('cat', helper::combine_id_context($this->category));
|
||||
$categoryname = format_string($this->category->name, true, ['context' => $this->context, 'escape' => false]);
|
||||
$categorylink = new category_link(
|
||||
$categoryname,
|
||||
$questionbankurl,
|
||||
$this->category->questioncount,
|
||||
);
|
||||
$editablename = new editable_name(
|
||||
$this->category,
|
||||
$output->render($categorylink),
|
||||
$canmanagecategory,
|
||||
);
|
||||
$idnumber = null;
|
||||
if ($this->category->idnumber !== null && $this->category->idnumber !== '') {
|
||||
$idnumber = $this->category->idnumber;
|
||||
@@ -212,10 +222,10 @@ class category implements renderable, templatable {
|
||||
$itemdata = [
|
||||
'categoryid' => $this->category->id,
|
||||
'contextid' => $this->category->contextid,
|
||||
'questionbankurl' => $questionbankurl->out(false),
|
||||
'categoryname' => $categoryname,
|
||||
'categorylink' => $categorylink->export_for_template($output),
|
||||
'editablename' => $editablename->export_for_template($output),
|
||||
'idnumber' => $idnumber,
|
||||
'questioncount' => $this->category->questioncount,
|
||||
'categorydesc' => $categorydesc,
|
||||
'editactionmenu' => $menu->export_for_template($output),
|
||||
'draghandle' => $canmanagecategory && $this->canreorder,
|
||||
@@ -227,7 +237,4 @@ class category implements renderable, templatable {
|
||||
];
|
||||
return $itemdata;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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_managecategories\output;
|
||||
|
||||
use core\output\renderable;
|
||||
use core\output\renderer_base;
|
||||
use core\output\templatable;
|
||||
use core\url;
|
||||
|
||||
/**
|
||||
* Category name and question count link.
|
||||
*
|
||||
* @package qbank_managecategories
|
||||
* @copyright 2025 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 category_link implements renderable, templatable {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The question bank name.
|
||||
* @param url $questionbankurl The URL for the question bank link.
|
||||
* @param int $questioncount The number of questions in the bank.
|
||||
*/
|
||||
public function __construct(
|
||||
/** @var string $name The question bank name. */
|
||||
protected string $name,
|
||||
/** @var url $questionbankurl The URL for the question bank link. */
|
||||
protected url $questionbankurl,
|
||||
/** @var int $questioncount The number of questions in the bank. */
|
||||
protected int $questioncount
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function export_for_template(renderer_base $output): array {
|
||||
return [
|
||||
'categoryname' => $this->name,
|
||||
'questionbankurl' => $this->questionbankurl->out(),
|
||||
'questioncount' => $this->questioncount,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
namespace qbank_managecategories\output;
|
||||
|
||||
use core\context;
|
||||
use core\output\inplace_editable;
|
||||
use core\output\named_templatable;
|
||||
use core\output\renderable;
|
||||
use core\url;
|
||||
use core_external\external_api;
|
||||
use core_question\category_manager;
|
||||
use core_question\output\question_category_selector;
|
||||
use qbank_managecategories\helper;
|
||||
|
||||
/**
|
||||
* Category name inplace editable
|
||||
*
|
||||
* @package qbank_managecategories
|
||||
* @copyright 2025 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 editable_name extends inplace_editable implements named_templatable, renderable {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param \stdClass $category The category record we are editing.
|
||||
* @param string $categorylink The link to the question category.
|
||||
* @param bool $editable Whether the user has permission to edit this category.
|
||||
*/
|
||||
public function __construct(\stdClass $category, string $categorylink, bool $editable) {
|
||||
parent::__construct(
|
||||
'qbank_managecategories',
|
||||
'categoryname',
|
||||
$category->id,
|
||||
$editable,
|
||||
$categorylink,
|
||||
$category->name,
|
||||
get_string('editcategorynamehint', 'qbank_managecategories'),
|
||||
get_string('editcategoryname', 'qbank_managecategories', $category->name),
|
||||
);
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function get_template_name(\renderer_base $renderer): string {
|
||||
return 'core/inplace_editable';
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the new name and return the updated output component.
|
||||
*
|
||||
* @param int $categoryid The ID of the category to update
|
||||
* @param string $newname The new name to save.
|
||||
* @return self
|
||||
*/
|
||||
public static function callback(int $categoryid, string $newname): self {
|
||||
global $DB, $OUTPUT;
|
||||
|
||||
$context = context::instance_by_id($DB->get_field('question_categories', 'contextid', ['id' => $categoryid]));
|
||||
external_api::validate_context($context);
|
||||
|
||||
$manager = new category_manager();
|
||||
$manager->update_category($categoryid, '', $newname);
|
||||
|
||||
$updatedcategory = $DB->get_record('question_categories', ['id' => $categoryid]);
|
||||
|
||||
$questionbankurl = new url(
|
||||
'/question/edit.php',
|
||||
[
|
||||
'cmid' => $context->instanceid,
|
||||
'cat' => helper::combine_id_context($updatedcategory),
|
||||
],
|
||||
);
|
||||
$categoryname = format_string($updatedcategory->name, true, ['context' => $context, 'escape' => false]);
|
||||
$questioncountsql = question_category_selector::question_count_sql(categoryparam: '?');
|
||||
$questioncount = $DB->get_field_sql($questioncountsql, [$categoryid]);
|
||||
|
||||
$categorylink = new category_link(
|
||||
$categoryname,
|
||||
$questionbankurl,
|
||||
$questioncount,
|
||||
);
|
||||
|
||||
// Prepare the element for the output.
|
||||
// The $editable argument is always true because `update_category()` throws an exception otherwise.
|
||||
return new self($updatedcategory, $OUTPUT->render($categorylink), true);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@ $string['categorymoved'] = 'The category has been moved successfully.';
|
||||
$string['confirmdelete'] = 'Delete category "{$a}"?. Any child categories will be moved to this category\'s parent.';
|
||||
$string['descriptionnotdisplayed'] = 'Categories description not displayed';
|
||||
$string['displaydescription'] = 'Categories description displayed';
|
||||
$string['editcategoryname'] = 'New name for {$a}';
|
||||
$string['editcategorynamehint'] = 'Edit category name';
|
||||
$string['error:category'] = 'You must select a valid category';
|
||||
$string['idnumberexists'] = 'ID number already in use. Change it to move or update category.';
|
||||
$string['lastcategoryinthiscontext'] = 'This category is the only one in this context. You need to add another category if this category is to be moved.';
|
||||
|
||||
@@ -44,3 +44,17 @@ function qbank_managecategories_user_preferences(): array {
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* In place editing callback for categories.
|
||||
*
|
||||
* @param string $itemtype type of the item.
|
||||
* @param int $itemid id of the category being edited.
|
||||
* @param string $newvalue the new value for the edited field.
|
||||
* @return \core\output\inplace_editable
|
||||
*/
|
||||
function qbank_managecategories_inplace_editable(string $itemtype, int $itemid, string $newvalue): \core\output\inplace_editable {
|
||||
if ($itemtype === 'categoryname') {
|
||||
return \qbank_managecategories\output\editable_name::callback($itemid, $newvalue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,28 +33,34 @@
|
||||
Example context (json):
|
||||
{
|
||||
"categoryid": "1",
|
||||
"questionbankurl": "question/edit.php?cmid=123",
|
||||
"categoryname": "Default for Miscellaneous & < > \" '",
|
||||
"categoryname": {
|
||||
"questionbankurl": "question/edit.php?cmid=123",
|
||||
"categoryname": "Default for Miscellaneous & < > \" '",
|
||||
"questioncount": "1"
|
||||
},
|
||||
"idnumber": "1",
|
||||
"questioncount": " 1",
|
||||
"categorydesc": "The default category for questions shared in context Miscellaneous",
|
||||
"children": "",
|
||||
"newchildtooltip": "New child of Default for Miscellaneous"
|
||||
}
|
||||
}}
|
||||
<strong>
|
||||
<a title="{{#str}}editquestions, question{{/str}}" href="{{{questionbankurl}}}" role="button">
|
||||
{{categoryname}}
|
||||
{{#idnumber}}
|
||||
<span class="badge text-bg-primary">
|
||||
<span class="accesshide">
|
||||
{{#str}}idnumber, question{{/str}}
|
||||
</span>
|
||||
{{idnumber}}
|
||||
</span>
|
||||
{{/idnumber}}
|
||||
({{questioncount}})
|
||||
</a>
|
||||
{{#editablename}}
|
||||
{{> core/inplace_editable }}
|
||||
{{/editablename}}
|
||||
{{^editablename}}
|
||||
{{#categoryname}}
|
||||
{{> qbank_managecategories/category_link }}
|
||||
{{/categoryname}}
|
||||
{{/editablename}}
|
||||
{{#idnumber}}
|
||||
<span class="badge text-bg-primary">
|
||||
<span class="accesshide">
|
||||
{{#str}}idnumber, question{{/str}}
|
||||
</span>
|
||||
{{idnumber}}
|
||||
</span>
|
||||
{{/idnumber}}
|
||||
</strong>
|
||||
{{^children}}
|
||||
{{> qbank_managecategories/newchild }}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
{{!
|
||||
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_managecategories/category_name
|
||||
|
||||
Partial template for rendering the category name and question count as a link
|
||||
|
||||
Comments around whitespace keep the output to one line so it works with inplace_editable::get_linkeverything().
|
||||
|
||||
Context variables required for this template:
|
||||
* questionbankurl - Url for corresponding question bank.
|
||||
* categoryname - Category name. Raw string. Will be HTML-escaped during rendering.
|
||||
* questioncount - Number of question in question bank.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"questionbankurl": "question/edit.php?cmid=123",
|
||||
"categoryname": "Default for Miscellaneous & < > \" '",
|
||||
"questioncount": "1"
|
||||
}
|
||||
}}
|
||||
|
||||
<a title="{{#str}}editquestions, question{{/str}}" href="{{{questionbankurl}}}" role="button">{{!
|
||||
}}{{categoryname}} ({{questioncount}}){{!
|
||||
}}</a>
|
||||
@@ -153,3 +153,10 @@ Feature: A teacher can put questions in categories in the question bank
|
||||
And I apply question bank filter "Category" with value "Used category"
|
||||
Then the "Category" field validity check should return "true"
|
||||
And I should see "Test question to be moved"
|
||||
|
||||
Scenario: A category name can be edited in place
|
||||
Given I am on the "Qbank 1" "core_question > question categories" page
|
||||
When I set the field "Edit category name" in the "Subcategory & < > \" ' &" "list_item" to "Subcategory edited & < > \" ' &"
|
||||
Then I should not see "Subcategory & < > \" ' &"
|
||||
And I should see "Subcategory edited & < > \" ' &"
|
||||
And ".qbank_managecategories-item[data-categoryname^='Subcategory edited']" "css_element" should exist
|
||||
|
||||
@@ -263,12 +263,14 @@ class category_manager {
|
||||
int $updateid,
|
||||
string $newparent,
|
||||
string $newname,
|
||||
string $newinfo,
|
||||
?string $newinfo = null,
|
||||
string $newinfoformat = FORMAT_HTML,
|
||||
?string $idnumber = null,
|
||||
?int $sortorder = null,
|
||||
): void {
|
||||
global $DB;
|
||||
global $DB, $CFG;
|
||||
require_once($CFG->libdir . '/questionlib.php');
|
||||
|
||||
if (empty($newname)) {
|
||||
throw new moodle_exception('categorynamecantbeblank', 'question');
|
||||
}
|
||||
@@ -284,6 +286,10 @@ class category_manager {
|
||||
$tocontextid = $oldcat->contextid;
|
||||
}
|
||||
|
||||
if (is_null($newinfo)) {
|
||||
$newinfo = $oldcat->info;
|
||||
}
|
||||
|
||||
// Check permissions.
|
||||
$fromcontext = context::instance_by_id($oldcat->contextid);
|
||||
$this->require_manage_category($fromcontext);
|
||||
|
||||
@@ -92,25 +92,9 @@ class question_category_selector implements renderable, templatable {
|
||||
[$insql, $inparams] = $DB->get_in_or_equal($validcontexts);
|
||||
|
||||
$topwhere = $top ? '' : 'AND c.parent <> 0';
|
||||
$statuscondition = "AND (qv.status = '" . question_version_status::QUESTION_STATUS_READY . "' " .
|
||||
" OR qv.status = '" . question_version_status::QUESTION_STATUS_DRAFT . "' )";
|
||||
$substatuscondition = "AND v.status <> '" . question_version_status::QUESTION_STATUS_HIDDEN . "' ";
|
||||
$countsql = $this->question_count_sql($showallversions);
|
||||
$sql = "SELECT c.*,
|
||||
(SELECT COUNT(1)
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE q.parent = '0'
|
||||
$statuscondition
|
||||
AND c.id = qbe.questioncategoryid
|
||||
AND ({$showallversions} = 1
|
||||
OR (qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id $substatuscondition)
|
||||
)
|
||||
)
|
||||
) AS questioncount
|
||||
({$countsql}) AS questioncount
|
||||
FROM {question_categories} c
|
||||
WHERE c.contextid {$insql} {$topwhere}
|
||||
ORDER BY {$sortorder}";
|
||||
@@ -118,6 +102,35 @@ class question_category_selector implements renderable, templatable {
|
||||
return $DB->get_records_sql($sql, $inparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL query for getting a count of questions in a category.
|
||||
*
|
||||
* @param int $showallversions 1 to show all versions not only the latest.
|
||||
* @param string $categoryparam Category ID parameter or field.
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public static function question_count_sql(int $showallversions = 0, string $categoryparam = 'c.id'): string {
|
||||
$statuscondition = "AND (qv.status = '" . question_version_status::QUESTION_STATUS_READY . "' " .
|
||||
" OR qv.status = '" . question_version_status::QUESTION_STATUS_DRAFT . "' )";
|
||||
$substatuscondition = "AND v.status <> '" . question_version_status::QUESTION_STATUS_HIDDEN . "' ";
|
||||
return "
|
||||
SELECT COUNT(1)
|
||||
FROM {question} q
|
||||
JOIN {question_versions} qv ON qv.questionid = q.id
|
||||
JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
|
||||
WHERE q.parent = '0'
|
||||
$statuscondition
|
||||
AND ({$showallversions} = 1
|
||||
OR (qv.version = (SELECT MAX(v.version)
|
||||
FROM {question_versions} v
|
||||
JOIN {question_bank_entries} be ON be.id = v.questionbankentryid
|
||||
WHERE be.id = qbe.id $substatuscondition)
|
||||
)
|
||||
)
|
||||
AND qbe.questioncategoryid = {$categoryparam}
|
||||
";
|
||||
}
|
||||
|
||||
/**
|
||||
* Output an array of question categories.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user