diff --git a/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs b/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs
index fce3b36373..e48e90c0e6 100644
--- a/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs
+++ b/src/Umbraco.Core/Configuration/Models/RichTextEditorSettings.cs
@@ -146,6 +146,11 @@ public class RichTextEditorSettings
[DefaultValue(StaticInvalidElements)]
public string InvalidElements { get; set; } = StaticInvalidElements;
+ ///
+ /// Cloud API Key for TinyMCE. This is required to use TinyMCE premium plugins.
+ ///
+ public string? CloudApiKey { get; set; }
+
public class RichTextEditorCommand
{
[Required]
diff --git a/src/Umbraco.Core/Models/ContentEditing/RichTextEditorConfiguration.cs b/src/Umbraco.Core/Models/ContentEditing/RichTextEditorConfiguration.cs
index c621aa8c59..b0e858136b 100644
--- a/src/Umbraco.Core/Models/ContentEditing/RichTextEditorConfiguration.cs
+++ b/src/Umbraco.Core/Models/ContentEditing/RichTextEditorConfiguration.cs
@@ -19,4 +19,7 @@ public class RichTextEditorConfiguration
[DataMember(Name = "customConfig")]
public IDictionary? CustomConfig { get; set; }
+
+ [DataMember(Name = "cloudApiKey")]
+ public string? CloudApiKey { get; set; }
}
diff --git a/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs b/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs
index 602914fe43..f075199882 100644
--- a/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs
+++ b/src/Umbraco.Web.BackOffice/PropertyEditors/RichTextPreValueController.cs
@@ -30,7 +30,8 @@ public class RichTextPreValueController : UmbracoAuthorizedJsonController
new RichTextEditorCommand { Alias = x.Alias, Mode = x.Mode, Name = x.Name }),
ValidElements = settings.ValidElements,
InvalidElements = settings.InvalidElements,
- CustomConfig = settings.CustomConfig
+ CustomConfig = settings.CustomConfig,
+ CloudApiKey = settings.CloudApiKey,
};
return config;
diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json
index ad30a030a1..16fbf7611f 100644
--- a/src/Umbraco.Web.UI.Client/package-lock.json
+++ b/src/Umbraco.Web.UI.Client/package-lock.json
@@ -39,7 +39,7 @@
"ng-file-upload": "12.2.13",
"nouislider": "15.7.1",
"spectrum-colorpicker2": "2.0.10",
- "tinymce": "6.7.3",
+ "tinymce": "6.8.1",
"typeahead.js": "0.11.1",
"underscore": "1.13.6",
"wicg-inert": "3.1.2"
@@ -16606,9 +16606,9 @@
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"node_modules/tinymce": {
- "version": "6.7.3",
- "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.7.3.tgz",
- "integrity": "sha512-J7WmYIi/gt1RvZ6Ap2oQiUjzAoiS9pfV+d4GnKuZuPu8agmlAEAInNmMvMjfCNBzHv4JnZXY7qlHUAI0IuYQVA=="
+ "version": "6.8.1",
+ "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.8.1.tgz",
+ "integrity": "sha512-WYPvMXIjBrXM/oBiqGCbT2a8ptiO3TWXm/xxPWDCl8SxRKMW7Rfp0Lk190E9fXmX6uh9lJMRCnmKHzvryz0ftA=="
},
"node_modules/to-absolute-glob": {
"version": "2.0.2",
diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json
index 3d3dc9da15..f23a0ab750 100644
--- a/src/Umbraco.Web.UI.Client/package.json
+++ b/src/Umbraco.Web.UI.Client/package.json
@@ -51,7 +51,7 @@
"ng-file-upload": "12.2.13",
"nouislider": "15.7.1",
"spectrum-colorpicker2": "2.0.10",
- "tinymce": "6.7.3",
+ "tinymce": "6.8.1",
"typeahead.js": "0.11.1",
"underscore": "1.13.6",
"wicg-inert": "3.1.2"
diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
index 7767e3c17b..e7c86c0303 100644
--- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
+++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js
@@ -387,8 +387,10 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
//create a baseline Config to extend upon
var config = {
+ cloudApiKey: tinyMceConfig.cloudApiKey,
+ promotion: false,
inline: modeInline,
- plugins: plugins,
+ plugins: [...new Set(plugins)],
custom_elements: 'umb-rte-block,~umb-rte-block-inline',
valid_elements: tinyMceConfig.validElements,
invalid_elements: tinyMceConfig.inValidElements,
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.component.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.component.js
index 4fe1beeb85..2bb5b6c3a0 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.component.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.component.js
@@ -177,24 +177,40 @@
vm.containerHeight = "auto";
vm.containerOverflow = "inherit";
- var promises = [blockModelObjectLoading];
+ const assetPromises = [blockModelObjectLoading];
//queue file loading
tinyMceAssets.forEach(function (tinyJsAsset) {
- promises.push(assetsService.loadJs(tinyJsAsset, $scope));
+ assetPromises.push(assetsService.loadJs(tinyJsAsset, $scope));
});
- promises.push(tinyMceService.getTinyMceEditorConfig({
+ const tinyMceConfigDeferred = $q.defer();
+
+ //wait for assets to load before proceeding
+ $q.all(assetPromises).then(function () {
+
+ tinyMceService.getTinyMceEditorConfig({
htmlId: vm.textAreaHtmlId,
stylesheets: editorConfig.stylesheets,
toolbar: editorConfig.toolbar,
mode: editorConfig.mode
- }));
+ })
+ .then(function (tinyMceConfig) {
+ // Load the plugins.min.js file from the TinyMCE Cloud if a Cloud Api Key is specified
+ if (tinyMceConfig.cloudApiKey) {
+ return assetsService.loadJs(`https://cdn.tiny.cloud/1/${tinyMceConfig.cloudApiKey}/tinymce/${tinymce.majorVersion}.${tinymce.minorVersion}/plugins.min.js`)
+ .then(() => tinyMceConfig);
+ }
- //wait for queue to end
- $q.all(promises).then(function (result) {
+ return tinyMceConfig;
+ })
+ .then(function (tinyMceConfig) {
+ tinyMceConfigDeferred.resolve(tinyMceConfig);
+ });
+ });
- var standardConfig = result[promises.length - 1];
+ //wait for config to be ready after assets have loaded
+ tinyMceConfigDeferred.promise.then(function (standardConfig) {
if (height !== null) {
standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1);