mirror of
https://github.com/moodle/moodle.git
synced 2025-01-17 21:49:15 +01:00
Merge branch 'wip_MDL-79802_line_v8' of https://github.com/gjb2048/moodle
This commit is contained in:
commit
4e341bfc97
@ -41,4 +41,18 @@ if (!empty($defaulth5plib)) {
|
||||
|
||||
$settings->add(new admin_settings_h5plib_handler_select('h5plibraryhandler', new lang_string('h5plibraryhandler', 'core_h5p'),
|
||||
new lang_string('h5plibraryhandler_help', 'core_h5p'), $defaulth5plib));
|
||||
|
||||
$setting = new admin_setting_configtextarea(
|
||||
'core_h5p/h5pcustomcss',
|
||||
new lang_string('h5pcustomcss', 'core_h5p'),
|
||||
new lang_string('h5pcustomcss_help', 'core_h5p'),
|
||||
'',
|
||||
PARAM_NOTAGS
|
||||
);
|
||||
$setting->set_updatedcallback(function () {
|
||||
// Enables use of file_storage constants.
|
||||
\core_h5p\local\library\autoloader::register();
|
||||
\core_h5p\file_storage::generate_custom_styles();
|
||||
});
|
||||
$settings->add($setting);
|
||||
}
|
||||
|
@ -393,6 +393,14 @@ class api {
|
||||
$settings->tool_dataprivacy_showdataretentionsummary = get_config('tool_dataprivacy', 'showdataretentionsummary');
|
||||
}
|
||||
|
||||
if (empty($section) || $section === 'h5psettings') {
|
||||
\core_h5p\local\library\autoloader::register();
|
||||
$customcss = \core_h5p\file_storage::get_custom_styles();
|
||||
if (!empty($customcss)) {
|
||||
$settings->h5pcustomcssurl = $customcss['cssurl']->out() . '?ver=' . $customcss['cssversion'];
|
||||
}
|
||||
}
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
|
@ -273,6 +273,18 @@ class externallib_test extends externallib_advanced_testcase {
|
||||
$this->assertCount(0, $result['warnings']);
|
||||
$this->assertEquals($expected, $result['settings']);
|
||||
|
||||
// H5P custom CSS.
|
||||
set_config('h5pcustomcss', '.debug { color: #fab; }', 'core_h5p');
|
||||
\core_h5p\local\library\autoloader::register();
|
||||
\core_h5p\file_storage::generate_custom_styles();
|
||||
$result = external::get_config();
|
||||
$result = external_api::clean_returnvalue(external::get_config_returns(), $result);
|
||||
|
||||
$customcss = \core_h5p\file_storage::get_custom_styles();
|
||||
$expected[] = ['name' => 'h5pcustomcssurl', 'value' => $customcss['cssurl']->out() . '?ver=' . $customcss['cssversion']];
|
||||
$this->assertCount(0, $result['warnings']);
|
||||
$this->assertEquals($expected, $result['settings']);
|
||||
|
||||
// Change a value and retrieve filtering by section.
|
||||
set_config('commentsperpage', 1);
|
||||
$expected[10]['value'] = 1;
|
||||
|
@ -54,6 +54,8 @@ class file_storage implements H5PFileStorage {
|
||||
public const CSS_FILEAREA = 'css';
|
||||
/** The icon filename */
|
||||
public const ICON_FILENAME = 'icon.svg';
|
||||
/** The custom CSS filename */
|
||||
private const CUSTOM_CSS_FILENAME = 'custom_h5p.css';
|
||||
|
||||
/**
|
||||
* @var \context $context Currently we use the system context everywhere.
|
||||
@ -879,4 +881,104 @@ class file_storage implements H5PFileStorage {
|
||||
|
||||
$this->fs->create_file_from_pathname($record, $sourcefile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate H5P custom styles if any.
|
||||
*/
|
||||
public static function generate_custom_styles(): void {
|
||||
$record = self::get_custom_styles_file_record();
|
||||
$cssfile = self::get_custom_styles_file($record);
|
||||
if ($cssfile) {
|
||||
// The CSS file needs to be updated, so delete and recreate it
|
||||
// if there is CSS in the 'h5pcustomcss' setting.
|
||||
$cssfile->delete();
|
||||
}
|
||||
|
||||
$css = get_config('core_h5p', 'h5pcustomcss');
|
||||
if (!empty($css)) {
|
||||
$fs = get_file_storage();
|
||||
$fs->create_file_from_string($record, $css);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get H5P custom styles if any.
|
||||
*
|
||||
* @throws \moodle_exception If the CSS setting is empty but there is a file to serve
|
||||
* or there is no file but the CSS setting is not empty.
|
||||
* @return array|null If there is CSS then an array with the keys 'cssurl'
|
||||
* and 'cssversion' is returned otherwise null. 'cssurl' is a link to the
|
||||
* generated 'custom_h5p.css' file and 'cssversion' the md5 hash of its contents.
|
||||
*/
|
||||
public static function get_custom_styles(): ?array {
|
||||
$record = self::get_custom_styles_file_record();
|
||||
|
||||
$css = get_config('core_h5p', 'h5pcustomcss');
|
||||
if (self::get_custom_styles_file($record)) {
|
||||
if (empty($css)) {
|
||||
// The custom CSS file exists and yet the setting 'h5pcustomcss' is empty.
|
||||
// This prevents an invalid content hash.
|
||||
throw new \moodle_exception(
|
||||
'The H5P \'h5pcustomcss\' setting is empty and yet the custom CSS file \''.
|
||||
$record['filename'].
|
||||
'\' exists.',
|
||||
'core_h5p'
|
||||
);
|
||||
}
|
||||
// File exists, so generate the url and version hash.
|
||||
$cssurl = \moodle_url::make_pluginfile_url(
|
||||
$record['contextid'],
|
||||
$record['component'],
|
||||
$record['filearea'],
|
||||
null,
|
||||
$record['filepath'],
|
||||
$record['filename']
|
||||
);
|
||||
return ['cssurl' => $cssurl, 'cssversion' => md5($css)];
|
||||
} else if (!empty($css)) {
|
||||
// The custom CSS file does not exist and yet should do.
|
||||
throw new \moodle_exception(
|
||||
'The H5P custom CSS file \''.
|
||||
$record['filename'].
|
||||
'\' does not exist and yet there is CSS in the \'h5pcustomcss\' setting.',
|
||||
'core_h5p'
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get H5P custom styles file record.
|
||||
*
|
||||
* @return array File record for the CSS custom styles.
|
||||
*/
|
||||
private static function get_custom_styles_file_record(): array {
|
||||
return [
|
||||
'contextid' => \context_system::instance()->id,
|
||||
'component' => self::COMPONENT,
|
||||
'filearea' => self::CSS_FILEAREA,
|
||||
'itemid' => 0,
|
||||
'filepath' => '/',
|
||||
'filename' => self::CUSTOM_CSS_FILENAME,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get H5P custom styles file.
|
||||
*
|
||||
* @param array $record The H5P custom styles file record.
|
||||
*
|
||||
* @return stored_file|bool stored_file instance if exists, false if not.
|
||||
*/
|
||||
private static function get_custom_styles_file($record): stored_file|bool {
|
||||
$fs = get_file_storage();
|
||||
return $fs->get_file(
|
||||
$record['contextid'],
|
||||
$record['component'],
|
||||
$record['filearea'],
|
||||
$record['itemid'],
|
||||
$record['filepath'],
|
||||
$record['filename']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -39,67 +39,14 @@ class renderer extends plugin_renderer_base {
|
||||
* @param string $embedtype Possible values: div, iframe, external, editor
|
||||
*/
|
||||
public function h5p_alter_styles(&$styles, array $libraries, string $embedtype) {
|
||||
global $CFG, $DB;
|
||||
|
||||
$record = [
|
||||
'contextid' => \context_system::instance()->id,
|
||||
'component' => \core_h5p\file_storage::COMPONENT,
|
||||
'filearea' => \core_h5p\file_storage::CSS_FILEAREA,
|
||||
'itemid' => 0,
|
||||
'filepath' => '/',
|
||||
'filename' => $CFG->theme . '_h5p.css',
|
||||
];
|
||||
$fs = get_file_storage();
|
||||
// Check if the CSS file for the current theme needs to be updated (because the SCSS settings have changed recently).
|
||||
if ($cssfile = $fs->get_file(
|
||||
$record['contextid'],
|
||||
$record['component'],
|
||||
$record['filearea'],
|
||||
$record['itemid'],
|
||||
$record['filepath'],
|
||||
$record['filename'])) {
|
||||
// Get the last time when the SCSS and CSSPRE settings were updated for the current theme and compare it with the
|
||||
// time modified of the H5P CSS file, to determine whether it needs to be updated.
|
||||
$sql = "SELECT MAX(timemodified) as timemodified
|
||||
FROM {config_log}
|
||||
WHERE plugin = :theme AND (name = 'scss' OR name = 'scsspre')";
|
||||
$params = ['theme' => 'theme_' . $CFG->theme];
|
||||
$setting = $DB->get_record_sql($sql, $params);
|
||||
if ($setting && $setting->timemodified > $cssfile->get_timemodified()) {
|
||||
// The CSS file needs to be updated. First, delete it to recreate it later with the current CSS.
|
||||
$cssfile->delete();
|
||||
$cssfile = null;
|
||||
}
|
||||
$customcss = \core_h5p\file_storage::get_custom_styles();
|
||||
if (!empty($customcss)) {
|
||||
// Add the CSS file to the styles array, to load it from the H5P player.
|
||||
$styles[] = (object) [
|
||||
'path' => $customcss['cssurl']->out(),
|
||||
'version' => '?ver='.$customcss['cssversion'],
|
||||
];
|
||||
}
|
||||
|
||||
$theme = \theme_config::load($CFG->theme);
|
||||
// When 'Raw initial SCSS' and 'Raw SCSS' theme settings are empty, the file doesn't need to be created.
|
||||
if (empty($theme->settings->scsspre) && empty($theme->settings->scss)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the CSS file doesn't exist, create it with the styles defined in 'Raw initial SCSS' and 'Raw SCSS' theme settings.
|
||||
// As these scss and scsspre settings might have dependencies on the theme, the whole CSS theme content will be used and
|
||||
// passed to the H5P player.
|
||||
if (!$cssfile) {
|
||||
$css = $theme->get_css_content();
|
||||
$cssfile = $fs->create_file_from_string($record, $css);
|
||||
}
|
||||
|
||||
$cssurl = \moodle_url::make_pluginfile_url(
|
||||
$record['contextid'],
|
||||
$record['component'],
|
||||
$record['filearea'],
|
||||
null,
|
||||
$record['filepath'],
|
||||
$record['filename']
|
||||
);
|
||||
|
||||
// Add the CSS file to the styles array, to load it from the H5P player.
|
||||
$styles[] = (object) [
|
||||
'path' => $cssurl->out(),
|
||||
'version' => '?ver='.$cssfile->get_timemodified(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -848,4 +848,115 @@ class file_storage_test extends \advanced_testcase {
|
||||
$this->assertFalse($this->h5p_fs_fs->file_exists($this->h5p_fs_context->id, file_storage::COMPONENT,
|
||||
file_storage::CONTENT_FILEAREA, $h5pcontentid, $filepath, $filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test H5P custom styles generation.
|
||||
*
|
||||
* @covers ::generate_custom_styles
|
||||
*/
|
||||
public function test_generate_custom_styles(): void {
|
||||
\set_config('h5pcustomcss', '.debug { color: #fab; }', 'core_h5p');
|
||||
$h5pfsrc = new \ReflectionClass(file_storage::class);
|
||||
$customcssfilename = $h5pfsrc->getConstant('CUSTOM_CSS_FILENAME');
|
||||
|
||||
// Test 'h5pcustomcss' with data.
|
||||
file_storage::generate_custom_styles();
|
||||
|
||||
$this->assertTrue($this->h5p_fs_fs->file_exists(
|
||||
\context_system::instance()->id,
|
||||
file_storage::COMPONENT,
|
||||
file_storage::CSS_FILEAREA,
|
||||
0,
|
||||
'/',
|
||||
$customcssfilename)
|
||||
);
|
||||
|
||||
$cssfile = $this->h5p_fs_fs->get_file(
|
||||
\context_system::instance()->id,
|
||||
file_storage::COMPONENT,
|
||||
file_storage::CSS_FILEAREA,
|
||||
0,
|
||||
'/',
|
||||
$customcssfilename
|
||||
);
|
||||
$this->assertInstanceOf('stored_file', $cssfile);
|
||||
|
||||
$csscontents = $cssfile->get_content();
|
||||
$this->assertEquals($csscontents, '.debug { color: #fab; }');
|
||||
|
||||
// Test 'h5pcustomcss' without data.
|
||||
\set_config('h5pcustomcss', '', 'core_h5p');
|
||||
file_storage::generate_custom_styles();
|
||||
$this->assertFalse($this->h5p_fs_fs->file_exists(
|
||||
\context_system::instance()->id,
|
||||
file_storage::COMPONENT,
|
||||
file_storage::CSS_FILEAREA,
|
||||
0,
|
||||
'/',
|
||||
$customcssfilename)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test H5P custom styles retrieval.
|
||||
*
|
||||
* @covers ::get_custom_styles
|
||||
*/
|
||||
public function test_get_custom_styles(): void {
|
||||
global $CFG;
|
||||
$css = '.debug { color: #fab; }';
|
||||
$cssurl = $CFG->wwwroot . '/pluginfile.php/1/core_h5p/css/custom_h5p.css';
|
||||
\set_config('h5pcustomcss', $css, 'core_h5p');
|
||||
$h5pfsrc = new \ReflectionClass(file_storage::class);
|
||||
$customcssfilename = $h5pfsrc->getConstant('CUSTOM_CSS_FILENAME');
|
||||
|
||||
// Normal operation without data.
|
||||
\set_config('h5pcustomcss', '', 'core_h5p');
|
||||
file_storage::generate_custom_styles();
|
||||
$style = file_storage::get_custom_styles();
|
||||
$this->assertNull($style);
|
||||
|
||||
// Normal operation with data.
|
||||
\set_config('h5pcustomcss', $css, 'core_h5p');
|
||||
file_storage::generate_custom_styles();
|
||||
$style = file_storage::get_custom_styles();
|
||||
|
||||
$this->assertNotEmpty($style);
|
||||
$this->assertEquals($style['cssurl']->out(), $cssurl);
|
||||
$this->assertEquals($style['cssversion'], md5($css));
|
||||
|
||||
// No CSS set when there is a file.
|
||||
\set_config('h5pcustomcss', '', 'core_h5p');
|
||||
try {
|
||||
$style = file_storage::get_custom_styles();
|
||||
$this->fail('moodle_exception for when there is no CSS and yet there is a file, was not thrown');
|
||||
} catch (\moodle_exception $me) {
|
||||
$this->assertEquals(
|
||||
'The H5P \'h5pcustomcss\' setting is empty and yet the custom CSS file \''.$customcssfilename.'\' exists.',
|
||||
$me->errorcode
|
||||
);
|
||||
}
|
||||
\set_config('h5pcustomcss', $css, 'core_h5p'); // Reset for next assertion.
|
||||
|
||||
// No CSS file when there is CSS.
|
||||
$cssfile = $this->h5p_fs_fs->get_file(
|
||||
\context_system::instance()->id,
|
||||
file_storage::COMPONENT,
|
||||
file_storage::CSS_FILEAREA,
|
||||
0,
|
||||
'/',
|
||||
$customcssfilename
|
||||
);
|
||||
$cssfile->delete();
|
||||
try {
|
||||
$style = file_storage::get_custom_styles();
|
||||
$this->fail('moodle_exception for when there is CSS and yet there is a file, was not thrown');
|
||||
} catch (\moodle_exception $me) {
|
||||
$this->assertEquals(
|
||||
'The H5P custom CSS file \''.$customcssfilename.
|
||||
'\' does not exist and yet there is CSS in the \'h5pcustomcss\' setting.',
|
||||
$me->errorcode
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,12 @@
|
||||
This files describes API changes in core libraries and APIs,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 4.4 ===
|
||||
* Added new methods, 'generate_custom_styles' and 'get_custom_styles' to 'core_h5p\file_storage' to generate then
|
||||
provide a CSS file to be used by the 'core_h5p\output\renderer\h5p_alter_styles' method, reference:
|
||||
(https://h5p.org/documentation/for-developers/visual-changes) when there is custom CSS in the 'core_h5p\h5pcustomcss'
|
||||
setting. This will then apply to the H5P content.
|
||||
|
||||
=== 4.3 ===
|
||||
* The `\core_h5p\file_storage::EDITOR_FILEAREA` constant has been removed, as all editor files are stored in user draft
|
||||
|
||||
|
@ -131,6 +131,8 @@ $string['h5pfilenotfound'] = 'H5P file not found';
|
||||
$string['h5pinvalidurl'] = 'Invalid H5P content URL.';
|
||||
$string['h5plibraryhandler'] = 'H5P framework handler';
|
||||
$string['h5plibraryhandler_help'] = 'The H5P framework used to display H5P content. The latest version is recommended.';
|
||||
$string['h5pcustomcss'] = 'Custom CSS';
|
||||
$string['h5pcustomcss_help'] = 'Custom CSS to apply to your H5P modules.';
|
||||
$string['h5pprivatefile'] = 'This H5P content can\'t be displayed because you don\'t have access to the .h5p file.';
|
||||
$string['h5pmanage'] = 'Manage H5P content types';
|
||||
$string['h5poverview'] = 'H5P overview';
|
||||
|
Loading…
x
Reference in New Issue
Block a user