diff --git a/.changes/unreleased/tinymce-TINY-10654-2024-02-23.yaml b/.changes/unreleased/tinymce-TINY-10654-2024-02-23.yaml new file mode 100644 index 0000000000..2307ad017e --- /dev/null +++ b/.changes/unreleased/tinymce-TINY-10654-2024-02-23.yaml @@ -0,0 +1,6 @@ +project: tinymce +kind: Removed +body: Removed deprecated `template` plugin. +time: 2024-02-23T12:25:39.49598509+10:00 +custom: + Issue: TINY-10654 diff --git a/modules/bridge/src/demo/ts/Demo.ts b/modules/bridge/src/demo/ts/Demo.ts index 6a41647d83..84f3c69a60 100644 --- a/modules/bridge/src/demo/ts/Demo.ts +++ b/modules/bridge/src/demo/ts/Demo.ts @@ -15,7 +15,6 @@ import { createPreviewDialog } from './dialogs/PreviewDialog'; import { createTableCellDialog } from './dialogs/TableCellDialog'; import { createTableDialog } from './dialogs/TableDialog'; import { createTableRowDialog } from './dialogs/TableRowDialog'; -import { createTemplateDialog } from './dialogs/TemplateDialog'; import { createWordcountDialog } from './dialogs/WordcountDialog'; import { registerDemoContextMenus } from './menus/ContextMenuDemo'; import { registerDemoMenuItems } from './menus/MenuItemDemo'; @@ -36,7 +35,6 @@ createPreviewDialog(); createTableCellDialog(); createTableDialog(); createTableRowDialog(); -createTemplateDialog(); registerDemoButtons(); registerDemoMenuItems(); registerDemoContextMenus(); diff --git a/modules/bridge/src/demo/ts/dialogs/TemplateDialog.ts b/modules/bridge/src/demo/ts/dialogs/TemplateDialog.ts deleted file mode 100644 index cf7ad7454c..0000000000 --- a/modules/bridge/src/demo/ts/dialogs/TemplateDialog.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { openDemoDialog } from './DemoDialogHelpers'; - -export const createTemplateDialog = (): void => { - openDemoDialog( - { - title: 'Insert template', - size: 'large', - body: { - type: 'panel', - items: [ - { - name: 'template', - type: 'selectbox', - label: 'Template', - items: [ - { - text: 'Some template 1', - value: 'url1.html' - }, - { - text: 'Some template 2', - value: 'url2.html' - } - ] - }, - { - name: 'preview', - type: 'iframe', - label: 'Preview of template', - sandboxed: false - } - ] - }, - buttons: [ - { - type: 'submit', - name: 'ok', - text: 'Ok', - primary: true - }, - { - type: 'cancel', - name: 'cancel', - text: 'Cancel' - } - ], - initialData: { - template: 'url2.html', - preview: 'some html url' - }, - onSubmit: (api) => { - const data = api.getData(); - - // eslint-disable-next-line no-console - console.log({ - template: data.template - }); - - api.close(); - } - } - ); -}; diff --git a/modules/bridge/src/demo/ts/plugins/TemplateItems.ts b/modules/bridge/src/demo/ts/plugins/TemplateItems.ts deleted file mode 100644 index 37f37113df..0000000000 --- a/modules/bridge/src/demo/ts/plugins/TemplateItems.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { getDemoRegistry } from '../buttons/DemoRegistry'; - -export const registerTemplateItems = (): void => { - getDemoRegistry().addButton('template', { - type: 'button', - enabled: true, - onAction: (_buttonApi) => { - // show dialog - } - }); -}; diff --git a/modules/tinymce/Gruntfile.js b/modules/tinymce/Gruntfile.js index ac6aaaac0a..89669ed376 100644 --- a/modules/tinymce/Gruntfile.js +++ b/modules/tinymce/Gruntfile.js @@ -14,7 +14,7 @@ let plugins = [ 'accordion', 'advlist', 'anchor', 'autolink', 'autoresize', 'autosave', 'charmap', 'code', 'codesample', 'directionality', 'emoticons', 'help', 'fullscreen', 'image', 'importcss', 'insertdatetime', 'link', 'lists', 'media', 'nonbreaking', 'pagebreak', 'preview', 'save', 'searchreplace', - 'table', 'template', 'visualblocks', 'visualchars', 'wordcount', 'quickbars' + 'table', 'visualblocks', 'visualchars', 'wordcount', 'quickbars' ]; let themes = [ diff --git a/modules/tinymce/src/core/demo/ts/demo/CommandsDemo.ts b/modules/tinymce/src/core/demo/ts/demo/CommandsDemo.ts index 95b8f9ff77..f99712aca0 100644 --- a/modules/tinymce/src/core/demo/ts/demo/CommandsDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/CommandsDemo.ts @@ -66,7 +66,6 @@ export default (): void => { cmd('mceSave'), cmd('SearchReplace'), cmd('mceSpellcheck'), - cmd('mceInsertTemplate', '{$user}'), cmd('mceVisualBlocks'), cmd('mceVisualChars'), cmd('mceMedia'), @@ -107,7 +106,7 @@ export default (): void => { plugins: [ 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'importcss', 'codesample' + 'save', 'table', 'directionality', 'emoticons', 'importcss', 'codesample' ], toolbar1: 'bold italic', menubar: false diff --git a/modules/tinymce/src/core/demo/ts/demo/ContentSecurityPolicyDemo.ts b/modules/tinymce/src/core/demo/ts/demo/ContentSecurityPolicyDemo.ts index a4c0783af7..80517f6fae 100644 --- a/modules/tinymce/src/core/demo/ts/demo/ContentSecurityPolicyDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/ContentSecurityPolicyDemo.ts @@ -73,12 +73,6 @@ const settings: RawEditorOptions = { callback('movie.mp4', { embed: '

test

' }); } }, - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content' }, - { title: 'Some title 2', description: 'Some desc 2', content: '
cdatemdateMy content2
' } - ], - template_cdate_format: '[CDATE: %m/%d/%Y : %H:%M:%S]', - template_mdate_format: '[MDATE: %m/%d/%Y : %H:%M:%S]', image_caption: true, theme: 'silver', setup: (ed) => { @@ -87,7 +81,7 @@ const settings: RawEditorOptions = { plugins: [ 'autosave', 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'importcss', 'codesample', 'help' + 'save', 'table', 'directionality', 'emoticons', 'importcss', 'codesample', 'help' ], // rtl_ui: true, add_unload_trigger: false, diff --git a/modules/tinymce/src/core/demo/ts/demo/FixedToolbarContainerDemo.ts b/modules/tinymce/src/core/demo/ts/demo/FixedToolbarContainerDemo.ts index d6aa41ae74..85361c5b27 100644 --- a/modules/tinymce/src/core/demo/ts/demo/FixedToolbarContainerDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/FixedToolbarContainerDemo.ts @@ -7,6 +7,5 @@ export default (): void => { selector: '#editor', inline: true, fixed_toolbar_container: '#toolbar', - plugins: 'template' // lets you check notification positioning }); }; diff --git a/modules/tinymce/src/core/demo/ts/demo/FullDemo.ts b/modules/tinymce/src/core/demo/ts/demo/FullDemo.ts index 425a433c78..cfc1c44550 100644 --- a/modules/tinymce/src/core/demo/ts/demo/FullDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/FullDemo.ts @@ -107,12 +107,6 @@ export default (): void => { callback('movie.mp4', { embed: '

test

' }); } }, - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content' }, - { title: 'Some title 2', description: 'Some desc 2', content: '
cdatemdateMy content2
' } - ], - template_cdate_format: '[CDATE: %m/%d/%Y : %H:%M:%S]', - template_mdate_format: '[MDATE: %m/%d/%Y : %H:%M:%S]', image_caption: true, theme: 'silver', setup: (ed) => { @@ -123,7 +117,7 @@ export default (): void => { plugins: [ 'autosave', 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'importcss', 'codesample', 'help', 'accordion' + 'save', 'table', 'directionality', 'emoticons', 'importcss', 'codesample', 'help', 'accordion' ], // rtl_ui: true, add_unload_trigger: false, diff --git a/modules/tinymce/src/core/demo/ts/demo/InlineDemo.ts b/modules/tinymce/src/core/demo/ts/demo/InlineDemo.ts index e5abafa2b4..f156b2d0c1 100644 --- a/modules/tinymce/src/core/demo/ts/demo/InlineDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/InlineDemo.ts @@ -21,14 +21,10 @@ export default (): void => { { title: 'None', value: '' }, { title: 'Some class', value: 'class-name' } ], - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content' }, - { title: 'Some title 2', description: 'Some desc 2', content: '
cdatemdateMy content2
' } - ], plugins: [ 'autosave', 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'codesample', 'help' + 'save', 'table', 'directionality', 'emoticons', 'codesample', 'help' ] }; diff --git a/modules/tinymce/src/core/demo/ts/demo/ResponsiveDemo.ts b/modules/tinymce/src/core/demo/ts/demo/ResponsiveDemo.ts index e23af75fea..421877c83b 100644 --- a/modules/tinymce/src/core/demo/ts/demo/ResponsiveDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/ResponsiveDemo.ts @@ -58,12 +58,6 @@ export default (): void => { callback('movie.mp4', { source2: 'alt.ogg', poster: 'https://www.google.com/logos/google.jpg' }); } }, - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content' }, - { title: 'Some title 2', description: 'Some desc 2', content: '
cdatemdateMy content2
' } - ], - template_cdate_format: '[CDATE: %m/%d/%Y : %H:%M:%S]', - template_mdate_format: '[MDATE: %m/%d/%Y : %H:%M:%S]', image_caption: true, theme: 'silver', mobile: { @@ -80,7 +74,7 @@ export default (): void => { plugins: [ 'fullscreen', 'help', 'autosave', 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'importcss', 'codesample', 'help' + 'save', 'table', 'directionality', 'emoticons', 'importcss', 'codesample', 'help' ], // rtl_ui: true, add_unload_trigger: false, diff --git a/modules/tinymce/src/core/demo/ts/demo/SourceDumpDemo.ts b/modules/tinymce/src/core/demo/ts/demo/SourceDumpDemo.ts index 34f459f872..f07c3878e2 100644 --- a/modules/tinymce/src/core/demo/ts/demo/SourceDumpDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/SourceDumpDemo.ts @@ -9,15 +9,11 @@ export default (): void => { selector: 'textarea#editor', skin_url: '../../../../js/tinymce/skins/ui/oxide', content_css: '../../../../js/tinymce/skins/content/default/content.css', - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content' }, - { title: 'Some title 2', description: 'Some desc 2', content: '
cdatemdateMy content2
' } - ], image_caption: true, plugins: [ 'autosave', 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'importcss', 'codesample', 'help' + 'save', 'table', 'directionality', 'emoticons', 'importcss', 'codesample', 'help' ], add_unload_trigger: false, autosave_ask_before_unload: false, diff --git a/modules/tinymce/src/core/demo/ts/demo/StickyToolbarDemo.ts b/modules/tinymce/src/core/demo/ts/demo/StickyToolbarDemo.ts index dc4891f29f..9b47460b3a 100644 --- a/modules/tinymce/src/core/demo/ts/demo/StickyToolbarDemo.ts +++ b/modules/tinymce/src/core/demo/ts/demo/StickyToolbarDemo.ts @@ -62,12 +62,6 @@ export default (): void => { callback('movie.mp4', { source2: 'alt.ogg', poster: 'https://www.google.com/logos/google.jpg' }); } }, - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content' }, - { title: 'Some title 2', description: 'Some desc 2', content: '
cdatemdateMy content2
' } - ], - template_cdate_format: '[CDATE: %m/%d/%Y : %H:%M:%S]', - template_mdate_format: '[MDATE: %m/%d/%Y : %H:%M:%S]', image_caption: true, theme: 'silver', setup: (ed) => { @@ -76,7 +70,7 @@ export default (): void => { plugins: [ 'autosave', 'advlist', 'autolink', 'link', 'image', 'lists', 'charmap', 'preview', 'anchor', 'pagebreak', 'searchreplace', 'wordcount', 'visualblocks', 'visualchars', 'code', 'fullscreen', 'insertdatetime', 'media', 'nonbreaking', - 'save', 'table', 'directionality', 'emoticons', 'template', 'importcss', 'codesample', 'help' + 'save', 'table', 'directionality', 'emoticons', 'importcss', 'codesample', 'help' ], // rtl_ui: true, add_unload_trigger: false, diff --git a/modules/tinymce/src/plugins/help/main/ts/data/PluginUrls.ts b/modules/tinymce/src/plugins/help/main/ts/data/PluginUrls.ts index 1964efa580..cf04d91e17 100644 --- a/modules/tinymce/src/plugins/help/main/ts/data/PluginUrls.ts +++ b/modules/tinymce/src/plugins/help/main/ts/data/PluginUrls.ts @@ -46,7 +46,6 @@ const urls = Arr.map([ { key: 'save', name: 'Save' }, { key: 'searchreplace', name: 'Search and Replace' }, { key: 'table', name: 'Table' }, - { key: 'template', name: 'Template' }, { key: 'textcolor', name: 'Text Color' }, { key: 'visualblocks', name: 'Visual Blocks' }, { key: 'visualchars', name: 'Visual Characters' }, diff --git a/modules/tinymce/src/plugins/template/demo/html/demo.html b/modules/tinymce/src/plugins/template/demo/html/demo.html deleted file mode 100644 index b5d92bca22..0000000000 --- a/modules/tinymce/src/plugins/template/demo/html/demo.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - Plugin: template Demo Page - - - -

Plugin: template Demo Page

-
- -
- - - - diff --git a/modules/tinymce/src/plugins/template/demo/html/development.html b/modules/tinymce/src/plugins/template/demo/html/development.html deleted file mode 100644 index 27acc23eee..0000000000 --- a/modules/tinymce/src/plugins/template/demo/html/development.html +++ /dev/null @@ -1,13 +0,0 @@ - -
- - - - - - - - - -
Header 1Header 2
Cell 1Cell 2
-
diff --git a/modules/tinymce/src/plugins/template/demo/ts/demo/Demo.ts b/modules/tinymce/src/plugins/template/demo/ts/demo/Demo.ts deleted file mode 100644 index c3649f1398..0000000000 --- a/modules/tinymce/src/plugins/template/demo/ts/demo/Demo.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { TinyMCE } from 'tinymce/core/api/PublicApi'; - -declare let tinymce: TinyMCE; - -tinymce.init({ - selector: 'textarea.tinymce', - plugins: 'template', - toolbar: 'template', - height: 600, - template_preview_replace_values: { - username: 'username here' - }, - template_replace_values: { - username: 'Jack', - staffid: '991234' - }, - templates: [ - { title: 'Some title 1', description: 'Some desc 1', content: 'My content {$username}' }, - { title: 'Some title 2', description: 'Some desc 2', content: 'My other content' }, - { title: 'Some remote file', description: 'Some desc 3', url: 'development.html' }, - { title: 'Nonexistent remote file', description: 'Some desc 4', url: 'invalid.html' } - ] -}); - -export {}; diff --git a/modules/tinymce/src/plugins/template/main/ts/Main.ts b/modules/tinymce/src/plugins/template/main/ts/Main.ts deleted file mode 100644 index 80d85c0581..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/Main.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Plugin from './Plugin'; - -Plugin(); - -/** ***** - * DO NOT EXPORT ANYTHING - * - * IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE - *******/ diff --git a/modules/tinymce/src/plugins/template/main/ts/Plugin.ts b/modules/tinymce/src/plugins/template/main/ts/Plugin.ts deleted file mode 100644 index 5d1d5357ee..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/Plugin.ts +++ /dev/null @@ -1,15 +0,0 @@ -import PluginManager from 'tinymce/core/api/PluginManager'; - -import * as Commands from './api/Commands'; -import * as Options from './api/Options'; -import * as FilterContent from './core/FilterContent'; -import * as Buttons from './ui/Buttons'; - -export default (): void => { - PluginManager.add('template', (editor) => { - Options.register(editor); - Buttons.register(editor); - Commands.register(editor); - FilterContent.setup(editor); - }); -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/api/Commands.ts b/modules/tinymce/src/plugins/template/main/ts/api/Commands.ts deleted file mode 100644 index 3a7591c614..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/api/Commands.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Fun } from '@ephox/katamari'; - -import Editor from 'tinymce/core/api/Editor'; - -import * as Templates from '../core/Templates'; -import { ExternalTemplate } from '../core/Types'; -import * as Dialog from '../ui/Dialog'; - -const showDialog = (editor: Editor) => (templates: ExternalTemplate[]): void => { - Dialog.open(editor, templates); -}; - -const register = (editor: Editor): void => { - editor.addCommand('mceInsertTemplate', Fun.curry(Templates.insertTemplate, editor)); - editor.addCommand('mceTemplate', Templates.createTemplateList(editor, showDialog(editor))); -}; - -export { - register -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/api/Options.ts b/modules/tinymce/src/plugins/template/main/ts/api/Options.ts deleted file mode 100644 index 0e1af04583..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/api/Options.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Type } from '@ephox/katamari'; - -import Editor from 'tinymce/core/api/Editor'; -import { EditorOptions } from 'tinymce/core/api/OptionTypes'; - -import { ExternalTemplate, TemplateValues } from '../core/Types'; - -type TemplateCallback = (callback: (templates: ExternalTemplate[]) => void) => void; - -const option: { - (name: K): (editor: Editor) => EditorOptions[K]; - (name: string): (editor: Editor) => T; -} = (name: string) => (editor: Editor) => - editor.options.get(name); - -const register = (editor: Editor): void => { - const registerOption = editor.options.register; - - registerOption('template_cdate_classes', { - processor: 'string', - default: 'cdate' - }); - - registerOption('template_mdate_classes', { - processor: 'string', - default: 'mdate' - }); - - registerOption('template_selected_content_classes', { - processor: 'string', - default: 'selcontent' - }); - - registerOption('template_preview_replace_values', { - processor: 'object' - }); - - registerOption('template_replace_values', { - processor: 'object' - }); - - registerOption('templates', { - processor: (value) => Type.isString(value) || Type.isArrayOf(value, Type.isObject) || Type.isFunction(value), - default: [] - }); - - registerOption('template_cdate_format', { - processor: 'string', - default: editor.translate('%Y-%m-%d') - }); - - registerOption('template_mdate_format', { - processor: 'string', - default: editor.translate('%Y-%m-%d') - }); -}; - -const getCreationDateClasses = option('template_cdate_classes'); -const getModificationDateClasses = option('template_mdate_classes'); -const getSelectedContentClasses = option('template_selected_content_classes'); -const getPreviewReplaceValues = option('template_preview_replace_values'); -const getTemplateReplaceValues = option('template_replace_values'); -const getTemplates = option('templates'); -const getCdateFormat = option('template_cdate_format'); -const getMdateFormat = option('template_mdate_format'); -const getContentStyle = option('content_style'); -const shouldUseContentCssCors = option('content_css_cors'); -const getBodyClass = option('body_class'); - -export { - register, - getCreationDateClasses, - getModificationDateClasses, - getSelectedContentClasses, - getPreviewReplaceValues, - getTemplateReplaceValues, - getTemplates, - getCdateFormat, - getMdateFormat, - getBodyClass, - getContentStyle, - shouldUseContentCssCors -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/core/DateTimeHelper.ts b/modules/tinymce/src/plugins/template/main/ts/core/DateTimeHelper.ts deleted file mode 100644 index fae326ed6c..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/core/DateTimeHelper.ts +++ /dev/null @@ -1,43 +0,0 @@ -import Editor from 'tinymce/core/api/Editor'; - -const addZeros = (value: string | number, len: number): string => { - value = '' + value; - - if (value.length < len) { - for (let i = 0; i < (len - value.length); i++) { - value = '0' + value; - } - } - - return value; -}; - -const getDateTime = (editor: Editor, fmt: string, date: Date = new Date()): string => { - const daysShort = 'Sun Mon Tue Wed Thu Fri Sat Sun'.split(' '); - const daysLong = 'Sunday Monday Tuesday Wednesday Thursday Friday Saturday Sunday'.split(' '); - const monthsShort = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'.split(' '); - const monthsLong = 'January February March April May June July August September October November December'.split(' '); - - fmt = fmt.replace('%D', '%m/%d/%Y'); - fmt = fmt.replace('%r', '%I:%M:%S %p'); - fmt = fmt.replace('%Y', '' + date.getFullYear()); - fmt = fmt.replace('%y', '' + (date as any).getYear()); - fmt = fmt.replace('%m', addZeros(date.getMonth() + 1, 2)); - fmt = fmt.replace('%d', addZeros(date.getDate(), 2)); - fmt = fmt.replace('%H', '' + addZeros(date.getHours(), 2)); - fmt = fmt.replace('%M', '' + addZeros(date.getMinutes(), 2)); - fmt = fmt.replace('%S', '' + addZeros(date.getSeconds(), 2)); - fmt = fmt.replace('%I', '' + ((date.getHours() + 11) % 12 + 1)); - fmt = fmt.replace('%p', '' + (date.getHours() < 12 ? 'AM' : 'PM')); - fmt = fmt.replace('%B', '' + editor.translate(monthsLong[date.getMonth()])); - fmt = fmt.replace('%b', '' + editor.translate(monthsShort[date.getMonth()])); - fmt = fmt.replace('%A', '' + editor.translate(daysLong[date.getDay()])); - fmt = fmt.replace('%a', '' + editor.translate(daysShort[date.getDay()])); - fmt = fmt.replace('%%', '%'); - - return fmt; -}; - -export { - getDateTime -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/core/FilterContent.ts b/modules/tinymce/src/plugins/template/main/ts/core/FilterContent.ts deleted file mode 100644 index 2b0bb75991..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/core/FilterContent.ts +++ /dev/null @@ -1,29 +0,0 @@ -import Editor from 'tinymce/core/api/Editor'; -import Tools from 'tinymce/core/api/util/Tools'; - -import * as Options from '../api/Options'; -import * as DateTimeHelper from './DateTimeHelper'; -import * as Templates from './Templates'; -import { hasAnyClasses } from './Utils'; - -const setup = (editor: Editor): void => { - editor.on('PreProcess', (o) => { - const dom = editor.dom, dateFormat = Options.getMdateFormat(editor); - - Tools.each(dom.select('div', o.node), (e) => { - if (dom.hasClass(e, 'mceTmpl')) { - Tools.each(dom.select('*', e), (e) => { - if (hasAnyClasses(dom, e, Options.getModificationDateClasses(editor))) { - e.innerHTML = DateTimeHelper.getDateTime(editor, dateFormat); - } - }); - - Templates.replaceVals(editor, e); - } - }); - }); -}; - -export { - setup -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/core/Templates.ts b/modules/tinymce/src/plugins/template/main/ts/core/Templates.ts deleted file mode 100644 index 029b4d30e7..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/core/Templates.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { Regex, Type } from '@ephox/katamari'; - -import Editor from 'tinymce/core/api/Editor'; -import Tools from 'tinymce/core/api/util/Tools'; - -import * as Options from '../api/Options'; -import * as DateTimeHelper from './DateTimeHelper'; -import { ExternalTemplate, TemplateValues } from './Types'; -import { hasAnyClasses, parseAndSerialize } from './Utils'; - -const createTemplateList = (editor: Editor, callback: (templates: ExternalTemplate[]) => void) => { - return (): void => { - const templateList = Options.getTemplates(editor); - - if (Type.isFunction(templateList)) { - templateList(callback); - } else if (Type.isString(templateList)) { - fetch(templateList) - .then((res) => { - if (res.ok) { - res.json().then(callback); - } - }); - } else { - callback(templateList); - } - }; -}; - -const replaceTemplateValues = (html: string, templateValues: TemplateValues | undefined): string => { - Tools.each(templateValues, (v, k) => { - if (Type.isFunction(v)) { - v = v(k); - } - - html = html.replace(new RegExp('\\{\\$' + Regex.escape(k) + '\\}', 'g'), v); - }); - - return html; -}; - -const replaceVals = (editor: Editor, scope: HTMLElement): void => { - const dom = editor.dom, vl = Options.getTemplateReplaceValues(editor); - - Tools.each(dom.select('*', scope), (e) => { - Tools.each(vl, (v, k) => { - if (dom.hasClass(e, k)) { - if (Type.isFunction(v)) { - // TODO: TINY-7792: Investigate as this appears to be a bug as "replaceTemplateValues" above uses - // the same values here and it expects a string and return value so this is not compatible. - v(e as any); - } - } - }); - }); -}; - -const insertTemplate = (editor: Editor, _ui: boolean, html: string): void => { - // Note: ui is unused here but is required since this can be called by execCommand - const dom = editor.dom; - const sel = editor.selection.getContent(); - - html = replaceTemplateValues(html, Options.getTemplateReplaceValues(editor)); - let el = dom.create('div', {}, parseAndSerialize(editor, html)); - - // Find template element within div - const n = dom.select('.mceTmpl', el); - if (n && n.length > 0) { - el = dom.create('div'); - el.appendChild(n[0].cloneNode(true)); - } - - Tools.each(dom.select('*', el), (n) => { - // Replace cdate - if (hasAnyClasses(dom, n, Options.getCreationDateClasses(editor))) { - n.innerHTML = DateTimeHelper.getDateTime(editor, Options.getCdateFormat(editor)); - } - - // Replace mdate - if (hasAnyClasses(dom, n, Options.getModificationDateClasses(editor))) { - n.innerHTML = DateTimeHelper.getDateTime(editor, Options.getMdateFormat(editor)); - } - - // Replace selection - if (hasAnyClasses(dom, n, Options.getSelectedContentClasses(editor))) { - n.innerHTML = sel; - } - }); - - replaceVals(editor, el); - - editor.execCommand('mceInsertContent', false, el.innerHTML); - editor.addVisual(); -}; - -export { - createTemplateList, - replaceTemplateValues, - replaceVals, - insertTemplate -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/core/Types.ts b/modules/tinymce/src/plugins/template/main/ts/core/Types.ts deleted file mode 100644 index 8e6e3c8b53..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/core/Types.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Optional } from '@ephox/katamari'; - -export interface UrlTemplate { - readonly title: string; - readonly description: string; - readonly url: string; -} - -export interface ContentTemplate { - readonly title: string; - readonly description: string; - readonly content: string; -} - -export type ExternalTemplate = UrlTemplate | ContentTemplate; - -export interface InternalTemplate { - readonly selected: boolean; - readonly text: string; - readonly value: { - readonly url: Optional; - readonly content: Optional; - readonly description: string; - }; -} - -export interface DialogData { - readonly template: string; - readonly preview: string; -} - -export type TemplateValues = Record string)>; diff --git a/modules/tinymce/src/plugins/template/main/ts/core/Utils.ts b/modules/tinymce/src/plugins/template/main/ts/core/Utils.ts deleted file mode 100644 index 67654f5403..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/core/Utils.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Arr, Obj } from '@ephox/katamari'; - -import DOMUtils from 'tinymce/core/api/dom/DOMUtils'; -import Editor from 'tinymce/core/api/Editor'; -import HtmlSerializer from 'tinymce/core/api/html/Serializer'; - -const entitiesAttr: Record = { - '"': '"', - '<': '<', - '>': '>', - '&': '&', - '\'': ''' -}; - -const htmlEscape = (html: string): string => - html.replace(/["'<>&]/g, (match) => Obj.get(entitiesAttr, match).getOr(match)); - -const hasAnyClasses = (dom: DOMUtils, n: Element, classes: string): boolean => - Arr.exists(classes.split(/\s+/), (c) => dom.hasClass(n, c)); - -const parseAndSerialize = (editor: Editor, html: string): string => - HtmlSerializer({ validate: true }, editor.schema).serialize( - editor.parser.parse(html, { insert: true }) - ); - -export { - hasAnyClasses, - htmlEscape, - parseAndSerialize -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/ui/Buttons.ts b/modules/tinymce/src/plugins/template/main/ts/ui/Buttons.ts deleted file mode 100644 index 4b575ab8cf..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/ui/Buttons.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Editor from 'tinymce/core/api/Editor'; -import { Menu, Toolbar } from 'tinymce/core/api/ui/Ui'; - -const onSetupEditable = (editor: Editor) => (api: Toolbar.ToolbarButtonInstanceApi | Menu.MenuItemInstanceApi): VoidFunction => { - const nodeChanged = () => { - api.setEnabled(editor.selection.isEditable()); - }; - - editor.on('NodeChange', nodeChanged); - nodeChanged(); - - return () => { - editor.off('NodeChange', nodeChanged); - }; -}; - -const register = (editor: Editor): void => { - const onAction = () => editor.execCommand('mceTemplate'); - - editor.ui.registry.addButton('template', { - icon: 'template', - tooltip: 'Insert template', - onSetup: onSetupEditable(editor), - onAction - }); - - editor.ui.registry.addMenuItem('template', { - icon: 'template', - text: 'Insert template...', - onSetup: onSetupEditable(editor), - onAction - }); -}; - -export { - register -}; diff --git a/modules/tinymce/src/plugins/template/main/ts/ui/Dialog.ts b/modules/tinymce/src/plugins/template/main/ts/ui/Dialog.ts deleted file mode 100644 index 0fc9a5286c..0000000000 --- a/modules/tinymce/src/plugins/template/main/ts/ui/Dialog.ts +++ /dev/null @@ -1,220 +0,0 @@ -import { Arr, Optional } from '@ephox/katamari'; - -import Editor from 'tinymce/core/api/Editor'; -import Env from 'tinymce/core/api/Env'; -import { Dialog } from 'tinymce/core/api/ui/Ui'; -import Tools from 'tinymce/core/api/util/Tools'; - -import * as Options from '../api/Options'; -import * as Templates from '../core/Templates'; -import { DialogData, ExternalTemplate, InternalTemplate, UrlTemplate } from '../core/Types'; -import * as Utils from '../core/Utils'; - -type UpdateDialogCallback = (dialogApi: Dialog.DialogInstanceApi, template: InternalTemplate, previewHtml: string) => void; - -const getPreviewContent = (editor: Editor, html: string): string => { - let previewHtml = Utils.parseAndSerialize(editor, html); - if (html.indexOf('') === -1) { - let contentCssEntries = ''; - const contentStyle = Options.getContentStyle(editor) ?? ''; - - const cors = Options.shouldUseContentCssCors(editor) ? ' crossorigin="anonymous"' : ''; - - Tools.each(editor.contentCSS, (url) => { - contentCssEntries += ''; - }); - - if (contentStyle) { - contentCssEntries += ''; - } - - const bodyClass = Options.getBodyClass(editor); - - const encode = editor.dom.encode; - - const isMetaKeyPressed = Env.os.isMacOS() || Env.os.isiOS() ? 'e.metaKey' : 'e.ctrlKey && !e.altKey'; - - const preventClicksOnLinksScript = ( - ' ' - ); - - const directionality = editor.getBody().dir; - const dirAttr = directionality ? ' dir="' + encode(directionality) + '"' : ''; - - previewHtml = ( - '' + - '' + - '' + - '' + - contentCssEntries + - preventClicksOnLinksScript + - '' + - '' + - previewHtml + - '' + - '' - ); - } - - return Templates.replaceTemplateValues(previewHtml, Options.getPreviewReplaceValues(editor)); -}; - -const open = (editor: Editor, templateList: ExternalTemplate[]): void => { - const createTemplates = (): Optional> => { - if (!templateList || templateList.length === 0) { - const message = editor.translate('No templates defined.'); - editor.notificationManager.open({ text: message, type: 'info' }); - return Optional.none(); - } - - return Optional.from(Tools.map(templateList, (template: ExternalTemplate, index) => { - const isUrlTemplate = (t: ExternalTemplate): t is UrlTemplate => (t as UrlTemplate).url !== undefined; - return { - selected: index === 0, - text: template.title, - value: { - url: isUrlTemplate(template) ? Optional.from(template.url) : Optional.none(), - content: !isUrlTemplate(template) ? Optional.from(template.content) : Optional.none(), - description: template.description - } - }; - })); - }; - - const createSelectBoxItems = (templates: InternalTemplate[]) => Arr.map(templates, (t) => ({ - text: t.text, - value: t.text - })); - - const findTemplate = (templates: InternalTemplate[], templateTitle: string) => Arr.find(templates, (t) => t.text === templateTitle); - - const loadFailedAlert = (api: Dialog.DialogInstanceApi) => { - editor.windowManager.alert('Could not load the specified template.', () => api.focus('template')); - }; - - const getTemplateContent = (t: InternalTemplate): Promise => - t.value.url.fold( - () => Promise.resolve(t.value.content.getOr('')), - (url) => fetch(url).then((res) => res.ok ? res.text() : Promise.reject()) - ); - - const onChange = (templates: InternalTemplate[], updateDialog: UpdateDialogCallback) => - (api: Dialog.DialogInstanceApi, change: { name: string }) => { - if (change.name === 'template') { - const newTemplateTitle = api.getData().template; - findTemplate(templates, newTemplateTitle).each((t) => { - api.block('Loading...'); - getTemplateContent(t).then((previewHtml) => { - updateDialog(api, t, previewHtml); - }).catch(() => { - updateDialog(api, t, ''); - api.setEnabled('save', false); - loadFailedAlert(api); - }); - }); - } - }; - - const onSubmit = (templates: InternalTemplate[]) => (api: Dialog.DialogInstanceApi) => { - const data = api.getData(); - findTemplate(templates, data.template).each((t) => { - getTemplateContent(t).then((previewHtml) => { - editor.execCommand('mceInsertTemplate', false, previewHtml); - api.close(); - }).catch(() => { - api.setEnabled('save', false); - loadFailedAlert(api); - }); - }); - }; - - const openDialog = (templates: InternalTemplate[]) => { - const selectBoxItems = createSelectBoxItems(templates); - - const buildDialogSpec = (bodyItems: Dialog.BodyComponentSpec[], initialData: DialogData): Dialog.DialogSpec => ({ - title: 'Insert Template', - size: 'large', - body: { - type: 'panel', - items: bodyItems - }, - initialData, - buttons: [ - { - type: 'cancel', - name: 'cancel', - text: 'Cancel' - }, - { - type: 'submit', - name: 'save', - text: 'Save', - primary: true - } - ], - onSubmit: onSubmit(templates), - onChange: onChange(templates, updateDialog) - }); - - const updateDialog = (dialogApi: Dialog.DialogInstanceApi, template: InternalTemplate, previewHtml: string) => { - const content = getPreviewContent(editor, previewHtml); - const bodyItems: Dialog.BodyComponentSpec[] = [ - { - type: 'listbox', - name: 'template', - label: 'Templates', - items: selectBoxItems - }, - { - type: 'htmlpanel', - html: `

${Utils.htmlEscape(template.value.description)}

` - }, - { - label: 'Preview', - type: 'iframe', - name: 'preview', - sandboxed: false, - transparent: false - } - ]; - - const initialData = { - template: template.text, - preview: content - }; - - dialogApi.unblock(); - dialogApi.redial(buildDialogSpec(bodyItems, initialData)); - dialogApi.focus('template'); - }; - - const dialogApi = editor.windowManager.open(buildDialogSpec([], { template: '', preview: '' })); - dialogApi.block('Loading...'); - - getTemplateContent(templates[0]).then((previewHtml) => { - updateDialog(dialogApi, templates[0], previewHtml); - }).catch(() => { - updateDialog(dialogApi, templates[0], ''); - dialogApi.setEnabled('save', false); - loadFailedAlert(dialogApi); - }); - }; - - const optTemplates: Optional = createTemplates(); - optTemplates.each(openDialog); -}; - -export { - open, - getPreviewContent -}; diff --git a/modules/tinymce/src/plugins/template/test/html/test_template.html b/modules/tinymce/src/plugins/template/test/html/test_template.html deleted file mode 100644 index 3112d8ec02..0000000000 --- a/modules/tinymce/src/plugins/template/test/html/test_template.html +++ /dev/null @@ -1 +0,0 @@ -

this is external

\ No newline at end of file diff --git a/modules/tinymce/src/plugins/template/test/ts/browser/DatesTest.ts b/modules/tinymce/src/plugins/template/test/ts/browser/DatesTest.ts deleted file mode 100644 index ee53f5b226..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/browser/DatesTest.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { afterEach, describe, it } from '@ephox/bedrock-client'; -import { TinyAssertions, TinyHooks, TinySelections } from '@ephox/wrap-mcagar'; - -import Editor from 'tinymce/core/api/Editor'; -import Plugin from 'tinymce/plugins/template/Plugin'; - -import { pInsertTemplate } from '../module/InsertTemplate'; -import { Settings } from '../module/Settings'; - -describe('browser.tinymce.plugins.template.DatesTest', () => { - const hook = TinyHooks.bddSetupLight({ - plugins: 'template', - toolbar: 'template', - base_url: '/project/tinymce/js/tinymce' - }, [ Plugin ]); - - const { addSettings, cleanupSettings } = Settings(hook); - - afterEach(() => { - const editor = hook.editor(); - cleanupSettings(); - editor.setContent(''); - }); - - it('TBA: Test cdate in snippet with default class', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', content: '

x

' }], - template_cdate_format: 'fake date', - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, '

fake date

'); - }); - - it('TBA: Test cdate in snippet with custom class', async () => { - const editor = hook.editor(); - addSettings({ - template_cdate_classes: 'customCdateClass', - templates: [{ title: 'a', description: 'b', content: '

x

' }], - template_cdate_format: 'fake date' - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, - '

fake date

' - ); - }); - - it('TBA: Test mdate updates with each serialization', async () => { - const editor = hook.editor(); - addSettings({ - template_mdate_format: 'fake modified date', - template_cdate_format: 'fake created date', - templates: [{ title: 'a', description: 'b', content: '

' }] - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, [ - '
', - '

fake modified date

', - '

fake created date

', - '
' - ].join('\n')); - addSettings({ template_mdate_format: 'changed modified date' }); - TinyAssertions.assertContent(editor, [ - '
', - '

changed modified date

', - '

fake created date

', - '
' - ].join('\n')); - }); - - it('TBA: Test mdate updates with each serialization with custom class', async () => { - const editor = hook.editor(); - addSettings({ - template_mdate_classes: 'modified', - template_mdate_format: 'fake modified date', - template_cdate_format: 'fake created date', - templates: [{ title: 'a', description: 'b', content: '

' }] - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, [ - '
', - '

fake modified date

', - '

fake created date

', - '
' - ].join('\n')); - addSettings({ template_mdate_format: 'changed modified date' }); - TinyAssertions.assertContent(editor, [ - '
', - '

changed modified date

', - '

fake created date

', - '
' - ].join('\n')); - }); - - it('TBA: Multiple replacement classes provided via options', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', content: '

x

y

' }], - template_cdate_classes: 'cdate1 cdate2', - template_cdate_format: 'fake created date', - template_mdate_classes: 'mdate1 mdate2', - template_mdate_format: 'fake modified date', - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, [ - '
', - '

fake created date

', - '

fake modified date

', - '
' - ].join('\n')); - TinySelections.setCursor(editor, [ 0, 1, 0 ], 'fake modified date'.length); - editor.insertContent('

inserted modified date

'); - addSettings({ template_mdate_format: 'changed modified date' }); - TinyAssertions.assertContent(editor, [ - '
', - '

fake created date

', - '

changed modified date

', - '

changed modified date

', - '
' - ].join('\n')); - }); - - it('TINY-7433: replacement classes with regex like names', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', content: '

x

y

' }], - template_cdate_classes: 'custom+cdate', - template_cdate_format: 'fake created date', - template_mdate_classes: 'custom+mdate', - template_mdate_format: 'fake modified date', - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, [ - '
', - '

fake created date

', - '

fake modified date

', - '
' - ].join('\n')); - }); -}); diff --git a/modules/tinymce/src/plugins/template/test/ts/browser/DialogGetPreviewContentTest.ts b/modules/tinymce/src/plugins/template/test/ts/browser/DialogGetPreviewContentTest.ts deleted file mode 100644 index 32044da46d..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/browser/DialogGetPreviewContentTest.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { afterEach, describe, it } from '@ephox/bedrock-client'; -import { TinyHooks } from '@ephox/wrap-mcagar'; -import { assert } from 'chai'; - -import Editor from 'tinymce/core/api/Editor'; -import Env from 'tinymce/core/api/Env'; -import Plugin from 'tinymce/plugins/template/Plugin'; -import { getPreviewContent } from 'tinymce/plugins/template/ui/Dialog'; - -import { Settings } from '../module/Settings'; - -const metaKey = Env.os.isMacOS() || Env.os.isiOS() ? 'e.metaKey' : 'e.ctrlKey && !e.altKey'; -const host = document.location.host; - -const noCorsNoStyle = '' + - `` + - `` + - `` + - ' ' + - ''; - -const corsNoStyle = '' + - `` + - `` + - `` + - ' '; - -const noCorsStyle = '' + - `` + - `` + - `` + - '' + - ' ' + - ''; - -const corsStyle = '' + - `` + - `` + - `` + - '' + - ' ' + - ''; - -const corsStyleAndContent = '' + - `` + - `` + - `` + - '' + - ' ' + - '' + - '

Custom content which was provided

'; - -// TODO TINY-10480: Investigate flaky tests -describe.skip('browser.tinymce.plugins.template.Dialog.getPreviewContent', () => { - const hook = TinyHooks.bddSetupLight({ - plugins: 'template', - base_url: '/project/tinymce/js/tinymce' - }, [ Plugin ]); - - const checkPreview = (expected: string, html: string = '') => { - const editor = hook.editor(); - assert.equal(getPreviewContent(editor, html), expected); - }; - - const { addSettings, cleanupSettings } = Settings(hook); - - afterEach(() => { - cleanupSettings(); - }); - - it('TINY-6115: Dialog.getPreviewContent: No CORS or content style, no previous HTML', () => { - checkPreview(noCorsNoStyle); - }); - - it('TINY-6115: Dialog.getPreviewContent: CORS but no content style, no previous HTML', () => { - addSettings({ content_css_cors: true }); - checkPreview(corsNoStyle); - }); - - it('TINY-6115: Dialog.getPreviewcontent: No CORS but content style, no previous HTML', () => { - addSettings({ content_style: 'This is the style inserted into the document' }); - checkPreview(noCorsStyle); - }); - - it('TINY-6115: Dialog.getPreviewContent: No CORS but content style, no previous HTML', () => { - addSettings({ - content_css_cors: true, - content_style: 'This is the style inserted into the document' - }); - checkPreview(corsStyle); - }); - - it('TINY-6115: Dialog.getPreviewContent: with provided content', () => { - addSettings({ - content_css_cors: true, - content_style: 'This is the style inserted into the document' - }); - checkPreview(corsStyleAndContent, 'Custom content which was provided'); - }); - - it('TINY-6115: Dialog.getPreviewContent: with provided html', () => { - addSettings({ - content_css_cors: true, - content_style: 'This is the style inserted into the document' - }); - // TINY-9867: Preview content is parsed to minimise visual discrepancy with inserted content - checkPreview('

Custom content here

', 'Custom content here'); - }); -}); diff --git a/modules/tinymce/src/plugins/template/test/ts/browser/InvalidUrlTest.ts b/modules/tinymce/src/plugins/template/test/ts/browser/InvalidUrlTest.ts deleted file mode 100644 index 87bfd3eab2..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/browser/InvalidUrlTest.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { UiFinder, Waiter } from '@ephox/agar'; -import { describe, it } from '@ephox/bedrock-client'; -import { SugarBody } from '@ephox/sugar'; -import { TinyAssertions, TinyHooks, TinyUiActions } from '@ephox/wrap-mcagar'; - -import Editor from 'tinymce/core/api/Editor'; -import Plugin from 'tinymce/plugins/template/Plugin'; - -const dialogSelector = 'div.tox-dialog'; -const alertDialogSelector = 'div.tox-dialog.tox-alert-dialog'; -const toolbarButtonSelector = '[role="toolbar"] button[aria-label="Insert template"]'; - -describe('browser.tinymce.plugins.template.InvalidUrlTest', () => { - const hook = TinyHooks.bddSetupLight({ - plugins: 'template', - toolbar: 'template', - base_url: '/project/tinymce/js/tinymce' - }, [ Plugin ]); - - it('TBA: Test loading in snippet from file that does not exist', async () => { - const editor = hook.editor(); - editor.setContent(''); - editor.options.set('templates', [{ title: 'invalid', description: 'b', url: '/custom/404' }, { title: 'a', description: 'a', content: 'c' }]); - TinyUiActions.clickOnToolbar(editor, toolbarButtonSelector); - await TinyUiActions.pWaitForDialog(editor); - await TinyUiActions.pWaitForPopup(editor, alertDialogSelector); - // Click on Save button (should be disabled) - TinyUiActions.clickOnUi(editor, 'button.tox-button:contains(OK)'); - await Waiter.pTryUntil('Alert dialog should close', () => UiFinder.notExists(SugarBody.body(), alertDialogSelector)); - TinyUiActions.submitDialog(editor); - await Waiter.pTryUntil('Dialog should not close', () => UiFinder.exists(SugarBody.body(), dialogSelector)); - TinyUiActions.cancelDialog(editor); - await Waiter.pTryUntil('Dialog should close', () => UiFinder.notExists(SugarBody.body(), dialogSelector)); - TinyAssertions.assertContent(editor, ''); - }); -}); diff --git a/modules/tinymce/src/plugins/template/test/ts/browser/NoneditableRootTest.ts b/modules/tinymce/src/plugins/template/test/ts/browser/NoneditableRootTest.ts deleted file mode 100644 index ef32ab7e3d..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/browser/NoneditableRootTest.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Keys, UiFinder } from '@ephox/agar'; -import { describe, it } from '@ephox/bedrock-client'; -import { SugarBody } from '@ephox/sugar'; -import { TinyHooks, TinySelections, TinyState, TinyUiActions } from '@ephox/wrap-mcagar'; - -import Editor from 'tinymce/core/api/Editor'; -import Plugin from 'tinymce/plugins/template/Plugin'; - -describe('browser.tinymce.plugins.template.NoneditableRootTest', () => { - const hook = TinyHooks.bddSetup({ - plugins: 'template', - toolbar: 'template', - base_url: '/project/tinymce/js/tinymce' - }, [ Plugin ], true); - - it('TINY-9669: Disable template button on noneditable content', () => { - TinyState.withNoneditableRootEditor(hook.editor(), (editor) => { - editor.setContent('
Noneditable content
Editable content
'); - TinySelections.setSelection(editor, [ 0, 0 ], 0, [ 0, 0 ], 2); - UiFinder.exists(SugarBody.body(), '[aria-label="Insert template"][aria-disabled="true"]'); - TinySelections.setSelection(editor, [ 1, 0 ], 0, [ 1, 0 ], 2); - UiFinder.exists(SugarBody.body(), '[aria-label="Insert template"][aria-disabled="false"]'); - }); - }); - - it('TINY-9669: Disable template menuitem on noneditable content', async () => { - await TinyState.withNoneditableRootEditorAsync(hook.editor(), async (editor) => { - editor.setContent('
Noneditable content
Editable content
'); - TinySelections.setSelection(editor, [ 0, 0 ], 0, [ 0, 0 ], 2); - TinyUiActions.clickOnMenu(editor, 'button:contains("Insert")'); - await TinyUiActions.pWaitForUi(editor, '[role="menuitem"][aria-label="Insert template..."][aria-disabled="true"]'); - TinyUiActions.keystroke(editor, Keys.escape()); - TinySelections.setSelection(editor, [ 1, 0 ], 0, [ 1, 0 ], 2); - TinyUiActions.clickOnMenu(editor, 'button:contains("Insert")'); - await TinyUiActions.pWaitForUi(editor, '[role="menuitem"][aria-label="Insert template..."][aria-disabled="false"]'); - TinyUiActions.keystroke(editor, Keys.escape()); - }); - }); -}); - diff --git a/modules/tinymce/src/plugins/template/test/ts/browser/SelectedContentTest.ts b/modules/tinymce/src/plugins/template/test/ts/browser/SelectedContentTest.ts deleted file mode 100644 index af1cec696c..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/browser/SelectedContentTest.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { afterEach, describe, it } from '@ephox/bedrock-client'; -import { TinyAssertions, TinyHooks, TinySelections } from '@ephox/wrap-mcagar'; - -import Editor from 'tinymce/core/api/Editor'; -import Plugin from 'tinymce/plugins/template/Plugin'; - -import { pInsertTemplate } from '../module/InsertTemplate'; -import { Settings } from '../module/Settings'; - -describe('browser.tinymce.plugins.template.SelectedContentTest', () => { - const hook = TinyHooks.bddSetupLight({ - plugins: 'template', - toolbar: 'template', - base_url: '/project/tinymce/js/tinymce' - }, [ Plugin ]); - - const { addSettings, cleanupSettings } = Settings(hook); - - afterEach(() => { - const editor = hook.editor(); - cleanupSettings(); - editor.setContent(''); - }); - - it('TBA: Test selected content replacement with default class', async () => { - const editor = hook.editor(); - editor.setContent('Text'); - TinySelections.setSelection(editor, [ 0, 0 ], 0, [ 0, 0 ], 4); - addSettings({ - templates: [{ title: 'a', description: 'b', content: '

This will be replaced

' }], - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, '

Text

'); - }); - - it('TBA: Test selected content replacement with custom class', async () => { - const editor = hook.editor(); - editor.setContent('Text'); - TinySelections.setSelection(editor, [ 0, 0 ], 0, [ 0, 0 ], 4); - addSettings({ - template_selected_content_classes: 'customSelected', - templates: [{ title: 'a', description: 'b', content: '

This will be replaced/h1>' }], - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, '

Text

' - ); - }); -}); diff --git a/modules/tinymce/src/plugins/template/test/ts/browser/TemplateSanityTest.ts b/modules/tinymce/src/plugins/template/test/ts/browser/TemplateSanityTest.ts deleted file mode 100644 index b840723b94..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/browser/TemplateSanityTest.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { UiFinder, Waiter } from '@ephox/agar'; -import { afterEach, beforeEach, context, describe, it } from '@ephox/bedrock-client'; -import { Cell } from '@ephox/katamari'; -import { SugarElement } from '@ephox/sugar'; -import { TinyAssertions, TinyHooks } from '@ephox/wrap-mcagar'; -import { assert } from 'chai'; - -import Editor from 'tinymce/core/api/Editor'; -import Plugin from 'tinymce/plugins/template/Plugin'; - -import { pInsertTemplate, pPreviewTemplate } from '../module/InsertTemplate'; -import { Settings } from '../module/Settings'; - -describe('browser.tinymce.plugins.template.TemplateSanityTest', () => { - const hook = TinyHooks.bddSetupLight({ - plugins: 'template', - toolbar: 'template', - base_url: '/project/tinymce/js/tinymce' - }, [ Plugin ]); - - const { addSettings, cleanupSettings } = Settings(hook); - - beforeEach(() => { - const editor = hook.editor(); - editor.setContent(''); - }); - - afterEach(() => { - cleanupSettings(); - }); - - it('TBA: Test basic template insertion', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', content: 'c' }], - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, '

c

'); - }); - - it('TBA: Test basic content replacement', async () => { - const editor = hook.editor(); - addSettings({ - template_replace_values: { name: 'Tester', email: 'test@test.com' }, - templates: [{ title: 'a', description: 'b', content: '

{$name} {$email}

' }] - }); - await pInsertTemplate(editor); - TinyAssertions.assertContent(editor, '

Tester test@test.com

'); - }); - - it('TBA: Test loading in snippet from other file', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', url: '/project/tinymce/src/plugins/template/test/html/test_template.html' }] - }); - await pInsertTemplate(editor, (dialogEl) => { - UiFinder.exists(dialogEl, 'p:contains("b")'); - }); - TinyAssertions.assertContent(editor, '

this is external

'); - }); - - it('TBA: Test command', () => { - const editor = hook.editor(); - addSettings({ - template_replace_values: { name: 'Tester', email: 'test@test.com' }, - }); - editor.execCommand('mceInsertTemplate', false, '

{$name}

'); - TinyAssertions.assertContent(editor, '

Tester

'); - }); - - it('TINY-7433: Replace template values with regex like keys', () => { - const editor = hook.editor(); - addSettings({ - template_replace_values: { 'first+name': 'Tester', 'email': 'test@test.com' }, - }); - editor.execCommand('mceInsertTemplate', false, '

{$first+name}

'); - TinyAssertions.assertContent(editor, '

Tester

'); - }); - - context('Previewing unparsed content', () => { - const unparsedHtml = ''; - const unparsedPreviewHtmlSelector = 'p > img[src="error"][onerror="throw new Error();"]'; - const parsedPreviewHtmlSelector = 'p > img[src="error"][data-mce-src="error"]'; - - const pPreviewAndAssertNoUnparsedContent = async (editor: Editor): Promise => { - const assertNoUnparsedContent = (dialogEl: SugarElement): void => { - UiFinder.findIn(dialogEl, 'iframe').fold( - () => assert.fail('Preview iframe not found'), - (iframe) => { - const iframeDoc = iframe.dom.contentDocument; - const iframeBody = SugarElement.fromDom(iframeDoc?.body as Node); - UiFinder.exists(iframeBody, parsedPreviewHtmlSelector); - UiFinder.notExists(iframeBody, unparsedPreviewHtmlSelector); - } - ); - }; - - try { - await pPreviewTemplate(editor, assertNoUnparsedContent); - } catch { - assert.fail('Unparsed html read'); - } - }; - - it('TINY-9244: Parsed html should be shown when previewing template', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', content: unparsedHtml }], - }); - await pPreviewAndAssertNoUnparsedContent(editor); - }); - - it('TINY-9867: Parsed html should be shown when previewing template containing tags', async () => { - const editor = hook.editor(); - addSettings({ - templates: [{ title: 'a', description: 'b', content: `${unparsedHtml}` }], - }); - await pPreviewAndAssertNoUnparsedContent(editor); - }); - }); - - context('Inserting unparsed content', () => { - const unparsedHtml = ''; - const assertFnDoesNotReadUnParsedHtmlInDom = async (editor: Editor, fn: (unparsedHtml: string) => void | Promise): Promise => { - const isUnParsedHtmlRead = Cell(false); - (editor.getDoc() as any).unparsedHtmlFn = () => { - isUnParsedHtmlRead.set(true); - }; - await fn(unparsedHtml); - // wait for any unparsed html to be read and error to be thrown if it is - await Waiter.pWait(1); - assert.isFalse(isUnParsedHtmlRead.get(), 'Unparsed html read'); - (editor.getDoc() as any).unparsedHtmlFn = null; - }; - - it('TINY-9244: Unparsed html should not be read when inserting template via command', async () => { - const editor = hook.editor(); - await assertFnDoesNotReadUnParsedHtmlInDom(editor, (unparsedHtml) => { - editor.execCommand('mceInsertTemplate', false, unparsedHtml); - }); - }); - - it('TINY-9244: Unparsed html should not be read when inserting template via dialog', async () => { - const editor = hook.editor(); - addSettings({ templates: [{ title: 'a', description: 'b', content: unparsedHtml }] }); - await assertFnDoesNotReadUnParsedHtmlInDom(editor, async () => { - await pInsertTemplate(editor); - }); - }); - }); -}); diff --git a/modules/tinymce/src/plugins/template/test/ts/module/InsertTemplate.ts b/modules/tinymce/src/plugins/template/test/ts/module/InsertTemplate.ts deleted file mode 100644 index d4e2a8c4fc..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/module/InsertTemplate.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { UiFinder, Waiter } from '@ephox/agar'; -import { Type } from '@ephox/katamari'; -import { SugarBody, SugarElement } from '@ephox/sugar'; -import { TinyUiActions } from '@ephox/wrap-mcagar'; - -import Editor from 'tinymce/core/api/Editor'; - -const toolbarButtonSelector = '[role="toolbar"] button[aria-label="Insert template"]'; -const dialogSelector = 'div.tox-dialog'; - -const waitUntilIframeLoaded = async (dialogEl: SugarElement): Promise => { - await UiFinder.pWaitForState('iframe is loaded', dialogEl, 'iframe', (elm) => { - // fallback for pre-IE 8 using contentWindow.document - const iframeDoc = elm.dom.contentDocument || elm.dom.contentWindow?.document; - return Type.isNonNullable(iframeDoc?.body.firstChild); - }); -}; - -const pUseTemplateDialog = async (editor: Editor, submit: boolean, assertFn?: (elm: SugarElement) => void): Promise => { - TinyUiActions.clickOnToolbar(editor, toolbarButtonSelector); - const dialogEl = await TinyUiActions.pWaitForDialog(editor); - if (Type.isFunction(assertFn)) { - await waitUntilIframeLoaded(dialogEl); - assertFn(dialogEl); - } - if (submit) { - TinyUiActions.submitDialog(editor); - } else { - TinyUiActions.closeDialog(editor); - } - await Waiter.pTryUntil('Dialog should close', () => UiFinder.notExists(SugarBody.body(), dialogSelector)); -}; - -const pInsertTemplate = async (editor: Editor, assertFn?: (elm: SugarElement) => void): Promise => { - await pUseTemplateDialog(editor, true, assertFn); -}; - -const pPreviewTemplate = async (editor: Editor, assertFn?: (elm: SugarElement) => void): Promise => { - await pUseTemplateDialog(editor, false, assertFn); -}; - -export { - pInsertTemplate, - pPreviewTemplate -}; diff --git a/modules/tinymce/src/plugins/template/test/ts/module/Settings.ts b/modules/tinymce/src/plugins/template/test/ts/module/Settings.ts deleted file mode 100644 index 87c34aff9b..0000000000 --- a/modules/tinymce/src/plugins/template/test/ts/module/Settings.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Obj } from '@ephox/katamari'; -import { TinyHooks } from '@ephox/wrap-mcagar'; - -import Editor from 'tinymce/core/api/Editor'; - -interface Settings { - readonly addSettings: (config: Record) => void; - readonly cleanupSettings: () => void; -} - -const Settings = (hook: TinyHooks.Hook): Settings => { - let settings = new Set(); - - const addSettings = (config: Record) => { - const editor = hook.editor(); - Obj.each(config, (val, key) => { - editor.options.set(key, val); - settings.add(key); - }); - }; - - const cleanupSettings = () => { - const editor = hook.editor(); - settings.forEach((key) => editor.options.unset(key)); - settings = new Set(); - }; - - return { addSettings, cleanupSettings }; -}; - -export { Settings }; diff --git a/modules/tinymce/src/themes/silver/main/ts/ui/menus/menubar/Integration.ts b/modules/tinymce/src/themes/silver/main/ts/ui/menus/menubar/Integration.ts index ac8789c3ee..d3f9863613 100644 --- a/modules/tinymce/src/themes/silver/main/ts/ui/menus/menubar/Integration.ts +++ b/modules/tinymce/src/themes/silver/main/ts/ui/menus/menubar/Integration.ts @@ -23,7 +23,7 @@ const defaultMenus: Record = { file: { title: 'File', items: 'newdocument restoredraft | preview | export print | deleteallconversations' }, edit: { title: 'Edit', items: 'undo redo | cut copy paste pastetext | selectall | searchreplace' }, view: { title: 'View', items: 'code revisionhistory | visualaid visualchars visualblocks | spellchecker | preview fullscreen | showcomments' }, - insert: { title: 'Insert', items: 'image link media addcomment pageembed template inserttemplate codesample inserttable accordion | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents footnotes | mergetags | insertdatetime' }, + insert: { title: 'Insert', items: 'image link media addcomment pageembed inserttemplate codesample inserttable accordion | charmap emoticons hr | pagebreak nonbreaking anchor tableofcontents footnotes | mergetags | insertdatetime' }, format: { title: 'Format', items: 'bold italic underline strikethrough superscript subscript codeformat | styles blocks fontfamily fontsize align lineheight | forecolor backcolor | language | removeformat' }, tools: { title: 'Tools', items: 'aidialog aishortcuts | spellchecker spellcheckerlanguage | autocorrect capitalization | a11ycheck code typography wordcount addtemplate' }, table: { title: 'Table', items: 'inserttable | cell row column | advtablesort | tableprops deletetable' }, diff --git a/modules/tinymce/tsconfig.json b/modules/tinymce/tsconfig.json index 3f1c4372cf..fdcf2a0e28 100644 --- a/modules/tinymce/tsconfig.json +++ b/modules/tinymce/tsconfig.json @@ -40,7 +40,6 @@ "tinymce/plugins/save/*": ["src/plugins/save/main/ts/*"], "tinymce/plugins/searchreplace/*": ["src/plugins/searchreplace/main/ts/*"], "tinymce/plugins/table/*": ["src/plugins/table/main/ts/*"], - "tinymce/plugins/template/*": ["src/plugins/template/main/ts/*"], "tinymce/plugins/visualblocks/*": ["src/plugins/visualblocks/main/ts/*"], "tinymce/plugins/visualchars/*": ["src/plugins/visualchars/main/ts/*"], "tinymce/plugins/wordcount/*": ["src/plugins/wordcount/main/ts/*"], diff --git a/tsconfig.demo.json b/tsconfig.demo.json index af790766d0..4f04b3339d 100644 --- a/tsconfig.demo.json +++ b/tsconfig.demo.json @@ -41,7 +41,6 @@ "tinymce/plugins/save/*": ["modules/tinymce/src/plugins/save/main/ts/*"], "tinymce/plugins/searchreplace/*": ["modules/tinymce/src/plugins/searchreplace/main/ts/*"], "tinymce/plugins/table/*": ["modules/tinymce/src/plugins/table/main/ts/*"], - "tinymce/plugins/template/*": ["modules/tinymce/src/plugins/template/main/ts/*"], "tinymce/plugins/visualblocks/*": ["modules/tinymce/src/plugins/visualblocks/main/ts/*"], "tinymce/plugins/visualchars/*": ["modules/tinymce/src/plugins/visualchars/main/ts/*"], "tinymce/plugins/wordcount/*": ["modules/tinymce/src/plugins/wordcount/main/ts/*"], diff --git a/tsconfig.json b/tsconfig.json index 5c3eccd558..6945e5d609 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -63,7 +63,6 @@ "tinymce/plugins/save/*": ["modules/tinymce/src/plugins/save/main/ts/*"], "tinymce/plugins/searchreplace/*": ["modules/tinymce/src/plugins/searchreplace/main/ts/*"], "tinymce/plugins/table/*": ["modules/tinymce/src/plugins/table/main/ts/*"], - "tinymce/plugins/template/*": ["modules/tinymce/src/plugins/template/main/ts/*"], "tinymce/plugins/visualblocks/*": ["modules/tinymce/src/plugins/visualblocks/main/ts/*"], "tinymce/plugins/visualchars/*": ["modules/tinymce/src/plugins/visualchars/main/ts/*"], "tinymce/plugins/wordcount/*": ["modules/tinymce/src/plugins/wordcount/main/ts/*"],