|
| 1 | +<?php |
| 2 | +// This file is part of Moodle - http://moodle.org/ |
| 3 | +// |
| 4 | +// Moodle is free software: you can redistribute it and/or modify |
| 5 | +// it under the terms of the GNU General Public License as published by |
| 6 | +// the Free Software Foundation, either version 3 of the License, or |
| 7 | +// (at your option) any later version. |
| 8 | +// |
| 9 | +// Moodle is distributed in the hope that it will be useful, |
| 10 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +// GNU General Public License for more details. |
| 13 | +// |
| 14 | +// You should have received a copy of the GNU General Public License |
| 15 | +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + |
| 17 | +/** |
| 18 | + * Admin page for managing eXeLearning styles exposed to the embedded editor. |
| 19 | + * |
| 20 | + * @package mod_exeweb |
| 21 | + * @copyright 2025 eXeLearning |
| 22 | + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later |
| 23 | + */ |
| 24 | + |
| 25 | +require('../../../config.php'); |
| 26 | +require_once($CFG->libdir . '/adminlib.php'); |
| 27 | + |
| 28 | +use mod_exeweb\local\styles_service; |
| 29 | + |
| 30 | +admin_externalpage_setup('mod_exeweb_styles'); |
| 31 | + |
| 32 | +$context = \context_system::instance(); |
| 33 | +require_capability('moodle/site:config', $context); |
| 34 | +require_capability('mod/exeweb:manageembeddededitor', $context); |
| 35 | + |
| 36 | +$action = optional_param('action', '', PARAM_ALPHA); |
| 37 | +$returnurl = new moodle_url('/mod/exeweb/admin/styles.php'); |
| 38 | + |
| 39 | +// -------------------------------------------------------------------- |
| 40 | +// Actions. |
| 41 | +// -------------------------------------------------------------------- |
| 42 | +if ($action !== '') { |
| 43 | + require_sesskey(); |
| 44 | + switch ($action) { |
| 45 | + case 'upload': |
| 46 | + $uploaded = $_FILES['style_zip'] ?? null; |
| 47 | + if (!is_array($uploaded) || (int) $uploaded['error'] !== UPLOAD_ERR_OK) { |
| 48 | + redirect($returnurl, get_string('stylesupload_failed', 'mod_exeweb'), null, |
| 49 | + \core\output\notification::NOTIFY_ERROR); |
| 50 | + } |
| 51 | + if (!is_uploaded_file($uploaded['tmp_name'])) { |
| 52 | + redirect($returnurl, get_string('stylesupload_failed', 'mod_exeweb'), null, |
| 53 | + \core\output\notification::NOTIFY_ERROR); |
| 54 | + } |
| 55 | + try { |
| 56 | + $entry = styles_service::install_from_zip( |
| 57 | + $uploaded['tmp_name'], |
| 58 | + clean_param($uploaded['name'] ?? '', PARAM_FILE) |
| 59 | + ); |
| 60 | + redirect($returnurl, |
| 61 | + get_string('stylesupload_success', 'mod_exeweb', s($entry['title'])), |
| 62 | + null, |
| 63 | + \core\output\notification::NOTIFY_SUCCESS |
| 64 | + ); |
| 65 | + } catch (\moodle_exception $e) { |
| 66 | + redirect($returnurl, $e->getMessage(), null, |
| 67 | + \core\output\notification::NOTIFY_ERROR); |
| 68 | + } |
| 69 | + break; |
| 70 | + |
| 71 | + case 'toggleuploaded': |
| 72 | + $slug = required_param('slug', PARAM_TEXT); |
| 73 | + $enabled = (bool) required_param('enabled', PARAM_INT); |
| 74 | + styles_service::set_uploaded_enabled($slug, $enabled); |
| 75 | + redirect($returnurl); |
| 76 | + break; |
| 77 | + |
| 78 | + case 'togglebuiltin': |
| 79 | + $id = required_param('id', PARAM_TEXT); |
| 80 | + $enabled = (bool) required_param('enabled', PARAM_INT); |
| 81 | + styles_service::set_builtin_enabled($id, $enabled); |
| 82 | + redirect($returnurl); |
| 83 | + break; |
| 84 | + |
| 85 | + case 'delete': |
| 86 | + $slug = required_param('slug', PARAM_TEXT); |
| 87 | + styles_service::delete_uploaded($slug); |
| 88 | + redirect($returnurl, |
| 89 | + get_string('stylesdelete_success', 'mod_exeweb'), |
| 90 | + null, |
| 91 | + \core\output\notification::NOTIFY_SUCCESS |
| 92 | + ); |
| 93 | + break; |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +// -------------------------------------------------------------------- |
| 98 | +// Render. |
| 99 | +// -------------------------------------------------------------------- |
| 100 | +echo $OUTPUT->header(); |
| 101 | +echo $OUTPUT->heading(get_string('stylesmanager', 'mod_exeweb')); |
| 102 | + |
| 103 | +if (get_config('exeweb', 'editormode') !== 'embedded') { |
| 104 | + echo $OUTPUT->notification(get_string('stylesonlywhenembedded', 'mod_exeweb'), |
| 105 | + \core\output\notification::NOTIFY_WARNING); |
| 106 | +} |
| 107 | + |
| 108 | +echo html_writer::tag('p', get_string('stylesmanager_intro', 'mod_exeweb')); |
| 109 | + |
| 110 | +// Upload form. |
| 111 | +echo html_writer::start_tag('form', [ |
| 112 | + 'method' => 'post', |
| 113 | + 'action' => $returnurl->out(false), |
| 114 | + 'enctype' => 'multipart/form-data', |
| 115 | + 'class' => 'mt-3 mb-4', |
| 116 | +]); |
| 117 | +echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'action', 'value' => 'upload']); |
| 118 | +echo html_writer::empty_tag('input', ['type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()]); |
| 119 | +echo html_writer::tag('label', |
| 120 | + get_string('stylesupload_label', 'mod_exeweb'), |
| 121 | + ['for' => 'style_zip', 'class' => 'd-block mb-1'] |
| 122 | +); |
| 123 | +echo html_writer::empty_tag('input', [ |
| 124 | + 'type' => 'file', |
| 125 | + 'id' => 'style_zip', |
| 126 | + 'name' => 'style_zip', |
| 127 | + 'accept' => '.zip,application/zip,application/x-zip-compressed', |
| 128 | + 'required' => 'required', |
| 129 | +]); |
| 130 | +echo ' '; |
| 131 | +echo html_writer::empty_tag('input', [ |
| 132 | + 'type' => 'submit', |
| 133 | + 'class' => 'btn btn-primary', |
| 134 | + 'value' => get_string('stylesupload_submit', 'mod_exeweb'), |
| 135 | +]); |
| 136 | +echo html_writer::tag('p', |
| 137 | + get_string('stylesupload_hint', 'mod_exeweb', |
| 138 | + display_size(styles_service::get_max_zip_size())), |
| 139 | + ['class' => 'text-muted small mt-2'] |
| 140 | +); |
| 141 | +echo html_writer::end_tag('form'); |
| 142 | + |
| 143 | +// Uploaded styles table. |
| 144 | +$uploaded = styles_service::list_uploaded_styles(); |
| 145 | +echo $OUTPUT->heading(get_string('stylesuploaded', 'mod_exeweb'), 3); |
| 146 | +if (empty($uploaded)) { |
| 147 | + echo html_writer::tag('p', get_string('stylesuploaded_empty', 'mod_exeweb'), ['class' => 'text-muted']); |
| 148 | +} else { |
| 149 | + $table = new html_table(); |
| 150 | + $table->head = [ |
| 151 | + get_string('stylestable_title', 'mod_exeweb'), |
| 152 | + get_string('stylestable_id', 'mod_exeweb'), |
| 153 | + get_string('stylestable_version', 'mod_exeweb'), |
| 154 | + get_string('stylestable_installed', 'mod_exeweb'), |
| 155 | + get_string('stylestable_enabled', 'mod_exeweb'), |
| 156 | + get_string('stylestable_actions', 'mod_exeweb'), |
| 157 | + ]; |
| 158 | + foreach ($uploaded as $style) { |
| 159 | + $toggleurl = new moodle_url('/mod/exeweb/admin/styles.php', [ |
| 160 | + 'action' => 'toggleuploaded', |
| 161 | + 'slug' => $style['id'], |
| 162 | + 'enabled' => empty($style['enabled']) ? 1 : 0, |
| 163 | + 'sesskey' => sesskey(), |
| 164 | + ]); |
| 165 | + $togglelabel = empty($style['enabled']) |
| 166 | + ? get_string('stylesenable', 'mod_exeweb') |
| 167 | + : get_string('stylesdisable', 'mod_exeweb'); |
| 168 | + $deleteurl = new moodle_url('/mod/exeweb/admin/styles.php', [ |
| 169 | + 'action' => 'delete', |
| 170 | + 'slug' => $style['id'], |
| 171 | + 'sesskey' => sesskey(), |
| 172 | + ]); |
| 173 | + $table->data[] = [ |
| 174 | + s($style['title'] ?? $style['id']), |
| 175 | + html_writer::tag('code', s($style['id'])), |
| 176 | + s($style['version'] ?? ''), |
| 177 | + s($style['installed_at'] ?? ''), |
| 178 | + html_writer::link($toggleurl, $togglelabel, ['class' => 'btn btn-secondary btn-sm']), |
| 179 | + html_writer::link( |
| 180 | + $deleteurl, |
| 181 | + get_string('stylesdelete', 'mod_exeweb'), |
| 182 | + [ |
| 183 | + 'class' => 'btn btn-danger btn-sm', |
| 184 | + 'onclick' => "return confirm('" |
| 185 | + . addslashes_js(get_string('stylesdelete_confirm', 'mod_exeweb')) |
| 186 | + . "');", |
| 187 | + ] |
| 188 | + ), |
| 189 | + ]; |
| 190 | + } |
| 191 | + echo html_writer::table($table); |
| 192 | +} |
| 193 | + |
| 194 | +// Built-in styles table. |
| 195 | +$builtins = styles_service::list_builtin_themes(); |
| 196 | +echo $OUTPUT->heading(get_string('stylesbuiltin', 'mod_exeweb'), 3); |
| 197 | +if (empty($builtins)) { |
| 198 | + echo html_writer::tag('p', get_string('stylesbuiltin_empty', 'mod_exeweb'), ['class' => 'text-muted']); |
| 199 | +} else { |
| 200 | + $registry = styles_service::get_registry(); |
| 201 | + $disabledlist = $registry['disabled_builtins']; |
| 202 | + $table = new html_table(); |
| 203 | + $table->head = [ |
| 204 | + get_string('stylestable_title', 'mod_exeweb'), |
| 205 | + get_string('stylestable_id', 'mod_exeweb'), |
| 206 | + get_string('stylestable_version', 'mod_exeweb'), |
| 207 | + get_string('stylestable_enabled', 'mod_exeweb'), |
| 208 | + ]; |
| 209 | + foreach ($builtins as $style) { |
| 210 | + $isdisabled = in_array($style['id'], $disabledlist, true); |
| 211 | + $toggleurl = new moodle_url('/mod/exeweb/admin/styles.php', [ |
| 212 | + 'action' => 'togglebuiltin', |
| 213 | + 'id' => $style['id'], |
| 214 | + 'enabled' => $isdisabled ? 1 : 0, |
| 215 | + 'sesskey' => sesskey(), |
| 216 | + ]); |
| 217 | + $togglelabel = $isdisabled |
| 218 | + ? get_string('stylesenable', 'mod_exeweb') |
| 219 | + : get_string('stylesdisable', 'mod_exeweb'); |
| 220 | + $table->data[] = [ |
| 221 | + s($style['title']), |
| 222 | + html_writer::tag('code', s($style['id'])), |
| 223 | + s($style['version']), |
| 224 | + html_writer::link($toggleurl, $togglelabel, ['class' => 'btn btn-secondary btn-sm']), |
| 225 | + ]; |
| 226 | + } |
| 227 | + echo html_writer::table($table); |
| 228 | +} |
| 229 | + |
| 230 | +echo $OUTPUT->footer(); |
0 commit comments