@umbraco-cms/backoffice 14.0.0--preview004-4ed648fc → 14.0.0--preview004-1ec98701

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. package/dist-cms/custom-elements.json +4 -7
  2. package/dist-cms/packages/core/components/input-color/input-color.element.js +4 -1
  3. package/dist-cms/packages/core/components/input-tiny-mce/input-tiny-mce.defaults.d.ts +0 -3
  4. package/dist-cms/packages/core/components/input-tiny-mce/input-tiny-mce.defaults.js +30 -24
  5. package/dist-cms/packages/core/components/input-tiny-mce/input-tiny-mce.element.d.ts +0 -1
  6. package/dist-cms/packages/core/components/input-tiny-mce/input-tiny-mce.element.js +56 -97
  7. package/dist-cms/packages/core/components/input-tiny-mce/input-tiny-mce.handlers.d.ts +0 -1
  8. package/dist-cms/packages/core/components/input-tiny-mce/input-tiny-mce.handlers.js +0 -30
  9. package/dist-cms/packages/core/index.d.ts +1 -0
  10. package/dist-cms/packages/core/index.js +1 -0
  11. package/dist-cms/packages/core/meta/index.d.ts +5 -0
  12. package/dist-cms/packages/core/meta/index.js +6 -0
  13. package/dist-cms/packages/core/property-editor/uis/color-picker/property-editor-ui-color-picker.element.js +1 -0
  14. package/dist-cms/packages/core/property-editor/uis/tiny-mce/plugins/tiny-mce-mediapicker.plugin.js +57 -4
  15. package/dist-cms/packages/core/property-editor/uis/tiny-mce/property-editor-ui-tiny-mce.element.d.ts +5 -1
  16. package/dist-cms/packages/core/property-editor/uis/tiny-mce/property-editor-ui-tiny-mce.element.js +10 -4
  17. package/dist-cms/shared/utils/media-helper.service.d.ts +6 -19
  18. package/dist-cms/shared/utils/media-helper.service.js +11 -35
  19. package/dist-cms/tsconfig.build.tsbuildinfo +1 -1
  20. package/dist-cms/vscode-html-custom-data.json +4 -7
  21. package/package.json +2 -1
@@ -1823,9 +1823,6 @@
1823
1823
  },
1824
1824
  {
1825
1825
  "name": "umb-rte-blur"
1826
- },
1827
- {
1828
- "name": "change"
1829
1826
  }
1830
1827
  ]
1831
1828
  },
@@ -4657,16 +4654,16 @@
4657
4654
  "attributes": [
4658
4655
  {
4659
4656
  "name": "value",
4660
- "type": "string",
4661
- "default": "\"\""
4657
+ "type": "RichTextEditorValue | undefined",
4658
+ "default": "{\"blocks\":{},\"markup\":\"\"}"
4662
4659
  }
4663
4660
  ],
4664
4661
  "properties": [
4665
4662
  {
4666
4663
  "name": "value",
4667
4664
  "attribute": "value",
4668
- "type": "string",
4669
- "default": "\"\""
4665
+ "type": "RichTextEditorValue | undefined",
4666
+ "default": "{\"blocks\":{},\"markup\":\"\"}"
4670
4667
  },
4671
4668
  {
4672
4669
  "name": "config"
@@ -26,7 +26,10 @@ let UmbInputColorElement = class UmbInputColorElement extends FormControlMixin(U
26
26
  }
27
27
  render() {
28
28
  return html `
29
- <uui-color-swatches @change="${this._onChange}" label="Color picker">${this._renderColors()} </uui-color-swatches>
29
+ <uui-color-swatches
30
+ @change="${this._onChange}"
31
+ label="Color picker"
32
+ value=${this.value ?? ''}>${this._renderColors()}</uui-color-swatches>
30
33
  `;
31
34
  }
32
35
  _renderColors() {
@@ -1,5 +1,2 @@
1
1
  import type { RawEditorOptions } from '../../../../external/tinymce/index.js';
2
- export type TinyStyleSheet = RawEditorOptions['style_formats'];
3
- export declare const defaultStyleFormats: TinyStyleSheet;
4
- export declare const defaultExtendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption";
5
2
  export declare const defaultFallbackConfig: RawEditorOptions;
@@ -1,28 +1,11 @@
1
- export const defaultStyleFormats = [
2
- {
3
- title: 'Headers',
4
- items: [
5
- { title: 'Page header', block: 'h2' },
6
- { title: 'Section header', block: 'h3' },
7
- { title: 'Paragraph header', block: 'h4' },
8
- ],
9
- },
10
- {
11
- title: 'Blocks',
12
- items: [{ title: 'Normal', block: 'p' }],
13
- },
14
- {
15
- title: 'Containers',
16
- items: [
17
- { title: 'Quote', block: 'blockquote' },
18
- { title: 'Code', block: 'code' },
19
- ],
20
- },
21
- ];
22
1
  //These are absolutely required in order for the macros to render inline
23
2
  //we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce
24
- export const defaultExtendedValidElements = '@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption';
3
+ //so we don't have to specify all the normal elements again
25
4
  export const defaultFallbackConfig = {
5
+ plugins: ['anchor', 'charmap', 'table', 'lists', 'advlist', 'autolink', 'directionality', 'searchreplace'],
6
+ valid_elements: '+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],-strike[class|style],-s[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],-sub[style|class],-sup[style|class],-blockquote[dir|style|class],-table[border=0|cellspacing|cellpadding|width|height|class|align|summary|style|dir|id|lang|bgcolor|background|bordercolor],-tr[id|lang|dir|class|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor],tbody[id|class],thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|bgcolor|background|bordercolor|scope],-th[id|lang|dir|class|colspan|rowspan|width|height|align|valign|style|scope],caption[id|lang|dir|class|style],-div[id|dir|class|align|style],-span[class|align|style],-pre[class|align|style],address[class|align|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],small[class|style],dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*],figure,figcaption,cite,video[*],audio[*],picture[*],source[*],canvas[*]',
7
+ invalid_elements: 'font',
8
+ extended_valid_elements: '@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style|lang],figure,figcaption',
26
9
  toolbar: [
27
10
  'styles',
28
11
  'bold',
@@ -39,7 +22,30 @@ export const defaultFallbackConfig = {
39
22
  'umbmacro',
40
23
  'umbembeddialog',
41
24
  ],
42
- mode: 'classic',
43
- stylesheets: [],
25
+ style_formats: [
26
+ {
27
+ title: 'Headers',
28
+ items: [
29
+ { title: 'Page header', block: 'h2' },
30
+ { title: 'Section header', block: 'h3' },
31
+ { title: 'Paragraph header', block: 'h4' },
32
+ ],
33
+ },
34
+ {
35
+ title: 'Blocks',
36
+ items: [{ title: 'Normal', block: 'p' }],
37
+ },
38
+ {
39
+ title: 'Containers',
40
+ items: [
41
+ { title: 'Quote', block: 'blockquote' },
42
+ { title: 'Code', block: 'code' },
43
+ ],
44
+ },
45
+ ],
46
+ /**
47
+ * @description The maximum image size in pixels that can be inserted into the editor.
48
+ * @remarks This is registered and used by the UmbMediaPicker plugin
49
+ */
44
50
  maxImageSize: 500,
45
51
  };
@@ -15,7 +15,6 @@ export declare class UmbInputTinyMceElement extends UmbInputTinyMceElement_base
15
15
  /**
16
16
  * Nothing rendered by default - TinyMCE initialisation creates
17
17
  * a target div and binds the RTE to that element
18
- * @returns
19
18
  */
20
19
  render(): import("lit-html").TemplateResult<1>;
21
20
  static styles: import("@lit/reactive-element/css-tag.js").CSSResult[];
@@ -15,13 +15,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
15
15
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
16
16
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
17
17
  };
18
- var _UmbInputTinyMceElement_instances, _UmbInputTinyMceElement_mediaHelper, _UmbInputTinyMceElement_currentUser, _UmbInputTinyMceElement_currentUserContext, _UmbInputTinyMceElement_plugins, _UmbInputTinyMceElement_editorRef, _UmbInputTinyMceElement_stylesheetRepository, _UmbInputTinyMceElement_serverUrl, _UmbInputTinyMceElement_observeCurrentUser, _UmbInputTinyMceElement_loadPlugins, _UmbInputTinyMceElement_setTinyConfig, _UmbInputTinyMceElement_setLanguage, _UmbInputTinyMceElement_editorSetup, _UmbInputTinyMceElement_onInit, _UmbInputTinyMceElement_onChange, _UmbInputTinyMceElement_isMediaPickerEnabled;
19
- import { defaultExtendedValidElements, defaultFallbackConfig, defaultStyleFormats } from './input-tiny-mce.defaults.js';
20
- import { pastePreProcessHandler, uploadImageHandler } from './input-tiny-mce.handlers.js';
18
+ var _UmbInputTinyMceElement_instances, _UmbInputTinyMceElement_mediaHelper, _UmbInputTinyMceElement_plugins, _UmbInputTinyMceElement_editorRef, _UmbInputTinyMceElement_stylesheetRepository, _UmbInputTinyMceElement_serverUrl, _UmbInputTinyMceElement_loadPlugins, _UmbInputTinyMceElement_setTinyConfig, _UmbInputTinyMceElement_setLanguage, _UmbInputTinyMceElement_editorSetup, _UmbInputTinyMceElement_onInit, _UmbInputTinyMceElement_onChange;
19
+ import { defaultFallbackConfig } from './input-tiny-mce.defaults.js';
20
+ import { pastePreProcessHandler } from './input-tiny-mce.handlers.js';
21
21
  import { availableLanguages } from './input-tiny-mce.languages.js';
22
22
  import { uriAttributeSanitizer } from './input-tiny-mce.sanitizer.js';
23
+ import { umbMeta } from '../../meta/index.js';
23
24
  import { FormControlMixin } from '../../../../external/uui/index.js';
24
- import { renderEditor, } from '../../../../external/tinymce/index.js';
25
+ import { renderEditor } from '../../../../external/tinymce/index.js';
25
26
  import { loadManifestApi } from '../../../../libs/extension-api/index.js';
26
27
  import { umbExtensionsRegistry } from '../../extension-registry/index.js';
27
28
  import { css, customElement, html, property, query, state, } from '../../../../external/lit/index.js';
@@ -30,7 +31,7 @@ import { UmbMediaHelper } from '../../../../shared/utils/index.js';
30
31
  import { UmbLitElement } from '../../../../shared/lit-element/index.js';
31
32
  import { UMB_APP_CONTEXT } from '../../../../apps/app/index.js';
32
33
  import { UmbStylesheetRepository } from '../../../templating/stylesheets/index.js';
33
- // TODO => integrate macro picker, update stylesheet fetch when backend CLI exists (ref tinymce.service.js in existing backoffice)
34
+ import { UmbChangeEvent } from '../../event/index.js';
34
35
  let UmbInputTinyMceElement = class UmbInputTinyMceElement extends FormControlMixin(UmbLitElement) {
35
36
  getFormElement() {
36
37
  return this._editorElement?.querySelector('iframe') ?? undefined;
@@ -40,8 +41,6 @@ let UmbInputTinyMceElement = class UmbInputTinyMceElement extends FormControlMix
40
41
  _UmbInputTinyMceElement_instances.add(this);
41
42
  this._tinyConfig = {};
42
43
  _UmbInputTinyMceElement_mediaHelper.set(this, new UmbMediaHelper());
43
- _UmbInputTinyMceElement_currentUser.set(this, void 0);
44
- _UmbInputTinyMceElement_currentUserContext.set(this, void 0);
45
44
  _UmbInputTinyMceElement_plugins.set(this, []);
46
45
  _UmbInputTinyMceElement_editorRef.set(this, null);
47
46
  _UmbInputTinyMceElement_stylesheetRepository.set(this, void 0);
@@ -50,12 +49,6 @@ let UmbInputTinyMceElement = class UmbInputTinyMceElement extends FormControlMix
50
49
  __classPrivateFieldSet(this, _UmbInputTinyMceElement_serverUrl, instance.getServerUrl(), "f");
51
50
  });
52
51
  __classPrivateFieldSet(this, _UmbInputTinyMceElement_stylesheetRepository, new UmbStylesheetRepository(this), "f");
53
- // TODO => this breaks tests, removing for now will ignore user language
54
- // and fall back to tinymce default language
55
- // this.consumeContext(UMB_AUTH, (instance) => {
56
- // this.#auth = instance;
57
- // this.#observeCurrentUser();
58
- // });
59
52
  }
60
53
  async firstUpdated(_changedProperties) {
61
54
  super.firstUpdated(_changedProperties);
@@ -72,17 +65,6 @@ let UmbInputTinyMceElement = class UmbInputTinyMceElement extends FormControlMix
72
65
  async getFormatStyles(stylesheetPath) {
73
66
  const rules = [];
74
67
  stylesheetPath.forEach((path) => {
75
- //TODO => Legacy path?
76
- /**
77
- * if (val.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/") === 0) {
78
- // current format (full path to stylesheet)
79
- stylesheets.push(val);
80
- }
81
- else {
82
- // legacy format (stylesheet name only) - must prefix with stylesheet folder and postfix with ".css"
83
- stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + val + ".css");
84
- }
85
- */
86
68
  __classPrivateFieldGet(this, _UmbInputTinyMceElement_stylesheetRepository, "f")?.getStylesheetRules(path).then(({ data }) => {
87
69
  data?.rules?.forEach((rule) => {
88
70
  const r = {
@@ -120,25 +102,17 @@ let UmbInputTinyMceElement = class UmbInputTinyMceElement extends FormControlMix
120
102
  /**
121
103
  * Nothing rendered by default - TinyMCE initialisation creates
122
104
  * a target div and binds the RTE to that element
123
- * @returns
124
105
  */
125
106
  render() {
126
107
  return html `<div id="editor"></div>`;
127
108
  }
128
109
  };
129
110
  _UmbInputTinyMceElement_mediaHelper = new WeakMap();
130
- _UmbInputTinyMceElement_currentUser = new WeakMap();
131
- _UmbInputTinyMceElement_currentUserContext = new WeakMap();
132
111
  _UmbInputTinyMceElement_plugins = new WeakMap();
133
112
  _UmbInputTinyMceElement_editorRef = new WeakMap();
134
113
  _UmbInputTinyMceElement_stylesheetRepository = new WeakMap();
135
114
  _UmbInputTinyMceElement_serverUrl = new WeakMap();
136
115
  _UmbInputTinyMceElement_instances = new WeakSet();
137
- _UmbInputTinyMceElement_observeCurrentUser = async function _UmbInputTinyMceElement_observeCurrentUser() {
138
- if (!__classPrivateFieldGet(this, _UmbInputTinyMceElement_currentUserContext, "f"))
139
- return;
140
- this.observe(__classPrivateFieldGet(this, _UmbInputTinyMceElement_currentUserContext, "f").currentUser, (currentUser) => (__classPrivateFieldSet(this, _UmbInputTinyMceElement_currentUser, currentUser, "f")));
141
- };
142
116
  _UmbInputTinyMceElement_loadPlugins =
143
117
  /**
144
118
  * Load all custom plugins - need to split loading and instantiating as these
@@ -161,26 +135,47 @@ async function _UmbInputTinyMceElement_loadPlugins() {
161
135
  }
162
136
  };
163
137
  _UmbInputTinyMceElement_setTinyConfig = async function _UmbInputTinyMceElement_setTinyConfig() {
138
+ const dimensions = this.configuration?.getValueByAlias('dimensions');
139
+ // Map the stylesheets with server url
140
+ const stylesheets = this.configuration
141
+ ?.getValueByAlias('stylesheets')
142
+ ?.map((stylesheetPath) => `${__classPrivateFieldGet(this, _UmbInputTinyMceElement_serverUrl, "f")}/css/${stylesheetPath.replace(/\\/g, '/')}`) ?? [];
143
+ const styleFormats = await this.getFormatStyles(stylesheets);
164
144
  // create an object by merging the configuration onto the fallback config
165
- // TODO: Seems like a too tight coupling between DataTypeConfigCollection and TinyMceConfig, I would love it begin more explicit what we take from DataTypeConfigCollection and parse on, but I understand that this gives some flexibility. Is this flexibility on purpose?
166
145
  const configurationOptions = {
167
146
  ...defaultFallbackConfig,
168
- ...(this.configuration ? this.configuration?.toObject() : {}),
147
+ height: dimensions?.height,
148
+ width: dimensions?.width,
149
+ content_css: stylesheets,
150
+ style_formats: styleFormats,
169
151
  };
170
- // Map the stylesheets with server url
171
- const stylesheets = configurationOptions.stylesheets.map((stylesheetPath) => `${__classPrivateFieldGet(this, _UmbInputTinyMceElement_serverUrl, "f")}/css/${stylesheetPath.replace(/\\/g, '/')}`);
172
- const styleFormats = await this.getFormatStyles(configurationOptions.stylesheets);
173
152
  // no auto resize when a fixed height is set
174
- if (!configurationOptions.dimensions?.height) {
175
- configurationOptions.plugins ?? (configurationOptions.plugins = []);
176
- configurationOptions.plugins.splice(configurationOptions.plugins.indexOf('autoresize'), 1);
153
+ if (!configurationOptions.height) {
154
+ if (Array.isArray(configurationOptions.plugins) && configurationOptions.plugins.includes('autoresize')) {
155
+ configurationOptions.plugins.splice(configurationOptions.plugins.indexOf('autoresize'), 1);
156
+ }
157
+ }
158
+ // set the configured toolbar if any
159
+ const toolbar = this.configuration?.getValueByAlias('toolbar');
160
+ if (toolbar) {
161
+ configurationOptions.toolbar = toolbar.join(' ');
162
+ }
163
+ // set the configured inline mode
164
+ const mode = this.configuration?.getValueByAlias('mode');
165
+ if (mode?.toLocaleLowerCase() === 'inline') {
166
+ configurationOptions.inline = true;
167
+ }
168
+ // set the maximum image size
169
+ const maxImageSize = this.configuration?.getValueByAlias('maxImageSize');
170
+ if (maxImageSize !== undefined) {
171
+ configurationOptions.maxImageSize = maxImageSize;
177
172
  }
178
173
  // set the default values that will not be modified via configuration
179
174
  this._tinyConfig = {
180
175
  autoresize_bottom_margin: 10,
181
176
  body_class: 'umb-rte',
182
177
  //see https://www.tiny.cloud/docs/tinymce/6/editor-important-options/#cache_suffix
183
- cache_suffix: '?umb__rnd=' + window.Umbraco?.Sys.ServerVariables.application.cacheBuster, // TODO: Cache buster
178
+ cache_suffix: `?umb__rnd=${umbMeta.clientVersion}`,
184
179
  contextMenu: false,
185
180
  inline_boundaries_selector: 'a[href],code,.mce-annotation,.umb-embed-holder,.umb-macro-holder',
186
181
  menubar: false,
@@ -191,33 +186,10 @@ _UmbInputTinyMceElement_setTinyConfig = async function _UmbInputTinyMceElement_s
191
186
  statusbar: false,
192
187
  setup: (editor) => __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_editorSetup).call(this, editor),
193
188
  target: this._editorElement,
189
+ paste_data_images: false,
190
+ // Extend with configuration options
191
+ ...configurationOptions,
194
192
  };
195
- // extend with configuration values
196
- this._tinyConfig = {
197
- ...this._tinyConfig,
198
- content_css: stylesheets,
199
- style_formats: styleFormats || defaultStyleFormats,
200
- extended_valid_elements: defaultExtendedValidElements,
201
- height: configurationOptions.height ?? 500,
202
- invalid_elements: configurationOptions.invalidElements,
203
- plugins: configurationOptions.plugins.map((x) => x.name),
204
- toolbar: configurationOptions.toolbar.join(' '),
205
- valid_elements: configurationOptions.validElements,
206
- width: configurationOptions.width,
207
- };
208
- // Need to check if we are allowed to UPLOAD images
209
- // This is done by checking if the insert image toolbar button is available
210
- if (__classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_isMediaPickerEnabled).call(this)) {
211
- this._tinyConfig = {
212
- ...this._tinyConfig,
213
- // Update the TinyMCE Config object to allow pasting
214
- images_upload_handler: uploadImageHandler,
215
- automatic_uploads: false,
216
- images_replace_blob_uris: false,
217
- // This allows images to be pasted in & stored as Base64 until they get uploaded to server
218
- paste_data_images: true,
219
- };
220
- }
221
193
  __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_setLanguage).call(this);
222
194
  if (__classPrivateFieldGet(this, _UmbInputTinyMceElement_editorRef, "f")) {
223
195
  __classPrivateFieldGet(this, _UmbInputTinyMceElement_editorRef, "f").destroy();
@@ -226,7 +198,7 @@ _UmbInputTinyMceElement_setTinyConfig = async function _UmbInputTinyMceElement_s
226
198
  __classPrivateFieldSet(this, _UmbInputTinyMceElement_editorRef, editors.pop(), "f");
227
199
  };
228
200
  _UmbInputTinyMceElement_setLanguage = function _UmbInputTinyMceElement_setLanguage() {
229
- const localeId = __classPrivateFieldGet(this, _UmbInputTinyMceElement_currentUser, "f")?.languageIsoCode;
201
+ const localeId = this.localize.lang();
230
202
  //try matching the language using full locale format
231
203
  let languageMatch = availableLanguages.find((x) => localeId?.localeCompare(x) === 0);
232
204
  //if no matches, try matching using only the language
@@ -243,8 +215,6 @@ _UmbInputTinyMceElement_setLanguage = function _UmbInputTinyMceElement_setLangua
243
215
  };
244
216
  _UmbInputTinyMceElement_editorSetup = function _UmbInputTinyMceElement_editorSetup(editor) {
245
217
  editor.suffix = '.min';
246
- // register custom option maxImageSize
247
- editor.options.register('maxImageSize', { processor: 'number', default: defaultFallbackConfig.maxImageSize });
248
218
  // instantiate plugins - these are already loaded in this.#loadPlugins
249
219
  // to ensure they are available before setting up the editor.
250
220
  // Plugins require a reference to the current editor as a param, so can not
@@ -260,7 +230,6 @@ _UmbInputTinyMceElement_editorSetup = function _UmbInputTinyMceElement_editorSet
260
230
  editor.on('Change', () => __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_onChange).call(this, editor.getContent()));
261
231
  editor.on('Dirty', () => __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_onChange).call(this, editor.getContent()));
262
232
  editor.on('Keyup', () => __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_onChange).call(this, editor.getContent()));
263
- editor.on('SetContent', () => __classPrivateFieldGet(this, _UmbInputTinyMceElement_mediaHelper, "f").uploadBlobImages(editor));
264
233
  editor.on('focus', () => this.dispatchEvent(new CustomEvent('umb-rte-focus', { composed: true, bubbles: true })));
265
234
  editor.on('blur', () => {
266
235
  __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_onChange).call(this, editor.getContent());
@@ -270,22 +239,22 @@ _UmbInputTinyMceElement_editorSetup = function _UmbInputTinyMceElement_editorSet
270
239
  __classPrivateFieldGet(this, _UmbInputTinyMceElement_mediaHelper, "f").onResize(e);
271
240
  __classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_onChange).call(this, editor.getContent());
272
241
  });
273
- editor.on('init', () => editor.setContent(this.value?.toString() ?? ''));
274
- // If we can not find the insert image/media toolbar button
275
- // Then we need to add an event listener to the editor
276
- // That will update native browser drag & drop events
277
- // To update the icon to show you can NOT drop something into the editor
278
- if (this._tinyConfig.toolbar && !__classPrivateFieldGet(this, _UmbInputTinyMceElement_instances, "m", _UmbInputTinyMceElement_isMediaPickerEnabled).call(this)) {
279
- // Wire up the event listener
280
- editor.on('dragstart dragend dragover draggesture dragdrop drop drag', (e) => {
281
- e.preventDefault();
282
- if (e.dataTransfer) {
283
- e.dataTransfer.effectAllowed = 'none';
284
- e.dataTransfer.dropEffect = 'none';
242
+ editor.on('SetContent', (e) => {
243
+ /**
244
+ * Prevent injecting arbitrary JavaScript execution in on-attributes.
245
+ *
246
+ * TODO: This used to be toggleable through server variables with window.Umbraco?.Sys.ServerVariables.umbracoSettings.sanitizeTinyMce
247
+ */
248
+ const allNodes = Array.from(editor.dom.doc.getElementsByTagName('*'));
249
+ allNodes.forEach((node) => {
250
+ for (let i = 0; i < node.attributes.length; i++) {
251
+ if (node.attributes[i].name.startsWith('on')) {
252
+ node.removeAttribute(node.attributes[i].name);
253
+ }
285
254
  }
286
- e.stopPropagation();
287
255
  });
288
- }
256
+ });
257
+ editor.on('init', () => editor.setContent(this.value?.toString() ?? ''));
289
258
  };
290
259
  _UmbInputTinyMceElement_onInit = function _UmbInputTinyMceElement_onInit(editor) {
291
260
  //enable browser based spell checking
@@ -293,18 +262,8 @@ _UmbInputTinyMceElement_onInit = function _UmbInputTinyMceElement_onInit(editor)
293
262
  uriAttributeSanitizer(editor);
294
263
  };
295
264
  _UmbInputTinyMceElement_onChange = function _UmbInputTinyMceElement_onChange(value) {
296
- super.value = value;
297
- this.dispatchEvent(new CustomEvent('change'));
298
- };
299
- _UmbInputTinyMceElement_isMediaPickerEnabled = function _UmbInputTinyMceElement_isMediaPickerEnabled() {
300
- const toolbar = this._tinyConfig.toolbar;
301
- if (Array.isArray(toolbar) && toolbar.includes('umbmediapicker')) {
302
- return true;
303
- }
304
- else if (typeof toolbar === 'string' && toolbar.includes('umbmediapicker')) {
305
- return true;
306
- }
307
- return false;
265
+ this.value = value;
266
+ this.dispatchEvent(new UmbChangeEvent());
308
267
  };
309
268
  UmbInputTinyMceElement.styles = [
310
269
  css `
@@ -1,3 +1,2 @@
1
1
  import type { RawEditorOptions } from '../../../../external/tinymce/index.js';
2
2
  export declare const pastePreProcessHandler: RawEditorOptions['paste_preprocess'];
3
- export declare const uploadImageHandler: RawEditorOptions['images_upload_handler'];
@@ -6,33 +6,3 @@ export const pastePreProcessHandler = (_editor, args) => {
6
6
  // convert i to em
7
7
  args.content = args.content.replace(/<\s*i([^>]*)>(.*?)<\s*\/\s*i([^>]*)>/g, '<em$1>$2</em$3>');
8
8
  };
9
- export const uploadImageHandler = (blobInfo, progress) => {
10
- return new Promise((resolve, reject) => {
11
- const xhr = new XMLHttpRequest();
12
- xhr.open('POST', window.Umbraco?.Sys.ServerVariables.umbracoUrls.tinyMceApiBaseUrl + 'UploadImage');
13
- xhr.onloadstart = () => document.dispatchEvent(new CustomEvent('rte.file.uploading', { composed: true, bubbles: true }));
14
- xhr.onloadend = () => document.dispatchEvent(new CustomEvent('rte.file.uploaded', { composed: true, bubbles: true }));
15
- xhr.upload.onprogress = (e) => progress((e.loaded / e.total) * 100);
16
- xhr.onerror = () => reject('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
17
- xhr.onload = () => {
18
- if (xhr.status < 200 || xhr.status >= 300) {
19
- reject('HTTP Error: ' + xhr.status);
20
- return;
21
- }
22
- const json = JSON.parse(xhr.responseText);
23
- if (!json || typeof json.tmpLocation !== 'string') {
24
- reject('Invalid JSON: ' + xhr.responseText);
25
- return;
26
- }
27
- // Put temp location into localstorage (used to update the img with data-tmpimg later on)
28
- localStorage.set(`tinymce__${blobInfo.blobUri()}`, json.tmpLocation);
29
- // We set the img src url to be the same as we started
30
- // The Blob URI is stored in TinyMce's cache
31
- // so the img still shows in the editor
32
- resolve(blobInfo.blobUri());
33
- };
34
- const formData = new FormData();
35
- formData.append('file', blobInfo.blob(), blobInfo.filename());
36
- xhr.send(formData);
37
- });
38
- };
@@ -11,6 +11,7 @@ export * from './extension-registry/index.js';
11
11
  export * from './id/index.js';
12
12
  export * from './macro/index.js';
13
13
  export * from './menu/index.js';
14
+ export * from './meta/index.js';
14
15
  export * from './modal/index.js';
15
16
  export * from './notification/index.js';
16
17
  export * from './picker-input/index.js';
@@ -26,6 +26,7 @@ export * from './extension-registry/index.js';
26
26
  export * from './id/index.js';
27
27
  export * from './macro/index.js';
28
28
  export * from './menu/index.js';
29
+ export * from './meta/index.js';
29
30
  export * from './modal/index.js';
30
31
  export * from './notification/index.js';
31
32
  export * from './picker-input/index.js';
@@ -0,0 +1,5 @@
1
+ export declare const umbMeta: {
2
+ name: string;
3
+ clientName: string;
4
+ clientVersion: string;
5
+ };
@@ -0,0 +1,6 @@
1
+ import packageJson from '../../../../package.json';
2
+ export const umbMeta = {
3
+ name: 'Bellissima',
4
+ clientName: packageJson.name,
5
+ clientVersion: packageJson.version,
6
+ };
@@ -34,6 +34,7 @@ let UmbPropertyEditorUIColorPickerElement = class UmbPropertyEditorUIColorPicker
34
34
  render() {
35
35
  return html `<umb-input-color
36
36
  @change="${this._onChange}"
37
+ .value=${this.value ?? ''}
37
38
  .swatches="${this._swatches}"
38
39
  .showLabels="${this._showLabels}"></umb-input-color>`;
39
40
  }
@@ -9,10 +9,12 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _UmbTinyMceMediaPickerPlugin_instances, _UmbTinyMceMediaPickerPlugin_mediaHelper, _UmbTinyMceMediaPickerPlugin_currentUser, _UmbTinyMceMediaPickerPlugin_modalContext, _UmbTinyMceMediaPickerPlugin_currentUserContext, _UmbTinyMceMediaPickerPlugin_onAction, _UmbTinyMceMediaPickerPlugin_showMediaPicker, _UmbTinyMceMediaPickerPlugin_insertInEditor;
12
+ var _UmbTinyMceMediaPickerPlugin_instances, _UmbTinyMceMediaPickerPlugin_mediaHelper, _UmbTinyMceMediaPickerPlugin_currentUser, _UmbTinyMceMediaPickerPlugin_modalContext, _UmbTinyMceMediaPickerPlugin_currentUserContext, _UmbTinyMceMediaPickerPlugin_temporaryFileRepository, _UmbTinyMceMediaPickerPlugin_onAction, _UmbTinyMceMediaPickerPlugin_showMediaPicker, _UmbTinyMceMediaPickerPlugin_insertInEditor, _UmbTinyMceMediaPickerPlugin_uploadImageHandler;
13
13
  import { UmbTinyMcePluginBase } from '../../../../components/index.js';
14
14
  import { UmbMediaHelper } from '../../../../../../shared/utils/index.js';
15
15
  import { UMB_MEDIA_TREE_PICKER_MODAL, UMB_MODAL_MANAGER_CONTEXT_TOKEN, } from '../../../../modal/index.js';
16
+ import { UmbTemporaryFileRepository } from '../../../../temporary-file/index.js';
17
+ import { UmbId } from '../../../../id/index.js';
16
18
  class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
17
19
  constructor(args) {
18
20
  super(args);
@@ -21,7 +23,37 @@ class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
21
23
  _UmbTinyMceMediaPickerPlugin_currentUser.set(this, void 0);
22
24
  _UmbTinyMceMediaPickerPlugin_modalContext.set(this, void 0);
23
25
  _UmbTinyMceMediaPickerPlugin_currentUserContext.set(this, void 0);
26
+ _UmbTinyMceMediaPickerPlugin_temporaryFileRepository.set(this, void 0);
27
+ _UmbTinyMceMediaPickerPlugin_uploadImageHandler.set(this, (blobInfo, progress) => {
28
+ return new Promise((resolve, reject) => {
29
+ // Fetch does not support progress, so we need to fake it.
30
+ progress(0);
31
+ const id = UmbId.new();
32
+ const fileBlob = blobInfo.blob();
33
+ const file = new File([fileBlob], blobInfo.filename(), { type: fileBlob.type });
34
+ progress(50);
35
+ document.dispatchEvent(new CustomEvent('rte.file.uploading', { composed: true, bubbles: true }));
36
+ __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_temporaryFileRepository, "f")
37
+ .upload(id, file)
38
+ .then((response) => {
39
+ if (response.error) {
40
+ reject(response.error);
41
+ return;
42
+ }
43
+ // Put temp location into localstorage (used to update the img with data-tmpimg later on)
44
+ const blobUri = window.URL.createObjectURL(fileBlob);
45
+ sessionStorage.setItem(`tinymce__${blobUri}`, id);
46
+ resolve(blobUri);
47
+ })
48
+ .catch(reject)
49
+ .finally(() => {
50
+ progress(100);
51
+ document.dispatchEvent(new CustomEvent('rte.file.uploaded', { composed: true, bubbles: true }));
52
+ });
53
+ });
54
+ });
24
55
  __classPrivateFieldSet(this, _UmbTinyMceMediaPickerPlugin_mediaHelper, new UmbMediaHelper(), "f");
56
+ __classPrivateFieldSet(this, _UmbTinyMceMediaPickerPlugin_temporaryFileRepository, new UmbTemporaryFileRepository(args.host), "f");
25
57
  this.consumeContext(UMB_MODAL_MANAGER_CONTEXT_TOKEN, (modalContext) => {
26
58
  __classPrivateFieldSet(this, _UmbTinyMceMediaPickerPlugin_modalContext, modalContext, "f");
27
59
  });
@@ -31,15 +63,36 @@ class UmbTinyMceMediaPickerPlugin extends UmbTinyMcePluginBase {
31
63
  // this.#currentUserContext = instance;
32
64
  // this.#observeCurrentUser();
33
65
  // });
34
- this.editor.ui.registry.addButton('umbmediapicker', {
66
+ this.editor.ui.registry.addToggleButton('umbmediapicker', {
35
67
  icon: 'image',
36
68
  tooltip: 'Media Picker',
37
- //stateSelector: 'img[data-udi]', TODO => Investigate where stateselector has gone, or if it is still needed
38
69
  onAction: () => __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_instances, "m", _UmbTinyMceMediaPickerPlugin_onAction).call(this),
70
+ onSetup: (api) => {
71
+ const changed = this.editor.selection.selectorChangedWithUnbind('img[data-udi]', (state) => api.setActive(state));
72
+ return () => changed.unbind();
73
+ },
39
74
  });
75
+ // Register global options for the editor
76
+ this.editor.options.register('maxImageSize', { processor: 'number', default: 500 });
77
+ // Adjust Editor settings to allow pasting images
78
+ // but only if the umbmediapicker button is present
79
+ const toolbar = this.configuration?.getValueByAlias('toolbar');
80
+ if (toolbar?.includes('umbmediapicker')) {
81
+ this.editor.options.set('paste_data_images', true);
82
+ this.editor.options.set('automatic_uploads', false);
83
+ this.editor.options.set('images_upload_handler', __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_uploadImageHandler, "f"));
84
+ // This allows images to be pasted in & stored as Base64 until they get uploaded to server
85
+ this.editor.options.set('images_replace_blob_uris', true);
86
+ // Listen for SetContent to update images
87
+ this.editor.on('SetContent', async (e) => {
88
+ const content = e.content;
89
+ // Handle images that are pasted in
90
+ __classPrivateFieldGet(this, _UmbTinyMceMediaPickerPlugin_mediaHelper, "f").uploadBlobImages(this.editor, content);
91
+ });
92
+ }
40
93
  }
41
94
  }
42
- _UmbTinyMceMediaPickerPlugin_mediaHelper = new WeakMap(), _UmbTinyMceMediaPickerPlugin_currentUser = new WeakMap(), _UmbTinyMceMediaPickerPlugin_modalContext = new WeakMap(), _UmbTinyMceMediaPickerPlugin_currentUserContext = new WeakMap(), _UmbTinyMceMediaPickerPlugin_instances = new WeakSet(), _UmbTinyMceMediaPickerPlugin_onAction =
95
+ _UmbTinyMceMediaPickerPlugin_mediaHelper = new WeakMap(), _UmbTinyMceMediaPickerPlugin_currentUser = new WeakMap(), _UmbTinyMceMediaPickerPlugin_modalContext = new WeakMap(), _UmbTinyMceMediaPickerPlugin_currentUserContext = new WeakMap(), _UmbTinyMceMediaPickerPlugin_temporaryFileRepository = new WeakMap(), _UmbTinyMceMediaPickerPlugin_uploadImageHandler = new WeakMap(), _UmbTinyMceMediaPickerPlugin_instances = new WeakSet(), _UmbTinyMceMediaPickerPlugin_onAction =
43
96
  /*
44
97
  async #observeCurrentUser() {
45
98
  if (!this.#currentUserContext) return;
@@ -1,12 +1,16 @@
1
1
  import { UmbPropertyEditorUiElement } from '../../../extension-registry/index.js';
2
2
  import { UmbLitElement } from '../../../../../shared/lit-element/index.js';
3
3
  import { UmbPropertyEditorConfigCollection } from '../../index.js';
4
+ type RichTextEditorValue = {
5
+ blocks: object;
6
+ markup: string;
7
+ };
4
8
  /**
5
9
  * @element umb-property-editor-ui-tiny-mce
6
10
  */
7
11
  export declare class UmbPropertyEditorUITinyMceElement extends UmbLitElement implements UmbPropertyEditorUiElement {
8
12
  #private;
9
- value: string;
13
+ value?: RichTextEditorValue;
10
14
  set config(config: UmbPropertyEditorConfigCollection | undefined);
11
15
  render(): import("lit-html").TemplateResult<1>;
12
16
  static styles: import("@lit/reactive-element/css-tag.js").CSSResult[];
@@ -27,7 +27,10 @@ let UmbPropertyEditorUITinyMceElement = class UmbPropertyEditorUITinyMceElement
27
27
  super(...arguments);
28
28
  _UmbPropertyEditorUITinyMceElement_instances.add(this);
29
29
  _UmbPropertyEditorUITinyMceElement_configuration.set(this, void 0);
30
- this.value = '';
30
+ this.value = {
31
+ blocks: {},
32
+ markup: '',
33
+ };
31
34
  }
32
35
  set config(config) {
33
36
  __classPrivateFieldSet(this, _UmbPropertyEditorUITinyMceElement_configuration, config, "f");
@@ -36,18 +39,21 @@ let UmbPropertyEditorUITinyMceElement = class UmbPropertyEditorUITinyMceElement
36
39
  return html `<umb-input-tiny-mce
37
40
  @change=${__classPrivateFieldGet(this, _UmbPropertyEditorUITinyMceElement_instances, "m", _UmbPropertyEditorUITinyMceElement_onChange)}
38
41
  .configuration=${__classPrivateFieldGet(this, _UmbPropertyEditorUITinyMceElement_configuration, "f")}
39
- .value=${this.value}></umb-input-tiny-mce>`;
42
+ .value=${this.value?.markup ?? ''}></umb-input-tiny-mce>`;
40
43
  }
41
44
  };
42
45
  _UmbPropertyEditorUITinyMceElement_configuration = new WeakMap();
43
46
  _UmbPropertyEditorUITinyMceElement_instances = new WeakSet();
44
47
  _UmbPropertyEditorUITinyMceElement_onChange = function _UmbPropertyEditorUITinyMceElement_onChange(event) {
45
- this.value = event.target.value;
48
+ this.value = {
49
+ blocks: {},
50
+ markup: event.target.value,
51
+ };
46
52
  this.dispatchEvent(new CustomEvent('property-value-change'));
47
53
  };
48
54
  UmbPropertyEditorUITinyMceElement.styles = [UmbTextStyles];
49
55
  __decorate([
50
- property({ type: String })
56
+ property({ type: Object })
51
57
  ], UmbPropertyEditorUITinyMceElement.prototype, "value", void 0);
52
58
  __decorate([
53
59
  property({ attribute: false })