From 193892f0847bec49f4f7891e8dd296c10627a7a1 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 24 Oct 2019 16:48:21 +1100 Subject: [PATCH] Creates method to create the relations based on the property editor's returned reference but have discovered a gotcha for relations, so next step is to resolve that. --- src/Umbraco.Core/Constants-Conventions.cs | 32 ++++++++-- .../Migrations/Install/DatabaseDataCreator.cs | 10 +++ .../Models/Editors/ContentPropertyFile.cs | 1 + .../Models/Editors/UmbracoEntityReference.cs | 55 ++++++++++++++++ .../Repositories/IRelationRepository.cs | 9 ++- .../Implement/ContentRepositoryBase.cs | 62 ++++++++++++++++++- .../Implement/DocumentBlueprintRepository.cs | 4 +- .../Implement/DocumentRepository.cs | 4 +- .../Repositories/Implement/MediaRepository.cs | 4 +- .../Implement/MemberRepository.cs | 4 +- .../Implement/RelationRepository.cs | 16 +++++ .../SqlSyntax/SqlSyntaxProviderExtensions.cs | 2 + .../PropertyEditors/IDataValueReference.cs | 3 +- src/Umbraco.Core/Services/IRelationService.cs | 2 +- .../Services/Implement/RelationService.cs | 4 +- src/Umbraco.Core/Udi.cs | 2 +- src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../Repositories/ContentTypeRepositoryTest.cs | 2 +- .../Repositories/DocumentRepositoryTest.cs | 2 +- .../Repositories/DomainRepositoryTest.cs | 2 +- .../Repositories/MediaRepositoryTest.cs | 2 +- .../Repositories/MemberRepositoryTest.cs | 2 +- .../PublicAccessRepositoryTest.cs | 2 +- .../Repositories/TagRepositoryTest.cs | 4 +- .../Repositories/TemplateRepositoryTest.cs | 2 +- .../Repositories/UserRepositoryTest.cs | 4 +- .../Services/ContentServicePerformanceTest.cs | 2 +- .../Services/ContentServiceTests.cs | 2 +- .../Services/RelationServiceTests.cs | 2 + .../PropertyEditors/RichTextPropertyEditor.cs | 7 ++- 30 files changed, 213 insertions(+), 37 deletions(-) create mode 100644 src/Umbraco.Core/Models/Editors/UmbracoEntityReference.cs diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 6c9407667a..25d259b7d1 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -309,32 +309,52 @@ namespace Umbraco.Core public static class RelationTypes { /// - /// ContentType name for default relation type "Relate Document On Copy". + /// Name for default relation type "Related Media". + /// + public const string RelatedMediaName = "Related Media"; + + /// + /// Alias for default relation type "Related Media" + /// + public const string RelatedMediaAlias = "umbMedia"; + + /// + /// Name for default relation type "Related Document". + /// + public const string RelatedDocumentName = "Related Document"; + + /// + /// Alias for default relation type "Related Document" + /// + public const string RelatedDocumentAlias = "umbDocument"; + + /// + /// Name for default relation type "Relate Document On Copy". /// public const string RelateDocumentOnCopyName = "Relate Document On Copy"; /// - /// ContentType alias for default relation type "Relate Document On Copy". + /// Alias for default relation type "Relate Document On Copy". /// public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy"; /// - /// ContentType name for default relation type "Relate Parent Document On Delete". + /// Name for default relation type "Relate Parent Document On Delete". /// public const string RelateParentDocumentOnDeleteName = "Relate Parent Document On Delete"; /// - /// ContentType alias for default relation type "Relate Parent Document On Delete". + /// Alias for default relation type "Relate Parent Document On Delete". /// public const string RelateParentDocumentOnDeleteAlias = "relateParentDocumentOnDelete"; /// - /// ContentType name for default relation type "Relate Parent Media Folder On Delete". + /// Name for default relation type "Relate Parent Media Folder On Delete". /// public const string RelateParentMediaFolderOnDeleteName = "Relate Parent Media Folder On Delete"; /// - /// ContentType alias for default relation type "Relate Parent Media Folder On Delete". + /// Alias for default relation type "Relate Parent Media Folder On Delete". /// public const string RelateParentMediaFolderOnDeleteAlias = "relateParentMediaFolderOnDelete"; } diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs index 94d8cfbc62..1027840995 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseDataCreator.cs @@ -318,6 +318,16 @@ namespace Umbraco.Core.Migrations.Install relationType = new RelationTypeDto { Id = 3, Alias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias, ChildObjectType = Constants.ObjectTypes.Media, ParentObjectType = Constants.ObjectTypes.Media, Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName }; relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); _database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); + + //TODO: We need to decide if we are going to change the relations APIs since it's pretty crappy that we have to explicitly define all relation type object type combinations... + + relationType = new RelationTypeDto { Id = 4, Alias = Constants.Conventions.RelationTypes.RelatedMediaAlias, ChildObjectType = Constants.ObjectTypes.Media, ParentObjectType = Constants.ObjectTypes.Document, Dual = false, Name = Constants.Conventions.RelationTypes.RelatedMediaName }; + relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); + _database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); + + relationType = new RelationTypeDto { Id = 4, Alias = Constants.Conventions.RelationTypes.RelatedDocumentAlias, ChildObjectType = Constants.ObjectTypes.Document, ParentObjectType = Constants.ObjectTypes.Document, Dual = false, Name = Constants.Conventions.RelationTypes.RelatedDocumentName }; + relationType.UniqueId = (relationType.Alias + "____" + relationType.Name).ToGuid(); + _database.Insert(Constants.DatabaseSchema.Tables.RelationType, "id", false, relationType); } private void CreateKeyValueData() diff --git a/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs b/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs index ac236e1fdd..225e29a8a1 100644 --- a/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs +++ b/src/Umbraco.Core/Models/Editors/ContentPropertyFile.cs @@ -1,5 +1,6 @@ namespace Umbraco.Core.Models.Editors { + /// /// Represents an uploaded file for a property. /// diff --git a/src/Umbraco.Core/Models/Editors/UmbracoEntityReference.cs b/src/Umbraco.Core/Models/Editors/UmbracoEntityReference.cs new file mode 100644 index 0000000000..f5121988f5 --- /dev/null +++ b/src/Umbraco.Core/Models/Editors/UmbracoEntityReference.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Models.Editors +{ + /// + /// Used to track reference to other entities in a property value + /// + public struct UmbracoEntityReference : IEquatable + { + private static readonly UmbracoEntityReference _empty = new UmbracoEntityReference(Udi.UnknownTypeUdi.Instance, string.Empty); + + public UmbracoEntityReference(Udi udi, string relationTypeAlias) + { + Udi = udi ?? throw new ArgumentNullException(nameof(udi)); + RelationTypeAlias = relationTypeAlias ?? throw new ArgumentNullException(nameof(relationTypeAlias)); + } + + public static UmbracoEntityReference Empty() => _empty; + + public static bool IsEmpty(UmbracoEntityReference reference) => reference == Empty(); + + public Udi Udi { get; } + public string RelationTypeAlias { get; } + + public override bool Equals(object obj) + { + return obj is UmbracoEntityReference reference && Equals(reference); + } + + public bool Equals(UmbracoEntityReference other) + { + return EqualityComparer.Default.Equals(Udi, other.Udi) && + RelationTypeAlias == other.RelationTypeAlias; + } + + public override int GetHashCode() + { + var hashCode = -487348478; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Udi); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(RelationTypeAlias); + return hashCode; + } + + public static bool operator ==(UmbracoEntityReference left, UmbracoEntityReference right) + { + return left.Equals(right); + } + + public static bool operator !=(UmbracoEntityReference left, UmbracoEntityReference right) + { + return !(left == right); + } + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs index 51d7656d8a..00ab158d83 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IRelationRepository.cs @@ -4,6 +4,13 @@ namespace Umbraco.Core.Persistence.Repositories { public interface IRelationRepository : IReadWriteQueryRepository { - + /// + /// Deletes all relations for a parent for any specified relation type alias + /// + /// + /// + /// A list of relation types to match for deletion, if none are specified then all relations for this parent id are deleted + /// + void DeleteByParent(int parentId, params string[] relationTypeAliases); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index eede78f4c7..3bb57dca9f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.Dtos; @@ -47,12 +48,13 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// Lazy property value collection - must be lazy because we have a circular dependency since some property editors require services, yet these services require property editors /// protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, - ILanguageRepository languageRepository, IRelationRepository relationRepository, + ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, Lazy propertyEditors) : base(scopeAccessor, cache, logger) { LanguageRepository = languageRepository; RelationRepository = relationRepository; + RelationTypeRepository = relationTypeRepository; _propertyEditors = propertyEditors; } @@ -60,6 +62,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected ILanguageRepository LanguageRepository { get; } protected IRelationRepository RelationRepository { get; } + protected IRelationTypeRepository RelationTypeRepository { get; } protected PropertyEditorCollection PropertyEditors => _propertyEditors.Value; @@ -818,7 +821,62 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected void PersistRelations(TEntity entity) { - //foreach(var p in entity.) + var trackedRelations = new List(); + + foreach (var p in entity.Properties) + { + if (!PropertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue; + if (!(editor is IDataValueReference reference)) continue; + + //TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here + if (!p.PropertyType.VariesByNothing()) continue; + + var val = p.GetValue(); // get the invariant value + var refs = reference.GetReferences(val); + trackedRelations.AddRange(refs); + } + + if (trackedRelations.Count == 0) return; + + //First delete all relations for this entity + var relationTypes = trackedRelations.Select(x => x.RelationTypeAlias).ToArray(); + RelationRepository.DeleteByParent(entity.Id, relationTypes); + + var udiToGuids = trackedRelations.Select(x => x.Udi as GuidUdi) + .ToDictionary(x => (Udi)x, x => x.Guid); + + //lookup in the DB all INT ids for the GUIDs and chuck into a dictionary + var keyToIds = Database.Fetch(Sql().Select(x => x.NodeId, x => x.UniqueId).From().WhereIn(x => x.UniqueId, udiToGuids.Values)) + .ToDictionary(x => x.UniqueId, x => x.NodeId); + + var allRelationTypes = RelationTypeRepository.GetMany(Array.Empty()) + .ToDictionary(x => x.Alias, x => x); + + foreach(var rel in trackedRelations) + { + if (!allRelationTypes.TryGetValue(rel.RelationTypeAlias, out var relationType)) + throw new InvalidOperationException($"The relation type {rel.RelationTypeAlias} does not exist"); + + if (!udiToGuids.TryGetValue(rel.Udi, out var guid)) + continue; // This shouldn't happen! + + if (!keyToIds.TryGetValue(guid, out var id)) + continue; // This shouldn't happen! + + //Create new relation + //TODO: This is N+1, we could do this all in one operation, just need a new method on the relations repo + RelationRepository.Save(new Relation(entity.Id, id, relationType)); + } + + } + + private class NodeIdKey + { + [Column("id")] + public int NodeId { get; set; } + + [Column("uniqueId")] + public Guid UniqueId { get; set; } } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs index de766ee5aa..60d4026ad5 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs @@ -19,9 +19,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement internal class DocumentBlueprintRepository : DocumentRepository, IDocumentBlueprintRepository { public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, - IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, + IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, Lazy propertyEditorCollection) - : base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditorCollection) + : base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditorCollection) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 067086ea6b..ce95875209 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -45,9 +45,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// Lazy property value collection - must be lazy because we have a circular dependency since some property editors require services, yet these services require property editors /// public DocumentRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, - IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, + IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, Lazy propertyEditors) - : base(scopeAccessor, appCaches, logger, languageRepository, relationRepository, propertyEditors) + : base(scopeAccessor, appCaches, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditors) { _contentTypeRepository = contentTypeRepository ?? throw new ArgumentNullException(nameof(contentTypeRepository)); _templateRepository = templateRepository ?? throw new ArgumentNullException(nameof(templateRepository)); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs index 0da35145cc..0423ac9125 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MediaRepository.cs @@ -28,9 +28,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly ITagRepository _tagRepository; private readonly MediaByGuidReadRepository _mediaByGuidReadRepository; - public MediaRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, + public MediaRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, IMediaTypeRepository mediaTypeRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, Lazy propertyEditorCollection) - : base(scopeAccessor, cache, logger, languageRepository, relationRepository, propertyEditorCollection) + : base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditorCollection) { _mediaTypeRepository = mediaTypeRepository ?? throw new ArgumentNullException(nameof(mediaTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs index 5274e99cc9..c36143d09a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/MemberRepository.cs @@ -27,9 +27,9 @@ namespace Umbraco.Core.Persistence.Repositories.Implement private readonly IMemberGroupRepository _memberGroupRepository; public MemberRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger logger, - IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, + IMemberTypeRepository memberTypeRepository, IMemberGroupRepository memberGroupRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, Lazy propertyEditors) - : base(scopeAccessor, cache, logger, languageRepository, relationRepository, propertyEditors) + : base(scopeAccessor, cache, logger, languageRepository, relationRepository, relationTypeRepository, propertyEditors) { _memberTypeRepository = memberTypeRepository ?? throw new ArgumentNullException(nameof(memberTypeRepository)); _tagRepository = tagRepository ?? throw new ArgumentNullException(nameof(tagRepository)); diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs index 4b4af505b8..88e28e6ab8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/RelationRepository.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; +using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.Repositories.Implement @@ -157,5 +158,20 @@ namespace Umbraco.Core.Persistence.Repositories.Implement } #endregion + + public void DeleteByParent(int parentId, params string[] relationTypeAliases) + { + var subQuery = Sql().Select(x => x.Id) + .From() + .InnerJoin().On(x => x.RelationType, x => x.Id) + .Where(x => x.ParentId == parentId); + + if (relationTypeAliases.Length > 0) + { + subQuery.WhereIn(x => x.Alias, relationTypeAliases); + } + + Database.Execute(Sql().Delete().WhereIn(x => x.Id, subQuery)); + } } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs index f7cf480830..b829f1fbc5 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderExtensions.cs @@ -35,6 +35,8 @@ namespace Umbraco.Core.Persistence.SqlSyntax /// public static Sql GetDeleteSubquery(this ISqlSyntaxProvider sqlProvider, string tableName, string columnName, Sql subQuery, WhereInType whereInType = WhereInType.In) { + //TODO: This is no longer necessary since this used to be a specific requirement for MySql! + // Now we can do a Delete + sub query, see RelationRepository.DeleteByParent for example return new Sql(string.Format( diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs b/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs index e71642f8a3..8c0806a4a4 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Umbraco.Core.Models.Editors; namespace Umbraco.Core.PropertyEditors { @@ -12,6 +13,6 @@ namespace Umbraco.Core.PropertyEditors /// /// /// - IEnumerable GetReferences(object value); + IEnumerable GetReferences(object value); } } diff --git a/src/Umbraco.Core/Services/IRelationService.cs b/src/Umbraco.Core/Services/IRelationService.cs index ef22632d6e..0f339688de 100644 --- a/src/Umbraco.Core/Services/IRelationService.cs +++ b/src/Umbraco.Core/Services/IRelationService.cs @@ -47,7 +47,7 @@ namespace Umbraco.Core.Services /// /// to retrieve Relations for /// An enumerable list of objects - IEnumerable GetAllRelationsByRelationType(RelationType relationType); + IEnumerable GetAllRelationsByRelationType(IRelationType relationType); /// /// Gets all objects by their 's Id diff --git a/src/Umbraco.Core/Services/Implement/RelationService.cs b/src/Umbraco.Core/Services/Implement/RelationService.cs index 405c3a2800..9de3492e09 100644 --- a/src/Umbraco.Core/Services/Implement/RelationService.cs +++ b/src/Umbraco.Core/Services/Implement/RelationService.cs @@ -96,7 +96,7 @@ namespace Umbraco.Core.Services.Implement /// /// to retrieve Relations for /// An enumerable list of objects - public IEnumerable GetAllRelationsByRelationType(RelationType relationType) + public IEnumerable GetAllRelationsByRelationType(IRelationType relationType) { return GetAllRelationsByRelationType(relationType.Id); } @@ -642,6 +642,8 @@ namespace Umbraco.Core.Services.Implement var query = Query().Where(x => x.RelationTypeId == relationType.Id); relations.AddRange(_relationRepository.Get(query).ToList()); + //TODO: N+1, we should be able to do this in a single call + foreach (var relation in relations) _relationRepository.Delete(relation); diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index e7d00fffa5..ea3ec0ed2d 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -368,7 +368,7 @@ namespace Umbraco.Core return (udi1 == udi2) == false; } - private class UnknownTypeUdi : Udi + internal class UnknownTypeUdi : Udi { private UnknownTypeUdi() : base("unknown", "umb://unknown/") diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2bd8bfc4fe..65118a877a 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -245,6 +245,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs index 4d2e8bc999..07ca7d238d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentTypeRepositoryTest.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository(scopeAccessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository(scopeAccessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index c2f897df80..45dc3de2e3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -72,7 +72,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs index 16a59d789f..27ea92fed6 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, Core.Cache.AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); var domainRepository = new DomainRepository(accessor, Core.Cache.AppCaches.Disabled, Logger); return domainRepository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs index c976912220..93587506e8 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(scopeAccessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, propertyEditors); + var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 9361bd5909..72b4691639 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -39,7 +39,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of(), relationRepository, propertyEditors); + var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs index 8cabc18d2c..84a0f608f7 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs @@ -314,7 +314,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index 61aec9f74c..a4155639be 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -963,7 +963,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } @@ -978,7 +978,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, propertyEditors); + var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 7bf113dfc3..e996c4f6a1 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -245,7 +245,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(ScopeProvider, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(ScopeProvider, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var contentRepo = new DocumentRepository(ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var contentRepo = new DocumentRepository(ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage"); ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! diff --git a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs index 01a5573119..0438e2193b 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, propertyEditors); + var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors); return repository; } @@ -55,7 +55,7 @@ namespace Umbraco.Tests.Persistence.Repositories var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index 5de5d9bd16..d7729c19c1 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -50,7 +50,7 @@ namespace Umbraco.Tests.Services var relationTypeRepository = new RelationTypeRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository((IScopeAccessor)provider, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index bd97772d12..1f53767dbb 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -3169,7 +3169,7 @@ namespace Umbraco.Tests.Services var relationTypeRepository = new RelationTypeRepository(accessor, AppCaches.Disabled, Logger); var relationRepository = new RelationRepository(accessor, Logger, relationTypeRepository); var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, propertyEditors); + var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors); return repository; } diff --git a/src/Umbraco.Tests/Services/RelationServiceTests.cs b/src/Umbraco.Tests/Services/RelationServiceTests.cs index cfef50a330..ad24d2345a 100644 --- a/src/Umbraco.Tests/Services/RelationServiceTests.cs +++ b/src/Umbraco.Tests/Services/RelationServiceTests.cs @@ -23,5 +23,7 @@ namespace Umbraco.Tests.Services Assert.AreEqual(rt.Name, "repeatedEventOccurence"); } + + //TODO: Create a relation for entities of the wrong Entity Type (GUID) based on the Relation Type's defined parent/child object types } } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 0287bdfefe..ed3f484a4e 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -4,6 +4,7 @@ using System.Linq; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Examine; @@ -135,15 +136,15 @@ namespace Umbraco.Web.PropertyEditors /// /// /// - public IEnumerable GetReferences(object value) + public IEnumerable GetReferences(object value) { var asString = value == null ? string.Empty : value is string str ? str : value.ToString(); foreach (var udi in _imageSourceParser.FindUdisFromDataAttributes(asString)) - yield return udi; + yield return new UmbracoEntityReference(udi, Constants.Conventions.RelationTypes.RelatedMediaAlias); foreach (var udi in _localLinkParser.FindUdisFromLocalLinks(asString)) - yield return udi; + yield return new UmbracoEntityReference(udi, Constants.Conventions.RelationTypes.RelatedMediaAlias); //TODO: Detect Macros too ... but we can save that for a later date, right now need to do media refs }