├── .upgrade.yml ├── _config └── extensions.yml ├── CONTRIBUTING.md ├── javascript ├── hide-error-pages.js ├── cms-tweaks.js ├── sitetree-noedit.js └── meta-stats.js ├── .editorconfig ├── docs └── en │ ├── MetadataTab.md │ └── CMSTweaks.md ├── composer.json ├── LICENSE ├── README.md ├── css └── cms-tweaks.css ├── CHANGELOG.md └── src ├── MetadataTab.php └── CMSTweaks.php /.upgrade.yml: -------------------------------------------------------------------------------- 1 | mappings: 2 | LeftAndMainCMSTweaksExt: Axllent\CMSTweaks\LeftAndMainCMSTweaksExt 3 | -------------------------------------------------------------------------------- /_config/extensions.yml: -------------------------------------------------------------------------------- 1 | SilverStripe\Admin\LeftAndMain: 2 | extensions: 3 | - Axllent\CMSTweaks\CMSTweaks 4 | 5 | SilverStripe\CMS\Model\SiteTree: 6 | extensions: 7 | - Axllent\CMSTweaks\MetadataTab 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | You can contribute by reporting bugs, sharing ideas and of course pull requests, 4 | all through the [Github project page](https://github.com/axllent/silverstripe-cms-tweaks). 5 | 6 | This code is open source (please refer to the [LICENCE](LICENCE)). 7 | -------------------------------------------------------------------------------- /javascript/hide-error-pages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Do not display error pages in SiteTree or file selector 3 | * loaded only when !Permission::check('SITETREE_REORGANISE') 4 | */ 5 | (function($) { 6 | $.entwine('ss', function($) { 7 | /* Hide error pages in SiteTree */ 8 | $('li[data-pagetype$="\ErrorPage"]').entwine({ 9 | onmatch: function() { 10 | this.hide(); 11 | } 12 | }); 13 | /* Hide error pages in page link dropdowns */ 14 | $('li.class-ErrorPage').entwine({ 15 | onmatch: function() { 16 | this.hide(); 17 | } 18 | }); 19 | }); 20 | })(jQuery); 21 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in this file, 2 | # please see the EditorConfig documentation: 3 | # http://editorconfig.org 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | indent_size = 4 9 | indent_style = space 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [{*.yml,package.json}] 14 | indent_size = 2 15 | 16 | [*.{js,css}] 17 | indent_size = 2 18 | indent_style = space 19 | 20 | [*.{ss}] 21 | indent_size = 4 22 | indent_style = tab 23 | 24 | # The indent size used in the package.json file cannot be changed: 25 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 26 | -------------------------------------------------------------------------------- /javascript/cms-tweaks.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | $.entwine('ss', function($) { 3 | /* remove `target` from logo links */ 4 | $('#cms-menu .cms-sitename a').removeAttr('target'); 5 | 6 | /* Prevent enter key from submitting */ 7 | $('.cms-edit-form .field.date input, .cms-edit-form .field.text input,' + 8 | '.cms-edit-form .noenter').entwine({ 9 | onkeydown: function(e) { 10 | if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) 11 | e.preventDefault(); 12 | } 13 | }); 14 | }); 15 | 16 | /* Set the default layout to 'content' */ 17 | $.entwine('ss.preview', function($) { 18 | $('.cms-preview').entwine({ 19 | DefaultMode: 'content' 20 | }); 21 | }); 22 | })(jQuery); 23 | -------------------------------------------------------------------------------- /docs/en/MetadataTab.md: -------------------------------------------------------------------------------- 1 | # Metadata Tab 2 | 3 | The metadata tab (by default) moves the content from the metadata composite field to its own tab, and positions it to the right. Using a custom yml config (eg: `app/_config/metadatatab.yml`) You can turn this option off, modify its tab name, and turn off the "float right" option. 4 | 5 | ## Config 6 | 7 | ```yml 8 | Axllent\CMSTweaks\MetadataTab: 9 | use_tab: true # default true 10 | tab_title: 'SEO' # default `Advanced` 11 | tab_to_right: true # default true 12 | page_name_title: 'Meta Title' # rename "Page name" to "Meta Title" 13 | move_title_to_advanced: true # for users without canCreate() permissions, move Title to `Advanced` tab 14 | ``` 15 | -------------------------------------------------------------------------------- /docs/en/CMSTweaks.md: -------------------------------------------------------------------------------- 1 | # CMS Tweaks 2 | 3 | CMSTweaks makes some minor alterations to the CMS, including the following: 4 | 5 | ## Hide help 6 | 7 | It removed the default `CMS User help`, `Developer docs`, `Community` & `Feedback` links. 8 | 9 | This can be disabled with 10 | 11 | ```yaml 12 | Axllent\CMSTweaks\CMSTweaks: 13 | hide_help: false 14 | ``` 15 | 16 | ## TinyMCE features 17 | 18 | To limit the nasty side effects of copy/paste, TinyMCE has the following two options set: 19 | 20 | 21 | ```yaml 22 | Axllent\CMSTweaks\CMSTweaks: 23 | invalid_elements: "div" 24 | extended_valid_elements: "span[!class|!style],p[class|style]" 25 | ``` 26 | 27 | These completely overrule any existing `HTMLEditorConfig` that was set. If you want to revert back to the system default, set both of these to `false`. 28 | 29 | All other editor options are inherited from `HTMLEditorConfig`. 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "axllent/silverstripe-cms-tweaks", 3 | "description": "Several CMS usability improvements", 4 | "type": "silverstripe-vendormodule", 5 | "homepage": "https://github.com/axllent/silverstripe-cms-tweaks", 6 | "keywords": [ 7 | "silverstripe", 8 | "cms", 9 | "tweaks", 10 | "javascript" 11 | ], 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "Ralph Slooten", 16 | "homepage": "https://www.axllent.org/" 17 | } 18 | ], 19 | "support": { 20 | "issues": "https://github.com/axllent/silverstripe-cms-tweaks/issues" 21 | }, 22 | "require": { 23 | "silverstripe/cms": "^4.0 || ^5.0 || ^6.0", 24 | "axllent/silverstripe-form-fields": "^1.2 || ^2.0" 25 | }, 26 | "extra": { 27 | "expose": [ 28 | "css", 29 | "javascript" 30 | ] 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Axllent\\CMSTweaks\\": "src/" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) Techno Joy www.technojoy.co.nz 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /javascript/sitetree-noedit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Do not display ability to add pages or Settings tab 3 | * loaded only when !Permission::check('SITETREE_REORGANISE') 4 | */ 5 | (function ($) { 6 | $.entwine('ss', function ($) { 7 | 8 | /* Hide "Add page" button */ 9 | $('a[href="admin/pages/add"], a[href="admin/pages/add"]').entwine({ 10 | onmatch: function () { 11 | this.hide(); 12 | } 13 | }); 14 | 15 | /* Hide "duplicate" content menu */ 16 | $('#vakata-contextmenu a[rel="duplicate"]').entwine({ 17 | onmatch: function () { 18 | this.parent().hide(); 19 | } 20 | }); 21 | 22 | /* Hide "Settings" tab */ 23 | $('ul.ui-tabs-nav > li').entwine({ 24 | onmatch: function () { 25 | var c = this.text().trim(); 26 | if (c.match(/^Settings$/)) 27 | this.hide(); 28 | } 29 | }); 30 | 31 | /* Hide page "drag" bars in SiteTree */ 32 | $('div.cms-tree a ins.jstree-icon').entwine({ 33 | onmatch: function () { 34 | if (!this.hasClass('jstree-checkbox')) { /* Fix cms inconsistencies when Pages loaded through ajax */ 35 | this.hide(); 36 | this.parent().css('padding-left', '0'); // Remove padding-left 37 | } 38 | } 39 | }); 40 | }); 41 | })(jQuery); 42 | -------------------------------------------------------------------------------- /javascript/meta-stats.js: -------------------------------------------------------------------------------- 1 | /* Meta title / description counter */ 2 | (function($) { 3 | $.entwine('ss', function($) { 4 | $('textarea#Form_EditForm_MetaDescription, input#Form_EditForm_Title').entwine({ 5 | onkeyup: function() { 6 | this.updateStats(); 7 | }, 8 | onkeydown: function(e) { 9 | if ((e.which && e.which == 13) || (e.keyCode && e.keyCode == 13)) { 10 | e.preventDefault(); 11 | } 12 | }, 13 | onfocusin: function() { 14 | $(this).parent().addClass('counter-visible'); 15 | }, 16 | onfocusout: function() { 17 | $(this).parent().removeClass('counter-visible'); 18 | }, 19 | updateStats: function() { 20 | var counter = $(this).parent().find('.charcounter'); 21 | if (!counter.length) { 22 | var c = $('
'); 23 | this.parent().append(c); 24 | counter = $(this).parent().find('.charcounter'); 25 | this.parent().addClass('counter-container'); 26 | } 27 | var wordCounts = {}; 28 | var v = this.val().trim(); 29 | var matches = v.match(/\b/g); 30 | wordCounts[this.id] = matches ? matches.length / 2 : 0; 31 | var words = 0; 32 | $.each(wordCounts, function(k, v) { 33 | words += v; 34 | }); 35 | var chars = v.replace(/\s+/g, ' ').length; 36 | counter.text(words + ' words | ' + chars + ' chars'); 37 | } 38 | }); 39 | }); 40 | })(jQuery); 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Silverstripe CMS Tweaks 2 | 3 | Module to add a series of tweaks/modifications to the Silverstripe CMS. 4 | The goal is to make the CMS less confusing for non-technical users, removing 5 | functionality some they can't use (ie: non SITETREE_REORGANISE users). 6 | 7 | ## Modifications (PHP,css/JavaScript) includes 8 | 9 | - Move MetaDescription and ExtraMeta to it's own "Advanced tab" 10 | - Adds MetaKeywords for those wanting to use them 11 | - Page name, URL Segment, Navigation label not shown to users without `SITETREE_REORGANISE` permissions 12 | - Meta Title is displayed on the Advanced tab if no `SITETREE_REORGANISE` permissions 13 | - "Page name" renamed "Meta Title" to avoid confusion 14 | - JavaScript word/character count for Meta Title & Meta Description 15 | - Position the Advanced tab on the right (floated after all other tabs) 16 | - Dependent pages (if any) displayed on Advanced tab 17 | - Hide add/Archive page buttons when user has no `SITETREE_REORGANISE` permissions 18 | - Hide CMS ErrorPages to non-admin users in CMS 19 | - Remove SiteTree drag handle when user has no `SITETREE_REORGANISE` permissions 20 | - Remove "Duplicate" from right-mouse menu when user has no `SITETREE_REORGANISE` permissions 21 | - Prevent accidental form submission in CMS when the enter key is used on input fields 22 | - Add "Remove formatting" button to TinyMCE to clean pasted code 23 | - Remove "Help" link 24 | 25 | Please see [CMSTweaks documentation](docs/en/CMSTweaks.md) for a configuration info. 26 | 27 | 28 | ## Metadata Tab 29 | 30 | The MetadataTab extension (by default) moves the Metadata information from below the Content field into its own tab. 31 | Please refer to the [Metadata Tab documentation](docs/en/MetadataTab.md) for configuration options. 32 | 33 | 34 | ## Requirements 35 | 36 | - Silverstripe ^4.0 || ^5.0 || ^6.0 37 | -------------------------------------------------------------------------------- /css/cms-tweaks.css: -------------------------------------------------------------------------------- 1 | Remove Firefox focus dotted outlines 2 | *:focus { 3 | outline-color: invert; 4 | outline-style: none; 5 | outline-width: 0; 6 | } 7 | 8 | .cms-logo span { 9 | text-overflow: ellipsis; 10 | white-space: nowrap; 11 | overflow: hidden; 12 | } 13 | 14 | .pull-right { 15 | float: right !important; 16 | } 17 | 18 | .cms .cms-content-fields.panel--scrollable { 19 | overflow-y: scroll; 20 | } 21 | 22 | .tab-right { 23 | order: 99; 24 | } 25 | 26 | /* Prevent wrapping when SiteTree pages panel becomes scrollable */ 27 | .btn-toolbar > .btn[class*="font-icon-"]::before { 28 | margin-right: 1px; 29 | } 30 | 31 | .btn-toolbar > .btn:not(:first-child) { 32 | margin-left: 2px; 33 | } 34 | 35 | @media (min-width: 768px) { 36 | .tab-right { 37 | margin-left: auto !important; 38 | } 39 | } 40 | 41 | .ss-toggle .ui-accordion-content { 42 | padding: 15px; 43 | } 44 | 45 | .counter-container { 46 | position: relative; 47 | } 48 | 49 | /* Meta title / description counters */ 50 | .counter-container .charcounter { 51 | display: none; 52 | } 53 | 54 | .counter-container.counter-visible .charcounter { 55 | position: absolute; 56 | display: block; 57 | top: -22px; 58 | right: 22px; 59 | background: rgba(0, 0, 0, 0.5); 60 | color: #fff; 61 | padding: 2px 5px; 62 | border-radius: 10px; 63 | font-size: 80%; 64 | } 65 | 66 | /* Why would placeholder text be almost the same colours as value? */ 67 | input::placeholder, 68 | textarea::placeholder { 69 | color: #a3a8af !important; 70 | } 71 | 72 | /* Gridfield relation editor "link existing" icons left */ 73 | .btn.action_gridfield_relationadd::before { 74 | float: left; 75 | } 76 | 77 | /* Contain UploadField previews */ 78 | .uploadfield-item__thumbnail { 79 | background-size: contain !important; 80 | } 81 | 82 | /* too many pages is warped, make inline */ 83 | .cms-content-tools li.readonly.jstree-leaf .subtree-list-link { 84 | display: inline; 85 | } 86 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Notable changes to this project will be documented in this file. 4 | 5 | ## [2.3.1] 6 | 7 | - Fix support for loading editor.css into TinyMCE on Silverstripe 6 8 | 9 | 10 | ## [2.3.0] 11 | 12 | - Add support for Silverstripe 6 13 | 14 | 15 | ## [2.2.5] 16 | 17 | - Replace deprecated extensions with Extension 18 | 19 | 20 | ## [2.2.4] 21 | 22 | - Leave "Advanced" tab as that, do not add linking page count 23 | 24 | 25 | ## [2.2.3] 26 | 27 | - Fix "too many pages" misalignment 28 | 29 | 30 | ## [2.2.2] 31 | 32 | - Fix for Silverstripe 5 (add page btn) 33 | 34 | 35 | ## [2.2.1] 36 | 37 | - Support for Silverstripe 5 38 | 39 | 40 | ## [2.2.0] 41 | 42 | - Allow embedding of videos by default 43 | 44 | 45 | ## [2.1.1] 46 | 47 | - Contain `UploadField` previews 48 | - Use same field label casing as SS4.4 49 | 50 | 51 | ## [2.1.0] 52 | 53 | - Revert merging of `HtmlEditorConfig:extended_valid_elements` 54 | - Set `CMSTweaks:extended_valid_elements` & `CMSTweaks:invalid_elements` via yaml config 55 | 56 | 57 | ## [2.0.14] 58 | 59 | - Allow merging of `HtmlEditorConfig` [extended_valid_elements](https://github.com/axllent/silverstripe-cms-tweaks/pull/7) 60 | - Code cleanup / PSR2 61 | 62 | 63 | ## [2.0.13] 64 | 65 | - Make hide_help compatible with SilverStripe 4.3 66 | 67 | 68 | ## [2.0.12] 69 | 70 | - Remove duplicate 'removeformat' button 71 | 72 | 73 | ## [2.0.11] 74 | 75 | - Fix typo in array count 76 | 77 | 78 | ## [2.0.10] 79 | 80 | - Allow `$parent->canAddChildren()` to determine editing capabilities 81 | 82 | 83 | ## [2.0.9] 84 | 85 | - Fix gridfield relation editor link button 86 | 87 | 88 | ## [2.0.8] 89 | 90 | - Remove TinyMCE branding patch 91 | - Update composer.json 92 | 93 | 94 | ## [2.0.7] 95 | 96 | - Remove Firefox focus dotted outlines 97 | 98 | 99 | ## [2.0.6] 100 | 101 | - Switch to silverstripe-vendormodule 102 | 103 | 104 | ## [2.0.5] 105 | 106 | - Simplify for users that cannot create. MenuTitle & URLSegment are removed, title is optionally moved to advanced tab 107 | 108 | 109 | ## [2.0.4] 110 | 111 | - Prevent wrapping when SiteStree pages panel becomes scrollable 112 | 113 | 114 | ## [2.0.3] 115 | 116 | - Hide TinyMCE branding 117 | 118 | 119 | ## [2.0.2] 120 | 121 | - Changes for SilverStripe 4-beta3 122 | - CSS for flex tab right alignment (order: 99) 123 | - Force scroll in `.cms-content-fields.panel--scrollable` 124 | 125 | 126 | ## [2.0.1] 127 | 128 | - Switch TinyMCE modifications to onAfterInit() 129 | - Correctly timestamp TinyMCE style files 130 | 131 | 132 | ## [2.0.0] 133 | 134 | - SilverStripe 4 135 | - Timestamp editor_css & content_css 136 | - Meta data character / word counters 137 | - Update dependencies 138 | - Split custom formfields into separate requirement axllent/silverstripe-form-fields 139 | -------------------------------------------------------------------------------- /src/MetadataTab.php: -------------------------------------------------------------------------------- 1 | get(self::class, 'use_tab'); 86 | $tab_title = $config->get(self::class, 'tab_title'); 87 | $tab_to_right = $config->get(self::class, 'tab_to_right'); 88 | $page_name_title = $config->get(self::class, 'page_name_title'); 89 | $move_title_to_advanced = $config->get(self::class, 'move_title_to_advanced'); 90 | 91 | if ($config->get(self::class, 'show_meta_lengths')) { 92 | Requirements::javascript( 93 | 'axllent/silverstripe-cms-tweaks: javascript/meta-stats.js' 94 | ); 95 | } 96 | 97 | $metadata_tab = $fields->fieldByName('Root.Main.Metadata'); 98 | 99 | if ($use_tab 100 | && $metadata_tab 101 | && $metadata_fields = $metadata_tab->FieldList() 102 | ) { 103 | $tab = $fields->findOrMakeTab('Root.' . $tab_title); 104 | 105 | $tab->setTitle($tab_title); 106 | if ($tab_to_right) { 107 | $tab->addExtraClass('tab-right'); 108 | } 109 | 110 | $dependent_tab = $fields->findOrMakeTab('Root.Dependent'); 111 | $tab_fields = $dependent_tab->fields(); 112 | if ($count = $this->owner->DependentPages()->count()) { 113 | $dependency_pages = ToggleCompositeField::create( 114 | 'Dependencies', 115 | 'Links to this page (' . $count . ')', 116 | $tab_fields 117 | )->setHeadingLevel(5); 118 | $fields->addFieldToTab('Root.' . $tab_title, $dependency_pages); 119 | } 120 | $fields->removeByName('Dependent'); 121 | 122 | $fields->removeFieldFromTab('Root.Main', 'Metadata'); 123 | 124 | foreach ($metadata_fields as $f) { 125 | $fields->addFieldToTab('Root.' . $tab_title, $f); 126 | } 127 | 128 | $title_field = $fields->dataFieldByName('Title'); 129 | 130 | if ($page_name_title && $title_field) { 131 | $title_field->setTitle($page_name_title); 132 | } 133 | 134 | // Detect whether a user is allowed to create pages in this section 135 | $parent = $this->owner->Parent(); 136 | if (($parent->exists() && !$parent->canAddChildren()) 137 | || (!$parent->exists() && !$this->owner->canCreate()) 138 | ) { 139 | if ($title_field && $move_title_to_advanced) { 140 | $meta_description = $fields->dataFieldByName('MetaDescription') ? 'MetaDescription' : false; 141 | $fields->addFieldToTab('Root.' . $tab_title, $title_field, $meta_description); 142 | } 143 | $fields->removeByName('MenuTitle'); 144 | $fields->removeByName('URLSegment'); 145 | } 146 | } 147 | 148 | return $fields; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/CMSTweaks.php: -------------------------------------------------------------------------------- 1 | loadModule(); 60 | $this->setHtmlEditorConfig(); 61 | } 62 | 63 | /** 64 | * LoadModule function 65 | * 66 | * @return void 67 | */ 68 | public function loadModule() 69 | { 70 | $config = Config::inst(); 71 | 72 | Requirements::css( 73 | 'axllent/silverstripe-cms-tweaks: css/cms-tweaks.css', 74 | ); 75 | 76 | Requirements::javascript( 77 | 'axllent/silverstripe-cms-tweaks: javascript/cms-tweaks.js', 78 | ); 79 | 80 | if ($config->get(self::class, 'hide_help')) { 81 | // backwards compatibility 82 | CMSMenu::remove_menu_item('Help'); 83 | // SilverStripe 4.3 84 | LeftAndMain::config()->set( 85 | 'help_links', 86 | [ 87 | 'CMS User help' => '', 88 | 'Developer docs' => '', 89 | 'Community' => '', 90 | 'Feedback' => '', 91 | ], 92 | ); 93 | } 94 | 95 | // Hide "Add new" page, page Settings tab 96 | if (!Permission::check('SITETREE_REORGANISE')) { 97 | Requirements::javascript( 98 | 'axllent/silverstripe-cms-tweaks: javascript/sitetree-noedit.js', 99 | ); 100 | } 101 | 102 | // Hide all error pages in SiteTree and Files (ModelAdmin) 103 | if (!Permission::check('ADMIN')) { 104 | Requirements::javascript( 105 | 'axllent/silverstripe-cms-tweaks: javascript/hide-error-pages.js', 106 | ); 107 | } 108 | } 109 | 110 | /** 111 | * Set default options for TinyMCE 112 | * Add timestamps to included css files 113 | * 114 | * @return void 115 | */ 116 | public function setHtmlEditorConfig() 117 | { 118 | $editor = HtmlEditorConfig::get('cms'); 119 | 120 | // includes backwards compatibility for SilverStripe 4 & 5 121 | if (!$editor instanceof \SilverStripe\Forms\HTMLEditor\TinyMCEConfig && !$editor instanceof TinyMCEConfig) { 122 | return; 123 | } 124 | 125 | $editor->removeButtons('paste'); 126 | 127 | $editor_options = []; 128 | 129 | $config = Config::inst(); 130 | 131 | if ($invalid_elements = $config->get(self::class, 'invalid_elements')) { 132 | $editor_options['invalid_elements'] = $invalid_elements; 133 | } 134 | 135 | // See and http://martinsikora.com/how-to-make-tinymce-to-output-clean-html 136 | if ($ext_valid_els = $config->get(self::class, 'extended_valid_elements')) { 137 | $editor_options['extended_valid_elements'] = $ext_valid_els; 138 | } 139 | 140 | // Set editor options 141 | if (count($editor_options)) { 142 | $editor->setOptions($editor_options); 143 | } 144 | 145 | // Add file timestamps for TinyMCE's editor_css 146 | $css_config = $editor->config()->get('editor_css'); 147 | if (!empty($css_config)) { 148 | $timestamped_css = []; 149 | $base_folder = Director::baseFolder(); 150 | foreach ($css_config as $file) { 151 | $file = $this->resolvePath($file); 152 | if (is_file($base_folder . '/' . $file)) { 153 | array_push($timestamped_css, $file . '?m=' . filemtime($base_folder . '/' . $file)); 154 | } else { 155 | array_push($timestamped_css, $file); 156 | } 157 | } 158 | $editor->config()->set('editor_css', $timestamped_css); 159 | } 160 | 161 | // Add file timestamps for TinyMCE's content_css 162 | $css = $editor->getOption('content_css'); 163 | if (!empty($css)) { 164 | $base_folder = Director::baseFolder(); 165 | $timestamped_css = []; 166 | $regular_css = preg_split('/,/', $css, -1, PREG_SPLIT_NO_EMPTY); 167 | foreach ($regular_css as $file) { 168 | $file = $this->resolvePath($file); 169 | if (is_file($base_folder . '/' . $file)) { 170 | array_push( 171 | $timestamped_css, 172 | $file . '?m=' . filemtime($base_folder . '/' . $file), 173 | ); 174 | } else { 175 | array_push($timestamped_css, $file); 176 | } 177 | } 178 | if (count($timestamped_css) > 0) { 179 | $editor->setOption('content_css', implode(',', $timestamped_css)); 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * Expand resource path to a relative filesystem path 186 | * Duplicated from TinyMCEConfig::resolvePath() 187 | * 188 | * @param string $path path 189 | * 190 | * @return string 191 | */ 192 | protected function resolvePath($path) 193 | { 194 | if (preg_match('#(?