mirror of
https://github.com/moodle/moodle.git
synced 2025-04-21 08:22:07 +02:00
MDL-78425 core_theme: Theme selection now uses cards and modal
This commit is contained in:
parent
e4d1369475
commit
103c2bb512
10
admin/amd/build/themeselector/preview_modal.min.js
vendored
Normal file
10
admin/amd/build/themeselector/preview_modal.min.js
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
define("core_admin/themeselector/preview_modal",["exports","core/modal_events","core/modal_cancel","core/modal_save_cancel","core/notification","core/templates","core/str"],(function(_exports,_modal_events,_modal_cancel,_modal_save_cancel,_notification,_templates,_str){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
/**
|
||||
* Modal for theme previews.
|
||||
*
|
||||
* @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()}))}}));
|
||||
|
||||
//# sourceMappingURL=preview_modal.min.js.map
|
1
admin/amd/build/themeselector/preview_modal.min.js.map
Normal file
1
admin/amd/build/themeselector/preview_modal.min.js.map
Normal file
@ -0,0 +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 <david.woloszyn@moodle.com>\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"}
|
100
admin/amd/src/themeselector/preview_modal.js
Normal file
100
admin/amd/src/themeselector/preview_modal.js
Normal file
@ -0,0 +1,100 @@
|
||||
// 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/>.
|
||||
|
||||
/**
|
||||
* Modal for theme previews.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
import ModalEvents from 'core/modal_events';
|
||||
import ModalCancel from 'core/modal_cancel';
|
||||
import ModalSaveCancel from 'core/modal_save_cancel';
|
||||
import Notification from 'core/notification';
|
||||
import Templates from 'core/templates';
|
||||
import {getString} from 'core/str';
|
||||
|
||||
const SELECTORS = {
|
||||
PREVIEW: '[data-action="preview"]',
|
||||
};
|
||||
|
||||
/**
|
||||
* Entrypoint of the js.
|
||||
*
|
||||
* @method init
|
||||
*/
|
||||
export const init = () => {
|
||||
registerListenerEvents();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register theme related event listeners.
|
||||
*
|
||||
* @method registerListenerEvents
|
||||
*/
|
||||
const registerListenerEvents = () => {
|
||||
document.addEventListener('click', (e) => {
|
||||
const preview = e.target.closest(SELECTORS.PREVIEW);
|
||||
if (preview) {
|
||||
buildModal(preview).catch(Notification.exception);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the modal with the provided data.
|
||||
*
|
||||
* @method buildModal
|
||||
* @param {object} element
|
||||
*/
|
||||
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'));
|
||||
|
||||
// Prepare data for modal.
|
||||
const data = {
|
||||
name: element.getAttribute('data-name'),
|
||||
image: element.getAttribute('data-image'),
|
||||
description: description.replace(/<[^>]+>/g, ' '), // Strip out HTML tags.
|
||||
current: element.getAttribute('data-current'),
|
||||
actionurl: element.getAttribute('data-actionurl'),
|
||||
choose: element.getAttribute('data-choose'),
|
||||
sesskey: element.getAttribute('data-sesskey'),
|
||||
};
|
||||
|
||||
// Determine which modal template we should use.
|
||||
let modalTemplate = ModalSaveCancel;
|
||||
if (data.current) {
|
||||
modalTemplate = ModalCancel;
|
||||
}
|
||||
|
||||
const modal = await modalTemplate.create({
|
||||
title: data.name,
|
||||
body: Templates.render('core_admin/themeselector/theme_preview_modal', data),
|
||||
large: true,
|
||||
buttons: {
|
||||
'save': getString('selecttheme', 'moodle'),
|
||||
'cancel': getString('closebuttontitle', 'moodle'),
|
||||
},
|
||||
show: true,
|
||||
});
|
||||
|
||||
modal.getRoot().on(ModalEvents.save, () => {
|
||||
modal.getRoot().find('form').submit();
|
||||
});
|
||||
};
|
65
admin/classes/output/theme_selector.php
Normal file
65
admin/classes/output/theme_selector.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?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_admin\output;
|
||||
|
||||
use moodle_url;
|
||||
use renderable;
|
||||
use renderer_base;
|
||||
use stdClass;
|
||||
use templatable;
|
||||
|
||||
/**
|
||||
* Theme selector renderable.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
|
||||
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class theme_selector implements renderable, templatable {
|
||||
|
||||
/** @var array $themedata Theme data to pass to the template. */
|
||||
private $themedata = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $themedata Theme data used for template.
|
||||
*/
|
||||
public function __construct(array $themedata) {
|
||||
$this->themedata = $themedata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export this data so it can be used as the context for a mustache template.
|
||||
*
|
||||
* @param renderer_base $output Renderer base.
|
||||
* @return stdClass
|
||||
*/
|
||||
public function export_for_template(renderer_base $output): stdClass {
|
||||
|
||||
$data = new stdClass();
|
||||
// Theme data used to populate cards and modal.
|
||||
$data->themes = $this->themedata;
|
||||
// Reset theme caches button.
|
||||
$reseturl = new moodle_url('admin/themeselector.php', ['sesskey' => sesskey(), 'reset' => 1]);
|
||||
$resetbutton = new \single_button($reseturl, get_string('themeresetcaches', 'admin'), 'post',
|
||||
\single_button::BUTTON_SECONDARY);
|
||||
$data->resetbutton = $resetbutton->export_for_template($output);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -2253,4 +2253,15 @@ class core_admin_renderer extends plugin_renderer_base {
|
||||
|
||||
return $this->warning($xmlrpcwarning);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the theme selector list.
|
||||
*
|
||||
* @param core_admin\output\theme_selector $themeselector
|
||||
* @return string HTML
|
||||
*/
|
||||
public function theme_selector_list(core_admin\output\theme_selector $themeselector): string {
|
||||
$renderable = $themeselector->export_for_template($this);
|
||||
return $this->render_from_template('core_admin/themeselector/theme_selector', $renderable);
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,8 @@ reports,core_reportbuilder|/reportbuilder/index.php',
|
||||
'10'
|
||||
));
|
||||
$ADMIN->add('themes', $temp);
|
||||
$ADMIN->add('themes', new admin_externalpage('themeselector', new lang_string('themeselector','admin'), $CFG->wwwroot . '/theme/index.php'));
|
||||
$ADMIN->add('themes', new admin_externalpage('themeselector',
|
||||
new lang_string('themeselector', 'admin'), $CFG->wwwroot . '/admin/themeselector.php'));
|
||||
|
||||
// settings for each theme
|
||||
foreach (core_component::get_plugin_list('theme') as $theme => $themedir) {
|
||||
|
76
admin/templates/themeselector/theme_card.mustache
Normal file
76
admin/templates/themeselector/theme_card.mustache
Normal file
@ -0,0 +1,76 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/themeselector/theme_card
|
||||
|
||||
This template renders the singular card for a theme.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "Boost",
|
||||
"choose": "boost",
|
||||
"image": "http://moodlesite/theme/image.php?theme=boost&image=screenshot&component=theme",
|
||||
"current": true,
|
||||
"actionurl": "http://moodlesite/admin/themeselector.php",
|
||||
"sesskey": "123XYZ"
|
||||
}
|
||||
}}
|
||||
<div class="card dashboard-card" role="listitem" id="theme-card-{{choose}}" aria-labelledby="theme-name-{{choose}} {{#current}}current-theme-{{choose}}{{/current}}">
|
||||
<div class="card-img dashboard-card-img" style='background-image: url("{{image}}");'></div>
|
||||
<div class="card-body p-3">
|
||||
<div class="d-flex">
|
||||
<div class="flex-grow-1">
|
||||
<h3 class="h5" id="theme-name-{{choose}}">{{name}}</h3>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
id="theme-preview-{{choose}}"
|
||||
class="btn btn-link p-0"
|
||||
title="{{#str}}previewthemename, moodle, {{name}}{{/str}}"
|
||||
data-action="preview"
|
||||
data-name="{{name}}"
|
||||
data-image="{{image}}"
|
||||
data-current="{{current}}"
|
||||
data-actionurl="{{actionurl}}"
|
||||
data-choose="{{choose}}"
|
||||
data-sesskey="{{sesskey}}">
|
||||
<i class="icon fa fa-info-circle m-0" aria-hidden="true"></i>
|
||||
<span class="sr-only">{{#str}}previewthemename, moodle, {{name}}{{/str}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
{{/current}}
|
||||
{{^current}}
|
||||
{{#actionurl}}
|
||||
<form method="post" action="{{actionurl}}" id="theme-select-form-{{choose}}">
|
||||
<input type="hidden" name="sesskey" value="{{sesskey}}">
|
||||
<input type="hidden" name="choose" value="{{choose}}">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<span aria-hidden="true">{{#str}}selecttheme, moodle{{/str}}</span>
|
||||
<span class="sr-only">{{#str}}selectthemename, moodle, {{name}}{{/str}}</span>
|
||||
</button>
|
||||
</form>
|
||||
{{/actionurl}}
|
||||
{{/current}}
|
||||
</div>
|
||||
</div>
|
||||
|
55
admin/templates/themeselector/theme_preview_modal.mustache
Normal file
55
admin/templates/themeselector/theme_preview_modal.mustache
Normal file
@ -0,0 +1,55 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/themeselector/theme_preview_modal
|
||||
|
||||
This template renders the modal for the selected theme.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"name": "Boost",
|
||||
"choose": "boost",
|
||||
"image": "http://moodlesite/theme/image.php?theme=boost&image=screenshot&component=theme",
|
||||
"description": "Boost is a modern highly-customisable theme...",
|
||||
"current": true,
|
||||
"actionurl": "http://moodlesite/admin/themeselector.php",
|
||||
"sesskey": "123XYZ"
|
||||
}
|
||||
}}
|
||||
<div>
|
||||
<div>
|
||||
<img
|
||||
src="{{image}}"
|
||||
alt=""
|
||||
id="modal-theme-preview-{{choose}}"
|
||||
class="w-100 mb-3">
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<div class="flex-grow-1">
|
||||
<p><strong>{{#str}}themepreviewdescription, moodle, {{name}}{{/str}}</strong></p>
|
||||
</div>
|
||||
<div>
|
||||
{{#current}}
|
||||
<strong><span class="text-success">{{#str}}currenttheme, moodle{{/str}}</span></strong>
|
||||
{{/current}}
|
||||
</div>
|
||||
</div>
|
||||
<p>{{description}}</p>
|
||||
{{#actionurl}}
|
||||
<form method="post" action="{{actionurl}}" id="modal-theme-select-form-{{choose}}">
|
||||
<input type="hidden" name="sesskey" value="{{sesskey}}">
|
||||
<input type="hidden" name="choose" value="{{choose}}">
|
||||
</form>
|
||||
{{/actionurl}}
|
||||
</div>
|
75
admin/templates/themeselector/theme_selector.mustache
Normal file
75
admin/templates/themeselector/theme_selector.mustache
Normal file
@ -0,0 +1,75 @@
|
||||
{{!
|
||||
This file is part of Moodle - http://moodle.org/
|
||||
|
||||
Moodle is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Moodle is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
{{!
|
||||
@template core_admin/themeselector/theme_selector
|
||||
|
||||
This template renders the cards view for choosing a theme.
|
||||
|
||||
Example context (json):
|
||||
{
|
||||
"themes": [
|
||||
{
|
||||
"name": "Boost",
|
||||
"choose": "boost",
|
||||
"image": "http://moodlesite/theme/image.php?theme=boost&image=screenshot&component=theme",
|
||||
"current": true,
|
||||
"actionurl": "http://moodlesite/admin/themeselector.php",
|
||||
"sesskey": "123XYZ"
|
||||
},
|
||||
{
|
||||
"name": "Classic",
|
||||
"choose": "classic",
|
||||
"image": "http://moodlesite/theme/image.php?theme=classic&image=screenshot&component=theme",
|
||||
"actionurl": "http://moodlesite/admin/themeselector.php",
|
||||
"sesskey": "123XYZ"
|
||||
}
|
||||
],
|
||||
"resetbutton": {
|
||||
"id": "single_button123",
|
||||
"method": "post",
|
||||
"url": "index.php",
|
||||
"label": "Clear theme caches",
|
||||
"params": [
|
||||
{
|
||||
"name": "sesskey",
|
||||
"value": "123XYZ"
|
||||
},
|
||||
{
|
||||
"name": "reset",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}}
|
||||
<h2>{{#str}}themeselector, admin{{/str}}</h2>
|
||||
{{#resetbutton}}
|
||||
<div class="mb-3">
|
||||
{{>core/single_button}}
|
||||
</div>
|
||||
{{/resetbutton}}
|
||||
<div class="card-deck dashboard-card-deck" id="themelist" data-region="card-deck" role="list">
|
||||
{{#themes}}
|
||||
{{>core_admin/themeselector/theme_card}}
|
||||
{{/themes}}
|
||||
</div>
|
||||
|
||||
{{#js}}
|
||||
require(['core_admin/themeselector/preview_modal'], function(Modal) {
|
||||
Modal.init();
|
||||
});
|
||||
{{/js}}
|
132
admin/themeselector.php
Normal file
132
admin/themeselector.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Theme selector page for admin use.
|
||||
*
|
||||
* @package core_admin
|
||||
* @copyright 2023 David Woloszyn <david.woloszyn@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
$choose = optional_param('choose', '', PARAM_PLUGIN);
|
||||
$reset = optional_param('reset', 0, PARAM_BOOL);
|
||||
$confirmation = optional_param('confirmation', 0, PARAM_BOOL);
|
||||
|
||||
admin_externalpage_setup('themeselector');
|
||||
|
||||
unset($SESSION->theme);
|
||||
|
||||
$PAGE->set_primary_active_tab('siteadminnode');
|
||||
$PAGE->navbar->add(get_string('themeselector', 'admin'), $PAGE->url);
|
||||
$PAGE->set_pagelayout('standard');
|
||||
|
||||
// Clear theme cache.
|
||||
if ($reset && confirm_sesskey()) {
|
||||
theme_reset_all_caches();
|
||||
}
|
||||
|
||||
// Change theme.
|
||||
if (!empty($choose) && confirm_sesskey()) {
|
||||
|
||||
// Load the theme to make sure it is valid.
|
||||
$theme = theme_config::load($choose);
|
||||
|
||||
if ($theme instanceof \theme_config) {
|
||||
set_config('theme', $theme->name);
|
||||
$notifytype = 'success';
|
||||
$notifymessage = get_string('themesaved');
|
||||
} else {
|
||||
$notifytype = 'error';
|
||||
$notifymessage = get_string('error');
|
||||
}
|
||||
|
||||
// Redirect with notification.
|
||||
redirect(new moodle_url('/admin/themeselector.php'), $notifymessage, null, $notifytype);
|
||||
}
|
||||
|
||||
// Insert header.
|
||||
echo $OUTPUT->header();
|
||||
|
||||
// Prepare data for rendering.
|
||||
$data = [];
|
||||
$index = 0;
|
||||
$currentthemeindex = 0;
|
||||
$themes = core_component::get_plugin_list('theme');
|
||||
|
||||
// Loop through available themes.
|
||||
foreach ($themes as $themename => $themedir) {
|
||||
|
||||
try {
|
||||
$theme = theme_config::load($themename);
|
||||
} catch (Exception $e) {
|
||||
// Bad theme, just skip it for now.
|
||||
continue;
|
||||
}
|
||||
if ($themename !== $theme->name) {
|
||||
// Obsoleted or broken theme, just skip for now.
|
||||
continue;
|
||||
}
|
||||
if (empty($CFG->themedesignermode) && $theme->hidefromselector) {
|
||||
// The theme doesn't want to be shown in the theme selector and as theme
|
||||
// designer mode is switched off we will respect that decision.
|
||||
continue;
|
||||
}
|
||||
|
||||
// All params for modal use are set here, except for 'choosereadme' (description).
|
||||
// That string can be long. We will fetch it with JS as opposed to passing it as an attribute.
|
||||
$themedata = [];
|
||||
|
||||
// The 'name' param is formatted and should not to be confused with 'choose'.
|
||||
$themedata['name'] = get_string('pluginname', 'theme_'.$themename);;
|
||||
$themedata['choose'] = $themename;
|
||||
|
||||
// Image to display for previewing.
|
||||
$image = new moodle_url('/theme/image.php', ['theme' => $themename, 'image' => 'screenshot', 'component' => 'theme']);
|
||||
$themedata['image'] = $image;
|
||||
|
||||
// Is this the current theme?
|
||||
if ($themename === $CFG->theme) {
|
||||
$themedata['current'] = true;
|
||||
$currentthemeindex = $index;
|
||||
} else {
|
||||
// Form params.
|
||||
$actionurl = new moodle_url('/admin/themeselector.php');
|
||||
$themedata['actionurl'] = $actionurl;
|
||||
$themedata['sesskey'] = sesskey();
|
||||
}
|
||||
|
||||
$data[$index] = $themedata;
|
||||
$index++;
|
||||
}
|
||||
|
||||
// Reorder the array to always have the current theme first.
|
||||
if (isset($data[$currentthemeindex])) {
|
||||
$currenttheme = $data[$currentthemeindex];
|
||||
unset($data[$currentthemeindex]);
|
||||
array_unshift($data, $currenttheme);
|
||||
}
|
||||
|
||||
// Show theme selector.
|
||||
$renderable = new \core_admin\output\theme_selector($data);
|
||||
$renderer = $PAGE->get_renderer('core', 'admin');
|
||||
echo $renderer->theme_selector_list($renderable);
|
||||
|
||||
// Show footer.
|
||||
echo $OUTPUT->footer();
|
@ -483,6 +483,7 @@ $string['currentlanguage'] = 'Current language';
|
||||
$string['currentlocaltime'] = 'your current local time';
|
||||
$string['currentpicture'] = 'Current picture';
|
||||
$string['currentrelease'] = 'Current release information';
|
||||
$string['currenttheme'] = 'Current theme';
|
||||
$string['currentversion'] = 'Current version';
|
||||
$string['databasechecking'] = 'Upgrading Moodle database from version {$a->oldversion} to {$a->newversion}';
|
||||
$string['databaseperformance'] = 'Database performance';
|
||||
@ -1723,6 +1724,7 @@ $string['preprocessingbackupfile'] = 'Preprocessing backup file';
|
||||
$string['prev'] = 'Prev';
|
||||
$string['preview'] = 'Preview';
|
||||
$string['previeworchoose'] = 'Preview or choose a theme';
|
||||
$string['previewthemename'] = 'Preview theme \'{$a}\'';
|
||||
$string['previous'] = 'Previous';
|
||||
$string['previouslyselectedusers'] = 'Previously selected users not matching \'{$a}\'';
|
||||
$string['previouspage'] = 'Previous page';
|
||||
@ -2004,6 +2006,8 @@ $string['selectperiod'] = 'Select period';
|
||||
$string['selectcategorysort'] = 'Which categories would you like to sort?';
|
||||
$string['selectcategorysortby'] = 'Select how you would like to sort categories';
|
||||
$string['selectcoursesortby'] = 'Select how you would like to sort courses';
|
||||
$string['selecttheme'] = 'Select theme';
|
||||
$string['selectthemename'] = 'Select theme \'{$a}\'';
|
||||
$string['senddetails'] = 'Send my details via email';
|
||||
$string['sent'] = 'Sent';
|
||||
$string['separate'] = 'Separate';
|
||||
@ -2242,6 +2246,8 @@ $string['timesplitting:tenpercentafterstart'] = '10% after start';
|
||||
$string['timesplitting:tenpercentafterstart_help'] = 'This analysis interval generates a prediction after the 10% of the course is completed.';
|
||||
$string['thanks'] = 'Thanks';
|
||||
$string['theme'] = 'Theme';
|
||||
$string['themepreviewdescription'] = '{$a} theme description';
|
||||
$string['themepreviewimage'] = '{$a} preview image';
|
||||
$string['themes'] = 'Themes';
|
||||
$string['themesaved'] = 'New theme saved';
|
||||
$string['thereareno'] = 'There are no {$a} in this course';
|
||||
|
@ -91,6 +91,6 @@ class theme extends base {
|
||||
* @return moodle_url
|
||||
*/
|
||||
public static function get_manage_url() {
|
||||
return new moodle_url('/theme/index.php');
|
||||
return new moodle_url('/admin/themeselector.php');
|
||||
}
|
||||
}
|
||||
|
@ -367,14 +367,6 @@
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
#adminthemeselector table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#adminthemeselector .selectedtheme {
|
||||
border: 1px solid $state-info-border;
|
||||
}
|
||||
|
||||
.admin_colourpicker,
|
||||
.admin_colourpicker_preview {
|
||||
display: none;
|
||||
|
@ -26674,14 +26674,6 @@ body.behat-site .action-menu .dropdown-subpanel-content.show {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
#adminthemeselector table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#adminthemeselector .selectedtheme {
|
||||
border: 1px solid #b8dce2;
|
||||
}
|
||||
|
||||
.admin_colourpicker,
|
||||
.admin_colourpicker_preview {
|
||||
display: none;
|
||||
|
28
theme/boost/tests/behat/theme_selector.feature
Normal file
28
theme/boost/tests/behat/theme_selector.feature
Normal file
@ -0,0 +1,28 @@
|
||||
@core @core_admin @theme_boost
|
||||
Feature: Select a theme in Boost theme
|
||||
In order to choose a theme
|
||||
As an admin
|
||||
I need to preview the theme and make a selection
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And I navigate to "Appearance > Themes > Theme selector" in site administration
|
||||
|
||||
@javascript
|
||||
Scenario: I am able to preview a theme using a modal window
|
||||
When I click on "Preview theme 'Boost'" "button"
|
||||
Then I should see "Boost" in the "Boost" "dialogue"
|
||||
And I should see "Boost is a modern highly-customisable theme." in the "Boost" "dialogue"
|
||||
And I should see "Current theme" in the "Boost" "dialogue"
|
||||
|
||||
@javascript
|
||||
Scenario: I am able to change the theme using the modal window select button
|
||||
Given I should see "Current theme" in the "#theme-card-boost" "css_element"
|
||||
When I click on "Preview theme 'Classic'" "button"
|
||||
And I click on "Select theme" "button" in the "Classic" "dialogue"
|
||||
Then I should see "Current theme" in the "#theme-card-classic" "css_element"
|
||||
|
||||
Scenario: I am able to change the theme using the normal select button
|
||||
Given I should see "Current theme" in the "#theme-card-boost" "css_element"
|
||||
When I click on "Select theme 'Classic'" "button"
|
||||
Then I should see "Current theme" in the "#theme-card-classic" "css_element"
|
@ -26674,14 +26674,6 @@ body.behat-site .action-menu .dropdown-subpanel-content.show {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
#adminthemeselector table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
#adminthemeselector .selectedtheme {
|
||||
border: 1px solid #b8dce2;
|
||||
}
|
||||
|
||||
.admin_colourpicker,
|
||||
.admin_colourpicker_preview {
|
||||
display: none;
|
||||
|
28
theme/classic/tests/behat/theme_selector.feature
Normal file
28
theme/classic/tests/behat/theme_selector.feature
Normal file
@ -0,0 +1,28 @@
|
||||
@core @core_admin @theme_classic
|
||||
Feature: Select a theme in Classic theme
|
||||
In order to choose a theme
|
||||
As an admin
|
||||
I need to preview the theme and make a selection
|
||||
|
||||
Background:
|
||||
Given I log in as "admin"
|
||||
And I navigate to "Appearance > Themes > Theme selector" in site administration
|
||||
|
||||
@javascript
|
||||
Scenario: I am able to preview a theme using a modal window
|
||||
When I click on "Preview theme 'Classic'" "button"
|
||||
Then I should see "Classic" in the "Classic" "dialogue"
|
||||
And I should see "Classic is a highly-customisable theme," in the "Classic" "dialogue"
|
||||
And I should see "Current theme" in the "Classic" "dialogue"
|
||||
|
||||
@javascript
|
||||
Scenario: I am able to change the theme using the modal window select button
|
||||
Given I should see "Current theme" in the "#theme-card-classic" "css_element"
|
||||
When I click on "Preview theme 'Boost'" "button"
|
||||
And I click on "Select theme" "button" in the "Boost" "dialogue"
|
||||
Then I should see "Current theme" in the "#theme-card-boost" "css_element"
|
||||
|
||||
Scenario: I am able to change the theme using the normal select button
|
||||
Given I should see "Current theme" in the "#theme-card-classic" "css_element"
|
||||
When I click on "Select theme 'Boost'" "button"
|
||||
Then I should see "Current theme" in the "#theme-card-boost" "css_element"
|
104
theme/index.php
104
theme/index.php
@ -24,105 +24,5 @@
|
||||
require_once(__DIR__ . '/../config.php');
|
||||
require_once($CFG->libdir . '/adminlib.php');
|
||||
|
||||
$choose = optional_param('choose', '', PARAM_PLUGIN);
|
||||
$reset = optional_param('reset', 0, PARAM_BOOL);
|
||||
$confirmation = optional_param('confirmation', 0, PARAM_BOOL);
|
||||
|
||||
admin_externalpage_setup('themeselector');
|
||||
|
||||
unset($SESSION->theme);
|
||||
|
||||
$PAGE->set_primary_active_tab('siteadminnode');
|
||||
$PAGE->navbar->add(get_string('themeselector', 'admin'), $PAGE->url);
|
||||
|
||||
// Clear theme cache.
|
||||
if ($reset and confirm_sesskey()) {
|
||||
theme_reset_all_caches();
|
||||
}
|
||||
|
||||
// Change theme.
|
||||
if (!empty($choose) && confirm_sesskey()) {
|
||||
|
||||
// Load the theme to make sure it is valid.
|
||||
$theme = theme_config::load($choose);
|
||||
|
||||
if ($theme instanceof \theme_config) {
|
||||
set_config('theme', $theme->name);
|
||||
$notifytype = 'success';
|
||||
$notifymessage = get_string('themesaved');
|
||||
} else {
|
||||
$notifytype = 'error';
|
||||
$notifymessage = get_string('error');
|
||||
}
|
||||
|
||||
// Redirect with notification.
|
||||
redirect(new moodle_url('/theme/index.php'), $notifymessage, null, $notifytype);
|
||||
}
|
||||
|
||||
$table = new html_table();
|
||||
$table->data = [];
|
||||
$table->id = 'adminthemeselector';
|
||||
$table->head = [get_string('theme'), get_string('info')];
|
||||
$table->align = ['left', 'left'];
|
||||
|
||||
$themes = core_component::get_plugin_list('theme');
|
||||
|
||||
// Loop through available themes.
|
||||
foreach ($themes as $themename => $themedir) {
|
||||
|
||||
try {
|
||||
$theme = theme_config::load($themename);
|
||||
} catch (Exception $e) {
|
||||
// Bad theme, just skip it for now.
|
||||
continue;
|
||||
}
|
||||
if ($themename !== $theme->name) {
|
||||
// Obsoleted or broken theme, just skip for now.
|
||||
continue;
|
||||
}
|
||||
if (empty($CFG->themedesignermode) && $theme->hidefromselector) {
|
||||
// The theme doesn't want to be shown in the theme selector and as theme
|
||||
// designer mode is switched off we will respect that decision.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build the table rows.
|
||||
$row = [];
|
||||
$rowclasses = [];
|
||||
$strthemename = get_string('pluginname', 'theme_'.$themename);
|
||||
|
||||
// Screenshot/preview cell.
|
||||
$screenshotpath = new moodle_url('/theme/image.php', ['theme' => $themename, 'image' => 'screenshot', 'component' => 'theme']);
|
||||
$row[] = html_writer::empty_tag('img', ['class' => 'img-fluid', 'src' => $screenshotpath, 'alt' => $strthemename]);
|
||||
|
||||
// Info cell.
|
||||
$infocell = $OUTPUT->heading($strthemename, 3);
|
||||
|
||||
// Is this the current theme?
|
||||
if ($themename == $CFG->theme) {
|
||||
$rowclasses[] = 'selectedtheme';
|
||||
} else {
|
||||
// Button to choose this as the main theme.
|
||||
$setthemestr = get_string('usetheme');
|
||||
$setthemeurl = new moodle_url('/theme/index.php', ['choose' => $themename, 'sesskey' => sesskey()]);
|
||||
$setthemebutton = new single_button($setthemeurl, $setthemestr, 'post');
|
||||
$infocell .= html_writer::div($OUTPUT->render($setthemebutton), 'mt-2');
|
||||
}
|
||||
$row[] = $infocell;
|
||||
|
||||
$table->data[$themename] = $row;
|
||||
$table->rowclasses[$themename] = join(' ', $rowclasses);
|
||||
$table->responsive = false;
|
||||
}
|
||||
|
||||
// Show heading.
|
||||
echo $OUTPUT->header();
|
||||
echo $OUTPUT->heading(get_string('themeselector', 'admin'));
|
||||
|
||||
// Reset theme caches button.
|
||||
$reseturl = new moodle_url('index.php', ['sesskey' => sesskey(), 'reset' => 1]);
|
||||
echo $OUTPUT->single_button($reseturl, get_string('themeresetcaches', 'admin'), 'post');
|
||||
|
||||
// Render main table.
|
||||
echo html_writer::table($table);
|
||||
echo $OUTPUT->footer();
|
||||
// Theme selector has been moved to admin/themeselector.php.
|
||||
redirect (new moodle_url('/admin/themeselector.php'));
|
||||
|
@ -1,6 +1,11 @@
|
||||
This files describes API changes in /theme/* themes,
|
||||
information provided here is intended especially for theme designer.
|
||||
|
||||
=== 4.4 ===
|
||||
* Theme selection is now performed using cards and modals.
|
||||
* The 'choosereadme' string for each theme is expected to be plain text. HTML content will not render when displayed in the modal.
|
||||
* Theme selector page has been moved to admin/themeselector.php (previously theme/index.php)
|
||||
|
||||
=== 4.3 ===
|
||||
* The core_renderer::htmllize_file_tree method has been deprecated. This was missed before Moodle 2.0.
|
||||
* CSS selectors using #categoryquestions have been changed to use .question-bank-table, see question/upgrade.txt
|
||||
|
Loading…
x
Reference in New Issue
Block a user