MDL-80037 theme: Indicate when $CFG->theme is defined in config.php
* And prevent selection of theme if $CFG->theme is defined in config.php
This commit is contained in:
+1
-1
@@ -5,6 +5,6 @@ define("core_admin/themeselector/preview_modal",["exports","core/modal_events","
|
||||
* @module core_admin/themeselector/preview_modal
|
||||
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_events=_interopRequireDefault(_modal_events),_modal_cancel=_interopRequireDefault(_modal_cancel),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);const SELECTORS_PREVIEW='[data-action="preview"]';_exports.init=()=>{registerListenerEvents()};const registerListenerEvents=()=>{document.addEventListener("click",(e=>{const preview=e.target.closest(SELECTORS_PREVIEW);preview&&buildModal(preview).catch(_notification.default.exception)}))},buildModal=async element=>{let description=await(0,_str.getString)("choosereadme","theme_"+element.getAttribute("data-choose"));const data={name:element.getAttribute("data-name"),image:element.getAttribute("data-image"),description:description.replace(/<[^>]+>/g," "),current:element.getAttribute("data-current"),actionurl:element.getAttribute("data-actionurl"),choose:element.getAttribute("data-choose"),sesskey:element.getAttribute("data-sesskey")};let modalTemplate=_modal_save_cancel.default;data.current&&(modalTemplate=_modal_cancel.default);const modal=await modalTemplate.create({title:data.name,body:_templates.default.render("core_admin/themeselector/theme_preview_modal",data),large:!0,buttons:{save:(0,_str.getString)("selecttheme","moodle"),cancel:(0,_str.getString)("closebuttontitle","moodle")},show:!0});modal.getRoot().on(_modal_events.default.save,(()=>{modal.getRoot().find("form").submit()}))}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_modal_events=_interopRequireDefault(_modal_events),_modal_cancel=_interopRequireDefault(_modal_cancel),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_notification=_interopRequireDefault(_notification),_templates=_interopRequireDefault(_templates);const SELECTORS_THEMES_CONTAINER="themelist",SELECTORS_PREVIEW='[data-action="preview"]';_exports.init=()=>{registerListenerEvents()};const registerListenerEvents=()=>{document.addEventListener("click",(e=>{const preview=e.target.closest(SELECTORS_PREVIEW);preview&&buildModal(preview).catch(_notification.default.exception)}))},buildModal=async element=>{let description=await(0,_str.getString)("choosereadme","theme_"+element.getAttribute("data-choose"));const themesContainer=document.getElementById(SELECTORS_THEMES_CONTAINER),definedInConfig=parseInt(themesContainer.dataset.definedinconfig),data={name:element.getAttribute("data-name"),image:element.getAttribute("data-image"),description:description.replace(/<[^>]+>/g," "),current:element.getAttribute("data-current"),actionurl:element.getAttribute("data-actionurl"),choose:element.getAttribute("data-choose"),sesskey:element.getAttribute("data-sesskey"),definedinconfig:definedInConfig};let modalTemplate=_modal_save_cancel.default;(data.current||data.definedinconfig)&&(modalTemplate=_modal_cancel.default);const modal=await modalTemplate.create({title:data.name,body:_templates.default.render("core_admin/themeselector/theme_preview_modal",data),large:!0,buttons:{save:(0,_str.getString)("selecttheme","moodle"),cancel:(0,_str.getString)("closebuttontitle","moodle")},show:!0});modal.getRoot().on(_modal_events.default.save,(()=>{modal.getRoot().find("form").submit()}))}}));
|
||||
|
||||
//# sourceMappingURL=preview_modal.min.js.map
|
||||
@@ -1 +1 @@
|
||||
{"version":3,"file":"preview_modal.min.js","sources":["../../src/themeselector/preview_modal.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 * Modal for theme previews.\n *\n * @module core_admin/themeselector/preview_modal\n * @copyright 2023 David Woloszyn <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalEvents from 'core/modal_events';\nimport ModalCancel from 'core/modal_cancel';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport Notification from 'core/notification';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\n\nconst SELECTORS = {\n PREVIEW: '[data-action=\"preview\"]',\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n */\nexport const init = () => {\n registerListenerEvents();\n};\n\n/**\n * Register theme related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n document.addEventListener('click', (e) => {\n const preview = e.target.closest(SELECTORS.PREVIEW);\n if (preview) {\n buildModal(preview).catch(Notification.exception);\n }\n });\n};\n\n/**\n * Build the modal with the provided data.\n *\n * @method buildModal\n * @param {object} element\n */\nconst buildModal = async(element) => {\n\n // This string can be long. We will fetch it with JS as opposed to passing it as an attribute.\n let description = await getString('choosereadme', 'theme_' + element.getAttribute('data-choose'));\n\n // Prepare data for modal.\n const data = {\n name: element.getAttribute('data-name'),\n image: element.getAttribute('data-image'),\n description: description.replace(/<[^>]+>/g, ' '), // Strip out HTML tags.\n current: element.getAttribute('data-current'),\n actionurl: element.getAttribute('data-actionurl'),\n choose: element.getAttribute('data-choose'),\n sesskey: element.getAttribute('data-sesskey'),\n };\n\n // Determine which modal template we should use.\n let modalTemplate = ModalSaveCancel;\n if (data.current) {\n modalTemplate = ModalCancel;\n }\n\n const modal = await modalTemplate.create({\n title: data.name,\n body: Templates.render('core_admin/themeselector/theme_preview_modal', data),\n large: true,\n buttons: {\n 'save': getString('selecttheme', 'moodle'),\n 'cancel': getString('closebuttontitle', 'moodle'),\n },\n show: true,\n });\n\n modal.getRoot().on(ModalEvents.save, () => {\n modal.getRoot().find('form').submit();\n });\n};\n"],"names":["SELECTORS","registerListenerEvents","document","addEventListener","e","preview","target","closest","buildModal","catch","Notification","exception","async","description","element","getAttribute","data","name","image","replace","current","actionurl","choose","sesskey","modalTemplate","ModalSaveCancel","ModalCancel","modal","create","title","body","Templates","render","large","buttons","show","getRoot","on","ModalEvents","save","find","submit"],"mappings":";;;;;;;gWA8BMA,kBACO,wCAQO,KAChBC,gCAQEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,UAC1BC,QAAUD,EAAEE,OAAOC,QAAQP,mBAC7BK,SACAG,WAAWH,SAASI,MAAMC,sBAAaC,eAW7CH,WAAaI,MAAAA,cAGXC,kBAAoB,kBAAU,eAAgB,SAAWC,QAAQC,aAAa,sBAG5EC,KAAO,CACTC,KAAMH,QAAQC,aAAa,aAC3BG,MAAOJ,QAAQC,aAAa,cAC5BF,YAAaA,YAAYM,QAAQ,WAAY,KAC7CC,QAASN,QAAQC,aAAa,gBAC9BM,UAAWP,QAAQC,aAAa,kBAChCO,OAAQR,QAAQC,aAAa,eAC7BQ,QAAST,QAAQC,aAAa,qBAI9BS,cAAgBC,2BAChBT,KAAKI,UACLI,cAAgBE,6BAGdC,YAAcH,cAAcI,OAAO,CACrCC,MAAOb,KAAKC,KACZa,KAAMC,mBAAUC,OAAO,+CAAgDhB,MACvEiB,OAAO,EACPC,QAAS,OACG,kBAAU,cAAe,kBACvB,kBAAU,mBAAoB,WAE5CC,MAAM,IAGVR,MAAMS,UAAUC,GAAGC,sBAAYC,MAAM,KACjCZ,MAAMS,UAAUI,KAAK,QAAQC"}
|
||||
{"version":3,"file":"preview_modal.min.js","sources":["../../src/themeselector/preview_modal.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 * Modal for theme previews.\n *\n * @module core_admin/themeselector/preview_modal\n * @copyright 2023 David Woloszyn <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport ModalEvents from 'core/modal_events';\nimport ModalCancel from 'core/modal_cancel';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport Notification from 'core/notification';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\n\nconst SELECTORS = {\n THEMES_CONTAINER: 'themelist',\n PREVIEW: '[data-action=\"preview\"]',\n};\n\n/**\n * Entrypoint of the js.\n *\n * @method init\n */\nexport const init = () => {\n registerListenerEvents();\n};\n\n/**\n * Register theme related event listeners.\n *\n * @method registerListenerEvents\n */\nconst registerListenerEvents = () => {\n document.addEventListener('click', (e) => {\n const preview = e.target.closest(SELECTORS.PREVIEW);\n if (preview) {\n buildModal(preview).catch(Notification.exception);\n }\n });\n};\n\n/**\n * Build the modal with the provided data.\n *\n * @method buildModal\n * @param {object} element\n */\nconst buildModal = async(element) => {\n\n // This string can be long. We will fetch it with JS as opposed to passing it as an attribute.\n let description = await getString('choosereadme', 'theme_' + element.getAttribute('data-choose'));\n\n const themesContainer = document.getElementById(SELECTORS.THEMES_CONTAINER);\n const definedInConfig = parseInt(themesContainer.dataset.definedinconfig);\n // Prepare data for modal.\n const data = {\n name: element.getAttribute('data-name'),\n image: element.getAttribute('data-image'),\n description: description.replace(/<[^>]+>/g, ' '), // Strip out HTML tags.\n current: element.getAttribute('data-current'),\n actionurl: element.getAttribute('data-actionurl'),\n choose: element.getAttribute('data-choose'),\n sesskey: element.getAttribute('data-sesskey'),\n definedinconfig: definedInConfig,\n };\n\n // Determine which modal template we should use.\n let modalTemplate = ModalSaveCancel;\n if (data.current || data.definedinconfig) {\n modalTemplate = ModalCancel;\n }\n\n const modal = await modalTemplate.create({\n title: data.name,\n body: Templates.render('core_admin/themeselector/theme_preview_modal', data),\n large: true,\n buttons: {\n 'save': getString('selecttheme', 'moodle'),\n 'cancel': getString('closebuttontitle', 'moodle'),\n },\n show: true,\n });\n\n modal.getRoot().on(ModalEvents.save, () => {\n modal.getRoot().find('form').submit();\n });\n};\n"],"names":["SELECTORS","registerListenerEvents","document","addEventListener","e","preview","target","closest","buildModal","catch","Notification","exception","async","description","element","getAttribute","themesContainer","getElementById","definedInConfig","parseInt","dataset","definedinconfig","data","name","image","replace","current","actionurl","choose","sesskey","modalTemplate","ModalSaveCancel","ModalCancel","modal","create","title","body","Templates","render","large","buttons","show","getRoot","on","ModalEvents","save","find","submit"],"mappings":";;;;;;;gWA8BMA,2BACgB,YADhBA,kBAEO,wCAQO,KAChBC,gCAQEA,uBAAyB,KAC3BC,SAASC,iBAAiB,SAAUC,UAC1BC,QAAUD,EAAEE,OAAOC,QAAQP,mBAC7BK,SACAG,WAAWH,SAASI,MAAMC,sBAAaC,eAW7CH,WAAaI,MAAAA,cAGXC,kBAAoB,kBAAU,eAAgB,SAAWC,QAAQC,aAAa,sBAE5EC,gBAAkBd,SAASe,eAAejB,4BAC1CkB,gBAAkBC,SAASH,gBAAgBI,QAAQC,iBAEnDC,KAAO,CACTC,KAAMT,QAAQC,aAAa,aAC3BS,MAAOV,QAAQC,aAAa,cAC5BF,YAAaA,YAAYY,QAAQ,WAAY,KAC7CC,QAASZ,QAAQC,aAAa,gBAC9BY,UAAWb,QAAQC,aAAa,kBAChCa,OAAQd,QAAQC,aAAa,eAC7Bc,QAASf,QAAQC,aAAa,gBAC9BM,gBAAiBH,qBAIjBY,cAAgBC,4BAChBT,KAAKI,SAAWJ,KAAKD,mBACrBS,cAAgBE,6BAGdC,YAAcH,cAAcI,OAAO,CACrCC,MAAOb,KAAKC,KACZa,KAAMC,mBAAUC,OAAO,+CAAgDhB,MACvEiB,OAAO,EACPC,QAAS,OACG,kBAAU,cAAe,kBACvB,kBAAU,mBAAoB,WAE5CC,MAAM,IAGVR,MAAMS,UAAUC,GAAGC,sBAAYC,MAAM,KACjCZ,MAAMS,UAAUI,KAAK,QAAQC"}
|
||||
@@ -29,6 +29,7 @@ import Templates from 'core/templates';
|
||||
import {getString} from 'core/str';
|
||||
|
||||
const SELECTORS = {
|
||||
THEMES_CONTAINER: 'themelist',
|
||||
PREVIEW: '[data-action="preview"]',
|
||||
};
|
||||
|
||||
@@ -66,6 +67,8 @@ const buildModal = async(element) => {
|
||||
// This string can be long. We will fetch it with JS as opposed to passing it as an attribute.
|
||||
let description = await getString('choosereadme', 'theme_' + element.getAttribute('data-choose'));
|
||||
|
||||
const themesContainer = document.getElementById(SELECTORS.THEMES_CONTAINER);
|
||||
const definedInConfig = parseInt(themesContainer.dataset.definedinconfig);
|
||||
// Prepare data for modal.
|
||||
const data = {
|
||||
name: element.getAttribute('data-name'),
|
||||
@@ -75,11 +78,12 @@ const buildModal = async(element) => {
|
||||
actionurl: element.getAttribute('data-actionurl'),
|
||||
choose: element.getAttribute('data-choose'),
|
||||
sesskey: element.getAttribute('data-sesskey'),
|
||||
definedinconfig: definedInConfig,
|
||||
};
|
||||
|
||||
// Determine which modal template we should use.
|
||||
let modalTemplate = ModalSaveCancel;
|
||||
if (data.current) {
|
||||
if (data.current || data.definedinconfig) {
|
||||
modalTemplate = ModalCancel;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,18 @@ class theme_selector implements renderable, templatable {
|
||||
/** @var array $themedata Theme data to pass to the template. */
|
||||
private $themedata = null;
|
||||
|
||||
/** @var bool Whether $CFG->theme is defined in config.php. */
|
||||
private $definedinconfig;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $themedata Theme data used for template.
|
||||
* @param bool $definedinconfig Whether $CFG->theme is defined in config.php.
|
||||
*/
|
||||
public function __construct(array $themedata) {
|
||||
public function __construct(array $themedata, bool $definedinconfig = false) {
|
||||
$this->themedata = $themedata;
|
||||
$this->definedinconfig = $definedinconfig;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,6 +64,7 @@ class theme_selector implements renderable, templatable {
|
||||
$resetbutton = new \single_button($reseturl, get_string('themeresetcaches', 'admin'), 'post',
|
||||
\single_button::BUTTON_SECONDARY);
|
||||
$data->resetbutton = $resetbutton->export_for_template($output);
|
||||
$data->definedinconfig = $this->definedinconfig;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,9 @@
|
||||
<div class="d-flex align-items-end flex-column p-3">
|
||||
{{#current}}
|
||||
<strong><span class="text-success" id="current-theme-{{choose}}">{{#str}}currenttheme, moodle{{/str}}</span></strong>
|
||||
{{#definedinconfig}}
|
||||
<div class="alert alert-info p-1">{{#str}}configoverride, admin{{/str}}</div>
|
||||
{{/definedinconfig}}
|
||||
{{/current}}
|
||||
{{^current}}
|
||||
{{#actionurl}}
|
||||
|
||||
@@ -39,9 +39,12 @@
|
||||
<div class="flex-grow-1">
|
||||
<p><strong>{{#str}}themepreviewdescription, moodle, {{name}}{{/str}}</strong></p>
|
||||
</div>
|
||||
<div>
|
||||
<div class="d-flex align-items-end flex-column">
|
||||
{{#current}}
|
||||
<strong><span class="text-success">{{#str}}currenttheme, moodle{{/str}}</span></strong>
|
||||
{{#definedinconfig}}
|
||||
<div class="alert alert-info p-1">{{#str}}configoverride, admin{{/str}}</div>
|
||||
{{/definedinconfig}}
|
||||
{{/current}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
{{>core/single_button}}
|
||||
</div>
|
||||
{{/resetbutton}}
|
||||
<div class="card-deck dashboard-card-deck" id="themelist" data-region="card-deck" role="list">
|
||||
<div class="card-deck dashboard-card-deck" id="themelist" data-region="card-deck" role="list" data-definedinconfig="{{definedinconfig}}">
|
||||
{{#themes}}
|
||||
{{>core_admin/themeselector/theme_card}}
|
||||
{{/themes}}
|
||||
|
||||
+10
-3
@@ -42,8 +42,15 @@ if ($reset && confirm_sesskey()) {
|
||||
theme_reset_all_caches();
|
||||
}
|
||||
|
||||
$definedinconfig = array_key_exists('theme', $CFG->config_php_settings);
|
||||
if ($definedinconfig) {
|
||||
$forcedthemename = get_string('pluginname', 'theme_'.$CFG->theme);
|
||||
// Show a notification that the theme is defined in config.php.
|
||||
\core\notification::info(get_string('themedefinedinconfigphp', 'admin', $forcedthemename));
|
||||
}
|
||||
|
||||
// Change theme.
|
||||
if (!empty($choose) && confirm_sesskey()) {
|
||||
if (!$definedinconfig && !empty($choose) && confirm_sesskey()) {
|
||||
|
||||
// Load the theme to make sure it is valid.
|
||||
$theme = theme_config::load($choose);
|
||||
@@ -105,7 +112,7 @@ foreach ($themes as $themename => $themedir) {
|
||||
if ($themename === $CFG->theme) {
|
||||
$themedata['current'] = true;
|
||||
$currentthemeindex = $index;
|
||||
} else {
|
||||
} else if (!$definedinconfig) {
|
||||
// Form params.
|
||||
$actionurl = new moodle_url('/admin/themeselector.php');
|
||||
$themedata['actionurl'] = $actionurl;
|
||||
@@ -124,7 +131,7 @@ if (isset($data[$currentthemeindex])) {
|
||||
}
|
||||
|
||||
// Show theme selector.
|
||||
$renderable = new \core_admin\output\theme_selector($data);
|
||||
$renderable = new \core_admin\output\theme_selector($data, $definedinconfig);
|
||||
$renderer = $PAGE->get_renderer('core', 'admin');
|
||||
echo $renderer->theme_selector_list($renderable);
|
||||
|
||||
|
||||
@@ -1452,6 +1452,7 @@ $string['testoutgoingmailconf_sentmail'] = 'This site has successfully sent a te
|
||||
$string['testoutgoingmailconf_subject'] = '{$a->site}: test message. {$a->additional} Sent: {$a->time}';
|
||||
$string['testoutgoingmailconf_subjectadditional'] = 'Additional subject';
|
||||
$string['testoutgoingmailconf_toemail'] = 'To email address';
|
||||
$string['themedefinedinconfigphp'] = 'The theme cannot be changed because it is set to {$a} in config.php.';
|
||||
$string['themedesignermode'] = 'Theme designer mode';
|
||||
$string['themedesignermodewarning'] = 'Theme designer mode is enabled. This should not be enabled on production sites as it can significantly reduce performance.';
|
||||
$string['themelist'] = 'Theme list';
|
||||
|
||||
Reference in New Issue
Block a user