diff --git a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs index a0a619b78d..b5f3ecf6fb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ITrackedReferencesRepository.cs @@ -81,13 +81,17 @@ public interface ITrackedReferencesRepository bool filterMustBeIsDependency, out long totalRecords); + /// + /// Gets a paged collection of node keys that have dependant references. + /// + /// The keys to check for relations. + /// The node object Id. + /// The amount of items to skip. + /// The amount of items to take. + /// Task> GetPagedNodeKeysWithDependantReferencesAsync( ISet keys, Guid nodeObjectTypeId, long skip, - long take) - { - IEnumerable pagedItems = GetPagedItemsWithRelations(keys, skip, take, true, out var total); - return Task.FromResult(new PagedModel(total, pagedItems.Select(i => i.NodeKey))); - } + long take); } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs index 8ddf8aae0b..c3ccb06e4f 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TrackedReferencesRepository.cs @@ -9,296 +9,24 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { + /// + /// Implements to provide database access for tracked references." + /// internal class TrackedReferencesRepository : ITrackedReferencesRepository { private readonly IScopeAccessor _scopeAccessor; private readonly IUmbracoMapper _umbracoMapper; + /// + /// Initializes a new instance of the class. + /// public TrackedReferencesRepository(IScopeAccessor scopeAccessor, IUmbracoMapper umbracoMapper) { _scopeAccessor = scopeAccessor; _umbracoMapper = umbracoMapper; } - /// - /// Gets a page of items used in any kind of relation from selected integer ids. - /// - public IEnumerable GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, - bool filterMustBeIsDependency, out long totalRecords) - { - Sql innerUnionSql = GetInnerUnionSql(); - - Sql? sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() - .SelectDistinct( - "[x].[id] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[d].[published] as nodePublished", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") - .From("n") - .InnerJoinNested(innerUnionSql, "x") - .On((n, x) => n.NodeId == x.Id, "n", "x") - .LeftJoin("c") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "c") - .LeftJoin("ct") - .On( - (left, right) => left.ContentTypeId == right.NodeId, - aliasLeft: "c", - aliasRight: "ct") - .LeftJoin("ctn") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "ct", - aliasRight: "ctn") - .LeftJoin("d") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "d"); - - if (ids.Any()) - { - sql = sql?.Where(x => ids.Contains(x.NodeId), "n"); - } - - if (filterMustBeIsDependency) - { - sql = sql?.Where(rt => rt.IsDependency, "x"); - } - - // Ordering is required for paging - sql = sql?.OrderBy(x => x.Alias, "x"); - - Page? pagedResult = _scopeAccessor.AmbientScope?.Database.Page(pageIndex + 1, pageSize, sql); - totalRecords = Convert.ToInt32(pagedResult?.TotalItems); - - return pagedResult?.Items.Select(MapDtoToEntity) ?? Enumerable.Empty(); - } - - private Sql GetInnerUnionSql() - { - if (_scopeAccessor.AmbientScope is null) - { - throw new InvalidOperationException("No Ambient Scope available"); - } - - Sql innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", - "[cn].trashed as [trashed]", - "[cn].nodeObjectType as [nodeObjectType]", - "[pn].uniqueId as otherKey," + - "[cr].childId as id", - "[cr].parentId as otherId", - "[rt].[alias]", - "[rt].[name]", - "[rt].[isDependency]", - "[rt].[dual]") - .From("cr") - .InnerJoin("rt") - .On((cr, rt) => rt.Dual == false && rt.Id == cr.RelationType, "cr", "rt") - .InnerJoin("cn") - .On((cr, cn) => cr.ChildId == cn.NodeId, "cr", "cn") - .InnerJoin("pn") - .On((cr, pn) => cr.ParentId == pn.NodeId, "cr", "pn"); - - Sql innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[pn].uniqueId as [key]", - "[pn].trashed as [trashed]", - "[pn].nodeObjectType as [nodeObjectType]", - "[cn].uniqueId as otherKey," + - "[dpr].parentId as id", - "[dpr].childId as otherId", - "[dprt].[alias]", - "[dprt].[name]", - "[dprt].[isDependency]", - "[dprt].[dual]") - .From("dpr") - .InnerJoin("dprt") - .On( - (dpr, dprt) => dprt.Dual == true && dprt.Id == dpr.RelationType, "dpr", "dprt") - .InnerJoin("cn") - .On((dpr, cn) => dpr.ChildId == cn.NodeId, "dpr", "cn") - .InnerJoin("pn") - .On((dpr, pn) => dpr.ParentId == pn.NodeId, "dpr", "pn"); - - Sql innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( - "[cn].uniqueId as [key]", - "[cn].trashed as [trashed]", - "[cn].nodeObjectType as [nodeObjectType]", - "[pn].uniqueId as otherKey," + - "[dcr].childId as id", - "[dcr].parentId as otherId", - "[dcrt].[alias]", - "[dcrt].[name]", - "[dcrt].[isDependency]", - "[dcrt].[dual]") - .From("dcr") - .InnerJoin("dcrt") - .On( - (dcr, dcrt) => dcrt.Dual == true && dcrt.Id == dcr.RelationType, "dcr", "dcrt") - .InnerJoin("cn") - .On((dcr, cn) => dcr.ChildId == cn.NodeId, "dcr", "cn") - .InnerJoin("pn") - .On((dcr, pn) => dcr.ParentId == pn.NodeId, "dcr", "pn"); - - Sql innerUnionSql = innerUnionSqlChild.Union(innerUnionSqlDualParent).Union(innerUnionSql3); - - return innerUnionSql; - } - - /// - /// Gets a page of the descending items that have any references, given a parent id. - /// - public IEnumerable GetPagedDescendantsInReferences( - int parentId, - long pageIndex, - int pageSize, - bool filterMustBeIsDependency, - out long totalRecords) - { - SqlSyntax.ISqlSyntaxProvider? syntax = _scopeAccessor.AmbientScope?.Database.SqlContext.SqlSyntax; - - // Gets the path of the parent with ",%" added - Sql? subsubQuery = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() - .Select(syntax?.GetConcat("[node].[path]", "',%'")) - .From("node") - .Where(x => x.NodeId == parentId, "node"); - - // Gets the descendants of the parent node - Sql? subQuery = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() - .Select(x => x.NodeId) - .From() - .WhereLike(x => x.Path, subsubQuery); - - Sql innerUnionSql = GetInnerUnionSql(); - - Sql? sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( - "[x].[id] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[d].[published] as nodePublished", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") - .From("n") - .InnerJoinNested(innerUnionSql, "x") - .On((n, x) => n.NodeId == x.Id, "n", "x") - .LeftJoin("c") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "c") - .LeftJoin("ct") - .On( - (left, right) => left.ContentTypeId == right.NodeId, - aliasLeft: "c", - aliasRight: "ct") - .LeftJoin("ctn") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "ct", - aliasRight: "ctn") - .LeftJoin("d") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "d"); - - sql = sql?.WhereIn((System.Linq.Expressions.Expression>)(x => x.NodeId), subQuery, "n"); - - if (filterMustBeIsDependency) - { - sql = sql?.Where(rt => rt.IsDependency, "x"); - } - - // Ordering is required for paging - sql = sql?.OrderBy(x => x.Alias, "x"); - - Page? pagedResult = _scopeAccessor.AmbientScope?.Database.Page(pageIndex + 1, pageSize, sql); - totalRecords = Convert.ToInt32(pagedResult?.TotalItems); - - return pagedResult?.Items.Select(MapDtoToEntity) ?? Enumerable.Empty(); - } - - - /// - /// Gets a page of items which are in relation with the current item. - /// Basically, shows the items which depend on the current item. - /// - public IEnumerable GetPagedRelationsForItem(int id, long pageIndex, int pageSize, - bool filterMustBeIsDependency, out long totalRecords) - { - Sql innerUnionSql = GetInnerUnionSql(); - Sql? sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( - "[x].[otherId] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[d].[published] as nodePublished", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") - .From("n") - .InnerJoinNested(innerUnionSql, "x") - .On((n, x) => n.NodeId == x.OtherId, "n", "x") - .LeftJoin("c") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "c") - .LeftJoin("ct") - .On( - (left, right) => left.ContentTypeId == right.NodeId, - aliasLeft: "c", - aliasRight: "ct") - .LeftJoin("ctn") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "ct", - aliasRight: "ctn") - .LeftJoin("d") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "d") - .Where(x => x.Id == id, "x"); - - if (filterMustBeIsDependency) - { - sql = sql?.Where(rt => rt.IsDependency, "x"); - } - - // Ordering is required for paging - sql = sql?.OrderBy(x => x.Alias, "x"); - - Page? pagedResult = _scopeAccessor.AmbientScope?.Database.Page(pageIndex + 1, pageSize, sql); - totalRecords = Convert.ToInt32(pagedResult?.TotalItems); - - return pagedResult?.Items.Select(MapDtoToEntity) ?? Enumerable.Empty(); - } - + /// public IEnumerable GetPagedRelationsForItem( Guid key, long skip, @@ -312,6 +40,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement filterMustBeIsDependency, out totalRecords); + /// public IEnumerable GetPagedRelationsForRecycleBin( Guid objectTypeKey, long skip, @@ -401,6 +130,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return _umbracoMapper.MapEnumerable(pagedResult); } + /// public IEnumerable GetPagedItemsWithRelations( ISet keys, long skip, @@ -466,6 +196,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return _umbracoMapper.MapEnumerable(pagedResult); } + /// public async Task> GetPagedNodeKeysWithDependantReferencesAsync( ISet keys, Guid nodeObjectTypeId, @@ -508,7 +239,11 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return new PagedModel(totalRecords, result); } - public IEnumerable GetPagedDescendantsInReferences(Guid parentKey, long skip, long take, + /// + public IEnumerable GetPagedDescendantsInReferences( + Guid parentKey, + long skip, + long take, bool filterMustBeIsDependency, out long totalRecords) { @@ -596,89 +331,75 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement return _umbracoMapper.MapEnumerable(pagedResult); } - public IEnumerable GetPagedDescendantsInReferences( - int parentId, - long skip, - long take, - bool filterMustBeIsDependency, - out long totalRecords) + private Sql GetInnerUnionSql() { - SqlSyntax.ISqlSyntaxProvider? syntax = _scopeAccessor.AmbientScope?.Database.SqlContext.SqlSyntax; - - // Gets the path of the parent with ",%" added - Sql? subsubQuery = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() - .Select(syntax?.GetConcat("[node].[path]", "',%'")) - .From("node") - .Where(x => x.NodeId == parentId, "node"); - - // Gets the descendants of the parent node - Sql? subQuery = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql() - .Select(x => x.NodeId) - .From() - .WhereLike(x => x.Path, subsubQuery); - - Sql innerUnionSql = GetInnerUnionSql(); - Sql? sql = _scopeAccessor.AmbientScope?.Database.SqlContext.Sql().SelectDistinct( - "[x].[id] as nodeId", - "[n].[uniqueId] as nodeKey", - "[n].[text] as nodeName", - "[n].[nodeObjectType] as nodeObjectType", - "[ctn].[uniqueId] as contentTypeKey", - "[ct].[icon] as contentTypeIcon", - "[ct].[alias] as contentTypeAlias", - "[ctn].[text] as contentTypeName", - "[x].[alias] as relationTypeAlias", - "[x].[name] as relationTypeName", - "[x].[isDependency] as relationTypeIsDependency", - "[x].[dual] as relationTypeIsBidirectional") - .From("n") - .InnerJoinNested(innerUnionSql, "x") - .On((n, x) => n.NodeId == x.Id, "n", "x") - .LeftJoin("c").On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "n", - aliasRight: "c") - .LeftJoin("ct") - .On( - (left, right) => left.ContentTypeId == right.NodeId, - aliasLeft: "c", - aliasRight: "ct") - .LeftJoin("ctn") - .On( - (left, right) => left.NodeId == right.NodeId, - aliasLeft: "ct", - aliasRight: "ctn"); - sql = sql?.WhereIn( - (System.Linq.Expressions.Expression>)(x => x.NodeId), - subQuery, - "n"); - - if (filterMustBeIsDependency) + if (_scopeAccessor.AmbientScope is null) { - sql = sql?.Where(rt => rt.IsDependency, "x"); + throw new InvalidOperationException("No Ambient Scope available"); } - // find the count before ordering - totalRecords = _scopeAccessor.AmbientScope?.Database.Count(sql!) ?? 0; + Sql innerUnionSqlChild = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( + "[cn].uniqueId as [key]", + "[cn].trashed as [trashed]", + "[cn].nodeObjectType as [nodeObjectType]", + "[pn].uniqueId as otherKey," + + "[cr].childId as id", + "[cr].parentId as otherId", + "[rt].[alias]", + "[rt].[name]", + "[rt].[isDependency]", + "[rt].[dual]") + .From("cr") + .InnerJoin("rt") + .On((cr, rt) => rt.Dual == false && rt.Id == cr.RelationType, "cr", "rt") + .InnerJoin("cn") + .On((cr, cn) => cr.ChildId == cn.NodeId, "cr", "cn") + .InnerJoin("pn") + .On((cr, pn) => cr.ParentId == pn.NodeId, "cr", "pn"); - RelationItemDto[] pagedResult; + Sql innerUnionSqlDualParent = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( + "[pn].uniqueId as [key]", + "[pn].trashed as [trashed]", + "[pn].nodeObjectType as [nodeObjectType]", + "[cn].uniqueId as otherKey," + + "[dpr].parentId as id", + "[dpr].childId as otherId", + "[dprt].[alias]", + "[dprt].[name]", + "[dprt].[isDependency]", + "[dprt].[dual]") + .From("dpr") + .InnerJoin("dprt") + .On( + (dpr, dprt) => dprt.Dual == true && dprt.Id == dpr.RelationType, "dpr", "dprt") + .InnerJoin("cn") + .On((dpr, cn) => dpr.ChildId == cn.NodeId, "dpr", "cn") + .InnerJoin("pn") + .On((dpr, pn) => dpr.ParentId == pn.NodeId, "dpr", "pn"); - //Only to all this, if there is items - if (totalRecords > 0) - { - // Ordering is required for paging - sql = sql?.OrderBy(x => x.Alias, "x"); + Sql innerUnionSql3 = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select( + "[cn].uniqueId as [key]", + "[cn].trashed as [trashed]", + "[cn].nodeObjectType as [nodeObjectType]", + "[pn].uniqueId as otherKey," + + "[dcr].childId as id", + "[dcr].parentId as otherId", + "[dcrt].[alias]", + "[dcrt].[name]", + "[dcrt].[isDependency]", + "[dcrt].[dual]") + .From("dcr") + .InnerJoin("dcrt") + .On( + (dcr, dcrt) => dcrt.Dual == true && dcrt.Id == dcr.RelationType, "dcr", "dcrt") + .InnerJoin("cn") + .On((dcr, cn) => dcr.ChildId == cn.NodeId, "dcr", "cn") + .InnerJoin("pn") + .On((dcr, pn) => dcr.ParentId == pn.NodeId, "dcr", "pn"); - pagedResult = - _scopeAccessor.AmbientScope?.Database.SkipTake(skip, take, sql).ToArray() ?? - Array.Empty(); - } - else - { - pagedResult = Array.Empty(); - } + Sql innerUnionSql = innerUnionSqlChild.Union(innerUnionSqlDualParent).Union(innerUnionSql3); - return _umbracoMapper.MapEnumerable(pagedResult); + return innerUnionSql; } private class UnionHelperDto @@ -704,9 +425,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement [Column("dual")] public bool Dual { get; set; } } - private RelationItem MapDtoToEntity(RelationItemDto dto) - { - return new RelationItem() + private RelationItem MapDtoToEntity(RelationItemDto dto) => + new() { NodeId = dto.ChildNodeId, NodeKey = dto.ChildNodeKey, @@ -721,6 +441,5 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement ContentTypeIcon = dto.ChildContentTypeIcon, ContentTypeName = dto.ChildContentTypeName, }; - } } } diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/entity-action/rollback.action.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/entity-action/rollback.action.ts index a486644add..3b9c94c806 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/entity-action/rollback.action.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/rollback/entity-action/rollback.action.ts @@ -1,6 +1,6 @@ import { UMB_ROLLBACK_MODAL } from '../constants.js'; import { UmbEntityActionBase } from '@umbraco-cms/backoffice/entity-action'; -import { UMB_MODAL_MANAGER_CONTEXT } from '@umbraco-cms/backoffice/modal'; +import { umbOpenModal } from '@umbraco-cms/backoffice/modal'; import { UMB_NOTIFICATION_CONTEXT } from '@umbraco-cms/backoffice/notification'; import { UmbLocalizationController } from '@umbraco-cms/backoffice/localization-api'; @@ -8,14 +8,7 @@ export class UmbRollbackDocumentEntityAction extends UmbEntityActionBase #localize = new UmbLocalizationController(this); override async execute() { - const modalManagerContext = await this.getContext(UMB_MODAL_MANAGER_CONTEXT); - if (!modalManagerContext) return; - - const modalContext = modalManagerContext.open(this, UMB_ROLLBACK_MODAL, {}); - - const data = await modalContext.onSubmit().catch(() => undefined); - if (!data) return; - + await umbOpenModal(this, UMB_ROLLBACK_MODAL, {}); const notificationContext = await this.getContext(UMB_NOTIFICATION_CONTEXT); if (!notificationContext) { throw new Error('Notification context not found');