From de12019fea240e2af7451e9450977f66040c5509 Mon Sep 17 00:00:00 2001 From: Mads Rasmussen Date: Thu, 26 Oct 2023 09:25:50 +0200 Subject: [PATCH 01/27] Bugfix: UI for preview bug related to single language & non variant content (#15032) * Bump tinymce from 6.7.0 to 6.7.1 * Ensure PackageVersion is set correctly before transforming template.json * rerun watchers every time we reinit the data * handle variant content with only one language --------- Co-authored-by: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Co-authored-by: Ronald Barendse --- src/Umbraco.Web.UI.Client/package-lock.json | 8 +++--- src/Umbraco.Web.UI.Client/package.json | 2 +- .../components/content/edit.controller.js | 25 +++++++++++++------ templates/Umbraco.Templates.csproj | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 1b74a78ced..171573e389 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.0", + "tinymce": "6.7.1", "typeahead.js": "0.11.1", "underscore": "1.13.6", "wicg-inert": "3.1.2" @@ -16518,9 +16518,9 @@ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "node_modules/tinymce": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.7.0.tgz", - "integrity": "sha512-Wf2RSobIXQ7XNw3/v4z1lPGiH3Pjsmc/6/7fG28aIS6uVWj/7IhvOPuwfJJDeOx0o0D3nSnoLHgR2KU8JAdE+w==" + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-6.7.1.tgz", + "integrity": "sha512-SIGJgWk2d/X59VbO+i81QfNx2EP1P5t+sza2/1So3OLGtmMBhEJMag7sN/Mo8sq4s0niwb65Z51yLju32jP11g==" }, "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 fc15db2004..bf2a4c9b0c 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.0", + "tinymce": "6.7.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/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 3099bc561c..1e455aea5f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -35,15 +35,18 @@ $scope.activeApp = null; //initializes any watches - function startWatches(content) { + var watchers = []; - $scope.$watchGroup(['culture', 'segment'], + function startWatches(content) { + clearWatchers(); + + watchers.push($scope.$watchGroup(['culture', 'segment'], function (value, oldValue) { createPreviewButton($scope.content, value[0], value[1]); - }); + })); //watch for changes to isNew, set the page.isNew accordingly and load the breadcrumb if we can - $scope.$watch('isNew', function (newVal, oldVal) { + watchers.push($scope.$watch('isNew', function (newVal, oldVal) { $scope.page.isNew = Object.toBoolean(newVal); @@ -59,8 +62,12 @@ }); } } - }); + })); + } + function clearWatchers () { + watchers.forEach(w => w()); + watchers = []; } //this initializes the editor with the data which will be called more than once if the data is re-loaded @@ -109,6 +116,7 @@ bindEvents(); resetVariantFlags(); + startWatches($scope.content); } function loadBreadcrumb() { @@ -241,7 +249,6 @@ appendRuntimeData(); init(); - startWatches($scope.content); syncTreeNode($scope.content, $scope.content.path, true); @@ -265,7 +272,6 @@ appendRuntimeData(); init(); - startWatches($scope.content); resetLastListPageNumber($scope.content); @@ -346,7 +352,10 @@ labelKey: "buttons_saveAndPreview" }; - const activeVariant = content.variants?.find((variant) => content.documentType?.variations === "Nothing" || variant.compositeId === compositeId); + let activeVariant = content.variants?.find((variant) => content.documentType?.variations === "Nothing" || variant.compositeId === compositeId); + /* if we can't find the active variant and there is only one variant available, we will use that. + this happens if we have a node that can vary by culture but there is only one language available. */ + activeVariant = !activeVariant && content.variants.length === 1 ? content.variants[0] : activeVariant; $scope.previewSubButtons = activeVariant?.additionalPreviewUrls?.map((additionalPreviewUrl) => { return { diff --git a/templates/Umbraco.Templates.csproj b/templates/Umbraco.Templates.csproj index f625e5e4c7..17aabb4253 100644 --- a/templates/Umbraco.Templates.csproj +++ b/templates/Umbraco.Templates.csproj @@ -52,7 +52,7 @@ - + <_TemplateJsonFiles Include="**\.template.config\template.json" Exclude="bin\**;obj\**" /> <_TemplateJsonFiles> From 107a70ad31c7c55d3d3196fdc8285606f2db1af9 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Mon, 30 Oct 2023 14:45:34 +0100 Subject: [PATCH 02/27] bump @umbraco-ui/uui from 1.4.0 to 1.5.0 --- src/Umbraco.Web.UI.Client/package-lock.json | 1099 ++++++++++--------- src/Umbraco.Web.UI.Client/package.json | 4 +- 2 files changed, 578 insertions(+), 525 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index 171573e389..cf9238f1a8 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -7,8 +7,8 @@ "name": "ui", "dependencies": { "@microsoft/signalr": "7.0.12", - "@umbraco-ui/uui": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", + "@umbraco-ui/uui": "1.5.0", + "@umbraco-ui/uui-css": "1.5.0", "ace-builds": "1.30.0", "angular": "1.8.3", "angular-animate": "1.8.3", @@ -2242,311 +2242,158 @@ "optional": true }, "node_modules/@types/trusted-types": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.4.tgz", - "integrity": "sha512-IDaobHimLQhjwsQ/NMwRVfa/yL7L/wriQPMhw1ZJall0KX6E1oxk29XMDeilW5qTIg5aoiqf5Udy8U/51aNoQQ==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.5.tgz", + "integrity": "sha512-I3pkr8j/6tmQtKV/ZzHtuaqYSQvyjGRKH4go60Rr0IDLlFxuRT5V32uvB1mecM5G1EVAUyF/4r4QZ1GHgz+mxA==" }, "node_modules/@umbraco-ui/uui": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui/-/uui-1.4.0.tgz", - "integrity": "sha512-VG+C37WIS5Uv7ERDs/jQHT9mIncD9UrEsEQlgFnf2XZWc/TcBlV1Tvvt3xSYzZz9kIjwoymEG6lc5t6wJMqSfw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui/-/uui-1.5.0.tgz", + "integrity": "sha512-V9pAdCsiaBy+Vq23sZd9JJCk+TX6xMsclJtTUWhwCq8/YUh6KNERbdoVfMYGUZ1yyJ/g+yddQsWlYOxHNp8msw==", "dependencies": { - "@umbraco-ui/uui-action-bar": "1.4.0", - "@umbraco-ui/uui-avatar": "1.4.0", - "@umbraco-ui/uui-avatar-group": "1.4.0", - "@umbraco-ui/uui-badge": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-boolean-input": "1.4.0", - "@umbraco-ui/uui-box": "1.4.0", - "@umbraco-ui/uui-breadcrumbs": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-button-group": "1.4.0", - "@umbraco-ui/uui-button-inline-create": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0", - "@umbraco-ui/uui-card-content-node": "1.4.0", - "@umbraco-ui/uui-card-media": "1.4.0", - "@umbraco-ui/uui-card-user": "1.4.0", - "@umbraco-ui/uui-caret": "1.4.0", - "@umbraco-ui/uui-checkbox": "1.4.0", - "@umbraco-ui/uui-color-area": "1.4.0", - "@umbraco-ui/uui-color-picker": "1.4.0", - "@umbraco-ui/uui-color-slider": "1.4.0", - "@umbraco-ui/uui-color-swatch": "1.4.0", - "@umbraco-ui/uui-color-swatches": "1.4.0", - "@umbraco-ui/uui-combobox": "1.4.0", - "@umbraco-ui/uui-combobox-list": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", - "@umbraco-ui/uui-dialog": "1.4.0", - "@umbraco-ui/uui-dialog-layout": "1.4.0", - "@umbraco-ui/uui-file-dropzone": "1.4.0", - "@umbraco-ui/uui-file-preview": "1.4.0", - "@umbraco-ui/uui-form": "1.4.0", - "@umbraco-ui/uui-form-layout-item": "1.4.0", - "@umbraco-ui/uui-form-validation-message": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-icon-registry": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0", - "@umbraco-ui/uui-input": "1.4.0", - "@umbraco-ui/uui-input-file": "1.4.0", - "@umbraco-ui/uui-input-lock": "1.4.0", - "@umbraco-ui/uui-input-password": "1.4.0", - "@umbraco-ui/uui-keyboard-shortcut": "1.4.0", - "@umbraco-ui/uui-label": "1.4.0", - "@umbraco-ui/uui-loader": "1.4.0", - "@umbraco-ui/uui-loader-bar": "1.4.0", - "@umbraco-ui/uui-loader-circle": "1.4.0", - "@umbraco-ui/uui-menu-item": "1.4.0", - "@umbraco-ui/uui-modal": "1.4.0", - "@umbraco-ui/uui-pagination": "1.4.0", - "@umbraco-ui/uui-popover": "1.4.0", - "@umbraco-ui/uui-progress-bar": "1.4.0", - "@umbraco-ui/uui-radio": "1.4.0", - "@umbraco-ui/uui-range-slider": "1.4.0", - "@umbraco-ui/uui-ref": "1.4.0", - "@umbraco-ui/uui-ref-list": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0", - "@umbraco-ui/uui-ref-node-data-type": "1.4.0", - "@umbraco-ui/uui-ref-node-document-type": "1.4.0", - "@umbraco-ui/uui-ref-node-form": "1.4.0", - "@umbraco-ui/uui-ref-node-member": "1.4.0", - "@umbraco-ui/uui-ref-node-package": "1.4.0", - "@umbraco-ui/uui-ref-node-user": "1.4.0", - "@umbraco-ui/uui-scroll-container": "1.4.0", - "@umbraco-ui/uui-select": "1.4.0", - "@umbraco-ui/uui-slider": "1.4.0", - "@umbraco-ui/uui-symbol-expand": "1.4.0", - "@umbraco-ui/uui-symbol-file": "1.4.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.4.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.4.0", - "@umbraco-ui/uui-symbol-folder": "1.4.0", - "@umbraco-ui/uui-symbol-lock": "1.4.0", - "@umbraco-ui/uui-symbol-more": "1.4.0", - "@umbraco-ui/uui-symbol-sort": "1.4.0", - "@umbraco-ui/uui-table": "1.4.0", - "@umbraco-ui/uui-tabs": "1.4.0", - "@umbraco-ui/uui-tag": "1.4.0", - "@umbraco-ui/uui-textarea": "1.4.0", - "@umbraco-ui/uui-toast-notification": "1.4.0", - "@umbraco-ui/uui-toast-notification-container": "1.4.0", - "@umbraco-ui/uui-toast-notification-layout": "1.4.0", - "@umbraco-ui/uui-toggle": "1.4.0" + "@umbraco-ui/uui-action-bar": "1.5.0", + "@umbraco-ui/uui-avatar": "1.5.0", + "@umbraco-ui/uui-avatar-group": "1.5.0", + "@umbraco-ui/uui-badge": "1.5.0", + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-boolean-input": "1.5.0", + "@umbraco-ui/uui-box": "1.5.0", + "@umbraco-ui/uui-breadcrumbs": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", + "@umbraco-ui/uui-button-group": "1.5.0", + "@umbraco-ui/uui-button-inline-create": "1.5.0", + "@umbraco-ui/uui-card": "1.5.0", + "@umbraco-ui/uui-card-content-node": "1.5.0", + "@umbraco-ui/uui-card-media": "1.5.0", + "@umbraco-ui/uui-card-user": "1.5.0", + "@umbraco-ui/uui-caret": "1.5.0", + "@umbraco-ui/uui-checkbox": "1.5.0", + "@umbraco-ui/uui-color-area": "1.5.0", + "@umbraco-ui/uui-color-picker": "1.5.0", + "@umbraco-ui/uui-color-slider": "1.5.0", + "@umbraco-ui/uui-color-swatch": "1.5.0", + "@umbraco-ui/uui-color-swatches": "1.5.0", + "@umbraco-ui/uui-combobox": "1.5.0", + "@umbraco-ui/uui-combobox-list": "1.5.0", + "@umbraco-ui/uui-css": "1.5.0", + "@umbraco-ui/uui-dialog": "1.5.0", + "@umbraco-ui/uui-dialog-layout": "1.5.0", + "@umbraco-ui/uui-file-dropzone": "1.5.0", + "@umbraco-ui/uui-file-preview": "1.5.0", + "@umbraco-ui/uui-form": "1.5.0", + "@umbraco-ui/uui-form-layout-item": "1.5.0", + "@umbraco-ui/uui-form-validation-message": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0", + "@umbraco-ui/uui-icon-registry": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0", + "@umbraco-ui/uui-input": "1.5.0", + "@umbraco-ui/uui-input-file": "1.5.0", + "@umbraco-ui/uui-input-lock": "1.5.0", + "@umbraco-ui/uui-input-password": "1.5.0", + "@umbraco-ui/uui-keyboard-shortcut": "1.5.0", + "@umbraco-ui/uui-label": "1.5.0", + "@umbraco-ui/uui-loader": "1.5.0", + "@umbraco-ui/uui-loader-bar": "1.5.0", + "@umbraco-ui/uui-loader-circle": "1.5.0", + "@umbraco-ui/uui-menu-item": "1.5.0", + "@umbraco-ui/uui-modal": "1.5.0", + "@umbraco-ui/uui-pagination": "1.5.0", + "@umbraco-ui/uui-popover": "1.5.0", + "@umbraco-ui/uui-popover-container": "1.5.0", + "@umbraco-ui/uui-progress-bar": "1.5.0", + "@umbraco-ui/uui-radio": "1.5.0", + "@umbraco-ui/uui-range-slider": "1.5.0", + "@umbraco-ui/uui-ref": "1.5.0", + "@umbraco-ui/uui-ref-list": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0", + "@umbraco-ui/uui-ref-node-data-type": "1.5.0", + "@umbraco-ui/uui-ref-node-document-type": "1.5.0", + "@umbraco-ui/uui-ref-node-form": "1.5.0", + "@umbraco-ui/uui-ref-node-member": "1.5.0", + "@umbraco-ui/uui-ref-node-package": "1.5.0", + "@umbraco-ui/uui-ref-node-user": "1.5.0", + "@umbraco-ui/uui-scroll-container": "1.5.0", + "@umbraco-ui/uui-select": "1.5.0", + "@umbraco-ui/uui-slider": "1.5.0", + "@umbraco-ui/uui-symbol-expand": "1.5.0", + "@umbraco-ui/uui-symbol-file": "1.5.0", + "@umbraco-ui/uui-symbol-file-dropzone": "1.5.0", + "@umbraco-ui/uui-symbol-file-thumbnail": "1.5.0", + "@umbraco-ui/uui-symbol-folder": "1.5.0", + "@umbraco-ui/uui-symbol-lock": "1.5.0", + "@umbraco-ui/uui-symbol-more": "1.5.0", + "@umbraco-ui/uui-symbol-sort": "1.5.0", + "@umbraco-ui/uui-table": "1.5.0", + "@umbraco-ui/uui-tabs": "1.5.0", + "@umbraco-ui/uui-tag": "1.5.0", + "@umbraco-ui/uui-textarea": "1.5.0", + "@umbraco-ui/uui-toast-notification": "1.5.0", + "@umbraco-ui/uui-toast-notification-container": "1.5.0", + "@umbraco-ui/uui-toast-notification-layout": "1.5.0", + "@umbraco-ui/uui-toggle": "1.5.0", + "@umbraco-ui/uui-visually-hidden": "1.5.0" } }, "node_modules/@umbraco-ui/uui-action-bar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-action-bar/-/uui-action-bar-1.4.0.tgz", - "integrity": "sha512-FMTSWXZOhWEziGL3OFvRGczAdRu2Ic82XLh4kCpCbRlKJHouqymOfo9FT3NbHEION37JUl9bv1nKiNA0m4s2bg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-action-bar/-/uui-action-bar-1.5.0.tgz", + "integrity": "sha512-2B4ONNRTEtoKjnBo8mtvQo2Y9WW7LDSx6q85UuA+YEWfMOgZ0hr0lFepPg+qq/q90/8ZIoItoxRo16UFrPVaHQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button-group": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button-group": "1.5.0" } }, "node_modules/@umbraco-ui/uui-avatar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar/-/uui-avatar-1.4.0.tgz", - "integrity": "sha512-sUvQKsaWXP+5xQO5p2YAqQyUITiyzIzK6cVRlGRUoEla3QlhCd7YHrRnrIJTNxwmfPygDtxGa9Zx8GNkW8N91w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar/-/uui-avatar-1.5.0.tgz", + "integrity": "sha512-Iw4MQ2IMfJq590ydA6d2WXJ3gC7wO1vpA6tZj3T772B81LBZR31ftoMn3ho4cpavV5Nv4LvBnGhc2YajbsVn5A==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-avatar-group": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar-group/-/uui-avatar-group-1.4.0.tgz", - "integrity": "sha512-xpWMumABRNqVH3sdLBH43gBk8RSNjknTvqfuvfMgdrVUqAYE3cIjeadUDf9OfmzMWVoQn7PXyLSX7l/JRUhZJQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-avatar-group/-/uui-avatar-group-1.5.0.tgz", + "integrity": "sha512-hlmqOGLQIN8uJMoLgT+RPHFWIxi8Ridhp/MrKgEjuNF6sTu4bCQyN28XuC9JD+4vBcSjU4a893QGvckalQxZiA==", "dependencies": { - "@umbraco-ui/uui-avatar": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-avatar": "1.5.0", + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-badge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-badge/-/uui-badge-1.4.0.tgz", - "integrity": "sha512-6qUhcoGL43FWFS/Q6yozieaigQfKp2zqIrUGkdDpC3LqvUBshzuCFuDQEE+nobW/0oUkGV9MaMfa7hBI88eQTQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-badge/-/uui-badge-1.5.0.tgz", + "integrity": "sha512-6azqqcqRzVHXYz/JfAody6kDZQG3hiBTiCS8EEYY9GcFNqh8BvFLX4yK9R6zz5BVrjgT3qkmPpE2iIpqV6J58A==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-base": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-base/-/uui-base-1.4.0.tgz", - "integrity": "sha512-RcNY2WfE2vTyAiDVyItBdo/o5owgMF16V+IFqa4xHeFlu1i08fp9/Qmyk+5Mb4LRJatt/V21zaOM0QlloyuNUg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-base/-/uui-base-1.5.0.tgz", + "integrity": "sha512-HzKRvbf/aPA1y8l9ZLTvF5Up7W6jX8UwqVUr1B8lwckI6tgxOEFPqLya+U4papqZDh4wz/lysXSDESeVfUy8cw==", "dependencies": { "lit": "^2.3.1" } }, "node_modules/@umbraco-ui/uui-boolean-input": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-boolean-input/-/uui-boolean-input-1.4.0.tgz", - "integrity": "sha512-yIhvUpT5KBE+nmROtYdrkyTg7k5OQd2f5YpSKK2RrAA1Ex7J7ZZpGIO4B7w6wNuZLLPA657YxRADwrPKU91nNw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-boolean-input/-/uui-boolean-input-1.5.0.tgz", + "integrity": "sha512-uhIPzi7n3Z4Li3n688Q8v3725apwasZvPntm7kMdtssXay6hUHOcor+hkpPavGXRVxZGg+9gIYRM6sQWp853cA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-box": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-box/-/uui-box-1.4.0.tgz", - "integrity": "sha512-dQ8IeX86rAEmaz/ulJGDTGvmP0bMgm6LkRhGumignIRaVDLJdK5AIcPauVoq2n39IuczmoFjAEm6MFTAeQqZaQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-box/-/uui-box-1.5.0.tgz", + "integrity": "sha512-uTHBvwzS9pRu0MVfN74+bux6lK0m1AmY/7xor9ez9/uzDyIK096D9jSLTQkfDyngIhqnV6kFLbG7PqcfQURFJQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", + "@umbraco-ui/uui-base": "1.5.0", "@umbraco-ui/uui-css": "1.4.0" } }, - "node_modules/@umbraco-ui/uui-breadcrumbs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-breadcrumbs/-/uui-breadcrumbs-1.4.0.tgz", - "integrity": "sha512-NfV8uVq093JceBC/Dog30iLi9z6ZwzwyS90At3qnCdIRn/ydxPghUA0xhS0Hf83GDQRgs9Ni7XbZv1P/SFdgrw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-button": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button/-/uui-button-1.4.0.tgz", - "integrity": "sha512-8a6lZ/PLWg8iDuOv4YDhKvczWv844C3OfhPngLlmaK6UdkaiPlkxEoK41zZaVUV70B0ZhKk/odQYBp5nEUeeDA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-button-group": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-group/-/uui-button-group-1.4.0.tgz", - "integrity": "sha512-Cwb1tFQbmo8XBpcTRwM5yolrselxBiDue0z+WyGWjKVuhNK/Cxlt1X2iT+MBlsgI1xW+I611+7d4n9V57wPXlQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-button-inline-create": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-inline-create/-/uui-button-inline-create-1.4.0.tgz", - "integrity": "sha512-pngszZKSk4uIaW0L06aBjBImKykxarNp7JTx6YJqi+rF+GXTS31/gRuckWN4pN0/BgUTJMd0Q11zVWfB0uwjvA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card/-/uui-card-1.4.0.tgz", - "integrity": "sha512-eS5QdKzNqQQ+en3ZpPq88YGSWD1mSr4Nk9okpZ06fQmEZlYMMliR0A3WKFBQHhnleZafaEgHq3VwpVL1SQrluw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card-content-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-content-node/-/uui-card-content-node-1.4.0.tgz", - "integrity": "sha512-8xbaSytLMsA7pXMKI4gttgiXjRgoQFh/pc3HzaQf3hKaWfeCPUxUaponXfZXmXjqMAi+eoyyxS1qeUt+Zlt0Rw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card-media": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-media/-/uui-card-media-1.4.0.tgz", - "integrity": "sha512-rQT4m0KFYMelEszFExFMYYNIBHHcYlDd0alqiKitEUBlpu2UXCHK7mXyQlU+sFWLJ262zSONMmwSaXsqhMLVug==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0", - "@umbraco-ui/uui-symbol-file": "1.4.0", - "@umbraco-ui/uui-symbol-folder": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-card-user": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-user/-/uui-card-user-1.4.0.tgz", - "integrity": "sha512-t7C7F1sFrxAizNZJG7JDu+Wk0vizm7lN8UZCNggPiua6AkVVDpH8YN013Tk/reKxfTp9PkYh9aVUeAyyhWYa4g==", - "dependencies": { - "@umbraco-ui/uui-avatar": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-card": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-caret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-caret/-/uui-caret-1.4.0.tgz", - "integrity": "sha512-RtWgCSvFelya+E0INy95XDiLNYDH3Tv7AdMvUTUKf/5PKYp/yR5MYo70P9EvUkCVMvIFVf/VVGd9mDwvLr2k+A==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-checkbox": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-checkbox/-/uui-checkbox-1.4.0.tgz", - "integrity": "sha512-VCcYycChEPmaOo5q2QF1xsxxYQ5XToGh/z+46GmFyc5TDFP2OyOWqVm6+4gVpljcvf4aS9IRqcoONa/Bv2LQqQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-boolean-input": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-color-area": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-area/-/uui-color-area-1.4.0.tgz", - "integrity": "sha512-csIswxLN9YDhmL6veZ9iR8SjQrDi8wscPPJB0i7w4TQDI8TwlvB0mAdb86FM0eoobXLPFeMDFkYGQijWpv69Gw==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-picker": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-picker/-/uui-color-picker-1.4.0.tgz", - "integrity": "sha512-zxOpmhEGEfQtLp/RYSPNBi8S2K+KjiuVyWhvmoqgO1gb/uNU5Om2xW1Q7pz/jiKe1qwWHO3whGl8LHM6el/C2w==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-slider": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-slider/-/uui-color-slider-1.4.0.tgz", - "integrity": "sha512-XEgi6shSGCnB4LhQgalcWfsHXyC2oLGw0ZCANr9l/4LpjaoZ0Uq4H/CL8UFfwiLXbJWdzZwqQqJcP928QmUFYQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-color-swatch": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatch/-/uui-color-swatch-1.4.0.tgz", - "integrity": "sha512-/k1SgzfdA1sCueqDaGYXJyb+bZjMdffHgM4Qk5LMSjX3JDL+c6yKvoc/w2Bvky+9N1NUp+tEMbJKD7bzQalQlg==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0", - "colord": "^2.9.3" - } - }, - "node_modules/@umbraco-ui/uui-color-swatches": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatches/-/uui-color-swatches-1.4.0.tgz", - "integrity": "sha512-U6+0fu9OULPqRW0TuwVpj1PLectXM7ha2dc1Cw+rEzOtqBEbDmJTs4bh7EosMmxksmZQdXFhVkxu1yBHhXUJtQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-color-swatch": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-combobox": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox/-/uui-combobox-1.4.0.tgz", - "integrity": "sha512-epBlmRtVlUKeToA+DbYJYEWzTvKQahm2RnUMzFk9BvISP1xE9X5q7MtZLPRoiTjA9wf4SYrxIgHlYBGUOmy9lQ==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-combobox-list": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-scroll-container": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-combobox-list": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox-list/-/uui-combobox-list-1.4.0.tgz", - "integrity": "sha512-T6fOqHcOSB/NxfUmjZHlNWUU1ct9eVghXdQpA4tcPE83HSfHhWS5F1nbE9Cr/LO/al2Fe8iFfub9ed9OOsNqdA==", - "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" - } - }, - "node_modules/@umbraco-ui/uui-css": { + "node_modules/@umbraco-ui/uui-box/node_modules/@umbraco-ui/uui-css": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.4.0.tgz", "integrity": "sha512-HBCFPuXJijeZbjnjdqmg3oqOGB3RmpQKT/s/Uy0TSJfaQGfz0e73o2eRghYHWF2rdqHw6brKFrZTZHBVvCE/xA==", @@ -2554,473 +2401,679 @@ "lit": "^2.2.2" } }, - "node_modules/@umbraco-ui/uui-dialog": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog/-/uui-dialog-1.4.0.tgz", - "integrity": "sha512-FCrz17nKh2zybsDeN0AIxBQJjSFhK1q8OdZGSzaegPKx6R/xmZBPx6KPZeQnmjdGzQJHwh4xILKHXGazZbIZXA==", + "node_modules/@umbraco-ui/uui-breadcrumbs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-breadcrumbs/-/uui-breadcrumbs-1.5.0.tgz", + "integrity": "sha512-mXuzt5o4NZ1E/HVTLYq+TklX9VQSH5zce+Ef1t2EgUE3EFQH0fwcdCRBC9SpklueNj46ngGHmVhyfv8ekne1Wg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-button": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button/-/uui-button-1.5.0.tgz", + "integrity": "sha512-ujicvfqUAN0JtBcgj8OG1YcyDaArTBdP5LvNsyYB8s0dePgcws71XzJ1mbHbXhuA386ioNue04yGDL+gSFlJ/A==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-button-group": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-group/-/uui-button-group-1.5.0.tgz", + "integrity": "sha512-8yhFdfg7p1B8MM2fIxIlc0Mmhnx46scdGhqeRhvaQ2/dcdpVTI1j1hI2JyOM18TUhJeot4olLqwatlXxlFFT+A==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-button-inline-create": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-button-inline-create/-/uui-button-inline-create-1.5.0.tgz", + "integrity": "sha512-J60vRf7nzQyRYKj+qYhMQR6LrQH6PyTrxyqyfDOVGzcWKzsTuRahxuVOIOzrs489cznwRYwL11jtK32MlrSjGQ==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-card": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card/-/uui-card-1.5.0.tgz", + "integrity": "sha512-RgpnQca3rpjMG/3DAmmrExI7gmNNHBNYwfjRqgCd/3QkBwRrtT/+jdppVsGRxxW5xAN90sJ/eLP7i3F5EfWlSA==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-card-content-node": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-content-node/-/uui-card-content-node-1.5.0.tgz", + "integrity": "sha512-aYGeTsppWT0KS9orrqkl9DF2v5l3gSGhBJZqIPiHVBOzczYIcgLWJbdAkaCgpwh1Zacbv3tnB/76965fd4EwPw==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-card": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-card-media": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-media/-/uui-card-media-1.5.0.tgz", + "integrity": "sha512-0KktT0IExh06W7QP1FMNqU+tpUL1qDwWeeA19PbZPXwHg15hbSW15a+Hc4aiwqlHYHOPT2gxXoiVc7jqWlMcSQ==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-card": "1.5.0", + "@umbraco-ui/uui-symbol-file": "1.5.0", + "@umbraco-ui/uui-symbol-folder": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-card-user": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-card-user/-/uui-card-user-1.5.0.tgz", + "integrity": "sha512-xJjfkRHkt2xim1o+IvEPQiTpIQR+Z9+69096ssuGb3EkxyyUsDmH3aZZH6/+LKdtKR+7mPZVJub9TTWB4VRnwQ==", + "dependencies": { + "@umbraco-ui/uui-avatar": "1.5.0", + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-card": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-caret": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-caret/-/uui-caret-1.5.0.tgz", + "integrity": "sha512-4Apw4TMALEydo5o31gsIyICuPVyKvG/oySNup+5psU3apS0JDQ1RXCgGVDFoFxt5xzM+iJ6/J8ZOOILMVNFM6Q==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-checkbox": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-checkbox/-/uui-checkbox-1.5.0.tgz", + "integrity": "sha512-Kve+XAIkSFG9kowbZI1MpDEKihpMTtD9q36pcHiVENqxL1+Tydy60yjy3tHV8o6uamJ8qjR6ZlvLttRwLId9tQ==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-boolean-input": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-color-area": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-area/-/uui-color-area-1.5.0.tgz", + "integrity": "sha512-FF6PrUCBo2nOg5iLbD+iB8aa3Vh+skIfqjFsPD80qLE0sKQ/53juZCnCbvvp7Z0YmIqwBlWP7xGEzJBGfS6OlA==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "colord": "^2.9.3" + } + }, + "node_modules/@umbraco-ui/uui-color-picker": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-picker/-/uui-color-picker-1.5.0.tgz", + "integrity": "sha512-y/IwXhtaQJWNjwnZtYTvv47+bsmUYJzFLtXqxGckcUmyJQvoZ6DDxslTSv1B9J3QTXU0zpakqpxPszlNNHUygw==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "colord": "^2.9.3" + } + }, + "node_modules/@umbraco-ui/uui-color-slider": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-slider/-/uui-color-slider-1.5.0.tgz", + "integrity": "sha512-nkUpUxfD7VlayBHirM56xKqi1h0Opg7Q2suzxEC4KLDVLO1+L0KzsDORn1tfeantSG0PahBMbuve1XOoOwCrAA==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-color-swatch": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatch/-/uui-color-swatch-1.5.0.tgz", + "integrity": "sha512-UDqlGmJIMGyn7C23q33v8dkJoISmIAL0XZNTiPkEhwGjKRlxkbexmGd4L4vFt+nhJDRrN86JoZ64BRTHVN8V7A==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0", + "colord": "^2.9.3" + } + }, + "node_modules/@umbraco-ui/uui-color-swatches": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-color-swatches/-/uui-color-swatches-1.5.0.tgz", + "integrity": "sha512-SvTKINbckKvqkkS4XnQfpELkW2x47CUa4PsnXqioXNIWP5sBJb9Kydiu0N1+lV57fAkteqNp+YY8mFxn3a6iPA==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-color-swatch": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-combobox": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox/-/uui-combobox-1.5.0.tgz", + "integrity": "sha512-SoK4+yR0dJViXZinZ7iqowl6tvWPTTPSOBVE7FfOqOAgFoccOE/nQqjeNjSM0co80OKXqHUsh+kX/HwLjdyNEA==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", + "@umbraco-ui/uui-combobox-list": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0", + "@umbraco-ui/uui-scroll-container": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-combobox-list": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-combobox-list/-/uui-combobox-list-1.5.0.tgz", + "integrity": "sha512-5cVlhnst3p6eEHFqn6O8LMswx3wdwpzlfAghleQJW+ZUIVo7ZPXznZz7+6yvnVWxnI7+xxFebHgC0KFxGMUVvg==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-css": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.5.0.tgz", + "integrity": "sha512-jBSJg8KTWDG7DOVzz7A+UpMxMNHtddcLgt9k25vC4H+84xl+TN51RFTqF8C0JCZdWFK0eKWYlJsGqVrDfoVCcg==", + "dependencies": { + "lit": "^2.2.2" + } + }, + "node_modules/@umbraco-ui/uui-dialog": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog/-/uui-dialog-1.5.0.tgz", + "integrity": "sha512-m6J5i+eiLdNApryIY1KW/4kyunAuTpkcWBjQmxyESmlDIqRGdW0lqaahQvcZSZHto03jleUdH5wYTLNgKIb/rw==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", "@umbraco-ui/uui-css": "1.4.0" } }, "node_modules/@umbraco-ui/uui-dialog-layout": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog-layout/-/uui-dialog-layout-1.4.0.tgz", - "integrity": "sha512-67/yVhysc+wMsyVEQXSP2E21YlzoQfir/CQjxCRlfKGe8FdCck/m3HSnzyb1rvPfbXrxGUMCUmcTqDBoazBfAw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-dialog-layout/-/uui-dialog-layout-1.5.0.tgz", + "integrity": "sha512-vfZ3FMzYccGBVvSSXvCeoHYX+VU8QppXtFR2OGDZwU0b8BOKtfKTP/2VLPEWCG4vJYKPmqZESo3N9bZXWDkWSg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-dialog/node_modules/@umbraco-ui/uui-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.4.0.tgz", + "integrity": "sha512-HBCFPuXJijeZbjnjdqmg3oqOGB3RmpQKT/s/Uy0TSJfaQGfz0e73o2eRghYHWF2rdqHw6brKFrZTZHBVvCE/xA==", + "dependencies": { + "lit": "^2.2.2" } }, "node_modules/@umbraco-ui/uui-file-dropzone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-dropzone/-/uui-file-dropzone-1.4.0.tgz", - "integrity": "sha512-pbNcTS7x7fvSyCrvR+yA7HzjWLtJXLHcLZvkJ4yNoAxS1d4/5ppyi/Fyz0QakBgLWzPuBv1mKj2o6RvBy29QWA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-dropzone/-/uui-file-dropzone-1.5.0.tgz", + "integrity": "sha512-3rkTWidY4k2fyktRxfsMVTSvF+EIguv9p1Fga7v4DCNkplCp6OyJnwWby5F//+NvTHphaGchxZirOWMLgLyDog==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-symbol-file-dropzone": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-symbol-file-dropzone": "1.5.0" } }, "node_modules/@umbraco-ui/uui-file-preview": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-preview/-/uui-file-preview-1.4.0.tgz", - "integrity": "sha512-UYi4Omww0/COjheTuAUdvZHqEAITT65Vsi5NSDHaUH3AM9BSVlj0FR3wOpwF7OwbOXjIeIonMEC8xMf1JtjusQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-file-preview/-/uui-file-preview-1.5.0.tgz", + "integrity": "sha512-Re+R8uZSD3t3jUgZvzG/DfQtihss7aw+rG41IAjmRO9wBZuUAsowfgCd2OJnuOYJXeaqOYYl+QQr7pmR2a/HNQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-symbol-file": "1.4.0", - "@umbraco-ui/uui-symbol-file-thumbnail": "1.4.0", - "@umbraco-ui/uui-symbol-folder": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-symbol-file": "1.5.0", + "@umbraco-ui/uui-symbol-file-thumbnail": "1.5.0", + "@umbraco-ui/uui-symbol-folder": "1.5.0" } }, "node_modules/@umbraco-ui/uui-form": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form/-/uui-form-1.4.0.tgz", - "integrity": "sha512-jjukKI+eoKmvw9Jc8n0ryle6gAA1ogQM3GLgId509qS9qiFGxMetMJ0KQjcRkrisRM/oQjz7huf9tF1es/prOQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form/-/uui-form-1.5.0.tgz", + "integrity": "sha512-rbXFZzAg93/fzvNkxHavUr62DnSeWuVghd9CK9lhe6A9ER9cfjOcGn/INTYK3HHPBalay9IOq+WV1xxC5H6zyg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-form-layout-item": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-layout-item/-/uui-form-layout-item-1.4.0.tgz", - "integrity": "sha512-aHBfwq7Y0YAWVHpiXZ1lnwSXyLbsGdk7lPkJ6hqVaBJ77VA/N2oDGMUjsRcCd1vKtD8AA3Nc2kT2e++NlUIPDg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-layout-item/-/uui-form-layout-item-1.5.0.tgz", + "integrity": "sha512-owla3DWo1deVUEG0JzC7pE70h6Ll6lmbR+B+utbMdEgM6shEMdokpPioeCaXb8v7On9Whz+zJGAGBAYl/oyjug==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-form-validation-message": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-form-validation-message": "1.5.0" } }, "node_modules/@umbraco-ui/uui-form-validation-message": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-validation-message/-/uui-form-validation-message-1.4.0.tgz", - "integrity": "sha512-AZXcvusVb48H5YrPIj71iMMUOXn2pZtensi3fUj55sVY1RNFa+QuJW/vC/79qDBLw/vQJu3NcZGbi4q4NBKh9A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-form-validation-message/-/uui-form-validation-message-1.5.0.tgz", + "integrity": "sha512-wuWCzttkUlEctqdJi9qzSzT8h10WvoK3+5usYB9V8NpdPYzOmbXU5RDYpoTWS0nPO56C6rlRlt3TH1khIQtPJA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-icon": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon/-/uui-icon-1.4.0.tgz", - "integrity": "sha512-aLzVYbubk+VSI4iKHJSKFxlHMe9CGq5JbaUfuy9a9U/D7VfUUrroM+tDMPFP4qEvSkjthyCzdPBxodJ+QQOZew==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon/-/uui-icon-1.5.0.tgz", + "integrity": "sha512-8Sz6PaYTC8KDCKj5ed+xnlnuh9/NOs0tQGPOma1bnVxGJN8LNjl+cJSLp+iU1m3Qq50H0TG+0K/dS3WUExjbZw==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-icon-registry": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry/-/uui-icon-registry-1.4.0.tgz", - "integrity": "sha512-76XXyxq96XIp4qIT58UgY4vp4+agD2YvfpCd+Dhs/rdu5iQq56PmYoxJ7qr7JYTSf8xxZ//0/PiuamwWkPmSEw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry/-/uui-icon-registry-1.5.0.tgz", + "integrity": "sha512-ei+HnaCKFjcCYjHYC0hqncY2vDfbgRkWhftOnrhqVZPJkE4omWDmVsLSGg/vm88ar1QleDmVj+CAa4J9T+uVeg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0" } }, "node_modules/@umbraco-ui/uui-icon-registry-essential": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry-essential/-/uui-icon-registry-essential-1.4.0.tgz", - "integrity": "sha512-o1woHz7YFjyOBIQHsdoCxE3vpXrJ/Sj0QNcGexdlFqUsvv/LhHAJ9a88cmTve1Y8nYDWW2pyyKZbyX1nDokByg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-icon-registry-essential/-/uui-icon-registry-essential-1.5.0.tgz", + "integrity": "sha512-nxNEQDI4SNBXnI2/Ov60vcdzKFyRCInwZDFNAKyt31F1yTNM0EM0ne5yV4AqM6YPOKVoWzqFcLz2rx64X+oLvQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-icon-registry": "1.5.0" } }, "node_modules/@umbraco-ui/uui-input": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input/-/uui-input-1.4.0.tgz", - "integrity": "sha512-mtlONZWmLV5OOYt2APhjl9cukTktrWNl1w4yF889F/wO2ZiGasBWwL9amtW4RIby/5nxns9yGgzXXG1/6GaqYw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input/-/uui-input-1.5.0.tgz", + "integrity": "sha512-TlbSIRh2Z7xJxW0GEPENd369W1hHgr9Y8IIRE5RDllXzZc8yho4QXPJSDFQTiHMf41LIkOTfIkrQst5047FiXg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-input-file": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-file/-/uui-input-file-1.4.0.tgz", - "integrity": "sha512-qdRce6NA6VDgFR71hUhuasX28N4qmCtWscWwoU+2E/rxfYWd2MIFOSsBqnIW6R4wagw+LnC7YXV6oy4vZiCKuQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-file/-/uui-input-file-1.5.0.tgz", + "integrity": "sha512-8h/qGED5KE7sb/YE7dHapZxcWXGm0qCPJft8AGOu/ZK/WdOUV1WHynLjV4yGVZgY9PVZGc+GQTzvdgwxxpltQw==", "dependencies": { - "@umbraco-ui/uui-action-bar": "1.4.0", - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-file-dropzone": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" + "@umbraco-ui/uui-action-bar": "1.5.0", + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", + "@umbraco-ui/uui-file-dropzone": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0" } }, "node_modules/@umbraco-ui/uui-input-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-lock/-/uui-input-lock-1.4.0.tgz", - "integrity": "sha512-LK9jgCmSJFENRA+Hj7qnwhuhuYmMgWYPc44LMYdowqTKlkffr67mY2VqaK+92WbjmH8PKStJr0wf0L8tuEczWQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-lock/-/uui-input-lock-1.5.0.tgz", + "integrity": "sha512-KBhZLLD+5qyibbcp0AiJo7V4e/+GiKouGz/rCk6/3vxEKpe8CtWekcHhjrdlsHcOluQeBcb1Pdqng0wC9UTO5Q==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-input": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0", + "@umbraco-ui/uui-input": "1.5.0" } }, "node_modules/@umbraco-ui/uui-input-password": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-password/-/uui-input-password-1.4.0.tgz", - "integrity": "sha512-slxRycyh8okgl6vH89O/y9lWPkfrga6s3Myijz4RXnprWfVtntIkB5pZoM17yT9bjSfo15UKd4E4GdOS9YpcaQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-input-password/-/uui-input-password-1.5.0.tgz", + "integrity": "sha512-8wvQ/10jfufU0QWhK3gBVo5V/fzk4AuX8wPuieKZDY9Jnwkr7ugZ11DOJtaV3Az/4a0nrfF3TQ2gbBC7zHx2JA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0", - "@umbraco-ui/uui-input": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0", + "@umbraco-ui/uui-input": "1.5.0" } }, "node_modules/@umbraco-ui/uui-keyboard-shortcut": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-keyboard-shortcut/-/uui-keyboard-shortcut-1.4.0.tgz", - "integrity": "sha512-3hTFxrilMW7hGwfFtsNUmJdF0e4wk5pM8oGMuwwkKxsuxMdGzdpmht0PnB3G0EPQAsA60Xypiuvm0EgFnX91zg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-keyboard-shortcut/-/uui-keyboard-shortcut-1.5.0.tgz", + "integrity": "sha512-KVTMHl6X0T4cUA3bUgM06xzwCN3VD5W3tZloF0i6e3PTHhkyCE5tKD/2Hizm56OGb+ifaI/oN3L1m7vEPC8IHw==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-label": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-label/-/uui-label-1.4.0.tgz", - "integrity": "sha512-XTKH92Z0Apu15qI3MvJew1z3oAyOVBgByIipxVmWPb52Nlvj/Haa8QUlfksJWp4E4c2IhhYTPVXeft8CpS2q1w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-label/-/uui-label-1.5.0.tgz", + "integrity": "sha512-Sc6XuMEyivBEQDfMOA6JT7nW5H4/eD6dzUtUNabOwzCG5GUpvTMfRccpdjmzOvl9VCGNWtE9ikqCBZWexWA6YA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-loader": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader/-/uui-loader-1.4.0.tgz", - "integrity": "sha512-5KgUdzusuJeMwgIwtScuqgMnJ9NW+/G0/Osj3B20UBPwcwVm1z4s3cWlt3kKJmPA/W4fzbdTxRt2MRdSEp3+cw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader/-/uui-loader-1.5.0.tgz", + "integrity": "sha512-lhl1KqRbM5NTp08fvxgzOsbHFz04z8/WjaOar6lqNnL0R+CcFtVWQrv69Opht9Sj1NdHESmHEVnX0yodod2LhQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-loader-bar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-bar/-/uui-loader-bar-1.4.0.tgz", - "integrity": "sha512-n+sxqJxp1aKy7lF8rbB9a72OzcdhTuHif5bR2XD2NwMmEZ7jl6xd+Em+sHo35ePqqmualpwetM2DlO50/uTDgg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-bar/-/uui-loader-bar-1.5.0.tgz", + "integrity": "sha512-qUcVXi4i+ClozPc0Vfw7g90CLAQVj04F71xtatxDY5nhSWDEMEI6b/pXtN/B9TklkqfgE1mf/gRziFrpbVjLhA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-loader-circle": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-circle/-/uui-loader-circle-1.4.0.tgz", - "integrity": "sha512-mFQB6psm9W4U/g9KEPPoUNFeEju2k/oJ+J5I1g0fz20HpfvDKIoebqErcCd0wngZfk4FZm1ditpN3t2eFGBR4A==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-loader-circle/-/uui-loader-circle-1.5.0.tgz", + "integrity": "sha512-059/DJDYbgOmr/LPXbiDaTkBcInmzUUu/YDtQt/SkZPCO33uuB7TDc+++cMgFYskdXBpqesNvVfZOUd4P6zJyA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-menu-item": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-menu-item/-/uui-menu-item-1.4.0.tgz", - "integrity": "sha512-GqrjrUlzQzbctDzzg1X0fVRO4Yxll/H5oqnXZBuDZBpqu++AlXknqMuAjur0cFkeiV0Kn7N9w+uZl1NYWW9OJA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-menu-item/-/uui-menu-item-1.5.0.tgz", + "integrity": "sha512-rmKuTz0Xgf0LyQRqs3tr2Z4O6oaNCd7UmI8kEbluk4yKpk5MU38BlFY9p39fpiEVUuzjcg9pBjrEyxrC/H9xjA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-loader-bar": "1.4.0", - "@umbraco-ui/uui-symbol-expand": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-loader-bar": "1.5.0", + "@umbraco-ui/uui-symbol-expand": "1.5.0" } }, "node_modules/@umbraco-ui/uui-modal": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-modal/-/uui-modal-1.4.0.tgz", - "integrity": "sha512-v+jiYIGCLTL4NY+Td5UIgoK52pxGVWxWEe+xxNJLYSUtiRsp+7dw9UwmNqLdflR3ngfyBVY+rfEXSfbcfjiQdA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-modal/-/uui-modal-1.5.0.tgz", + "integrity": "sha512-q9g4rA8OYCPlOmZMES/O17NiAu18wtMxNHMuT6dADP2tuULE+TKT6A8vqC7aq8JkWOTAXRAFvTjTmcvm6L2pvg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-pagination": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-pagination/-/uui-pagination-1.4.0.tgz", - "integrity": "sha512-f38AuUTyZ5/JNWZFU02EzAaQ81R3sa38jClSjyDScQ9Vh+8Uwj16sRPnbnveFWU/c5URVMFpG5OGXA/RXI3WEg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-pagination/-/uui-pagination-1.5.0.tgz", + "integrity": "sha512-I3gCWbyLRFvi5fAlezQZarvj7FuEZ7NVZbbKJxqEhbo1bwOxDMXlDNxIIrxSg3R8YAuDNP9Pbdw+rnQwupuOMQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", - "@umbraco-ui/uui-button-group": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", + "@umbraco-ui/uui-button-group": "1.5.0" } }, "node_modules/@umbraco-ui/uui-popover": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover/-/uui-popover-1.4.0.tgz", - "integrity": "sha512-jbYHUGoN1S81VU4TbUh0HKipGcCnqiwINtQNDGf2W61Rgy++wBR3MfWqCaXd1K10GL8+wgkly6RsJKKUzqrDNw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover/-/uui-popover-1.5.0.tgz", + "integrity": "sha512-Ab8UL4UGxTUn6hYbTqPrMtyGpQr3Xw1E/PVKG3+j+UrNw1Ro5piKgh0TahwxLnrsXWOPXfy53oaXNYsMGenndA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-popover-container": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-popover-container/-/uui-popover-container-1.5.0.tgz", + "integrity": "sha512-issjf86TwvwLA6sJOs5pLRMFY+WBc4oeTZiJMz5mhZ5C5UoRmU65L6RP/0UnzZ4ZGY2Gpdh2YatNnZ7hVMg5ig==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-progress-bar": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-progress-bar/-/uui-progress-bar-1.4.0.tgz", - "integrity": "sha512-lJdoxiJMaDl7Qsaa6TkeuiudWV7Zer1LjWS9yO0aAZ4xWkkVxLf89qIlaTukdOat+Sr8ZtI2mjmRih5IjMdalg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-progress-bar/-/uui-progress-bar-1.5.0.tgz", + "integrity": "sha512-B/v7VsBBwo19Y+4NBRllt7Ls+WLQfx6vY57rfO8MQG7zxGznxpTSIYvd3wxdRuDsFQeVwwoYjF1/YBJ7iWUnEQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-radio": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-radio/-/uui-radio-1.4.0.tgz", - "integrity": "sha512-pIJjmzWRIKPDxzwmB4CbBJNmMhlB97NOcgMoiIruiacVGEfZTWqXYXAkNtMragYGVQ0oz+ySYxEgl4iVvg2tdw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-radio/-/uui-radio-1.5.0.tgz", + "integrity": "sha512-3e52VZHcgHB/17eLTmiZwdm7ENgfX6AF4Dw+8H2x8jdRjyvt8lbykCq+6xewAZFsLAu7vTOEKtd2RhQFI2+hwg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-range-slider": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-range-slider/-/uui-range-slider-1.4.0.tgz", - "integrity": "sha512-3rGrXEAOfztQHvD8aJlGuBfe0tXkpZgWtzq888D+8X68RMvPHs89X32FVqT8e34kK1/vfm8I7BwbDSXL6FTzbg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-range-slider/-/uui-range-slider-1.5.0.tgz", + "integrity": "sha512-oHmIoF+KrHDWiOKonIWq7n94C6CzStBXrleS6iwCgWY++ayaHKCPlCuQIYp3BmGjnMQn8Ou0r2x/RuBPuraLVQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref/-/uui-ref-1.4.0.tgz", - "integrity": "sha512-zESd9N+72zON+kLCv95zzQtfmFY10zJU9DzzLR0GdZouujtyysU5qIwJG+dTy5ewm1jzGq5DHAyJtwO6IQSx7Q==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref/-/uui-ref-1.5.0.tgz", + "integrity": "sha512-wba/OP6b/mG5kp4bUgBBcBAAy3RWTbokVyjb52FR7nyqNMnIE/UBdgi0XeBx4j6lZeEbr5k5ZOGQ1knEHbPWyQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-list": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-list/-/uui-ref-list-1.4.0.tgz", - "integrity": "sha512-r8X0dSUsbvbyvK+2Yy7jsaCE4Q+PV7CDGQAO1eArYywCuJWjdVO18zt26Abvl1Z+v5qAWnbPiJHvF0h6mYTGMg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-list/-/uui-ref-list-1.5.0.tgz", + "integrity": "sha512-sxs3hC97zDuFaV8mvXLAbqqtWk0kqDdHY9ORt9CxacdT36nQS58Sw60/plCryqoyp7P2cUZVtlEeff53OKOTCQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node/-/uui-ref-node-1.4.0.tgz", - "integrity": "sha512-Jc8ews6mC9au4gUvzjRYfTeQWgFkrSICcsxd1oPz1qxVsyXWk66b3tWjAwkyjWwI13EOp4YoGK9QsPXbQKeTvg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node/-/uui-ref-node-1.5.0.tgz", + "integrity": "sha512-bjmMgrIW+/4bmUXwMwFFaPrg2MeTxXssb6EpbBItJ+s0QhTEcTNyAD/DK3RlSMRE5VPO11sRwgCr06aIhklx0Q==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-ref": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-icon": "1.5.0", + "@umbraco-ui/uui-ref": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node-data-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-data-type/-/uui-ref-node-data-type-1.4.0.tgz", - "integrity": "sha512-tcuRnbYJxV8X3/ezP1gQ/DY2Vy9f+TDB/HFKtsNp+n891zShRbcEQ1As/fOoXGtM2JVAJ7VUYboyMhJ195hBVw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-data-type/-/uui-ref-node-data-type-1.5.0.tgz", + "integrity": "sha512-k14MI3cRELOmAwmtFeBzgCFw4+uin0JSqf85ZaqNkXSAmg+4I0ayUI6PGz+Jw66yGHvw3YNeUMKPmLO8l6M79A==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node-document-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-document-type/-/uui-ref-node-document-type-1.4.0.tgz", - "integrity": "sha512-pWESJsEm+Dect5kUws+sse0Xj8Z9+ZZkR1ZaeTHDL3kPMLxD6wMfMwWJtMeAIh7OvqJY0B/ldLonTof/ysebdA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-document-type/-/uui-ref-node-document-type-1.5.0.tgz", + "integrity": "sha512-ouytDUaSls7Hsd0WaDy4wgfKMLpxlxx16WWyHlzX5lMyhkR+S3olyNZcgDRtz9xIQV+dVE3iDsUeQcNAigCdaw==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node-form": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-form/-/uui-ref-node-form-1.4.0.tgz", - "integrity": "sha512-Xd17jQycvjq5TGfxkTZr+Kb/OU/lsUPkh4ft8/V4W/p0xv4sTio6txPw0bjDDcjJ/75zuHOLyTYicmcchcjXbA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-form/-/uui-ref-node-form-1.5.0.tgz", + "integrity": "sha512-D86A1+ScVGTer2kci6Y9X4ZAhCnm4kxUi7bCFH7dn7oi/Fq8fhs3PBuA7mr1FrZgrPvXVdW+Qa7ldxxU58NIWA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node-member": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-member/-/uui-ref-node-member-1.4.0.tgz", - "integrity": "sha512-Lhpsh1CAwQRKOaR4tPkXBBZN3fjuEJMENlVHDB2UmmSJvFozl2byEWX4dEHwvPQpe0cbU8lE0By8iNDaEbl7Qw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-member/-/uui-ref-node-member-1.5.0.tgz", + "integrity": "sha512-/UPmUNk6KP2unKnJKjr1qGkdPlFGTRj3K7H/mczCY7IbtzEccdEswWJCdUy/doIkAKbDdaqKe3/9HBoA3JtWPw==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node-package": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-package/-/uui-ref-node-package-1.4.0.tgz", - "integrity": "sha512-FQgAZ8NOjBVUWLyDg93pg6bqgONcM275qbqM3Htd+JMmmYcoYii/oTXlBqhGq7+9eDhcb8tGko2RN/tH9p8KSQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-package/-/uui-ref-node-package-1.5.0.tgz", + "integrity": "sha512-XkET8XKb3XxmjlIDrmtwm9o0QsaG81bcpUBEBA/wUC0OcJNrjTKyv6ciAVDP7HaW6XpN8XwsRbqdcrYwM8lXDQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0" } }, "node_modules/@umbraco-ui/uui-ref-node-user": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-user/-/uui-ref-node-user-1.4.0.tgz", - "integrity": "sha512-dmp44LDXJbnupP8dnUpAMSPCU2+udhMSE9uQDx1hfmX08Q49Phw6R4Az9h1ESh5uSxSm6UEb/Y7JEblods7C3w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-ref-node-user/-/uui-ref-node-user-1.5.0.tgz", + "integrity": "sha512-9TrIr1JWw3cIkWfQrdv9iLRIqm/dd10d6uZEWaGJ/MuxyCywqMg/LSApV/NLapB4HXhIG4pGCiXvUa8OVW99ew==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-ref-node": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-ref-node": "1.5.0" } }, "node_modules/@umbraco-ui/uui-scroll-container": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-scroll-container/-/uui-scroll-container-1.4.0.tgz", - "integrity": "sha512-/Rfqjtw+9LCCjvxl/MEmAjVfn4+aE8elfZ77EoItbF79R8WVmoJsIJUezjFp/Hvtp51PsgVgu/Da94dxTR4QBA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-scroll-container/-/uui-scroll-container-1.5.0.tgz", + "integrity": "sha512-Xj5jnmCEDyRENmWtuPI1QYEMzrmi/9/LaajkPEIZEYVu2owI940F0viS5X+X/FvKehSxoSt9ainCwkLphgzNiw==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-select": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-select/-/uui-select-1.4.0.tgz", - "integrity": "sha512-bvdVIGot2vWiuoQmQL9dCriY8KnmpqLyn0q6FCvx7xGAl9nFBn1MfZFbs4INxriIGWjq17YFvUXklTWuhMLGTA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-select/-/uui-select-1.5.0.tgz", + "integrity": "sha512-lcMiIM6WxF5YraIXAqSpujx3OJzq6Snfik0BUypTWbUZdKVQTgLPh3A6We9PdD6K64AX2Zk4eH8yhQ+5GNImzQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-slider": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-slider/-/uui-slider-1.4.0.tgz", - "integrity": "sha512-eDTIcXhYAiMSpPwI5e5gnMMpr0zOpx8te8pxF6K2YrGo8mCO2CI1zXZTzuv7e4ImL4HLmLoph8kbk+/wlrEtLw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-slider/-/uui-slider-1.5.0.tgz", + "integrity": "sha512-Mp6xz7C7GbAuQ1Totd2WLzvS56ekx4l31mAvUvor0GqrUF/hHxwfrGZOAWoBqoTdKQAFKbZVSM782a+cwNv3hg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-expand": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-expand/-/uui-symbol-expand-1.4.0.tgz", - "integrity": "sha512-vSWRYiUwTjERuWtbiAW7IB49s57bqjN2XrSmCrOtyS9i4t5jIjsZ11If97WD+gQI/tt+khQZ85oPWNcj6C3eVQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-expand/-/uui-symbol-expand-1.5.0.tgz", + "integrity": "sha512-ZCuGAJT2qFs4wQ6Z+g/qV3obv/SbriMnaIOGy6XTTAuMlh2+aNAwm33Je0wYKCTwHNUmnl427wTMEkQcMziD4g==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-file": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file/-/uui-symbol-file-1.4.0.tgz", - "integrity": "sha512-wGmdw47jXjIcjpThf/TZ+6EZh+aQwqBA/1SMlgTtNBbUZDSy77NZ0pOWw8SaXzKqRrDqgFqIZukb7MILio3fwQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file/-/uui-symbol-file-1.5.0.tgz", + "integrity": "sha512-ClB/lT/ebyUBmPqExB2ZinMOo/bCMEgjGxjkXy2THX4lOLUqvjDNEKLq99MAREKSh/mmGq7iB3Z/hd9/EDu75Q==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-file-dropzone": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-dropzone/-/uui-symbol-file-dropzone-1.4.0.tgz", - "integrity": "sha512-GftR6cK+9kbY43fV9a3+ICJX0rn8iT6SEe9vt85Uu4JMi2GCOT3TnKnIxgXRP3u9SyHhMNMiWmSgRfLpgJ2v/w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-dropzone/-/uui-symbol-file-dropzone-1.5.0.tgz", + "integrity": "sha512-0YL88rFFI5SOzzORtm1VtMihN4if7r0CIRe5Q3Sv0WwHjrMfIM08DeONCgN2j+ZoKgnTvt9KpE1OGigshouRug==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-file-thumbnail": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-thumbnail/-/uui-symbol-file-thumbnail-1.4.0.tgz", - "integrity": "sha512-Xlu7NQ88AiQI9kfKOQKi1kH0zMkop7GqtGyuIXbnt7rM3EZfioTdltW1NvqgKzc2QpZPqMY1s449hravObHUUg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-file-thumbnail/-/uui-symbol-file-thumbnail-1.5.0.tgz", + "integrity": "sha512-/qkf6AdAIsRmUfsBdtFkFk5wPWw6JvSVHvgk/UvZulHHb2F8TamPSJfb6voh86Vq8DzVIcy3ZbqatxH7LZBY1g==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-folder": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-folder/-/uui-symbol-folder-1.4.0.tgz", - "integrity": "sha512-/cpV6Br3bOZkOh6YNr5PbIA/+NKKjyj1PkJwITSGm5/TnW2a4J5nzJTVn5ez7IjId176loRDZM2w05bemRavmA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-folder/-/uui-symbol-folder-1.5.0.tgz", + "integrity": "sha512-Sxt4n5IBT+XIqu2nJxP4RnhourwC+1X5bD40YgUBmqZJ9KV//tox4zo2elU19WCeRZFkklZGfn2smLY1FD0OGg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-lock": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-lock/-/uui-symbol-lock-1.4.0.tgz", - "integrity": "sha512-BUPxOwhjjl4GVixbbGkKOPi9FI+C1fr1cy5NT2uLNY64z5r3jFzbnHMySKGzvpfig8wD+1hsuSPGP3lypzknOQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-lock/-/uui-symbol-lock-1.5.0.tgz", + "integrity": "sha512-EH7tEPCB+PTyjWbW+bdekk4M5hcjvYYpCKTnl3Pdpzh0mrxHPt9xa8908JB0tG8n0m0EcP+L7k8pthUmkgpK7A==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-more": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-more/-/uui-symbol-more-1.4.0.tgz", - "integrity": "sha512-fx125CCeBY+sspQpWITYt79AKYZ11NFaa72Zquz8cxH+hQA1z32jOUDL+m6oF3jTYwQkKQlCoff3VnOaJ91VyA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-more/-/uui-symbol-more-1.5.0.tgz", + "integrity": "sha512-EuhU4kle4swMFZnsguWPz77rOtrk0IQcXuEA60fjzFGJCwsg7yyu9Ns209IEUsYh5ktstj8pXKT8+ZDila5umg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-symbol-sort": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-sort/-/uui-symbol-sort-1.4.0.tgz", - "integrity": "sha512-pVeT7qrKhRK8NUX3IDodSK0GNAKOKyWyzRhrxKrDT7wRuMManKmAK6WAVYpLaRqO+PRF8+NljfoCOEtJAHlGUg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-symbol-sort/-/uui-symbol-sort-1.5.0.tgz", + "integrity": "sha512-/cifoZXuZbDmuZFPD0rr95Gpuy18DnboOYb/Ir6G3PANJ0fWOhzykHUrdx18ItLzhhwfE3dcZk4EWcGrEkfnfg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-table": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-table/-/uui-table-1.4.0.tgz", - "integrity": "sha512-wpEqTmUQrAWjloeHZQqzAt5HR+j5ihMJusHpqZmY4076LcvnmpZHPhtmwpIzosZNqRq2N1rbrPIyEotlzSg9Fw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-table/-/uui-table-1.5.0.tgz", + "integrity": "sha512-tjhpEzBYCQdgieoXcIgcOjROrScF0Ifutz/6gmpcdrXYbgZ+YkWX7dSLAeQj3fzGebaPbNYzGOmGZA9/opZ1rg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-tabs": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tabs/-/uui-tabs-1.4.0.tgz", - "integrity": "sha512-RWoLJHwMb9MbKqMyuyz3DaSc9ZGCa/NBtgBDpKpn/8oolbmNYBnr9e4sabHARtqfsEWFWKWP3kUw9iTQZNa0oA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tabs/-/uui-tabs-1.5.0.tgz", + "integrity": "sha512-0D5NLufis9Tzc5Vr+fl8Z0wABHyz1Tep76Qnx0nXyYzAZvdNq2IxThHbGqA1cb+FjVJSKdfp6ONfiPc/SIVAzA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", + "@umbraco-ui/uui-popover-container": "1.5.0", + "@umbraco-ui/uui-symbol-more": "1.5.0" } }, "node_modules/@umbraco-ui/uui-tag": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tag/-/uui-tag-1.4.0.tgz", - "integrity": "sha512-9R+WJrJav780ZoA+dbZb7bHYazxrHxADnLdNOHoLvNyggLyxIT/SRsSxrP3x9zFRwbcRLZ8MRxQ3I32YiWacKw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-tag/-/uui-tag-1.5.0.tgz", + "integrity": "sha512-OZGitHjdn4coj1x7F7zfeIx5M9NhGd8+CqpD915V9Qm8YlTQxFLq1M8tqjIxaYAB5EcHXuyzRpSUCrt/WUvipA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-textarea": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-textarea/-/uui-textarea-1.4.0.tgz", - "integrity": "sha512-nd6kWBmAvWaNLmXbEhfLRnWMfAp8rkll7XtHec9W32EQJwcHlYrS3wga6Xu32d3rKb3zUg+VXHh3EKKQH8M4uQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-textarea/-/uui-textarea-1.5.0.tgz", + "integrity": "sha512-+zDqbYKYfaiG0IXEaQatUaWsD4umtkTtbCMnqVPMhxwneVoE9d69ejat2zLFUI/ERm3nKMyq/NRfxzXJgzlDng==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/@umbraco-ui/uui-toast-notification": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification/-/uui-toast-notification-1.4.0.tgz", - "integrity": "sha512-ioiTTxqaOV/2ggnK9/IrnJPf1KRaKEIXd6qrXkMaYH1orCmv3BIdQMnl3TxFOM1YMlnbVZrfxBe2++iqV6TxHA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification/-/uui-toast-notification-1.5.0.tgz", + "integrity": "sha512-cFjz4/uZudR3yuSqK5gqzAio55ZOOxQAOc8bC5keS0HXL84JcDwrEP4/Nz7X/uUNUqauYZG/iBUirAvqfv7Osw==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-button": "1.4.0", + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-button": "1.5.0", "@umbraco-ui/uui-css": "1.4.0", - "@umbraco-ui/uui-icon": "1.4.0", - "@umbraco-ui/uui-icon-registry-essential": "1.4.0" + "@umbraco-ui/uui-icon": "1.5.0", + "@umbraco-ui/uui-icon-registry-essential": "1.5.0" } }, "node_modules/@umbraco-ui/uui-toast-notification-container": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-container/-/uui-toast-notification-container-1.4.0.tgz", - "integrity": "sha512-VIftKhOoQ0EdtM9pvDUM2IcvR8S9Fveh/QwMHgGLVlsgUogBNkCPGJKLfh9hzE5RS2v9FdPIkk72qP2A4fpspQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-container/-/uui-toast-notification-container-1.5.0.tgz", + "integrity": "sha512-AB4kwgocUeDwkxiCYNH0AOMEtExDS6sEq9sk2i8AGDAEjprAB3m0HM9AlrA+T0V1GtSuv+Q1DEuCyxnVbuK0WQ==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-toast-notification": "1.4.0" + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-toast-notification": "1.5.0" } }, "node_modules/@umbraco-ui/uui-toast-notification-layout": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-layout/-/uui-toast-notification-layout-1.4.0.tgz", - "integrity": "sha512-Secrk5+GlZYzOrg1MQ28+rLGW5krXYxYSAhSe5uDKOqTFLjuag7/qiraQDG3xBtf9ZfAAJ3qUy9n50adshoDbA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toast-notification-layout/-/uui-toast-notification-layout-1.5.0.tgz", + "integrity": "sha512-rM7cGCdMolhsndfZT9zGAPI9P3bl1lNpjDhWI124Mgx+KS8t2Q2h9O+7FGqFnjCTJOQES1pdQ+enl2NxCuEkNg==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", + "@umbraco-ui/uui-base": "1.5.0", "@umbraco-ui/uui-css": "1.4.0" } }, - "node_modules/@umbraco-ui/uui-toggle": { + "node_modules/@umbraco-ui/uui-toast-notification-layout/node_modules/@umbraco-ui/uui-css": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toggle/-/uui-toggle-1.4.0.tgz", - "integrity": "sha512-APIOxs96fcn6HvD/SksN7rhEk6IAta7XU6s0T2Fa+RPIeOBS0NbbvFUX6hW3qjpiD5DdsjOpO2jn/R1fH3nqnQ==", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.4.0.tgz", + "integrity": "sha512-HBCFPuXJijeZbjnjdqmg3oqOGB3RmpQKT/s/Uy0TSJfaQGfz0e73o2eRghYHWF2rdqHw6brKFrZTZHBVvCE/xA==", "dependencies": { - "@umbraco-ui/uui-base": "1.4.0", - "@umbraco-ui/uui-boolean-input": "1.4.0" + "lit": "^2.2.2" + } + }, + "node_modules/@umbraco-ui/uui-toast-notification/node_modules/@umbraco-ui/uui-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-css/-/uui-css-1.4.0.tgz", + "integrity": "sha512-HBCFPuXJijeZbjnjdqmg3oqOGB3RmpQKT/s/Uy0TSJfaQGfz0e73o2eRghYHWF2rdqHw6brKFrZTZHBVvCE/xA==", + "dependencies": { + "lit": "^2.2.2" + } + }, + "node_modules/@umbraco-ui/uui-toggle": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-toggle/-/uui-toggle-1.5.0.tgz", + "integrity": "sha512-vsJSpBSmlrLzspCa1dGQGYXfc6RwTGTzSlNQdnzzP7qefVRP4GlOaqYV0TJhHMcYdbai+iEkrLznzJQvM9JFLA==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0", + "@umbraco-ui/uui-boolean-input": "1.5.0" + } + }, + "node_modules/@umbraco-ui/uui-visually-hidden": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@umbraco-ui/uui-visually-hidden/-/uui-visually-hidden-1.5.0.tgz", + "integrity": "sha512-3Imqxp8+hvirakPogqzvRlU+uhshpGRdrEMU7phCS5VGzDEl8NL1BhxR31EQAw7DspwbD5non3ZwbTwLYydfCg==", + "dependencies": { + "@umbraco-ui/uui-base": "1.5.0" } }, "node_modules/abab": { diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index bf2a4c9b0c..7b7f83c34b 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -19,8 +19,8 @@ }, "dependencies": { "@microsoft/signalr": "7.0.12", - "@umbraco-ui/uui": "1.4.0", - "@umbraco-ui/uui-css": "1.4.0", + "@umbraco-ui/uui": "1.5.0", + "@umbraco-ui/uui-css": "1.5.0", "ace-builds": "1.30.0", "angular": "1.8.3", "angular-animate": "1.8.3", From d5ff80352e2acbeec5ff9cf3de4a0a8d7be79a8e Mon Sep 17 00:00:00 2001 From: Nikolaj Brask-Nielsen Date: Wed, 1 Nov 2023 09:59:32 +0100 Subject: [PATCH 03/27] Fix custom dbcontexts extention methods (#14937) * test: Create failing test * feat: New extension methods for adding Umbraco DBContexts * test: Cleaned up integration tests --- .../SqlServerMigrationProvider.cs | 2 +- .../SqlServerMigrationProviderSetup.cs | 2 +- .../SqliteMigrationProvider.cs | 3 +- .../SqliteMigrationProviderSetup.cs | 2 +- .../Composition/UmbracoEFCoreComposer.cs | 2 +- .../Constants-ProviderNames.cs | 11 ++ ...mbracoEFCoreServiceCollectionExtensions.cs | 115 +++++++++++++++++- .../UmbracoDbContext.cs | 3 +- .../DbContext/CustomDbContextTests.cs | 101 +++++++++++++++ 9 files changed, 231 insertions(+), 10 deletions(-) create mode 100644 src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs create mode 100644 tests/Umbraco.Tests.Integration/Umbraco.Persistence.EFCore/DbContext/CustomDbContextTests.cs diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs index bac08556a3..57a3a634fe 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs @@ -10,7 +10,7 @@ public class SqlServerMigrationProvider : IMigrationProvider public SqlServerMigrationProvider(IDbContextFactory dbContextFactory) => _dbContextFactory = dbContextFactory; - public string ProviderName => "Microsoft.Data.SqlClient"; + public string ProviderName => Constants.ProviderNames.SQLServer; public async Task MigrateAsync(EFCoreMigration migration) { diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs index 6b161fc47f..6425e712f6 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs @@ -5,7 +5,7 @@ namespace Umbraco.Cms.Persistence.EFCore.SqlServer; public class SqlServerMigrationProviderSetup : IMigrationProviderSetup { - public string ProviderName => "Microsoft.Data.SqlClient"; + public string ProviderName => Constants.ProviderNames.SQLServer; public void Setup(DbContextOptionsBuilder builder, string? connectionString) { diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs index 05d4024bb3..1ff7b1535e 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Umbraco.Cms.Persistence.EFCore.Migrations; using Umbraco.Extensions; +using Umbraco.Cms.Persistence.EFCore; namespace Umbraco.Cms.Persistence.EFCore.Sqlite; @@ -11,7 +12,7 @@ public class SqliteMigrationProvider : IMigrationProvider public SqliteMigrationProvider(IDbContextFactory dbContextFactory) => _dbContextFactory = dbContextFactory; - public string ProviderName => "Microsoft.Data.Sqlite"; + public string ProviderName => Constants.ProviderNames.SQLLite; public async Task MigrateAsync(EFCoreMigration migration) { diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs index 4cba457768..bb6b9feac7 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs @@ -5,7 +5,7 @@ namespace Umbraco.Cms.Persistence.EFCore.Sqlite; public class SqliteMigrationProviderSetup : IMigrationProviderSetup { - public string ProviderName => "Microsoft.Data.Sqlite"; + public string ProviderName => Constants.ProviderNames.SQLLite; public void Setup(DbContextOptionsBuilder builder, string? connectionString) { diff --git a/src/Umbraco.Cms.Persistence.EFCore/Composition/UmbracoEFCoreComposer.cs b/src/Umbraco.Cms.Persistence.EFCore/Composition/UmbracoEFCoreComposer.cs index 8b8627df47..1b751c558b 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Composition/UmbracoEFCoreComposer.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Composition/UmbracoEFCoreComposer.cs @@ -20,7 +20,7 @@ public class UmbracoEFCoreComposer : IComposer builder.AddNotificationAsyncHandler(); builder.AddNotificationAsyncHandler(); - builder.Services.AddUmbracoEFCoreContext((options, connectionString, providerName) => + builder.Services.AddUmbracoDbContext((options) => { // Register the entity sets needed by OpenIddict. options.UseOpenIddict(); diff --git a/src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs b/src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs new file mode 100644 index 0000000000..0f29e5944c --- /dev/null +++ b/src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs @@ -0,0 +1,11 @@ +namespace Umbraco.Cms.Persistence.EFCore; + +public static partial class Constants +{ + public static class ProviderNames + { + public const string SQLLite = "Microsoft.Data.Sqlite"; + + public const string SQLServer = "Microsoft.Data.SqlClient"; + } +} diff --git a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs index ded5be40fd..8fb8e53617 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs @@ -1,7 +1,9 @@ +using System; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using Serilog; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DistributedLocking; @@ -16,6 +18,7 @@ public static class UmbracoEFCoreServiceCollectionExtensions { public delegate void DefaultEFCoreOptionsAction(DbContextOptionsBuilder options, string? providerName, string? connectionString); + [Obsolete("Use AddUmbracoDbContext(this IServiceCollection services, Action? optionsAction = null) instead.")] public static IServiceCollection AddUmbracoEFCoreContext(this IServiceCollection services, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null) where T : DbContext { @@ -24,7 +27,7 @@ public static class UmbracoEFCoreServiceCollectionExtensions sp => { SetupDbContext(defaultEFCoreOptionsAction, sp, optionsBuilder); - return new UmbracoPooledDbContextFactory(sp.GetRequiredService(),optionsBuilder.Options); + return new UmbracoPooledDbContextFactory(sp.GetRequiredService(), optionsBuilder.Options); }); services.AddPooledDbContextFactory((provider, builder) => SetupDbContext(defaultEFCoreOptionsAction, provider, builder)); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); @@ -38,6 +41,7 @@ public static class UmbracoEFCoreServiceCollectionExtensions return services; } + [Obsolete("Use AddUmbracoDbContext(this IServiceCollection services, Action? optionsAction = null) instead.")] public static IServiceCollection AddUmbracoEFCoreContext(this IServiceCollection services, string connectionString, string providerName, DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction = null) where T : DbContext { @@ -52,8 +56,8 @@ public static class UmbracoEFCoreServiceCollectionExtensions services.TryAddSingleton>( sp => { - SetupDbContext(defaultEFCoreOptionsAction, sp, optionsBuilder); - return new UmbracoPooledDbContextFactory(sp.GetRequiredService(),optionsBuilder.Options); + defaultEFCoreOptionsAction?.Invoke(optionsBuilder, providerName, connectionString); + return new UmbracoPooledDbContextFactory(sp.GetRequiredService(), optionsBuilder.Options); }); services.AddPooledDbContextFactory(options => defaultEFCoreOptionsAction?.Invoke(options, providerName, connectionString)); services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); @@ -67,12 +71,117 @@ public static class UmbracoEFCoreServiceCollectionExtensions return services; } + /// + /// Adds a EFCore DbContext with all the services needed to integrate with Umbraco scopes. + /// + /// + /// + /// + /// + public static IServiceCollection AddUmbracoDbContext(this IServiceCollection services, Action? optionsAction = null) + where T : DbContext + { + return AddUmbracoDbContext(services, (IServiceProvider _, DbContextOptionsBuilder options) => + { + optionsAction?.Invoke(options); + }); + } + + /// + /// Adds a EFCore DbContext with all the services needed to integrate with Umbraco scopes. + /// + /// + /// + /// + /// + public static IServiceCollection AddUmbracoDbContext(this IServiceCollection services, Action? optionsAction = null) + where T : DbContext + { + optionsAction ??= (sp, options) => { }; + + var optionsBuilder = new DbContextOptionsBuilder(); + + services.TryAddSingleton>(sp => + { + optionsAction.Invoke(sp, optionsBuilder); + return new UmbracoPooledDbContextFactory(sp.GetRequiredService(), optionsBuilder.Options); + }); + services.AddPooledDbContextFactory(optionsAction); + services.AddTransient(services => services.GetRequiredService>().CreateDbContext()); + + services.AddUnique, AmbientEFCoreScopeStack>(); + services.AddUnique, EFCoreScopeAccessor>(); + services.AddUnique, EFCoreScopeProvider>(); + services.AddSingleton>(); + services.AddSingleton>(); + + return services; + } + + /// + /// Sets the database provider. I.E UseSqlite or UseSqlServer based on the provider name. + /// + /// + /// + /// + /// + /// + /// Only supports the databases normally supported in Umbraco. + /// + public static void UseDatabaseProvider(this DbContextOptionsBuilder builder, string providerName, string connectionString) + { + switch (providerName) + { + case Cms.Persistence.EFCore.Constants.ProviderNames.SQLServer: + builder.UseSqlServer(connectionString); + break; + case Cms.Persistence.EFCore.Constants.ProviderNames.SQLLite: + builder.UseSqlite(connectionString); + break; + default: + throw new InvalidDataException($"The provider {providerName} is not supported. Manually add the add the UseXXX statement to the options. I.E UseNpgsql()"); + } + } + + /// + /// Sets the database provider to use based on the Umbraco connection string. + /// + /// + /// + public static void UseUmbracoDatabaseProvider(this DbContextOptionsBuilder builder, IServiceProvider serviceProvider) + { + ConnectionStrings connectionStrings = serviceProvider.GetRequiredService>().CurrentValue; + + // Replace data directory + string? dataDirectory = AppDomain.CurrentDomain.GetData(Constants.System.DataDirectoryName)?.ToString(); + if (string.IsNullOrEmpty(dataDirectory) is false) + { + connectionStrings.ConnectionString = connectionStrings.ConnectionString?.Replace(Constants.System.DataDirectoryPlaceholder, dataDirectory); + } + + if (string.IsNullOrEmpty(connectionStrings.ProviderName)) + { + Log.Warning("No database provider was set. ProviderName is null"); + return; + } + + if (string.IsNullOrEmpty(connectionStrings.ConnectionString)) + { + Log.Warning("No database provider was set. Connection string is null"); + return; + } + + builder.UseDatabaseProvider(connectionStrings.ProviderName, connectionStrings.ConnectionString); + } + + [Obsolete] private static void SetupDbContext(DefaultEFCoreOptionsAction? defaultEFCoreOptionsAction, IServiceProvider provider, DbContextOptionsBuilder builder) { ConnectionStrings connectionStrings = GetConnectionStringAndProviderName(provider); defaultEFCoreOptionsAction?.Invoke(builder, connectionStrings.ConnectionString, connectionStrings.ProviderName); } + [Obsolete] private static ConnectionStrings GetConnectionStringAndProviderName(IServiceProvider serviceProvider) { ConnectionStrings connectionStrings = serviceProvider.GetRequiredService>().CurrentValue; diff --git a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs index 042cf2a52f..60e519de4c 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/UmbracoDbContext.cs @@ -3,7 +3,6 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Persistence.EFCore.Migrations; @@ -77,7 +76,7 @@ public class UmbracoDbContext : DbContext foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes()) { - entity.SetTableName(Constants.DatabaseSchema.TableNamePrefix + entity.GetTableName()); + entity.SetTableName(Core.Constants.DatabaseSchema.TableNamePrefix + entity.GetTableName()); } } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Persistence.EFCore/DbContext/CustomDbContextTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Persistence.EFCore/DbContext/CustomDbContextTests.cs new file mode 100644 index 0000000000..bfa3adb92b --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Persistence.EFCore/DbContext/CustomDbContextTests.cs @@ -0,0 +1,101 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using NUnit.Framework; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Persistence.EFCore.DbContext; + +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] +public class CustomDbContextUmbracoProviderTests : UmbracoIntegrationTest +{ + [Test] + public void Can_Register_Custom_DbContext_And_Resolve() + { + var dbContext = Services.GetRequiredService(); + + Assert.IsNotNull(dbContext); + Assert.IsNotEmpty(dbContext.Database.GetConnectionString()); + } + + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.Services.AddUmbracoDbContext((serviceProvider, options) => + { + options.UseUmbracoDatabaseProvider(serviceProvider); + }); + } + + internal class CustomDbContext : Microsoft.EntityFrameworkCore.DbContext + { + public CustomDbContext(DbContextOptions options) + : base(options) + { + } + } +} + + +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] +public class CustomDbContextCustomSqliteProviderTests : UmbracoIntegrationTest +{ + [Test] + public void Can_Register_Custom_DbContext_And_Resolve() + { + var dbContext = Services.GetRequiredService(); + + Assert.IsNotNull(dbContext); + Assert.IsNotEmpty(dbContext.Database.GetConnectionString()); + } + + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.Services.AddUmbracoDbContext((serviceProvider, options) => + { + options.UseSqlite("Data Source=:memory:;Version=3;New=True;"); + }); + } + + internal class CustomDbContext : Microsoft.EntityFrameworkCore.DbContext + { + public CustomDbContext(DbContextOptions options) + : base(options) + { + } + } +} + +[Obsolete] +[TestFixture] +[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Logger = UmbracoTestOptions.Logger.Console)] +public class CustomDbContextLegacyExtensionProviderTests : UmbracoIntegrationTest +{ + [Test] + public void Can_Register_Custom_DbContext_And_Resolve() + { + var dbContext = Services.GetRequiredService(); + + Assert.IsNotNull(dbContext); + Assert.IsNotEmpty(dbContext.Database.GetConnectionString()); + } + + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.Services.AddUmbracoEFCoreContext("Data Source=:memory:;Version=3;New=True;", "Microsoft.Data.Sqlite", (options, connectionString, providerName) => + { + options.UseSqlite(connectionString); + }); + } + + internal class CustomDbContext : Microsoft.EntityFrameworkCore.DbContext + { + public CustomDbContext(DbContextOptions options) + : base(options) + { + } + } +} + From 52fd853d52851fc19b3f55162c86491f57af148e Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:47:27 +0100 Subject: [PATCH 04/27] Move to core (#15084) --- .../SqlServerMigrationProvider.cs | 1 + .../SqlServerMigrationProviderSetup.cs | 1 + .../SqliteMigrationProvider.cs | 2 +- .../SqliteMigrationProviderSetup.cs | 1 + .../Extensions/UmbracoEFCoreServiceCollectionExtensions.cs | 4 ++-- .../Constants-ProviderNames.cs | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) rename src/{Umbraco.Cms.Persistence.EFCore => Umbraco.Core}/Constants-ProviderNames.cs (84%) diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs index 57a3a634fe..823a9e737f 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProvider.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Umbraco.Cms.Core; using Umbraco.Cms.Persistence.EFCore.Migrations; using Umbraco.Extensions; diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs index 6425e712f6..2d561e9f5a 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/SqlServerMigrationProviderSetup.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Umbraco.Cms.Core; using Umbraco.Cms.Persistence.EFCore.Migrations; namespace Umbraco.Cms.Persistence.EFCore.SqlServer; diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs index 1ff7b1535e..468f52021f 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProvider.cs @@ -1,7 +1,7 @@ using Microsoft.EntityFrameworkCore; +using Umbraco.Cms.Core; using Umbraco.Cms.Persistence.EFCore.Migrations; using Umbraco.Extensions; -using Umbraco.Cms.Persistence.EFCore; namespace Umbraco.Cms.Persistence.EFCore.Sqlite; diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs index bb6b9feac7..3a1b97e76c 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/SqliteMigrationProviderSetup.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Umbraco.Cms.Core; using Umbraco.Cms.Persistence.EFCore.Migrations; namespace Umbraco.Cms.Persistence.EFCore.Sqlite; diff --git a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs index 8fb8e53617..d901088064 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs +++ b/src/Umbraco.Cms.Persistence.EFCore/Extensions/UmbracoEFCoreServiceCollectionExtensions.cs @@ -132,10 +132,10 @@ public static class UmbracoEFCoreServiceCollectionExtensions { switch (providerName) { - case Cms.Persistence.EFCore.Constants.ProviderNames.SQLServer: + case Constants.ProviderNames.SQLServer: builder.UseSqlServer(connectionString); break; - case Cms.Persistence.EFCore.Constants.ProviderNames.SQLLite: + case Constants.ProviderNames.SQLLite: builder.UseSqlite(connectionString); break; default: diff --git a/src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs b/src/Umbraco.Core/Constants-ProviderNames.cs similarity index 84% rename from src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs rename to src/Umbraco.Core/Constants-ProviderNames.cs index 0f29e5944c..67f376612c 100644 --- a/src/Umbraco.Cms.Persistence.EFCore/Constants-ProviderNames.cs +++ b/src/Umbraco.Core/Constants-ProviderNames.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Persistence.EFCore; +namespace Umbraco.Cms.Core; public static partial class Constants { From fc81cc8f380e1d0a8e8c3cb49566d18a0926f5c1 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:30:32 +0100 Subject: [PATCH 05/27] Check content permissions before performing action (#15043) * Setting actionContext.Result when authz wasn't successful * Taking into account permissions when it is a new node * Cleanup * Passing nodeId as path when new item --- .../Security/ContentPermissions.cs | 59 ++++++++----------- .../Controllers/ContentController.cs | 23 ++++---- .../Controllers/ContentControllerBase.cs | 1 - .../Filters/ContentSaveValidationAttribute.cs | 1 + 4 files changed, 36 insertions(+), 48 deletions(-) diff --git a/src/Umbraco.Core/Security/ContentPermissions.cs b/src/Umbraco.Core/Security/ContentPermissions.cs index d43e527a62..6c70326699 100644 --- a/src/Umbraco.Core/Security/ContentPermissions.cs +++ b/src/Umbraco.Core/Security/ContentPermissions.cs @@ -165,12 +165,7 @@ public class ContentPermissions throw new ArgumentNullException(nameof(user)); } - if (permissionsToCheck == null) - { - permissionsToCheck = Array.Empty(); - } - - bool? hasPathAccess = null; + bool hasPathAccess; entity = null; if (nodeId == Constants.System.Root) @@ -181,19 +176,17 @@ public class ContentPermissions { hasPathAccess = user.HasContentBinAccess(_entityService, _appCaches); } - - if (hasPathAccess.HasValue) + else { - return hasPathAccess.Value ? ContentAccess.Granted : ContentAccess.Denied; - } + entity = _entityService.Get(nodeId, UmbracoObjectTypes.Document); - entity = _entityService.Get(nodeId, UmbracoObjectTypes.Document); - if (entity == null) - { - return ContentAccess.NotFound; - } + if (entity == null) + { + return ContentAccess.NotFound; + } - hasPathAccess = user.HasContentPathAccess(entity, _entityService, _appCaches); + hasPathAccess = user.HasContentPathAccess(entity, _entityService, _appCaches); + } if (hasPathAccess == false) { @@ -206,7 +199,8 @@ public class ContentPermissions } // get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(entity.Path, user, permissionsToCheck) + // if there is no entity for this id, than just use the id as the path (i.e. -1 or -20) + return CheckPermissionsPath(entity?.Path ?? nodeId.ToString(), user, permissionsToCheck) ? ContentAccess.Granted : ContentAccess.Denied; } @@ -230,12 +224,7 @@ public class ContentPermissions throw new ArgumentNullException(nameof(user)); } - if (permissionsToCheck == null) - { - permissionsToCheck = Array.Empty(); - } - - bool? hasPathAccess = null; + bool hasPathAccess; contentItem = null; if (nodeId == Constants.System.Root) @@ -246,19 +235,17 @@ public class ContentPermissions { hasPathAccess = user.HasContentBinAccess(_entityService, _appCaches); } - - if (hasPathAccess.HasValue) + else { - return hasPathAccess.Value ? ContentAccess.Granted : ContentAccess.Denied; - } + contentItem = _contentService.GetById(nodeId); - contentItem = _contentService.GetById(nodeId); - if (contentItem == null) - { - return ContentAccess.NotFound; - } + if (contentItem == null) + { + return ContentAccess.NotFound; + } - hasPathAccess = user.HasPathAccess(contentItem, _entityService, _appCaches); + hasPathAccess = user.HasPathAccess(contentItem, _entityService, _appCaches); + } if (hasPathAccess == false) { @@ -271,7 +258,8 @@ public class ContentPermissions } // get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(contentItem.Path, user, permissionsToCheck) + // if there is no content item for this id, than just use the id as the path (i.e. -1 or -20) + return CheckPermissionsPath(contentItem?.Path ?? nodeId.ToString(), user, permissionsToCheck) ? ContentAccess.Granted : ContentAccess.Denied; } @@ -283,8 +271,7 @@ public class ContentPermissions permissionsToCheck = Array.Empty(); } - // get the implicit/inherited permissions for the user for this path, - // if there is no content item for this id, than just use the id as the path (i.e. -1 or -20) + // get the implicit/inherited permissions for the user for this path EntityPermissionSet permission = _userService.GetPermissionsForPath(user, path); var allowed = true; diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs index d8c306bff4..83063f156f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentController.cs @@ -819,6 +819,7 @@ public class ContentController : ContentControllerBase return pagedResult; } + /// /// Creates a blueprint from a content item /// @@ -1117,7 +1118,7 @@ public class ContentController : ContentControllerBase AddDomainWarnings(publishStatus.Content, successfulCultures, globalNotifications); AddPublishStatusNotifications(new[] { publishStatus }, globalNotifications, notifications, successfulCultures); } - break; + break; case ContentSaveAction.PublishWithDescendants: case ContentSaveAction.PublishWithDescendantsNew: { @@ -1134,7 +1135,7 @@ public class ContentController : ContentControllerBase AddDomainWarnings(publishStatus, successfulCultures, globalNotifications); AddPublishStatusNotifications(publishStatus, globalNotifications, notifications, successfulCultures); } - break; + break; case ContentSaveAction.PublishWithDescendantsForce: case ContentSaveAction.PublishWithDescendantsForceNew: { @@ -1150,7 +1151,7 @@ public class ContentController : ContentControllerBase var publishStatus = PublishBranchInternal(contentItem, true, cultureForInvariantErrors, out wasCancelled, out var successfulCultures).ToList(); AddPublishStatusNotifications(publishStatus, globalNotifications, notifications, successfulCultures); } - break; + break; default: throw new ArgumentOutOfRangeException(); } @@ -2804,7 +2805,7 @@ public class ContentController : ContentControllerBase } } } - break; + break; case PublishResultType.SuccessPublish: { // TODO: Here we should have messaging for when there are release dates specified like https://github.com/umbraco/Umbraco-CMS/pull/3507 @@ -2832,7 +2833,7 @@ public class ContentController : ContentControllerBase } } } - break; + break; case PublishResultType.FailedPublishPathNotPublished: { //TODO: This doesn't take into account variations with the successfulCultures param @@ -2841,14 +2842,14 @@ public class ContentController : ContentControllerBase _localizedTextService.Localize(null, "publish"), _localizedTextService.Localize("publish", "contentPublishedFailedByParent", new[] { names }).Trim()); } - break; + break; case PublishResultType.FailedPublishCancelledByEvent: { //TODO: This doesn't take into account variations with the successfulCultures param var names = string.Join(", ", status.Select(x => $"'{x.Content?.Name}'")); AddCancelMessage(display, "publish", "contentPublishedFailedByEvent", new[] { names }); } - break; + break; case PublishResultType.FailedPublishAwaitingRelease: { //TODO: This doesn't take into account variations with the successfulCultures param @@ -2857,7 +2858,7 @@ public class ContentController : ContentControllerBase _localizedTextService.Localize(null, "publish"), _localizedTextService.Localize("publish", "contentPublishedFailedAwaitingRelease", new[] { names }).Trim()); } - break; + break; case PublishResultType.FailedPublishHasExpired: { //TODO: This doesn't take into account variations with the successfulCultures param @@ -2866,7 +2867,7 @@ public class ContentController : ContentControllerBase _localizedTextService.Localize(null, "publish"), _localizedTextService.Localize("publish", "contentPublishedFailedExpired", new[] { names }).Trim()); } - break; + break; case PublishResultType.FailedPublishIsTrashed: { //TODO: This doesn't take into account variations with the successfulCultures param @@ -2875,7 +2876,7 @@ public class ContentController : ContentControllerBase _localizedTextService.Localize(null, "publish"), _localizedTextService.Localize("publish", "contentPublishedFailedIsTrashed", new[] { names }).Trim()); } - break; + break; case PublishResultType.FailedPublishContentInvalid: { if (successfulCultures == null) @@ -2899,7 +2900,7 @@ public class ContentController : ContentControllerBase } } } - break; + break; case PublishResultType.FailedPublishMandatoryCultureMissing: display.AddWarningNotification( _localizedTextService.Localize(null, "publish"), diff --git a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs index e97d7dd055..d70d262d39 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/ContentControllerBase.cs @@ -78,7 +78,6 @@ public abstract class ContentControllerBase : BackOfficeNotificationsController ModelState.AddModelError("id", $"content with id: {id} was not found"); NotFoundObjectResult errorResponse = NotFound(ModelState); - return errorResponse; } diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 003b6676fe..5c9a96b71c 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -277,6 +277,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute if (!authorizationResult.Succeeded) { + actionContext.Result = new ForbidResult(); return false; } From c42eb281297e85e3251a6a83f121ad75cf2bac35 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Wed, 1 Nov 2023 14:40:51 +0100 Subject: [PATCH 06/27] Bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 9213512058..b830ca0ae6 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": "12.3.0-rc", + "version": "12.3.0", "assemblyVersion": { "precision": "build" }, From 67771450797eb2c72449ddcabfc38d5ebf99c7f3 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 2 Nov 2023 08:45:27 +0100 Subject: [PATCH 07/27] Ensure that missing access rules do not break the site (#15081) --- src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs index 354083dfa8..0821232826 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/AccessDto.cs @@ -37,5 +37,5 @@ internal class AccessDto [ResultColumn] [Reference(ReferenceType.Many, ReferenceMemberName = "AccessId")] - public List Rules { get; set; } = null!; + public List Rules { get; set; } = new(); } From 10264f244bfa0d3fbf3afe66f96d1c9302045d33 Mon Sep 17 00:00:00 2001 From: Zeegaan Date: Thu, 2 Nov 2023 09:14:18 +0100 Subject: [PATCH 08/27] bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index b830ca0ae6..220f9d3fc6 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": "12.3.0", + "version": "12.4.0-rc", "assemblyVersion": { "precision": "build" }, From ff05886e432786c6506ff340e5cea32d818deee9 Mon Sep 17 00:00:00 2001 From: Nikolaj Geisle <70372949+Zeegaan@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:37:28 +0100 Subject: [PATCH 09/27] Remove setter from WebhookEvent (#15106) Co-authored-by: Zeegaan --- src/Umbraco.Core/Webhooks/IWebhookEvent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Webhooks/IWebhookEvent.cs b/src/Umbraco.Core/Webhooks/IWebhookEvent.cs index 85857c1aec..954055d104 100644 --- a/src/Umbraco.Core/Webhooks/IWebhookEvent.cs +++ b/src/Umbraco.Core/Webhooks/IWebhookEvent.cs @@ -2,5 +2,5 @@ public interface IWebhookEvent { - string EventName { get; set; } + string EventName { get; } } From c31df57bdc2670e7fd9fc0b3ef4aaabee33fe828 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Fri, 3 Nov 2023 13:30:13 +0000 Subject: [PATCH 10/27] New WebhookEventBase class (#15129) (cherry picked from commit 145107be45cb466395c06d7b15b2389e0765f6bb) --- .../Events/ContentDeleteWebhookEvent.cs | 2 +- .../Events/ContentPublishWebhookEvent.cs | 2 +- .../Events/ContentUnpublishWebhookEvent.cs | 2 +- .../Events/MediaDeleteWebhookEvent.cs | 2 +- .../Webhooks/Events/MediaSaveWebhookEvent.cs | 2 +- src/Umbraco.Core/Webhooks/WebhookEventBase.cs | 89 +++++++++++-------- .../Webhooks/WebhookEventContentBase.cs | 49 ++++++++++ 7 files changed, 105 insertions(+), 43 deletions(-) create mode 100644 src/Umbraco.Core/Webhooks/WebhookEventContentBase.cs diff --git a/src/Umbraco.Core/Webhooks/Events/ContentDeleteWebhookEvent.cs b/src/Umbraco.Core/Webhooks/Events/ContentDeleteWebhookEvent.cs index 629f47539a..52b8d233e5 100644 --- a/src/Umbraco.Core/Webhooks/Events/ContentDeleteWebhookEvent.cs +++ b/src/Umbraco.Core/Webhooks/Events/ContentDeleteWebhookEvent.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Webhooks.Events; -public class ContentDeleteWebhookEvent : WebhookEventBase +public class ContentDeleteWebhookEvent : WebhookEventContentBase { public ContentDeleteWebhookEvent( IWebhookFiringService webhookFiringService, diff --git a/src/Umbraco.Core/Webhooks/Events/ContentPublishWebhookEvent.cs b/src/Umbraco.Core/Webhooks/Events/ContentPublishWebhookEvent.cs index 4c75516420..8f308432b8 100644 --- a/src/Umbraco.Core/Webhooks/Events/ContentPublishWebhookEvent.cs +++ b/src/Umbraco.Core/Webhooks/Events/ContentPublishWebhookEvent.cs @@ -10,7 +10,7 @@ using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Webhooks.Events; -public class ContentPublishWebhookEvent : WebhookEventBase +public class ContentPublishWebhookEvent : WebhookEventContentBase { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IApiContentBuilder _apiContentBuilder; diff --git a/src/Umbraco.Core/Webhooks/Events/ContentUnpublishWebhookEvent.cs b/src/Umbraco.Core/Webhooks/Events/ContentUnpublishWebhookEvent.cs index 6c8fdf3598..c8a8fd789e 100644 --- a/src/Umbraco.Core/Webhooks/Events/ContentUnpublishWebhookEvent.cs +++ b/src/Umbraco.Core/Webhooks/Events/ContentUnpublishWebhookEvent.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Webhooks.Events; -public class ContentUnpublishWebhookEvent : WebhookEventBase +public class ContentUnpublishWebhookEvent : WebhookEventContentBase { public ContentUnpublishWebhookEvent( IWebhookFiringService webhookFiringService, diff --git a/src/Umbraco.Core/Webhooks/Events/MediaDeleteWebhookEvent.cs b/src/Umbraco.Core/Webhooks/Events/MediaDeleteWebhookEvent.cs index 51e1337f7d..eb19e3e888 100644 --- a/src/Umbraco.Core/Webhooks/Events/MediaDeleteWebhookEvent.cs +++ b/src/Umbraco.Core/Webhooks/Events/MediaDeleteWebhookEvent.cs @@ -7,7 +7,7 @@ using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Webhooks.Events; -public class MediaDeleteWebhookEvent : WebhookEventBase +public class MediaDeleteWebhookEvent : WebhookEventContentBase { public MediaDeleteWebhookEvent( IWebhookFiringService webhookFiringService, diff --git a/src/Umbraco.Core/Webhooks/Events/MediaSaveWebhookEvent.cs b/src/Umbraco.Core/Webhooks/Events/MediaSaveWebhookEvent.cs index d5a4dc57c5..9a7dcaa3d5 100644 --- a/src/Umbraco.Core/Webhooks/Events/MediaSaveWebhookEvent.cs +++ b/src/Umbraco.Core/Webhooks/Events/MediaSaveWebhookEvent.cs @@ -10,7 +10,7 @@ using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Webhooks.Events; -public class MediaSaveWebhookEvent : WebhookEventBase +public class MediaSaveWebhookEvent : WebhookEventContentBase { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly IApiMediaBuilder _apiMediaBuilder; diff --git a/src/Umbraco.Core/Webhooks/WebhookEventBase.cs b/src/Umbraco.Core/Webhooks/WebhookEventBase.cs index 01384ea43f..8753eeecf9 100644 --- a/src/Umbraco.Core/Webhooks/WebhookEventBase.cs +++ b/src/Umbraco.Core/Webhooks/WebhookEventBase.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.Options; +using Microsoft.Extensions.Options; + using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -8,14 +9,17 @@ using Umbraco.Cms.Core.Sync; namespace Umbraco.Cms.Core.Webhooks; -public abstract class WebhookEventBase : IWebhookEvent, INotificationAsyncHandler +public abstract class WebhookEventBase : IWebhookEvent, INotificationAsyncHandler where TNotification : INotification - where TEntity : IContentBase { - private readonly IWebhookFiringService _webhookFiringService; - private readonly IWebHookService _webHookService; private readonly IServerRoleAccessor _serverRoleAccessor; - private WebhookSettings _webhookSettings; + + /// + public string EventName { get; set; } + + protected IWebhookFiringService WebhookFiringService { get; } + protected IWebHookService WebHookService { get; } + protected WebhookSettings WebhookSettings { get; private set; } protected WebhookEventBase( IWebhookFiringService webhookFiringService, @@ -24,50 +28,59 @@ public abstract class WebhookEventBase : IWebhookEvent, IServerRoleAccessor serverRoleAccessor, string eventName) { - _webhookFiringService = webhookFiringService; - _webHookService = webHookService; - _serverRoleAccessor = serverRoleAccessor; EventName = eventName; - _webhookSettings = webhookSettings.CurrentValue; - webhookSettings.OnChange(x => _webhookSettings = x); + + WebhookFiringService = webhookFiringService; + WebHookService = webHookService; + _serverRoleAccessor = serverRoleAccessor; + + WebhookSettings = webhookSettings.CurrentValue; + webhookSettings.OnChange(x => WebhookSettings = x); } - public string EventName { get; set; } - - public virtual async Task HandleAsync(TNotification notification, CancellationToken cancellationToken) + /// + /// Process the webhooks for the given notification. + /// + public virtual async Task ProcessWebhooks(TNotification notification, IEnumerable webhooks, CancellationToken cancellationToken) { - if (_serverRoleAccessor.CurrentServerRole is not ServerRole.Single && _serverRoleAccessor.CurrentServerRole is not ServerRole.SchedulingPublisher) - { - return; - } - - if (_webhookSettings.Enabled is false) - { - return; - } - - IEnumerable webhooks = await _webHookService.GetByEventNameAsync(EventName); - foreach (Webhook webhook in webhooks) { - if (!webhook.Enabled) + if (webhook.Enabled is false) { continue; } - foreach (TEntity entity in GetEntitiesFromNotification(notification)) - { - if (webhook.ContentTypeKeys.Any() && !webhook.ContentTypeKeys.Contains(entity.ContentType.Key)) - { - continue; - } - - await _webhookFiringService.FireAsync(webhook, EventName, ConvertEntityToRequestPayload(entity), cancellationToken); - } + await WebhookFiringService.FireAsync(webhook, EventName, notification, cancellationToken); } } - protected abstract IEnumerable GetEntitiesFromNotification(TNotification notification); + /// + /// should webhooks fire for this notification. + /// + /// true if webhooks should be fired. + public virtual bool ShouldFireWebhookForNotification(TNotification notificationObject) + => true; - protected abstract object? ConvertEntityToRequestPayload(TEntity entity); + public async Task HandleAsync(TNotification notification, CancellationToken cancellationToken) + { + if (WebhookSettings.Enabled is false) + { + return; + } + + if (_serverRoleAccessor.CurrentServerRole is not ServerRole.Single + && _serverRoleAccessor.CurrentServerRole is not ServerRole.SchedulingPublisher) + { + return; + } + + if (ShouldFireWebhookForNotification(notification) is false) + { + return; + } + + IEnumerable webhooks = await WebHookService.GetByEventNameAsync(EventName); + + await ProcessWebhooks(notification, webhooks, cancellationToken); + } } diff --git a/src/Umbraco.Core/Webhooks/WebhookEventContentBase.cs b/src/Umbraco.Core/Webhooks/WebhookEventContentBase.cs new file mode 100644 index 0000000000..5b8b8c626e --- /dev/null +++ b/src/Umbraco.Core/Webhooks/WebhookEventContentBase.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Options; + +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Sync; + +namespace Umbraco.Cms.Core.Webhooks; + +public abstract class WebhookEventContentBase : WebhookEventBase + where TNotification : INotification + where TEntity : IContentBase +{ + protected WebhookEventContentBase( + IWebhookFiringService webhookFiringService, + IWebHookService webHookService, + IOptionsMonitor webhookSettings, + IServerRoleAccessor serverRoleAccessor, + string eventName) + : base(webhookFiringService, webHookService, webhookSettings, serverRoleAccessor, eventName) + { + } + + public override async Task ProcessWebhooks(TNotification notification, IEnumerable webhooks, CancellationToken cancellationToken) + { + foreach (Webhook webhook in webhooks) + { + if (!webhook.Enabled) + { + continue; + } + + foreach (TEntity entity in GetEntitiesFromNotification(notification)) + { + if (webhook.ContentTypeKeys.Any() && !webhook.ContentTypeKeys.Contains(entity.ContentType.Key)) + { + continue; + } + + await WebhookFiringService.FireAsync(webhook, EventName, ConvertEntityToRequestPayload(entity), cancellationToken); + } + } + } + + protected abstract IEnumerable GetEntitiesFromNotification(TNotification notification); + + protected abstract object? ConvertEntityToRequestPayload(TEntity entity); +} From 79d9ac759b38be6fb9fe6a51339b040a7674813d Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Wed, 1 Nov 2023 16:55:03 +0100 Subject: [PATCH 11/27] V13: Webhook corrections (#15077) * Update icons * Update tree headers * Cleanup and change icon name * Use button element instead * Disable button instead * Fix overlay title * Simplify labels * Add datalist for common headers * Use Utilties function * Events in plural form * Cleanup and formatting * Formatting * More formatting * Stop event bubbling when clicking delete button * Sync tree node and show loading indicator * Add webhook icon * Remove globe icon to not confuse with languages * Update logs * Remove extra column with delete button which shouldn't be there * Use umb-icon and update titles * Use content type picker (cherry picked from commit 29be27ba466e9e1eec543ae2e088a19c2d92f5df) --- src/Umbraco.Core/Constants-Icons.cs | 58 ++++++++------ .../EmbeddedResources/Lang/da.xml | 1 + .../EmbeddedResources/Lang/en.xml | 1 + .../Trees/PackagesTreeController.cs | 2 +- .../Trees/WebhooksTreeController.cs | 4 +- .../src/assets/icons/icon-webhook.svg | 4 + .../src/views/webhooks/logs.controller.js | 30 +++++-- .../src/views/webhooks/logs.html | 63 ++++++--------- .../src/views/webhooks/overlays/details.html | 6 +- .../webhooks/overlays/edit.controller.js | 61 ++++++++------ .../src/views/webhooks/overlays/edit.html | 80 ++++++++++++------- .../webhooks/overlays/header.controller.js | 10 ++- .../src/views/webhooks/overlays/header.html | 14 ++-- .../src/views/webhooks/overview.controller.js | 18 +++-- .../src/views/webhooks/overview.html | 15 ++-- .../src/views/webhooks/webhooks.controller.js | 56 +++++++++---- .../src/views/webhooks/webhooks.html | 30 +++---- 17 files changed, 261 insertions(+), 192 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/assets/icons/icon-webhook.svg diff --git a/src/Umbraco.Core/Constants-Icons.cs b/src/Umbraco.Core/Constants-Icons.cs index 5aaeb2ba61..a18ff387e7 100644 --- a/src/Umbraco.Core/Constants-Icons.cs +++ b/src/Umbraco.Core/Constants-Icons.cs @@ -5,7 +5,7 @@ public static partial class Constants public static class Icons { /// - /// System default icon + /// System default icon. /// public const string DefaultIcon = Content; @@ -15,37 +15,37 @@ public static partial class Constants public const string Blueprint = "icon-blueprint"; /// - /// System content icon + /// System content icon. /// public const string Content = "icon-document"; /// - /// System content type icon + /// System content type icon. /// public const string ContentType = "icon-item-arrangement"; /// - /// System data type icon + /// System data type icon. /// public const string DataType = "icon-autofill"; /// - /// System dictionary icon + /// System dictionary icon. /// public const string Dictionary = "icon-book-alt"; /// - /// System generic folder icon + /// System generic folder icon. /// public const string Folder = "icon-folder"; /// - /// System language icon + /// System language icon. /// public const string Language = "icon-globe"; /// - /// System logviewer icon + /// System logviewer icon. /// public const string LogViewer = "icon-box-alt"; @@ -55,12 +55,12 @@ public static partial class Constants public const string ListView = "icon-thumbnail-list"; /// - /// System macro icon + /// System macro icon. /// public const string Macro = "icon-settings-alt"; /// - /// System media file icon + /// System media file icon. /// public const string MediaFile = "icon-document"; @@ -70,7 +70,7 @@ public static partial class Constants public const string MediaVideo = "icon-video"; /// - /// System media audio icon + /// System media audio icon. /// public const string MediaAudio = "icon-sound-waves"; @@ -80,12 +80,12 @@ public static partial class Constants public const string MediaArticle = "icon-article"; /// - /// System media vector icon + /// System media vector icon. /// public const string MediaVectorGraphics = "icon-picture"; /// - /// System media folder icon + /// System media folder icon. /// public const string MediaFolder = "icon-folder"; @@ -95,17 +95,17 @@ public static partial class Constants public const string MediaImage = "icon-picture"; /// - /// System media type icon + /// System media type icon. /// public const string MediaType = "icon-thumbnails"; /// - /// System member icon + /// System member icon. /// public const string Member = "icon-user"; /// - /// System member group icon + /// System member group icon. /// public const string MemberGroup = "icon-users-alt"; @@ -115,8 +115,14 @@ public static partial class Constants public const string MemberType = "icon-users"; /// - /// System packages icon + /// System package icon. /// + public const string Package = "icon-box"; + + /// + /// System packages icon. + /// + [Obsolete("Use Package icon instead.")] public const string Packages = "icon-box"; /// @@ -125,43 +131,43 @@ public static partial class Constants public const string PartialView = "icon-article"; /// - /// System property editor icon + /// System property editor icon. /// public const string PropertyEditor = "icon-autofill"; /// - /// Relation type icon + /// Relation type icon. /// public const string RelationType = "icon-trafic"; /// - /// Script type icon + /// Script type icon. /// public const string Script = "icon-script"; /// - /// Stylesheet type icon + /// Stylesheet type icon. /// public const string Stylesheet = "icon-brackets"; /// - /// System member icon + /// System member icon. /// public const string Template = "icon-layout"; /// - /// System user icon + /// System user icon. /// public const string User = "icon-user"; /// - /// System user group icon + /// System user group icon. /// public const string UserGroup = "icon-users"; /// - /// Webhooks icon + /// Webhook icon. /// - public const string Webhooks = "icon-directions-alt"; + public const string Webhook = "icon-webhook"; } } diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml index 128ed70229..fa21b1e377 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/da.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/da.xml @@ -1848,6 +1848,7 @@ Mange hilsner fra Umbraco robotten Indstillinger Design og layout Tredjepart + Webhooks Ny opdatering er klar diff --git a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml index 9d1b110b8a..4dc13c1ede 100644 --- a/src/Umbraco.Core/EmbeddedResources/Lang/en.xml +++ b/src/Umbraco.Core/EmbeddedResources/Lang/en.xml @@ -2068,6 +2068,7 @@ To manage your website, simply open the Umbraco backoffice and start adding cont Settings Templating Third Party + Webhooks New update ready diff --git a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs index 88f04ba823..2fec0ae075 100644 --- a/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/PackagesTreeController.cs @@ -45,7 +45,7 @@ public class PackagesTreeController : TreeController { // This will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Packages}/{Constants.Trees.Packages}/repo"; - root.Icon = Constants.Icons.Packages; + root.Icon = Constants.Icons.Package; root.HasChildren = false; } diff --git a/src/Umbraco.Web.BackOffice/Trees/WebhooksTreeController.cs b/src/Umbraco.Web.BackOffice/Trees/WebhooksTreeController.cs index 5f315f3cbb..c8a7f7707c 100644 --- a/src/Umbraco.Web.BackOffice/Trees/WebhooksTreeController.cs +++ b/src/Umbraco.Web.BackOffice/Trees/WebhooksTreeController.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; @@ -52,7 +52,7 @@ public class WebhooksTreeController : TreeController { // This will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Settings}/{Constants.Trees.Webhooks}/overview"; - root.Icon = Constants.Icons.Webhooks; + root.Icon = Constants.Icons.Webhook; root.HasChildren = false; root.MenuUrl = null; } diff --git a/src/Umbraco.Web.UI.Client/src/assets/icons/icon-webhook.svg b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-webhook.svg new file mode 100644 index 0000000000..a7c4a87680 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/assets/icons/icon-webhook.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.controller.js b/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.controller.js index 11f5debee9..b278638e34 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.controller.js @@ -1,20 +1,34 @@ -(function () { +(function () { "use strict"; - function WebhookLogController($q,$scope, webhooksResource, notificationsService, overlayService) { - var vm = this; + function WebhookLogController($q, webhooksResource, overlayService) { + + const vm = this; + vm.logs = []; vm.openLogOverlay = openLogOverlay; vm.isChecked = isChecked; - function loadLogs (){ + function init() { + vm.loading = true; + + let promises = []; + + promises.push(loadLogs()); + + $q.all(promises).then(function () { + vm.loading = false; + }); + } + + function loadLogs() { return webhooksResource.getLogs() - .then((data) => { + .then(data => { vm.logs = data.items; }); } - function openLogOverlay (log) { + function openLogOverlay(log) { overlayService.open({ view: "views/webhooks/overlays/details.html", title: 'Details', @@ -27,11 +41,11 @@ }); } - function isChecked (log) { + function isChecked(log) { return log.statusCode === "OK"; } - loadLogs(); + init(); } angular.module("umbraco").controller("Umbraco.Editors.Webhooks.WebhookLogController", WebhookLogController); diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.html b/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.html index 10603d0ac1..6c13daf764 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.html +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/logs.html @@ -1,47 +1,30 @@ -
+
- - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + +
Webhook keyDateUrlEventRetryCount
Webhook keyDateUrlEventRetry count
- - - - - {{ log.webhookKey}} - - {{ log.date}} - - {{ log.url }} - - {{ log.eventName }} - - {{ log.retryCount}} - - - -
+ + + {{ log.webhookKey }}{{ log.date }}{{ log.url }}{{ log.eventName }}{{ log.retryCount }}
diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/details.html b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/details.html index 9c0856cb67..3878c1ae87 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/details.html @@ -1,9 +1,9 @@ -
+
- +
{{model.webhookLogEntry.response.statusDescription}} ({{model.webhookLogEntry.response.statusCode}})
@@ -17,7 +17,7 @@
{{model.log.url}}
- Status Code + Status code
{{model.log.statusCode}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.controller.js index c25ccaa6ba..9df465f738 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.controller.js @@ -1,8 +1,10 @@ -(function () { +(function () { "use strict"; function EditController($scope, editorService, contentTypeResource, mediaTypeResource) { - var vm = this; + + const vm = this; + vm.clearContentType = clearContentType; vm.clearEvent = clearEvent; vm.removeHeader = removeHeader; @@ -12,9 +14,7 @@ vm.close = close; vm.submit = submit; - - function openEventPicker() - { + function openEventPicker() { editorService.eventPicker({ title: "Select event", selectedEvents: $scope.model.webhook.events, @@ -28,23 +28,39 @@ }); } - function openContentTypePicker() - { + function openContentTypePicker() { const isContent = $scope.model.webhook ? $scope.model.webhook.events[0].toLowerCase().includes("content") : null; - editorService.treePicker({ - section: 'settings', - treeAlias: isContent ? 'documentTypes' : 'mediaTypes', - entityType: isContent ? 'DocumentType' : 'MediaType', + + const editor = { multiPicker: true, + filterCssClass: "not-allowed not-published", + filter: function (item) { + // filter out folders (containers), element types (for content) and already selected items + return item.nodeType === "container"; // || item.metaData.isElement || !!_.findWhere(vm.itemTypes, { udi: item.udi }); + }, submit(model) { getEntities(model.selection, isContent); - $scope.model.webhook.contentTypeKeys = model.selection.map((item) => item.key); + $scope.model.webhook.contentTypeKeys = model.selection.map(item => item.key); editorService.close(); }, close() { editorService.close(); } - }); + }; + + const itemType = isContent ? "content" : "media"; + + switch (itemType) { + case "content": + editorService.contentTypePicker(editor); + break; + case "media": + editorService.mediaTypePicker(editor); + break; + case "member": + editorService.memberTypePicker(editor); + break; + } } function openCreateHeader() { @@ -70,29 +86,29 @@ const resource = isContent ? contentTypeResource : mediaTypeResource; $scope.model.contentTypes = []; - selection.forEach((entity) => { + selection.forEach(entity => { resource.getById(entity.key) - .then((data) => { + .then(data => { $scope.model.contentTypes.push(data); }); }); } function clearContentType(contentTypeKey) { - if (Array.isArray($scope.model.webhook.contentTypeKeys)) { + if (Utilities.isArray($scope.model.webhook.contentTypeKeys)) { $scope.model.webhook.contentTypeKeys = $scope.model.webhook.contentTypeKeys.filter(x => x !== contentTypeKey); } - if (Array.isArray($scope.model.contentTypes)) { + if (Utilities.isArray($scope.model.contentTypes)) { $scope.model.contentTypes = $scope.model.contentTypes.filter(x => x.key !== contentTypeKey); } } function clearEvent(event) { - if (Array.isArray($scope.model.webhook.events)) { + if (Utilities.isArray($scope.model.webhook.events)) { $scope.model.webhook.events = $scope.model.webhook.events.filter(x => x !== event); } - if (Array.isArray($scope.model.contentTypes)) { + if (Utilities.isArray($scope.model.contentTypes)) { $scope.model.events = $scope.model.events.filter(x => x.key !== event); } } @@ -101,16 +117,13 @@ delete $scope.model.webhook.headers[key]; } - - function close() - { + function close() { if ($scope.model.close) { $scope.model.close(); } } - function submit() - { + function submit() { if ($scope.model.submit) { $scope.model.submit($scope.model); } diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.html b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.html index 40e216c79a..cf55320f2f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/edit.html @@ -1,4 +1,4 @@ -
+
- + + + + - + + - + ng-click="vm.openEventPicker()"> Add - + + - + + - - Add - - Please select an event first. + ng-disabled="!model.webhook.events || model.webhook.events.length === 0" + ng-click="vm.openContentTypePicker()"> + + Add + + + Please select an event first. + + + @@ -78,8 +95,11 @@ on-click="model.webhook.enabled = !model.webhook.enabled"> - + + @@ -89,12 +109,8 @@ - - + +
- {{ key }} - - {{ value }} - {{key}}{{value}}
- + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.controller.js b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.controller.js index a77a4f5c00..298cb95bd8 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.controller.js @@ -1,11 +1,15 @@ -(function () { +(function () { "use strict"; function HeaderController($scope) { - var vm = this; - $scope.headerModel = { key: "", value: "" }; + const vm = this; + vm.submit = submit; vm.close = close; + vm.headers = ["Accept", "Content-Type", "User-Agent", "Content-Length"]; + + $scope.headerModel = { key: "", value: "" }; + function submit () { if ($scope.headerModel.key && $scope.headerModel.value) { $scope.model.submit($scope.headerModel); diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.html b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.html index b9adcb2bf6..114eab70c9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.html +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/overlays/header.html @@ -1,8 +1,8 @@ -
+
- + + required /> + + + - + { vm.page.labels.webhooks = data[0]; vm.page.labels.logs = data[1]; vm.page.navigation = [ { "name": vm.page.labels.webhooks, - "icon": "icon-directions-alt", + "icon": "icon-webhook", "view": "views/webhooks/webhooks.html", "active": webhookUri === 'overview', "alias": "umbWebhooks", @@ -53,7 +55,7 @@ } function setPageName() { - localizationService.localize("treeHeaders_webhooks").then(function (data) { + localizationService.localize("treeHeaders_webhooks").then(data => { vm.page.name = data; }) } diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/overview.html b/src/Umbraco.Web.UI.Client/src/views/webhooks/overview.html index 2576fa1e8b..981cf09f44 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/overview.html @@ -1,13 +1,14 @@ -
+
- + diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.controller.js b/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.controller.js index 5814d6f6dc..94b1c21742 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.controller.js @@ -1,8 +1,9 @@ -(function () { +(function () { "use strict"; - function WebhookController($q,$scope, webhooksResource, notificationsService, editorService, overlayService, contentTypeResource, mediaTypeResource) { - var vm = this; + function WebhookController($q, $timeout, $routeParams, webhooksResource, navigationService, notificationsService, editorService, overlayService, contentTypeResource, mediaTypeResource) { + + const vm = this; vm.openWebhookOverlay = openWebhookOverlay; vm.deleteWebhook = deleteWebhook; @@ -16,15 +17,33 @@ vm.webHooksContentTypes = {}; vm.webhookEvents = {}; - function loadEvents (){ + function init() { + vm.loading = true; + + let promises = []; + + promises.push(loadEvents()); + promises.push(loadWebhooks()); + + $q.all(promises).then(function () { + vm.loading = false; + }); + + // Activate tree node + $timeout(function () { + navigationService.syncTree({ tree: $routeParams.tree, path: [-1], activate: true }); + }); + } + + function loadEvents() { return webhooksResource.getAllEvents() - .then((data) => { + .then(data => { vm.events = data.map(item => item.eventName); }); } function resolveEventNames(webhook) { - webhook.events.forEach((event) => { + webhook.events.forEach(event => { if (!vm.webhookEvents[webhook.key]) { vm.webhookEvents[webhook.key] = event; } else { @@ -38,9 +57,9 @@ const resource = isContent ? contentTypeResource : mediaTypeResource; let entities = []; - webhook.contentTypeKeys.forEach((key) => { + webhook.contentTypeKeys.forEach(key => { resource.getById(key) - .then((data) => { + .then(data => { entities.push(data); }); }); @@ -56,9 +75,9 @@ delete vm.webHooksContentTypes[webhook.key]; } - webhook.contentTypeKeys.forEach((key) => { + webhook.contentTypeKeys.forEach(key => { resource.getById(key) - .then((data) => { + .then(data => { if (!vm.webHooksContentTypes[webhook.key]) { vm.webHooksContentTypes[webhook.key] = data.name; } else { @@ -97,7 +116,8 @@ handleSubmissionError(model, 'Please provide the event for which the webhook should trigger'); return; } - if(isCreating){ + + if (isCreating) { webhooksResource.create(model.webhook) .then(() => { loadWebhooks() @@ -111,7 +131,7 @@ handleSubmissionError(model, `Error saving webhook. ${errorMessage ?? ''}`); }); } - else{ + else { webhooksResource.update(model.webhook) .then(() => { loadWebhooks() @@ -136,19 +156,19 @@ function loadWebhooks(){ webhooksResource .getAll() - .then((result) => { + .then(result => { vm.webhooks = result; vm.webhookEvents = {}; vm.webHooksContentTypes = {}; - vm.webhooks.forEach((webhook) => { + vm.webhooks.forEach(webhook => { resolveTypeNames(webhook); resolveEventNames(webhook); }) }); } - function deleteWebhook (webhook) { + function deleteWebhook (webhook, event) { overlayService.open({ title: 'Confirm delete webhook', content: 'Are you sure you want to delete the webhook?', @@ -171,10 +191,12 @@ overlayService.close(); } }); + + event.preventDefault(); + event.stopPropagation(); } - loadWebhooks() - loadEvents() + init(); } angular.module("umbraco").controller("Umbraco.Editors.Webhooks.WebhookController", WebhookController); diff --git a/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.html b/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.html index 241a4a2015..e6f4e8f53d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.html +++ b/src/Umbraco.Web.UI.Client/src/views/webhooks/webhooks.html @@ -1,4 +1,4 @@ -
+
@@ -17,7 +17,6 @@ - @@ -27,27 +26,24 @@ - - + + From 5f3c0c56ccb5a87e69abb2786ae83ac426b145e9 Mon Sep 17 00:00:00 2001 From: Bjarne Fyrstenborg Date: Thu, 2 Nov 2023 13:27:48 +0100 Subject: [PATCH 12/27] Update webhook log table using constant (#15101) (cherry picked from commit f7d1ef722e7ead683feabaf4d976945d625b5a92) --- src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs index bfaad81c59..cdc5af450a 100644 --- a/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs +++ b/src/Umbraco.Core/Persistence/Constants-DatabaseSchema.cs @@ -90,7 +90,7 @@ public static partial class Constants public const string Webhook2ContentTypeKeys = Webhook + "2ContentTypeKeys"; public const string Webhook2Events = Webhook + "2Events"; public const string Webhook2Headers = Webhook + "2Headers"; - public const string WebhookLog = TableNamePrefix + "WebhookLog"; + public const string WebhookLog = Webhook + "Log"; } } } From ffaa77eb25b44304adeffcfa8976bbcb9c517716 Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:54:16 +0100 Subject: [PATCH 13/27] Update correct action (#15142) --- .../Filters/ContentSaveValidationAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 7cf6ba3cf5..f2e61b694a 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -189,7 +189,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute break; case ContentSaveAction.Schedule: permissionToCheck.Add(ActionUpdate.ActionLetter); - permissionToCheck.Add(ActionToPublish.ActionLetter); + permissionToCheck.Add(ActionPublish.ActionLetter); contentToCheck = contentItem.PersistedContent; contentIdToCheck = contentToCheck?.Id ?? default; break; From b12d88818d470fe6cf0380e48744edb770519963 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 6 Nov 2023 17:54:38 +0100 Subject: [PATCH 14/27] bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 220f9d3fc6..006a78b8bd 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": "12.4.0-rc", + "version": "12.3.1", "assemblyVersion": { "precision": "build" }, From d35c6f0e94f51bbbf6ef63757d227307dcd9273d Mon Sep 17 00:00:00 2001 From: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> Date: Mon, 6 Nov 2023 17:54:16 +0100 Subject: [PATCH 15/27] Update correct action (#15142) (cherry picked from commit ffaa77eb25b44304adeffcfa8976bbcb9c517716) --- .../Filters/ContentSaveValidationAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs index 5c9a96b71c..100d089451 100644 --- a/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs +++ b/src/Umbraco.Web.BackOffice/Filters/ContentSaveValidationAttribute.cs @@ -188,7 +188,7 @@ internal sealed class ContentSaveValidationAttribute : TypeFilterAttribute break; case ContentSaveAction.Schedule: permissionToCheck.Add(ActionUpdate.ActionLetter); - permissionToCheck.Add(ActionToPublish.ActionLetter); + permissionToCheck.Add(ActionPublish.ActionLetter); contentToCheck = contentItem.PersistedContent; contentIdToCheck = contentToCheck?.Id ?? default; break; From e7bab6995a550585579dc5fee0e9f994a6afe862 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 6 Nov 2023 18:05:02 +0100 Subject: [PATCH 16/27] Move IContextCache parameter to base Deploy interfaces and add checksum to artifact dependency (#15144) * Move IContextCache parameter to base interfaces * Add checksum to artifact dependency --- src/Umbraco.Core/Deploy/ArtifactDependency.cs | 46 +++++++++++---- ...ataTypeConfigurationConnectorExtensions.cs | 48 ---------------- .../Deploy/IDataTypeConfigurationConnector.cs | 8 +-- .../IDataTypeConfigurationConnector2.cs | 56 ------------------- src/Umbraco.Core/Deploy/IServiceConnector.cs | 18 +++--- src/Umbraco.Core/Deploy/IServiceConnector2.cs | 38 ------------- src/Umbraco.Core/Deploy/IValueConnector.cs | 8 +-- src/Umbraco.Core/Deploy/IValueConnector2.cs | 44 --------------- .../Deploy/ServiceConnectorExtensions.cs | 44 --------------- .../Deploy/ValueConnectorExtensions.cs | 50 ----------------- .../Umbraco.Core/CoreThings/UdiTests.cs | 2 +- 11 files changed, 52 insertions(+), 310 deletions(-) delete mode 100644 src/Umbraco.Core/Deploy/DataTypeConfigurationConnectorExtensions.cs delete mode 100644 src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector2.cs delete mode 100644 src/Umbraco.Core/Deploy/IServiceConnector2.cs delete mode 100644 src/Umbraco.Core/Deploy/IValueConnector2.cs delete mode 100644 src/Umbraco.Core/Deploy/ServiceConnectorExtensions.cs delete mode 100644 src/Umbraco.Core/Deploy/ValueConnectorExtensions.cs diff --git a/src/Umbraco.Core/Deploy/ArtifactDependency.cs b/src/Umbraco.Core/Deploy/ArtifactDependency.cs index 07ba917dc2..31c8025ddb 100644 --- a/src/Umbraco.Core/Deploy/ArtifactDependency.cs +++ b/src/Umbraco.Core/Deploy/ArtifactDependency.cs @@ -1,42 +1,64 @@ +using System.Text.Json.Serialization; + namespace Umbraco.Cms.Core.Deploy; /// -/// Represents an artifact dependency. +/// Represents an artifact dependency. /// /// -/// Dependencies have an order property which indicates whether it must be respected when ordering artifacts. -/// -/// Dependencies have a mode which can be Match or Exist depending on whether the checksum should -/// match. -/// +/// +/// Dependencies have an order property which indicates whether it must be respected when ordering artifacts. +/// +/// +/// Dependencies have a mode which can be or depending on whether the checksum should match. +/// /// public class ArtifactDependency { /// - /// Initializes a new instance of the ArtifactDependency class with an entity identifier and a mode. + /// Initializes a new instance of the class. /// - /// The entity identifier of the artifact that is a dependency. + /// The entity identifier of the artifact dependency. /// A value indicating whether the dependency is ordering. /// The dependency mode. - public ArtifactDependency(Udi udi, bool ordering, ArtifactDependencyMode mode) + /// The checksum. + public ArtifactDependency(Udi udi, bool ordering, ArtifactDependencyMode mode, string? checksum = null) { Udi = udi; Ordering = ordering; Mode = mode; + Checksum = checksum; } /// - /// Gets the entity id of the artifact that is a dependency. + /// Gets the entity identifier of the artifact dependency. /// + /// + /// The entity identifier of the artifact dependency. + /// public Udi Udi { get; } /// - /// Gets a value indicating whether the dependency is ordering. + /// Gets a value indicating whether the dependency is ordering. /// + /// + /// true if the dependency is ordering; otherwise, false. + /// public bool Ordering { get; } /// - /// Gets the dependency mode. + /// Gets the dependency mode. /// + /// + /// The dependency mode. + /// public ArtifactDependencyMode Mode { get; } + + /// + /// Gets the checksum. + /// + /// + /// The checksum. + /// + public string? Checksum { get; } } diff --git a/src/Umbraco.Core/Deploy/DataTypeConfigurationConnectorExtensions.cs b/src/Umbraco.Core/Deploy/DataTypeConfigurationConnectorExtensions.cs deleted file mode 100644 index dbd501d277..0000000000 --- a/src/Umbraco.Core/Deploy/DataTypeConfigurationConnectorExtensions.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Deploy; - -/// -/// Extension methods adding backwards-compatability between and . -/// -/// -/// These extension methods will be removed in Umbraco 13. -/// -public static class DataTypeConfigurationConnectorExtensions -{ - /// - /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. - /// - /// The connector. - /// The data type. - /// The dependencies. - /// The context cache. - /// - /// The artifact configuration value. - /// - /// - /// This extension method tries to make use of the on types also implementing . - /// - public static string? ToArtifact(this IDataTypeConfigurationConnector connector, IDataType dataType, ICollection dependencies, IContextCache contextCache) - => connector is IDataTypeConfigurationConnector2 connector2 - ? connector2.ToArtifact(dataType, dependencies, contextCache) - : connector.ToArtifact(dataType, dependencies); - - /// - /// Gets the data type configuration corresponding to an artifact configuration value. - /// - /// The connector. - /// The data type. - /// The artifact configuration value. - /// The context cache. - /// - /// The data type configuration. - /// - /// - /// This extension method tries to make use of the on types also implementing . - /// - public static object? FromArtifact(this IDataTypeConfigurationConnector connector, IDataType dataType, string? configuration, IContextCache contextCache) - => connector is IDataTypeConfigurationConnector2 connector2 - ? connector2.FromArtifact(dataType, configuration, contextCache) - : connector.FromArtifact(dataType, configuration); -} diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs index 36302efd07..4cb0690d1f 100644 --- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs +++ b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector.cs @@ -24,20 +24,20 @@ public interface IDataTypeConfigurationConnector /// /// The data type. /// The dependencies. + /// The context cache. /// /// The artifact configuration value. /// - [Obsolete($"Implement {nameof(IDataTypeConfigurationConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - string? ToArtifact(IDataType dataType, ICollection dependencies); + string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache); /// /// Gets the data type configuration corresponding to an artifact configuration value. /// /// The data type. /// The artifact configuration value. + /// The context cache. /// /// The data type configuration. /// - [Obsolete($"Implement {nameof(IDataTypeConfigurationConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - object? FromArtifact(IDataType dataType, string? configuration); + object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector2.cs b/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector2.cs deleted file mode 100644 index 772bc35dc4..0000000000 --- a/src/Umbraco.Core/Deploy/IDataTypeConfigurationConnector2.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Deploy; - -/// -/// -/// This interface will be merged back into and removed in Umbraco 13. -/// -public interface IDataTypeConfigurationConnector2 : IDataTypeConfigurationConnector -{ - /// - /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. - /// - /// The data type. - /// The dependencies. - /// - /// The artifact configuration value. - /// - [Obsolete($"Use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - string? IDataTypeConfigurationConnector.ToArtifact(IDataType dataType, ICollection dependencies) - => ToArtifact(dataType, dependencies, PassThroughCache.Instance); - - /// - /// Gets the artifact configuration value corresponding to a data type configuration and gather dependencies. - /// - /// The data type. - /// The dependencies. - /// The context cache. - /// - /// The artifact configuration value. - /// - string? ToArtifact(IDataType dataType, ICollection dependencies, IContextCache contextCache); - - /// - /// Gets the data type configuration corresponding to an artifact configuration value. - /// - /// The data type. - /// The artifact configuration value. - /// - /// The data type configuration. - /// - [Obsolete($"Use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - object? IDataTypeConfigurationConnector.FromArtifact(IDataType dataType, string? configuration) - => FromArtifact(dataType, configuration, PassThroughCache.Instance); - - /// - /// Gets the data type configuration corresponding to an artifact configuration value. - /// - /// The data type. - /// The artifact configuration value. - /// The context cache. - /// - /// The data type configuration. - /// - object? FromArtifact(IDataType dataType, string? configuration, IContextCache contextCache); -} diff --git a/src/Umbraco.Core/Deploy/IServiceConnector.cs b/src/Umbraco.Core/Deploy/IServiceConnector.cs index adf4c57502..84617943c6 100644 --- a/src/Umbraco.Core/Deploy/IServiceConnector.cs +++ b/src/Umbraco.Core/Deploy/IServiceConnector.cs @@ -12,21 +12,21 @@ public interface IServiceConnector : IDiscoverable /// Gets an artifact. /// /// The entity identifier of the artifact. + /// The context cache. /// /// The corresponding artifact, or null. /// - [Obsolete($"Implement {nameof(IServiceConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - IArtifact? GetArtifact(Udi udi); + IArtifact? GetArtifact(Udi udi, IContextCache contextCache); /// /// Gets an artifact. /// /// The entity. + /// The context cache. /// /// The corresponding artifact. /// - [Obsolete($"Implement {nameof(IServiceConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - IArtifact GetArtifact(object entity); + IArtifact GetArtifact(object entity, IContextCache contextCache); /// /// Initializes processing for an artifact. @@ -47,10 +47,10 @@ public interface IServiceConnector : IDiscoverable void Process(ArtifactDeployState dart, IDeployContext context, int pass); /// - /// Explodes a range into udis. + /// Explodes a range into UDIs. /// /// The range. - /// The list of udis where to add the new udis. + /// The list of UDIs where to add the new UDIs. /// /// Also, it's cool to have a method named Explode. Kaboom! /// @@ -78,9 +78,9 @@ public interface IServiceConnector : IDiscoverable /// /// This is temporary. At least we thought it would be, in sept. 2016. What day is it now? /// - /// At the moment our UI has a hard time returning proper udis, mainly because Core's tree do - /// not manage guids but only ints... so we have to provide a way to support it. The string id here - /// can be either a real string (for string udis) or an "integer as a string", using the value "-1" to + /// At the moment our UI has a hard time returning proper UDIs, mainly because Core's tree do + /// not manage GUIDs but only integers... so we have to provide a way to support it. The string id here + /// can be either a real string (for string UDIs) or an "integer as a string", using the value "-1" to /// indicate the "root" i.e. an open udi. /// /// diff --git a/src/Umbraco.Core/Deploy/IServiceConnector2.cs b/src/Umbraco.Core/Deploy/IServiceConnector2.cs deleted file mode 100644 index 6c1558a956..0000000000 --- a/src/Umbraco.Core/Deploy/IServiceConnector2.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace Umbraco.Cms.Core.Deploy; - -/// -/// -/// This interface will be merged back into and removed in Umbraco 13. -/// -public interface IServiceConnector2 : IServiceConnector -{ - /// - [Obsolete($"Use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - IArtifact? IServiceConnector.GetArtifact(Udi udi) - => GetArtifact(udi, PassThroughCache.Instance); - - /// - /// Gets an artifact. - /// - /// The entity identifier of the artifact. - /// The context cache. - /// - /// The corresponding artifact, or null. - /// - IArtifact? GetArtifact(Udi udi, IContextCache contextCache); - - /// - [Obsolete($"Use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - IArtifact IServiceConnector.GetArtifact(object entity) - => GetArtifact(entity, PassThroughCache.Instance); - - /// - /// Gets an artifact. - /// - /// The entity. - /// The context cache. - /// - /// The corresponding artifact. - /// - IArtifact GetArtifact(object entity, IContextCache contextCache); -} diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs index fe28e41017..5e5e2da1a4 100644 --- a/src/Umbraco.Core/Deploy/IValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -26,11 +26,11 @@ public interface IValueConnector /// The content property value. /// The value property type /// The content dependencies. + /// The context cache. /// /// The deploy property value. /// - [Obsolete($"Implement {nameof(IValueConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies); + string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache); /// /// Gets the content property value corresponding to a deploy property value. @@ -38,9 +38,9 @@ public interface IValueConnector /// The deploy property value. /// The value property type /// The current content property value. + /// The context cache. /// /// The content property value. /// - [Obsolete($"Implement {nameof(IValueConnector2)} and use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue); + object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache); } diff --git a/src/Umbraco.Core/Deploy/IValueConnector2.cs b/src/Umbraco.Core/Deploy/IValueConnector2.cs deleted file mode 100644 index a0c99dca06..0000000000 --- a/src/Umbraco.Core/Deploy/IValueConnector2.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Deploy; - -/// -/// -/// This interface will be merged back into and removed in Umbraco 13. -/// -public interface IValueConnector2 : IValueConnector -{ - /// - [Obsolete($"Use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - string? IValueConnector.ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies) - => ToArtifact(value, propertyType, dependencies, PassThroughCache.Instance); - - /// - /// Gets the deploy property value corresponding to a content property value, and gather dependencies. - /// - /// The content property value. - /// The value property type - /// The content dependencies. - /// The context cache. - /// - /// The deploy property value. - /// - string? ToArtifact(object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache); - - /// - [Obsolete($"Use the overload accepting {nameof(IContextCache)} instead. This overload will be removed in Umbraco 13.")] - object? IValueConnector.FromArtifact(string? value, IPropertyType propertyType, object? currentValue) - => FromArtifact(value, propertyType, currentValue, PassThroughCache.Instance); - - /// - /// Gets the content property value corresponding to a deploy property value. - /// - /// The deploy property value. - /// The value property type - /// The current content property value. - /// The context cache. - /// - /// The content property value. - /// - object? FromArtifact(string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache); -} diff --git a/src/Umbraco.Core/Deploy/ServiceConnectorExtensions.cs b/src/Umbraco.Core/Deploy/ServiceConnectorExtensions.cs deleted file mode 100644 index 0d0000f97c..0000000000 --- a/src/Umbraco.Core/Deploy/ServiceConnectorExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -namespace Umbraco.Cms.Core.Deploy; - -/// -/// Extension methods adding backwards-compatability between and . -/// -/// -/// These extension methods will be removed in Umbraco 13. -/// -public static class ServiceConnectorExtensions -{ - /// - /// Gets an artifact. - /// - /// The connector. - /// The entity identifier of the artifact. - /// The context cache. - /// - /// The corresponding artifact, or null. - /// - /// - /// This extension method tries to make use of the on types also implementing . - /// - public static IArtifact? GetArtifact(this IServiceConnector connector, Udi udi, IContextCache contextCache) - => connector is IServiceConnector2 connector2 - ? connector2.GetArtifact(udi, contextCache) - : connector.GetArtifact(udi); - - /// - /// Gets an artifact. - /// - /// The connector. - /// The entity. - /// The context cache. - /// - /// The corresponding artifact. - /// - /// - /// This extension method tries to make use of the on types also implementing . - /// - public static IArtifact GetArtifact(this IServiceConnector connector, object entity, IContextCache contextCache) - => connector is IServiceConnector2 connector2 - ? connector2.GetArtifact(entity, contextCache) - : connector.GetArtifact(entity); -} diff --git a/src/Umbraco.Core/Deploy/ValueConnectorExtensions.cs b/src/Umbraco.Core/Deploy/ValueConnectorExtensions.cs deleted file mode 100644 index eadcee55e0..0000000000 --- a/src/Umbraco.Core/Deploy/ValueConnectorExtensions.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Cms.Core.Deploy; - -/// -/// Extension methods adding backwards-compatability between and . -/// -/// -/// These extension methods will be removed in Umbraco 13. -/// -public static class ValueConnectorExtensions -{ - /// - /// Gets the artifact value corresponding to a property value and gather dependencies. - /// - /// The connector. - /// The property value. - /// The property type. - /// The dependencies. - /// The context cache. - /// - /// The artifact value. - /// - /// - /// This extension method tries to make use of the on types also implementing . - /// - public static string? ToArtifact(this IValueConnector connector, object? value, IPropertyType propertyType, ICollection dependencies, IContextCache contextCache) - => connector is IValueConnector2 connector2 - ? connector2.ToArtifact(value, propertyType, dependencies, contextCache) - : connector.ToArtifact(value, propertyType, dependencies); - - /// - /// Gets the property value corresponding to an artifact value. - /// - /// The connector. - /// The artifact value. - /// The property type. - /// The current property value. - /// The context cache. - /// - /// The property value. - /// - /// - /// This extension method tries to make use of the on types also implementing . - /// - public static object? FromArtifact(this IValueConnector connector, string? value, IPropertyType propertyType, object? currentValue, IContextCache contextCache) - => connector is IValueConnector2 connector2 - ? connector2.FromArtifact(value, propertyType, currentValue, contextCache) - : connector.FromArtifact(value, propertyType, currentValue); -} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs index ccca8f9f3c..30a8d6a1fa 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/CoreThings/UdiTests.cs @@ -326,7 +326,7 @@ public class UdiTests } [UdiDefinition("foo", UdiType.GuidUdi)] - public class FooConnector : IServiceConnector2 + public class FooConnector : IServiceConnector { public IArtifact GetArtifact(Udi udi, IContextCache contextCache) => throw new NotImplementedException(); From 289ab6740b339301d6425ea9050de55205fa9f3d Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Mon, 6 Nov 2023 18:10:09 +0100 Subject: [PATCH 17/27] Fix WithCollectionBuilder helper methods nullability (#15143) * Fix WithCollectionBuilder helper methods nullability * Remove unnecessary lamba expressions --- .../DependencyInjection/UmbracoBuilder.Collections.cs | 10 +++++----- .../DependencyInjection/UmbracoBuilder.Collections.cs | 6 +++--- .../DependencyInjection/UmbracoBuilder.CoreServices.cs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs index a34d4e377c..9b4a751f8f 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.Collections.cs @@ -36,9 +36,9 @@ public static partial class UmbracoBuilderExtensions /// internal static void AddAllCoreCollectionBuilders(this IUmbracoBuilder builder) { - builder.CacheRefreshers().Add(() => builder.TypeLoader.GetCacheRefreshers()); - builder.DataEditors().Add(() => builder.TypeLoader.GetDataEditors()); - builder.Actions().Add(() => builder .TypeLoader.GetActions()); + builder.CacheRefreshers().Add(builder.TypeLoader.GetCacheRefreshers); + builder.DataEditors().Add(builder.TypeLoader.GetDataEditors); + builder.Actions().Add(builder.TypeLoader.GetActions); // register known content apps builder.ContentApps() @@ -242,14 +242,14 @@ public static partial class UmbracoBuilderExtensions /// Gets the partial view snippets collection builder. /// /// The builder. - public static PartialViewSnippetCollectionBuilder? PartialViewSnippets(this IUmbracoBuilder builder) + public static PartialViewSnippetCollectionBuilder PartialViewSnippets(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); /// /// Gets the partial view macro snippets collection builder. /// /// The builder. - public static PartialViewMacroSnippetCollectionBuilder? PartialViewMacroSnippets(this IUmbracoBuilder builder) + public static PartialViewMacroSnippetCollectionBuilder PartialViewMacroSnippets(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); /// diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs index 609c5305dc..0dd42485a2 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Collections.cs @@ -15,21 +15,21 @@ public static partial class UmbracoBuilderExtensions /// Gets the mappers collection builder. /// /// The builder. - public static MapperCollectionBuilder? Mappers(this IUmbracoBuilder builder) + public static MapperCollectionBuilder Mappers(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); /// /// Gets the NPoco mappers collection builder. /// /// The builder. - public static NPocoMapperCollectionBuilder? NPocoMappers(this IUmbracoBuilder builder) + public static NPocoMapperCollectionBuilder NPocoMappers(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); /// /// Gets the package migration plans collection builder. /// /// The builder. - public static PackageMigrationPlanCollectionBuilder? PackageMigrationPlans(this IUmbracoBuilder builder) + public static PackageMigrationPlanCollectionBuilder PackageMigrationPlans(this IUmbracoBuilder builder) => builder.WithCollectionBuilder(); /// diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 373c4a2008..55b4336b9f 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -78,8 +78,8 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(factory => factory.GetRequiredService().SqlContext); - builder.NPocoMappers()?.Add(); - builder.PackageMigrationPlans()?.Add(() => builder.TypeLoader.GetPackageMigrationPlans()); + builder.NPocoMappers().Add(); + builder.PackageMigrationPlans().Add(builder.TypeLoader.GetPackageMigrationPlans); builder.Services.AddSingleton(); builder.Services.AddSingleton(); @@ -107,7 +107,7 @@ public static partial class UmbracoBuilderExtensions // register persistence mappers - required by database factory so needs to be done here // means the only place the collection can be modified is in a runtime - afterwards it // has been frozen and it is too late - builder.Mappers()?.AddCoreMappers(); + builder.Mappers().AddCoreMappers(); // register the scope provider builder.Services.AddSingleton(sp => ActivatorUtilities.CreateInstance(sp, sp.GetRequiredService())); // implements IScopeProvider, IScopeAccessor From c8dff604a01bb1a1a314b64c02372edc0d567c8e Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Mon, 6 Nov 2023 11:27:10 +0100 Subject: [PATCH 18/27] Changes PackageMigrationsPlans to be a weighted collection like dashboards --- .../Packaging/PackageMigrationPlanCollectionBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollectionBuilder.cs b/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollectionBuilder.cs index 91b1364139..324257286e 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollectionBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollectionBuilder.cs @@ -2,7 +2,7 @@ using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Packaging; -public class PackageMigrationPlanCollectionBuilder : LazyCollectionBuilderBase { protected override PackageMigrationPlanCollectionBuilder This => this; From 3306fe53e8c42866dbc9e4c3f787e790621ef4b7 Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Mon, 6 Nov 2023 20:15:34 +0100 Subject: [PATCH 19/27] Fix build --- .../DependencyInjection/UmbracoBuilder.CoreServices.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 55b4336b9f..1dff6e7eeb 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -79,7 +79,7 @@ public static partial class UmbracoBuilderExtensions builder.Services.AddSingleton(); builder.Services.AddSingleton(factory => factory.GetRequiredService().SqlContext); builder.NPocoMappers().Add(); - builder.PackageMigrationPlans().Add(builder.TypeLoader.GetPackageMigrationPlans); + builder.PackageMigrationPlans().Add(builder.TypeLoader.GetPackageMigrationPlans()); builder.Services.AddSingleton(); builder.Services.AddSingleton(); From 06d61094cc6a9e68cfa86102bb07b6efd18511c9 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 7 Nov 2023 09:01:27 +0100 Subject: [PATCH 20/27] Ensure invariant properties return the correct cache value at source level (#15145) Co-authored-by: Bjarke Berg --- .../Property.cs | 5 +- .../PublishedContentVarianceTests.cs | 152 ++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs diff --git a/src/Umbraco.PublishedCache.NuCache/Property.cs b/src/Umbraco.PublishedCache.NuCache/Property.cs index d921eb3f7c..6a9e1a982c 100644 --- a/src/Umbraco.PublishedCache.NuCache/Property.cs +++ b/src/Umbraco.PublishedCache.NuCache/Property.cs @@ -25,6 +25,7 @@ internal class Property : PublishedPropertyBase // the invariant-neutral source and inter values private readonly object? _sourceValue; private readonly ContentVariation _variations; + private bool _sourceValueIsInvariant; // the variant and non-variant object values private CacheValues? _cacheValues; @@ -89,6 +90,7 @@ internal class Property : PublishedPropertyBase // this variable is used for contextualizing the variation level when calculating property values. // it must be set to the union of variance (the combination of content type and property type variance). _variations = propertyType.Variations | content.ContentType.Variations; + _sourceValueIsInvariant = propertyType.Variations is ContentVariation.Nothing; } // clone for previewing as draft a published content that is published and has no draft @@ -104,6 +106,7 @@ internal class Property : PublishedPropertyBase _isMember = origin._isMember; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; _variations = origin._variations; + _sourceValueIsInvariant = origin._sourceValueIsInvariant; } // used to cache the CacheValues of this property @@ -152,7 +155,7 @@ internal class Property : PublishedPropertyBase { _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); - if (culture == string.Empty && segment == string.Empty) + if (_sourceValueIsInvariant || (culture == string.Empty && segment == string.Empty)) { return _sourceValue; } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs new file mode 100644 index 0000000000..7d117b96c5 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs @@ -0,0 +1,152 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published; + +[TestFixture] +public class PublishedContentVarianceTests +{ + private const string PropertyTypeAlias = "theProperty"; + private const string DaCulture = "da-DK"; + private const string EnCulture = "en-US"; + private const string Segment1 = "segment1"; + private const string Segment2 = "segment2"; + + [Test] + public void No_Content_Variation_Can_Get_Invariant_Property() + { + var content = CreatePublishedContent(ContentVariation.Nothing, ContentVariation.Nothing); + var value = GetPropertyValue(content); + Assert.AreEqual("Invariant property value", value); + } + + [TestCase(DaCulture)] + [TestCase(EnCulture)] + [TestCase("")] + public void Content_Culture_Variation_Can_Get_Invariant_Property(string culture) + { + var content = CreatePublishedContent(ContentVariation.Culture, ContentVariation.Nothing, variationContextCulture: culture); + var value = GetPropertyValue(content); + Assert.AreEqual("Invariant property value", value); + } + + [TestCase(Segment1)] + [TestCase(Segment2)] + [TestCase("")] + public void Content_Segment_Variation_Can_Get_Invariant_Property(string segment) + { + var content = CreatePublishedContent(ContentVariation.Culture, ContentVariation.Nothing, variationContextSegment: segment); + var value = GetPropertyValue(content); + Assert.AreEqual("Invariant property value", value); + } + + [TestCase(DaCulture, "DaDk property value")] + [TestCase(EnCulture, "EnUs property value")] + public void Content_Culture_Variation_Can_Get_Culture_Variant_Property(string culture, string expectedValue) + { + var content = CreatePublishedContent(ContentVariation.Culture, ContentVariation.Culture, variationContextCulture: culture); + var value = GetPropertyValue(content); + Assert.AreEqual(expectedValue, value); + } + + [TestCase(Segment1, "Segment1 property value")] + [TestCase(Segment2, "Segment2 property value")] + public void Content_Segment_Variation_Can_Get_Segment_Variant_Property(string segment, string expectedValue) + { + var content = CreatePublishedContent(ContentVariation.Segment, ContentVariation.Segment, variationContextSegment: segment); + var value = GetPropertyValue(content); + Assert.AreEqual(expectedValue, value); + } + + [TestCase(DaCulture, Segment1, "DaDk Segment1 property value")] + [TestCase(DaCulture, Segment2, "DaDk Segment2 property value")] + [TestCase(EnCulture, Segment1, "EnUs Segment1 property value")] + [TestCase(EnCulture, Segment2, "EnUs Segment2 property value")] + public void Content_Culture_And_Segment_Variation_Can_Get_Culture_And_Segment_Variant_Property(string culture, string segment, string expectedValue) + { + var content = CreatePublishedContent(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment, variationContextCulture: culture, variationContextSegment: segment); + var value = GetPropertyValue(content); + Assert.AreEqual(expectedValue, value); + } + + private object? GetPropertyValue(IPublishedContent content) => content.GetProperty(PropertyTypeAlias)!.GetValue(); + + private IPublishedContent CreatePublishedContent(ContentVariation contentTypeVariation, ContentVariation propertyTypeVariation, string? variationContextCulture = null, string? variationContextSegment = null) + { + var propertyType = new Mock(); + propertyType.SetupGet(p => p.Alias).Returns(PropertyTypeAlias); + propertyType.SetupGet(p => p.CacheLevel).Returns(PropertyCacheLevel.None); + propertyType.SetupGet(p => p.DeliveryApiCacheLevel).Returns(PropertyCacheLevel.None); + propertyType.SetupGet(p => p.Variations).Returns(propertyTypeVariation); + propertyType + .Setup(p => p.ConvertSourceToInter(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((IPublishedElement _, object? source, bool _) => source); + propertyType + .Setup(p => p.ConvertInterToObject(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((IPublishedElement _, PropertyCacheLevel _, object? inter, bool _) => inter); + + var contentType = new Mock(); + contentType.SetupGet(c => c.PropertyTypes).Returns(new[] { propertyType.Object }); + contentType.SetupGet(c => c.Variations).Returns(contentTypeVariation); + + var propertyData = new List(); + + switch (propertyTypeVariation) + { + case ContentVariation.Culture: + propertyData.Add(CreatePropertyData("EnUs property value", culture: EnCulture)); + propertyData.Add(CreatePropertyData("DaDk property value", culture: DaCulture)); + break; + case ContentVariation.Segment: + propertyData.Add(CreatePropertyData("Segment1 property value", segment: Segment1)); + propertyData.Add(CreatePropertyData("Segment2 property value", segment: Segment2)); + break; + case ContentVariation.CultureAndSegment: + propertyData.Add(CreatePropertyData("EnUs Segment1 property value", culture: EnCulture, segment: Segment1)); + propertyData.Add(CreatePropertyData("EnUs Segment2 property value", culture: EnCulture, segment: Segment2)); + propertyData.Add(CreatePropertyData("DaDk Segment1 property value", culture: DaCulture, segment: Segment1)); + propertyData.Add(CreatePropertyData("DaDk Segment2 property value", culture: DaCulture, segment: Segment2)); + break; + case ContentVariation.Nothing: + propertyData.Add(CreatePropertyData("Invariant property value")); + break; + } + + var properties = new Dictionary { { PropertyTypeAlias, propertyData.ToArray() } }; + + var contentNode = new ContentNode(123, Guid.NewGuid(), contentType.Object, 1, string.Empty, 1, 1, DateTime.Now, 1); + var contentData = new ContentData("bla", "bla", 1, DateTime.Now, 1, 1, true, properties, null); + + var elementCache = new FastDictionaryAppCache(); + var snapshotCache = new FastDictionaryAppCache(); + var publishedSnapshotMock = new Mock(); + publishedSnapshotMock.SetupGet(p => p.ElementsCache).Returns(elementCache); + publishedSnapshotMock.SetupGet(p => p.SnapshotCache).Returns(snapshotCache); + + var publishedSnapshot = publishedSnapshotMock.Object; + var publishedSnapshotAccessor = new Mock(); + publishedSnapshotAccessor.Setup(p => p.TryGetPublishedSnapshot(out publishedSnapshot)).Returns(true); + + var variationContextAccessorMock = new Mock(); + variationContextAccessorMock + .SetupGet(mock => mock.VariationContext) + .Returns(() => new VariationContext(variationContextCulture, variationContextSegment)); + + return new PublishedContent( + contentNode, + contentData, + publishedSnapshotAccessor.Object, + variationContextAccessorMock.Object, + Mock.Of()); + + PropertyData CreatePropertyData(string value, string? culture = null, string? segment = null) + => new() { Culture = culture ?? string.Empty, Segment = segment ?? string.Empty, Value = value }; + } +} From 80fac866c90ff4e6bcca5a9ee5a6b9917e41a660 Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 7 Nov 2023 09:01:27 +0100 Subject: [PATCH 21/27] Ensure invariant properties return the correct cache value at source level (#15145) Co-authored-by: Bjarke Berg --- .../Property.cs | 5 +- .../PublishedContentVarianceTests.cs | 152 ++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs diff --git a/src/Umbraco.PublishedCache.NuCache/Property.cs b/src/Umbraco.PublishedCache.NuCache/Property.cs index d921eb3f7c..6a9e1a982c 100644 --- a/src/Umbraco.PublishedCache.NuCache/Property.cs +++ b/src/Umbraco.PublishedCache.NuCache/Property.cs @@ -25,6 +25,7 @@ internal class Property : PublishedPropertyBase // the invariant-neutral source and inter values private readonly object? _sourceValue; private readonly ContentVariation _variations; + private bool _sourceValueIsInvariant; // the variant and non-variant object values private CacheValues? _cacheValues; @@ -89,6 +90,7 @@ internal class Property : PublishedPropertyBase // this variable is used for contextualizing the variation level when calculating property values. // it must be set to the union of variance (the combination of content type and property type variance). _variations = propertyType.Variations | content.ContentType.Variations; + _sourceValueIsInvariant = propertyType.Variations is ContentVariation.Nothing; } // clone for previewing as draft a published content that is published and has no draft @@ -104,6 +106,7 @@ internal class Property : PublishedPropertyBase _isMember = origin._isMember; _publishedSnapshotAccessor = origin._publishedSnapshotAccessor; _variations = origin._variations; + _sourceValueIsInvariant = origin._sourceValueIsInvariant; } // used to cache the CacheValues of this property @@ -152,7 +155,7 @@ internal class Property : PublishedPropertyBase { _content.VariationContextAccessor.ContextualizeVariation(_variations, _content.Id, ref culture, ref segment); - if (culture == string.Empty && segment == string.Empty) + if (_sourceValueIsInvariant || (culture == string.Empty && segment == string.Empty)) { return _sourceValue; } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs new file mode 100644 index 0000000000..7d117b96c5 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PublishedContentVarianceTests.cs @@ -0,0 +1,152 @@ +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published; + +[TestFixture] +public class PublishedContentVarianceTests +{ + private const string PropertyTypeAlias = "theProperty"; + private const string DaCulture = "da-DK"; + private const string EnCulture = "en-US"; + private const string Segment1 = "segment1"; + private const string Segment2 = "segment2"; + + [Test] + public void No_Content_Variation_Can_Get_Invariant_Property() + { + var content = CreatePublishedContent(ContentVariation.Nothing, ContentVariation.Nothing); + var value = GetPropertyValue(content); + Assert.AreEqual("Invariant property value", value); + } + + [TestCase(DaCulture)] + [TestCase(EnCulture)] + [TestCase("")] + public void Content_Culture_Variation_Can_Get_Invariant_Property(string culture) + { + var content = CreatePublishedContent(ContentVariation.Culture, ContentVariation.Nothing, variationContextCulture: culture); + var value = GetPropertyValue(content); + Assert.AreEqual("Invariant property value", value); + } + + [TestCase(Segment1)] + [TestCase(Segment2)] + [TestCase("")] + public void Content_Segment_Variation_Can_Get_Invariant_Property(string segment) + { + var content = CreatePublishedContent(ContentVariation.Culture, ContentVariation.Nothing, variationContextSegment: segment); + var value = GetPropertyValue(content); + Assert.AreEqual("Invariant property value", value); + } + + [TestCase(DaCulture, "DaDk property value")] + [TestCase(EnCulture, "EnUs property value")] + public void Content_Culture_Variation_Can_Get_Culture_Variant_Property(string culture, string expectedValue) + { + var content = CreatePublishedContent(ContentVariation.Culture, ContentVariation.Culture, variationContextCulture: culture); + var value = GetPropertyValue(content); + Assert.AreEqual(expectedValue, value); + } + + [TestCase(Segment1, "Segment1 property value")] + [TestCase(Segment2, "Segment2 property value")] + public void Content_Segment_Variation_Can_Get_Segment_Variant_Property(string segment, string expectedValue) + { + var content = CreatePublishedContent(ContentVariation.Segment, ContentVariation.Segment, variationContextSegment: segment); + var value = GetPropertyValue(content); + Assert.AreEqual(expectedValue, value); + } + + [TestCase(DaCulture, Segment1, "DaDk Segment1 property value")] + [TestCase(DaCulture, Segment2, "DaDk Segment2 property value")] + [TestCase(EnCulture, Segment1, "EnUs Segment1 property value")] + [TestCase(EnCulture, Segment2, "EnUs Segment2 property value")] + public void Content_Culture_And_Segment_Variation_Can_Get_Culture_And_Segment_Variant_Property(string culture, string segment, string expectedValue) + { + var content = CreatePublishedContent(ContentVariation.CultureAndSegment, ContentVariation.CultureAndSegment, variationContextCulture: culture, variationContextSegment: segment); + var value = GetPropertyValue(content); + Assert.AreEqual(expectedValue, value); + } + + private object? GetPropertyValue(IPublishedContent content) => content.GetProperty(PropertyTypeAlias)!.GetValue(); + + private IPublishedContent CreatePublishedContent(ContentVariation contentTypeVariation, ContentVariation propertyTypeVariation, string? variationContextCulture = null, string? variationContextSegment = null) + { + var propertyType = new Mock(); + propertyType.SetupGet(p => p.Alias).Returns(PropertyTypeAlias); + propertyType.SetupGet(p => p.CacheLevel).Returns(PropertyCacheLevel.None); + propertyType.SetupGet(p => p.DeliveryApiCacheLevel).Returns(PropertyCacheLevel.None); + propertyType.SetupGet(p => p.Variations).Returns(propertyTypeVariation); + propertyType + .Setup(p => p.ConvertSourceToInter(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((IPublishedElement _, object? source, bool _) => source); + propertyType + .Setup(p => p.ConvertInterToObject(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((IPublishedElement _, PropertyCacheLevel _, object? inter, bool _) => inter); + + var contentType = new Mock(); + contentType.SetupGet(c => c.PropertyTypes).Returns(new[] { propertyType.Object }); + contentType.SetupGet(c => c.Variations).Returns(contentTypeVariation); + + var propertyData = new List(); + + switch (propertyTypeVariation) + { + case ContentVariation.Culture: + propertyData.Add(CreatePropertyData("EnUs property value", culture: EnCulture)); + propertyData.Add(CreatePropertyData("DaDk property value", culture: DaCulture)); + break; + case ContentVariation.Segment: + propertyData.Add(CreatePropertyData("Segment1 property value", segment: Segment1)); + propertyData.Add(CreatePropertyData("Segment2 property value", segment: Segment2)); + break; + case ContentVariation.CultureAndSegment: + propertyData.Add(CreatePropertyData("EnUs Segment1 property value", culture: EnCulture, segment: Segment1)); + propertyData.Add(CreatePropertyData("EnUs Segment2 property value", culture: EnCulture, segment: Segment2)); + propertyData.Add(CreatePropertyData("DaDk Segment1 property value", culture: DaCulture, segment: Segment1)); + propertyData.Add(CreatePropertyData("DaDk Segment2 property value", culture: DaCulture, segment: Segment2)); + break; + case ContentVariation.Nothing: + propertyData.Add(CreatePropertyData("Invariant property value")); + break; + } + + var properties = new Dictionary { { PropertyTypeAlias, propertyData.ToArray() } }; + + var contentNode = new ContentNode(123, Guid.NewGuid(), contentType.Object, 1, string.Empty, 1, 1, DateTime.Now, 1); + var contentData = new ContentData("bla", "bla", 1, DateTime.Now, 1, 1, true, properties, null); + + var elementCache = new FastDictionaryAppCache(); + var snapshotCache = new FastDictionaryAppCache(); + var publishedSnapshotMock = new Mock(); + publishedSnapshotMock.SetupGet(p => p.ElementsCache).Returns(elementCache); + publishedSnapshotMock.SetupGet(p => p.SnapshotCache).Returns(snapshotCache); + + var publishedSnapshot = publishedSnapshotMock.Object; + var publishedSnapshotAccessor = new Mock(); + publishedSnapshotAccessor.Setup(p => p.TryGetPublishedSnapshot(out publishedSnapshot)).Returns(true); + + var variationContextAccessorMock = new Mock(); + variationContextAccessorMock + .SetupGet(mock => mock.VariationContext) + .Returns(() => new VariationContext(variationContextCulture, variationContextSegment)); + + return new PublishedContent( + contentNode, + contentData, + publishedSnapshotAccessor.Object, + variationContextAccessorMock.Object, + Mock.Of()); + + PropertyData CreatePropertyData(string value, string? culture = null, string? segment = null) + => new() { Culture = culture ?? string.Empty, Segment = segment ?? string.Empty, Value = value }; + } +} From 9e5ff305c8a969856f27cf3f63f0e85ad3033e14 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 7 Nov 2023 10:49:46 +0100 Subject: [PATCH 22/27] Batched bulk WhereIn query to avoid To mana paramaters error (#15004) Co-authored-by: Sven Geusens --- .../Implement/ContentTypeRepositoryBase.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 3e92a4ae7f..59aa92bb82 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -1385,10 +1385,15 @@ AND umbracoNode.id <> @id", } // Now bulk update the umbracoDocument table - foreach (IGrouping> editValue in editedDocument.GroupBy(x => x.Value)) + // we need to do this in batches as the WhereIn Npoco method translates to all the nodeIds being passed in as parameters when using the SqlClient provider + // this results in to many parameters (>2100) being passed to the client when there are a lot of documents being normalized + foreach (IGrouping> groupByValue in editedDocument.GroupBy(x => x.Value)) { - Database.Execute(Sql().Update(u => u.Set(x => x.Edited, editValue.Key)) - .WhereIn(x => x.NodeId, editValue.Select(x => x.Key))); + foreach (IEnumerable> batch in groupByValue.InGroupsOf(2000)) + { + Database.Execute(Sql().Update(u => u.Set(x => x.Edited, groupByValue.Key)) + .WhereIn(x => x.NodeId, batch.Select(x => x.Key))); + } } } From c90f05c57c515423b5e3d788347714dc41609a2d Mon Sep 17 00:00:00 2001 From: Bjarke Berg Date: Tue, 7 Nov 2023 11:55:06 +0100 Subject: [PATCH 23/27] bump version --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index 006a78b8bd..5994a80c7d 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": "12.3.1", + "version": "12.4.0", "assemblyVersion": { "precision": "build" }, From 997434cebbca7b5c33ce176a8090807d43d194e9 Mon Sep 17 00:00:00 2001 From: Sven Geusens Date: Tue, 7 Nov 2023 16:02:20 +0100 Subject: [PATCH 24/27] Move localdb file management of PublishedSnapshotService into itself. (#15085) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move localdb file management of PublishedSnapshotService into itself. Added a way for the PublishedSnapshot service to clean up it's local files so (for example) Upgrade migrations have a reliable way of removing known invalid cache files without running into locking issues * Small rename to differentiate existing method from simple getter * Fix breaking change Long live default implementations 🎉 * Another breaking change fix --------- Co-authored-by: Sven Geusens --- .../IPublishedSnapshotService.cs | 4 ++++ .../Migrations/Upgrade/V_12_0_0/ResetCache.cs | 20 ++++++++++++---- .../PublishedSnapshotService.cs | 24 ++++++++++++++++--- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs index 5bd5ff23cc..8e661aa758 100644 --- a/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs +++ b/src/Umbraco.Core/PublishedCache/IPublishedSnapshotService.cs @@ -125,4 +125,8 @@ public interface IPublishedSnapshotService : IDisposable /// Cleans up unused snapshots /// Task CollectAsync(); + + void ResetLocalDb() + { + } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs index b55b4c4ca7..0e41ad89ca 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs @@ -1,22 +1,34 @@ -using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.PublishedCache; +using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment; namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_12_0_0; public class ResetCache : MigrationBase { private readonly IHostingEnvironment _hostingEnvironment; + private readonly IPublishedSnapshotService _publishedSnapshotService; + [Obsolete("Use ctor with all params - This will be removed in Umbraco 14.")] public ResetCache(IMigrationContext context, IHostingEnvironment hostingEnvironment) - : base(context) => + : this(context, hostingEnvironment, StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public ResetCache(IMigrationContext context, IHostingEnvironment hostingEnvironment, IPublishedSnapshotService publishedSnapshotService) + : base(context) + { _hostingEnvironment = hostingEnvironment; + _publishedSnapshotService = publishedSnapshotService; + } protected override void Migrate() { RebuildCache = true; var distCacheFolderAbsolutePath = Path.Combine(_hostingEnvironment.LocalTempPath, "DistCache"); - var nuCacheFolderAbsolutePath = Path.Combine(_hostingEnvironment.LocalTempPath, "NuCache"); DeleteAllFilesInFolder(distCacheFolderAbsolutePath); - DeleteAllFilesInFolder(nuCacheFolderAbsolutePath); + _publishedSnapshotService.ResetLocalDb(); } private void DeleteAllFilesInFolder(string path) diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 68b50cfd91..a7f8c42823 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -70,6 +70,8 @@ internal class PublishedSnapshotService : IPublishedSnapshotService private long _mediaGen; private ContentStore _mediaStore = null!; + private string LocalFilePath => Path.Combine(_hostingEnvironment.LocalTempPath, "NuCache"); + public PublishedSnapshotService( PublishedSnapshotServiceOptions options, ISyncBootStateAccessor syncBootStateAccessor, @@ -475,6 +477,22 @@ internal class PublishedSnapshotService : IPublishedSnapshotService return GetUid(_mediaStore, id); } + + public void ResetLocalDb() + { + _logger.LogInformation( + "Resetting NuCache local db"); + var path = LocalFilePath; + if (Directory.Exists(path) is false) + { + return; + } + + MainDomRelease(); + Directory.Delete(path, true); + MainDomRegister(); + } + /// /// Lazily populates the stores only when they are first requested /// @@ -603,7 +621,7 @@ internal class PublishedSnapshotService : IPublishedSnapshotService /// private void MainDomRegister() { - var path = GetLocalFilesPath(); + var path = GetAndEnsureLocalFilesPathExists(); var localContentDbPath = Path.Combine(path, "NuCache.Content.db"); var localMediaDbPath = Path.Combine(path, "NuCache.Media.db"); @@ -652,9 +670,9 @@ internal class PublishedSnapshotService : IPublishedSnapshotService } } - private string GetLocalFilesPath() + private string GetAndEnsureLocalFilesPathExists() { - var path = Path.Combine(_hostingEnvironment.LocalTempPath, "NuCache"); + var path = LocalFilePath; if (!Directory.Exists(path)) { From 915b63db29c79c6c07fe735dba946bce09cbd805 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 9 Nov 2023 06:30:25 +0100 Subject: [PATCH 25/27] Fix rendering and parsing new RTE markup object in backoffice (#15166) * Render RTE markup in custom view * Fix ncRichText filter to support RTE markup object --- .../umbBlockGridDemoRichTextBlock.html | 4 ++-- .../src/common/filters/nestedcontent.filter.js | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html index b3362fcda9..adda418723 100644 --- a/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html +++ b/src/Umbraco.Cms.StaticAssets/wwwroot/App_Plugins/Umbraco.BlockGridEditor.DefaultCustomViews/umbBlockGridDemoRichTextBlock.html @@ -21,5 +21,5 @@ -
-
\ No newline at end of file +
+
diff --git a/src/Umbraco.Web.UI.Client/src/common/filters/nestedcontent.filter.js b/src/Umbraco.Web.UI.Client/src/common/filters/nestedcontent.filter.js index 2d09b521e3..4fc10dbfa3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/filters/nestedcontent.filter.js +++ b/src/Umbraco.Web.UI.Client/src/common/filters/nestedcontent.filter.js @@ -1,4 +1,4 @@ -// Filter to take a node id and grab it's name instead +// Filter to take a node id and grab it's name instead // Usage: {{ pickerAlias | ncNodeName }} // Cache for node names so we don't make a ton of requests @@ -78,6 +78,9 @@ angular.module("umbraco.filters").filter("ncNodeName", function (editorState, en }).filter("ncRichText", function () { return function (input) { - return $("
").html(input).text(); + // Get markup from RTE object or assume HTML + var html = input && Object.hasOwn(input, 'markup') ? input.markup : input; + + return $("
").html(html).text(); }; }); From d301679e532edbbd2266c6bf5dc834d341ed8a88 Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 9 Nov 2023 08:39:42 +0100 Subject: [PATCH 26/27] Include Umbraco.Web.UI.Login project, update compatibility suppressions and remove unnecessary package references (#15163) * Fix Umbraco.Web.UI.Login not showing up in solution * Update PackageValidationBaselineVersion to 13.0.0-rc1 and remove all CompatibilitySuppressions.xml files * Remove unnecessary package references * Move Microsoft.EntityFrameworkCore.Design reference and add comment * Disable package validation and fix override in tests * Inherit package validation setting for EF Core projects --- Directory.Build.props | 4 +-- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- ...co.Cms.Persistence.EFCore.SqlServer.csproj | 2 -- ...braco.Cms.Persistence.EFCore.Sqlite.csproj | 2 -- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- src/Umbraco.Core/Umbraco.Core.csproj | 9 ------ .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../Umbraco.Infrastructure.csproj | 3 -- .../CompatibilitySuppressions.xml | 8 ----- .../CompatibilitySuppressions.xml | 8 ----- .../Umbraco.Web.BackOffice.csproj | 1 - .../CompatibilitySuppressions.xml | 8 ----- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 8 ++--- .../CompatibilitySuppressions.xml | 8 ----- tests/Directory.Build.props | 1 + .../Umbraco.Tests.Benchmarks.csproj | 2 -- .../CompatibilitySuppressions.xml | 8 ----- .../Umbraco.Tests.Common.csproj | 2 +- .../CompatibilitySuppressions.xml | 8 ----- .../Umbraco.Tests.Integration.csproj | 3 +- umbraco.sln | 31 ++++++++++--------- 29 files changed, 24 insertions(+), 180 deletions(-) delete mode 100644 src/Umbraco.Cms.Api.Common/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Api.Delivery/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Imaging.ImageSharp/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Imaging.ImageSharp2/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Persistence.EFCore/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Core/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Infrastructure/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.Common/CompatibilitySuppressions.xml delete mode 100644 src/Umbraco.Web.Website/CompatibilitySuppressions.xml delete mode 100644 tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml delete mode 100644 tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml diff --git a/Directory.Build.props b/Directory.Build.props index b0b655f4b0..0364d4e086 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -30,8 +30,8 @@ false - true - 12.0.0 + false + 13.0.0 true true diff --git a/src/Umbraco.Cms.Api.Common/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Api.Common/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Api.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Api.Delivery/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Api.Delivery/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Api.Delivery/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Imaging.ImageSharp/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Imaging.ImageSharp/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Imaging.ImageSharp/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Imaging.ImageSharp2/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Imaging.ImageSharp2/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Imaging.ImageSharp2/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj index a75d681949..04e711f8d9 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore.SqlServer/Umbraco.Cms.Persistence.EFCore.SqlServer.csproj @@ -2,8 +2,6 @@ Umbraco CMS - Persistence - Entity Framework Core - SQL Server migrations Adds support for Entity Framework Core SQL Server migrations to Umbraco CMS. - - false diff --git a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj index d6559a35ea..5cb9949c04 100644 --- a/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj +++ b/src/Umbraco.Cms.Persistence.EFCore.Sqlite/Umbraco.Cms.Persistence.EFCore.Sqlite.csproj @@ -2,8 +2,6 @@ Umbraco CMS - Persistence - Entity Framework Core - SQLite migrations Adds support for Entity Framework Core SQLite migrations to Umbraco CMS. - - false diff --git a/src/Umbraco.Cms.Persistence.EFCore/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.EFCore/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Persistence.EFCore/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Persistence.SqlServer/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml b/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.Persistence.Sqlite/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml b/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Cms.StaticAssets/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Core/CompatibilitySuppressions.xml b/src/Umbraco.Core/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Core/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 8cdb0d2b82..4f4dd097b1 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -16,18 +16,9 @@ - - - - - - - - - <_Parameter1>Umbraco.Tests diff --git a/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml b/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Examine.Lucene/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml b/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Infrastructure/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 80d19e7a94..1c2e897ebc 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -37,9 +37,6 @@ - - - diff --git a/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml b/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.PublishedCache.NuCache/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml b/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Web.BackOffice/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 12364baaf1..a64d0d2408 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -7,7 +7,6 @@ - diff --git a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml b/src/Umbraco.Web.Common/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Web.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 9c90527608..c7258061d5 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -9,6 +9,8 @@ + + @@ -17,12 +19,6 @@ - - - all - - - true diff --git a/src/Umbraco.Web.Website/CompatibilitySuppressions.xml b/src/Umbraco.Web.Website/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/src/Umbraco.Web.Website/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index ec220ec3a8..1d532e6664 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -5,6 +5,7 @@ false + $(EnablePackageValidation) false diff --git a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index 3884fec209..342622c094 100644 --- a/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/tests/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -7,8 +7,6 @@ - - diff --git a/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml b/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml deleted file mode 100644 index d864a9d227..0000000000 --- a/tests/Umbraco.Tests.Common/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - \ No newline at end of file diff --git a/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj b/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj index ae194a9f77..c84c8fad6e 100644 --- a/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj +++ b/tests/Umbraco.Tests.Common/Umbraco.Tests.Common.csproj @@ -5,7 +5,7 @@ Contains commonly used tools to write tests for Umbraco CMS, such as various builders for content etc. Umbraco.Cms.Tests.Common true - true + $(BaseEnablePackageValidation) diff --git a/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml b/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml deleted file mode 100644 index 0abc4e0e3a..0000000000 --- a/tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PKV006 - net7.0 - - diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 74f8d64b4a..e706538a02 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -6,8 +6,7 @@ Contains helper classes for integration tests with Umbraco CMS, including all internal integration tests. Umbraco.Cms.Tests.Integration true - true - true + $(BaseEnablePackageValidation) diff --git a/umbraco.sln b/umbraco.sln index 696a51bf17..36d381c729 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -34,20 +34,20 @@ Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Client", "ht StartServerOnDebug = "false" EndProjectSection EndProject -Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Login", "http://localhost:3961", "{A5A4D377-A873-4469-AB5F-24B32BC0E55A}" +Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "Umbraco.Web.UI.Login", "http://localhost:3962", "{A5A4D377-A873-4469-AB5F-24B32BC0E55A}" ProjectSection(WebsiteProperties) = preProject UseIISExpress = "true" TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.8" - Debug.AspNetCompiler.VirtualPath = "/localhost_3961" + Debug.AspNetCompiler.VirtualPath = "/localhost_3962" Debug.AspNetCompiler.PhysicalPath = "src\Umbraco.Web.UI.Login\" - Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3961\" + Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3962\" Debug.AspNetCompiler.Updateable = "true" Debug.AspNetCompiler.ForceOverwrite = "true" Debug.AspNetCompiler.FixedNames = "false" Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/localhost_3961" + Release.AspNetCompiler.VirtualPath = "/localhost_3962" Release.AspNetCompiler.PhysicalPath = "src\Umbraco.Web.UI.Login\" - Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3961\" + Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3962\" Release.AspNetCompiler.Updateable = "true" Release.AspNetCompiler.ForceOverwrite = "true" Release.AspNetCompiler.FixedNames = "false" @@ -174,11 +174,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Api.Common", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Imaging.ImageSharp", "src\Umbraco.Cms.Imaging.ImageSharp\Umbraco.Cms.Imaging.ImageSharp.csproj", "{35E3DA10-5549-41DE-B7ED-CC29355BA9FD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.Persistence.EFCore", "src\Umbraco.Cms.Persistence.EFCore\Umbraco.Cms.Persistence.EFCore.csproj", "{9046F56E-4AC3-4603-A6A3-3ACCF632997E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFCore", "src\Umbraco.Cms.Persistence.EFCore\Umbraco.Cms.Persistence.EFCore.csproj", "{9046F56E-4AC3-4603-A6A3-3ACCF632997E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.Persistence.EFCore.Sqlite", "src\Umbraco.Cms.Persistence.EFCore.Sqlite\Umbraco.Cms.Persistence.EFCore.Sqlite.csproj", "{8B4771F0-8EC2-4761-BBCE-DDE073DB3B7A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFCore.Sqlite", "src\Umbraco.Cms.Persistence.EFCore.Sqlite\Umbraco.Cms.Persistence.EFCore.Sqlite.csproj", "{8B4771F0-8EC2-4761-BBCE-DDE073DB3B7A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Cms.Persistence.EFCore.SqlServer", "src\Umbraco.Cms.Persistence.EFCore.SqlServer\Umbraco.Cms.Persistence.EFCore.SqlServer.csproj", "{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Umbraco.Cms.Persistence.EFCore.SqlServer", "src\Umbraco.Cms.Persistence.EFCore.SqlServer\Umbraco.Cms.Persistence.EFCore.SqlServer.csproj", "{9276C3F0-0DC9-46C9-BF32-9EE79D92AE02}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -196,6 +196,9 @@ Global {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {3819A550-DCEC-4153-91B4-8BA9F7F0B9B4}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU + {A5A4D377-A873-4469-AB5F-24B32BC0E55A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5A4D377-A873-4469-AB5F-24B32BC0E55A}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {A5A4D377-A873-4469-AB5F-24B32BC0E55A}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E4C8A12-FBE0-4673-8CE2-DF99D5D57817}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU @@ -326,18 +329,18 @@ Global {D48B5D6B-82FF-4235-986C-CDE646F41DEC}.Release|Any CPU.Build.0 = Release|Any CPU {D48B5D6B-82FF-4235-986C-CDE646F41DEC}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU {D48B5D6B-82FF-4235-986C-CDE646F41DEC}.SkipTests|Any CPU.Build.0 = Debug|Any CPU - {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Release|Any CPU.Build.0 = Release|Any CPU - {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU - {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.SkipTests|Any CPU.Build.0 = Debug|Any CPU {35E3DA10-5549-41DE-B7ED-CC29355BA9FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {35E3DA10-5549-41DE-B7ED-CC29355BA9FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {35E3DA10-5549-41DE-B7ED-CC29355BA9FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {35E3DA10-5549-41DE-B7ED-CC29355BA9FD}.Release|Any CPU.Build.0 = Release|Any CPU {35E3DA10-5549-41DE-B7ED-CC29355BA9FD}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU {35E3DA10-5549-41DE-B7ED-CC29355BA9FD}.SkipTests|Any CPU.Build.0 = Debug|Any CPU + {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.Release|Any CPU.Build.0 = Release|Any CPU + {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.SkipTests|Any CPU.ActiveCfg = Debug|Any CPU + {9046F56E-4AC3-4603-A6A3-3ACCF632997E}.SkipTests|Any CPU.Build.0 = Debug|Any CPU {8B4771F0-8EC2-4761-BBCE-DDE073DB3B7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8B4771F0-8EC2-4761-BBCE-DDE073DB3B7A}.Debug|Any CPU.Build.0 = Debug|Any CPU {8B4771F0-8EC2-4761-BBCE-DDE073DB3B7A}.Release|Any CPU.ActiveCfg = Release|Any CPU From 9f062e38eb6c96cc98513962842b8e8f6f23554a Mon Sep 17 00:00:00 2001 From: Ronald Barendse Date: Thu, 9 Nov 2023 08:42:22 +0100 Subject: [PATCH 27/27] Include automatic relation type aliases from factory and fix SQL parameter overflow (#15141) * Include automatic relation type aliases from factory * Remove unnessecary distinct and fix SQL parameter overflow issue * Fixed assertions and test distinct aliases * Simplified collection assertions --- .../DataValueReferenceFactoryCollection.cs | 109 ++++++++++++++---- .../Implement/ContentRepositoryBase.cs | 46 ++------ ...ataValueReferenceFactoryCollectionTests.cs | 42 ++++++- 3 files changed, 132 insertions(+), 65 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs index 24d6f17eb0..c605d45a9a 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs @@ -8,60 +8,119 @@ public class DataValueReferenceFactoryCollection : BuilderCollectionBase> items) : base(items) - { - } + { } // TODO: We could further reduce circular dependencies with PropertyEditorCollection by not having IDataValueReference implemented // by property editors and instead just use the already built in IDataValueReferenceFactory and/or refactor that into a more normal collection - public IEnumerable GetAllReferences( - IPropertyCollection properties, - PropertyEditorCollection propertyEditors) + public ISet GetAllReferences(IPropertyCollection properties, PropertyEditorCollection propertyEditors) { - var trackedRelations = new HashSet(); + var references = new HashSet(); - foreach (IProperty p in properties) + foreach (IProperty property in properties) { - if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out IDataEditor? editor)) + if (!propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out IDataEditor? dataEditor)) { continue; } // TODO: We will need to change this once we support tracking via variants/segments // for now, we are tracking values from ALL variants - foreach (IPropertyValue propertyVal in p.Values) + foreach (IPropertyValue propertyValue in property.Values) { - var val = propertyVal.EditedValue; + object? value = propertyValue.EditedValue; - IDataValueEditor? valueEditor = editor?.GetValueEditor(); - if (valueEditor is IDataValueReference reference) + if (dataEditor.GetValueEditor() is IDataValueReference dataValueReference) { - IEnumerable refs = reference.GetReferences(val); - foreach (UmbracoEntityReference r in refs) - { - trackedRelations.Add(r); - } + references.UnionWith(dataValueReference.GetReferences(value)); } // Loop over collection that may be add to existing property editors // implementation of GetReferences in IDataValueReference. // Allows developers to add support for references by a // package /property editor that did not implement IDataValueReference themselves - foreach (IDataValueReferenceFactory item in this) + foreach (IDataValueReferenceFactory dataValueReferenceFactory in this) { // Check if this value reference is for this datatype/editor // Then call it's GetReferences method - to see if the value stored - // in the dataeditor/property has referecnes to media/content items - if (item.IsForEditor(editor)) + // in the dataeditor/property has references to media/content items + if (dataValueReferenceFactory.IsForEditor(dataEditor)) { - foreach (UmbracoEntityReference r in item.GetDataValueReference().GetReferences(val)) - { - trackedRelations.Add(r); - } + references.UnionWith(dataValueReferenceFactory.GetDataValueReference().GetReferences(value)); } } } } - return trackedRelations; + return references; + } + + /// + /// Gets all relation type aliases that are automatically tracked. + /// + /// The property editors. + /// + /// All relation type aliases that are automatically tracked. + /// + public ISet GetAutomaticRelationTypesAliases(PropertyEditorCollection propertyEditors) + { + // Always add default automatic relation types + var automaticRelationTypeAliases = new HashSet(Constants.Conventions.RelationTypes.AutomaticRelationTypes); + + // Add relation types for all property editors + foreach (IDataEditor dataEditor in propertyEditors) + { + automaticRelationTypeAliases.UnionWith(GetAutomaticRelationTypesAliases(dataEditor)); + } + + return automaticRelationTypeAliases; + } + + /// + /// Gets the relation type aliases that are automatically tracked for all properties. + /// + /// The properties. + /// The property editors. + /// + /// The relation type aliases that are automatically tracked for all properties. + /// + public ISet GetAutomaticRelationTypesAliases(IPropertyCollection properties, PropertyEditorCollection propertyEditors) + { + // Always add default automatic relation types + var automaticRelationTypeAliases = new HashSet(Constants.Conventions.RelationTypes.AutomaticRelationTypes); + + // Only add relation types that are used in the properties + foreach (IProperty property in properties) + { + if (propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out IDataEditor? dataEditor)) + { + automaticRelationTypeAliases.UnionWith(GetAutomaticRelationTypesAliases(dataEditor)); + } + } + + return automaticRelationTypeAliases; + } + + private IEnumerable GetAutomaticRelationTypesAliases(IDataEditor dataEditor) + { + if (dataEditor.GetValueEditor() is IDataValueReference dataValueReference) + { + // Return custom relation types from value editor implementation + foreach (var alias in dataValueReference.GetAutomaticRelationTypesAliases()) + { + yield return alias; + } + } + + foreach (IDataValueReferenceFactory dataValueReferenceFactory in this) + { + if (dataValueReferenceFactory.IsForEditor(dataEditor)) + { + // Return custom relation types from factory + foreach (var alias in dataValueReferenceFactory.GetDataValueReference().GetAutomaticRelationTypesAliases()) + { + yield return alias; + } + } + } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs index de247a6120..fde1c6ca05 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -1083,13 +1083,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected void PersistRelations(TEntity entity) { // Get all references from our core built in DataEditors/Property Editors - // Along with seeing if deverlopers want to collect additional references from the DataValueReferenceFactories collection - var trackedRelations = new List(); - trackedRelations.AddRange(_dataValueReferenceFactories.GetAllReferences(entity.Properties, PropertyEditors)); - - var relationTypeAliases = GetAutomaticRelationTypesAliases(entity.Properties, PropertyEditors).ToArray(); + // Along with seeing if developers want to collect additional references from the DataValueReferenceFactories collection + var trackedRelations = _dataValueReferenceFactories.GetAllReferences(entity.Properties, PropertyEditors); // First delete all auto-relations for this entity + var relationTypeAliases = _dataValueReferenceFactories.GetAutomaticRelationTypesAliases(entity.Properties, PropertyEditors).ToArray(); RelationRepository.DeleteByParent(entity.Id, relationTypeAliases); if (trackedRelations.Count == 0) @@ -1097,23 +1095,20 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return; } - trackedRelations = trackedRelations.Distinct().ToList(); - var udiToGuids = trackedRelations.Select(x => x.Udi as GuidUdi) - .ToDictionary(x => (Udi)x!, x => x!.Guid); + var udiToGuids = trackedRelations.Select(x => x.Udi as GuidUdi).WhereNotNull().ToDictionary(x => (Udi)x, x => x.Guid); // lookup in the DB all INT ids for the GUIDs and chuck into a dictionary - var keyToIds = Database.Fetch(Sql() + var keyToIds = Database.FetchByGroups(udiToGuids.Values, Constants.Sql.MaxParameterCount, guids => Sql() .Select(x => x.NodeId, x => x.UniqueId) .From() - .WhereIn(x => x.UniqueId, udiToGuids.Values)) + .WhereIn(x => x.UniqueId, guids)) .ToDictionary(x => x.UniqueId, x => x.NodeId); - var allRelationTypes = RelationTypeRepository.GetMany(Array.Empty())? - .ToDictionary(x => x.Alias, x => x); + var allRelationTypes = RelationTypeRepository.GetMany(Array.Empty()).ToDictionary(x => x.Alias, x => x); IEnumerable toSave = trackedRelations.Select(rel => { - if (allRelationTypes is null || !allRelationTypes.TryGetValue(rel.RelationTypeAlias, out IRelationType? relationType)) + if (!allRelationTypes.TryGetValue(rel.RelationTypeAlias, out IRelationType? relationType)) { throw new InvalidOperationException($"The relation type {rel.RelationTypeAlias} does not exist"); } @@ -1135,31 +1130,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement RelationRepository.SaveBulk(toSave); } - private IEnumerable GetAutomaticRelationTypesAliases( - IPropertyCollection properties, - PropertyEditorCollection propertyEditors) - { - var automaticRelationTypesAliases = new HashSet(Constants.Conventions.RelationTypes.AutomaticRelationTypes); - - foreach (IProperty property in properties) - { - if (propertyEditors.TryGet(property.PropertyType.PropertyEditorAlias, out IDataEditor? editor) is false ) - { - continue; - } - - if (editor.GetValueEditor() is IDataValueReference reference) - { - foreach (var alias in reference.GetAutomaticRelationTypesAliases()) - { - automaticRelationTypesAliases.Add(alias); - } - } - } - - return automaticRelationTypesAliases; - } - /// /// Inserts property values for the content entity /// diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs index 6108f59e2e..cff072873f 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Umbraco. // See LICENSE for more details. -using System.Collections.Generic; -using System.Linq; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; @@ -173,6 +171,40 @@ public class DataValueReferenceFactoryCollectionTests Assert.AreEqual(trackedUdi4, result.ElementAt(1).Udi.ToString()); } + [Test] + public void GetAutomaticRelationTypesAliases_ContainsDefault() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty)); + var properties = new PropertyCollection(); + + var resultA = collection.GetAutomaticRelationTypesAliases(propertyEditors).ToArray(); + var resultB = collection.GetAutomaticRelationTypesAliases(properties, propertyEditors).ToArray(); + + var expected = Constants.Conventions.RelationTypes.AutomaticRelationTypes; + CollectionAssert.AreEquivalent(expected, resultA, "Result A does not contain the expected relation type aliases."); + CollectionAssert.AreEquivalent(expected, resultB, "Result B does not contain the expected relation type aliases."); + } + + [Test] + public void GetAutomaticRelationTypesAliases_ContainsCustom() + { + var collection = new DataValueReferenceFactoryCollection(() => new TestDataValueReferenceFactory().Yield()); + + var labelPropertyEditor = new LabelPropertyEditor(DataValueEditorFactory, IOHelper, EditorConfigurationParser); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => labelPropertyEditor.Yield())); + var serializer = new ConfigurationEditorJsonSerializer(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(labelPropertyEditor, serializer))); + var properties = new PropertyCollection { property, property }; // Duplicate on purpose to test distinct aliases + + var resultA = collection.GetAutomaticRelationTypesAliases(propertyEditors).ToArray(); + var resultB = collection.GetAutomaticRelationTypesAliases(properties, propertyEditors).ToArray(); + + var expected = Constants.Conventions.RelationTypes.AutomaticRelationTypes.Append("umbTest"); + CollectionAssert.AreEquivalent(expected, resultA, "Result A does not contain the expected relation type aliases."); + CollectionAssert.AreEquivalent(expected, resultB, "Result B does not contain the expected relation type aliases."); + } + private class TestDataValueReferenceFactory : IDataValueReferenceFactory { public IDataValueReference GetDataValueReference() => new TestMediaDataValueReference(); @@ -196,6 +228,12 @@ public class DataValueReferenceFactoryCollectionTests yield return new UmbracoEntityReference(udi); } } + + public IEnumerable GetAutomaticRelationTypesAliases() => new[] + { + "umbTest", + "umbTest", // Duplicate on purpose to test distinct aliases + }; } } }
Enabled Events Url
- + - {{ vm.webhookEvents[webhook.key] }} - {{ webhook.url }} - - {{ vm.webHooksContentTypes[webhook.key] }} - {{ webhook.url }}{{ vm.webHooksContentTypes[webhook.key] }} - +