From e8cbbfa43b3403327319b43482019629a0732497 Mon Sep 17 00:00:00 2001 From: Elmar Ludwig <elmar.ludwig@uni-osnabrueck.de> Date: Tue, 14 Jun 2022 16:59:42 +0000 Subject: [PATCH] drop studip-settings editor toolbar icon, fixes #1165 Closes #1165 Merge request studip/studip!694 --- app/controllers/wysiwyg.php | 356 ------------------ .../studip-settings/dialogs/settings.js | 124 ------ .../studip-settings/icons/hidpi/settings.png | Bin 897 -> 0 bytes .../studip-settings/icons/settings.png | Bin 215 -> 0 bytes .../plugins/studip-settings/lang/de.js | 18 - .../plugins/studip-settings/lang/en.js | 17 - .../plugins/studip-settings/plugin.js | 20 - resources/assets/javascripts/lib/toolbar.js | 35 -- resources/assets/javascripts/lib/wysiwyg.js | 5 +- 9 files changed, 2 insertions(+), 573 deletions(-) delete mode 100644 public/assets/javascripts/ckeditor/plugins/studip-settings/dialogs/settings.js delete mode 100644 public/assets/javascripts/ckeditor/plugins/studip-settings/icons/hidpi/settings.png delete mode 100644 public/assets/javascripts/ckeditor/plugins/studip-settings/icons/settings.png delete mode 100644 public/assets/javascripts/ckeditor/plugins/studip-settings/lang/de.js delete mode 100644 public/assets/javascripts/ckeditor/plugins/studip-settings/lang/en.js delete mode 100644 public/assets/javascripts/ckeditor/plugins/studip-settings/plugin.js diff --git a/app/controllers/wysiwyg.php b/app/controllers/wysiwyg.php index 7b550326831..cb07e6fcd06 100644 --- a/app/controllers/wysiwyg.php +++ b/app/controllers/wysiwyg.php @@ -20,41 +20,8 @@ * @author Robert Costa <rcosta@uos.de> */ -class WysiwygException extends Exception {}; - -class WysiwygHttpException extends WysiwygException {}; - -class WysiwygHttpExceptionBadRequest extends WysiwygHttpException -{ - public function __construct($message = '', $previous = null) { - parent::__construct($message, 400, $previous); - } -} - -class WysiwygHttpExceptionForbidden extends WysiwygHttpException -{ - public function __construct($message = '', $previous = null) { - parent::__construct($message, 403, $previous); - } -} - -class WysiwygHttpExceptionNotFound extends WysiwygHttpException -{ - public function __construct($message = '', $previous = null) { - parent::__construct($message, 404, $previous); - } -} - -class WysiwygHttpExceptionMethodNotAllowed extends WysiwygHttpException -{ - public function __construct($message = '', $previous = null) { - parent::__construct($message, 405, $previous); - } -} - class WysiwygController extends AuthenticatedController { - const UPLOAD_PERMISSION = 'autor'; // minimum permission level for uploading const FOLDER_NAME = 'Wysiwyg Uploads'; const FOLDER_DESCRIPTION = 'Vom WYSIWYG Editor hochgeladene Dateien.'; @@ -153,327 +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()); - } - } - - /** - * 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/public/assets/javascripts/ckeditor/plugins/studip-settings/dialogs/settings.js b/public/assets/javascripts/ckeditor/plugins/studip-settings/dialogs/settings.js deleted file mode 100644 index de6979117f4..00000000000 --- a/public/assets/javascripts/ckeditor/plugins/studip-settings/dialogs/settings.js +++ /dev/null @@ -1,124 +0,0 @@ -CKEDITOR.dialog.add('settingsDialog', function (editor) { - var lang = editor.lang['studip-settings']; - - // Time span after which the UI will display an abort option - // to the user if the HTTP request hasn't yet finished. - var uiTimeout = 3000; - - var settings = { - url: STUDIP.URLHelper.resolveURL( - 'dispatch.php/wysiwyg/settings/users/current' - ), - save: function (data) { - return $.ajax({ - url: this.url, - type: 'PUT', - contentType: 'application/json', - data: JSON.stringify(data) - }); - } - }; - - var - dialog = null, - status = $('<span>').attr('class', 'cke_disabled'), - saveEvents = 0; // remember how many save events are currently active - - function save(data) { - status.html(lang.savingChanges); - saveEvents++; - - dialog.disableButton('ok'); - - var request = settings.save(data); - - var timeoutId = setTimeout(function () { - status - .append(' (') - .append( - $('<a>') - .text(lang.abort) - .css('text-decoration', 'underline') // TODO use default styles - .click(function (event) { - event.preventDefault(); - request.abort(); - }) - ) - .append(')'); - }, uiTimeout); - - request - .done(function () { - status.html(lang.savedChanges); - }) - .fail(function (xhr) { - var $error = $('<a>') - .text(lang.information) - .css('text-decoration', 'underline') // TODO use default styles - .click(function (event) { - event.preventDefault(); - alert( - settings.url + - '\n\n' + lang.status + ' ' + xhr.status + - ' ' + xhr.statusText + - '\n' + lang.response + ' ' + xhr.responseText - ); - }); - - status - .html(lang.savingFailed + ' (') - .append($error) - .append(')'); - }) - .always(function () { - clearTimeout(timeoutId); - saveEvents--; - if (saveEvents <= 0) { - dialog.enableButton('ok'); - } - }); - - return request; - } - - return { - title: lang.dialogTitle, - width: 400, - height: 200, - resizable: CKEDITOR.DIALOG_RESIZE_NONE, - contents: [{ - elements: [{ - type: 'checkbox', - id: 'disable', - label: lang.disableEditorLabel, - onClick: function() { - var checkbox = this; - checkbox.disable(); // prevent multiple save events - - save({ disabled: checkbox.getValue() }) - .done(function (settings) { - checkbox.setValue(settings.disabled); - }) - .fail(function () { - checkbox.setValue(!checkbox.getValue()); - }) - .always(function () { - checkbox.enable(); - }); - } - }, { - type: 'html', - style: 'white-space: normal', - html: lang.disableEditorInfo - }] - }], - onLoad: function (event) { - $(this.parts.footer.$).append(status); - dialog = this; - }, - onShow: function (event) { - status.text(''); - } - }; -}); - diff --git a/public/assets/javascripts/ckeditor/plugins/studip-settings/icons/hidpi/settings.png b/public/assets/javascripts/ckeditor/plugins/studip-settings/icons/hidpi/settings.png deleted file mode 100644 index 4e18a94429f4542f25281006feec635f0741f385..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 897 zcmV-{1AhF8P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80000WV@Og>004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00004XF*Lt006O$eEU(800001 zb5ch_0Itp)=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m_e01m_fl`9S#0008m zNkl<Zc-pO(U4&F&7{|}<97$4h(ZybjjLfpK5j&}isEBN$Vk4R|(if#IEfw9Q(z0D_ z%w?&i=wef`Q7Ic6eb`h)OS?68SB8?cMv|5O5C6m4>p5rkJ?A|9%*^}wywCf5yp!>g zOA9y!`oS~s)9C91$H6T4tW9o8xOq3Y0(|~{3Od0;a2Xsk##X^9P&XIw6DqLST4OW# z4rV~!8k+@k_5w*2s48t)VoMGDwi}xhY%a?0b{O9V)+P0Uqu>m91=_i&i^vrWf*h}? zHRE{VVDd_^EQZyZsrYiRM6S@q2exqYsMA%zZSVthfQt>Rbi{Xz_qhni?E()h2Ym>J zwYv`-*5wM`fl07K>AMfkhgjtUIHdGllbqoi1p{D6X@4FuXVgwU49AnAV4N@<4{=Zz zkxaQZP9E3}4k+#0BIayl#<b<8M3RdSbaT=Ir7zD+_g2Y@_d-He!f?Nf0&<Fd5&dF? z0{8=#f?ePiSP?PjjcX?S6eIgZNmr&p;*n4(g9(OUo%uk*?w3;W8p#aT{KD^NeWhTG z@p}&)NaQeh!7zMnjJZJt=UWHX*iqzLPS(%3t+KWUQsQSjkt%qX50J7$_>PB_<JH_; zq{y^tONrw|={vMmK^Z4W=8;nSb6PkC7I+V)8x$K;#>h19GHYbLz3h{Nj4<hleAHe< zrW0<pLZ-&*Hb`ePWBCc=Tk6AADk6*TF<wdEXa#Y&gW!^;fP~?CaN5dd^5EEHm{CkO zWj(bdDP5;?#uo8`3rZ@jSa(=w_?S}WTra5tUdY38J`YeY4FxK6p8sAdinErK65N|q zfhJ>vVfsTLLo|Dy3bk1ekez8*z&El~i&9*|%4{ypD+m%%!C_u8t)i4MVm`~qV~t6} zKBa`)B65NcPA@GVFe~6=Ud>CM)MIMxWcvpG3-;2qKp+$R{L8=8MW)(w?34DdX@=oH Xzkrs}K&r<V00000NkvXXu0mjfo|lf2 diff --git a/public/assets/javascripts/ckeditor/plugins/studip-settings/icons/settings.png b/public/assets/javascripts/ckeditor/plugins/studip-settings/icons/settings.png deleted file mode 100644 index a53b271e922e12db29d4039fccca923e790b2251..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6=6Sj}hEy=t1~3X9RuJfBQBh#z zoZQgjIitmDMT_SO7L^btp$P{*Mdwv7*nZ*hJQdztgNJRWL<4r*FKlH0RsMGK49~ge zE^O%46j2p1IeS%_?__83f|TV<{f%1hgG*MeSl429<9~hg-o%t+m;bXq@0FRaSO56d zb-6?CAqz|^Cs|)vlDDmBa-_zY3%UE3um(<hRsH#d^Y4$hgmjy$qmGJK@RYu2{*e6@ P=u!qxS3j3^P6<r_g{4`1 diff --git a/public/assets/javascripts/ckeditor/plugins/studip-settings/lang/de.js b/public/assets/javascripts/ckeditor/plugins/studip-settings/lang/de.js deleted file mode 100644 index ab7e9de3840..00000000000 --- a/public/assets/javascripts/ckeditor/plugins/studip-settings/lang/de.js +++ /dev/null @@ -1,18 +0,0 @@ -CKEDITOR.plugins.setLang('studip-settings', 'de', { - buttonLabel: 'WYSIWYG Einstellungen\n(Editor deaktivieren, usw.)', - savingChanges: '...speichere Änderungen.', - abort: 'Abbrechen', - savedChanges: 'Änderungen wurden gespeichert.', - information: 'Info', - status: 'Status:', - response: 'Response:', - savingFailed: 'Speichern fehlgeschlagen.', - dialogTitle: 'Einstellungen', - disableEditorLabel: 'WYSIWYG Editor ausschalten', - disableEditorInfo: '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 man die Seite neu lädt.', -}); - diff --git a/public/assets/javascripts/ckeditor/plugins/studip-settings/lang/en.js b/public/assets/javascripts/ckeditor/plugins/studip-settings/lang/en.js deleted file mode 100644 index 511e97dbfb3..00000000000 --- a/public/assets/javascripts/ckeditor/plugins/studip-settings/lang/en.js +++ /dev/null @@ -1,17 +0,0 @@ -CKEDITOR.plugins.setLang('studip-settings', 'en', { - buttonLabel: 'WYSIWYG Settings\n(Deactivate the editor, etc.)', - savingChanges: '...saving changes.', - abort: 'Abort', - savedChanges: 'All changes saved.', - information: 'Info', - status: 'Status:', - response: 'Response:', - savingFailed: 'Saving failed.', - dialogTitle: 'Settings', - disableEditorLabel: 'Switch WYSIWYG editor off', - disableEditorInfo: 'This setting will disable the WYSIWYG' - + ' Editor. You might have to write HTML code if you' - + ' do so. The editor will only be fully removed after' - + ' you reload the page.', -}); - diff --git a/public/assets/javascripts/ckeditor/plugins/studip-settings/plugin.js b/public/assets/javascripts/ckeditor/plugins/studip-settings/plugin.js deleted file mode 100644 index 186ba6785f5..00000000000 --- a/public/assets/javascripts/ckeditor/plugins/studip-settings/plugin.js +++ /dev/null @@ -1,20 +0,0 @@ -CKEDITOR.plugins.add('studip-settings', { - icons: 'settings', - hidipi: true, - lang: 'de,en', - init: function (editor) { - CKEDITOR.dialog.add( - 'settingsDialog', - this.path + 'dialogs/settings.js' - ); - editor.addCommand( - 'settings', - new CKEDITOR.dialogCommand('settingsDialog') - ); - editor.ui.addButton('settings', { - label: editor.lang['studip-settings'].buttonLabel, - command: 'settings', - toolbar: 'settings' - }); - } -}); diff --git a/resources/assets/javascripts/lib/toolbar.js b/resources/assets/javascripts/lib/toolbar.js index 3251065c0df..c707b49df63 100644 --- a/resources/assets/javascripts/lib/toolbar.js +++ b/resources/assets/javascripts/lib/toolbar.js @@ -44,41 +44,6 @@ const Toolbar = { button_set = button_set || Toolbar.buttonSet; - // if WYSIWYG is globally enabled then add a button so - // the user can activate it - if (STUDIP.wysiwyg_enabled && $element.hasClass('wysiwyg')) { - button_set.right.wysiwyg = { - label: 'WYSIWYG', - evaluate: function() { - var question = [ - $gettext('Soll der WYSIWYG Editor aktiviert werden?'), - '', - $gettext('Die Seite muss danach neu geladen werden, um den WYSIWYG Editor zu laden.') - ].join('\n'); - Dialog.confirm(question, function() { - var url = STUDIP.URLHelper.resolveURL('dispatch.php/wysiwyg/settings/users/current'); - - $.ajax({ - url: url, - type: 'PUT', - contentType: 'application/json', - data: JSON.stringify({ disabled: false }) - }).fail(function(xhr) { - window.alert( - [ - $gettext('Das Aktivieren des WYSIWYG Editors ist fehlgeschlagen.'), - '', - $gettext('URL') + ': ' + url, - $gettext('Status') + ': ' + xhr.status + ' ' + xhr.statusText, - $gettext('Antwort') + ': ' + xhr.responseText - ].join('\n') - ); - }); - }); - } - }; - } - // Add flag so one element will never have more than one toolbar $element.data('toolbar-added', true); diff --git a/resources/assets/javascripts/lib/wysiwyg.js b/resources/assets/javascripts/lib/wysiwyg.js index 933d93fa07a..1f2f094abb4 100644 --- a/resources/assets/javascripts/lib/wysiwyg.js +++ b/resources/assets/javascripts/lib/wysiwyg.js @@ -170,7 +170,7 @@ const wysiwyg = { skin: 'studip,' + STUDIP.ASSETS_URL + 'stylesheets/ckeditor-skin/', // NOTE codemirror crashes when not explicitely loaded in CKEditor 4.4.7 extraPlugins: - 'emojione,studip-floatbar,studip-quote,studip-upload,studip-settings' + + 'emojione,studip-floatbar,studip-quote,studip-upload' + (extraPlugins ? ',' + extraPlugins : ''), removePlugins: removePlugins ? removePlugins : textarea.closest('.ui-dialog').length ? 'autogrow' : '', enterMode: CKEDITOR.ENTER_BR, @@ -192,8 +192,7 @@ const wysiwyg = { { name: 'basicstyles', groups: ['undo', 'basicstyles', 'cleanup'] }, { name: 'paragraph', groups: ['list', 'indent', 'blocks', 'align', 'quote'] }, '/', - { name: 'styles', groups: ['styles', 'colors', 'tools', 'links', 'insert'] }, - { name: 'others', groups: ['mode', 'settings'] } + { name: 'styles', groups: ['styles', 'colors', 'tools', 'links', 'insert', 'mode'] } ], removeButtons: 'Font,FontSize', toolbarCanCollapse: true, -- GitLab