MDL-78425 core_theme: Theme selection now uses cards and modal

This commit is contained in:
David Woloszyn 2023-09-27 10:44:36 +10:00
parent e4d1369475
commit 103c2bb512
19 changed files with 597 additions and 128 deletions

View 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

View 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"}

View 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();
});
};

View 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;
}
}

View File

@ -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);
}
}

View File

@ -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) {

View 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>

View 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>

View 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
View 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();

View File

@ -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';

View File

@ -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');
}
}

View File

@ -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;

View File

@ -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;

View 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"

View File

@ -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;

View 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"

View File

@ -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'));

View File

@ -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