MDL-86065 customfield: Allow to select shared categories on each entity
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
define("core_customfield/repository/toggle_shared",["exports","core/ajax"],(function(_exports,_ajax){var obj;
|
||||
/**
|
||||
* Module to handle toggle shared category AJAX requests
|
||||
*
|
||||
* @module core_customfield/repository/toggle_shared
|
||||
* @copyright 2025 David Carrillo <davidmc@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.toggleCategory=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.toggleCategory=(categoryId,component,area,itemid,state)=>{const request={methodname:"core_customfield_toggle_shared",args:{categoryid:categoryId,component:component,area:area,itemid:itemid,state:state}};return _ajax.default.call([request])[0]}}));
|
||||
|
||||
//# sourceMappingURL=toggle_shared.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"toggle_shared.min.js","sources":["../../src/repository/toggle_shared.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 * Module to handle toggle shared category AJAX requests\n *\n * @module core_customfield/repository/toggle_shared\n * @copyright 2025 David Carrillo <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\n\n/**\n * Toggle shared category enabled\n *\n * @method\n * @param {Number} categoryId\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n * @param {Boolean} state\n * @return {Promise}\n */\nexport const toggleCategory = (categoryId, component, area, itemid, state) => {\n const request = {\n methodname: 'core_customfield_toggle_shared',\n args: {categoryid: categoryId, component: component, area: area, itemid: itemid, state: state}\n };\n\n return Ajax.call([request])[0];\n};\n"],"names":["categoryId","component","area","itemid","state","request","methodname","args","categoryid","Ajax","call"],"mappings":";;;;;;;wKAoC8B,CAACA,WAAYC,UAAWC,KAAMC,OAAQC,eAC1DC,QAAU,CACZC,WAAY,iCACZC,KAAM,CAACC,WAAYR,WAAYC,UAAWA,UAAWC,KAAMA,KAAMC,OAAQA,OAAQC,MAAOA,eAGrFK,cAAKC,KAAK,CAACL,UAAU"}
|
||||
@@ -0,0 +1,3 @@
|
||||
define("core_customfield/toggle_shared",["exports","core/inplace_editable","core/notification","core/pending","core_customfield/repository/toggle_shared"],(function(_exports,_inplace_editable,_notification,_pending,_toggle_shared){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);let initialized=!1;_exports.init=()=>{initialized||(document.addEventListener("click",(event=>{const sharedToggle=event.target.closest('[data-action="shared-toggle"]');if(sharedToggle){const pendingPromise=new _pending.default("core_customfield/shared:toggle"),categoryId=sharedToggle.dataset.id,component=sharedToggle.dataset.component,area=sharedToggle.dataset.area,itemid=sharedToggle.dataset.itemid,sharedStateToggle=+!Number(sharedToggle.dataset.state);(0,_toggle_shared.toggleCategory)(categoryId,component,area,itemid,sharedStateToggle).then((()=>(sharedToggle.dataset.state=sharedStateToggle,pendingPromise.resolve()))).catch(_notification.default.exception)}})),initialized=!0)}}));
|
||||
|
||||
//# sourceMappingURL=toggle_shared.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"toggle_shared.min.js","sources":["../src/toggle_shared.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 * Custom fields shared category toggle\n *\n * @module core_customfield/toggle_shared\n * @copyright 2025 David Carrillo <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport 'core/inplace_editable';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {toggleCategory} from 'core_customfield/repository/toggle_shared';\n\nlet initialized = false;\n\n/**\n * Initialise toggle\n */\nexport const init = () => {\n if (initialized) {\n // We already added the event listeners (can be called multiple times by mustache template).\n return;\n }\n\n document.addEventListener('click', event => {\n // Toggle shared category.\n const sharedToggle = event.target.closest('[data-action=\"shared-toggle\"]');\n if (sharedToggle) {\n const pendingPromise = new Pending('core_customfield/shared:toggle');\n const categoryId = sharedToggle.dataset.id;\n const component = sharedToggle.dataset.component;\n const area = sharedToggle.dataset.area;\n const itemid = sharedToggle.dataset.itemid;\n const sharedStateToggle = +!Number(sharedToggle.dataset.state);\n\n toggleCategory(categoryId, component, area, itemid, sharedStateToggle)\n .then(() => {\n sharedToggle.dataset.state = sharedStateToggle;\n return pendingPromise.resolve();\n })\n .catch(Notification.exception);\n }\n });\n\n initialized = true;\n};\n"],"names":["initialized","document","addEventListener","event","sharedToggle","target","closest","pendingPromise","Pending","categoryId","dataset","id","component","area","itemid","sharedStateToggle","Number","state","then","resolve","catch","Notification","exception"],"mappings":"weA8BIA,aAAc,gBAKE,KACZA,cAKJC,SAASC,iBAAiB,SAASC,cAEzBC,aAAeD,MAAME,OAAOC,QAAQ,oCACtCF,aAAc,OACRG,eAAiB,IAAIC,iBAAQ,kCAC7BC,WAAaL,aAAaM,QAAQC,GAClCC,UAAYR,aAAaM,QAAQE,UACjCC,KAAOT,aAAaM,QAAQG,KAC5BC,OAASV,aAAaM,QAAQI,OAC9BC,oBAAsBC,OAAOZ,aAAaM,QAAQO,yCAEzCR,WAAYG,UAAWC,KAAMC,OAAQC,mBAC/CG,MAAK,KACFd,aAAaM,QAAQO,MAAQF,kBACtBR,eAAeY,aAEzBC,MAAMC,sBAAaC,eAIhCtB,aAAc"}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Module to handle toggle shared category AJAX requests
|
||||
*
|
||||
* @module core_customfield/repository/toggle_shared
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
|
||||
/**
|
||||
* Toggle shared category enabled
|
||||
*
|
||||
* @method
|
||||
* @param {Number} categoryId
|
||||
* @param {String} component
|
||||
* @param {String} area
|
||||
* @param {Number} itemid
|
||||
* @param {Boolean} state
|
||||
* @return {Promise}
|
||||
*/
|
||||
export const toggleCategory = (categoryId, component, area, itemid, state) => {
|
||||
const request = {
|
||||
methodname: 'core_customfield_toggle_shared',
|
||||
args: {categoryid: categoryId, component: component, area: area, itemid: itemid, state: state}
|
||||
};
|
||||
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Custom fields shared category toggle
|
||||
*
|
||||
* @module core_customfield/toggle_shared
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import 'core/inplace_editable';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import {toggleCategory} from 'core_customfield/repository/toggle_shared';
|
||||
|
||||
let initialized = false;
|
||||
|
||||
/**
|
||||
* Initialise toggle
|
||||
*/
|
||||
export const init = () => {
|
||||
if (initialized) {
|
||||
// We already added the event listeners (can be called multiple times by mustache template).
|
||||
return;
|
||||
}
|
||||
|
||||
document.addEventListener('click', event => {
|
||||
// Toggle shared category.
|
||||
const sharedToggle = event.target.closest('[data-action="shared-toggle"]');
|
||||
if (sharedToggle) {
|
||||
const pendingPromise = new Pending('core_customfield/shared:toggle');
|
||||
const categoryId = sharedToggle.dataset.id;
|
||||
const component = sharedToggle.dataset.component;
|
||||
const area = sharedToggle.dataset.area;
|
||||
const itemid = sharedToggle.dataset.itemid;
|
||||
const sharedStateToggle = +!Number(sharedToggle.dataset.state);
|
||||
|
||||
toggleCategory(categoryId, component, area, itemid, sharedStateToggle)
|
||||
.then(() => {
|
||||
sharedToggle.dataset.state = sharedStateToggle;
|
||||
return pendingPromise.resolve();
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
}
|
||||
});
|
||||
|
||||
initialized = true;
|
||||
};
|
||||
@@ -467,4 +467,24 @@ class api {
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a shared category is enabled for the given entity
|
||||
*
|
||||
* @param int $categoryid
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param int $itemid
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_shared_category_enabled(int $categoryid, string $component, string $area, int $itemid): bool {
|
||||
$sql = "categoryid = :categoryid AND component = :component AND area = :area AND itemid = :itemid";
|
||||
$params = [
|
||||
'categoryid' => $categoryid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
];
|
||||
return shared::record_exists_select($sql, $params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_customfield\event;
|
||||
|
||||
use context;
|
||||
use core_customfield\shared;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Custom field shared category usage disabled event class.
|
||||
*
|
||||
* @package core_customfield
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class shared_category_usage_disabled extends \core\event\base {
|
||||
|
||||
/**
|
||||
* Initialise the event data.
|
||||
*/
|
||||
protected function init(): void {
|
||||
$this->data['objecttable'] = 'customfield_shared';
|
||||
$this->data['crud'] = 'd';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance from a shared object
|
||||
*
|
||||
* @param shared $shared
|
||||
* @param context $context
|
||||
* @return category_updated
|
||||
*/
|
||||
public static function create_from_object(shared $shared, context $context): shared_category_usage_disabled {
|
||||
$eventparams = [
|
||||
'objectid' => $shared->get('id'),
|
||||
'context' => $context,
|
||||
'other' => [
|
||||
'categoryid' => $shared->get('categoryid'),
|
||||
'component' => $shared->get('component'),
|
||||
'area' => $shared->get('area'),
|
||||
'itemid' => $shared->get('itemid'),
|
||||
],
|
||||
];
|
||||
$event = self::create($eventparams);
|
||||
$event->add_record_snapshot($event->objecttable, $shared->to_record());
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns localised general event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name(): string {
|
||||
return get_string('eventsharedcategoryusagedisabled', 'core_customfield');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-localised description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return "The user with ID '$this->userid' disabled usage of shared category with ID '{$this->other['categoryid']}'" .
|
||||
" in '{$this->other['component']}/{$this->other['area']}'";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_customfield\event;
|
||||
|
||||
use context;
|
||||
use core_customfield\shared;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Custom field shared category usage enabled event class.
|
||||
*
|
||||
* @package core_customfield
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class shared_category_usage_enabled extends \core\event\base {
|
||||
|
||||
/**
|
||||
* Initialise the event data.
|
||||
*/
|
||||
protected function init(): void {
|
||||
$this->data['objecttable'] = 'customfield_shared';
|
||||
$this->data['crud'] = 'c';
|
||||
$this->data['edulevel'] = self::LEVEL_OTHER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance from a shared object
|
||||
*
|
||||
* @param shared $shared
|
||||
* @param context $context
|
||||
* @return category_updated
|
||||
*/
|
||||
public static function create_from_object(shared $shared, context $context): shared_category_usage_enabled {
|
||||
$eventparams = [
|
||||
'objectid' => $shared->get('id'),
|
||||
'context' => $context,
|
||||
'other' => [
|
||||
'categoryid' => $shared->get('categoryid'),
|
||||
'component' => $shared->get('component'),
|
||||
'area' => $shared->get('area'),
|
||||
'itemid' => $shared->get('itemid'),
|
||||
],
|
||||
];
|
||||
$event = self::create($eventparams);
|
||||
$event->add_record_snapshot($event->objecttable, $shared->to_record());
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns localised general event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name(): string {
|
||||
return get_string('eventsharedcategoryusageenabled', 'core_customfield');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-localised description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description(): string {
|
||||
return "The user with ID '$this->userid' enabled usage of shared category with ID '{$this->other['categoryid']}'" .
|
||||
" in '{$this->other['component']}/{$this->other['area']}'";
|
||||
}
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_customfield\external;
|
||||
|
||||
use core_customfield\event\shared_category_usage_disabled;
|
||||
use core_customfield\event\shared_category_usage_enabled;
|
||||
use core_customfield\handler;
|
||||
use core_customfield\shared;
|
||||
use core_external\external_api;
|
||||
use core_external\external_value;
|
||||
use core_external\external_function_parameters;
|
||||
|
||||
/**
|
||||
* External method for toggling shared categories
|
||||
*
|
||||
* @package core_customfield
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class toggle_shared_category extends external_api {
|
||||
/**
|
||||
* External method parameters
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters(): external_function_parameters {
|
||||
return new external_function_parameters([
|
||||
'categoryid' => new external_value(PARAM_INT, 'Category ID'),
|
||||
'component' => new external_value(PARAM_COMPONENT, 'Component'),
|
||||
'area' => new external_value(PARAM_AREA, 'Area'),
|
||||
'itemid' => new external_value(PARAM_INT, 'Item ID'),
|
||||
'state' => new external_value(PARAM_BOOL, 'New state'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* External method execution
|
||||
*
|
||||
* @param int $categoryid
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param int $itemid
|
||||
* @param bool $state
|
||||
* @return bool
|
||||
*/
|
||||
public static function execute(int $categoryid, string $component, string $area, int $itemid, bool $state): bool {
|
||||
[
|
||||
'categoryid' => $categoryid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
'state' => $state,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'categoryid' => $categoryid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
'state' => $state,
|
||||
]);
|
||||
|
||||
// Validate context.
|
||||
$context = \core\context\system::instance();
|
||||
self::validate_context($context);
|
||||
|
||||
$handler = handler::get_handler($component, $area, $itemid);
|
||||
if (!$handler->can_configure()) {
|
||||
throw new \moodle_exception('nopermissions', 'error', '', get_string('customfield:configureshared', 'core_role'));
|
||||
}
|
||||
|
||||
global $DB;
|
||||
|
||||
$params = [
|
||||
'categoryid' => $categoryid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
];
|
||||
|
||||
if ($state) {
|
||||
$record = new shared(0, (object) $params);
|
||||
$record->create();
|
||||
shared_category_usage_enabled::create_from_object($record, $handler->get_configuration_context())->trigger();
|
||||
} else {
|
||||
$record = shared::get_record($params);
|
||||
$DB->delete_records('customfield_shared', $params);
|
||||
shared_category_usage_disabled::create_from_object($record, $handler->get_configuration_context())->trigger();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* External method return value
|
||||
*
|
||||
* @return external_value
|
||||
*/
|
||||
public static function execute_returns(): external_value {
|
||||
return new external_value(PARAM_BOOL);
|
||||
}
|
||||
}
|
||||
@@ -519,15 +519,27 @@ abstract class handler {
|
||||
/**
|
||||
* Returns array of categories, each of them contains a list of fields definitions.
|
||||
*
|
||||
* @param bool $ismanagementpage Whether we are on the management page to show all shared categories or not.
|
||||
* @return category_controller[]
|
||||
*/
|
||||
public function get_categories_with_fields(): array {
|
||||
public function get_categories_with_fields(bool $ismanagementpage = false): array {
|
||||
if ($this->categories === null) {
|
||||
$sharedcategories = [];
|
||||
$this->categories = api::get_categories_with_fields($this->get_component(), $this->get_area(), $this->get_itemid());
|
||||
// Avoid duplication when we are in the shared custom fields page.
|
||||
if ($this->get_component() !== 'core_customfield' && $this->get_area() !== 'shared') {
|
||||
$sharedcategories = api::get_categories_with_fields('core_customfield', 'shared', 0);
|
||||
// Filter only by enabled shared categories.
|
||||
if (!$ismanagementpage) {
|
||||
$sharedcategories = array_filter($sharedcategories, function (category_controller $cc) {
|
||||
return api::is_shared_category_enabled(
|
||||
$cc->get('id'),
|
||||
$this->get_component(),
|
||||
$this->get_area(),
|
||||
$this->get_itemid()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
$this->categories = array_merge($this->categories, $sharedcategories);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace core_customfield\output;
|
||||
|
||||
use core_customfield\api;
|
||||
use core_customfield\handler;
|
||||
use core_customfield\shared;
|
||||
use renderable;
|
||||
use templatable;
|
||||
|
||||
@@ -65,6 +66,7 @@ class management implements renderable, templatable {
|
||||
* @return array|object|\stdClass
|
||||
*/
|
||||
public function export_for_template(\renderer_base $output) {
|
||||
global $DB;
|
||||
$data = new \stdClass();
|
||||
|
||||
$fieldtypes = $this->handler->get_available_field_types();
|
||||
@@ -73,7 +75,14 @@ class management implements renderable, templatable {
|
||||
$data->area = $this->handler->get_area();
|
||||
$data->itemid = $this->handler->get_itemid();
|
||||
$data->usescategories = $this->handler->uses_categories();
|
||||
$categories = $this->handler->get_categories_with_fields();
|
||||
$categories = $this->handler->get_categories_with_fields(true);
|
||||
|
||||
// Get all enabled shared categories at once.
|
||||
$sharedcategoriesenabled = shared::get_records([
|
||||
'component' => $data->component,
|
||||
'area' => $data->area,
|
||||
'itemid' => $data->itemid,
|
||||
]);
|
||||
|
||||
$categoriesarray = array();
|
||||
|
||||
@@ -89,6 +98,26 @@ class management implements renderable, templatable {
|
||||
$category->get_formatted_name());
|
||||
$categoryarray['canedit'] = $canedit;
|
||||
|
||||
$toggleenabled = (bool) array_filter(
|
||||
$sharedcategoriesenabled,
|
||||
fn($record) => $record->get('categoryid') === $category->get('id')
|
||||
);
|
||||
$attributes = [
|
||||
['name' => 'data-id', 'value' => $category->get('id')],
|
||||
['name' => 'data-action', 'value' => 'shared-toggle'],
|
||||
['name' => 'data-state', 'value' => $toggleenabled],
|
||||
['name' => 'data-component', 'value' => $data->component],
|
||||
['name' => 'data-area', 'value' => $data->area],
|
||||
['name' => 'data-itemid', 'value' => $data->itemid],
|
||||
];
|
||||
$categoryarray['toggle'] = $output->render_from_template('core/toggle', [
|
||||
'id' => 'shared-toggle-' . $category->get('id'),
|
||||
'checked' => $toggleenabled,
|
||||
'extraattributes' => $attributes,
|
||||
'label' => get_string('enableplugin', 'core_admin', $category->get_formatted_name()),
|
||||
'labelclasses' => 'visually-hidden',
|
||||
]);
|
||||
|
||||
$categoryarray['fields'] = array();
|
||||
|
||||
foreach ($category->get_fields() as $field) {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?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_customfield;
|
||||
|
||||
use core\persistent;
|
||||
|
||||
/**
|
||||
* Customfield shared persistent class
|
||||
*
|
||||
* @package core_customfield
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class shared extends persistent {
|
||||
/**
|
||||
* Database table.
|
||||
*/
|
||||
const TABLE = 'customfield_shared';
|
||||
|
||||
/**
|
||||
* Return the definition of the properties of this model.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function define_properties(): array {
|
||||
return [
|
||||
'categoryid' => [
|
||||
'type' => PARAM_INT,
|
||||
],
|
||||
'component' => [
|
||||
'type' => PARAM_COMPONENT,
|
||||
],
|
||||
'area' => [
|
||||
'type' => PARAM_COMPONENT,
|
||||
],
|
||||
'itemid' => [
|
||||
'type' => PARAM_INT,
|
||||
'optional' => true,
|
||||
'default' => 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -131,6 +131,7 @@ class core_customfield_external extends external_api {
|
||||
)
|
||||
)
|
||||
, '', VALUE_OPTIONAL),
|
||||
'toggle' => new external_value(PARAM_RAW, 'toggle'),
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
{{#canedit}}<div class="categorieslist">{{/canedit}}
|
||||
<div data-category-id="{{id}}" id="category-{{id}}" class="mt-2">
|
||||
<div class="row justify-content-between align-items-end">
|
||||
<div class="col-6 categoryinstance">
|
||||
<div class="col-6 categoryinstance d-flex align-items-center gap-3">
|
||||
{{#usescategories}}
|
||||
<h3>
|
||||
{{#canedit}}<span class="movecategory icon-size-3">{{> core/drag_handle}}</span>{{/canedit}}
|
||||
@@ -88,6 +88,11 @@
|
||||
</a>
|
||||
{{/canedit}}
|
||||
</h3>
|
||||
{{^canedit}}
|
||||
<div data-bs-toggle="tooltip" data-bs-placement="top" title="{{#str}} sharedcategoryhelptext, customfield {{/str}}">
|
||||
{{{toggle}}}
|
||||
</div>
|
||||
{{/canedit}}
|
||||
{{/usescategories}}
|
||||
</div>
|
||||
<div class="col-auto text-end">
|
||||
@@ -132,7 +137,8 @@
|
||||
</div>
|
||||
|
||||
{{#js}}
|
||||
require(['core_customfield/form'], function(s) {
|
||||
require(['core_customfield/form', 'core_customfield/toggle_shared'], function(s, toggle) {
|
||||
s.init();
|
||||
toggle.init();
|
||||
});
|
||||
{{/js}}
|
||||
|
||||
@@ -53,3 +53,31 @@ Feature: Create shared categories and fields
|
||||
# Check that the move field option exists for course fields but not for shared fields.
|
||||
And "//tr[@data-field-name='Course field 1']//span[@title='Move \"Course field 1\"']" "xpath_element" should exist
|
||||
And "//tr[@data-field-name='Shared field 1']//span[@title='Move \"Shared field 1\"']" "xpath_element" should not exist
|
||||
|
||||
Scenario: Select which shared custom fields categories are used in the course entity
|
||||
Given the following "custom field categories" exist:
|
||||
| name | component | area | itemid |
|
||||
| My shared category 1 | core_customfield | shared | 0 |
|
||||
| My shared category 2 | core_customfield | shared | 0 |
|
||||
| My course category | core_course | course | 0 |
|
||||
And the following "custom fields" exist:
|
||||
| name | category | type | shortname |
|
||||
| Shared field 1 | My shared category 1 | text | f1 |
|
||||
| Shared field 2 | My shared category 2 | text | f2 |
|
||||
| Course field 1 | My course category | text | f3 |
|
||||
And the following "courses" exist:
|
||||
| shortname | fullname |
|
||||
| C1 | Course 1 |
|
||||
And I log in as "admin"
|
||||
When I am on the "C1" "Course" page
|
||||
And I navigate to "Settings" in current page administration
|
||||
Then I should see "My course category"
|
||||
And I should not see "My shared category 1"
|
||||
And I should not see "My shared category 2"
|
||||
And I navigate to "Courses > Default settings > Course custom fields" in site administration
|
||||
And I toggle the "Enable My shared category 1" admin switch "on"
|
||||
And I am on the "C1" "Course" page
|
||||
And I navigate to "Settings" in current page administration
|
||||
And I should see "My course category"
|
||||
And I should see "My shared category 1"
|
||||
And I should not see "My shared category 2"
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
<?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/>.
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace core_customfield\external;
|
||||
|
||||
use core_external\external_api;
|
||||
use core_customfield\shared;
|
||||
use core_customfield_generator;
|
||||
|
||||
/**
|
||||
* Unit tests for custom field toggle_shared_category external method
|
||||
*
|
||||
* @package core_customfield
|
||||
* @covers \core_customfield\external\toggle_shared_category
|
||||
* @copyright 2025 David Carrillo <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class toggle_shared_category_test extends \core_external\tests\externallib_testcase {
|
||||
/**
|
||||
* Test execute
|
||||
*/
|
||||
public function test_execute(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
/** @var core_customfield_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_customfield');
|
||||
$sharedcategory = $generator->create_category(['component' => 'core_customfield', 'area' => 'shared']);
|
||||
$generator->create_field([
|
||||
'categoryid' => $sharedcategory->get('id'),
|
||||
'name' => 'My shared field',
|
||||
'shortname' => 'mysharedfield',
|
||||
'type' => 'text',
|
||||
]);
|
||||
|
||||
$this->assertEmpty(shared::get_records());
|
||||
|
||||
$result = toggle_shared_category::execute(
|
||||
$sharedcategory->get('id'),
|
||||
'core_course',
|
||||
'course',
|
||||
0,
|
||||
true
|
||||
);
|
||||
$result = external_api::clean_returnvalue(toggle_shared_category::execute_returns(), $result);
|
||||
$this->assertTrue($result);
|
||||
$records = shared::get_records();
|
||||
$record = reset($records);
|
||||
$this->assertEquals($sharedcategory->get('id'), $record->get('categoryid'));
|
||||
$this->assertEquals('core_course', $record->get('component'));
|
||||
$this->assertEquals('course', $record->get('area'));
|
||||
$this->assertEquals(0, $record->get('itemid'));
|
||||
|
||||
$result = toggle_shared_category::execute(
|
||||
$sharedcategory->get('id'),
|
||||
'core_course',
|
||||
'course',
|
||||
0,
|
||||
false
|
||||
);
|
||||
$result = external_api::clean_returnvalue(toggle_shared_category::execute_returns(), $result);
|
||||
$this->assertTrue($result);
|
||||
$this->assertEmpty(shared::get_records());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execute with no permission
|
||||
*/
|
||||
public function test_execute_no_permission(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
/** @var core_customfield_generator $generator */
|
||||
$generator = $this->getDataGenerator()->get_plugin_generator('core_customfield');
|
||||
$sharedcategory = $generator->create_category(['component' => 'core_customfield', 'area' => 'shared']);
|
||||
$generator->create_field([
|
||||
'categoryid' => $sharedcategory->get('id'),
|
||||
'name' => 'My shared field',
|
||||
'shortname' => 'mysharedfield',
|
||||
'type' => 'text',
|
||||
]);
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
$this->expectException(\moodle_exception::class);
|
||||
$str = "Sorry, but you do not currently have permissions to do that (Configure shared custom fields).";
|
||||
$this->expectExceptionMessage($str);
|
||||
toggle_shared_category::execute(
|
||||
$sharedcategory->get('id'),
|
||||
'core_course',
|
||||
'course',
|
||||
0,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,8 @@ $string['eventcategoryupdated'] = 'Custom field category updated';
|
||||
$string['eventfieldcreated'] = 'Custom field created';
|
||||
$string['eventfielddeleted'] = 'Custom field deleted';
|
||||
$string['eventfieldupdated'] = 'Custom field updated';
|
||||
$string['eventsharedcategoryusagedisabled'] = 'Shared category usage disabled';
|
||||
$string['eventsharedcategoryusageenabled'] = 'Shared category usage enabled';
|
||||
$string['fieldname'] = 'Name';
|
||||
$string['fieldnotfound'] = 'Field not found';
|
||||
$string['fieldshortname'] = 'Short name';
|
||||
@@ -85,6 +87,7 @@ $string['privacy:metadata:customfield_data:valueformat'] = 'The format of the va
|
||||
$string['privacy:metadata:customfield_data:valuetrust'] = 'The trust flag of the value, when it is text';
|
||||
$string['privacy:metadata:customfieldpluginsummary'] = 'Fields for various components';
|
||||
$string['privacy:metadata:filepurpose'] = 'File attached to the custom field data';
|
||||
$string['sharedcategoryhelptext'] = 'When enabled, the custom fields in this category will be used';
|
||||
$string['sharedcustomfields'] = 'Shared custom fields';
|
||||
$string['shortname'] = 'Short name';
|
||||
$string['shortname_help'] = 'The short name must be unique and can only contain alphanumeric lowercase characters and underscores (_). It is not displayed anywhere on the site, but may be used for synchronisation with external systems or in web services.';
|
||||
|
||||
@@ -4355,6 +4355,26 @@
|
||||
<INDEX NAME="fieldid-decvalue" UNIQUE="false" FIELDS="fieldid, decvalue"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="customfield_shared" COMMENT="core_customfield shared category table">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
|
||||
<FIELD NAME="categoryid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="area" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
|
||||
</FIELDS>
|
||||
<KEYS>
|
||||
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
|
||||
<KEY NAME="categoryid" TYPE="foreign" FIELDS="categoryid" REFTABLE="customfield_category" REFFIELDS="id"/>
|
||||
<KEY NAME="usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/>
|
||||
</KEYS>
|
||||
<INDEXES>
|
||||
<INDEX NAME="categoryid-component-area-itemid" UNIQUE="true" FIELDS="categoryid, component, area, itemid"/>
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
<TABLE NAME="h5p_libraries" COMMENT="Stores information about libraries used by H5P content.">
|
||||
<FIELDS>
|
||||
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" COMMENT="Primary Key: The id of the library"/>
|
||||
|
||||
@@ -2927,6 +2927,13 @@ $functions = array(
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
),
|
||||
'core_customfield_toggle_shared' => [
|
||||
'classname' => core_customfield\external\toggle_shared_category::class,
|
||||
'methodname' => 'execute',
|
||||
'description' => 'Toggle shared category state',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
'core_h5p_get_trusted_h5p_file' => [
|
||||
'classname' => 'core_h5p\external',
|
||||
'methodname' => 'get_trusted_h5p_file',
|
||||
|
||||
Reference in New Issue
Block a user