From 380fdb075644a985b1ee49f01ff66e36ae1fdbc5 Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Thu, 4 Sep 2025 14:29:32 +0200 Subject: [PATCH 01/14] Avoid throwing an exception on getting references when migrating content with changed data types. --- .../BlockEditorPropertyValueEditor.cs | 11 +++++--- .../PropertyEditors/BlockEditorValues.cs | 25 +++++++++++++++++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs index e986905aa77f..ae452305ac04 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs @@ -49,8 +49,11 @@ protected BlockEditorValues BlockEditorValues /// public override IEnumerable GetReferences(object? value) { - // Group by property editor alias to avoid duplicate lookups and optimize value parsing - foreach (var valuesByPropertyEditorAlias in GetAllPropertyValues(value).GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value)) + // Group by property editor alias to avoid duplicate lookups and optimize value parsing. + // We don't throw on error here because we want to be able to parse what we can, even if some of the data is invalid. In cases where migrating + // from nested content to blocks, we don't want to trigger a fatal error for retrieving references, as this isn't vital to the operation. + // See: https://github.com/umbraco/Umbraco-CMS/issues/19784 and Umbraco support cases. + foreach (IGrouping valuesByPropertyEditorAlias in GetAllPropertyValues(value, throwOnError: false).GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value)) { if (!_propertyEditors.TryGet(valuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor)) { @@ -84,11 +87,11 @@ public override IEnumerable GetTags(object? value, object? dataTypeConfigu } } - private IEnumerable GetAllPropertyValues(object? value) + private IEnumerable GetAllPropertyValues(object? value, bool throwOnError = true) { var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString(); - BlockEditorData? blockEditorData = BlockEditorValues.DeserializeAndClean(rawJson); + BlockEditorData? blockEditorData = BlockEditorValues.DeserializeAndClean(rawJson, throwOnError); if (blockEditorData is null) { yield break; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs index 98dbb8588930..df416af3016b 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using Microsoft.Extensions.Logging; +using Newtonsoft.Json; using Umbraco.Cms.Core.Cache.PropertyEditors; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; @@ -25,7 +26,7 @@ public BlockEditorValues(BlockEditorDataConverter dataConverter, IBlockEditorEle _logger = logger; } - public BlockEditorData? DeserializeAndClean(object? propertyValue) + public BlockEditorData? DeserializeAndClean(object? propertyValue, bool throwOnError = true) { var propertyValueAsString = propertyValue?.ToString(); if (string.IsNullOrWhiteSpace(propertyValueAsString)) @@ -33,7 +34,27 @@ public BlockEditorValues(BlockEditorDataConverter dataConverter, IBlockEditorEle return null; } - BlockEditorData blockEditorData = _dataConverter.Deserialize(propertyValueAsString); + BlockEditorData blockEditorData; + try + { + blockEditorData = _dataConverter.Deserialize(propertyValueAsString); + } + catch (JsonSerializationException ex) + { + if (throwOnError) + { + throw; + } + else + { + _logger.LogWarning( + "Could not deserialize the provided property value into a block editor value: {PropertyValue}. Error: {ErrorMessage}.", + propertyValueAsString, + ex.Message); + return null; + } + } + return Clean(blockEditorData); } From 8f9e0365c9e7074f3d65040ccf5c676d3e2f9e1c Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 16 Sep 2025 10:58:03 +0200 Subject: [PATCH 02/14] Revert and handle exception at the consumer side --- .../BlockEditorPropertyValueEditor.cs | 52 +++++++++---------- .../PropertyEditors/BlockEditorValues.cs | 24 +-------- 2 files changed, 26 insertions(+), 50 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs index ae452305ac04..3a9d85ec51d5 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs @@ -50,10 +50,7 @@ protected BlockEditorValues BlockEditorValues public override IEnumerable GetReferences(object? value) { // Group by property editor alias to avoid duplicate lookups and optimize value parsing. - // We don't throw on error here because we want to be able to parse what we can, even if some of the data is invalid. In cases where migrating - // from nested content to blocks, we don't want to trigger a fatal error for retrieving references, as this isn't vital to the operation. - // See: https://github.com/umbraco/Umbraco-CMS/issues/19784 and Umbraco support cases. - foreach (IGrouping valuesByPropertyEditorAlias in GetAllPropertyValues(value, throwOnError: false).GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value)) + foreach (var valuesByPropertyEditorAlias in GetAllPropertyValues(value).GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value)) { if (!_propertyEditors.TryGet(valuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor)) { @@ -87,11 +84,11 @@ public override IEnumerable GetTags(object? value, object? dataTypeConfigu } } - private IEnumerable GetAllPropertyValues(object? value, bool throwOnError = true) + private IEnumerable GetAllPropertyValues(object? value) { var rawJson = value == null ? string.Empty : value is string str ? str : value.ToString(); - BlockEditorData? blockEditorData = BlockEditorValues.DeserializeAndClean(rawJson, throwOnError); + BlockEditorData? blockEditorData = SafeParseBlockEditorData(rawJson); if (blockEditorData is null) { yield break; @@ -118,17 +115,7 @@ public override object ToEditor(IProperty property, string? culture = null, stri { var val = property.GetValue(culture, segment); - BlockEditorData? blockEditorData; - try - { - blockEditorData = BlockEditorValues.DeserializeAndClean(val); - } - catch (JsonSerializationException) - { - // if this occurs it means the data is invalid, shouldn't happen but has happened if we change the data format. - return string.Empty; - } - + BlockEditorData? blockEditorData = SafeParseBlockEditorData(val); if (blockEditorData == null) { return string.Empty; @@ -153,17 +140,7 @@ public override object ToEditor(IProperty property, string? culture = null, stri return null; } - BlockEditorData? blockEditorData; - try - { - blockEditorData = BlockEditorValues.DeserializeAndClean(editorValue.Value); - } - catch (JsonSerializationException) - { - // if this occurs it means the data is invalid, shouldn't happen but has happened if we change the data format. - return string.Empty; - } - + BlockEditorData? blockEditorData = SafeParseBlockEditorData(editorValue.Value); if (blockEditorData == null || blockEditorData.BlockValue.ContentData.Count == 0) { return string.Empty; @@ -174,4 +151,23 @@ public override object ToEditor(IProperty property, string? culture = null, stri // return json return JsonConvert.SerializeObject(blockEditorData.BlockValue, Formatting.None); } + + // We don't throw on error here because we want to be able to parse what we can, even if some of the data is invalid. In cases where migrating + // from nested content to blocks, we don't want to trigger a fatal error for retrieving references, as this isn't vital to the operation. + // See: https://github.com/umbraco/Umbraco-CMS/issues/19784 and Umbraco support cases. + private BlockEditorData? SafeParseBlockEditorData(object? value) + { + try + { + return BlockEditorValues.DeserializeAndClean(value); + } + catch (JsonSerializationException ex) + { + _logger.LogWarning( + "Could not deserialize the provided property value into a block editor value: {PropertyValue}. Error: {ErrorMessage}.", + value, + ex.Message); + return null; + } + } } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs index df416af3016b..7a0691be3739 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs @@ -26,7 +26,7 @@ public BlockEditorValues(BlockEditorDataConverter dataConverter, IBlockEditorEle _logger = logger; } - public BlockEditorData? DeserializeAndClean(object? propertyValue, bool throwOnError = true) + public BlockEditorData? DeserializeAndClean(object? propertyValue) { var propertyValueAsString = propertyValue?.ToString(); if (string.IsNullOrWhiteSpace(propertyValueAsString)) @@ -34,27 +34,7 @@ public BlockEditorValues(BlockEditorDataConverter dataConverter, IBlockEditorEle return null; } - BlockEditorData blockEditorData; - try - { - blockEditorData = _dataConverter.Deserialize(propertyValueAsString); - } - catch (JsonSerializationException ex) - { - if (throwOnError) - { - throw; - } - else - { - _logger.LogWarning( - "Could not deserialize the provided property value into a block editor value: {PropertyValue}. Error: {ErrorMessage}.", - propertyValueAsString, - ex.Message); - return null; - } - } - + BlockEditorData blockEditorData = _dataConverter.Deserialize(propertyValueAsString); return Clean(blockEditorData); } From b8d9b4b1488399edae794daa94cac9b82ca138f2 Mon Sep 17 00:00:00 2001 From: kjac Date: Tue, 16 Sep 2025 10:59:03 +0200 Subject: [PATCH 03/14] Clean up --- .../PropertyEditors/BlockEditorPropertyValueEditor.cs | 2 +- src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs index 3a9d85ec51d5..af75e5626fc9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyValueEditor.cs @@ -49,7 +49,7 @@ protected BlockEditorValues BlockEditorValues /// public override IEnumerable GetReferences(object? value) { - // Group by property editor alias to avoid duplicate lookups and optimize value parsing. + // Group by property editor alias to avoid duplicate lookups and optimize value parsing foreach (var valuesByPropertyEditorAlias in GetAllPropertyValues(value).GroupBy(x => x.PropertyType.PropertyEditorAlias, x => x.Value)) { if (!_propertyEditors.TryGet(valuesByPropertyEditorAlias.Key, out IDataEditor? dataEditor)) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs index 7a0691be3739..98dbb8588930 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorValues.cs @@ -2,7 +2,6 @@ // See LICENSE for more details. using Microsoft.Extensions.Logging; -using Newtonsoft.Json; using Umbraco.Cms.Core.Cache.PropertyEditors; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Blocks; From 4d867345eb5a827bb0d51527fbac076faf5b2d95 Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Tue, 16 Sep 2025 12:10:29 +0200 Subject: [PATCH 04/14] Fix issue 12364 fix bug for Media Picker is slow when you have a large number of images at the root folder #12364 --- src/Umbraco.Web.BackOffice/Controllers/EntityController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index f4ef16e041ff..3d391b3f0cdd 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -732,7 +732,8 @@ public IEnumerable GetChildren(int id, UmbracoEntityTypes type, Gui // else proceed as usual - return _entityService.GetChildren(id, objectType.Value) + return _entityService.GetPagedChildren(id, objectType.Value, 0, 100, out _, null, + Ordering.By("VersionDate", Direction.Descending)) .Select(_umbracoMapper.Map) .WhereNotNull(); } From eae26d79360bc398c7d37a4df49550ad8f09afb3 Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Fri, 19 Sep 2025 15:10:44 +0200 Subject: [PATCH 05/14] Revert "Fix issue 12364" This reverts commit 4d867345eb5a827bb0d51527fbac076faf5b2d95. --- src/Umbraco.Web.BackOffice/Controllers/EntityController.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index 3d391b3f0cdd..f4ef16e041ff 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -732,8 +732,7 @@ public IEnumerable GetChildren(int id, UmbracoEntityTypes type, Gui // else proceed as usual - return _entityService.GetPagedChildren(id, objectType.Value, 0, 100, out _, null, - Ordering.By("VersionDate", Direction.Descending)) + return _entityService.GetChildren(id, objectType.Value) .Select(_umbracoMapper.Map) .WhereNotNull(); } From dae8d8c8fd477f18a56166c5edf5f3d7cf03399d Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Fri, 19 Sep 2025 15:30:51 +0200 Subject: [PATCH 06/14] Media Picker: use getPagedChildren with paging & sorting Replaces folder view calls to entityResource.getChildren with entityResource.getPagedChildren in MediaPickerController. - Adds orderBy/Direction and increases default pageSize to 200 - Resets pagination when entering folders or clearing search - Updates changePagination to work for both search and folder views - Keeps legacy behaviour for searchMedia (no breaking server changes) --- .../mediapicker/mediapicker.controller.js | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 08ecf390fe49..4eb2123e2854 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -88,7 +88,9 @@ angular.module("umbraco") totalItems: 0, totalPages: 0, filter: '', - dataTypeKey: dataTypeKey + dataTypeKey: dataTypeKey, + orderBy: "VersionDate", // NEW + orderDirection: "Descending" // NEW }; vm.layout = { layouts: [{ name: "Grid", icon: "icon-thumbnails-small", path: "gridpath", selected: true }, @@ -272,7 +274,12 @@ angular.module("umbraco") performGotoFolder(folder); } - return getChildren(folder.id); + // --- MOD: reset pagination when entering a folder --- + vm.searchOptions.pageNumber = 1; + vm.searchOptions.totalItems = 0; + vm.searchOptions.totalPages = 0; + + return getChildren(folder.id); } function performGotoFolder(folder) { @@ -484,8 +491,14 @@ angular.module("umbraco") function changePagination(pageNumber) { vm.loading = true; vm.searchOptions.pageNumber = pageNumber; - searchMedia(); - }; + + if (vm.searchOptions.filter) { + // paginazione della ricerca (già presente) + searchMedia(); + } else { + // paginazione nella vista cartella + getChildren($scope.currentFolder.id); + } function searchMedia() { vm.loading = true; @@ -559,20 +572,34 @@ angular.module("umbraco") function getChildren(id) { vm.loading = true; - return entityResource.getChildren(id, "Media", vm.searchOptions).then(function (data) { + + return entityResource.getPagedChildren(id, "Media", vm.searchOptions).then(function (data) { var allowedTypes = dialogOptions.filter ? dialogOptions.filter.split(",") : null; - for (var i = 0; i < data.length; i++) { - setDefaultData(data[i]); - data[i].filtered = allowedTypes && allowedTypes.indexOf(data[i].metaData.ContentTypeAlias) < 0; + var items = (data && data.items) ? data.items : []; + for (var i = 0; i < items.length; i++) { + setDefaultData(items[i]); + items[i].filtered = allowedTypes && allowedTypes.indexOf(items[i].metaData.ContentTypeAlias) < 0; } + // update images (page) + $scope.images = items; + + // update pagination (mirror di searchMedia) + if (data.pageNumber > 0) vm.searchOptions.pageNumber = data.pageNumber; + if (data.pageSize > 0) vm.searchOptions.pageSize = data.pageSize; + + vm.searchOptions.totalItems = data.totalItems || 0; + vm.searchOptions.totalPages = data.totalPages || 0; + + // reset filtro come prima vm.searchOptions.filter = ""; - $scope.images = data ? data : []; - // set already selected medias to selected + // set già selezionati preSelectMedia(); + + }).finally(function () { vm.loading = false; }); } From 5fce87d4228e37d80e2040aa6a34674fe9914e55 Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Fri, 19 Sep 2025 15:44:08 +0200 Subject: [PATCH 07/14] Update mediapicker.controller.js --- .../mediapicker/mediapicker.controller.js | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 4eb2123e2854..109c97dd75f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -572,36 +572,38 @@ angular.module("umbraco") function getChildren(id) { vm.loading = true; + return entityResource + .getPagedChildren(id, "Media", vm.searchOptions) + .then(handlePagedChildren) + .finally(function () { vm.loading = false; }); + } - return entityResource.getPagedChildren(id, "Media", vm.searchOptions).then(function (data) { + // --- helpers to reduce cyclomatic complexity --- + function handlePagedChildren(data) { + var items = transformItems(data && data.items ? data.items : [], getAllowedTypes()); + $scope.images = items; + syncPagination(vm.searchOptions, data); + vm.searchOptions.filter = ""; // keep legacy behaviour + preSelectMedia(); + } - var allowedTypes = dialogOptions.filter ? dialogOptions.filter.split(",") : null; + function getAllowedTypes() { + return dialogOptions.filter ? dialogOptions.filter.split(",") : null; + } - var items = (data && data.items) ? data.items : []; - for (var i = 0; i < items.length; i++) { + function transformItems(items, allowedTypes) { + for (var i = 0; i < items.length; i++) { setDefaultData(items[i]); - items[i].filtered = allowedTypes && allowedTypes.indexOf(items[i].metaData.ContentTypeAlias) < 0; - } - - // update images (page) - $scope.images = items; - - // update pagination (mirror di searchMedia) - if (data.pageNumber > 0) vm.searchOptions.pageNumber = data.pageNumber; - if (data.pageSize > 0) vm.searchOptions.pageSize = data.pageSize; - - vm.searchOptions.totalItems = data.totalItems || 0; - vm.searchOptions.totalPages = data.totalPages || 0; - - // reset filtro come prima - vm.searchOptions.filter = ""; - - // set già selezionati - preSelectMedia(); + items[i].filtered = !!(allowedTypes && allowedTypes.indexOf(items[i].metaData.ContentTypeAlias) < 0); + } + return items; + } - }).finally(function () { - vm.loading = false; - }); + function syncPagination(opts, data) { + if (data && data.pageNumber > 0) { opts.pageNumber = data.pageNumber; } + if (data && data.pageSize > 0) { opts.pageSize = data.pageSize; } + opts.totalItems = (data && data.totalItems) ? data.totalItems : 0; + opts.totalPages = (data && data.totalPages) ? data.totalPages : 0; } function setDefaultData(item) { From c0add5e4bd60d721a7fd53b2358bc235661b0f5b Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Fri, 19 Sep 2025 15:48:13 +0200 Subject: [PATCH 08/14] Update mediapicker.controller.js --- .../mediapicker/mediapicker.controller.js | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 109c97dd75f3..29c1fe5f794f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -599,13 +599,23 @@ angular.module("umbraco") return items; } - function syncPagination(opts, data) { - if (data && data.pageNumber > 0) { opts.pageNumber = data.pageNumber; } - if (data && data.pageSize > 0) { opts.pageSize = data.pageSize; } - opts.totalItems = (data && data.totalItems) ? data.totalItems : 0; - opts.totalPages = (data && data.totalPages) ? data.totalPages : 0; + // Helpers to avoid conditionals in syncPagination (lower cyclomatic complexity) + function pickPositive(value, fallback) { + return (typeof value === "number" && value > 0) ? value : fallback; + } + + function pickNonNegative(value, fallback) { + return (typeof value === "number" && value >= 0) ? value : fallback; } + function syncPagination(opts, data) { + var d = data || {}; + opts.pageNumber = pickPositive(d.pageNumber, opts.pageNumber); + opts.pageSize = pickPositive(d.pageSize, opts.pageSize); + opts.totalItems = pickNonNegative(d.totalItems, 0); + opts.totalPages = pickNonNegative(d.totalPages, 0); + } + function setDefaultData(item) { if (item.metaData.MediaPath !== null) { item.thumbnail = mediaHelper.resolveFileFromEntity(item, true); From c5ca264fae4b03b65ecde4cad30d3123b92b9cd7 Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Fri, 19 Sep 2025 15:57:21 +0200 Subject: [PATCH 09/14] Update mediapicker.controller.js --- .../common/infiniteeditors/mediapicker/mediapicker.controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 29c1fe5f794f..6eee8d3601af 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -498,6 +498,7 @@ angular.module("umbraco") } else { // paginazione nella vista cartella getChildren($scope.currentFolder.id); + } } function searchMedia() { From 039b38341f39333e33ce5b8f3b472d9b9e8c35c7 Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Fri, 19 Sep 2025 19:43:10 +0200 Subject: [PATCH 10/14] Update mediapicker.controller.js --- .../infiniteeditors/mediapicker/mediapicker.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 6eee8d3601af..f911c7e9e914 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -493,10 +493,10 @@ angular.module("umbraco") vm.searchOptions.pageNumber = pageNumber; if (vm.searchOptions.filter) { - // paginazione della ricerca (già presente) + // search pagination (already present) searchMedia(); } else { - // paginazione nella vista cartella + // pagination in folder view getChildren($scope.currentFolder.id); } } From efb9fd7a26e3387d7ec1a7866de65693acdb91dc Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Wed, 24 Sep 2025 20:34:21 +0200 Subject: [PATCH 11/14] Fix default values for paging. --- .../src/common/resources/entity.resource.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index fdcc4946327f..e3ab51ad5800 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -514,10 +514,9 @@ function entityResource($q, $http, umbRequestHelper) { * */ getPagedChildren: function (parentId, type, options) { - var defaults = { - pageSize: 1, - pageNumber: 100, + pageSize: 100, + pageNumber: 1, filter: '', orderDirection: "Ascending", orderBy: "SortOrder", From 6a8abe0254460b1598866dac3ae1de56517543bf Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Thu, 25 Sep 2025 09:53:12 +0200 Subject: [PATCH 12/14] Refactor Media Picker controller: simplify pagination and clarify comments - Removed unnecessary helper methods (pickPositive / pickNonNegative) and assign pagination values directly, as backend always provides valid positive/ non-negative numbers. - Renamed or removed review-only comments to keep codebase clean. - Clarified purpose of resetting `vm.searchOptions.filter` to explain why the filter is cleared after loading items. - Adjusted indentation and minor formatting for consistency. --- .../mediapicker/mediapicker.controller.js | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index f911c7e9e914..400b981ffa82 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -88,9 +88,7 @@ angular.module("umbraco") totalItems: 0, totalPages: 0, filter: '', - dataTypeKey: dataTypeKey, - orderBy: "VersionDate", // NEW - orderDirection: "Descending" // NEW + dataTypeKey: dataTypeKey }; vm.layout = { layouts: [{ name: "Grid", icon: "icon-thumbnails-small", path: "gridpath", selected: true }, @@ -274,7 +272,7 @@ angular.module("umbraco") performGotoFolder(folder); } - // --- MOD: reset pagination when entering a folder --- + // Reset pagination to first page whenever a new folder is opened vm.searchOptions.pageNumber = 1; vm.searchOptions.totalItems = 0; vm.searchOptions.totalPages = 0; @@ -574,17 +572,16 @@ angular.module("umbraco") function getChildren(id) { vm.loading = true; return entityResource - .getPagedChildren(id, "Media", vm.searchOptions) + .getPagedChildren(id, "Media", vm.searchOptions) .then(handlePagedChildren) .finally(function () { vm.loading = false; }); } - // --- helpers to reduce cyclomatic complexity --- function handlePagedChildren(data) { var items = transformItems(data && data.items ? data.items : [], getAllowedTypes()); $scope.images = items; syncPagination(vm.searchOptions, data); - vm.searchOptions.filter = ""; // keep legacy behaviour + vm.searchOptions.filter = ""; // reset filter to ensure folder navigation always starts unfiltered preSelectMedia(); } @@ -600,21 +597,12 @@ angular.module("umbraco") return items; } - // Helpers to avoid conditionals in syncPagination (lower cyclomatic complexity) - function pickPositive(value, fallback) { - return (typeof value === "number" && value > 0) ? value : fallback; - } - - function pickNonNegative(value, fallback) { - return (typeof value === "number" && value >= 0) ? value : fallback; - } - function syncPagination(opts, data) { var d = data || {}; - opts.pageNumber = pickPositive(d.pageNumber, opts.pageNumber); - opts.pageSize = pickPositive(d.pageSize, opts.pageSize); - opts.totalItems = pickNonNegative(d.totalItems, 0); - opts.totalPages = pickNonNegative(d.totalPages, 0); + opts.pageNumber = d.pageNumber; + opts.pageSize = d.pageSize; + opts.totalItems = d.totalItems; + opts.totalPages = d.totalPages; } function setDefaultData(item) { From 10c1d8127cae08695f9fcb46f3a824b29b862b7e Mon Sep 17 00:00:00 2001 From: LandLogic IT Date: Thu, 25 Sep 2025 17:42:18 +0200 Subject: [PATCH 13/14] Media Picker: fix lost paging on reopen by chaining navigation promises MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures the Media Picker always loads a paged result when restoring the last visited folder. - run(), ensureWithinStartNode(), gotoStartNode(), and gotoFolder() now return and chain the same promise flow. - gotoFolder() resolves path → sets current folder → resets pagination → calls getChildren() (which uses getPagedChildren). - Fixed cases where reopening the picker showed all items with no pager. - Kept existing UX: filter is cleared on folder navigation to start unfiltered. - Minor indentation/formatting cleanups. --- .../mediapicker/mediapicker.controller.js | 80 +++++++++---------- 1 file changed, 38 insertions(+), 42 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 400b981ffa82..0f3981b0d88a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -130,17 +130,13 @@ angular.module("umbraco") } } - function onInit() { - - localizationService.localizeMany(["defaultdialogs_selectMedia", "mediaPicker_tabClipboard"]) .then(function (localizationResult) { setTitle(localizationResult); setNavigation(localizationResult); }); - userService.getCurrentUser().then(function (userData) { userStartNodes = userData.startMediaIds; @@ -148,10 +144,10 @@ angular.module("umbraco") entityResource.getById($scope.startNodeId, "media") .then(function (ent) { $scope.startNodeId = ent.id; - run(); + return run(); }); } else { - run(); + return run(); } }); } @@ -160,10 +156,11 @@ angular.module("umbraco") //default root item if (!$scope.target) { if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) { - entityResource.getById($scope.lastOpenedNode, "media") - .then(ensureWithinStartNode, gotoStartNode); + return entityResource.getById($scope.lastOpenedNode, "media") + .then(ensureWithinStartNode) + .catch(function () { return gotoStartNode(); }); } else { - gotoStartNode(); + return gotoStartNode(); } } else { // if a target is specified, go look it up - generally this target will just contain ids not the actual full @@ -176,11 +173,11 @@ angular.module("umbraco") // ID of a UDI or legacy int ID still could be null/undefinied here // As user may dragged in an image that has not been saved to media section yet if (id) { - entityResource.getById(id, "Media") + return entityResource.getById(id, "Media") .then(function (node) { $scope.target = node; - // Moving directly to existing node's folder - gotoFolder({ id: node.parentId }).then(function () { + // Move directly to existing node's folder, then open details + return gotoFolder({ id: node.parentId }).then(function () { selectMedia(node); $scope.target.url = mediaHelper.resolveFileFromEntity(node); $scope.target.thumbnail = mediaHelper.resolveFileFromEntity(node, true); @@ -195,6 +192,7 @@ angular.module("umbraco") // No ID set - then this is going to be a tmpimg that has not been uploaded // User editing this will want to be changing the ALT text openDetailsDialog(); + return; } } } @@ -257,27 +255,27 @@ angular.module("umbraco") folder = { id: -1, name: "Media", icon: "icon-folder" }; } - if (folder.id > 0) { - entityResource.getAncestors(folder.id, "media", null, { dataTypeKey: dataTypeKey }) - .then(function (anc) { - $scope.path = _.filter(anc, - function (f) { - return f.path.indexOf($scope.startNodeId) !== -1; - }); - folder.path = $scope.path[0].path; - performGotoFolder(folder); - }); - } else { - $scope.path = []; - performGotoFolder(folder); - } - - // Reset pagination to first page whenever a new folder is opened - vm.searchOptions.pageNumber = 1; - vm.searchOptions.totalItems = 0; - vm.searchOptions.totalPages = 0; + var setPathPromise = (folder.id > 0) + ? entityResource.getAncestors(folder.id, "media", null, { dataTypeKey: dataTypeKey }) + .then(function (anc) { + $scope.path = _.filter(anc, function (f) { + return f.path.indexOf($scope.startNodeId) !== -1; + }); + folder.path = $scope.path[0].path; + }) + : Promise.resolve().then(function () { + $scope.path = []; + }); - return getChildren(folder.id); + // Chain: resolve path → set current folder → reset paging → fetch page + return setPathPromise.then(function () { + performGotoFolder(folder); + // Reset pagination to the first page on folder change + vm.searchOptions.pageNumber = 1; + vm.searchOptions.totalItems = 0; + vm.searchOptions.totalPages = 0; + return getChildren(folder.id); + }); } function performGotoFolder(folder) { @@ -393,11 +391,9 @@ angular.module("umbraco") // also make sure the node is not trashed if (nodePath.indexOf($scope.startNodeId.toString()) !== -1 && node.trashed === false) { - gotoFolder({ id: $scope.lastOpenedNode || $scope.startNodeId, name: "Media", icon: "icon-folder", path: node.path }); - return true; + return gotoFolder({ id: $scope.lastOpenedNode || $scope.startNodeId, name: "Media", icon: "icon-folder", path: node.path }); } else { - gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); - return false; + return gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); } } @@ -413,7 +409,7 @@ angular.module("umbraco") } function gotoStartNode() { - gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); + return gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); } function openDetailsDialog() { @@ -572,9 +568,9 @@ angular.module("umbraco") function getChildren(id) { vm.loading = true; return entityResource - .getPagedChildren(id, "Media", vm.searchOptions) - .then(handlePagedChildren) - .finally(function () { vm.loading = false; }); + .getPagedChildren(id, "Media", vm.searchOptions) + .then(handlePagedChildren) + .finally(function () { vm.loading = false; }); } function handlePagedChildren(data) { @@ -592,7 +588,7 @@ angular.module("umbraco") function transformItems(items, allowedTypes) { for (var i = 0; i < items.length; i++) { setDefaultData(items[i]); - items[i].filtered = !!(allowedTypes && allowedTypes.indexOf(items[i].metaData.ContentTypeAlias) < 0); + items[i].filtered = !!(allowedTypes && allowedTypes.indexOf(items[i].metaData.ContentTypeAlias) < 0); } return items; } @@ -603,7 +599,7 @@ angular.module("umbraco") opts.pageSize = d.pageSize; opts.totalItems = d.totalItems; opts.totalPages = d.totalPages; - } + } function setDefaultData(item) { if (item.metaData.MediaPath !== null) { From 7cd75680a18c6db1b7fca195a97c4aea76365bce Mon Sep 17 00:00:00 2001 From: Andy Butland Date: Fri, 26 Sep 2025 08:10:17 +0200 Subject: [PATCH 14/14] Tidied up indentation. --- .../infiniteeditors/mediapicker/mediapicker.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 0f3981b0d88a..cf7e7289a1ad 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -569,8 +569,8 @@ angular.module("umbraco") vm.loading = true; return entityResource .getPagedChildren(id, "Media", vm.searchOptions) - .then(handlePagedChildren) - .finally(function () { vm.loading = false; }); + .then(handlePagedChildren) + .finally(function () { vm.loading = false; }); } function handlePagedChildren(data) {