Only prevent the unpublish or delete of a related item when configured to do so if it is related as a child, not as a parent (#18886)
* Only prevent the unpubkish or delete of a related item when configured to do so if it is related as a child, not as a parent. * Fixed incorect parameter names. * Fixed failing integration tests. * Use using variable instead to reduce nesting * Applied suggestions from code review. * Used simple using statement throughout RelationService for consistency. * Applied XML header comments consistently. --------- Co-authored-by: mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
12
src/Umbraco.Core/Models/RelationDirectionFilter.cs
Normal file
12
src/Umbraco.Core/Models/RelationDirectionFilter.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Cms.Core.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Definition of relation directions used as a filter when requesting if a given item has relations.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum RelationDirectionFilter
|
||||
{
|
||||
Parent = 1,
|
||||
Child = 2,
|
||||
Any = Parent | Child
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -198,7 +198,7 @@ internal abstract class ContentEditingServiceBase<TContent, TContentType, TConte
|
||||
return await Task.FromResult(Attempt.FailWithStatus<TContent?, ContentEditingOperationStatus>(status, content));
|
||||
}
|
||||
|
||||
if (disabledWhenReferenced && _relationService.IsRelated(content.Id))
|
||||
if (disabledWhenReferenced && _relationService.IsRelated(content.Id, RelationDirectionFilter.Child))
|
||||
{
|
||||
return Attempt.FailWithStatus<TContent?, ContentEditingOperationStatus>(referenceFailStatus, content);
|
||||
}
|
||||
|
||||
@@ -311,7 +311,7 @@ internal sealed class ContentPublishingService : IContentPublishingService
|
||||
return Attempt.Fail(ContentPublishingOperationStatus.ContentNotFound);
|
||||
}
|
||||
|
||||
if (_contentSettings.DisableUnpublishWhenReferenced && _relationService.IsRelated(content.Id))
|
||||
if (_contentSettings.DisableUnpublishWhenReferenced && _relationService.IsRelated(content.Id, RelationDirectionFilter.Child))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt<ContentPublishingOperationStatus>.Fail(ContentPublishingOperationStatus.CannotUnpublishWhenReferenced);
|
||||
|
||||
@@ -2767,7 +2767,7 @@ public class ContentService : RepositoryService, IContentService
|
||||
{
|
||||
foreach (IContent content in contents)
|
||||
{
|
||||
if (_contentSettings.DisableDeleteWhenReferenced && _relationService.IsRelated(content.Id))
|
||||
if (_contentSettings.DisableDeleteWhenReferenced && _relationService.IsRelated(content.Id, RelationDirectionFilter.Child))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -297,8 +297,20 @@ public interface IRelationService : IService
|
||||
/// </summary>
|
||||
/// <param name="id">Id of an object to check relations for</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists with the given Id, otherwise <c>False</c></returns>
|
||||
[Obsolete("Please use the overload taking a RelationDirectionFilter parameter. Scheduled for removal in Umbraco 17.")]
|
||||
bool IsRelated(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether any relations exists for the passed in Id and direction.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of an object to check relations for</param>
|
||||
/// <param name="directionFilter">Indicates whether to check for relations as parent, child or in either direction.</param>
|
||||
/// <returns>Returns <c>True</c> if any relations exists with the given Id, otherwise <c>False</c></returns>
|
||||
bool IsRelated(int id, RelationDirectionFilter directionFilter)
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
=> IsRelated(id);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether two items are related
|
||||
/// </summary>
|
||||
|
||||
@@ -63,28 +63,22 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IRelation? GetById(int id)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationRepository.Get(id);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationRepository.Get(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRelationType? GetRelationTypeById(int id)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationTypeRepository.Get(id);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationTypeRepository.Get(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRelationType? GetRelationTypeById(Guid id)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationTypeRepository.Get(id);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationTypeRepository.Get(id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -93,10 +87,8 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetAllRelations(params int[] ids)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationRepository.GetMany(ids);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationRepository.GetMany(ids);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -106,20 +98,16 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetAllRelationsByRelationType(int relationTypeId)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelationType> GetAllRelationTypes(params int[] ids)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationTypeRepository.GetMany(ids);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationTypeRepository.GetMany(ids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -144,10 +132,8 @@ public class RelationService : RepositoryService, IRelationService
|
||||
.Take(take));
|
||||
}
|
||||
|
||||
public int CountRelationTypes()
|
||||
{
|
||||
return _relationTypeRepository.Count(null);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public int CountRelationTypes() => _relationTypeRepository.Count(null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentId(int id) => GetByParentId(id, null);
|
||||
@@ -155,24 +141,22 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentId(int id, string? relationTypeAlias)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
if (relationTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (relationTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
IQuery<IRelation> qry1 = Query<IRelation>().Where(x => x.ParentId == id);
|
||||
return _relationRepository.Get(qry1);
|
||||
}
|
||||
|
||||
IRelationType? relationType = GetRelationType(relationTypeAlias!);
|
||||
if (relationType == null)
|
||||
{
|
||||
return Enumerable.Empty<IRelation>();
|
||||
}
|
||||
|
||||
IQuery<IRelation> qry2 =
|
||||
Query<IRelation>().Where(x => x.ParentId == id && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(qry2);
|
||||
IQuery<IRelation> qry1 = Query<IRelation>().Where(x => x.ParentId == id);
|
||||
return _relationRepository.Get(qry1);
|
||||
}
|
||||
|
||||
IRelationType? relationType = GetRelationType(relationTypeAlias!);
|
||||
if (relationType == null)
|
||||
{
|
||||
return Enumerable.Empty<IRelation>();
|
||||
}
|
||||
|
||||
IQuery<IRelation> qry2 =
|
||||
Query<IRelation>().Where(x => x.ParentId == id && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(qry2);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -188,24 +172,22 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByChildId(int id, string? relationTypeAlias)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
if (relationTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
if (relationTypeAlias.IsNullOrWhiteSpace())
|
||||
{
|
||||
IQuery<IRelation> qry1 = Query<IRelation>().Where(x => x.ChildId == id);
|
||||
return _relationRepository.Get(qry1);
|
||||
}
|
||||
|
||||
IRelationType? relationType = GetRelationType(relationTypeAlias!);
|
||||
if (relationType == null)
|
||||
{
|
||||
return Enumerable.Empty<IRelation>();
|
||||
}
|
||||
|
||||
IQuery<IRelation> qry2 =
|
||||
Query<IRelation>().Where(x => x.ChildId == id && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(qry2);
|
||||
IQuery<IRelation> qry1 = Query<IRelation>().Where(x => x.ChildId == id);
|
||||
return _relationRepository.Get(qry1);
|
||||
}
|
||||
|
||||
IRelationType? relationType = GetRelationType(relationTypeAlias!);
|
||||
if (relationType == null)
|
||||
{
|
||||
return Enumerable.Empty<IRelation>();
|
||||
}
|
||||
|
||||
IQuery<IRelation> qry2 =
|
||||
Query<IRelation>().Where(x => x.ChildId == id && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(qry2);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -218,39 +200,34 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentOrChildId(int id)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ChildId == id || x.ParentId == id);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ChildId == id || x.ParentId == id);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByParentOrChildId(int id, string relationTypeAlias)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IRelationType? relationType = GetRelationType(relationTypeAlias);
|
||||
if (relationType == null)
|
||||
{
|
||||
IRelationType? relationType = GetRelationType(relationTypeAlias);
|
||||
if (relationType == null)
|
||||
{
|
||||
return Enumerable.Empty<IRelation>();
|
||||
}
|
||||
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x =>
|
||||
(x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query);
|
||||
return Enumerable.Empty<IRelation>();
|
||||
}
|
||||
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x =>
|
||||
(x.ChildId == id || x.ParentId == id) && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRelation? GetByParentAndChildId(int parentId, int childId, IRelationType relationType)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ParentId == parentId &&
|
||||
x.ChildId == childId &&
|
||||
x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query).FirstOrDefault();
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ParentId == parentId &&
|
||||
x.ChildId == childId &&
|
||||
x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -283,47 +260,41 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetByRelationTypeId(int relationTypeId)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.Get(query);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IRelation> GetPagedByRelationTypeId(int relationTypeId, long pageIndex, int pageSize, out long totalRecords, Ordering? ordering = null)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation>? query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.GetPagedRelationsByQuery(query, pageIndex, pageSize, out totalRecords, ordering);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation>? query = Query<IRelation>().Where(x => x.RelationTypeId == relationTypeId);
|
||||
return _relationRepository.GetPagedRelationsByQuery(query, pageIndex, pageSize, out totalRecords, ordering);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<PagedModel<IRelation>> GetPagedByChildKeyAsync(Guid childKey, int skip, int take, string? relationTypeAlias)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return await _relationRepository.GetPagedByChildKeyAsync(childKey, skip, take, relationTypeAlias);
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return await _relationRepository.GetPagedByChildKeyAsync(childKey, skip, take, relationTypeAlias);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKeyAsync(Guid key, int skip, int take, Ordering? ordering = null)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IRelationType? relationType = _relationTypeRepository.Get(key);
|
||||
if (relationType is null)
|
||||
{
|
||||
IRelationType? relationType = _relationTypeRepository.Get(key);
|
||||
if (relationType is null)
|
||||
{
|
||||
return await Task.FromResult(Attempt.FailWithStatus<PagedModel<IRelation>, RelationOperationStatus>(RelationOperationStatus.RelationTypeNotFound, null!));
|
||||
}
|
||||
|
||||
PaginationHelper.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize);
|
||||
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
IEnumerable<IRelation> relations = _relationRepository.GetPagedRelationsByQuery(query, pageNumber, pageSize, out var totalRecords, ordering);
|
||||
return await Task.FromResult(Attempt.SucceedWithStatus(RelationOperationStatus.Success, new PagedModel<IRelation>(totalRecords, relations)));
|
||||
return await Task.FromResult(Attempt.FailWithStatus<PagedModel<IRelation>, RelationOperationStatus>(RelationOperationStatus.RelationTypeNotFound, null!));
|
||||
}
|
||||
|
||||
PaginationHelper.ConvertSkipTakeToPaging(skip, take, out var pageNumber, out var pageSize);
|
||||
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
IEnumerable<IRelation> relations = _relationRepository.GetPagedRelationsByQuery(query, pageNumber, pageSize, out var totalRecords, ordering);
|
||||
return await Task.FromResult(Attempt.SucceedWithStatus(RelationOperationStatus.Success, new PagedModel<IRelation>(totalRecords, relations)));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -394,19 +365,15 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationRepository.GetPagedParentEntitiesByChildId(id, pageIndex, pageSize, out totalChildren, entityTypes.Select(x => x.GetGuid()).ToArray());
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationRepository.GetPagedParentEntitiesByChildId(id, pageIndex, pageSize, out totalChildren, entityTypes.Select(x => x.GetGuid()).ToArray());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int id, long pageIndex, int pageSize, out long totalChildren, params UmbracoObjectTypes[] entityTypes)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
return _relationRepository.GetPagedChildEntitiesByParentId(id, pageIndex, pageSize, out totalChildren, entityTypes.Select(x => x.GetGuid()).ToArray());
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
return _relationRepository.GetPagedChildEntitiesByParentId(id, pageIndex, pageSize, out totalChildren, entityTypes.Select(x => x.GetGuid()).ToArray());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -440,22 +407,20 @@ public class RelationService : RepositoryService, IRelationService
|
||||
// TODO: We don't check if this exists first, it will throw some sort of data integrity exception if it already exists, is that ok?
|
||||
var relation = new Relation(parentId, childId, relationType);
|
||||
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationSavingNotification(relation, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationSavingNotification(relation, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return relation; // TODO: returning sth that does not exist here?!
|
||||
}
|
||||
|
||||
_relationRepository.Save(relation);
|
||||
scope.Notifications.Publish(
|
||||
new RelationSavedNotification(relation, eventMessages).WithStateFrom(savingNotification));
|
||||
scope.Complete();
|
||||
return relation;
|
||||
return relation; // TODO: returning sth that does not exist here?!
|
||||
}
|
||||
|
||||
_relationRepository.Save(relation);
|
||||
scope.Notifications.Publish(
|
||||
new RelationSavedNotification(relation, eventMessages).WithStateFrom(savingNotification));
|
||||
scope.Complete();
|
||||
return relation;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -491,31 +456,37 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public bool HasRelations(IRelationType relationType)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsRelated(int id)
|
||||
public bool IsRelated(int id) => IsRelated(id, RelationDirectionFilter.Any);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsRelated(int id, RelationDirectionFilter directionFilter)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>();
|
||||
|
||||
query = directionFilter switch
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ParentId == id || x.ChildId == id);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
RelationDirectionFilter.Parent => query.Where(x => x.ParentId == id),
|
||||
RelationDirectionFilter.Child => query.Where(x => x.ChildId == id),
|
||||
RelationDirectionFilter.Any => query.Where(x => x.ParentId == id || x.ChildId == id),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(directionFilter)),
|
||||
};
|
||||
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(int parentId, int childId)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ParentId == parentId && x.ChildId == childId);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.ParentId == parentId && x.ChildId == childId);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -540,65 +511,61 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public void Save(IRelation relation)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationSavingNotification(relation, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationSavingNotification(relation, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Save(relation);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationSavedNotification(relation, eventMessages).WithStateFrom(savingNotification));
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Save(relation);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationSavedNotification(relation, eventMessages).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save(IEnumerable<IRelation> relations)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
IRelation[] relationsA = relations.ToArray();
|
||||
|
||||
EventMessages messages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationSavingNotification(relationsA, messages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
IRelation[] relationsA = relations.ToArray();
|
||||
|
||||
EventMessages messages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationSavingNotification(relationsA, messages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Save(relationsA);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationSavedNotification(relationsA, messages).WithStateFrom(savingNotification));
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Save(relationsA);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationSavedNotification(relationsA, messages).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save(IRelationType relationType)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationTypeSavingNotification(relationType, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var savingNotification = new RelationTypeSavingNotification(relationType, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_relationTypeRepository.Save(relationType);
|
||||
Audit(AuditType.Save, Constants.Security.SuperUserId, relationType.Id, $"Saved relation type: {relationType.Name}");
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationTypeSavedNotification(relationType, eventMessages).WithStateFrom(savingNotification));
|
||||
return;
|
||||
}
|
||||
|
||||
_relationTypeRepository.Save(relationType);
|
||||
Audit(AuditType.Save, Constants.Security.SuperUserId, relationType.Id, $"Saved relation type: {relationType.Name}");
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationTypeSavedNotification(relationType, eventMessages).WithStateFrom(savingNotification));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IRelationType, RelationTypeOperationStatus>> CreateAsync(IRelationType relationType, Guid userKey)
|
||||
{
|
||||
if (relationType.Id != 0)
|
||||
@@ -614,6 +581,7 @@ public class RelationService : RepositoryService, IRelationService
|
||||
userKey);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IRelationType, RelationTypeOperationStatus>> UpdateAsync(IRelationType relationType, Guid userKey) =>
|
||||
await SaveAsync(
|
||||
relationType,
|
||||
@@ -669,105 +637,97 @@ public class RelationService : RepositoryService, IRelationService
|
||||
/// <inheritdoc />
|
||||
public void Delete(IRelation relation)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new RelationDeletingNotification(relation, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new RelationDeletingNotification(relation, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Delete(relation);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationDeletedNotification(relation, eventMessages).WithStateFrom(deletingNotification));
|
||||
return;
|
||||
}
|
||||
|
||||
_relationRepository.Delete(relation);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationDeletedNotification(relation, eventMessages).WithStateFrom(deletingNotification));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Delete(IRelationType relationType)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new RelationTypeDeletingNotification(relationType, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new RelationTypeDeletingNotification(relationType, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
}
|
||||
|
||||
_relationTypeRepository.Delete(relationType);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationTypeDeletedNotification(relationType, eventMessages).WithStateFrom(deletingNotification));
|
||||
return;
|
||||
}
|
||||
|
||||
_relationTypeRepository.Delete(relationType);
|
||||
scope.Complete();
|
||||
scope.Notifications.Publish(
|
||||
new RelationTypeDeletedNotification(relationType, eventMessages).WithStateFrom(deletingNotification));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Attempt<IRelationType?, RelationTypeOperationStatus>> DeleteAsync(Guid key, Guid userKey)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
IRelationType? relationType = _relationTypeRepository.Get(key);
|
||||
if (relationType is null)
|
||||
{
|
||||
IRelationType? relationType = _relationTypeRepository.Get(key);
|
||||
if (relationType is null)
|
||||
{
|
||||
return Attempt.FailWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.NotFound, null);
|
||||
}
|
||||
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new RelationTypeDeletingNotification(relationType, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.CancelledByNotification, null);
|
||||
}
|
||||
|
||||
_relationTypeRepository.Delete(relationType);
|
||||
var currentUser = await _userIdKeyResolver.GetAsync(userKey);
|
||||
Audit(AuditType.Delete, currentUser, relationType.Id, "Deleted relation type");
|
||||
scope.Notifications.Publish(new RelationTypeDeletedNotification(relationType, eventMessages).WithStateFrom(deletingNotification));
|
||||
scope.Complete();
|
||||
return await Task.FromResult(Attempt.SucceedWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.Success, relationType));
|
||||
return Attempt.FailWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.NotFound, null);
|
||||
}
|
||||
|
||||
EventMessages eventMessages = EventMessagesFactory.Get();
|
||||
var deletingNotification = new RelationTypeDeletingNotification(relationType, eventMessages);
|
||||
if (scope.Notifications.PublishCancelable(deletingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return Attempt.FailWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.CancelledByNotification, null);
|
||||
}
|
||||
|
||||
_relationTypeRepository.Delete(relationType);
|
||||
var currentUser = await _userIdKeyResolver.GetAsync(userKey);
|
||||
Audit(AuditType.Delete, currentUser, relationType.Id, "Deleted relation type");
|
||||
scope.Notifications.Publish(new RelationTypeDeletedNotification(relationType, eventMessages).WithStateFrom(deletingNotification));
|
||||
scope.Complete();
|
||||
return await Task.FromResult(Attempt.SucceedWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.Success, relationType));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DeleteRelationsOfType(IRelationType relationType)
|
||||
{
|
||||
var relations = new List<IRelation>();
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
var allRelations = _relationRepository.Get(query).ToList();
|
||||
relations.AddRange(allRelations);
|
||||
|
||||
// TODO: N+1, we should be able to do this in a single call
|
||||
foreach (IRelation relation in relations)
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == relationType.Id);
|
||||
var allRelations = _relationRepository.Get(query).ToList();
|
||||
relations.AddRange(allRelations);
|
||||
|
||||
// TODO: N+1, we should be able to do this in a single call
|
||||
foreach (IRelation relation in relations)
|
||||
{
|
||||
_relationRepository.Delete(relation);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(new RelationDeletedNotification(relations, EventMessagesFactory.Get()));
|
||||
_relationRepository.Delete(relation);
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
|
||||
scope.Notifications.Publish(new RelationDeletedNotification(relations, EventMessagesFactory.Get()));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AreRelated(int parentId, int childId, IRelationType relationType)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x =>
|
||||
x.ParentId == parentId && x.ChildId == childId && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelation> query = Query<IRelation>().Where(x =>
|
||||
x.ParentId == parentId && x.ChildId == childId && x.RelationTypeId == relationType.Id);
|
||||
return _relationRepository.Get(query).Any();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<UmbracoObjectTypes> GetAllowedObjectTypes() =>
|
||||
new[]
|
||||
{
|
||||
[
|
||||
UmbracoObjectTypes.Document,
|
||||
UmbracoObjectTypes.Media,
|
||||
UmbracoObjectTypes.Member,
|
||||
@@ -778,17 +738,15 @@ public class RelationService : RepositoryService, IRelationService
|
||||
UmbracoObjectTypes.MemberGroup,
|
||||
UmbracoObjectTypes.ROOT,
|
||||
UmbracoObjectTypes.RecycleBin,
|
||||
};
|
||||
];
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private IRelationType? GetRelationType(string relationTypeAlias)
|
||||
{
|
||||
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
||||
{
|
||||
IQuery<IRelationType> query = Query<IRelationType>().Where(x => x.Alias == relationTypeAlias);
|
||||
return _relationTypeRepository.Get(query).FirstOrDefault();
|
||||
}
|
||||
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
||||
IQuery<IRelationType> query = Query<IRelationType>().Where(x => x.Alias == relationTypeAlias);
|
||||
return _relationTypeRepository.Get(query).FirstOrDefault();
|
||||
}
|
||||
|
||||
private IEnumerable<IRelation> GetRelationsByListOfTypeIds(IEnumerable<int> relationTypeIds)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Tests.Integration.Attributes;
|
||||
@@ -12,29 +11,20 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
public partial class ContentEditingServiceTests
|
||||
{
|
||||
protected IRelationService RelationService => GetRequiredService<IRelationService>();
|
||||
|
||||
public static void ConfigureDisableDeleteWhenReferenced(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<ContentSettings>(config =>
|
||||
=> builder.Services.Configure<ContentSettings>(config =>
|
||||
config.DisableDeleteWhenReferenced = true);
|
||||
}
|
||||
|
||||
public void Relate(IContent child, IContent parent)
|
||||
{
|
||||
var relatedContentRelType = RelationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelatedDocumentAlias);
|
||||
|
||||
var relation = RelationService.Relate(child.Id, parent.Id, relatedContentRelType);
|
||||
RelationService.Save(relation);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableDeleteWhenReferenced))]
|
||||
public async Task Cannot_Delete_Referenced_Content()
|
||||
public async Task Cannot_Delete_When_Content_Is_Related_As_A_Child_And_Configured_To_Disable_When_Related()
|
||||
{
|
||||
var moveAttempt = await ContentEditingService.MoveToRecycleBinAsync(Subpage.Key, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(moveAttempt.Success);
|
||||
|
||||
Relate(Subpage, Subpage2);
|
||||
// Setup a relation where the page being deleted is related to another page as a child (e.g. the other page has a picker and has selected this page).
|
||||
Relate(Subpage2, Subpage);
|
||||
var result = await ContentEditingService.DeleteFromRecycleBinAsync(Subpage.Key, Constants.Security.SuperUserKey);
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(ContentEditingOperationStatus.CannotDeleteWhenReferenced, result.Status);
|
||||
@@ -44,6 +34,24 @@ public partial class ContentEditingServiceTests
|
||||
Assert.IsNotNull(subpage);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableDeleteWhenReferenced))]
|
||||
public async Task Can_Delete_When_Content_Is_Related_As_A_Parent_And_Configured_To_Disable_When_Related()
|
||||
{
|
||||
var moveAttempt = await ContentEditingService.MoveToRecycleBinAsync(Subpage.Key, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(moveAttempt.Success);
|
||||
|
||||
// Setup a relation where the page being deleted is related to another page as a child (e.g. the other page has a picker and has selected this page).
|
||||
Relate(Subpage, Subpage2);
|
||||
var result = await ContentEditingService.DeleteFromRecycleBinAsync(Subpage.Key, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(ContentEditingOperationStatus.Success, result.Status);
|
||||
|
||||
// re-get and verify deleted
|
||||
var subpage = await ContentEditingService.GetAsync(Subpage.Key);
|
||||
Assert.IsNull(subpage);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
public async Task Can_Delete_FromRecycleBin(bool variant)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -9,12 +9,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
|
||||
public partial class ContentEditingServiceTests
|
||||
{
|
||||
|
||||
public static void ConfigureDisableDelete(IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<ContentSettings>(config =>
|
||||
public static new void ConfigureDisableUnpublishWhenReferencedTrue(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<ContentSettings>(config =>
|
||||
config.DisableUnpublishWhenReferenced = true);
|
||||
}
|
||||
|
||||
[TestCase(true)]
|
||||
[TestCase(false)]
|
||||
@@ -33,10 +30,11 @@ public partial class ContentEditingServiceTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableDelete))]
|
||||
public async Task Cannot_Move_To_Recycle_Bin_If_Referenced()
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableUnpublishWhenReferencedTrue))]
|
||||
public async Task Cannot_Move_To_Recycle_Bin_When_Content_Is_Related_As_A_Child_And_Configured_To_Disable_When_Related()
|
||||
{
|
||||
Relate(Subpage, Subpage2);
|
||||
// Setup a relation where the page being deleted is related to another page as a child (e.g. the other page has a picker and has selected this page).
|
||||
Relate(Subpage2, Subpage);
|
||||
var moveAttempt = await ContentEditingService.MoveToRecycleBinAsync(Subpage.Key, Constants.Security.SuperUserKey);
|
||||
Assert.IsFalse(moveAttempt.Success);
|
||||
Assert.AreEqual(ContentEditingOperationStatus.CannotMoveToRecycleBinWhenReferenced, moveAttempt.Status);
|
||||
@@ -47,6 +45,22 @@ public partial class ContentEditingServiceTests
|
||||
Assert.IsFalse(content.Trashed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableUnpublishWhenReferencedTrue))]
|
||||
public async Task Can_Move_To_Recycle_Bin_When_Content_Is_Related_As_A_Parent_And_Configured_To_Disable_When_Related()
|
||||
{
|
||||
// Setup a relation where the page being deleted is related to another page as a child (e.g. the other page has a picker and has selected this page).
|
||||
Relate(Subpage, Subpage2);
|
||||
var moveAttempt = await ContentEditingService.MoveToRecycleBinAsync(Subpage.Key, Constants.Security.SuperUserKey);
|
||||
Assert.IsTrue(moveAttempt.Success);
|
||||
Assert.AreEqual(ContentEditingOperationStatus.Success, moveAttempt.Status);
|
||||
|
||||
// re-get and verify moved
|
||||
var content = await ContentEditingService.GetAsync(Subpage.Key);
|
||||
Assert.IsNotNull(content);
|
||||
Assert.IsTrue(content.Trashed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Cannot_Move_Non_Existing_To_Recycle_Bin()
|
||||
{
|
||||
|
||||
@@ -16,6 +16,14 @@ public partial class ContentEditingServiceTests : ContentEditingServiceTestsBase
|
||||
[SetUp]
|
||||
public void Setup() => ContentRepositoryBase.ThrowOnWarning = true;
|
||||
|
||||
public void Relate(IContent parent, IContent child)
|
||||
{
|
||||
var relatedContentRelType = RelationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelatedDocumentAlias);
|
||||
|
||||
var relation = RelationService.Relate(parent.Id, child.Id, relatedContentRelType);
|
||||
RelationService.Save(relation);
|
||||
}
|
||||
|
||||
protected override void CustomTestSetup(IUmbracoBuilder builder)
|
||||
=> builder.AddNotificationHandler<ContentCopiedNotification, RelateOnCopyNotificationHandler>();
|
||||
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.OperationStatus;
|
||||
using Umbraco.Cms.Tests.Common.Builders;
|
||||
using Umbraco.Cms.Tests.Common.Builders.Extensions;
|
||||
using Umbraco.Cms.Tests.Integration.Attributes;
|
||||
|
||||
namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services;
|
||||
|
||||
public partial class ContentPublishingServiceTests
|
||||
{
|
||||
public static new void ConfigureDisableUnpublishWhenReferencedTrue(IUmbracoBuilder builder)
|
||||
=> builder.Services.Configure<ContentSettings>(config =>
|
||||
config.DisableUnpublishWhenReferenced = true);
|
||||
|
||||
[Test]
|
||||
public async Task Can_Unpublish_Root()
|
||||
{
|
||||
@@ -92,6 +100,40 @@ public partial class ContentPublishingServiceTests
|
||||
VerifyIsPublished(Textpage.Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableUnpublishWhenReferencedTrue))]
|
||||
public async Task Cannot_Unpublish_When_Content_Is_Related_As_A_Child_And_Configured_To_Disable_When_Related()
|
||||
{
|
||||
await ContentPublishingService.PublishAsync(Textpage.Key, MakeModel(_allCultures), Constants.Security.SuperUserKey);
|
||||
VerifyIsPublished(Textpage.Key);
|
||||
|
||||
// Setup a relation where the page being unpublished is related to another page as a child (e.g. the other page has a picker and has selected this page).
|
||||
RelationService.Relate(Subpage, Textpage, Constants.Conventions.RelationTypes.RelatedDocumentAlias);
|
||||
|
||||
var result = await ContentPublishingService.UnpublishAsync(Textpage.Key, null, Constants.Security.SuperUserKey);
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
Assert.AreEqual(ContentPublishingOperationStatus.CannotUnpublishWhenReferenced, result.Result);
|
||||
VerifyIsPublished(Textpage.Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ConfigureBuilder(ActionName = nameof(ConfigureDisableUnpublishWhenReferencedTrue))]
|
||||
public async Task Can_Unpublish_When_Content_Is_Related_As_A_Parent_And_Configured_To_Disable_When_Related()
|
||||
{
|
||||
await ContentPublishingService.PublishAsync(Textpage.Key, MakeModel(_allCultures), Constants.Security.SuperUserKey);
|
||||
VerifyIsPublished(Textpage.Key);
|
||||
|
||||
// Setup a relation where the page being unpublished is related to another page as a parent (e.g. this page has a picker and has selected the other page).
|
||||
RelationService.Relate(Textpage, Subpage, Constants.Conventions.RelationTypes.RelatedDocumentAlias);
|
||||
|
||||
var result = await ContentPublishingService.UnpublishAsync(Textpage.Key, null, Constants.Security.SuperUserKey);
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
Assert.AreEqual(ContentPublishingOperationStatus.Success, result.Result);
|
||||
VerifyIsNotPublished(Textpage.Key);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Can_Unpublish_Single_Culture()
|
||||
{
|
||||
@@ -229,8 +271,6 @@ public partial class ContentPublishingServiceTests
|
||||
Assert.AreEqual(0, content.PublishedCultures.Count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task Can_Unpublish_Non_Mandatory_Cultures()
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -20,6 +20,8 @@ public partial class ContentPublishingServiceTests : UmbracoIntegrationTestWithC
|
||||
{
|
||||
private IContentPublishingService ContentPublishingService => GetRequiredService<IContentPublishingService>();
|
||||
|
||||
private IRelationService RelationService => GetRequiredService<IRelationService>();
|
||||
|
||||
private static readonly ISet<string> _allCultures = new HashSet<string>(){ "*" };
|
||||
|
||||
private static CultureAndScheduleModel MakeModel(ISet<string> cultures) => new CultureAndScheduleModel()
|
||||
|
||||
Reference in New Issue
Block a user