diff --git a/app/controllers/wysiwyg.php b/app/controllers/wysiwyg.php index 88e640c406ba79d0c182a55e44d8bf2b6355d9c7..cb07e6fcd063d383a0860e552d01acae10cd8f48 100644 --- a/app/controllers/wysiwyg.php +++ b/app/controllers/wysiwyg.php @@ -120,333 +120,4 @@ class WysiwygController extends AuthenticatedController } $this->render_json($response); // send HTTP response to client } - - /** - * Store or retrieve settings. - * - * Settings are further subdivided into groups. For example: global, - * seminar- and user-specific settings (see below). - * - * HTTP GET - * returns a JSON object with current settings. - * - * HTTP PUT - * expects a JSON object with settings to store and returns - * updated settings as a JSON object. Some settings are read-only, - * others can only be set if the user has the necessary access level. - * - * Currently only the following basic features are supported: - * - * HTTP GET wysiwyg/settings/global - * Always returns: - * { - * "upload": { - * "permission": "autor", - * "folder": { - * "name": "Wysiwyg Uploads", - * "description": "Vom WYSIWYG Editor hochgeladene Dateien." - * } - * } - * } - * } - * - * HTTP GET wysiwyg/settings/users/current - * Always returns following setting for the authenticated user: - * { - * "disabled": false | true - * } - * - * HTTP PUT wysiwyg/settings/users/current - * Allows only to reset or set the disabled state with: - * { - * "disabled": false | true - * } - * - * Below is a specification of possible future extensions to this - * interface, that are based on current feature requests by users - * (mainly people from ELMO, ELAN and ECULT). - * - * wysiwyg/settings/global - * Common settings for all WYSIWYG editors throughout Stud.IP. - * wysiwyg/settings/seminars - * Settings of all seminars. - * Listed seminars depend on access level: - * root => full access to all seminars - * dozent, tutor of a seminar => full access to those seminars - * others => read-access to seminars they are a member of - * wysiwyg/settings/seminars/ID - * Settings of the seminar with the given ID. - * Access permissions: see above. - * wysiwyg/settings/seminars/ID/users - * Seminar's settings for all its users. - * Access permissions: see above. - * wysiwyg/settings/seminars/ID/users/ID - * Seminar's settings for a specific user in that seminar. - * Access permissions: see above. - * wysiwyg/settings/users - * Settings of all users. - * Listed users depend on access level: - * root => full access to all users - * not root => full access to own settings only - * wysiwyg/settings/users/ID - * Settings of the user with the given ID. - * Access permissions: see above. - * wysiwyg/settings/users/ID/seminars - * User's settings for all seminars the user is a member of. - * Access permissions: see above. - * wysiwyg/settings/users/ID/seminars/ID - * User's settings for the seminar with the given ID. - * Access permissions: see above. - * - * The difference of seminar's settings for a user and user's settings - * for a seminar: - * - * A seminar's teacher may want to set the upload directory for each user - * to a separate one, which should not be overwritable by a user, in - * order to make sure that users cannot see other users uploads (there - * are other ways to do this, but it's just an example). - * - * A user might want to have a specific upload directory in order to - * collaborate better with other users in the same seminar (e.g. when - * students form a study group). - * - * For example the ELMO module needs such settings. - * - * JSON scheme for access to wysiwyg/settings: - * { - * "global": { "SETTING": ..., ... }, - * "seminars": { - * "ID": { - * "users": { "ID": {...}, ... }, - * "SETTING": ..., - * ... - * }, - * "ID": {...}, - * ... - * }, - * "users": { - * "ID": { - * "seminars": { "ID": {...}, ... }, - * "SETTING": ..., - * ... - * }, - * "ID": {...}, - * ... - * } - * } - * - * When accessing a sub-resource that resource's branch of the JSON scheme - * will be returned. - */ - public function settings_action() - { - try { - if (!Request::isGet() && !Request::isPut()) { - throw new WysiwygHttpExceptionMethodNotAllowed( - _('Nur die HTTP-Methoden GET und PUT sind erlaubt.') - ); - } - - $arguments = func_get_args(); - $settingsGroup = array_shift($arguments); - - if (Request::isPut()) { - $this->setSettings($settingsGroup, $arguments); - } - $this->render_json($this->objectToArray( - $this->getSettings($settingsGroup, $arguments) - )); - } catch (WysiwygHttpException $e) { - $this->set_status($e->getCode()); - $this->set_content_type('text/plain; charset=utf-8'); - $this->render_text($e->getMessage()); - } - } - - public function a11yhelp_action() - { - // nothing to do - PageLayout::setTitle(_('Hilfe zur Bedienung des Editors')); - } - - /** - * Set WYSIWYG settings for a specific group. - * - * Dummy implementation: Currently only accepts setting the - * disabled flag for wysiwyg/settings/users/current. - * - * The HTTP request's body must contain a JSON document of the form: - * { - * "disabled": true | false - * } - * - * If the JSON contains other additional values they will be ignored. - * - * @param string $group Must be set to 'users'. - * @param array $arguments Must contain exactly one entry: 'current'. - */ - private function setSettings($group, $arguments) { - $user = array_shift($arguments); - if ($group !== 'users' || $user !== 'current') { - throw new WysiwygHttpExceptionForbidden( - _('Zugriff verweigert') - ); - } - - $subgroup = array_shift($arguments); - if (($subgroup !== null && $subgroup !== '') || count($arguments) > 0) { - throw new WysiwygHttpExceptionNotFound( - _('Die Benutzereinstellungen enthalten keine Untergruppen.') - ); - } - - $data = json_decode(file_get_contents('php://input')); - if (isset($data->disabled)) { - $config = $GLOBALS['user']->cfg; - //$config->WYSIWYG_DISABLED = (boolean)$data->disabled; - $config->store( - 'WYSIWYG_DISABLED', - (boolean)$data->disabled - ); - } else { - throw new WysiwygHttpExceptionBadRequest( - _('Die Anfrage enthält ungültige Werte.') - ); - } - // all unknown parameters are ignored - } - - /** - * Return WYSIWYG settings for a specific group. - * - * @param $group string The requested settings group: 'user', 'seminar', - * 'global' or 'all'. If the group is set to 'all' then all levels will be - * returned. If the group is unknown an error will be thrown. - * - * @return object Settings for the requested group. - */ - private function getSettings($group, $arguments) - { - switch ($group) { - case null: return $this->getAllSettings(); - case 'global': return $this->getGlobalSettings($arguments); - case 'users': return $this->getUserSettings($arguments); - } - throw new WysiwygHttpExceptionNotFound( - _('Die angeforderte Gruppe von Einstellungen existiert nicht.') - ); - } - - /** - * Return all WYSIWYG settings. - * - * Returns an object with properties named after settings groups, - * containing the respective group's settings. For example: - * - * { - * "global": {...} - * "seminars": {...}, - * "users": {...}, - * } - * - * @return object All settings. - */ - private function getAllSettings() - { - $settings = new stdClass; - $settings->global = $this->getGlobalSettings(); - $settings->users = $this->getUserSettings(); - return $settings; - } - - /** - * Return global WYSIWYG settings. - * - * @return object Global settings. - */ - private function getGlobalSettings($arguments = []) - { - $subgroup = array_shift($arguments); - if (($subgroup !== null && $subgroup !== '') || count($arguments) > 0) { - throw new WysiwygHttpExceptionNotFound(_('Die globalen Einstellungen enthalten keine Untergruppen.')); - } - $settings = new stdClass; - $settings->disabled = !\Config::get()->WYSIWYG; - $settings->upload = new stdClass; - $settings->upload->permission = self::UPLOAD_PERMISSION; - $settings->upload->folder = new stdClass; - $settings->upload->folder->name = self::FOLDER_NAME; - $settings->upload->folder->description = self::FOLDER_DESCRIPTION; - return $settings; - } - - /** - * Return current user's WYSIWYG settings. - * - * @return object User's settings. - */ - private function getUserSettings($arguments = []) - { - // NOTE simulate a list of users containing only the current - // user until this is implemented correctly - $settings = new stdClass; - $settings->current = $this->getCurrentUserSettings(); - - $userId = array_shift($arguments); - if ($userId === null || $userId === '' && count($arguments) === 0) { - return $settings; - } - - if ($userId === 'current') { - $subgroup = array_shift($arguments); - if (($subgroup !== null && $subgroup !== '') || count($arguments) > 0) { - throw new WysiwygHttpExceptionNotFound( - _('Die Benutzereinstellungen enthalten keine Untergruppen.') - ); - } - return $settings->current; - } - - throw new WysiwygHttpExceptionForbidden( - _('Zugriff verweigert.') - ); - } - - /** - * Return current user's WYSIWYG settings. - * - * @return object User's settings. - */ - public function getCurrentUserSettings() - { - $config = $GLOBALS['user']->cfg; - $settings = new stdClass; - $settings->disabled = (boolean)$config->WYSIWYG_DISABLED; - return $settings; - } - - /** - * Recursively convert objects to associative arrays. - * - * Workaround for broken StudipController::render_json. - * - * If the data is neither object nor array then it will be - * returned unchanged. - * - * @param mixed $data Data to convert. - * - * @return mixed Converted data. - */ - private function objectToArray($data) - { - if (gettype($data) === 'object') { - $data = (array)$data; - } - if (gettype($data) === 'array') { - foreach ($data as $key => $value) { - $data[$key] = $this->objectToArray($value); - } - } - return $data; - } } diff --git a/resources/assets/javascripts/chunks/wysiwyg.js b/resources/assets/javascripts/chunks/wysiwyg.js index bc3f172e359239393363293012ca721a5b25b718..0ac0e4d25d488a006422c127f9906f051bf45f69 100644 --- a/resources/assets/javascripts/chunks/wysiwyg.js +++ b/resources/assets/javascripts/chunks/wysiwyg.js @@ -53,7 +53,6 @@ import Mathematics from 'ckeditor5-math/src/math'; import StudipA11YDialog, { updateVoiceLabel } from '../cke/studip-a11y-dialog/a11y-dialog.js'; import StudipBlockQuote from '../cke/studip-quote/StudipBlockQuote.js'; import StudipUpload from '../cke/StudipUpload.js'; -import { StudipSettings } from '../cke/StudipSettings.js'; import StudipWikiLink from '../cke/wiki-link/wiki-link.js'; import SpecialCharactersEmojiFood from '../cke/special_characters/SpecialCharactersEmojiFood.js'; import SpecialCharactersEmojiNature from '../cke/special_characters/SpecialCharactersEmojiNature.js'; @@ -124,7 +123,6 @@ ClassicEditor.builtinPlugins = [ Underline, FileRepository, StudipA11YDialog, - StudipSettings, StudipWikiLink, ]; @@ -198,7 +196,6 @@ ClassicEditor.defaultConfig = { 'outdent', 'indent', '|', - 'studipSettings', 'open-a11y-dialog', ], shouldNotGroupWhenFull: true, diff --git a/resources/assets/javascripts/cke/StudipSettings.js b/resources/assets/javascripts/cke/StudipSettings.js deleted file mode 100644 index f498a1e7f5495834625e5e92f067c9aab998dfe4..0000000000000000000000000000000000000000 --- a/resources/assets/javascripts/cke/StudipSettings.js +++ /dev/null @@ -1,138 +0,0 @@ -import Plugin from '@ckeditor/ckeditor5-core/src/plugin'; -import { createDropdown, ButtonView, View } from 'ckeditor5/src/ui'; -import { $gettext } from '../lib/gettext.js'; - -const settings = { - url: STUDIP.URLHelper.getURL('dispatch.php/wysiwyg/settings/users/current'), - save: function (data) { - return $.ajax({ - url: this.url, - type: 'PUT', - contentType: 'application/json', - data: JSON.stringify(data), - }); - }, -}; - -const gearsIcon = - '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 54 54"><path d="M50.58,22.76l-.95-.4a3.12,3.12,0,0,1,0-5.8l.95-.41h0a.69.69,0,0,0,.37-.89h0l-.7-1.69-1.13-2.75A.71.71,0,0,0,49,10.6a.67.67,0,0,0-.74-.15h0l-.95.39a3.05,3.05,0,0,1-3.43-.65,3.15,3.15,0,0,1-.65-3.45l.4-1a.69.69,0,0,0-.36-.89L38.82,3.05a.67.67,0,0,0-.88.37l-.41,1a3.08,3.08,0,0,1-5.75,0l-.41-1a.68.68,0,0,0-.89-.37L26.07,4.9a.68.68,0,0,0-.37.89l.39,1A3.1,3.1,0,0,1,22,10.85h0l-.95-.4a.68.68,0,0,0-.89.37l-1.83,4.44a.69.69,0,0,0,.37.89l1,.41a3.12,3.12,0,0,1,0,5.79l-.95.42a.69.69,0,0,0-.38.89l1.83,4.44a.7.7,0,0,0,.89.37l1-.4a3.05,3.05,0,0,1,3.42.65,3.12,3.12,0,0,1,.64,3.45h0l-.4,1a.68.68,0,0,0,.37.89l4.41,1.84a.68.68,0,0,0,.89-.36l.4-1a3.08,3.08,0,0,1,5.76,0l.4,1a.68.68,0,0,0,.89.36L43.23,34a.68.68,0,0,0,.37-.89l-.4-1h0a3.15,3.15,0,0,1,.65-3.45,3.06,3.06,0,0,1,3.42-.65h0l.95.4a.69.69,0,0,0,.89-.37L51,23.65A.69.69,0,0,0,50.58,22.76ZM36.89,24.9a5.84,5.84,0,0,1-7.65-3.19A5.91,5.91,0,0,1,32.41,14a5.84,5.84,0,0,1,7.65,3.18A5.91,5.91,0,0,1,36.89,24.9Z"/><path d="M25.82,37.11H25.1a2.14,2.14,0,0,1-2-1.33,2.21,2.21,0,0,1,.5-2.41l.51-.51a.5.5,0,0,0,0-.68l-2.36-2.38a.46.46,0,0,0-.67,0l-.52.52a2.16,2.16,0,0,1-2.39.5,2.19,2.19,0,0,1-1.33-2.06V28a.47.47,0,0,0-.47-.48H13a.48.48,0,0,0-.48.47v.73a2.16,2.16,0,0,1-3.72,1.56h0l-.51-.52a.46.46,0,0,0-.67,0L5.24,32.17a.48.48,0,0,0,0,.68l.5.51a2.19,2.19,0,0,1,.5,2.41,2.14,2.14,0,0,1-2,1.33H3.48a.48.48,0,0,0-.48.48V41a.48.48,0,0,0,.48.48H4.2a2.15,2.15,0,0,1,2,1.34,2.17,2.17,0,0,1-.5,2.4h0l-.51.52a.48.48,0,0,0,0,.67l2.36,2.38a.46.46,0,0,0,.67,0l.52-.51a2.16,2.16,0,0,1,2.39-.5A2.18,2.18,0,0,1,12.5,49.8v.72h0A.49.49,0,0,0,13,51h3.34a.47.47,0,0,0,.48-.48h0V49.8a2.18,2.18,0,0,1,1.33-2.06,2.16,2.16,0,0,1,2.39.5l.51.52a.46.46,0,0,0,.67,0l2.37-2.38a.48.48,0,0,0,0-.67l-.51-.53a2.19,2.19,0,0,1-.5-2.4,2.15,2.15,0,0,1,2-1.34h.72A.48.48,0,0,0,26.3,41V37.59A.47.47,0,0,0,25.82,37.11ZM14.65,43.39a4.12,4.12,0,1,1,4.09-4.12A4.1,4.1,0,0,1,14.65,43.39Z"/></svg>'; - -export class StudipSettings extends Plugin { - init() { - this.editor.ui.componentFactory.add('studipSettings', (locale) => { - const dropdownView = createDropdown(locale); - - dropdownView.buttonView.set({ - label: $gettext('Stud.IP Einstellungen'), - icon: gearsIcon, - tooltip: true, - }); - - dropdownView.render(); - - const studipSettingsView = new StudipSettingsView(locale); - dropdownView.panelView.children.add(studipSettingsView); - studipSettingsView.on('wysiwyg:change', (eventInfo, disabled) => { - this._save(studipSettingsView, disabled).then(() => (dropdownView.isOpen = false)); - }); - - return dropdownView; - }); - } - - _save(view, disabled) { - view.functional = false; - - return settings - .save({ disabled }) - .fail(function (xhr) { - console.error("couldn't save changes"); - }) - .always(() => { - view.functional = true; - }); - } -} - -class StudipSettingsView extends View { - constructor(locale) { - super(locale); - - const bind = this.bindTemplate; - this.set({ - checked: false, - functional: true, - }); - - const button = createButton(); - this.button = button; - button.on('execute', () => { - this.fire('wysiwyg:change', this.checked); - }); - - this.on('checking', (...args) => { - this.checked = !this.checked; - }); - - this.setTemplate({ - tag: 'form', - attributes: { - class: ['default ck-studip-settings-form'], - tabindex: '-1', - style: 'max-width: 20em; padding: 1em;', - }, - children: [createCheckbox(), createHelpText(), button], - }); - - function createCheckbox() { - return { - tag: 'label', - children: [ - { - tag: 'input', - attributes: { - id: 'disable', - type: 'checkbox', - checked: bind.to('checked'), - style: 'margin-right: 0.5em' - }, - on: { - change: bind.to('checking'), - }, - }, - { - text: $gettext('WYSIWYG Editor ausschalten'), - }, - ], - }; - } - - function createHelpText() { - return { - tag: 'p', - attributes: { - style: 'white-space: normal; font-size: 1em; line-height: 1.5em; margin-bottom: 1em;', - }, - children: [ - { - text: $gettext( - 'Mit dieser Einstellung können Sie den WYSIWYG Editor ausschalten. Dadurch müssen Sie gegebenenfalls Texte in HTML schreiben. Der Editor wird erst vollständig entfernt, wenn die Seite neu geladen wird.' - ), - }, - ], - }; - } - - function createButton() { - const button = new ButtonView(locale); - - button.set({ - label: $gettext('Speichern'), - withText: true, - isEnabled: bind.to('functional'), - }); - - return button; - } - } -}