MDL-87690 customfield: Feature to convert CF category to shared category
This feature adds the ability to convert an existing custom field category to a shared custom field category via adding a button to the custom field management interface.
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
issueNumber: MDL-87690
|
||||
notes:
|
||||
core_customfield:
|
||||
- message: >-
|
||||
A new WebService `core_customfield_convert_category` has been added.
|
||||
It allows the conversion of any entity custom field category to a shared category.
|
||||
type: improved
|
||||
- message: >-
|
||||
The WebService `core_customfield_reload_template` now returns a new
|
||||
parameter "canconvert".
|
||||
type: changed
|
||||
- message: >-
|
||||
The Javascript module `core_customfield/repository/toggle_shared` has been deprecated.
|
||||
Please, use `core_customfield/repository` instead.
|
||||
type: deprecated
|
||||
+2
-2
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
|
||||
define("core_customfield/repository",["exports","core/ajax"],(function(_exports,_ajax){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.toggleCategory=_exports.reloadTemplate=_exports.convertCategory=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.convertCategory=(categoryId,component,area,itemid)=>{const request={methodname:"core_customfield_convert_category",args:{categoryid:categoryId,component:component,area:area,itemid:itemid}};return _ajax.default.call([request])[0]};_exports.reloadTemplate=(component,area,itemid)=>{const request={methodname:"core_customfield_reload_template",args:{component:component,area:area,itemid:itemid}};return _ajax.default.call([request])[0]};_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=repository.min.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"repository.min.js","sources":["../src/repository.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 custom fields AJAX requests.\n *\n * @module core_customfield/repository\n * @copyright 2026 Yerai Rodríguez <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n\"use strict\";\n\nimport Ajax from 'core/ajax';\n\n/**\n * Convert an entity custom field category to a shared category.\n *\n * @param {Number} categoryId\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n * @return {Promise}\n */\nexport const convertCategory = (categoryId, component, area, itemid) => {\n const request = {\n methodname: 'core_customfield_convert_category',\n args: {categoryid: categoryId, component: component, area: area, itemid: itemid}\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Reload custom fields list.\n *\n * @param {String} component\n * @param {String} area\n * @param {Number} itemid\n * @return {Promise}\n */\nexport const reloadTemplate = (component, area, itemid) => {\n const request = {\n methodname: 'core_customfield_reload_template',\n args: {component: component, area: area, itemid: itemid}\n };\n\n return Ajax.call([request])[0];\n};\n\n/**\n * Toggle shared category enabled.\n *\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","request","methodname","args","categoryid","Ajax","call","state"],"mappings":"oTAoC+B,CAACA,WAAYC,UAAWC,KAAMC,gBACnDC,QAAU,CACZC,WAAY,oCACZC,KAAM,CAACC,WAAYP,WAAYC,UAAWA,UAAWC,KAAMA,KAAMC,OAAQA,gBAGtEK,cAAKC,KAAK,CAACL,UAAU,4BAWF,CAACH,UAAWC,KAAMC,gBACtCC,QAAU,CACZC,WAAY,mCACZC,KAAM,CAACL,UAAWA,UAAWC,KAAMA,KAAMC,OAAQA,gBAG9CK,cAAKC,KAAK,CAACL,UAAU,4BAaF,CAACJ,WAAYC,UAAWC,KAAMC,OAAQO,eAC1DN,QAAU,CACZC,WAAY,iCACZC,KAAM,CAACC,WAAYP,WAAYC,UAAWA,UAAWC,KAAMA,KAAMC,OAAQA,OAAQO,MAAOA,eAGrFF,cAAKC,KAAK,CAACL,UAAU"}
|
||||
@@ -5,6 +5,7 @@ define("core_customfield/repository/toggle_shared",["exports","core/ajax"],(func
|
||||
* @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
|
||||
* @deprecated since Moodle 5.1 - please use core_customfield/repository instead.
|
||||
*/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
|
||||
@@ -1 +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"}
|
||||
{"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 * @deprecated since Moodle 5.1 - please use core_customfield/repository instead.\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":";;;;;;;;wKAqC8B,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"}
|
||||
+1
-1
@@ -1,3 +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)}}));
|
||||
define("core_customfield/toggle_shared",["exports","core/inplace_editable","core/notification","core/pending","core_customfield/repository"],(function(_exports,_inplace_editable,_notification,_pending,_repository){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,_repository.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
|
||||
@@ -1 +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"}
|
||||
{"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';\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":"udA8BIA,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,sCAEzCR,WAAYG,UAAWC,KAAMC,OAAQC,mBAC/CG,MAAK,KACFd,aAAaM,QAAQO,MAAQF,kBACtBR,eAAeY,aAEzBC,MAAMC,sBAAaC,eAIhCtB,aAAc"}
|
||||
@@ -27,6 +27,8 @@ import {
|
||||
getString,
|
||||
getStrings,
|
||||
} from 'core/str';
|
||||
import {add as addToast} from 'core/toast';
|
||||
import * as Repository from 'core_customfield/repository';
|
||||
import ModalForm from 'core_form/modalform';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
@@ -60,8 +62,8 @@ const confirmDelete = (id, type, component, area, itemid) => {
|
||||
methodname: (type === 'field') ? 'core_customfield_delete_field' : 'core_customfield_delete_category',
|
||||
args: {id},
|
||||
},
|
||||
{methodname: 'core_customfield_reload_template', args: {component, area, itemid}}
|
||||
])[1]
|
||||
])[0]
|
||||
.then(() => Repository.reloadTemplate(component, area, itemid))
|
||||
.then(response => Templates.render('core_customfield/list', response))
|
||||
.then((html, js) => Templates.replaceNode(jQuery('[data-region="list-page"]'), html, js))
|
||||
.then(pendingDeletePromise.resolve)
|
||||
@@ -72,6 +74,67 @@ const confirmDelete = (id, type, component, area, itemid) => {
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a confirmation modal to convert an entity custom field category to a shared category.
|
||||
*
|
||||
* @param {Number} categoryId
|
||||
* @param {String} component
|
||||
* @param {String} area
|
||||
* @param {Number} itemid
|
||||
*/
|
||||
const confirmConvert = (categoryId, component, area, itemid) => {
|
||||
getStrings([
|
||||
{'key': 'convertcategorytitle', component: 'core_customfield'},
|
||||
{'key': 'convertcategoryconfirm', component: 'core_customfield'},
|
||||
{'key': 'proceed'},
|
||||
{'key': 'cancel'},
|
||||
])
|
||||
.then(strings => {
|
||||
return Notification.confirm(strings[0], strings[1], strings[2], strings[3], function() {
|
||||
handleConversion(categoryId, component, area, itemid);
|
||||
});
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle the conversion of an entity custom field category to a shared category.
|
||||
*
|
||||
* @param {Number} categoryId
|
||||
* @param {String} component
|
||||
* @param {String} area
|
||||
* @param {Number} itemid
|
||||
*/
|
||||
const handleConversion = (categoryId, component, area, itemid) => {
|
||||
const pendingPromise = new Pending('core_customfield/form:handleConversion');
|
||||
|
||||
Repository.convertCategory(categoryId, component, area, itemid)
|
||||
.then(() => Repository.toggleCategory(categoryId, component, area, itemid, true))
|
||||
.then(() => Repository.reloadTemplate(component, area, itemid))
|
||||
.then((response) => Templates.renderForPromise('core_customfield/list', response))
|
||||
.then(({html, js}) => {
|
||||
const listPage = document.querySelector('[data-region="list-page"]');
|
||||
return Templates.replaceNode(listPage, html, js);
|
||||
})
|
||||
.then(() => addToast(getString('categoryconverted', 'core_customfield'), {type: 'success'}))
|
||||
.then(() => pendingPromise.resolve())
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display an alert when conversion of a category to a shared category is blocked due to duplicate names.
|
||||
*/
|
||||
const alertDuplicateField = () => {
|
||||
getStrings([
|
||||
{'key': 'conversionerror', component: 'core_customfield'},
|
||||
{'key': 'sharedcustomfieldalreadyexists', component: 'core_customfield'},
|
||||
{'key': 'closebuttontitle'},
|
||||
])
|
||||
.then(strings => {
|
||||
return Notification.alert(strings[0], strings[1], strings[2]);
|
||||
})
|
||||
.catch(Notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new custom fields category with default name and updates the list
|
||||
@@ -84,10 +147,10 @@ const createNewCategory = (component, area, itemid) => {
|
||||
const pendingPromise = new Pending('core_customfield/form:createNewCategory');
|
||||
const promises = fetchMany([
|
||||
{methodname: 'core_customfield_create_category', args: {component, area, itemid}},
|
||||
{methodname: 'core_customfield_reload_template', args: {component, area, itemid}}
|
||||
]);
|
||||
|
||||
promises[1].then(response => Templates.render('core_customfield/list', response))
|
||||
promises[0].then(() => Repository.reloadTemplate(component, area, itemid))
|
||||
.then(response => Templates.render('core_customfield/list', response))
|
||||
.then((html, js) => Templates.replaceNode(jQuery('[data-region="list-page"]'), html, js))
|
||||
.then(() => pendingPromise.resolve())
|
||||
.catch(Notification.exception);
|
||||
@@ -119,11 +182,9 @@ const createNewField = (element, component, area, itemid) => {
|
||||
|
||||
form.addEventListener(form.events.FORM_SUBMITTED, () => {
|
||||
const pendingCreatedPromise = new Pending('core_customfield/form:createdNewField');
|
||||
const promises = fetchMany([
|
||||
{methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}
|
||||
]);
|
||||
|
||||
promises[0].then(response => Templates.render('core_customfield/list', response))
|
||||
Repository.reloadTemplate(component, area, itemid)
|
||||
.then(response => Templates.render('core_customfield/list', response))
|
||||
.then((html, js) => Templates.replaceNode(jQuery('[data-region="list-page"]'), html, js))
|
||||
.then(() => pendingCreatedPromise.resolve())
|
||||
.catch(() => window.location.reload());
|
||||
@@ -158,11 +219,9 @@ const editField = (element, component, area, itemid) => {
|
||||
|
||||
form.addEventListener(form.events.FORM_SUBMITTED, () => {
|
||||
const pendingCreatedPromise = new Pending('core_customfield/form:createdNewField');
|
||||
const promises = fetchMany([
|
||||
{methodname: 'core_customfield_reload_template', args: {component: component, area: area, itemid: itemid}}
|
||||
]);
|
||||
|
||||
promises[0].then(response => Templates.render('core_customfield/list', response))
|
||||
Repository.reloadTemplate(component, area, itemid)
|
||||
.then(response => Templates.render('core_customfield/list', response))
|
||||
.then((html, js) => Templates.replaceNode(jQuery('[data-region="list-page"]'), html, js))
|
||||
.then(() => pendingCreatedPromise.resolve())
|
||||
.catch(() => window.location.reload());
|
||||
@@ -308,6 +367,20 @@ export const init = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (roleHolder.dataset.role === 'convertcategory') {
|
||||
e.preventDefault();
|
||||
|
||||
confirmConvert(roleHolder.dataset.id, component, area, itemid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roleHolder.dataset.role === 'hasduplicatecustomfield') {
|
||||
e.preventDefault();
|
||||
|
||||
alertDuplicateField();
|
||||
return;
|
||||
}
|
||||
|
||||
if (roleHolder.dataset.role === 'addnewcategory') {
|
||||
e.preventDefault();
|
||||
createNewCategory(component, area, itemid);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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 custom fields AJAX requests.
|
||||
*
|
||||
* @module core_customfield/repository
|
||||
* @copyright 2026 Yerai Rodríguez <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
|
||||
/**
|
||||
* Convert an entity custom field category to a shared category.
|
||||
*
|
||||
* @param {Number} categoryId
|
||||
* @param {String} component
|
||||
* @param {String} area
|
||||
* @param {Number} itemid
|
||||
* @return {Promise}
|
||||
*/
|
||||
export const convertCategory = (categoryId, component, area, itemid) => {
|
||||
const request = {
|
||||
methodname: 'core_customfield_convert_category',
|
||||
args: {categoryid: categoryId, component: component, area: area, itemid: itemid}
|
||||
};
|
||||
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Reload custom fields list.
|
||||
*
|
||||
* @param {String} component
|
||||
* @param {String} area
|
||||
* @param {Number} itemid
|
||||
* @return {Promise}
|
||||
*/
|
||||
export const reloadTemplate = (component, area, itemid) => {
|
||||
const request = {
|
||||
methodname: 'core_customfield_reload_template',
|
||||
args: {component: component, area: area, itemid: itemid}
|
||||
};
|
||||
|
||||
return Ajax.call([request])[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle shared category enabled.
|
||||
*
|
||||
* @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];
|
||||
};
|
||||
@@ -19,6 +19,7 @@
|
||||
* @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
|
||||
* @deprecated since Moodle 5.1 - please use core_customfield/repository instead.
|
||||
*/
|
||||
|
||||
import Ajax from 'core/ajax';
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
import 'core/inplace_editable';
|
||||
import Notification from 'core/notification';
|
||||
import Pending from 'core/pending';
|
||||
import {toggleCategory} from 'core_customfield/repository/toggle_shared';
|
||||
import {toggleCategory} from 'core_customfield/repository';
|
||||
|
||||
let initialized = false;
|
||||
|
||||
|
||||
@@ -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\api;
|
||||
use core_customfield\customfield\shared_handler;
|
||||
use core_customfield\handler;
|
||||
use core_customfield\category;
|
||||
use core_external\external_api;
|
||||
use core_external\external_value;
|
||||
use core_external\external_function_parameters;
|
||||
|
||||
/**
|
||||
* External method for converting any entity custom field category to a shared category.
|
||||
*
|
||||
* @package core_customfield
|
||||
* @copyright 2026 Yerai Rodríguez <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class convert_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'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* External method execution
|
||||
*
|
||||
* @param int $categoryid
|
||||
* @param string $component
|
||||
* @param string $area
|
||||
* @param int $itemid
|
||||
* @return bool
|
||||
*/
|
||||
public static function execute(int $categoryid, string $component, string $area, int $itemid): bool {
|
||||
[
|
||||
'categoryid' => $categoryid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
] = self::validate_parameters(self::execute_parameters(), [
|
||||
'categoryid' => $categoryid,
|
||||
'component' => $component,
|
||||
'area' => $area,
|
||||
'itemid' => $itemid,
|
||||
]);
|
||||
|
||||
// Validate context.
|
||||
$context = \core\context\system::instance();
|
||||
self::validate_context($context);
|
||||
|
||||
$handler = handler::get_handler($component, $area, $itemid);
|
||||
$sharedhandler = shared_handler::get_handler('core_customfield', 'shared');
|
||||
if (!$handler->can_configure() || !$sharedhandler->can_configure()) {
|
||||
throw new \core\exception\access_denied_exception('You do not have permission to convert this category to shared.');
|
||||
}
|
||||
|
||||
// Make sure no other shared custom fields with the same short name exist.
|
||||
$categorieswithfields = $handler->get_categories_with_fields();
|
||||
$convertingcategory = $handler->get_category_from_array($categorieswithfields, $categoryid, $component, $area, $itemid);
|
||||
if (!$convertingcategory) {
|
||||
throw new \core\exception\invalid_parameter_exception('Custom field category with ID ' . $categoryid .
|
||||
' does not exist for the specified component, area and itemid.');
|
||||
}
|
||||
foreach ($convertingcategory->get_fields() as $field) {
|
||||
if (!api::is_shortname_unique($handler, $field->get('shortname'), $field->get('id'))) {
|
||||
throw new \core\exception\moodle_exception('sharedcustomfieldalreadyexists', 'core_customfield');
|
||||
}
|
||||
}
|
||||
|
||||
// Convert category to shared.
|
||||
$record = new category($categoryid);
|
||||
$record->set_many([
|
||||
'component' => 'core_customfield',
|
||||
'area' => 'shared',
|
||||
'shared' => 1,
|
||||
]);
|
||||
$record->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* External method return value
|
||||
*
|
||||
* @return external_value
|
||||
*/
|
||||
public static function execute_returns(): external_value {
|
||||
return new external_value(PARAM_BOOL);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
namespace core_customfield\output;
|
||||
|
||||
use core_customfield\api;
|
||||
use core_customfield\customfield\shared_handler;
|
||||
use core_customfield\handler;
|
||||
use core_customfield\shared;
|
||||
use renderable;
|
||||
@@ -122,6 +123,10 @@ class management implements renderable, templatable {
|
||||
]);
|
||||
}
|
||||
|
||||
$sharedhandler = shared_handler::get_handler('core_customfield', 'shared');
|
||||
$categoryarray['canconvert'] = $sharedhandler->can_configure() && !$category->get('shared');
|
||||
$categoryarray['hasduplicatecustomfield'] = false;
|
||||
|
||||
$categoryarray['fields'] = array();
|
||||
|
||||
foreach ($category->get_fields() as $field) {
|
||||
@@ -135,6 +140,11 @@ class management implements renderable, templatable {
|
||||
$categoryarray['canedit'] = $canedit;
|
||||
|
||||
$categoryarray['fields'][] = $fieldarray;
|
||||
|
||||
if ($categoryarray['canconvert'] && !$categoryarray['hasduplicatecustomfield']) {
|
||||
$fieldisunique = api::is_shortname_unique($this->handler, $fieldarray['shortname'], $fieldarray['id']);
|
||||
$categoryarray['hasduplicatecustomfield'] = !$fieldisunique;
|
||||
}
|
||||
if ($canedit) {
|
||||
$movablefieldscount++;
|
||||
}
|
||||
|
||||
@@ -122,6 +122,8 @@ class core_customfield_external extends external_api {
|
||||
'nameeditable' => new external_value(PARAM_RAW, 'inplace editable name'),
|
||||
'addfieldmenu' => new external_value(PARAM_RAW, 'addfieldmenu'),
|
||||
'canedit' => new external_value(PARAM_BOOL, 'can edit'),
|
||||
'canconvert' => new external_value(PARAM_BOOL, 'can convert to shared category'),
|
||||
'hasduplicatecustomfield' => new external_value(PARAM_BOOL, 'category has a duplicate field name?'),
|
||||
'fields' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
"nameeditable": "Other fields",
|
||||
"addfieldmenu": "Add field",
|
||||
"canedit": true,
|
||||
"canconvert": true,
|
||||
"hasduplicatecustomfield": false,
|
||||
"fields": [
|
||||
{ "id": 0, "name": "Field name", "shortname": "shortname", "type": "Text" },
|
||||
{ "id": 0, "name": "Another field", "shortname": "checkme", "type": "Checkbox" }
|
||||
@@ -89,7 +91,21 @@
|
||||
{{/canmovecategories}}
|
||||
{{{nameeditable}}}
|
||||
{{#canedit}}
|
||||
<button type="button" class="btn btn-link btn-icon icon-size-3" data-role="deletecategory" data-id="{{id}}" title="{{#str}} deletecategory, customfield, {{name}} {{/str}}" aria-label="{{#str}} deletecategory, customfield, {{name}} {{/str}}">
|
||||
{{#canconvert}}
|
||||
{{^hasduplicatecustomfield}}
|
||||
<button type="button" class="btn btn-icon icon-size-3" data-role="convertcategory" data-id="{{id}}" title="{{#str}} convertcategory, customfield {{/str}}">
|
||||
{{#pix}} i/siteevent, core {{/pix}}
|
||||
<span class="visually-hidden">{{#str}} convertcategory, customfield {{/str}}</span>
|
||||
</button>
|
||||
{{/hasduplicatecustomfield}}
|
||||
{{#hasduplicatecustomfield}}
|
||||
<button type="button" class="btn btn-icon icon-size-3" data-role="hasduplicatecustomfield" data-id="{{id}}" title="{{#str}} convertcategory, customfield {{/str}}">
|
||||
{{#pix}} i/siteevent, core {{/pix}}
|
||||
<span class="visually-hidden">{{#str}} convertcategory, customfield {{/str}}</span>
|
||||
</button>
|
||||
{{/hasduplicatecustomfield}}
|
||||
{{/canconvert}}
|
||||
<button type="button" class="btn btn-icon icon-size-3" data-role="deletecategory" data-id="{{id}}" title="{{#str}} deletecategory, customfield, {{name}} {{/str}}" aria-label="{{#str}} deletecategory, customfield, {{name}} {{/str}}">
|
||||
{{#pix}} t/delete, core {{/pix}}
|
||||
</button>
|
||||
{{/canedit}}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
@core @core_course @core_customfield @javascript
|
||||
Feature: Managers can convert categories for any entity custom field category to shared custom field
|
||||
In order to use custom fields across the site
|
||||
As a manager
|
||||
I need to convert any entity custom field's categories to shared custom fields
|
||||
|
||||
Background:
|
||||
Given the following "custom field categories" exist:
|
||||
| name | component | area | itemid |
|
||||
| Category for course 1 | core_course | course | 0 |
|
||||
| Category for course 2 | core_course | course | 0 |
|
||||
| Shared category | core_customfield | shared | 0 |
|
||||
And the following "custom fields" exist:
|
||||
| name | category | type | shortname | description |
|
||||
| Field 1 | Category for course 1 | text | f1 | d1 |
|
||||
| Field 2 | Category for course 1 | text | f2 | d2 |
|
||||
| Field 3 | Category for course 2 | text | shf1 | d3 |
|
||||
| Field 4 | Shared category | text | shf1 | shd1 |
|
||||
|
||||
Scenario: Convert a course custom field category to shared custom fields
|
||||
Given I log in as "admin"
|
||||
When I navigate to "Courses > Default settings > Course custom fields" in site administration
|
||||
And I click on "Convert to shared custom fields" "button" in the "Category for course 1" "core_customfield > Category"
|
||||
And I should see "Are you sure you want to convert this category" in the "Convert to shared custom fields?" "dialogue"
|
||||
And I click on "Proceed" "button" in the "Convert to shared custom fields?" "dialogue"
|
||||
Then "The category has been successfully converted to shared custom fields" "toast_message" should be visible
|
||||
And "Enable Category for course 1" "checkbox" should exist in the "Category for course 1" "core_customfield > Category"
|
||||
And I reload the page
|
||||
And I navigate to "Custom fields > Shared custom fields" in site administration
|
||||
And "Category for course 1" "core_customfield > Category" should exist
|
||||
And "Convert to shared custom fields" "button" should not exist
|
||||
|
||||
Scenario: Cannot convert category with duplicate custom field short names in shared category
|
||||
Given I log in as "admin"
|
||||
When I navigate to "Courses > Default settings > Course custom fields" in site administration
|
||||
And I click on "Convert to shared custom fields" "button" in the "Category for course 2" "core_customfield > Category"
|
||||
Then I should see "One or more short names in this category already exist as shared custom fields." in the "Cannot convert to shared custom fields" "dialogue"
|
||||
And I click on "Close" "button" in the "Cannot convert to shared custom fields" "dialogue"
|
||||
And "Enable Category for course 2" "checkbox" should not exist in the "Category for course 2" "core_customfield > Category"
|
||||
@@ -0,0 +1,154 @@
|
||||
<?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\category;
|
||||
use core_external\external_api;
|
||||
|
||||
/**
|
||||
* Unit tests for custom field convert_category external method
|
||||
*
|
||||
* @package core_customfield
|
||||
* @covers \core_customfield\external\convert_category
|
||||
* @copyright 2026 Yerai Rodríguez <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
final class convert_category_test extends \core_external\tests\externallib_testcase {
|
||||
/**
|
||||
* Test execute
|
||||
*/
|
||||
public function test_execute(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$category = $this->getDataGenerator()->create_custom_field_category([
|
||||
'component' => 'core_course',
|
||||
'area' => 'course',
|
||||
]);
|
||||
$this->getDataGenerator()->create_custom_field([
|
||||
'shortname' => 'customfield1',
|
||||
'type' => 'text',
|
||||
'categoryid' => $category->get('id'),
|
||||
]);
|
||||
|
||||
$result = convert_category::execute(
|
||||
categoryid: $category->get('id'),
|
||||
component: 'core_course',
|
||||
area: 'course',
|
||||
itemid: 0,
|
||||
);
|
||||
|
||||
$result = external_api::clean_returnvalue(convert_category::execute_returns(), $result);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Assert category is now shared.
|
||||
$categoryrecord = category::get_record(['id' => $category->get('id')]);
|
||||
$this->assertEquals($category->get('id'), $categoryrecord->get('id'));
|
||||
$this->assertEquals('core_customfield', $categoryrecord->get('component'));
|
||||
$this->assertEquals('shared', $categoryrecord->get('area'));
|
||||
$this->assertEquals(0, $categoryrecord->get('itemid'));
|
||||
$this->assertEquals(1, $categoryrecord->get('shared'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execute with category not found
|
||||
*/
|
||||
public function test_execute_category_not_found(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$this->expectException(\core\exception\invalid_parameter_exception::class);
|
||||
$this->expectExceptionMessage(
|
||||
'Custom field category with ID 999 does not exist for the specified component, area and itemid.'
|
||||
);
|
||||
convert_category::execute(
|
||||
categoryid: 999,
|
||||
component: 'core_course',
|
||||
area: 'course',
|
||||
itemid: 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execute with no permission
|
||||
*/
|
||||
public function test_execute_no_permission(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
$category = $this->getDataGenerator()->create_custom_field_category([
|
||||
'component' => 'core_course',
|
||||
'area' => 'course',
|
||||
]);
|
||||
$this->getDataGenerator()->create_custom_field([
|
||||
'shortname' => 'customfield1',
|
||||
'type' => 'text',
|
||||
'categoryid' => $category->get('id'),
|
||||
]);
|
||||
|
||||
$this->setUser($user);
|
||||
|
||||
$this->expectException(\core\exception\access_denied_exception::class);
|
||||
$this->expectExceptionMessage('You do not have permission to convert this category to shared.');
|
||||
convert_category::execute(
|
||||
categoryid: $category->get('id'),
|
||||
component: 'core_course',
|
||||
area: 'course',
|
||||
itemid: 0,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test execute with existing custom field in shared category
|
||||
*/
|
||||
public function test_execute_existing_customfield(): void {
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$category = $this->getDataGenerator()->create_custom_field_category([
|
||||
'component' => 'core_course',
|
||||
'area' => 'course',
|
||||
]);
|
||||
$this->getDataGenerator()->create_custom_field([
|
||||
'shortname' => 'customfield1',
|
||||
'type' => 'text',
|
||||
'categoryid' => $category->get('id'),
|
||||
]);
|
||||
|
||||
$sharedcategory = $this->getDataGenerator()->create_custom_field_category([
|
||||
'component' => 'core_customfield',
|
||||
'area' => 'shared',
|
||||
]);
|
||||
$this->getDataGenerator()->create_custom_field([
|
||||
'shortname' => 'customfield1',
|
||||
'type' => 'text',
|
||||
'categoryid' => $sharedcategory->get('id'),
|
||||
]);
|
||||
|
||||
$this->expectException(\core\exception\moodle_exception::class);
|
||||
$this->expectExceptionMessage(get_string('sharedcustomfieldalreadyexists', 'core_customfield'));
|
||||
convert_category::execute(
|
||||
categoryid: $category->get('id'),
|
||||
component: 'core_course',
|
||||
area: 'course',
|
||||
itemid: 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,12 +26,17 @@ $string['action'] = 'Action';
|
||||
$string['addingnewcustomfield'] = 'Adding a new {$a}';
|
||||
$string['addnewcategory'] = 'Add a new category';
|
||||
$string['afterfield'] = 'After field {$a}';
|
||||
$string['categoryconverted'] = 'The category has been successfully converted to shared custom fields';
|
||||
$string['categorynotfound'] = 'Category not found';
|
||||
$string['checked'] = 'Checked';
|
||||
$string['commonsettings'] = 'General';
|
||||
$string['componentsettings'] = 'Component settings';
|
||||
$string['confirmdeletecategory'] = 'Are you sure you want to delete this category? All fields inside the category will also be deleted and all data associated with them. This action cannot be undone.';
|
||||
$string['confirmdeletefield'] = 'Are you sure you want to delete this field and all associated data? This action cannot be undone.';
|
||||
$string['conversionerror'] = 'Cannot convert to shared custom fields';
|
||||
$string['convertcategory'] = 'Convert to shared custom fields';
|
||||
$string['convertcategoryconfirm'] = 'Are you sure you want to convert this category and all its fields to shared custom fields? This will make these fields available to all entities that support custom fields, such as courses, groups, cohorts, etc. <br/><br/>Once converted, the shared category will be enabled by default but can be disabled manually. </br><br/>This action cannot be undone.';
|
||||
$string['convertcategorytitle'] = 'Convert to shared custom fields?';
|
||||
$string['createnewcustomfield'] = 'Add a new custom field';
|
||||
$string['customfield'] = 'Custom field';
|
||||
$string['customfielddata'] = 'Custom fields data';
|
||||
@@ -96,6 +101,7 @@ $string['privacy:metadata:customfield_shared:itemid'] = 'The ID of the item the
|
||||
$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['sharedcustomfieldalreadyexists'] = 'One or more short names in this category already exist as shared custom fields. To proceed with the conversion, please rename the conflicting fields in this category or modify the existing shared custom fields.';
|
||||
$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.';
|
||||
|
||||
@@ -2918,6 +2918,12 @@ $functions = array(
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
),
|
||||
'core_customfield_convert_category' => [
|
||||
'classname' => core_customfield\external\convert_category::class,
|
||||
'description' => 'Convert an entity custom field category to a shared category.',
|
||||
'type' => 'write',
|
||||
'ajax' => true,
|
||||
],
|
||||
'core_customfield_move_field' => array(
|
||||
'classname' => 'core_customfield_external',
|
||||
'methodname' => 'move_field',
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2026022700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2026022700.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '5.2dev (Build: 20260227)'; // Human-friendly version name
|
||||
|
||||
Reference in New Issue
Block a user