751 lines
31 KiB
C#
751 lines
31 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Umbraco.Cms.Core.Events;
|
|
using Umbraco.Cms.Core.Models;
|
|
using Umbraco.Cms.Core.Models.Entities;
|
|
using Umbraco.Cms.Core.Notifications;
|
|
using Umbraco.Cms.Core.Persistence.Querying;
|
|
using Umbraco.Cms.Core.Persistence.Repositories;
|
|
using Umbraco.Cms.Core.Scoping;
|
|
using Umbraco.Cms.Core.Services.OperationStatus;
|
|
using Umbraco.Extensions;
|
|
|
|
namespace Umbraco.Cms.Core.Services;
|
|
|
|
public class RelationService : RepositoryService, IRelationService
|
|
{
|
|
private readonly IAuditRepository _auditRepository;
|
|
private readonly IUserIdKeyResolver _userIdKeyResolver;
|
|
private readonly IEntityService _entityService;
|
|
private readonly IRelationRepository _relationRepository;
|
|
private readonly IRelationTypeRepository _relationTypeRepository;
|
|
|
|
public RelationService(
|
|
ICoreScopeProvider uowProvider,
|
|
ILoggerFactory loggerFactory,
|
|
IEventMessagesFactory eventMessagesFactory,
|
|
IEntityService entityService,
|
|
IRelationRepository relationRepository,
|
|
IRelationTypeRepository relationTypeRepository,
|
|
IAuditRepository auditRepository,
|
|
IUserIdKeyResolver userIdKeyResolver)
|
|
: base(uowProvider, loggerFactory, eventMessagesFactory)
|
|
{
|
|
_relationRepository = relationRepository;
|
|
_relationTypeRepository = relationTypeRepository;
|
|
_auditRepository = auditRepository;
|
|
_userIdKeyResolver = userIdKeyResolver;
|
|
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IRelation? GetById(int 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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IRelationType? GetRelationTypeById(Guid id)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
return _relationTypeRepository.Get(id);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IRelationType? GetRelationTypeByAlias(string alias) => GetRelationType(alias);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetAllRelations(params int[] ids)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
return _relationRepository.GetMany(ids);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetAllRelationsByRelationType(IRelationType relationType) =>
|
|
GetAllRelationsByRelationType(relationType.Id);
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelationType> GetAllRelationTypes(params int[] ids)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
return _relationTypeRepository.GetMany(ids);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the Relation types in a paged manner.
|
|
/// Currently implements the paging in memory on the name property because the underlying repository does not support paging yet
|
|
/// </summary>
|
|
public Task<PagedModel<IRelationType>> GetPagedRelationTypesAsync(int skip, int take, params int[] ids)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
|
|
if (take == 0)
|
|
{
|
|
return Task.FromResult(new PagedModel<IRelationType>(CountRelationTypes(), Enumerable.Empty<IRelationType>()));
|
|
}
|
|
|
|
IRelationType[] items = _relationTypeRepository.GetMany(ids).ToArray();
|
|
|
|
return Task.FromResult(new PagedModel<IRelationType>(
|
|
items.Length,
|
|
items.OrderBy(relationType => relationType.Name)
|
|
.Skip(skip)
|
|
.Take(take)));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public int CountRelationTypes() => _relationTypeRepository.Count(null);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByParentId(int id) => GetByParentId(id, null);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByParentId(int id, string? relationTypeAlias)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByParent(IUmbracoEntity parent) => GetByParentId(parent.Id);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByParent(IUmbracoEntity parent, string relationTypeAlias) =>
|
|
GetByParentId(parent.Id, relationTypeAlias);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByChildId(int id) => GetByChildId(id, null);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByChildId(int id, string? relationTypeAlias)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByChild(IUmbracoEntity child) => GetByChildId(child.Id);
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByChild(IUmbracoEntity child, string relationTypeAlias) =>
|
|
GetByChildId(child.Id, relationTypeAlias);
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByParentOrChildId(int id, string relationTypeAlias)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
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);
|
|
}
|
|
|
|
/// <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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByRelationTypeName(string relationTypeName)
|
|
{
|
|
List<int>? relationTypeIds;
|
|
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
|
{
|
|
// This is a silly query - but i guess it's needed in case someone has more than one relation with the same Name (not alias), odd.
|
|
IQuery<IRelationType> query = Query<IRelationType>().Where(x => x.Name == relationTypeName);
|
|
IEnumerable<IRelationType> relationTypes = _relationTypeRepository.Get(query);
|
|
relationTypeIds = relationTypes.Select(x => x.Id).ToList();
|
|
}
|
|
|
|
return relationTypeIds.Count == 0
|
|
? []
|
|
: GetRelationsByListOfTypeIds(relationTypeIds);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IRelation> GetByRelationTypeAlias(string relationTypeAlias)
|
|
{
|
|
IRelationType? relationType = GetRelationType(relationTypeAlias);
|
|
|
|
return relationType == null
|
|
? []
|
|
: GetRelationsByListOfTypeIds([relationType.Id]);
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task<Attempt<PagedModel<IRelation>, RelationOperationStatus>> GetPagedByRelationTypeKeyAsync(Guid key, int skip, int take, Ordering? ordering = null)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
IRelationType? relationType = _relationTypeRepository.Get(key);
|
|
if (relationType is null)
|
|
{
|
|
return 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 Task.FromResult(Attempt.SucceedWithStatus(RelationOperationStatus.Success, new PagedModel<IRelation>(totalRecords, relations)));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IUmbracoEntity? GetChildEntityFromRelation(IRelation relation)
|
|
{
|
|
UmbracoObjectTypes objectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType);
|
|
return _entityService.Get(relation.ChildId, objectType);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IUmbracoEntity? GetParentEntityFromRelation(IRelation relation)
|
|
{
|
|
UmbracoObjectTypes objectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType);
|
|
return _entityService.Get(relation.ParentId, objectType);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Tuple<IUmbracoEntity, IUmbracoEntity>? GetEntitiesFromRelation(IRelation relation)
|
|
{
|
|
UmbracoObjectTypes childObjectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType);
|
|
UmbracoObjectTypes parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType);
|
|
|
|
IEntitySlim? child = _entityService.Get(relation.ChildId, childObjectType);
|
|
IEntitySlim? parent = _entityService.Get(relation.ParentId, parentObjectType);
|
|
|
|
if (parent is null || child is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return new Tuple<IUmbracoEntity, IUmbracoEntity>(parent, child);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IUmbracoEntity> GetChildEntitiesFromRelations(IEnumerable<IRelation> relations)
|
|
{
|
|
// Trying to avoid full N+1 lookups, so we'll group by the object type and then use the GetAll
|
|
// method to lookup batches of entities for each parent object type
|
|
foreach (IGrouping<UmbracoObjectTypes, IRelation> groupedRelations in relations.GroupBy(x =>
|
|
ObjectTypes.GetUmbracoObjectType(x.ChildObjectType)))
|
|
{
|
|
UmbracoObjectTypes objectType = groupedRelations.Key;
|
|
var ids = groupedRelations.Select(x => x.ChildId).ToArray();
|
|
foreach (IEntitySlim e in _entityService.GetAll(objectType, ids))
|
|
{
|
|
yield return e;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<IUmbracoEntity> GetParentEntitiesFromRelations(IEnumerable<IRelation> relations)
|
|
{
|
|
// Trying to avoid full N+1 lookups, so we'll group by the object type and then use the GetAll
|
|
// method to lookup batches of entities for each parent object type
|
|
foreach (IGrouping<UmbracoObjectTypes, IRelation> groupedRelations in relations.GroupBy(x =>
|
|
ObjectTypes.GetUmbracoObjectType(x.ParentObjectType)))
|
|
{
|
|
UmbracoObjectTypes objectType = groupedRelations.Key;
|
|
var ids = groupedRelations.Select(x => x.ParentId).ToArray();
|
|
foreach (IEntitySlim e in _entityService.GetAll(objectType, ids))
|
|
{
|
|
yield return e;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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());
|
|
}
|
|
|
|
/// <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());
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<Tuple<IUmbracoEntity, IUmbracoEntity>> GetEntitiesFromRelations(IEnumerable<IRelation> relations)
|
|
{
|
|
// TODO: Argh! N+1
|
|
foreach (IRelation relation in relations)
|
|
{
|
|
UmbracoObjectTypes childObjectType = ObjectTypes.GetUmbracoObjectType(relation.ChildObjectType);
|
|
UmbracoObjectTypes parentObjectType = ObjectTypes.GetUmbracoObjectType(relation.ParentObjectType);
|
|
|
|
IEntitySlim? child = _entityService.Get(relation.ChildId, childObjectType);
|
|
IEntitySlim? parent = _entityService.Get(relation.ParentId, parentObjectType);
|
|
|
|
if (parent is not null && child is not null)
|
|
{
|
|
yield return new Tuple<IUmbracoEntity, IUmbracoEntity>(parent, child);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IRelation Relate(int parentId, int childId, IRelationType relationType)
|
|
{
|
|
// Ensure that the RelationType has an identity before using it to relate two entities
|
|
if (relationType.HasIdentity == false)
|
|
{
|
|
Save(relationType);
|
|
}
|
|
|
|
// 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();
|
|
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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, IRelationType relationType) =>
|
|
Relate(parent.Id, child.Id, relationType);
|
|
|
|
/// <inheritdoc />
|
|
public IRelation Relate(int parentId, int childId, string relationTypeAlias)
|
|
{
|
|
IRelationType? relationType = GetRelationTypeByAlias(relationTypeAlias);
|
|
if (relationType == null || string.IsNullOrEmpty(relationType.Alias))
|
|
{
|
|
throw new ArgumentNullException(
|
|
string.Format("No RelationType with Alias '{0}' exists.", relationTypeAlias));
|
|
}
|
|
|
|
return Relate(parentId, childId, relationType);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IRelation Relate(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias)
|
|
{
|
|
IRelationType? relationType = GetRelationTypeByAlias(relationTypeAlias);
|
|
if (relationType == null || string.IsNullOrEmpty(relationType.Alias))
|
|
{
|
|
throw new ArgumentNullException(
|
|
string.Format("No RelationType with Alias '{0}' exists.", relationTypeAlias));
|
|
}
|
|
|
|
return Relate(parent.Id, child.Id, relationType);
|
|
}
|
|
|
|
/// <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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool IsRelated(int id) => IsRelated(id, RelationDirectionFilter.Any);
|
|
|
|
/// <inheritdoc />
|
|
public bool IsRelated(int id, RelationDirectionFilter directionFilter)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true);
|
|
IQuery<IRelation> query = Query<IRelation>();
|
|
|
|
query = directionFilter switch
|
|
{
|
|
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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool AreRelated(int parentId, int childId, string relationTypeAlias)
|
|
{
|
|
IRelationType? relType = GetRelationTypeByAlias(relationTypeAlias);
|
|
if (relType == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return AreRelated(parentId, childId, relType);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child) => AreRelated(parent.Id, child.Id);
|
|
|
|
/// <inheritdoc />
|
|
public bool AreRelated(IUmbracoEntity parent, IUmbracoEntity child, string relationTypeAlias) =>
|
|
AreRelated(parent.Id, child.Id, relationTypeAlias);
|
|
|
|
/// <inheritdoc />
|
|
public void Save(IRelation relation)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
|
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));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Save(IEnumerable<IRelation> relations)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
|
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));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Save(IRelationType relationType)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
|
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));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Attempt<IRelationType, RelationTypeOperationStatus>> CreateAsync(IRelationType relationType, Guid userKey)
|
|
{
|
|
if (relationType.Id != 0)
|
|
{
|
|
return Attempt.FailWithStatus(RelationTypeOperationStatus.InvalidId, relationType);
|
|
}
|
|
|
|
return await SaveAsync(
|
|
relationType,
|
|
() => _relationTypeRepository.Get(relationType.Key) is not null ? RelationTypeOperationStatus.KeyAlreadyExists : RelationTypeOperationStatus.Success,
|
|
AuditType.New,
|
|
$"Created relation type: {relationType.Name}",
|
|
userKey);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Attempt<IRelationType, RelationTypeOperationStatus>> UpdateAsync(IRelationType relationType, Guid userKey) =>
|
|
await SaveAsync(
|
|
relationType,
|
|
() => _relationTypeRepository.Get(relationType.Key) is null ? RelationTypeOperationStatus.NotFound : RelationTypeOperationStatus.Success,
|
|
AuditType.Save,
|
|
$"Created relation type: {relationType.Name}",
|
|
userKey);
|
|
|
|
private async Task<Attempt<IRelationType, RelationTypeOperationStatus>> SaveAsync(IRelationType relationType, Func<RelationTypeOperationStatus> operationValidation, AuditType auditType, string auditMessage, Guid userKey)
|
|
{
|
|
// Validate that parent & child object types are allowed
|
|
UmbracoObjectTypes[] allowedObjectTypes = GetAllowedObjectTypes().ToArray();
|
|
var childObjectTypeAllowed = allowedObjectTypes.Any(x => x.GetGuid() == relationType.ChildObjectType);
|
|
if (childObjectTypeAllowed is false)
|
|
{
|
|
return Attempt.FailWithStatus(RelationTypeOperationStatus.InvalidChildObjectType, relationType);
|
|
}
|
|
|
|
var parentObjectTypeAllowed = allowedObjectTypes.Any(x => x.GetGuid() == relationType.ParentObjectType);
|
|
|
|
if (parentObjectTypeAllowed is false)
|
|
{
|
|
return Attempt.FailWithStatus(RelationTypeOperationStatus.InvalidParentObjectType, relationType);
|
|
}
|
|
|
|
using (ICoreScope scope = ScopeProvider.CreateCoreScope())
|
|
{
|
|
RelationTypeOperationStatus status = operationValidation();
|
|
if (status != RelationTypeOperationStatus.Success)
|
|
{
|
|
return Attempt.FailWithStatus(status, relationType);
|
|
}
|
|
|
|
EventMessages eventMessages = EventMessagesFactory.Get();
|
|
var savingNotification = new RelationTypeSavingNotification(relationType, eventMessages);
|
|
if (await scope.Notifications.PublishCancelableAsync(savingNotification))
|
|
{
|
|
scope.Complete();
|
|
return Attempt.FailWithStatus(RelationTypeOperationStatus.CancelledByNotification, relationType);
|
|
}
|
|
|
|
_relationTypeRepository.Save(relationType);
|
|
var currentUser = await _userIdKeyResolver.GetAsync(userKey);
|
|
Audit(auditType, currentUser, relationType.Id, auditMessage);
|
|
scope.Complete();
|
|
scope.Notifications.Publish(
|
|
new RelationTypeSavedNotification(relationType, eventMessages).WithStateFrom(savingNotification));
|
|
}
|
|
|
|
return Attempt.SucceedWithStatus(RelationTypeOperationStatus.Success, relationType);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Delete(IRelation relation)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
|
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));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void Delete(IRelationType relationType)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
|
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));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<Attempt<IRelationType?, RelationTypeOperationStatus>> DeleteAsync(Guid key, Guid userKey)
|
|
{
|
|
using ICoreScope scope = ScopeProvider.CreateCoreScope();
|
|
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 (await scope.Notifications.PublishCancelableAsync(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 Attempt.SucceedWithStatus<IRelationType?, RelationTypeOperationStatus>(RelationTypeOperationStatus.Success, relationType);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void DeleteRelationsOfType(IRelationType relationType)
|
|
{
|
|
var relations = new List<IRelation>();
|
|
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)
|
|
{
|
|
_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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IEnumerable<UmbracoObjectTypes> GetAllowedObjectTypes() =>
|
|
[
|
|
UmbracoObjectTypes.Document,
|
|
UmbracoObjectTypes.Media,
|
|
UmbracoObjectTypes.Member,
|
|
UmbracoObjectTypes.DocumentType,
|
|
UmbracoObjectTypes.MediaType,
|
|
UmbracoObjectTypes.MemberType,
|
|
UmbracoObjectTypes.DataType,
|
|
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();
|
|
}
|
|
|
|
private List<IRelation> GetRelationsByListOfTypeIds(IEnumerable<int> relationTypeIds)
|
|
{
|
|
var relations = new List<IRelation>();
|
|
using (ICoreScope scope = ScopeProvider.CreateCoreScope(autoComplete: true))
|
|
{
|
|
foreach (var relationTypeId in relationTypeIds)
|
|
{
|
|
var id = relationTypeId;
|
|
IQuery<IRelation> query = Query<IRelation>().Where(x => x.RelationTypeId == id);
|
|
IEnumerable<IRelation> relation = _relationRepository.Get(query);
|
|
relations.AddRange(relation);
|
|
}
|
|
}
|
|
|
|
return relations;
|
|
}
|
|
|
|
private void Audit(AuditType type, int userId, int objectId, string? message = null) =>
|
|
_auditRepository.Save(new AuditItem(objectId, type, userId, UmbracoObjectTypes.RelationType.GetName(), message));
|
|
|
|
#endregion
|
|
}
|