From 9df6e6555267599db6902a1f354f0011c110f751 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:19:36 +0100 Subject: [PATCH 1/5] V13: Rich text editor does not show its toolbar on grid layout rte's (#15595) * synchronize normal rte with grid-rte * restore pinToolbar and unpinToolbar from v10 and update to tinymce v6 and apply to grid-rte * linting * Reverting `pinToolbar` from v8 * remove unused variable --------- Co-authored-by: leekelleher --- .../components/grid/grid.rte.directive.js | 141 ++++----- .../src/common/services/tinymce.service.js | 5 + .../propertyeditors/grid/editors/rte.html | 4 +- .../propertyeditors/rte/rte.component.js | 273 +++++++++--------- 4 files changed, 214 insertions(+), 209 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js index 812fec6e9c..7821c00e69 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/grid/grid.rte.directive.js @@ -12,22 +12,18 @@ angular.module("umbraco.directives") replace: true, link: function (scope, element, attrs) { - // TODO: A lot of the code below should be shared between the grid rte and the normal rte - scope.isLoading = true; - var promises = []; - //To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias // because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because // we have this mini content editor panel that can be launched with MNTP. scope.textAreaHtmlId = scope.uniqueId + "_" + String.CreateGuid(); - var editorConfig = scope.configuration ? scope.configuration : null; + let editorConfig = scope.configuration ? scope.configuration : null; if (!editorConfig || Utilities.isString(editorConfig)) { editorConfig = tinyMceService.defaultPrevalues(); //for the grid by default, we don't want to include the macro or the block-picker toolbar - editorConfig.toolbar = _.without(editorConfig, "umbmacro", "umbblockpicker"); + editorConfig.toolbar = _.without(editorConfig.toolbar, "umbmacro", "umbblockpicker"); } //ensure the grid's global config is being passed up to the RTE, these 2 properties need to be in this format @@ -39,46 +35,50 @@ angular.module("umbraco.directives") scope.dataTypeKey = scope.datatypeKey; //Yes - this casing is rediculous, but it's because the var starts with `data` so it can't be `data-type-id` :/ //stores a reference to the editor - var tinyMceEditor = null; + let tinyMceEditor = null; + + const assetPromises = []; //queue file loading tinyMceAssets.forEach(function (tinyJsAsset) { - promises.push(assetsService.loadJs(tinyJsAsset, scope)); + assetPromises.push(assetsService.loadJs(tinyJsAsset, scope)); }); - promises.push(tinyMceService.getTinyMceEditorConfig({ - htmlId: scope.textAreaHtmlId, - stylesheets: editorConfig.stylesheets, - toolbar: editorConfig.toolbar, - mode: editorConfig.mode - })); - $q.all(promises).then(function (result) { + //wait for assets to load before proceeding + $q.all(assetPromises) + .then(function () { + return tinyMceService.getTinyMceEditorConfig({ + htmlId: scope.textAreaHtmlId, + stylesheets: editorConfig.stylesheets, + toolbar: editorConfig.toolbar, + mode: editorConfig.mode + }) + }) - var standardConfig = result[promises.length - 1]; + // Handle additional assets loading depending on the configuration before initializing the editor + .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); + } + + return tinyMceConfig; + }) + + //wait for config to be ready after assets have loaded + .then(function (standardConfig) { //create a baseline Config to extend upon - var baseLineConfigObj = { - maxImageSize: editorConfig.maxImageSize, - toolbar_sticky: true + let baseLineConfigObj = { + maxImageSize: editorConfig.maxImageSize }; - Utilities.extend(baseLineConfigObj, standardConfig); - baseLineConfigObj.setup = function (editor) { //set the reference tinyMceEditor = editor; - //initialize the standard editor functionality for Umbraco - tinyMceService.initializeEditor({ - editor: editor, - toolbar: editorConfig.toolbar, - model: scope, - // Form is found in the scope of the grid controller above us, not in our isolated scope - // https://github.com/umbraco/Umbraco-CMS/issues/7461 - currentForm: angularHelper.getCurrentForm(scope.$parent) - }); - //custom initialization for this editor within the grid editor.on('init', function (e) { @@ -96,49 +96,52 @@ angular.module("umbraco.directives") }, 400); }); + + //initialize the standard editor functionality for Umbraco + tinyMceService.initializeEditor({ + editor: editor, + toolbar: editorConfig.toolbar, + model: scope, + // Form is found in the scope of the grid controller above us, not in our isolated scope + // https://github.com/umbraco/Umbraco-CMS/issues/7461 + currentForm: angularHelper.getCurrentForm(scope.$parent) + }); }; - /** Loads in the editor */ - function loadTinyMce() { - - //we need to add a timeout here, to force a redraw so TinyMCE can find - //the elements needed - $timeout(function () { - tinymce.init(baseLineConfigObj); - }, 150, false); - } - - loadTinyMce(); - - // TODO: This should probably be in place for all RTE, not just for the grid, which means - // this code can live in tinyMceService.initializeEditor - var tabShownListener = eventsService.on("app.tabChange", function (e, args) { - - var tabId = args.id; - var myTabId = element.closest(".umb-tab-pane").attr("rel"); - - if (String(tabId) === myTabId) { - //the tab has been shown, trigger the mceAutoResize (as it could have timed out before the tab was shown) - if (tinyMceEditor !== undefined && tinyMceEditor != null) { - tinyMceEditor.execCommand('mceAutoResize', false, null, null); - } - } - - }); - - //when the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom - // element might still be there even after the modal has been hidden. - scope.$on('$destroy', function () { - eventsService.unsubscribe(tabShownListener); - //ensure we unbind this in case the blur doesn't fire above - if (tinyMceEditor !== undefined && tinyMceEditor != null) { - tinyMceEditor.destroy() - } - }); + Utilities.extend(baseLineConfigObj, standardConfig); + //we need to add a timeout here, to force a redraw so TinyMCE can find + //the elements needed + $timeout(function () { + tinymce.init(baseLineConfigObj); + }, 150); }); + const tabShownListener = eventsService.on("app.tabChange", function (e, args) { + + const tabId = String(args.id); + const myTabId = element.closest(".umb-tab-pane").attr("rel"); + + if (tabId === myTabId) { + //the tab has been shown, trigger the mceAutoResize (as it could have timed out before the tab was shown) + if (tinyMceEditor !== undefined && tinyMceEditor != null) { + tinyMceEditor.execCommand('mceAutoResize', false, null, null); + } + } + }); + + //when the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom + // element might still be there even after the modal has been hidden. + scope.$on('$destroy', function () { + eventsService.unsubscribe(tabShownListener); + + //ensure we unbind this in case the blur doesn't fire above + if (tinyMceEditor) { + tinyMceEditor.destroy(); + tinyMceEditor = null; + } + }); } }; }); 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 cfea1d5594..ebf759c20c 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 @@ -797,6 +797,11 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }); }); + // Do not add any further controls if the block editor is not available + if (!blockEditorApi) { + return; + } + editor.ui.registry.addToggleButton('umbblockpicker', { icon: 'visualblocks', tooltip: 'Insert Block', diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html index fb6f3b2a4b..2fbd0b2fe3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.html @@ -4,8 +4,8 @@ configuration="model.config.rte" value="control.value" unique-id="control.$uniqueId" - datatype-key="{{model.dataTypeKey}}" - ignore-user-start-nodes="{{model.config.ignoreUserStartNodes}}"> + datatype-key="{{model.dataTypeKey}}" + ignore-user-start-nodes="{{model.config.ignoreUserStartNodes}}"> 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 4e9cf7d014..7f06215148 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 @@ -195,162 +195,148 @@ assetPromises.push(assetsService.loadJs(tinyJsAsset, $scope)); }); - 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 + $q.all(assetPromises) + .then(function () { + return 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); + + // Handle additional assets loading depending on the configuration before initializing the editor + .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); + } + + return tinyMceConfig; + }) + + //wait for config to be ready after assets have loaded + .then(function (standardConfig) { + + if (height !== null) { + standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1); } - return tinyMceConfig; - }) - .then(function (tinyMceConfig) { - tinyMceConfigDeferred.resolve(tinyMceConfig); - }); - }); + //create a baseline Config to extend upon + let baseLineConfigObj = { + maxImageSize: editorConfig.maxImageSize, + width: width, + height: height + }; - //wait for config to be ready after assets have loaded - tinyMceConfigDeferred.promise.then(function (standardConfig) { + baseLineConfigObj.setup = function (editor) { - if (height !== null) { - standardConfig.plugins.splice(standardConfig.plugins.indexOf("autoresize"), 1); - } + //set the reference + vm.tinyMceEditor = editor; - //create a baseline Config to extend upon - var baseLineConfigObj = { - maxImageSize: editorConfig.maxImageSize, - width: width, - height: height - }; - - baseLineConfigObj.setup = function (editor) { - - //set the reference - vm.tinyMceEditor = editor; - - vm.tinyMceEditor.on('init', function (e) { - $timeout(function () { - vm.rteLoading = false; - vm.updateLoading(); - }); - }); - vm.tinyMceEditor.on("focus", function () { - $element[0].dispatchEvent(new CustomEvent('umb-rte-focus', {composed: true, bubbles: true})); - }); - vm.tinyMceEditor.on("blur", function () { - $element[0].dispatchEvent(new CustomEvent('umb-rte-blur', {composed: true, bubbles: true})); - }); - - //initialize the standard editor functionality for Umbraco - tinyMceService.initializeEditor({ - //scope: $scope, - editor: editor, - toolbar: editorConfig.toolbar, - model: vm.model, - getValue: function () { - return vm.model.value.markup; - }, - setValue: function (newVal) { - vm.model.value.markup = newVal; - $scope.$evalAsync(); - }, - culture: vm.umbProperty?.culture ?? null, - segment: vm.umbProperty?.segment ?? null, - blockEditorApi: vm.noBlocksMode ? undefined : vm.blockEditorApi, - parentForm: vm.propertyForm, - valFormManager: vm.valFormManager, - currentFormInput: $scope.rteForm.modelValue - }); - - }; - - Utilities.extend(baseLineConfigObj, standardConfig); - - // Readonly mode - baseLineConfigObj.toolbar = vm.readonly ? false : baseLineConfigObj.toolbar; - baseLineConfigObj.readonly = vm.readonly ? 1 : baseLineConfigObj.readonly; - - // We need to wait for DOM to have rendered before we can find the element by ID. - $timeout(function () { - tinymce.init(baseLineConfigObj); - }, 50); - - //listen for formSubmitting event (the result is callback used to remove the event subscription) - unsubscribe.push($scope.$on("formSubmitting", function () { - if (vm.tinyMceEditor != null && !vm.rteLoading) { - - // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. - var blockElements = vm.tinyMceEditor.dom.select(`umb-rte-block, umb-rte-block-inline`); - const usedContentUdis = blockElements.map(blockElement => blockElement.getAttribute('data-content-udi')); - - const unusedBlocks = vm.layout.filter(x => usedContentUdis.indexOf(x.contentUdi) === -1); - unusedBlocks.forEach(blockLayout => { - deleteBlock(blockLayout.$block); - }); - - - // Remove Angular Classes from markup: - var parser = new DOMParser(); - var doc = parser.parseFromString(vm.model.value.markup, 'text/html'); - - // Get all elements in the parsed document - var elements = doc.querySelectorAll('*[class]'); - elements.forEach(element => { - var classAttribute = element.getAttribute("class"); - if (classAttribute) { - // Split the class attribute by spaces and remove "ng-scope" and "ng-isolate-scope" - var classes = classAttribute.split(" "); - var newClasses = classes.filter(function (className) { - return className !== "ng-scope" && className !== "ng-isolate-scope"; + vm.tinyMceEditor.on('init', function (e) { + $timeout(function () { + vm.rteLoading = false; + vm.updateLoading(); }); - - // Update the class attribute with the remaining classes - if (newClasses.length > 0) { - element.setAttribute('class', newClasses.join(' ')); - } else { - // If no remaining classes, remove the class attribute - element.removeAttribute('class'); - } - } + }); + vm.tinyMceEditor.on("focus", function () { + $element[0].dispatchEvent(new CustomEvent('umb-rte-focus', {composed: true, bubbles: true})); + }); + vm.tinyMceEditor.on("blur", function () { + $element[0].dispatchEvent(new CustomEvent('umb-rte-blur', {composed: true, bubbles: true})); }); - vm.model.value.markup = doc.body.innerHTML; + //initialize the standard editor functionality for Umbraco + tinyMceService.initializeEditor({ + //scope: $scope, + editor: editor, + toolbar: editorConfig.toolbar, + model: vm.model, + getValue: function () { + return vm.model.value.markup; + }, + setValue: function (newVal) { + vm.model.value.markup = newVal; + $scope.$evalAsync(); + }, + culture: vm.umbProperty?.culture ?? null, + segment: vm.umbProperty?.segment ?? null, + blockEditorApi: vm.noBlocksMode ? undefined : vm.blockEditorApi, + parentForm: vm.propertyForm, + valFormManager: vm.valFormManager, + currentFormInput: $scope.rteForm.modelValue + }); - } - })); + }; - vm.focusRTE = function () { - vm.tinyMceEditor.focus(); - } + Utilities.extend(baseLineConfigObj, standardConfig); + + // Readonly mode + baseLineConfigObj.toolbar = vm.readonly ? false : baseLineConfigObj.toolbar; + baseLineConfigObj.readonly = vm.readonly ? 1 : baseLineConfigObj.readonly; + + // We need to wait for DOM to have rendered before we can find the element by ID. + $timeout(function () { + tinymce.init(baseLineConfigObj); + }, 50); + + //listen for formSubmitting event (the result is callback used to remove the event subscription) + unsubscribe.push($scope.$on("formSubmitting", function () { + if (vm.tinyMceEditor != null && !vm.rteLoading) { + + // Remove unused Blocks of Blocks Layout. Leaving only the Blocks that are present in Markup. + var blockElements = vm.tinyMceEditor.dom.select(`umb-rte-block, umb-rte-block-inline`); + const usedContentUdis = blockElements.map(blockElement => blockElement.getAttribute('data-content-udi')); + + const unusedBlocks = vm.layout.filter(x => usedContentUdis.indexOf(x.contentUdi) === -1); + unusedBlocks.forEach(blockLayout => { + deleteBlock(blockLayout.$block); + }); + + + // Remove Angular Classes from markup: + var parser = new DOMParser(); + var doc = parser.parseFromString(vm.model.value.markup, 'text/html'); + + // Get all elements in the parsed document + var elements = doc.querySelectorAll('*[class]'); + elements.forEach(element => { + var classAttribute = element.getAttribute("class"); + if (classAttribute) { + // Split the class attribute by spaces and remove "ng-scope" and "ng-isolate-scope" + var classes = classAttribute.split(" "); + var newClasses = classes.filter(function (className) { + return className !== "ng-scope" && className !== "ng-isolate-scope"; + }); + + // Update the class attribute with the remaining classes + if (newClasses.length > 0) { + element.setAttribute('class', newClasses.join(' ')); + } else { + // If no remaining classes, remove the class attribute + element.removeAttribute('class'); + } + } + }); + + vm.model.value.markup = doc.body.innerHTML; - // When the element is disposed we need to unsubscribe! - // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom - // element might still be there even after the modal has been hidden. - $scope.$on('$destroy', function () { - if (vm.tinyMceEditor != null) { - if($element) { - $element[0]?.dispatchEvent(new CustomEvent('blur', {composed: true, bubbles: true})); } - vm.tinyMceEditor.destroy(); - vm.tinyMceEditor = null; - } - }); + })); - }); + }); }; + vm.focusRTE = function () { + if (vm.tinyMceEditor) { + vm.tinyMceEditor.focus(); + } + } + // Called when we save the value, the server may return an updated data and our value is re-synced // we need to deal with that here so that our model values are all in sync so we basically re-initialize. function onServerValueChanged(newVal, oldVal) { @@ -978,6 +964,17 @@ for (const subscription of unsubscribe) { subscription(); } + + // When the element is disposed we need to unsubscribe! + // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom + // element might still be there even after the modal has been hidden. + if (vm.tinyMceEditor != null) { + if($element) { + $element[0]?.dispatchEvent(new CustomEvent('blur', {composed: true, bubbles: true})); + } + vm.tinyMceEditor.destroy(); + vm.tinyMceEditor = null; + } }); } From f3ed9352134bfec213cadde81fe21274bcae2c3d Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 22 Jan 2024 13:37:15 +0100 Subject: [PATCH 2/5] allow the blockeditormodelobject.service.js to fall back to -20 for currentPageId if we are not in an editor mode (#15615) we detect the editor mode by checking for `contentTypeKey` which only exists on content contexts --- .../common/services/blockeditormodelobject.service.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js index 1367d58151..ab6c176c85 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/blockeditormodelobject.service.js @@ -367,7 +367,6 @@ * @name load * @methodOf umbraco.services.blockEditorModelObject * @description Load the scaffolding models for the given configuration, these are needed to provide useful models for each block. - * @param {Object} blockObject BlockObject to receive data values from. * @returns {Promise} A Promise object which resolves when all scaffold models are loaded. */ load: function () { @@ -398,9 +397,15 @@ scaffoldKeys = scaffoldKeys.filter((value, index, self) => self.indexOf(value) === index); if(scaffoldKeys.length > 0) { - var currentPage = editorState.getCurrent(); - var currentPageId = currentPage ? (currentPage.id > 0 ? currentPage.id : currentPage.parentId) : null || -20; + // We need to know if we are in the document type editor or content editor. + // If we are in the document type editor, we need to use -20 as the current page id. + // If we are in the content editor, we need to use the current page id or parent id if the current page is new. + // We can recognize a content editor context by checking if the current editor state has a contentTypeKey. + const currentEditorState = editorState.getCurrent(); + const currentPageId = currentEditorState.contentTypeKey ? currentEditorState.id || currentEditorState.parentId || -20 : -20; + // Load all scaffolds for the block types. + // The currentPageId is used to determine the access level for the current user. tasks.push(contentResource.getScaffoldByKeys(currentPageId, scaffoldKeys).then(scaffolds => { Object.values(scaffolds).forEach(scaffold => { // self.scaffolds might not exists anymore, this happens if this instance has been destroyed before the load is complete. From 5940a545851fe71851ff24acafeccd7ba43a8b5f Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 22 Jan 2024 15:40:26 +0100 Subject: [PATCH 3/5] Updated minor and patch dependencies. (#15619) --- Directory.Packages.props | 32 ++++++++++++++++---------------- tests/Directory.Packages.props | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7279fa21c0..59b0f032a1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -12,24 +12,24 @@ - - + + - - - - + + + + - + - - + + - + @@ -44,9 +44,9 @@ - - - + + + @@ -61,7 +61,7 @@ - + @@ -72,7 +72,7 @@ - + @@ -89,4 +89,4 @@ - + \ No newline at end of file diff --git a/tests/Directory.Packages.props b/tests/Directory.Packages.props index 1ecbee80a5..586469c2c8 100644 --- a/tests/Directory.Packages.props +++ b/tests/Directory.Packages.props @@ -4,8 +4,8 @@ - - + + From 0821fb64bbbba0b1c1c3e05cfdbd991da8c1198d Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Wed, 24 Jan 2024 10:47:06 +0100 Subject: [PATCH 4/5] version bump --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 15469ada83..2822aad6ef 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "13.1.0-rc2", + "version": "13.1.0", "assemblyVersion": { "precision": "build" }, From 802e5a8c27b332565b8b4d53af6652978ea960de Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 24 Jan 2024 16:29:00 +0100 Subject: [PATCH 5/5] update apidocs ui section to Umbraco 13 --- src/Umbraco.Web.UI.Docs/.nvmrc | 1 + src/Umbraco.Web.UI.Docs/README.md | 8 +++++--- src/Umbraco.Web.UI.Docs/gulpfile.js | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 src/Umbraco.Web.UI.Docs/.nvmrc diff --git a/src/Umbraco.Web.UI.Docs/.nvmrc b/src/Umbraco.Web.UI.Docs/.nvmrc new file mode 100644 index 0000000000..ec3053c8dc --- /dev/null +++ b/src/Umbraco.Web.UI.Docs/.nvmrc @@ -0,0 +1 @@ +10.15 diff --git a/src/Umbraco.Web.UI.Docs/README.md b/src/Umbraco.Web.UI.Docs/README.md index c6f3893e55..6a42c38915 100644 --- a/src/Umbraco.Web.UI.Docs/README.md +++ b/src/Umbraco.Web.UI.Docs/README.md @@ -1,14 +1,16 @@ # Umbraco Backoffice UI API Documentation -This project builds the documentation for the UI of the Umbraco backoffice, it is published on Our Umbraco in the "Reference" section of the documentation. +This project builds the documentation for the UI of the Umbraco backoffice, it is published on the Umbraco Docs in the "Reference" section of the documentation. + +All versions can be accessed through the https://apidocs.umbraco.com/vXX/ui/ url where XX is the major version number of Umbraco. In order to build the documentation, please follow the following two steps: ``` npm ci -npx gulp docs +npm start ``` After this, you should have an `api` directory which contains index.html. -In order to check if the documentation works properly, you would need to run the `api` directory in a webserver. On Windows, this can be accomplished by opening the `api` directory in [Visual Studio Code](https://code.visualstudio.com/) and running it with the [IIS Express plugin](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express). \ No newline at end of file +In order to check if the documentation works properly, you would need to run the `api` directory in a webserver. On Windows, this can be accomplished by opening the `api` directory in [Visual Studio Code](https://code.visualstudio.com/) and running it with the [IIS Express plugin](https://marketplace.visualstudio.com/items?itemName=warren-buckley.iis-express). diff --git a/src/Umbraco.Web.UI.Docs/gulpfile.js b/src/Umbraco.Web.UI.Docs/gulpfile.js index e595eabe5d..53f7f2cf8d 100644 --- a/src/Umbraco.Web.UI.Docs/gulpfile.js +++ b/src/Umbraco.Web.UI.Docs/gulpfile.js @@ -18,7 +18,7 @@ gulp.task('docs', [], function (cb) { var options = { html5Mode: false, startPage: '/api', - title: "Umbraco 12 Backoffice UI API Documentation", + title: "Umbraco 13 Backoffice UI API Documentation", dest: './api', styles: ['./umb-docs.css'], image: "https://our.umbraco.com/assets/images/logo.svg"