Skip to content
Snippets Groups Projects
Commit b83b7b36 authored by Marcus Eibrink-Lunzenauer's avatar Marcus Eibrink-Lunzenauer Committed by Jan-Hendrik Willms
Browse files

Remove WYSIWYG toolbar icon to disable WYSIWYG, fixes #1495

Closes #1495

Merge request studip/studip!946
parent 5a89d3f5
No related branches found
No related tags found
No related merge requests found
......@@ -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;
}
}
......@@ -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,
......
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;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment