Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 183 additions & 0 deletions admin/styles.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?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/>.

/**
* Admin page for managing eXeLearning styles exposed to the embedded editor.
*
* @package mod_exeweb
* @copyright 2025 eXeLearning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

require('../../../config.php');
require_once($CFG->libdir . '/adminlib.php');

use mod_exeweb\local\styles_service;

admin_externalpage_setup('mod_exeweb_styles');

$context = \context_system::instance();
require_capability('moodle/site:config', $context);
require_capability('mod/exeweb:manageembeddededitor', $context);

$action = optional_param('action', '', PARAM_ALPHA);
$returnurl = new moodle_url('/mod/exeweb/admin/styles.php');
$settingsurl = new moodle_url('/admin/settings.php', ['section' => 'modsettingexeweb']);

// --------------------------------------------------------------------
// Toggle/delete actions use GET + sesskey (simple URL handlers).
// Upload happens inline in the plugin settings page.
// --------------------------------------------------------------------
if ($action !== '') {
require_sesskey();
switch ($action) {
case 'toggleuploaded':
$slug = required_param('slug', PARAM_TEXT);
$enabled = (bool) required_param('enabled', PARAM_INT);
styles_service::set_uploaded_enabled($slug, $enabled);
redirect($returnurl);
break;

case 'togglebuiltin':
$id = required_param('id', PARAM_TEXT);
$enabled = (bool) required_param('enabled', PARAM_INT);
styles_service::set_builtin_enabled($id, $enabled);
redirect($returnurl);
break;

case 'delete':
$slug = required_param('slug', PARAM_TEXT);
styles_service::delete_uploaded($slug);
redirect($returnurl,
get_string('stylesdelete_success', 'mod_exeweb'),
null,
\core\output\notification::NOTIFY_SUCCESS
);
break;
}
}

// --------------------------------------------------------------------
// Render.
// --------------------------------------------------------------------
echo $OUTPUT->header();
echo $OUTPUT->heading(get_string('stylesmanager', 'mod_exeweb'));

if (get_config('exeweb', 'editormode') !== 'embedded') {
echo $OUTPUT->notification(get_string('stylesonlywhenembedded', 'mod_exeweb'),
\core\output\notification::NOTIFY_WARNING);
}

echo html_writer::tag('p', get_string('stylesmanager_intro', 'mod_exeweb'));

// Point admins at the inline uploader on the plugin settings page —
// the filemanager there is the single entry point for uploading styles.
echo html_writer::tag('p',
html_writer::link(
$settingsurl,
get_string('stylesupload_goto_settings', 'mod_exeweb'),
['class' => 'btn btn-secondary']
)
);

// Uploaded styles table.
$uploaded = styles_service::list_uploaded_styles();
echo $OUTPUT->heading(get_string('stylesuploaded', 'mod_exeweb'), 3);
if (empty($uploaded)) {
echo html_writer::tag('p', get_string('stylesuploaded_empty', 'mod_exeweb'), ['class' => 'text-muted']);
} else {
$table = new html_table();
$table->head = [
get_string('stylestable_title', 'mod_exeweb'),
get_string('stylestable_id', 'mod_exeweb'),
get_string('stylestable_version', 'mod_exeweb'),
get_string('stylestable_installed', 'mod_exeweb'),
get_string('stylestable_enabled', 'mod_exeweb'),
get_string('stylestable_actions', 'mod_exeweb'),
];
foreach ($uploaded as $style) {
$toggleurl = new moodle_url('/mod/exeweb/admin/styles.php', [
'action' => 'toggleuploaded',
'slug' => $style['id'],
'enabled' => empty($style['enabled']) ? 1 : 0,
'sesskey' => sesskey(),
]);
$togglelabel = empty($style['enabled'])
? get_string('stylesenable', 'mod_exeweb')
: get_string('stylesdisable', 'mod_exeweb');
$deleteurl = new moodle_url('/mod/exeweb/admin/styles.php', [
'action' => 'delete',
'slug' => $style['id'],
'sesskey' => sesskey(),
]);
$table->data[] = [
s($style['title'] ?? $style['id']),
html_writer::tag('code', s($style['id'])),
s($style['version'] ?? ''),
s($style['installed_at'] ?? ''),
html_writer::link($toggleurl, $togglelabel, ['class' => 'btn btn-secondary btn-sm']),
html_writer::link(
$deleteurl,
get_string('stylesdelete', 'mod_exeweb'),
[
'class' => 'btn btn-danger btn-sm',
'onclick' => "return confirm('"
. addslashes_js(get_string('stylesdelete_confirm', 'mod_exeweb'))
. "');",
]
),
];
}
echo html_writer::table($table);
}

// Built-in styles table.
$builtins = styles_service::list_builtin_themes();
echo $OUTPUT->heading(get_string('stylesbuiltin', 'mod_exeweb'), 3);
if (empty($builtins)) {
echo html_writer::tag('p', get_string('stylesbuiltin_empty', 'mod_exeweb'), ['class' => 'text-muted']);
} else {
$registry = styles_service::get_registry();
$disabledlist = $registry['disabled_builtins'];
$table = new html_table();
$table->head = [
get_string('stylestable_title', 'mod_exeweb'),
get_string('stylestable_id', 'mod_exeweb'),
get_string('stylestable_version', 'mod_exeweb'),
get_string('stylestable_enabled', 'mod_exeweb'),
];
foreach ($builtins as $style) {
$isdisabled = in_array($style['id'], $disabledlist, true);
$toggleurl = new moodle_url('/mod/exeweb/admin/styles.php', [
'action' => 'togglebuiltin',
'id' => $style['id'],
'enabled' => $isdisabled ? 1 : 0,
'sesskey' => sesskey(),
]);
$togglelabel = $isdisabled
? get_string('stylesenable', 'mod_exeweb')
: get_string('stylesdisable', 'mod_exeweb');
$table->data[] = [
s($style['title']),
html_writer::tag('code', s($style['id'])),
s($style['version']),
html_writer::link($toggleurl, $togglelabel, ['class' => 'btn btn-secondary btn-sm']),
];
}
echo html_writer::table($table);
}

echo $OUTPUT->footer();
124 changes: 124 additions & 0 deletions classes/admin/admin_setting_stylesbuiltins.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?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/>.

/**
* Admin setting that renders built-in styles as a list of checkboxes.
*
* @package mod_exeweb
* @copyright 2025 eXeLearning
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace mod_exeweb\admin;

defined('MOODLE_INTERNAL') || die();

use mod_exeweb\local\styles_service;

/**
* Per-built-in style enable/disable checkboxes, inline in the plugin
* settings page. Source of truth stays in the styles registry
* (`config_plugin(exeweb).styles_registry`), so the widget reads state
* from it at render time and writes back through the service.
*/
class admin_setting_stylesbuiltins extends \admin_setting {

/**
* @param string $name Setting key (used for the HTML input name).
*/
public function __construct(string $name) {
parent::__construct(
$name,
get_string('stylesbuiltin', 'mod_exeweb'),
get_string('stylesbuiltin_hint', 'mod_exeweb'),
[]
);
}

/**
* This setting does not live in $CFG; the registry owns the state.
*
* @return array
*/
public function get_setting() {
return [];
}

/**
* @return array
*/
public function get_defaultsetting() {
return [];
}

/**
* Persist checkbox state back into the registry.
*
* @param array $data Posted value map (slug => '1' when checked).
* @return string Empty on success.
*/
public function write_setting($data) {
if (!is_array($data)) {
$data = [];
}
foreach (styles_service::list_builtin_themes() as $theme) {
$id = $theme['id'];
$enabled = !empty($data[$id]);
styles_service::set_builtin_enabled($id, $enabled);
}
return '';
}

/**
* Render the checkbox list.
*
* @param mixed $data Unused — current state comes from the registry.
* @param string $query Current admin search query.
* @return string
*/
public function output_html($data, $query = '') {
$registry = styles_service::get_registry();
$disabled = $registry['disabled_builtins'];
$builtins = styles_service::list_builtin_themes();
if (empty($builtins)) {
$html = \html_writer::tag('p',
get_string('stylesbuiltin_empty', 'mod_exeweb'),
['class' => 'text-muted']
);
return format_admin_setting($this, $this->visiblename, $html, $this->description);
}
$rows = '';
foreach ($builtins as $theme) {
$id = $theme['id'];
$checked = in_array($id, $disabled, true) ? '' : 'checked';
$inputname = $this->get_full_name() . '[' . $id . ']';
$version = !empty($theme['version'])
? ' <span class="text-muted small">v' . s($theme['version']) . '</span>'
: '';
$rows .= '<li style="margin-bottom:.25em;">'
. '<label>'
. '<input type="checkbox" name="' . s($inputname) . '" value="1" ' . $checked . '> '
. s($theme['title'])
. $version
. ' <code class="text-muted small">' . s($id) . '</code>'
. '</label>'
. '</li>';
}
$html = '<ul class="mod_exeweb-styles-builtins list-unstyled" '
. 'style="list-style:none;padding:0;margin:0;">' . $rows . '</ul>';
return format_admin_setting($this, $this->visiblename, $html, $this->description);
}
}
Loading
Loading