* Adding TrackedReferencesController * Adding/changing views * Adding/Editing js files * RelationService changes * RelationRepository changes * Adding missing translations * Adding/Modifying tests * Beginning of #9125 migration * Introducing a new component + refactoring based on that * Abstracting + refactoring * Work on content unpublishing * Work on media.delete * Various small changes * Beginning of #9119 migration * Changes on content.delete * Various fixes * Adding new keys used in the listview bulk actions * Adding methods to get the items used in relations from array of ids * Adding the checkLinkedItems function to the trackedReferencesResource * Passing the selected items from a listview to unpublish and delete * Adding umb-tracked-references-listview * Adding umb-tracked-references-listview-table with language column * Fixes for tracked references * Changes in listview unpublish dialog * Changes in listview delete dialog * Removing Variants logic as it is not currently supported * Visual fixes * Closing dialogs on click * Fix wording * Fix breaking changes * Change to a single title "Items in use" instead of 2 different for Content and Media * No need for obsoleting because we can change new controllers * Return ActionResult from actions * V9: Prevent delete or unpublish of items that have references (#12047) * Introducing config settings that prevent delete or unpublish of items referenced by other items * Disable deletion of content items and show a new warning * Disable deletion of media items and show a new warning * Disable deletion of list view items * Disable unpublish and bulk unpublish * Add a new warning * V9: Displaying descendants in use as part of item tracking (#12039) * Replace HasReferencesInDescendants with GetPagedDescendantsInReferences * Display descendants in use on parent's info tab * Add getPagedDescendantsInReferences to trackedReferencesResource * Add lang keys for Descendants in use * Refactoring controller actions * Don't call check descendants usage when it is a new item * rename busfy to busy * always show references * rearrange for scrollbar to appear at the edge of the dialog * use the word referenced instead of used * change fallback texts * Added "IsDependency" to relation types * refactor of umb-tracked-references * rename checkLinkedItems to getPagedReferencedItems * rename check to load, to be consistent with the rest. * Refactored backend . Needs frontend fixes * Cleanup * Use filters * Front-end refactor to match refactored end-points * Fixed bug + warning * Fixed query (more then 2100 descensdants) and optimized it (using nested select instead of inner join). * remove comment * hideNoneDependencies including varying text for the configuration * Hack for SqlCE :( * some final adjustments for item tracking ui * Unbreak change Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com> Co-authored-by: Bjarke Berg <mail@bergmania.dk>
195 lines
11 KiB
C#
195 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using NPoco;
|
|
using Umbraco.Cms.Core.Models;
|
|
using Umbraco.Cms.Core.Persistence;
|
|
using Umbraco.Cms.Core.Persistence.Repositories;
|
|
using Umbraco.Cms.Core.Scoping;
|
|
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
|
using Umbraco.Extensions;
|
|
|
|
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
|
{
|
|
internal class TrackedReferencesRepository : ITrackedReferencesRepository
|
|
{
|
|
private readonly IScopeAccessor _scopeAccessor;
|
|
|
|
public TrackedReferencesRepository(IScopeAccessor scopeAccessor)
|
|
{
|
|
_scopeAccessor = scopeAccessor;
|
|
}
|
|
|
|
public IEnumerable<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize,
|
|
bool filterMustBeIsDependency, out long totalRecords)
|
|
{
|
|
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
|
"[pn].[id] as nodeId",
|
|
"[pn].[uniqueId] as nodeKey",
|
|
"[pn].[text] as nodeName",
|
|
"[pn].[nodeObjectType] as nodeObjectType",
|
|
"[ct].[icon] as contentTypeIcon",
|
|
"[ct].[alias] as contentTypeAlias",
|
|
"[ctn].[text] as contentTypeName",
|
|
"[umbracoRelationType].[alias] as relationTypeAlias",
|
|
"[umbracoRelationType].[name] as relationTypeName",
|
|
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
|
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
|
.From<RelationDto>("r")
|
|
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
|
|
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
|
|
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
|
|
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c")
|
|
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
|
|
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn");
|
|
|
|
if (ids.Any())
|
|
{
|
|
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "pn");
|
|
}
|
|
|
|
if (filterMustBeIsDependency)
|
|
{
|
|
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
|
}
|
|
|
|
// Ordering is required for paging
|
|
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
|
|
|
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
|
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
|
|
|
return pagedResult.Items.Select(MapDtoToEntity);
|
|
}
|
|
|
|
public IEnumerable<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency,
|
|
out long totalRecords)
|
|
{
|
|
var syntax = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax;
|
|
|
|
// Gets the path of the parent with ",%" added
|
|
var subsubQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql()
|
|
.Select(syntax.GetConcat("[node].[path]", "',%'"))
|
|
.From<NodeDto>("node")
|
|
.Where<NodeDto>(x => x.NodeId == parentId, "node");
|
|
|
|
|
|
// Gets the descendants of the parent node
|
|
Sql<ISqlContext> subQuery;
|
|
|
|
if (_scopeAccessor.AmbientScope.Database.DatabaseType.IsSqlCe())
|
|
{
|
|
// SqlCE do not support nested selects that returns a scalar. So we need to do this in multiple queries
|
|
|
|
var pathForLike = _scopeAccessor.AmbientScope.Database.ExecuteScalar<string>(subsubQuery);
|
|
|
|
subQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql()
|
|
.Select<NodeDto>(x => x.NodeId)
|
|
.From<NodeDto>()
|
|
.WhereLike<NodeDto>(x => x.Path, pathForLike);
|
|
}
|
|
else
|
|
{
|
|
subQuery = _scopeAccessor.AmbientScope.Database.SqlContext.Sql()
|
|
.Select<NodeDto>(x => x.NodeId)
|
|
.From<NodeDto>()
|
|
.WhereLike<NodeDto>(x => x.Path, subsubQuery);
|
|
}
|
|
|
|
|
|
|
|
// Get all relations where parent is in the sub query
|
|
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
|
"[pn].[id] as nodeId",
|
|
"[pn].[uniqueId] as nodeKey",
|
|
"[pn].[text] as nodeName",
|
|
"[pn].[nodeObjectType] as nodeObjectType",
|
|
"[ct].[icon] as contentTypeIcon",
|
|
"[ct].[alias] as contentTypeAlias",
|
|
"[ctn].[text] as contentTypeName",
|
|
"[umbracoRelationType].[alias] as relationTypeAlias",
|
|
"[umbracoRelationType].[name] as relationTypeName",
|
|
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
|
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
|
.From<RelationDto>("r")
|
|
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
|
|
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
|
|
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
|
|
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c")
|
|
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
|
|
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn")
|
|
.WhereIn((System.Linq.Expressions.Expression<Func<NodeDto, object>>)(x => x.NodeId), subQuery, "pn");
|
|
if (filterMustBeIsDependency)
|
|
{
|
|
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
|
}
|
|
// Ordering is required for paging
|
|
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
|
|
|
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
|
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
|
|
|
return pagedResult.Items.Select(MapDtoToEntity);
|
|
}
|
|
|
|
public IEnumerable<RelationItem> GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
|
{
|
|
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
|
"[cn].[id] as nodeId",
|
|
"[cn].[uniqueId] as nodeKey",
|
|
"[cn].[text] as nodeName",
|
|
"[cn].[nodeObjectType] as nodeObjectType",
|
|
"[ct].[icon] as contentTypeIcon",
|
|
"[ct].[alias] as contentTypeAlias",
|
|
"[ctn].[text] as contentTypeName",
|
|
"[umbracoRelationType].[alias] as relationTypeAlias",
|
|
"[umbracoRelationType].[name] as relationTypeName",
|
|
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
|
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
|
.From<RelationDto>("r")
|
|
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
|
|
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
|
|
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
|
|
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"cn", aliasRight:"c")
|
|
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
|
|
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn");
|
|
|
|
if (ids.Any())
|
|
{
|
|
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "pn");
|
|
}
|
|
|
|
if (filterMustBeIsDependency)
|
|
{
|
|
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
|
}
|
|
|
|
// Ordering is required for paging
|
|
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
|
|
|
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
|
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
|
|
|
return pagedResult.Items.Select(MapDtoToEntity);
|
|
}
|
|
|
|
private RelationItem MapDtoToEntity(RelationItemDto dto)
|
|
{
|
|
var type = ObjectTypes.GetUdiType(dto.ChildNodeObjectType);
|
|
return new RelationItem()
|
|
{
|
|
NodeId = dto.ChildNodeId,
|
|
NodeKey = dto.ChildNodeKey,
|
|
NodeType = ObjectTypes.GetUdiType(dto.ChildNodeObjectType),
|
|
NodeName = dto.ChildNodeName,
|
|
RelationTypeName = dto.RelationTypeName,
|
|
RelationTypeIsBidirectional = dto.RelationTypeIsBidirectional,
|
|
RelationTypeIsDependency = dto.RelationTypeIsDependency,
|
|
ContentTypeAlias = dto.ChildContentTypeAlias,
|
|
ContentTypeIcon = dto.ChildContentTypeIcon,
|
|
ContentTypeName = dto.ChildContentTypeName,
|
|
};
|
|
}
|
|
}
|
|
}
|