MDL-62757 themes: validate theme preset files

Preset files need to be validated to ensure compiling SCSS does not
break.
This commit is contained in:
Bas Brands 2019-08-06 17:10:56 +02:00
parent 800563e415
commit 8040fafaa8
5 changed files with 100 additions and 4 deletions

View File

@ -660,6 +660,7 @@ $string['intlrecommended'] = 'Intl extension is used to improve internationaliza
$string['intlrequired'] = 'Intl extension is required to improve internationalization support, such as locale aware sorting and international domain names.';
$string['invalidagedigitalconsent'] = 'The digital age of consent is not valid: {$a}';
$string['invalidforgottenpasswordurl'] = 'The forgotten password URL is not a valid URL.';
$string['invalidthemepreset'] = 'The chosen preset file is not compatible with this theme. The SCSS compile error was: "{$a}"';
$string['invalidsection'] = 'Invalid section.';
$string['invaliduserchangeme'] = 'Username "changeme" is reserved -- you cannot create an account with it.';
$string['ipblocked'] = 'This site is not available currently.';

View File

@ -11092,3 +11092,95 @@ class admin_settings_sitepolicy_handler_select extends admin_setting_configselec
return true;
}
}
/**
* Used to validate theme presets code and ensuring they compile well.
*
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @copyright 2019 Bas Brands <bas@moodle.com>
*/
class admin_setting_configthemepreset extends admin_setting_configselect {
/** @var string The name of the theme to check for */
private $themename;
/**
* Constructor
* @param string $name unique ascii name, either 'mysetting' for settings that in config,
* or 'myplugin/mysetting' for ones in config_plugins.
* @param string $visiblename localised
* @param string $description long localised info
* @param string|int $defaultsetting
* @param array $choices array of $value=>$label for each selection
* @param string $themename name of theme to check presets for.
*/
public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
$this->themename = $themename;
parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
}
/**
* Write settings if validated
*
* @param string $data
* @return string
*/
public function write_setting($data) {
$validated = $this->validate($data);
if ($validated !== true) {
return $validated;
}
return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
}
/**
* Validate the preset file to ensure its parsable.
*
* @param string $data The preset file chosen.
* @return mixed bool true for success or string:error on failure.
*/
public function validate($data) {
if (in_array($data, ['default.scss', 'plain.scss'])) {
return true;
}
$fs = get_file_storage();
$theme = theme_config::load($this->themename);
$context = context_system::instance();
// If the preset has not changed there is no need to validate it.
if ($theme->settings->preset == $data) {
return true;
}
if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
// This operation uses a lot of resources.
raise_memory_limit(MEMORY_EXTRA);
core_php_time_limit::raise(300);
// TODO: MDL-62757 When changing anything in this method please do not forget to check
// if the get_css_content_from_scss() method in class theme_config needs updating too.
$compiler = new core_scss();
$compiler->prepend_raw_scss($theme->get_pre_scss_code());
$compiler->append_raw_scss($presetfile->get_content());
if ($scssproperties = $theme->get_scss_property()) {
$compiler->setImportPaths($scssproperties[0]);
}
$compiler->append_raw_scss($theme->get_extra_scss_code());
try {
$compiler->to_css();
} catch (Exception $e) {
return get_string('invalidthemepreset', 'admin', $e->getMessage());
}
// Try to save memory.
$compiler = null;
unset($compiler);
}
return true;
}
}

View File

@ -1409,6 +1409,9 @@ class theme_config {
raise_memory_limit(MEMORY_EXTRA);
core_php_time_limit::raise(300);
// TODO: MDL-62757 When changing anything in this method please do not forget to check
// if the validate() method in class admin_setting_configthemepreset needs updating too.
// Set-up the compiler.
$compiler = new core_scss();
$compiler->prepend_raw_scss($this->get_pre_scss_code());
@ -1485,7 +1488,7 @@ class theme_config {
*
* @return string The SCSS code to inject.
*/
protected function get_extra_scss_code() {
public function get_extra_scss_code() {
$content = '';
// Getting all the candidate functions.
@ -1515,7 +1518,7 @@ class theme_config {
*
* @return string The SCSS code to inject.
*/
protected function get_pre_scss_code() {
public function get_pre_scss_code() {
$content = '';
// Getting all the candidate functions.

View File

@ -44,7 +44,7 @@ if ($ADMIN->fulltree) {
$choices['default.scss'] = 'default.scss';
$choices['plain.scss'] = 'plain.scss';
$setting = new admin_setting_configselect($name, $title, $description, $default, $choices);
$setting = new admin_setting_configthemepreset($name, $title, $description, $default, $choices, 'boost');
$setting->set_updatedcallback('theme_reset_all_caches');
$page->add($setting);

View File

@ -54,7 +54,7 @@ if ($ADMIN->fulltree) {
$choices['default.scss'] = 'default.scss';
$choices['plain.scss'] = 'plain.scss';
$setting = new admin_setting_configselect($name, $title, $description, $default, $choices);
$setting = new admin_setting_configthemepreset($name, $title, $description, $default, $choices, 'classic');
$setting->set_updatedcallback('theme_reset_all_caches');
$page->add($setting);