From 13f7ee8a5fff4c3e584cb8d4405e8ac12060ed8d Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 19:40:06 +1100 Subject: [PATCH 01/15] Adds IDataValueReference --- .../PropertyEditors/IDataValueEditor.cs | 19 +++++++++++++++++++ .../PropertyEditors/IDataValueReference.cs | 17 +++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 3 files changed, 37 insertions(+) create mode 100644 src/Umbraco.Core/PropertyEditors/IDataValueReference.cs diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs index cb68531cc705..a02fa71ec709 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueEditor.cs @@ -7,6 +7,7 @@ namespace Umbraco.Core.PropertyEditors { + /// /// Represents an editor for editing data values. /// @@ -63,8 +64,26 @@ public interface IDataValueEditor // TODO: / deal with this when unplugging the xml cache // why property vs propertyType? services should be injected! etc... + + /// + /// Used for serializing an item for packaging + /// + /// + /// + /// + /// + /// IEnumerable ConvertDbToXml(Property property, IDataTypeService dataTypeService, ILocalizationService localizationService, bool published); + + /// + /// Used for serializing an item for packaging + /// + /// + /// + /// + /// XNode ConvertDbToXml(PropertyType propertyType, object value, IDataTypeService dataTypeService); + string ConvertDbToString(PropertyType propertyType, object value, IDataTypeService dataTypeService); } } diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs b/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs new file mode 100644 index 000000000000..d7d848f1bfe1 --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Used to resolve references from values + /// + public interface IDataValueReference + { + /// + /// Returns any references contained in the value + /// + /// + /// + IEnumerable GetReferences(object value); + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 43d168f442b7..be1686df4fe2 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -270,6 +270,7 @@ + From a7dfba58d10c6f91a5a59ac7e1969ac1ea1abbfc Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 22:49:39 +1100 Subject: [PATCH 02/15] Removes singleton accessors from repositories --- .../Repositories/Implement/ContentRepositoryBase.cs | 5 +++-- .../Implement/DocumentBlueprintRepository.cs | 5 +++-- .../Repositories/Implement/DocumentRepository.cs | 5 +++-- .../Repositories/Implement/MediaRepository.cs | 5 +++-- .../Repositories/Implement/MemberRepository.cs | 5 +++-- .../Repositories/ContentTypeRepositoryTest.cs | 3 ++- .../Persistence/Repositories/DocumentRepositoryTest.cs | 2 +- .../Persistence/Repositories/DomainRepositoryTest.cs | 5 +++-- .../Persistence/Repositories/MediaRepositoryTest.cs | 5 +++-- .../Persistence/Repositories/MemberRepositoryTest.cs | 3 ++- .../Repositories/PublicAccessRepositoryTest.cs | 5 +++-- .../Persistence/Repositories/TagRepositoryTest.cs | 7 ++++--- .../Persistence/Repositories/TemplateRepositoryTest.cs | 3 ++- .../Persistence/Repositories/UserRepositoryTest.cs | 5 +++-- .../Services/ContentServicePerformanceTest.cs | 9 +++++---- src/Umbraco.Tests/Services/ContentServiceTests.cs | 2 +- 16 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 7ab73f3f2d51..a6ae2fc7e096 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -33,17 +33,18 @@ internal abstract class ContentRepositoryBase : NPoco where TEntity : class, IUmbracoEntity where TRepository : class, IRepository { - protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILanguageRepository languageRepository, ILogger logger) + protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILanguageRepository languageRepository, ILogger logger, PropertyEditorCollection propertyEditors) : base(scopeAccessor, cache, logger) { LanguageRepository = languageRepository; + PropertyEditors = propertyEditors; } protected abstract TRepository This { get; } protected ILanguageRepository LanguageRepository { get; } - protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // TODO: inject + protected PropertyEditorCollection PropertyEditors { get; } #region Versions diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs index d137d7ac76b8..c654e1a6c2cc 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs @@ -2,6 +2,7 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.Repositories.Implement @@ -17,8 +18,8 @@ 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) - : base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository) + public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository, PropertyEditorCollection propertyEditors) + : base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, propertyEditors) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index 2649b9993fed..eb392e86cb0b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; @@ -30,8 +31,8 @@ internal class DocumentRepository : ContentRepositoryBase()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 4d62ec830173..424ab145bca1 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -69,7 +69,7 @@ private DocumentRepository CreateRepository(IScopeAccessor scopeAccessor, out Co var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches); var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger); contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs index 628f8d75a78d..65b5e8365e49 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs @@ -3,9 +3,10 @@ using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -25,7 +26,7 @@ private DomainRepository CreateRepository(IScopeProvider provider, out ContentTy var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); languageRepository = new LanguageRepository(accessor, Core.Cache.AppCaches.Disabled, Logger); contentTypeRepository = new ContentTypeRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, commonRepository, languageRepository); - documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); 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 e2123df9e3fd..462c44d325ea 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core.IO; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence; @@ -17,6 +17,7 @@ using Umbraco.Core.Scoping; using Umbraco.Tests.Testing; using Umbraco.Core.Services; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.Persistence.Repositories { @@ -41,7 +42,7 @@ private MediaRepository CreateRepository(IScopeProvider provider, out MediaTypeR var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger); mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository); var tagRepository = new TagRepository(scopeAccessor, appCaches, Logger); - var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Factory.GetInstance()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 17b16ad7ab7f..0b23240615cb 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -35,7 +36,7 @@ private MemberRepository CreateRepository(IScopeProvider provider, out MemberTyp memberTypeRepository = new MemberTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); memberGroupRepository = new MemberGroupRepository(accessor, AppCaches.Disabled, Logger); var tagRepo = new TagRepository(accessor, AppCaches.Disabled, Logger); - var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of()); + var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of(), Factory.GetInstance()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs index 56041c24aa61..3398e7d24d6b 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs @@ -3,10 +3,11 @@ using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -310,7 +311,7 @@ private DocumentRepository CreateRepository(IScopeProvider provider, out Content var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); var languageRepository = new LanguageRepository(accessor, AppCaches, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index e3de2c28928c..9e7593f4ea0d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -2,11 +2,12 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Cache; -using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -958,7 +959,7 @@ private DocumentRepository CreateContentRepository(IScopeProvider provider, out var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled); var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); return repository; } @@ -970,7 +971,7 @@ private MediaRepository CreateMediaRepository(IScopeProvider provider, out Media var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled); var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger); mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Factory.GetInstance()); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index b0f9a5335b6a..941ba843515e 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -241,7 +242,7 @@ public void Can_Perform_Delete_When_Assigned_To_Doc() var commonRepository = new ContentTypeCommonRepository(ScopeProvider, templateRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger); var contentTypeRepository = new ContentTypeRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var contentRepo = new DocumentRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + var contentRepo = new DocumentRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); 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 3e5919d7f30c..c406a5c70479 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -14,6 +14,7 @@ using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Core.Persistence; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.Persistence.Repositories { @@ -29,7 +30,7 @@ private MediaRepository CreateMediaRepository(IScopeProvider provider, out IMedi var languageRepository = new LanguageRepository(accessor, AppCaches, Logger); mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches, Mock.Of(), commonRepository, languageRepository); var tagRepository = new TagRepository(accessor, AppCaches, Mock.Of()); - var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of()); + var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), Factory.GetInstance()); return repository; } @@ -47,7 +48,7 @@ private DocumentRepository CreateContentRepository(IScopeProvider provider, out var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); var languageRepository = new LanguageRepository(accessor, AppCaches, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); return repository; } diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index ef80672bafee..c77bb714940d 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -14,6 +14,7 @@ using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -168,7 +169,7 @@ public void Getting_100_Uncached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); // Act Stopwatch watch = Stopwatch.StartNew(); @@ -202,7 +203,7 @@ public void Getting_1000_Uncached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); // Act Stopwatch watch = Stopwatch.StartNew(); @@ -234,7 +235,7 @@ public void Getting_100_Cached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor) provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); // Act var contents = repository.GetMany(); @@ -269,7 +270,7 @@ public void Getting_1000_Cached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); // Act var contents = repository.GetMany(); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index e26e764cd16f..65c3b9f8a715 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -3166,7 +3166,7 @@ private DocumentRepository CreateRepository(IScopeProvider provider, out Content var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); + var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); return repository; } From a78a4ff8075ace6b97d49b4b20c992c5c7647a33 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 22:56:02 +1100 Subject: [PATCH 03/15] Less statics, new InternalLinkParserTests, better testing support on UDI, less singletons --- .../PropertyEditors/IDataValueReference.cs | 2 +- src/Umbraco.Core/Udi.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../Web/InternalLinkParserTests.cs | 88 ++++++++++++++++++ .../Web/TemplateUtilitiesTests.cs | 71 +-------------- .../MarkdownEditorValueConverter.cs | 9 +- .../RteMacroRenderingValueConverter.cs | 6 +- .../TextStringValueConverter.cs | 8 +- src/Umbraco.Web/Routing/UrlProvider.cs | 13 +++ src/Umbraco.Web/Runtime/WebInitialComposer.cs | 2 + .../Templates/InternalLinkParser.cs | 91 +++++++++++++++++++ .../Templates/TemplateUtilities.cs | 73 ++------------- src/Umbraco.Web/Umbraco.Web.csproj | 1 + src/Umbraco.Web/UmbracoComponentRenderer.cs | 6 +- 14 files changed, 232 insertions(+), 141 deletions(-) create mode 100644 src/Umbraco.Tests/Web/InternalLinkParserTests.cs create mode 100644 src/Umbraco.Web/Templates/InternalLinkParser.cs diff --git a/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs b/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs index d7d848f1bfe1..e71642f8a385 100644 --- a/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs +++ b/src/Umbraco.Core/PropertyEditors/IDataValueReference.cs @@ -3,7 +3,7 @@ namespace Umbraco.Core.PropertyEditors { /// - /// Used to resolve references from values + /// Resolve references from values /// public interface IDataValueReference { diff --git a/src/Umbraco.Core/Udi.cs b/src/Umbraco.Core/Udi.cs index c7297b8c093b..e7d00fffa59a 100644 --- a/src/Umbraco.Core/Udi.cs +++ b/src/Umbraco.Core/Udi.cs @@ -233,7 +233,7 @@ private static void EnsureScanForUdiTypes() // just pick every service connectors - just making sure that not two of them // would register the same entity type, with different udi types (would not make // much sense anyways). - var connectors = Current.TypeLoader.GetTypes(); + var connectors = Current.HasFactory ? (Current.TypeLoader?.GetTypes() ?? Enumerable.Empty()) : Enumerable.Empty(); var result = new Dictionary(); foreach (var connector in connectors) { diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 39826fcc38db..e73caf45171a 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -253,6 +253,7 @@ + diff --git a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs new file mode 100644 index 000000000000..815ce408eeef --- /dev/null +++ b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs @@ -0,0 +1,88 @@ +using System; +using System.Linq; +using System.Web; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; +using Umbraco.Web.Templates; + +namespace Umbraco.Tests.Web +{ + [TestFixture] + public class InternalLinkParserTests + { + [TestCase("", "")] + [TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")] + [TestCase("hello href=\"{localLink:umb://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")] + [TestCase("hello href=\"{localLink:umb://document/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")] + [TestCase("hello href=\"{localLink:umb://media/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/media/1001/my-image.jpg\" world ")] + //this one has an invalid char so won't match + [TestCase("hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")] + [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")] + public void ParseLocalLinks(string input, string result) + { + var serviceCtxMock = new TestObjects(null).GetServiceContextMock(); + + //setup a mock url provider which we'll use for testing + var testUrlProvider = new Mock(); + testUrlProvider + .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(UrlInfo.Url("/my-test-url")); + + var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); + + var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var publishedContent = new Mock(); + publishedContent.Setup(x => x.Id).Returns(1234); + publishedContent.Setup(x => x.ContentType).Returns(contentType); + var contentCache = new Mock(); + contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); + contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); + var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var media = new Mock(); + media.Setup(x => x.Url).Returns("/media/1001/my-image.jpg"); + media.Setup(x => x.ContentType).Returns(mediaType); + var mediaCache = new Mock(); + mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); + mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); + var snapshot = new Mock(); + snapshot.Setup(x => x.Content).Returns(contentCache.Object); + snapshot.Setup(x => x.Media).Returns(mediaCache.Object); + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot.Object); + var mediaUrlProvider = new Mock(); + mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(UrlInfo.Url("/media/1001/my-image.jpg")); + + var umbracoContextAccessor = new TestUmbracoContextAccessor(); + + var umbracoContextFactory = new UmbracoContextFactory( + umbracoContextAccessor, + snapshotService.Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), + globalSettings, + new UrlProviderCollection(new[] { testUrlProvider.Object }), + new MediaUrlProviderCollection(new[] { mediaUrlProvider.Object }), + Mock.Of()); + + using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) + { + var linkParser = new InternalLinkParser(umbracoContextAccessor); + + var output = linkParser.ParseInternalLinks(input); + + Assert.AreEqual(result, output); + } + } + } +} diff --git a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs index 3a5405548b70..ef2333043124 100644 --- a/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs +++ b/src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs @@ -1,6 +1,4 @@ using System; -using System.Linq; -using System.Web; using Moq; using NUnit.Framework; using Umbraco.Core; @@ -9,20 +7,16 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing.Objects.Accessors; -using Umbraco.Web; -using Umbraco.Web.PublishedCache; -using Umbraco.Web.Routing; using Umbraco.Web.Security; -using Umbraco.Web.Templates; using Umbraco.Core.Configuration; using Umbraco.Core.IO; namespace Umbraco.Tests.Web { + [TestFixture] public class TemplateUtilitiesTests { @@ -59,67 +53,6 @@ public void TearDown() Current.Reset(); } - [TestCase("", "")] - [TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")] - [TestCase("hello href=\"{localLink:umb://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")] - [TestCase("hello href=\"{localLink:umb://document/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")] - [TestCase("hello href=\"{localLink:umb://media/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/media/1001/my-image.jpg\" world ")] - //this one has an invalid char so won't match - [TestCase("hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")] - [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")] - public void ParseLocalLinks(string input, string result) - { - var serviceCtxMock = new TestObjects(null).GetServiceContextMock(); - - //setup a mock entity service from the service context to return an integer for a GUID - var entityService = Mock.Get(serviceCtxMock.EntityService); - //entityService.Setup(x => x.GetId(It.IsAny(), It.IsAny())) - // .Returns((Guid id, UmbracoObjectTypes objType) => - // { - // return Attempt.Succeed(1234); - // }); - - //setup a mock url provider which we'll use for testing - var testUrlProvider = new Mock(); - testUrlProvider - .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns((UmbracoContext umbCtx, IPublishedContent content, UrlMode mode, string culture, Uri url) => UrlInfo.Url("/my-test-url")); - - var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); - var publishedContent = Mock.Of(); - Mock.Get(publishedContent).Setup(x => x.Id).Returns(1234); - Mock.Get(publishedContent).Setup(x => x.ContentType).Returns(contentType); - var contentCache = Mock.Of(); - Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny())).Returns(publishedContent); - Mock.Get(contentCache).Setup(x => x.GetById(It.IsAny())).Returns(publishedContent); - var snapshot = Mock.Of(); - Mock.Get(snapshot).Setup(x => x.Content).Returns(contentCache); - var snapshotService = Mock.Of(); - Mock.Get(snapshotService).Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot); - var media = Mock.Of(); - Mock.Get(media).Setup(x => x.Url).Returns("/media/1001/my-image.jpg"); - var mediaCache = Mock.Of(); - Mock.Get(mediaCache).Setup(x => x.GetById(It.IsAny())).Returns(media); - - var umbracoContextFactory = new UmbracoContextFactory( - Umbraco.Web.Composing.Current.UmbracoContextAccessor, - snapshotService, - new TestVariationContextAccessor(), - new TestDefaultCultureAccessor(), - Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), - globalSettings, - new UrlProviderCollection(new[] { testUrlProvider.Object }), - new MediaUrlProviderCollection(Enumerable.Empty()), - Mock.Of()); - - using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) - { - var output = TemplateUtilities.ParseInternalLinks(input, reference.UmbracoContext.UrlProvider, mediaCache); - - Assert.AreEqual(result, output); - } - } + } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index e11f3e0d3ac7..98413c7b7088 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -12,6 +12,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { + private readonly InternalLinkParser _localLinkParser; + + public MarkdownEditorValueConverter(InternalLinkParser localLinkParser) + { + _localLinkParser = localLinkParser; + } + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MarkdownEditor == propertyType.EditorAlias; @@ -27,7 +34,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, Current.UmbracoContext); + sourceString = _localLinkParser.ParseInternalLinks(sourceString, preview); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); return sourceString; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index d5e1f841ea1d..95cf2cfc855e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -24,6 +24,7 @@ public class RteMacroRenderingValueConverter : TinyMceValueConverter { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; + private readonly InternalLinkParser _internalLinkParser; public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) { @@ -32,10 +33,11 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType return PropertyCacheLevel.Snapshot; } - public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer) + public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, InternalLinkParser internalLinkParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; + _internalLinkParser = internalLinkParser; } // NOT thread-safe over a request because it modifies the @@ -81,7 +83,7 @@ private string Convert(object source, bool preview) var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls and media are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, Current.UmbracoContext); + sourceString = _internalLinkParser.ParseInternalLinks(sourceString, preview); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); sourceString = TemplateUtilities.ResolveMediaFromTextString(sourceString); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index b8ad1477b4e8..ee49536d9d72 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -11,11 +11,17 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class TextStringValueConverter : PropertyValueConverterBase { + public TextStringValueConverter(InternalLinkParser internalLinkParser) + { + _internalLinkParser = internalLinkParser; + } + private static readonly string[] PropertyTypeAliases = { Constants.PropertyEditors.Aliases.TextBox, Constants.PropertyEditors.Aliases.TextArea }; + private readonly InternalLinkParser _internalLinkParser; public override bool IsConverter(IPublishedPropertyType propertyType) => PropertyTypeAliases.Contains(propertyType.EditorAlias); @@ -32,7 +38,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = TemplateUtilities.ParseInternalLinks(sourceString, preview, Current.UmbracoContext); + sourceString = _internalLinkParser.ParseInternalLinks(sourceString, preview); sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); return sourceString; diff --git a/src/Umbraco.Web/Routing/UrlProvider.cs b/src/Umbraco.Web/Routing/UrlProvider.cs index 59e39fa80aba..d42639b781c5 100644 --- a/src/Umbraco.Web/Routing/UrlProvider.cs +++ b/src/Umbraco.Web/Routing/UrlProvider.cs @@ -75,6 +75,7 @@ public UrlProvider(UmbracoContext umbracoContext, IEnumerable urlP private UrlMode GetMode(bool absolute) => absolute ? UrlMode.Absolute : Mode; private IPublishedContent GetDocument(int id) => _umbracoContext.Content.GetById(id); private IPublishedContent GetDocument(Guid id) => _umbracoContext.Content.GetById(id); + private IPublishedContent GetMedia(Guid id) => _umbracoContext.Media.GetById(id); /// /// Gets the url of a published content. @@ -184,6 +185,18 @@ public IEnumerable GetOtherUrls(int id, Uri current) #region GetMediaUrl + /// + /// Gets the url of a media item. + /// + /// + /// + /// + /// + /// + /// + public string GetMediaUrl(Guid id, UrlMode mode = UrlMode.Default, string culture = null, string propertyAlias = Constants.Conventions.Media.File, Uri current = null) + => GetMediaUrl(GetMedia(id), mode, culture, propertyAlias, current); + /// /// Gets the url of a media item. /// diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 87c0f46fba43..82137bbd9d68 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -107,6 +107,8 @@ public override void Compose(Composition composition) composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); + // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) // so inject a "void" helper (not exactly pretty but...) diff --git a/src/Umbraco.Web/Templates/InternalLinkParser.cs b/src/Umbraco.Web/Templates/InternalLinkParser.cs new file mode 100644 index 000000000000..0d8a480a9048 --- /dev/null +++ b/src/Umbraco.Web/Templates/InternalLinkParser.cs @@ -0,0 +1,91 @@ +using System; +using System.Text.RegularExpressions; +using Umbraco.Core; +using Umbraco.Core.Logging; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Web.Templates +{ + /// + /// Utility class used to parse internal links + /// + public sealed class InternalLinkParser + { + + private static readonly Regex LocalLinkPattern = new Regex(@"href=""[/]?(?:\{|\%7B)localLink:([a-zA-Z0-9-://]+)(?:\}|\%7D)", + RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + + public InternalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) + { + _umbracoContextAccessor = umbracoContextAccessor; + } + + public string ParseInternalLinks(string text, bool preview) + { + if (_umbracoContextAccessor.UmbracoContext == null) + throw new InvalidOperationException("Could not parse internal links, there is no current UmbracoContext"); + + if (!preview) + return ParseInternalLinks(text); + + using (_umbracoContextAccessor.UmbracoContext.ForcedPreview(preview)) // force for url provider + { + return ParseInternalLinks(text); + } + } + + /// + /// Parses the string looking for the {localLink} syntax and updates them to their correct links. + /// + /// + /// + /// + public string ParseInternalLinks(string text) + { + if (_umbracoContextAccessor.UmbracoContext == null) + throw new InvalidOperationException("Could not parse internal links, there is no current UmbracoContext"); + + var urlProvider = _umbracoContextAccessor.UmbracoContext.UrlProvider; + + // Parse internal links + var tags = LocalLinkPattern.Matches(text); + foreach (Match tag in tags) + { + if (tag.Groups.Count > 0) + { + var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1); + + //The id could be an int or a UDI + if (Udi.TryParse(id, out var udi)) + { + var guidUdi = udi as GuidUdi; + if (guidUdi != null) + { + var newLink = "#"; + if (guidUdi.EntityType == Constants.UdiEntityType.Document) + newLink = urlProvider.GetUrl(guidUdi.Guid); + else if (guidUdi.EntityType == Constants.UdiEntityType.Media) + newLink = urlProvider.GetMediaUrl(guidUdi.Guid); + + if (newLink == null) + newLink = "#"; + + text = text.Replace(tag.Value, "href=\"" + newLink); + } + } + + if (int.TryParse(id, out var intId)) + { + var newLink = urlProvider.GetUrl(intId); + text = text.Replace(tag.Value, "href=\"" + newLink); + } + } + } + + return text; + } + } +} diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 58d3ed341e8d..1092be73e24f 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -15,8 +15,6 @@ namespace Umbraco.Web.Templates { - //NOTE: I realize there is only one class in this namespace but I'm pretty positive that there will be more classes in - //this namespace once we start migrating and cleaning up more code. /// /// Utility class used for templates @@ -24,7 +22,14 @@ namespace Umbraco.Web.Templates public static class TemplateUtilities { const string TemporaryImageDataAttribute = "data-tmpimg"; + + private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + + private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + [Obsolete("Inject and use an instance of InternalLinkParser instead")] internal static string ParseInternalLinks(string text, bool preview, UmbracoContext umbracoContext) { using (umbracoContext.ForcedPreview(preview)) // force for url provider @@ -35,69 +40,9 @@ internal static string ParseInternalLinks(string text, bool preview, UmbracoCont return text; } - /// - /// Parses the string looking for the {localLink} syntax and updates them to their correct links. - /// - /// - /// - /// + [Obsolete("Inject and use an instance of InternalLinkParser instead")] public static string ParseInternalLinks(string text, UrlProvider urlProvider) => - ParseInternalLinks(text, urlProvider, Current.UmbracoContext.MediaCache); - - // TODO: Replace mediaCache with media url provider - internal static string ParseInternalLinks(string text, UrlProvider urlProvider, IPublishedMediaCache mediaCache) - { - if (urlProvider == null) throw new ArgumentNullException(nameof(urlProvider)); - if (mediaCache == null) throw new ArgumentNullException(nameof(mediaCache)); - - // Parse internal links - var tags = LocalLinkPattern.Matches(text); - foreach (Match tag in tags) - { - if (tag.Groups.Count > 0) - { - var id = tag.Groups[1].Value; //.Remove(tag.Groups[1].Value.Length - 1, 1); - - //The id could be an int or a UDI - if (Udi.TryParse(id, out var udi)) - { - var guidUdi = udi as GuidUdi; - if (guidUdi != null) - { - var newLink = "#"; - if (guidUdi.EntityType == Constants.UdiEntityType.Document) - newLink = urlProvider.GetUrl(guidUdi.Guid); - else if (guidUdi.EntityType == Constants.UdiEntityType.Media) - newLink = mediaCache.GetById(guidUdi.Guid)?.Url; - - if (newLink == null) - newLink = "#"; - - text = text.Replace(tag.Value, "href=\"" + newLink); - } - } - - if (int.TryParse(id, out var intId)) - { - var newLink = urlProvider.GetUrl(intId); - text = text.Replace(tag.Value, "href=\"" + newLink); - } - } - } - - return text; - } - - - // static compiled regex for faster performance - private static readonly Regex LocalLinkPattern = new Regex(@"href=""[/]?(?:\{|\%7B)localLink:([a-zA-Z0-9-://]+)(?:\}|\%7D)", - RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - - private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - - private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + Current.Factory.GetInstance().ParseInternalLinks(text); /// /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index cf149968889a..616ed908e104 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -247,6 +247,7 @@ + diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index f6c3d30da359..805b9267f92e 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -27,12 +27,14 @@ internal class UmbracoComponentRenderer : IUmbracoComponentRenderer private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; private readonly ITemplateRenderer _templateRenderer; + private readonly InternalLinkParser _internalLinkParser; - public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer) + public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer, InternalLinkParser internalLinkParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; _templateRenderer = templateRenderer ?? throw new ArgumentNullException(nameof(templateRenderer)); + _internalLinkParser = internalLinkParser; } /// @@ -157,7 +159,7 @@ private IHtmlString RenderMacro(string alias, IDictionary parame _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType = contentType; //Now, we need to ensure that local links are parsed - html = TemplateUtilities.ParseInternalLinks(output.ToString(), _umbracoContextAccessor.UmbracoContext.UrlProvider); + html = _internalLinkParser.ParseInternalLinks(output.ToString()); } } From 8ccebd8006b8eba225fb496d67c3e1b4364e5675 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 23:04:25 +1100 Subject: [PATCH 04/15] Revert "Removes singleton accessors from repositories" - this causes a circular ref?! --- .../Repositories/Implement/ContentRepositoryBase.cs | 5 ++--- .../Implement/DocumentBlueprintRepository.cs | 5 ++--- .../Repositories/Implement/DocumentRepository.cs | 5 ++--- .../Repositories/Implement/MediaRepository.cs | 5 ++--- .../Repositories/Implement/MemberRepository.cs | 5 ++--- .../Repositories/ContentTypeRepositoryTest.cs | 3 +-- .../Persistence/Repositories/DocumentRepositoryTest.cs | 2 +- .../Persistence/Repositories/DomainRepositoryTest.cs | 5 ++--- .../Persistence/Repositories/MediaRepositoryTest.cs | 5 ++--- .../Persistence/Repositories/MemberRepositoryTest.cs | 3 +-- .../Repositories/PublicAccessRepositoryTest.cs | 5 ++--- .../Persistence/Repositories/TagRepositoryTest.cs | 7 +++---- .../Persistence/Repositories/TemplateRepositoryTest.cs | 3 +-- .../Persistence/Repositories/UserRepositoryTest.cs | 5 ++--- .../Services/ContentServicePerformanceTest.cs | 9 ++++----- src/Umbraco.Tests/Services/ContentServiceTests.cs | 2 +- 16 files changed, 30 insertions(+), 44 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index a6ae2fc7e096..7ab73f3f2d51 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -33,18 +33,17 @@ internal abstract class ContentRepositoryBase : NPoco where TEntity : class, IUmbracoEntity where TRepository : class, IRepository { - protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILanguageRepository languageRepository, ILogger logger, PropertyEditorCollection propertyEditors) + protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, ILanguageRepository languageRepository, ILogger logger) : base(scopeAccessor, cache, logger) { LanguageRepository = languageRepository; - PropertyEditors = propertyEditors; } protected abstract TRepository This { get; } protected ILanguageRepository LanguageRepository { get; } - protected PropertyEditorCollection PropertyEditors { get; } + protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // TODO: inject #region Versions diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs index c654e1a6c2cc..d137d7ac76b8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs @@ -2,7 +2,6 @@ using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Logging; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; namespace Umbraco.Core.Persistence.Repositories.Implement @@ -18,8 +17,8 @@ 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, PropertyEditorCollection propertyEditors) - : base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, propertyEditors) + public DocumentBlueprintRepository(IScopeAccessor scopeAccessor, AppCaches appCaches, ILogger logger, IContentTypeRepository contentTypeRepository, ITemplateRepository templateRepository, ITagRepository tagRepository, ILanguageRepository languageRepository) + : base(scopeAccessor, appCaches, logger, contentTypeRepository, templateRepository, tagRepository, languageRepository) { } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs index eb392e86cb0b..2649b9993fed 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/DocumentRepository.cs @@ -12,7 +12,6 @@ using Umbraco.Core.Persistence.Factories; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.SqlSyntax; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; @@ -31,8 +30,8 @@ internal class DocumentRepository : ContentRepositoryBase()); + var repository = new DocumentRepository(scopeAccessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs index 424ab145bca1..4d62ec830173 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DocumentRepositoryTest.cs @@ -69,7 +69,7 @@ private DocumentRepository CreateRepository(IScopeAccessor scopeAccessor, out Co var commonRepository = new ContentTypeCommonRepository(scopeAccessor, templateRepository, appCaches); var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger); contentTypeRepository = new ContentTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository(scopeAccessor, appCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs index 65b5e8365e49..628f8d75a78d 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/DomainRepositoryTest.cs @@ -3,10 +3,9 @@ using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -26,7 +25,7 @@ private DomainRepository CreateRepository(IScopeProvider provider, out ContentTy var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); languageRepository = new LanguageRepository(accessor, Core.Cache.AppCaches.Disabled, Logger); contentTypeRepository = new ContentTypeRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, commonRepository, languageRepository); - documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + documentRepository = new DocumentRepository(accessor, Core.Cache.AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); 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 462c44d325ea..e2123df9e3fd 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs @@ -4,7 +4,7 @@ using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Configuration.UmbracoSettings; -using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Persistence; @@ -17,7 +17,6 @@ using Umbraco.Core.Scoping; using Umbraco.Tests.Testing; using Umbraco.Core.Services; -using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.Persistence.Repositories { @@ -42,7 +41,7 @@ private MediaRepository CreateRepository(IScopeProvider provider, out MediaTypeR var languageRepository = new LanguageRepository(scopeAccessor, appCaches, Logger); mediaTypeRepository = new MediaTypeRepository(scopeAccessor, appCaches, Logger, commonRepository, languageRepository); var tagRepository = new TagRepository(scopeAccessor, appCaches, Logger); - var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Factory.GetInstance()); + var repository = new MediaRepository(scopeAccessor, appCaches, Logger, mediaTypeRepository, tagRepository, Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs index 0b23240615cb..17b16ad7ab7f 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs @@ -15,7 +15,6 @@ using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -36,7 +35,7 @@ private MemberRepository CreateRepository(IScopeProvider provider, out MemberTyp memberTypeRepository = new MemberTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); memberGroupRepository = new MemberGroupRepository(accessor, AppCaches.Disabled, Logger); var tagRepo = new TagRepository(accessor, AppCaches.Disabled, Logger); - var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of(), Factory.GetInstance()); + var repository = new MemberRepository(accessor, AppCaches.Disabled, Logger, memberTypeRepository, memberGroupRepository, tagRepo, Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs index 3398e7d24d6b..56041c24aa61 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs @@ -3,11 +3,10 @@ using System.Linq; using Moq; using NUnit.Framework; -using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -311,7 +310,7 @@ private DocumentRepository CreateRepository(IScopeProvider provider, out Content var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); var languageRepository = new LanguageRepository(accessor, AppCaches, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); return repository; } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs index 9e7593f4ea0d..e3de2c28928c 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs @@ -2,12 +2,11 @@ using Moq; using NUnit.Framework; using Umbraco.Core.Cache; -using Umbraco.Core; +using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -959,7 +958,7 @@ private DocumentRepository CreateContentRepository(IScopeProvider provider, out var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled); var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); return repository; } @@ -971,7 +970,7 @@ private MediaRepository CreateMediaRepository(IScopeProvider provider, out Media var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches.Disabled); var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger); mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of(), Factory.GetInstance()); + var repository = new MediaRepository(accessor, AppCaches.Disabled, Logger, mediaTypeRepository, tagRepository, Mock.Of()); return repository; } } diff --git a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs index 941ba843515e..b0f9a5335b6a 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/TemplateRepositoryTest.cs @@ -12,7 +12,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -242,7 +241,7 @@ public void Can_Perform_Delete_When_Assigned_To_Doc() var commonRepository = new ContentTypeCommonRepository(ScopeProvider, templateRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger); var contentTypeRepository = new ContentTypeRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var contentRepo = new DocumentRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + var contentRepo = new DocumentRepository((IScopeAccessor) ScopeProvider, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); 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 c406a5c70479..3e5919d7f30c 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs @@ -14,7 +14,6 @@ using Umbraco.Tests.TestHelpers.Entities; using Umbraco.Tests.Testing; using Umbraco.Core.Persistence; -using Umbraco.Core.PropertyEditors; namespace Umbraco.Tests.Persistence.Repositories { @@ -30,7 +29,7 @@ private MediaRepository CreateMediaRepository(IScopeProvider provider, out IMedi var languageRepository = new LanguageRepository(accessor, AppCaches, Logger); mediaTypeRepository = new MediaTypeRepository(accessor, AppCaches, Mock.Of(), commonRepository, languageRepository); var tagRepository = new TagRepository(accessor, AppCaches, Mock.Of()); - var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of(), Factory.GetInstance()); + var repository = new MediaRepository(accessor, AppCaches, Mock.Of(), mediaTypeRepository, tagRepository, Mock.Of()); return repository; } @@ -48,7 +47,7 @@ private DocumentRepository CreateContentRepository(IScopeProvider provider, out var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); var languageRepository = new LanguageRepository(accessor, AppCaches, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository(accessor, AppCaches, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); return repository; } diff --git a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs index c77bb714940d..ef80672bafee 100644 --- a/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs +++ b/src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs @@ -14,7 +14,6 @@ using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.Repositories.Implement; -using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; @@ -169,7 +168,7 @@ public void Getting_100_Uncached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); // Act Stopwatch watch = Stopwatch.StartNew(); @@ -203,7 +202,7 @@ public void Getting_1000_Uncached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); // Act Stopwatch watch = Stopwatch.StartNew(); @@ -235,7 +234,7 @@ public void Getting_100_Cached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor) provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); // Act var contents = repository.GetMany(); @@ -270,7 +269,7 @@ public void Getting_1000_Cached_Items() var commonRepository = new ContentTypeCommonRepository((IScopeAccessor)provider, tRepository, AppCaches); var languageRepository = new LanguageRepository((IScopeAccessor)provider, AppCaches.Disabled, Logger); var ctRepository = new ContentTypeRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository((IScopeAccessor) provider, AppCaches.Disabled, Logger, ctRepository, tRepository, tagRepo, languageRepository); // Act var contents = repository.GetMany(); diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index 65c3b9f8a715..e26e764cd16f 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -3166,7 +3166,7 @@ private DocumentRepository CreateRepository(IScopeProvider provider, out Content var commonRepository = new ContentTypeCommonRepository(accessor, templateRepository, AppCaches); var languageRepository = new LanguageRepository(accessor, AppCaches.Disabled, Logger); contentTypeRepository = new ContentTypeRepository(accessor, AppCaches.Disabled, Logger, commonRepository, languageRepository); - var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository, Factory.GetInstance()); + var repository = new DocumentRepository(accessor, AppCaches.Disabled, Logger, contentTypeRepository, templateRepository, tagRepository, languageRepository); return repository; } From 4f9e0fcb92e55757b7a0591fa7fa59e4ff257899 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 21 Oct 2019 23:53:14 +1100 Subject: [PATCH 05/15] No more using TemplateUtilities --- .../Implement/ContentRepositoryBase.cs | 3 +- .../PublishedContentTestBase.cs | 5 +- .../PublishedContent/PublishedContentTests.cs | 4 +- .../Web/InternalLinkParserTests.cs | 2 +- .../PropertyEditors/GridPropertyEditor.cs | 16 +- .../PropertyEditors/RichTextPropertyEditor.cs | 41 ++-- .../MarkdownEditorValueConverter.cs | 8 +- .../RteMacroRenderingValueConverter.cs | 13 +- .../TextStringValueConverter.cs | 8 +- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 2 + .../Templates/InternalLinkParser.cs | 14 +- src/Umbraco.Web/Templates/MediaParser.cs | 186 +++++++++++++++ .../Templates/TemplateUtilities.cs | 213 ++---------------- src/Umbraco.Web/Templates/UrlParser.cs | 61 +++++ src/Umbraco.Web/Umbraco.Web.csproj | 2 + src/Umbraco.Web/UmbracoComponentRenderer.cs | 2 +- 16 files changed, 337 insertions(+), 243 deletions(-) create mode 100644 src/Umbraco.Web/Templates/MediaParser.cs create mode 100644 src/Umbraco.Web/Templates/UrlParser.cs diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 7ab73f3f2d51..7ab9f10e1cdf 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -24,6 +24,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement internal sealed class ContentRepositoryBase { /// + /// /// This is used for unit tests ONLY /// public static bool ThrowOnWarning = false; @@ -43,7 +44,7 @@ protected ContentRepositoryBase(IScopeAccessor scopeAccessor, AppCaches cache, I protected ILanguageRepository LanguageRepository { get; } - protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // TODO: inject + protected PropertyEditorCollection PropertyEditors => Current.PropertyEditors; // TODO: inject ... this causes circular refs, not sure which refs they are though #region Versions diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index f1e2bf20d6df..1fa3384d08e4 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -11,6 +11,7 @@ using Umbraco.Web.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Web; +using Umbraco.Web.Templates; namespace Umbraco.Tests.PublishedContent { @@ -38,9 +39,11 @@ protected override void Initialize() base.Initialize(); var converters = Factory.GetInstance(); + var umbracoCtxAccessor = Mock.Of(); + var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new RichTextPropertyEditor(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of())) { Id = 1 }); + new DataType(new RichTextPropertyEditor(logger, umbracoCtxAccessor, new MediaParser(umbracoCtxAccessor, logger, Mock.Of(), Mock.Of()))) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 6ef632bf90dc..d2f7283ee549 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -21,6 +21,7 @@ using Umbraco.Tests.Testing; using Umbraco.Web.Models.PublishedContent; using Umbraco.Web.PropertyEditors; +using Umbraco.Web.Templates; namespace Umbraco.Tests.PublishedContent { @@ -45,11 +46,12 @@ protected override void Compose() var mediaService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); + var mediaParser = new MediaParser(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(logger)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 }, - new DataType(new RichTextPropertyEditor(logger, mediaService, contentTypeBaseServiceProvider, umbracoContextAccessor)) { Id = 1002 }, + new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, mediaParser)) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 }, new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 }); diff --git a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs index 815ce408eeef..6cdff240b8a4 100644 --- a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs +++ b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs @@ -79,7 +79,7 @@ public void ParseLocalLinks(string input, string result) { var linkParser = new InternalLinkParser(umbracoContextAccessor); - var output = linkParser.ParseInternalLinks(input); + var output = linkParser.EnsureInternalLinks(input); Assert.AreEqual(result, output); } diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index f782f0928921..bec28e33fdfb 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -28,14 +28,16 @@ public class GridPropertyEditor : DataEditor private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; + private readonly MediaParser _mediaParser; private ILogger _logger; - public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor) + public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, MediaParser mediaParser) : base(logger) { _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _umbracoContextAccessor = umbracoContextAccessor; + _mediaParser = mediaParser; _logger = logger; } @@ -45,7 +47,7 @@ public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTy /// Overridden to ensure that the value is validated /// /// - protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger); + protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _mediaParser); protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor(); @@ -55,14 +57,16 @@ internal class GridPropertyValueEditor : DataValueEditor private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; private ILogger _logger; + private readonly MediaParser _mediaParser; - public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger) + public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, MediaParser _mediaParser) : base(attribute) { _mediaService = mediaService; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _umbracoContextAccessor = umbracoContextAccessor; _logger = logger; + this._mediaParser = _mediaParser; } /// @@ -97,8 +101,8 @@ public override object FromEditor(ContentPropertyData editorValue, object curren // Parse the HTML var html = rte.Value?.ToString(); - var parseAndSavedTempImages = TemplateUtilities.FindAndPersistPastedTempImages(html, mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); - var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(parseAndSavedTempImages); + var parseAndSavedTempImages = _mediaParser.FindAndPersistPastedTempImages(html, mediaParentId, userId); + var editorValueWithMediaUrlsRemoved = _mediaParser.RemoveImageSources(parseAndSavedTempImages); rte.Value = editorValueWithMediaUrlsRemoved; } @@ -127,7 +131,7 @@ public override object ToEditor(Property property, IDataTypeService dataTypeServ { var html = rte.Value?.ToString(); - var propertyValueWithMediaResolved = TemplateUtilities.ResolveMediaFromTextString(html); + var propertyValueWithMediaResolved = _mediaParser.EnsureImageSources(html); rte.Value = propertyValueWithMediaResolved; } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 3eed40c8bf81..03dc7b669425 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -24,27 +24,24 @@ namespace Umbraco.Web.PropertyEditors Icon = "icon-browser-window")] public class RichTextPropertyEditor : DataEditor { - private IMediaService _mediaService; - private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; - private ILogger _logger; + private readonly MediaParser _mediaParser; /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RichTextPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor) : base(logger) + public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, MediaParser mediaParser) + : base(logger) { - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _umbracoContextAccessor = umbracoContextAccessor; - _logger = logger; + _mediaParser = mediaParser; } /// /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger); + protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _umbracoContextAccessor, _mediaParser); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); @@ -53,20 +50,16 @@ public RichTextPropertyEditor(ILogger logger, IMediaService mediaService, IConte /// /// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for display in the editor /// - internal class RichTextPropertyValueEditor : DataValueEditor + internal class RichTextPropertyValueEditor : DataValueEditor, IDataValueReference { - private IMediaService _mediaService; - private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; - private ILogger _logger; + private readonly MediaParser _mediaParser; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger) + public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, MediaParser _mediaParser) : base(attribute) { - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _umbracoContextAccessor = umbracoContextAccessor; - _logger = logger; + this._mediaParser = _mediaParser; } /// @@ -98,7 +91,7 @@ public override object ToEditor(Property property, IDataTypeService dataTypeServ if (val == null) return null; - var propertyValueWithMediaResolved = TemplateUtilities.ResolveMediaFromTextString(val.ToString()); + var propertyValueWithMediaResolved = _mediaParser.EnsureImageSources(val.ToString()); var parsed = MacroTagParser.FormatRichTextPersistedDataForEditor(propertyValueWithMediaResolved, new Dictionary()); return parsed; } @@ -120,12 +113,22 @@ public override object FromEditor(Core.Models.Editors.ContentPropertyData editor var mediaParent = config?.MediaParentId; var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid; - var parseAndSavedTempImages = TemplateUtilities.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId, _mediaService, _contentTypeBaseServiceProvider, _logger); - var editorValueWithMediaUrlsRemoved = TemplateUtilities.RemoveMediaUrlsFromTextString(parseAndSavedTempImages); + var parseAndSavedTempImages = _mediaParser.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId); + var editorValueWithMediaUrlsRemoved = _mediaParser.RemoveImageSources(parseAndSavedTempImages); var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved); return parsed; } + + /// + /// Resolve references from values + /// + /// + /// + public IEnumerable GetReferences(object value) + { + throw new NotImplementedException(); + } } internal class RichTextPropertyIndexValueFactory : IPropertyIndexValueFactory diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index 98413c7b7088..578a4cad065f 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -13,10 +13,12 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters public class MarkdownEditorValueConverter : PropertyValueConverterBase { private readonly InternalLinkParser _localLinkParser; + private readonly UrlParser _urlResolver; - public MarkdownEditorValueConverter(InternalLinkParser localLinkParser) + public MarkdownEditorValueConverter(InternalLinkParser localLinkParser, UrlParser urlResolver) { _localLinkParser = localLinkParser; + _urlResolver = urlResolver; } public override bool IsConverter(IPublishedPropertyType propertyType) @@ -34,8 +36,8 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = _localLinkParser.ParseInternalLinks(sourceString, preview); - sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); + sourceString = _localLinkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _urlResolver.EnsureUrls(sourceString); return sourceString; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 95cf2cfc855e..88c1429b1600 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -25,6 +25,8 @@ public class RteMacroRenderingValueConverter : TinyMceValueConverter private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; private readonly InternalLinkParser _internalLinkParser; + private readonly UrlParser _urlResolver; + private readonly MediaParser _mediaParser; public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) { @@ -33,11 +35,14 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType return PropertyCacheLevel.Snapshot; } - public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, InternalLinkParser internalLinkParser) + public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, + InternalLinkParser internalLinkParser, UrlParser urlResolver, MediaParser mediaParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; _internalLinkParser = internalLinkParser; + _urlResolver = urlResolver; + _mediaParser = mediaParser; } // NOT thread-safe over a request because it modifies the @@ -83,9 +88,9 @@ private string Convert(object source, bool preview) var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls and media are resolved correctly - sourceString = _internalLinkParser.ParseInternalLinks(sourceString, preview); - sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); - sourceString = TemplateUtilities.ResolveMediaFromTextString(sourceString); + sourceString = _internalLinkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _urlResolver.EnsureUrls(sourceString); + sourceString = _mediaParser.EnsureImageSources(sourceString); // ensure string is parsed for macros and macros are executed correctly sourceString = RenderRteMacros(sourceString, preview); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index ee49536d9d72..1b85d6e608de 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -11,9 +11,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class TextStringValueConverter : PropertyValueConverterBase { - public TextStringValueConverter(InternalLinkParser internalLinkParser) + public TextStringValueConverter(InternalLinkParser internalLinkParser, UrlParser urlParser) { _internalLinkParser = internalLinkParser; + _urlParser = urlParser; } private static readonly string[] PropertyTypeAliases = @@ -22,6 +23,7 @@ public TextStringValueConverter(InternalLinkParser internalLinkParser) Constants.PropertyEditors.Aliases.TextArea }; private readonly InternalLinkParser _internalLinkParser; + private readonly UrlParser _urlParser; public override bool IsConverter(IPublishedPropertyType propertyType) => PropertyTypeAliases.Contains(propertyType.EditorAlias); @@ -38,8 +40,8 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = _internalLinkParser.ParseInternalLinks(sourceString, preview); - sourceString = TemplateUtilities.ResolveUrlsFromTextString(sourceString); + sourceString = _internalLinkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _urlParser.EnsureUrls(sourceString); return sourceString; } diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 82137bbd9d68..1b3128388dcf 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -108,6 +108,8 @@ public override void Compose(Composition composition) composition.RegisterUnique(); composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) diff --git a/src/Umbraco.Web/Templates/InternalLinkParser.cs b/src/Umbraco.Web/Templates/InternalLinkParser.cs index 0d8a480a9048..32d7d42eac3f 100644 --- a/src/Umbraco.Web/Templates/InternalLinkParser.cs +++ b/src/Umbraco.Web/Templates/InternalLinkParser.cs @@ -23,17 +23,23 @@ public InternalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) _umbracoContextAccessor = umbracoContextAccessor; } - public string ParseInternalLinks(string text, bool preview) + /// + /// Parses the string looking for the {localLink} syntax and updates them to their correct links. + /// + /// + /// + /// + public string EnsureInternalLinks(string text, bool preview) { if (_umbracoContextAccessor.UmbracoContext == null) throw new InvalidOperationException("Could not parse internal links, there is no current UmbracoContext"); if (!preview) - return ParseInternalLinks(text); + return EnsureInternalLinks(text); using (_umbracoContextAccessor.UmbracoContext.ForcedPreview(preview)) // force for url provider { - return ParseInternalLinks(text); + return EnsureInternalLinks(text); } } @@ -43,7 +49,7 @@ public string ParseInternalLinks(string text, bool preview) /// /// /// - public string ParseInternalLinks(string text) + public string EnsureInternalLinks(string text) { if (_umbracoContextAccessor.UmbracoContext == null) throw new InvalidOperationException("Could not parse internal links, there is no current UmbracoContext"); diff --git a/src/Umbraco.Web/Templates/MediaParser.cs b/src/Umbraco.Web/Templates/MediaParser.cs new file mode 100644 index 000000000000..9a3f8def3c52 --- /dev/null +++ b/src/Umbraco.Web/Templates/MediaParser.cs @@ -0,0 +1,186 @@ +using HtmlAgilityPack; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using Umbraco.Core; +using Umbraco.Core.Exceptions; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Templates +{ + public sealed class MediaParser + { + public MediaParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + { + _umbracoContextAccessor = umbracoContextAccessor; + _logger = logger; + _mediaService = mediaService; + _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + } + + private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + private readonly IUmbracoContextAccessor _umbracoContextAccessor; + private readonly ILogger _logger; + private readonly IMediaService _mediaService; + private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + const string TemporaryImageDataAttribute = "data-tmpimg"; + + /// + /// Parses the string looking for Umbraco image tags and updates them to their up-to-date image sources. + /// + /// + /// + /// Umbraco image tags are identified by their data-udi attributes + public string EnsureImageSources(string text) + { + // don't attempt to proceed without a context + if (_umbracoContextAccessor?.UmbracoContext?.Media == null) + { + return text; + } + + return ResolveImgPattern.Replace(text, match => + { + // match groups: + // - 1 = from the beginning of the image tag until src attribute value begins + // - 2 = the src attribute value excluding the querystring (if present) + // - 3 = anything after group 2 and before the data-udi attribute value begins + // - 4 = the data-udi attribute value + // - 5 = anything after group 4 until the image tag is closed + var udi = match.Groups[4].Value; + if (udi.IsNullOrWhiteSpace() || GuidUdi.TryParse(udi, out var guidUdi) == false) + { + return match.Value; + } + var media = _umbracoContextAccessor?.UmbracoContext?.Media.GetById(guidUdi.Guid); + if (media == null) + { + // image does not exist - we could choose to remove the image entirely here (return empty string), + // but that would leave the editors completely in the dark as to why the image doesn't show + return match.Value; + } + + var url = media.Url; + return $"{match.Groups[1].Value}{url}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; + }); + } + + /// + /// Removes media urls from <img> tags where a data-udi attribute is present + /// + /// + /// + internal string RemoveImageSources(string text) + // see comment in ResolveMediaFromTextString for group reference + => ResolveImgPattern.Replace(text, "$1$3$4$5"); + + internal string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId) + { + // Find all img's that has data-tmpimg attribute + // Use HTML Agility Pack - https://html-agility-pack.net + var htmlDoc = new HtmlDocument(); + htmlDoc.LoadHtml(html); + + var tmpImages = htmlDoc.DocumentNode.SelectNodes($"//img[@{TemporaryImageDataAttribute}]"); + if (tmpImages == null || tmpImages.Count == 0) + return html; + + // An array to contain a list of URLs that + // we have already processed to avoid dupes + var uploadedImages = new Dictionary(); + + foreach (var img in tmpImages) + { + // The data attribute contains the path to the tmp img to persist as a media item + var tmpImgPath = img.GetAttributeValue(TemporaryImageDataAttribute, string.Empty); + + if (string.IsNullOrEmpty(tmpImgPath)) + continue; + + var absoluteTempImagePath = IOHelper.MapPath(tmpImgPath); + var fileName = Path.GetFileName(absoluteTempImagePath); + var safeFileName = fileName.ToSafeFileName(); + + var mediaItemName = safeFileName.ToFriendlyName(); + IMedia mediaFile; + GuidUdi udi; + + if (uploadedImages.ContainsKey(tmpImgPath) == false) + { + if (mediaParentFolder == Guid.Empty) + mediaFile = _mediaService.CreateMedia(mediaItemName, Constants.System.Root, Constants.Conventions.MediaTypes.Image, userId); + else + mediaFile = _mediaService.CreateMedia(mediaItemName, mediaParentFolder, Constants.Conventions.MediaTypes.Image, userId); + + var fileInfo = new FileInfo(absoluteTempImagePath); + + var fileStream = fileInfo.OpenReadWithRetry(); + if (fileStream == null) throw new InvalidOperationException("Could not acquire file stream"); + using (fileStream) + { + mediaFile.SetValue(_contentTypeBaseServiceProvider, Constants.Conventions.Media.File, safeFileName, fileStream); + } + + _mediaService.Save(mediaFile, userId); + + udi = mediaFile.GetUdi(); + } + else + { + // Already been uploaded & we have it's UDI + udi = uploadedImages[tmpImgPath]; + } + + // Add the UDI to the img element as new data attribute + img.SetAttributeValue("data-udi", udi.ToString()); + + // Get the new persisted image url + var mediaTyped = _umbracoContextAccessor?.UmbracoContext?.Media.GetById(udi.Guid); + if (mediaTyped == null) + throw new PanicException($"Could not find media by id {udi.Guid} or there was no UmbracoContext available."); + + var location = mediaTyped.Url; + + // Find the width & height attributes as we need to set the imageprocessor QueryString + var width = img.GetAttributeValue("width", int.MinValue); + var height = img.GetAttributeValue("height", int.MinValue); + + if (width != int.MinValue && height != int.MinValue) + { + location = $"{location}?width={width}&height={height}&mode=max"; + } + + img.SetAttributeValue("src", location); + + // Remove the data attribute (so we do not re-process this) + img.Attributes.Remove(TemporaryImageDataAttribute); + + // Add to the dictionary to avoid dupes + if (uploadedImages.ContainsKey(tmpImgPath) == false) + { + uploadedImages.Add(tmpImgPath, udi); + + // Delete folder & image now its saved in media + // The folder should contain one image - as a unique guid folder created + // for each image uploaded from TinyMceController + var folderName = Path.GetDirectoryName(absoluteTempImagePath); + try + { + Directory.Delete(folderName, true); + } + catch (Exception ex) + { + _logger.Error(typeof(MediaParser), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); + } + } + } + + return htmlDoc.DocumentNode.OuterHtml; + } + } +} diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index 1092be73e24f..d4bae3814726 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text.RegularExpressions; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Logging; @@ -16,19 +15,9 @@ namespace Umbraco.Web.Templates { - /// - /// Utility class used for templates - /// + [Obsolete("This class is obsolete, all methods have been moved to other classes such as InternalLinkHelper, UrlResolver and MediaParser")] public static class TemplateUtilities { - const string TemporaryImageDataAttribute = "data-tmpimg"; - - private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - - private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", - RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - [Obsolete("Inject and use an instance of InternalLinkParser instead")] internal static string ParseInternalLinks(string text, bool preview, UmbracoContext umbracoContext) { @@ -41,201 +30,27 @@ internal static string ParseInternalLinks(string text, bool preview, UmbracoCont } [Obsolete("Inject and use an instance of InternalLinkParser instead")] - public static string ParseInternalLinks(string text, UrlProvider urlProvider) => - Current.Factory.GetInstance().ParseInternalLinks(text); + public static string ParseInternalLinks(string text, UrlProvider urlProvider) + => Current.Factory.GetInstance().EnsureInternalLinks(text); - /// - /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. - /// - /// - /// - /// - /// When used with a Virtual-Directory set-up, this would resolve all URLs correctly. - /// The recommendation is that the "ResolveUrlsFromTextString" option (in umbracoSettings.config) is set to false for non-Virtual-Directory installs. - /// + [Obsolete("Inject and use an instance of UrlResolver")] public static string ResolveUrlsFromTextString(string text) - { - if (Current.Configs.Settings().Content.ResolveUrlsFromTextString == false) return text; - - using (var timer = Current.ProfilingLogger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) - { - // find all relative urls (ie. urls that contain ~) - var tags = ResolveUrlPattern.Matches(text); - Current.Logger.Debug(typeof(IOHelper), "After regex: {Duration} matched: {TagsCount}", timer.Stopwatch.ElapsedMilliseconds, tags.Count); - foreach (Match tag in tags) - { - var url = ""; - if (tag.Groups[1].Success) - url = tag.Groups[1].Value; - - // The richtext editor inserts a slash in front of the url. That's why we need this little fix - // if (url.StartsWith("/")) - // text = text.Replace(url, ResolveUrl(url.Substring(1))); - // else - if (String.IsNullOrEmpty(url) == false) - { - var resolvedUrl = (url.Substring(0, 1) == "/") ? IOHelper.ResolveUrl(url.Substring(1)) : IOHelper.ResolveUrl(url); - text = text.Replace(url, resolvedUrl); - } - } - } - - return text; - } + => Current.Factory.GetInstance().EnsureUrls(text); + [Obsolete("Use StringExtensions.CleanForXss instead")] public static string CleanForXss(string text, params char[] ignoreFromClean) - { - return text.CleanForXss(ignoreFromClean); - } + => text.CleanForXss(ignoreFromClean); - /// - /// Parses the string looking for Umbraco image tags and updates them to their up-to-date image sources. - /// - /// - /// - /// Umbraco image tags are identified by their data-udi attributes + [Obsolete("Use MediaParser.EnsureImageSources instead")] public static string ResolveMediaFromTextString(string text) - { - // don't attempt to proceed without a context - if (Current.UmbracoContext == null || Current.UmbracoContext.Media == null) - { - return text; - } - - return ResolveImgPattern.Replace(text, match => - { - // match groups: - // - 1 = from the beginning of the image tag until src attribute value begins - // - 2 = the src attribute value excluding the querystring (if present) - // - 3 = anything after group 2 and before the data-udi attribute value begins - // - 4 = the data-udi attribute value - // - 5 = anything after group 4 until the image tag is closed - var udi = match.Groups[4].Value; - if(udi.IsNullOrWhiteSpace() || GuidUdi.TryParse(udi, out var guidUdi) == false) - { - return match.Value; - } - var media = Current.UmbracoContext.Media.GetById(guidUdi.Guid); - if(media == null) - { - // image does not exist - we could choose to remove the image entirely here (return empty string), - // but that would leave the editors completely in the dark as to why the image doesn't show - return match.Value; - } - - var url = media.Url; - return $"{match.Groups[1].Value}{url}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; - }); - } - - /// - /// Removes media urls from <img> tags where a data-udi attribute is present - /// - /// - /// + => Current.Factory.GetInstance().EnsureImageSources(text); + + [Obsolete("Use MediaParser.RemoveImageSources instead")] internal static string RemoveMediaUrlsFromTextString(string text) - // see comment in ResolveMediaFromTextString for group reference - => ResolveImgPattern.Replace(text, "$1$3$4$5"); + => Current.Factory.GetInstance().RemoveImageSources(text); + [Obsolete("Use MediaParser.RemoveImageSources instead")] internal static string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger) - { - // Find all img's that has data-tmpimg attribute - // Use HTML Agility Pack - https://html-agility-pack.net - var htmlDoc = new HtmlDocument(); - htmlDoc.LoadHtml(html); - - var tmpImages = htmlDoc.DocumentNode.SelectNodes($"//img[@{TemporaryImageDataAttribute}]"); - if (tmpImages == null || tmpImages.Count == 0) - return html; - - // An array to contain a list of URLs that - // we have already processed to avoid dupes - var uploadedImages = new Dictionary(); - - foreach (var img in tmpImages) - { - // The data attribute contains the path to the tmp img to persist as a media item - var tmpImgPath = img.GetAttributeValue(TemporaryImageDataAttribute, string.Empty); - - if (string.IsNullOrEmpty(tmpImgPath)) - continue; - - var absoluteTempImagePath = IOHelper.MapPath(tmpImgPath); - var fileName = Path.GetFileName(absoluteTempImagePath); - var safeFileName = fileName.ToSafeFileName(); - - var mediaItemName = safeFileName.ToFriendlyName(); - IMedia mediaFile; - GuidUdi udi; - - if (uploadedImages.ContainsKey(tmpImgPath) == false) - { - if (mediaParentFolder == Guid.Empty) - mediaFile = mediaService.CreateMedia(mediaItemName, Constants.System.Root, Constants.Conventions.MediaTypes.Image, userId); - else - mediaFile = mediaService.CreateMedia(mediaItemName, mediaParentFolder, Constants.Conventions.MediaTypes.Image, userId); - - var fileInfo = new FileInfo(absoluteTempImagePath); - - var fileStream = fileInfo.OpenReadWithRetry(); - if (fileStream == null) throw new InvalidOperationException("Could not acquire file stream"); - using (fileStream) - { - mediaFile.SetValue(contentTypeBaseServiceProvider, Constants.Conventions.Media.File, safeFileName, fileStream); - } - - mediaService.Save(mediaFile, userId); - - udi = mediaFile.GetUdi(); - } - else - { - // Already been uploaded & we have it's UDI - udi = uploadedImages[tmpImgPath]; - } - - // Add the UDI to the img element as new data attribute - img.SetAttributeValue("data-udi", udi.ToString()); - - // Get the new persisted image url - var mediaTyped = Current.UmbracoHelper.Media(udi.Guid); - var location = mediaTyped.Url; - - // Find the width & height attributes as we need to set the imageprocessor QueryString - var width = img.GetAttributeValue("width", int.MinValue); - var height = img.GetAttributeValue("height", int.MinValue); - - if(width != int.MinValue && height != int.MinValue) - { - location = $"{location}?width={width}&height={height}&mode=max"; - } - - img.SetAttributeValue("src", location); - - // Remove the data attribute (so we do not re-process this) - img.Attributes.Remove(TemporaryImageDataAttribute); - - // Add to the dictionary to avoid dupes - if(uploadedImages.ContainsKey(tmpImgPath) == false) - { - uploadedImages.Add(tmpImgPath, udi); - - // Delete folder & image now its saved in media - // The folder should contain one image - as a unique guid folder created - // for each image uploaded from TinyMceController - var folderName = Path.GetDirectoryName(absoluteTempImagePath); - try - { - Directory.Delete(folderName, true); - } - catch (Exception ex) - { - logger.Error(typeof(TemplateUtilities), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); - } - } - } - - return htmlDoc.DocumentNode.OuterHtml; - } + => Current.Factory.GetInstance().FindAndPersistPastedTempImages(html, mediaParentFolder, userId); } } diff --git a/src/Umbraco.Web/Templates/UrlParser.cs b/src/Umbraco.Web/Templates/UrlParser.cs new file mode 100644 index 000000000000..e5c40b73655d --- /dev/null +++ b/src/Umbraco.Web/Templates/UrlParser.cs @@ -0,0 +1,61 @@ +using System.Text.RegularExpressions; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Templates +{ + public sealed class UrlParser + { + private readonly IContentSection _contentSection; + private readonly IProfilingLogger _logger; + + private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", + RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); + + public UrlParser(IContentSection contentSection, IProfilingLogger logger) + { + _contentSection = contentSection; + _logger = logger; + } + + /// + /// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path. + /// + /// + /// + /// + /// When used with a Virtual-Directory set-up, this would resolve all URLs correctly. + /// The recommendation is that the "ResolveUrlsFromTextString" option (in umbracoSettings.config) is set to false for non-Virtual-Directory installs. + /// + public string EnsureUrls(string text) + { + if (_contentSection.ResolveUrlsFromTextString == false) return text; + + using (var timer = _logger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) + { + // find all relative urls (ie. urls that contain ~) + var tags = ResolveUrlPattern.Matches(text); + _logger.Debug(typeof(IOHelper), "After regex: {Duration} matched: {TagsCount}", timer.Stopwatch.ElapsedMilliseconds, tags.Count); + foreach (Match tag in tags) + { + var url = ""; + if (tag.Groups[1].Success) + url = tag.Groups[1].Value; + + // The richtext editor inserts a slash in front of the url. That's why we need this little fix + // if (url.StartsWith("/")) + // text = text.Replace(url, ResolveUrl(url.Substring(1))); + // else + if (string.IsNullOrEmpty(url) == false) + { + var resolvedUrl = (url.Substring(0, 1) == "/") ? IOHelper.ResolveUrl(url.Substring(1)) : IOHelper.ResolveUrl(url); + text = text.Replace(url, resolvedUrl); + } + } + } + + return text; + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 616ed908e104..74ac3f65f397 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -248,6 +248,8 @@ + + diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 805b9267f92e..c0f83fd1af3e 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -159,7 +159,7 @@ private IHtmlString RenderMacro(string alias, IDictionary parame _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType = contentType; //Now, we need to ensure that local links are parsed - html = _internalLinkParser.ParseInternalLinks(output.ToString()); + html = _internalLinkParser.EnsureInternalLinks(output.ToString()); } } From f8e04eb1d624fbefcb39b889a2babeb7bbe255bb Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 00:53:52 +1100 Subject: [PATCH 06/15] Adds tests for MediaParser and simplifies UmbracoContext mocking --- .../PropertyEditors/ImageCropperTest.cs | 1 + .../Testing/Objects/TestDataSource.cs | 3 + .../Objects/TestUmbracoContextFactory.cs | 49 ++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 2 + .../Web/InternalLinkParserTests.cs | 46 ++++----- src/Umbraco.Tests/Web/MediaParserTests.cs | 97 +++++++++++++++++++ src/Umbraco.Web/Templates/MediaParser.cs | 12 ++- 7 files changed, 176 insertions(+), 34 deletions(-) create mode 100644 src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs create mode 100644 src/Umbraco.Tests/Web/MediaParserTests.cs diff --git a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs index 8d2ab84d3524..433ba64b386c 100644 --- a/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs +++ b/src/Umbraco.Tests/PropertyEditors/ImageCropperTest.cs @@ -24,6 +24,7 @@ namespace Umbraco.Tests.PropertyEditors { + [TestFixture] public class ImageCropperTest { diff --git a/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs b/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs index 0291715e460e..4476a7464e10 100644 --- a/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs +++ b/src/Umbraco.Tests/Testing/Objects/TestDataSource.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; +using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Scoping; +using Umbraco.Web; using Umbraco.Web.PublishedCache.NuCache; using Umbraco.Web.PublishedCache.NuCache.DataSource; namespace Umbraco.Tests.Testing.Objects { + internal class TestDataSource : IDataSource { public TestDataSource(params ContentNodeKit[] kits) diff --git a/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs b/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs new file mode 100644 index 000000000000..7f891a258024 --- /dev/null +++ b/src/Umbraco.Tests/Testing/Objects/TestUmbracoContextFactory.cs @@ -0,0 +1,49 @@ +using Moq; +using Umbraco.Core.Configuration; +using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.Services; +using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; + +namespace Umbraco.Tests.Testing.Objects +{ + /// + /// Simplify creating test UmbracoContext's + /// + public class TestUmbracoContextFactory + { + public static IUmbracoContextFactory Create(IGlobalSettings globalSettings = null, IUrlProvider urlProvider = null, + IMediaUrlProvider mediaUrlProvider = null, + IUmbracoContextAccessor umbracoContextAccessor = null) + { + if (globalSettings == null) globalSettings = SettingsForTests.GenerateMockGlobalSettings(); + if (urlProvider == null) urlProvider = Mock.Of(); + if (mediaUrlProvider == null) mediaUrlProvider = Mock.Of(); + if (umbracoContextAccessor == null) umbracoContextAccessor = new TestUmbracoContextAccessor(); + + var contentCache = new Mock(); + var mediaCache = new Mock(); + var snapshot = new Mock(); + snapshot.Setup(x => x.Content).Returns(contentCache.Object); + snapshot.Setup(x => x.Media).Returns(mediaCache.Object); + var snapshotService = new Mock(); + snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot.Object); + + var umbracoContextFactory = new UmbracoContextFactory( + umbracoContextAccessor, + snapshotService.Object, + new TestVariationContextAccessor(), + new TestDefaultCultureAccessor(), + Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), + globalSettings, + new UrlProviderCollection(new[] { urlProvider }), + new MediaUrlProviderCollection(new[] { mediaUrlProvider }), + Mock.Of()); + + return umbracoContextFactory; + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index e73caf45171a..20b29a31471d 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -212,6 +212,7 @@ + @@ -254,6 +255,7 @@ + diff --git a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs index 6cdff240b8a4..0a948a861762 100644 --- a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs +++ b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing.Objects; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web; using Umbraco.Web.PublishedCache; @@ -16,6 +17,7 @@ namespace Umbraco.Tests.Web { + [TestFixture] public class InternalLinkParserTests { @@ -29,54 +31,40 @@ public class InternalLinkParserTests [TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"#\" world ")] public void ParseLocalLinks(string input, string result) { - var serviceCtxMock = new TestObjects(null).GetServiceContextMock(); - //setup a mock url provider which we'll use for testing - var testUrlProvider = new Mock(); - testUrlProvider + var contentUrlProvider = new Mock(); + contentUrlProvider .Setup(x => x.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/my-test-url")); - - var globalSettings = SettingsForTests.GenerateMockGlobalSettings(); - var contentType = new PublishedContentType(666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var publishedContent = new Mock(); publishedContent.Setup(x => x.Id).Returns(1234); publishedContent.Setup(x => x.ContentType).Returns(contentType); - var contentCache = new Mock(); - contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); - contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); + var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); - media.Setup(x => x.Url).Returns("/media/1001/my-image.jpg"); media.Setup(x => x.ContentType).Returns(mediaType); - var mediaCache = new Mock(); - mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var snapshot = new Mock(); - snapshot.Setup(x => x.Content).Returns(contentCache.Object); - snapshot.Setup(x => x.Media).Returns(mediaCache.Object); - var snapshotService = new Mock(); - snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(snapshot.Object); var mediaUrlProvider = new Mock(); mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) .Returns(UrlInfo.Url("/media/1001/my-image.jpg")); var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var umbracoContextFactory = new UmbracoContextFactory( - umbracoContextAccessor, - snapshotService.Object, - new TestVariationContextAccessor(), - new TestDefaultCultureAccessor(), - Mock.Of(section => section.WebRouting == Mock.Of(routingSection => routingSection.UrlProviderMode == "Auto")), - globalSettings, - new UrlProviderCollection(new[] { testUrlProvider.Object }), - new MediaUrlProviderCollection(new[] { mediaUrlProvider.Object }), - Mock.Of()); + var umbracoContextFactory = TestUmbracoContextFactory.Create( + urlProvider: contentUrlProvider.Object, + mediaUrlProvider: mediaUrlProvider.Object, + umbracoContextAccessor: umbracoContextAccessor); using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) { + var contentCache = Mock.Get(reference.UmbracoContext.Content); + contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); + contentCache.Setup(x => x.GetById(It.IsAny())).Returns(publishedContent.Object); + + var mediaCache = Mock.Get(reference.UmbracoContext.Media); + mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); + mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); + var linkParser = new InternalLinkParser(umbracoContextAccessor); var output = linkParser.EnsureInternalLinks(input); diff --git a/src/Umbraco.Tests/Web/MediaParserTests.cs b/src/Umbraco.Tests/Web/MediaParserTests.cs new file mode 100644 index 000000000000..7c9e7576b585 --- /dev/null +++ b/src/Umbraco.Tests/Web/MediaParserTests.cs @@ -0,0 +1,97 @@ +using Umbraco.Core.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Services; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web.Templates; +using Umbraco.Web; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Routing; +using Umbraco.Tests.Testing.Objects; +using System.Web; +using System; +using System.Linq; +using Umbraco.Core.Models; + +namespace Umbraco.Tests.Web +{ + [TestFixture] + public class MediaParserTests + { + [Test] + public void Remove_Image_Sources() + { + var logger = Mock.Of(); + var umbracoContextAccessor = new TestUmbracoContextAccessor(); + var mediaParser = new MediaParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + + var result = mediaParser.RemoveImageSources(@"

+

+ +

+

+

+

"); + + Assert.AreEqual(@"

+

+ +

+

+

+

", result); + } + + [Test] + public void Ensure_Image_Sources() + { + //setup a mock url provider which we'll use for testing + + var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); + var media = new Mock(); + media.Setup(x => x.ContentType).Returns(mediaType); + var mediaUrlProvider = new Mock(); + mediaUrlProvider.Setup(x => x.GetMediaUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(UrlInfo.Url("/media/1001/my-image.jpg")); + + var umbracoContextAccessor = new TestUmbracoContextAccessor(); + + var umbracoContextFactory = TestUmbracoContextFactory.Create( + mediaUrlProvider: mediaUrlProvider.Object, + umbracoContextAccessor: umbracoContextAccessor); + + using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of())) + { + var mediaCache = Mock.Get(reference.UmbracoContext.Media); + mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); + + var mediaParser = new MediaParser(umbracoContextAccessor, Mock.Of(), Mock.Of(), Mock.Of()); + + var result = mediaParser.EnsureImageSources(@"

+

+ +

+

+

+

+

+

+

"); + + Assert.AreEqual(@"

+

+ +

+

+

+

+

+

+

", result); + + } + + + } + } +} diff --git a/src/Umbraco.Web/Templates/MediaParser.cs b/src/Umbraco.Web/Templates/MediaParser.cs index 9a3f8def3c52..4130b21da22b 100644 --- a/src/Umbraco.Web/Templates/MediaParser.cs +++ b/src/Umbraco.Web/Templates/MediaParser.cs @@ -9,6 +9,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; +using Umbraco.Web.Routing; namespace Umbraco.Web.Templates { @@ -39,11 +40,13 @@ public MediaParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logge public string EnsureImageSources(string text) { // don't attempt to proceed without a context - if (_umbracoContextAccessor?.UmbracoContext?.Media == null) + if (_umbracoContextAccessor?.UmbracoContext?.UrlProvider == null) { return text; } + var urlProvider = _umbracoContextAccessor.UmbracoContext.UrlProvider; + return ResolveImgPattern.Replace(text, match => { // match groups: @@ -57,16 +60,15 @@ public string EnsureImageSources(string text) { return match.Value; } - var media = _umbracoContextAccessor?.UmbracoContext?.Media.GetById(guidUdi.Guid); - if (media == null) + var mediaUrl = urlProvider.GetMediaUrl(guidUdi.Guid); + if (mediaUrl == null) { // image does not exist - we could choose to remove the image entirely here (return empty string), // but that would leave the editors completely in the dark as to why the image doesn't show return match.Value; } - var url = media.Url; - return $"{match.Groups[1].Value}{url}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; + return $"{match.Groups[1].Value}{mediaUrl}{match.Groups[3].Value}{udi}{match.Groups[5].Value}"; }); } From bb2a3a5e3dc365b472a2e9f4a896bfd21b7256a3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 11:09:21 +1100 Subject: [PATCH 07/15] stub class for udi parser --- src/Umbraco.Web/Templates/MediaParser.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Umbraco.Web/Templates/MediaParser.cs b/src/Umbraco.Web/Templates/MediaParser.cs index 4130b21da22b..2d20602e8e89 100644 --- a/src/Umbraco.Web/Templates/MediaParser.cs +++ b/src/Umbraco.Web/Templates/MediaParser.cs @@ -13,6 +13,22 @@ namespace Umbraco.Web.Templates { + /// + /// Parses out UDIs in strings + /// + public sealed class UdiParser + { + /// + /// Parses out UDIs from an html string based on 'data-udi' html attributes + /// + /// + /// + public IEnumerable ParseUdisFromDataAttributes(string text) + { + + } + } + public sealed class MediaParser { public MediaParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) From 6bb8a15a878346a9188577a2cca966df1e927bdf Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 15:07:01 +1100 Subject: [PATCH 08/15] Adds UdiParserTests --- .../{Web => Templates}/MediaParserTests.cs | 7 ++-- src/Umbraco.Tests/Templates/UdiParserTests.cs | 28 +++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 3 +- src/Umbraco.Web/Templates/MediaParser.cs | 15 -------- src/Umbraco.Web/Templates/UdiParser.cs | 34 +++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 6 files changed, 69 insertions(+), 19 deletions(-) rename src/Umbraco.Tests/{Web => Templates}/MediaParserTests.cs (98%) create mode 100644 src/Umbraco.Tests/Templates/UdiParserTests.cs create mode 100644 src/Umbraco.Web/Templates/UdiParser.cs diff --git a/src/Umbraco.Tests/Web/MediaParserTests.cs b/src/Umbraco.Tests/Templates/MediaParserTests.cs similarity index 98% rename from src/Umbraco.Tests/Web/MediaParserTests.cs rename to src/Umbraco.Tests/Templates/MediaParserTests.cs index 7c9e7576b585..f7b5933a52c1 100644 --- a/src/Umbraco.Tests/Web/MediaParserTests.cs +++ b/src/Umbraco.Tests/Templates/MediaParserTests.cs @@ -13,8 +13,9 @@ using System.Linq; using Umbraco.Core.Models; -namespace Umbraco.Tests.Web +namespace Umbraco.Tests.Templates { + [TestFixture] public class MediaParserTests { @@ -46,7 +47,7 @@ public void Remove_Image_Sources() public void Ensure_Image_Sources() { //setup a mock url provider which we'll use for testing - + var mediaType = new PublishedContentType(777, "image", PublishedItemType.Media, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); var media = new Mock(); media.Setup(x => x.ContentType).Returns(mediaType); @@ -91,7 +92,7 @@ public void Ensure_Image_Sources() } - + } } } diff --git a/src/Umbraco.Tests/Templates/UdiParserTests.cs b/src/Umbraco.Tests/Templates/UdiParserTests.cs new file mode 100644 index 000000000000..eca7f6c4f02d --- /dev/null +++ b/src/Umbraco.Tests/Templates/UdiParserTests.cs @@ -0,0 +1,28 @@ +using NUnit.Framework; +using Umbraco.Web.Templates; +using System.Linq; +using Umbraco.Core; + +namespace Umbraco.Tests.Templates +{ + [TestFixture] + public class UdiParserTests + { + [Test] + public void Returns_Udi_From_Data_Udi_Html_Attributes() + { + var input = @"

+

+ +
+

"; + + var parser = new UdiParser(); + var result = parser.ParseUdisFromDataAttributes(input).ToList(); + Assert.AreEqual(2, result.Count); + Assert.AreEqual(Udi.Parse("umb://media/D4B18427A1544721B09AC7692F35C264"), result[0]); + Assert.AreEqual(Udi.Parse("umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"), result[1]); + } + + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 20b29a31471d..519f36012c22 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -159,6 +159,7 @@ + @@ -255,7 +256,7 @@ - + diff --git a/src/Umbraco.Web/Templates/MediaParser.cs b/src/Umbraco.Web/Templates/MediaParser.cs index 2d20602e8e89..071c6a56966b 100644 --- a/src/Umbraco.Web/Templates/MediaParser.cs +++ b/src/Umbraco.Web/Templates/MediaParser.cs @@ -13,21 +13,6 @@ namespace Umbraco.Web.Templates { - /// - /// Parses out UDIs in strings - /// - public sealed class UdiParser - { - /// - /// Parses out UDIs from an html string based on 'data-udi' html attributes - /// - /// - /// - public IEnumerable ParseUdisFromDataAttributes(string text) - { - - } - } public sealed class MediaParser { diff --git a/src/Umbraco.Web/Templates/UdiParser.cs b/src/Umbraco.Web/Templates/UdiParser.cs new file mode 100644 index 000000000000..8bb5f8c57971 --- /dev/null +++ b/src/Umbraco.Web/Templates/UdiParser.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using Umbraco.Core; + +namespace Umbraco.Web.Templates +{ + /// + /// Parses out UDIs in strings + /// + public sealed class UdiParser + { + private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')", + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + + /// + /// Parses out UDIs from an html string based on 'data-udi' html attributes + /// + /// + /// + public IEnumerable ParseUdisFromDataAttributes(string text) + { + var matches = DataUdiAttributeRegex.Matches(text); + if (matches.Count == 0) + yield break; + + foreach (Match match in matches) + { + if (match.Groups.Count == 2 && Udi.TryParse(match.Groups[1].Value, out var udi)) + yield return udi; + } + } + } +} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 74ac3f65f397..276cdf9ebc02 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -249,6 +249,7 @@ + From 1adf5a30f361c4d9b779c28af35229c79db1e68c Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 15:48:47 +1100 Subject: [PATCH 09/15] Renames some stuff, updates RTE editor to return media/content refs and adds tests for locallink parsing --- .../PublishedContentTestBase.cs | 2 +- .../PublishedContent/PublishedContentTests.cs | 2 +- ...rserTests.cs => ImageSourceParserTests.cs} | 27 ++++++++- .../Templates/LocalLinkParserTests.cs | 34 +++++++++++ src/Umbraco.Tests/Templates/UdiParserTests.cs | 28 --------- src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +- .../Web/InternalLinkParserTests.cs | 2 +- .../PropertyEditors/GridPropertyEditor.cs | 8 +-- .../PropertyEditors/RichTextPropertyEditor.cs | 12 ++-- .../MarkdownEditorValueConverter.cs | 4 +- .../RteMacroRenderingValueConverter.cs | 6 +- .../TextStringValueConverter.cs | 4 +- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 4 +- .../{MediaParser.cs => ImageSourceParser.cs} | 27 ++++++++- ...ternalLinkParser.cs => LocalLinkParser.cs} | 58 +++++++++++++------ .../Templates/TemplateUtilities.cs | 8 +-- src/Umbraco.Web/Templates/UdiParser.cs | 34 ----------- src/Umbraco.Web/Umbraco.Web.csproj | 5 +- src/Umbraco.Web/UmbracoComponentRenderer.cs | 4 +- 19 files changed, 156 insertions(+), 117 deletions(-) rename src/Umbraco.Tests/Templates/{MediaParserTests.cs => ImageSourceParserTests.cs} (69%) create mode 100644 src/Umbraco.Tests/Templates/LocalLinkParserTests.cs delete mode 100644 src/Umbraco.Tests/Templates/UdiParserTests.cs rename src/Umbraco.Web/Templates/{MediaParser.cs => ImageSourceParser.cs} (86%) rename src/Umbraco.Web/Templates/{InternalLinkParser.cs => LocalLinkParser.cs} (63%) delete mode 100644 src/Umbraco.Web/Templates/UdiParser.cs diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 1fa3384d08e4..fec852f97abd 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -43,7 +43,7 @@ protected override void Initialize() var logger = Mock.Of(); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new RichTextPropertyEditor(logger, umbracoCtxAccessor, new MediaParser(umbracoCtxAccessor, logger, Mock.Of(), Mock.Of()))) { Id = 1 }); + new DataType(new RichTextPropertyEditor(logger, umbracoCtxAccessor, new ImageSourceParser(umbracoCtxAccessor, logger, Mock.Of(), Mock.Of()))) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index d2f7283ee549..95d00998a15a 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -46,7 +46,7 @@ protected override void Compose() var mediaService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); - var mediaParser = new MediaParser(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider); + var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(logger)) { Id = 1 }, diff --git a/src/Umbraco.Tests/Templates/MediaParserTests.cs b/src/Umbraco.Tests/Templates/ImageSourceParserTests.cs similarity index 69% rename from src/Umbraco.Tests/Templates/MediaParserTests.cs rename to src/Umbraco.Tests/Templates/ImageSourceParserTests.cs index f7b5933a52c1..d383ab02dff3 100644 --- a/src/Umbraco.Tests/Templates/MediaParserTests.cs +++ b/src/Umbraco.Tests/Templates/ImageSourceParserTests.cs @@ -12,19 +12,40 @@ using System; using System.Linq; using Umbraco.Core.Models; +using Umbraco.Core; namespace Umbraco.Tests.Templates { + [TestFixture] - public class MediaParserTests + public class ImageSourceParserTests { + [Test] + public void Returns_Udis_From_Data_Udi_Html_Attributes() + { + var input = @"

+

+ +
+

"; + + var logger = Mock.Of(); + var umbracoContextAccessor = new TestUmbracoContextAccessor(); + var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + + var result = mediaParser.FindUdisFromDataAttributes(input).ToList(); + Assert.AreEqual(2, result.Count); + Assert.AreEqual(Udi.Parse("umb://media/D4B18427A1544721B09AC7692F35C264"), result[0]); + Assert.AreEqual(Udi.Parse("umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"), result[1]); + } + [Test] public void Remove_Image_Sources() { var logger = Mock.Of(); var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var mediaParser = new MediaParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); var result = mediaParser.RemoveImageSources(@"

@@ -66,7 +87,7 @@ public void Ensure_Image_Sources() var mediaCache = Mock.Get(reference.UmbracoContext.Media); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var mediaParser = new MediaParser(umbracoContextAccessor, Mock.Of(), Mock.Of(), Mock.Of()); + var mediaParser = new ImageSourceParser(umbracoContextAccessor, Mock.Of(), Mock.Of(), Mock.Of()); var result = mediaParser.EnsureImageSources(@"

diff --git a/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs b/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs new file mode 100644 index 000000000000..30c79b311595 --- /dev/null +++ b/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs @@ -0,0 +1,34 @@ +using NUnit.Framework; +using System.Linq; +using Umbraco.Core; +using Umbraco.Tests.Testing.Objects.Accessors; +using Umbraco.Web.Templates; + +namespace Umbraco.Tests.Templates +{ + [TestFixture] + public class LocalLinkParserTests + { + [Test] + public void Returns_Udis_From_LocalLinks() + { + var input = @"

+

+ + hello +
+

+hello +

"; + + var umbracoContextAccessor = new TestUmbracoContextAccessor(); + var parser = new LocalLinkParser(umbracoContextAccessor); + + var result = parser.FindUdisFromLocalLinks(input).ToList(); + + Assert.AreEqual(2, result.Count); + Assert.AreEqual(Udi.Parse("umb://document/C093961595094900AAF9170DDE6AD442"), result[0]); + Assert.AreEqual(Udi.Parse("umb://document-type/2D692FCB070B4CDA92FB6883FDBFD6E2"), result[1]); + } + } +} diff --git a/src/Umbraco.Tests/Templates/UdiParserTests.cs b/src/Umbraco.Tests/Templates/UdiParserTests.cs deleted file mode 100644 index eca7f6c4f02d..000000000000 --- a/src/Umbraco.Tests/Templates/UdiParserTests.cs +++ /dev/null @@ -1,28 +0,0 @@ -using NUnit.Framework; -using Umbraco.Web.Templates; -using System.Linq; -using Umbraco.Core; - -namespace Umbraco.Tests.Templates -{ - [TestFixture] - public class UdiParserTests - { - [Test] - public void Returns_Udi_From_Data_Udi_Html_Attributes() - { - var input = @"

-

- -
-

"; - - var parser = new UdiParser(); - var result = parser.ParseUdisFromDataAttributes(input).ToList(); - Assert.AreEqual(2, result.Count); - Assert.AreEqual(Udi.Parse("umb://media/D4B18427A1544721B09AC7692F35C264"), result[0]); - Assert.AreEqual(Udi.Parse("umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"), result[1]); - } - - } -} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 519f36012c22..d9583b939322 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -159,7 +159,7 @@ - + @@ -256,7 +256,7 @@ - + diff --git a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs index 0a948a861762..49da8d6566c6 100644 --- a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs +++ b/src/Umbraco.Tests/Web/InternalLinkParserTests.cs @@ -65,7 +65,7 @@ public void ParseLocalLinks(string input, string result) mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var linkParser = new InternalLinkParser(umbracoContextAccessor); + var linkParser = new LocalLinkParser(umbracoContextAccessor); var output = linkParser.EnsureInternalLinks(input); diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index bec28e33fdfb..fa6346fdd86f 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -28,10 +28,10 @@ public class GridPropertyEditor : DataEditor private IMediaService _mediaService; private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; - private readonly MediaParser _mediaParser; + private readonly ImageSourceParser _mediaParser; private ILogger _logger; - public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, MediaParser mediaParser) + public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser) : base(logger) { _mediaService = mediaService; @@ -57,9 +57,9 @@ internal class GridPropertyValueEditor : DataValueEditor private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; private ILogger _logger; - private readonly MediaParser _mediaParser; + private readonly ImageSourceParser _mediaParser; - public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, MediaParser _mediaParser) + public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, ImageSourceParser _mediaParser) : base(attribute) { _mediaService = mediaService; diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 03dc7b669425..4c7df4163c49 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Models; @@ -25,12 +26,13 @@ namespace Umbraco.Web.PropertyEditors public class RichTextPropertyEditor : DataEditor { private IUmbracoContextAccessor _umbracoContextAccessor; - private readonly MediaParser _mediaParser; + private readonly ImageSourceParser _mediaParser; + /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, MediaParser mediaParser) + public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser) : base(logger) { _umbracoContextAccessor = umbracoContextAccessor; @@ -53,9 +55,9 @@ public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoCon internal class RichTextPropertyValueEditor : DataValueEditor, IDataValueReference { private IUmbracoContextAccessor _umbracoContextAccessor; - private readonly MediaParser _mediaParser; + private readonly ImageSourceParser _mediaParser; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, MediaParser _mediaParser) + public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser _mediaParser) : base(attribute) { _umbracoContextAccessor = umbracoContextAccessor; @@ -127,7 +129,7 @@ public override object FromEditor(Core.Models.Editors.ContentPropertyData editor /// public IEnumerable GetReferences(object value) { - throw new NotImplementedException(); + return _mediaParser.FindUdisFromDataAttributes(value == null ? string.Empty : value is string str ? str : value.ToString()).ToList(); } } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index 578a4cad065f..e8a2ac11a63e 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -12,10 +12,10 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { - private readonly InternalLinkParser _localLinkParser; + private readonly LocalLinkParser _localLinkParser; private readonly UrlParser _urlResolver; - public MarkdownEditorValueConverter(InternalLinkParser localLinkParser, UrlParser urlResolver) + public MarkdownEditorValueConverter(LocalLinkParser localLinkParser, UrlParser urlResolver) { _localLinkParser = localLinkParser; _urlResolver = urlResolver; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 88c1429b1600..2caac9e1f483 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -24,9 +24,9 @@ public class RteMacroRenderingValueConverter : TinyMceValueConverter { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; - private readonly InternalLinkParser _internalLinkParser; + private readonly LocalLinkParser _internalLinkParser; private readonly UrlParser _urlResolver; - private readonly MediaParser _mediaParser; + private readonly ImageSourceParser _mediaParser; public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) { @@ -36,7 +36,7 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType } public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, - InternalLinkParser internalLinkParser, UrlParser urlResolver, MediaParser mediaParser) + LocalLinkParser internalLinkParser, UrlParser urlResolver, ImageSourceParser mediaParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 1b85d6e608de..5efc2ee2db07 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class TextStringValueConverter : PropertyValueConverterBase { - public TextStringValueConverter(InternalLinkParser internalLinkParser, UrlParser urlParser) + public TextStringValueConverter(LocalLinkParser internalLinkParser, UrlParser urlParser) { _internalLinkParser = internalLinkParser; _urlParser = urlParser; @@ -22,7 +22,7 @@ public TextStringValueConverter(InternalLinkParser internalLinkParser, UrlParser Constants.PropertyEditors.Aliases.TextBox, Constants.PropertyEditors.Aliases.TextArea }; - private readonly InternalLinkParser _internalLinkParser; + private readonly LocalLinkParser _internalLinkParser; private readonly UrlParser _urlParser; public override bool IsConverter(IPublishedPropertyType propertyType) diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 1b3128388dcf..2f78ac97329b 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -107,9 +107,9 @@ public override void Compose(Composition composition) composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); + composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); + composition.RegisterUnique(); // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) diff --git a/src/Umbraco.Web/Templates/MediaParser.cs b/src/Umbraco.Web/Templates/ImageSourceParser.cs similarity index 86% rename from src/Umbraco.Web/Templates/MediaParser.cs rename to src/Umbraco.Web/Templates/ImageSourceParser.cs index 071c6a56966b..6a0bba499844 100644 --- a/src/Umbraco.Web/Templates/MediaParser.cs +++ b/src/Umbraco.Web/Templates/ImageSourceParser.cs @@ -14,9 +14,9 @@ namespace Umbraco.Web.Templates { - public sealed class MediaParser + public sealed class ImageSourceParser { - public MediaParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + public ImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) { _umbracoContextAccessor = umbracoContextAccessor; _logger = logger; @@ -32,6 +32,27 @@ public MediaParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logge private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; const string TemporaryImageDataAttribute = "data-tmpimg"; + private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')", + RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + + /// + /// Parses out UDIs from an html string based on 'data-udi' html attributes + /// + /// + /// + public IEnumerable FindUdisFromDataAttributes(string text) + { + var matches = DataUdiAttributeRegex.Matches(text); + if (matches.Count == 0) + yield break; + + foreach (Match match in matches) + { + if (match.Groups.Count == 2 && Udi.TryParse(match.Groups[1].Value, out var udi)) + yield return udi; + } + } + /// /// Parses the string looking for Umbraco image tags and updates them to their up-to-date image sources. /// @@ -178,7 +199,7 @@ internal string FindAndPersistPastedTempImages(string html, Guid mediaParentFold } catch (Exception ex) { - _logger.Error(typeof(MediaParser), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); + _logger.Error(typeof(ImageSourceParser), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); } } } diff --git a/src/Umbraco.Web/Templates/InternalLinkParser.cs b/src/Umbraco.Web/Templates/LocalLinkParser.cs similarity index 63% rename from src/Umbraco.Web/Templates/InternalLinkParser.cs rename to src/Umbraco.Web/Templates/LocalLinkParser.cs index 32d7d42eac3f..f394f56d854e 100644 --- a/src/Umbraco.Web/Templates/InternalLinkParser.cs +++ b/src/Umbraco.Web/Templates/LocalLinkParser.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using Umbraco.Core; using Umbraco.Core.Logging; @@ -10,7 +11,7 @@ namespace Umbraco.Web.Templates /// /// Utility class used to parse internal links /// - public sealed class InternalLinkParser + public sealed class LocalLinkParser { private static readonly Regex LocalLinkPattern = new Regex(@"href=""[/]?(?:\{|\%7B)localLink:([a-zA-Z0-9-://]+)(?:\}|\%7D)", @@ -18,11 +19,20 @@ public sealed class InternalLinkParser private readonly IUmbracoContextAccessor _umbracoContextAccessor; - public InternalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) + public LocalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) { _umbracoContextAccessor = umbracoContextAccessor; } + internal IEnumerable FindUdisFromLocalLinks(string text) + { + foreach ((int? intId, GuidUdi udi, string tagValue) in FindLocalLinkIds(text)) + { + if (udi != null) + yield return udi; // In v8, we only care abuot UDIs + } + } + /// /// Parses the string looking for the {localLink} syntax and updates them to their correct links. /// @@ -56,6 +66,33 @@ public string EnsureInternalLinks(string text) var urlProvider = _umbracoContextAccessor.UmbracoContext.UrlProvider; + foreach((int? intId, GuidUdi udi, string tagValue) in FindLocalLinkIds(text)) + { + if (udi != null) + { + var newLink = "#"; + if (udi.EntityType == Constants.UdiEntityType.Document) + newLink = urlProvider.GetUrl(udi.Guid); + else if (udi.EntityType == Constants.UdiEntityType.Media) + newLink = urlProvider.GetMediaUrl(udi.Guid); + + if (newLink == null) + newLink = "#"; + + text = text.Replace(tagValue, "href=\"" + newLink); + } + else if (intId.HasValue) + { + var newLink = urlProvider.GetUrl(intId.Value); + text = text.Replace(tagValue, "href=\"" + newLink); + } + } + + return text; + } + + private IEnumerable<(int? intId, GuidUdi udi, string tagValue)> FindLocalLinkIds(string text) + { // Parse internal links var tags = LocalLinkPattern.Matches(text); foreach (Match tag in tags) @@ -69,29 +106,16 @@ public string EnsureInternalLinks(string text) { var guidUdi = udi as GuidUdi; if (guidUdi != null) - { - var newLink = "#"; - if (guidUdi.EntityType == Constants.UdiEntityType.Document) - newLink = urlProvider.GetUrl(guidUdi.Guid); - else if (guidUdi.EntityType == Constants.UdiEntityType.Media) - newLink = urlProvider.GetMediaUrl(guidUdi.Guid); - - if (newLink == null) - newLink = "#"; - - text = text.Replace(tag.Value, "href=\"" + newLink); - } + yield return (null, guidUdi, tag.Value); } if (int.TryParse(id, out var intId)) { - var newLink = urlProvider.GetUrl(intId); - text = text.Replace(tag.Value, "href=\"" + newLink); + yield return (intId, null, tag.Value); } } } - return text; } } } diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index d4bae3814726..db0366dbf3d5 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -31,7 +31,7 @@ internal static string ParseInternalLinks(string text, bool preview, UmbracoCont [Obsolete("Inject and use an instance of InternalLinkParser instead")] public static string ParseInternalLinks(string text, UrlProvider urlProvider) - => Current.Factory.GetInstance().EnsureInternalLinks(text); + => Current.Factory.GetInstance().EnsureInternalLinks(text); [Obsolete("Inject and use an instance of UrlResolver")] public static string ResolveUrlsFromTextString(string text) @@ -43,14 +43,14 @@ public static string CleanForXss(string text, params char[] ignoreFromClean) [Obsolete("Use MediaParser.EnsureImageSources instead")] public static string ResolveMediaFromTextString(string text) - => Current.Factory.GetInstance().EnsureImageSources(text); + => Current.Factory.GetInstance().EnsureImageSources(text); [Obsolete("Use MediaParser.RemoveImageSources instead")] internal static string RemoveMediaUrlsFromTextString(string text) - => Current.Factory.GetInstance().RemoveImageSources(text); + => Current.Factory.GetInstance().RemoveImageSources(text); [Obsolete("Use MediaParser.RemoveImageSources instead")] internal static string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger) - => Current.Factory.GetInstance().FindAndPersistPastedTempImages(html, mediaParentFolder, userId); + => Current.Factory.GetInstance().FindAndPersistPastedTempImages(html, mediaParentFolder, userId); } } diff --git a/src/Umbraco.Web/Templates/UdiParser.cs b/src/Umbraco.Web/Templates/UdiParser.cs deleted file mode 100644 index 8bb5f8c57971..000000000000 --- a/src/Umbraco.Web/Templates/UdiParser.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using Umbraco.Core; - -namespace Umbraco.Web.Templates -{ - /// - /// Parses out UDIs in strings - /// - public sealed class UdiParser - { - private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')", - RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); - - /// - /// Parses out UDIs from an html string based on 'data-udi' html attributes - /// - /// - /// - public IEnumerable ParseUdisFromDataAttributes(string text) - { - var matches = DataUdiAttributeRegex.Matches(text); - if (matches.Count == 0) - yield break; - - foreach (Match match in matches) - { - if (match.Groups.Count == 2 && Udi.TryParse(match.Groups[1].Value, out var udi)) - yield return udi; - } - } - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 276cdf9ebc02..8fc06c75c273 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -247,9 +247,8 @@ - - - + + diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index c0f83fd1af3e..83c8a7f0faa1 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -27,9 +27,9 @@ internal class UmbracoComponentRenderer : IUmbracoComponentRenderer private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; private readonly ITemplateRenderer _templateRenderer; - private readonly InternalLinkParser _internalLinkParser; + private readonly LocalLinkParser _internalLinkParser; - public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer, InternalLinkParser internalLinkParser) + public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer, LocalLinkParser internalLinkParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; From aee3b9f9d28b810e893cfd73ad4c0c35504f5371 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 15:52:09 +1100 Subject: [PATCH 10/15] updates rte editor to return local link udis --- .../PropertyEditors/RichTextPropertyEditor.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 4c7df4163c49..8cb9536182e1 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -56,12 +56,14 @@ internal class RichTextPropertyValueEditor : DataValueEditor, IDataValueReferenc { private IUmbracoContextAccessor _umbracoContextAccessor; private readonly ImageSourceParser _mediaParser; + private readonly LocalLinkParser _localLinkParser; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser _mediaParser) + public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser, LocalLinkParser localLinkParser) : base(attribute) { _umbracoContextAccessor = umbracoContextAccessor; - this._mediaParser = _mediaParser; + _mediaParser = mediaParser; + _localLinkParser = localLinkParser; } /// @@ -129,7 +131,15 @@ public override object FromEditor(Core.Models.Editors.ContentPropertyData editor /// public IEnumerable GetReferences(object value) { - return _mediaParser.FindUdisFromDataAttributes(value == null ? string.Empty : value is string str ? str : value.ToString()).ToList(); + var asString = value == null ? string.Empty : value is string str ? str : value.ToString(); + + foreach (var udi in _mediaParser.FindUdisFromDataAttributes(asString)) + yield return udi; + + foreach (var udi in _localLinkParser.FindUdisFromLocalLinks(asString)) + yield return udi; + + //TODO: Detect Macros too ... but we can save that for a later date, right now need to do media refs } } From 611ba7de46e9aa4c113ae6e1c081a7a994b594f7 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 22 Oct 2019 16:12:31 +1100 Subject: [PATCH 11/15] fix build --- src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 8cb9536182e1..2b3e7499534e 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -27,23 +27,25 @@ public class RichTextPropertyEditor : DataEditor { private IUmbracoContextAccessor _umbracoContextAccessor; private readonly ImageSourceParser _mediaParser; - + private readonly LocalLinkParser _localLinkParser; + /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser) + public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser, LocalLinkParser localLinkParser) : base(logger) { _umbracoContextAccessor = umbracoContextAccessor; _mediaParser = mediaParser; + _localLinkParser = localLinkParser; } /// /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _umbracoContextAccessor, _mediaParser); + protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _umbracoContextAccessor, _mediaParser, _localLinkParser); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); From 832803a9f64d7ab3a8f6dbb2b94f59f75f7f3318 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Oct 2019 14:35:43 +1100 Subject: [PATCH 12/15] fix build --- .../PublishedContent/PublishedContentTestBase.cs | 6 ++++-- src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index fec852f97abd..6c68fecdd290 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -39,11 +39,13 @@ protected override void Initialize() base.Initialize(); var converters = Factory.GetInstance(); - var umbracoCtxAccessor = Mock.Of(); + var umbracoContextAccessor = Mock.Of(); var logger = Mock.Of(); + var imageSourceParser = new ImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + var localLinkParser = new LocalLinkParser(umbracoContextAccessor); var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new RichTextPropertyEditor(logger, umbracoCtxAccessor, new ImageSourceParser(umbracoCtxAccessor, logger, Mock.Of(), Mock.Of()))) { Id = 1 }); + new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, localLinkParser)) { Id = 1 }); var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 95d00998a15a..333f3ca7c04c 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -47,11 +47,12 @@ protected override void Compose() var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider); + var localLinkParser = new LocalLinkParser(umbracoContextAccessor); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(logger)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 }, - new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, mediaParser)) { Id = 1002 }, + new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, mediaParser, localLinkParser)) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 }, new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 }); From 17fd09fe3df0576dc6823cf3fa8d2d9d695a0912 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Oct 2019 14:55:18 +1100 Subject: [PATCH 13/15] Naming --- .../PublishedContentTestBase.cs | 4 +-- .../PublishedContent/PublishedContentTests.cs | 6 ++-- .../Templates/ImageSourceParserTests.cs | 12 +++---- .../Templates/LocalLinkParserTests.cs | 2 +- src/Umbraco.Tests/Umbraco.Tests.csproj | 2 +- ...erTests.cs => HtmlLocalLinkParserTests.cs} | 4 +-- .../PropertyEditors/GridPropertyEditor.cs | 32 ++++++------------- .../PropertyEditors/RichTextPropertyEditor.cs | 26 +++++++-------- .../MarkdownEditorValueConverter.cs | 10 +++--- .../RteMacroRenderingValueConverter.cs | 20 ++++++------ .../TextStringValueConverter.cs | 10 +++--- src/Umbraco.Web/Runtime/WebInitialComposer.cs | 6 ++-- ...urceParser.cs => HtmlImageSourceParser.cs} | 15 ++++++--- ...alLinkParser.cs => HtmlLocalLinkParser.cs} | 4 +-- .../{UrlParser.cs => HtmlUrlParser.cs} | 4 +-- .../Templates/TemplateUtilities.cs | 24 +++++++------- src/Umbraco.Web/Umbraco.Web.csproj | 6 ++-- src/Umbraco.Web/UmbracoComponentRenderer.cs | 8 ++--- 18 files changed, 95 insertions(+), 100 deletions(-) rename src/Umbraco.Tests/Web/{InternalLinkParserTests.cs => HtmlLocalLinkParserTests.cs} (97%) rename src/Umbraco.Web/Templates/{ImageSourceParser.cs => HtmlImageSourceParser.cs} (92%) rename src/Umbraco.Web/Templates/{LocalLinkParser.cs => HtmlLocalLinkParser.cs} (97%) rename src/Umbraco.Web/Templates/{UrlParser.cs => HtmlUrlParser.cs} (95%) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs index 6c68fecdd290..497c62196363 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs @@ -42,8 +42,8 @@ protected override void Initialize() var umbracoContextAccessor = Mock.Of(); var logger = Mock.Of(); - var imageSourceParser = new ImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); - var localLinkParser = new LocalLinkParser(umbracoContextAccessor); + var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, localLinkParser)) { Id = 1 }); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 333f3ca7c04c..9f5dc8198641 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -46,13 +46,13 @@ protected override void Compose() var mediaService = Mock.Of(); var contentTypeBaseServiceProvider = Mock.Of(); var umbracoContextAccessor = Mock.Of(); - var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider); - var localLinkParser = new LocalLinkParser(umbracoContextAccessor); + var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor, logger, mediaService, contentTypeBaseServiceProvider); + var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor); var dataTypeService = new TestObjects.TestDataTypeService( new DataType(new VoidEditor(logger)) { Id = 1 }, new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 }, - new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, mediaParser, localLinkParser)) { Id = 1002 }, + new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, linkParser)) { Id = 1002 }, new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 }, new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 }, new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 }); diff --git a/src/Umbraco.Tests/Templates/ImageSourceParserTests.cs b/src/Umbraco.Tests/Templates/ImageSourceParserTests.cs index d383ab02dff3..ca01caf34481 100644 --- a/src/Umbraco.Tests/Templates/ImageSourceParserTests.cs +++ b/src/Umbraco.Tests/Templates/ImageSourceParserTests.cs @@ -32,9 +32,9 @@ public void Returns_Udis_From_Data_Udi_Html_Attributes() var logger = Mock.Of(); var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); - var result = mediaParser.FindUdisFromDataAttributes(input).ToList(); + var result = imageSourceParser.FindUdisFromDataAttributes(input).ToList(); Assert.AreEqual(2, result.Count); Assert.AreEqual(Udi.Parse("umb://media/D4B18427A1544721B09AC7692F35C264"), result[0]); Assert.AreEqual(Udi.Parse("umb://media-type/B726D735E4C446D58F703F3FBCFC97A5"), result[1]); @@ -45,9 +45,9 @@ public void Remove_Image_Sources() { var logger = Mock.Of(); var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var mediaParser = new ImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); + var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor, logger, Mock.Of(), Mock.Of()); - var result = mediaParser.RemoveImageSources(@"

+ var result = imageSourceParser.RemoveImageSources(@"

@@ -87,9 +87,9 @@ public void Ensure_Image_Sources() var mediaCache = Mock.Get(reference.UmbracoContext.Media); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var mediaParser = new ImageSourceParser(umbracoContextAccessor, Mock.Of(), Mock.Of(), Mock.Of()); + var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor, Mock.Of(), Mock.Of(), Mock.Of()); - var result = mediaParser.EnsureImageSources(@"

+ var result = imageSourceParser.EnsureImageSources(@"

diff --git a/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs b/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs index 30c79b311595..e09d71196ebc 100644 --- a/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs +++ b/src/Umbraco.Tests/Templates/LocalLinkParserTests.cs @@ -22,7 +22,7 @@ public void Returns_Udis_From_LocalLinks()

"; var umbracoContextAccessor = new TestUmbracoContextAccessor(); - var parser = new LocalLinkParser(umbracoContextAccessor); + var parser = new HtmlLocalLinkParser(umbracoContextAccessor); var result = parser.FindUdisFromLocalLinks(input).ToList(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index d9583b939322..fa654ad4a49d 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -255,7 +255,7 @@ - + diff --git a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs b/src/Umbraco.Tests/Web/HtmlLocalLinkParserTests.cs similarity index 97% rename from src/Umbraco.Tests/Web/InternalLinkParserTests.cs rename to src/Umbraco.Tests/Web/HtmlLocalLinkParserTests.cs index 49da8d6566c6..e6a0abeb4c4d 100644 --- a/src/Umbraco.Tests/Web/InternalLinkParserTests.cs +++ b/src/Umbraco.Tests/Web/HtmlLocalLinkParserTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Web { [TestFixture] - public class InternalLinkParserTests + public class HtmlLocalLinkParserTests { [TestCase("", "")] [TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")] @@ -65,7 +65,7 @@ public void ParseLocalLinks(string input, string result) mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); mediaCache.Setup(x => x.GetById(It.IsAny())).Returns(media.Object); - var linkParser = new LocalLinkParser(umbracoContextAccessor); + var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor); var output = linkParser.EnsureInternalLinks(input); diff --git a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs index fa6346fdd86f..6481099f45e5 100644 --- a/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs @@ -25,20 +25,14 @@ namespace Umbraco.Web.PropertyEditors Group = Constants.PropertyEditors.Groups.RichContent)] public class GridPropertyEditor : DataEditor { - private IMediaService _mediaService; - private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; - private readonly ImageSourceParser _mediaParser; - private ILogger _logger; + private readonly HtmlImageSourceParser _imageSourceParser; - public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser) + public GridPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, HtmlImageSourceParser imageSourceParser) : base(logger) { - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _umbracoContextAccessor = umbracoContextAccessor; - _mediaParser = mediaParser; - _logger = logger; + _imageSourceParser = imageSourceParser; } public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory(); @@ -47,26 +41,20 @@ public GridPropertyEditor(ILogger logger, IMediaService mediaService, IContentTy /// Overridden to ensure that the value is validated ///
/// - protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _mediaService, _contentTypeBaseServiceProvider, _umbracoContextAccessor, _logger, _mediaParser); + protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute, _umbracoContextAccessor, _imageSourceParser); protected override IConfigurationEditor CreateConfigurationEditor() => new GridConfigurationEditor(); internal class GridPropertyValueEditor : DataValueEditor { - private IMediaService _mediaService; - private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private IUmbracoContextAccessor _umbracoContextAccessor; - private ILogger _logger; - private readonly ImageSourceParser _mediaParser; + private readonly HtmlImageSourceParser _imageSourceParser; - public GridPropertyValueEditor(DataEditorAttribute attribute, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, ImageSourceParser _mediaParser) + public GridPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, HtmlImageSourceParser imageSourceParser) : base(attribute) { - _mediaService = mediaService; - _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; _umbracoContextAccessor = umbracoContextAccessor; - _logger = logger; - this._mediaParser = _mediaParser; + _imageSourceParser = imageSourceParser; } /// @@ -101,8 +89,8 @@ public override object FromEditor(ContentPropertyData editorValue, object curren // Parse the HTML var html = rte.Value?.ToString(); - var parseAndSavedTempImages = _mediaParser.FindAndPersistPastedTempImages(html, mediaParentId, userId); - var editorValueWithMediaUrlsRemoved = _mediaParser.RemoveImageSources(parseAndSavedTempImages); + var parseAndSavedTempImages = _imageSourceParser.FindAndPersistPastedTempImages(html, mediaParentId, userId); + var editorValueWithMediaUrlsRemoved = _imageSourceParser.RemoveImageSources(parseAndSavedTempImages); rte.Value = editorValueWithMediaUrlsRemoved; } @@ -131,7 +119,7 @@ public override object ToEditor(Property property, IDataTypeService dataTypeServ { var html = rte.Value?.ToString(); - var propertyValueWithMediaResolved = _mediaParser.EnsureImageSources(html); + var propertyValueWithMediaResolved = _imageSourceParser.EnsureImageSources(html); rte.Value = propertyValueWithMediaResolved; } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 2b3e7499534e..0dbe5426a29a 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -26,18 +26,18 @@ namespace Umbraco.Web.PropertyEditors public class RichTextPropertyEditor : DataEditor { private IUmbracoContextAccessor _umbracoContextAccessor; - private readonly ImageSourceParser _mediaParser; - private readonly LocalLinkParser _localLinkParser; + private readonly HtmlImageSourceParser _imageSourceParser; + private readonly HtmlLocalLinkParser _localLinkParser; /// /// The constructor will setup the property editor based on the attribute if one is found /// - public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser, LocalLinkParser localLinkParser) + public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser) : base(logger) { _umbracoContextAccessor = umbracoContextAccessor; - _mediaParser = mediaParser; + _imageSourceParser = imageSourceParser; _localLinkParser = localLinkParser; } @@ -45,7 +45,7 @@ public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoCon /// Create a custom value editor /// /// - protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _umbracoContextAccessor, _mediaParser, _localLinkParser); + protected override IDataValueEditor CreateValueEditor() => new RichTextPropertyValueEditor(Attribute, _umbracoContextAccessor, _imageSourceParser, _localLinkParser); protected override IConfigurationEditor CreateConfigurationEditor() => new RichTextConfigurationEditor(); @@ -57,14 +57,14 @@ public RichTextPropertyEditor(ILogger logger, IUmbracoContextAccessor umbracoCon internal class RichTextPropertyValueEditor : DataValueEditor, IDataValueReference { private IUmbracoContextAccessor _umbracoContextAccessor; - private readonly ImageSourceParser _mediaParser; - private readonly LocalLinkParser _localLinkParser; + private readonly HtmlImageSourceParser _imageSourceParser; + private readonly HtmlLocalLinkParser _localLinkParser; - public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, ImageSourceParser mediaParser, LocalLinkParser localLinkParser) + public RichTextPropertyValueEditor(DataEditorAttribute attribute, IUmbracoContextAccessor umbracoContextAccessor, HtmlImageSourceParser imageSourceParser, HtmlLocalLinkParser localLinkParser) : base(attribute) { _umbracoContextAccessor = umbracoContextAccessor; - _mediaParser = mediaParser; + _imageSourceParser = imageSourceParser; _localLinkParser = localLinkParser; } @@ -97,7 +97,7 @@ public override object ToEditor(Property property, IDataTypeService dataTypeServ if (val == null) return null; - var propertyValueWithMediaResolved = _mediaParser.EnsureImageSources(val.ToString()); + var propertyValueWithMediaResolved = _imageSourceParser.EnsureImageSources(val.ToString()); var parsed = MacroTagParser.FormatRichTextPersistedDataForEditor(propertyValueWithMediaResolved, new Dictionary()); return parsed; } @@ -119,8 +119,8 @@ public override object FromEditor(Core.Models.Editors.ContentPropertyData editor var mediaParent = config?.MediaParentId; var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid; - var parseAndSavedTempImages = _mediaParser.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId); - var editorValueWithMediaUrlsRemoved = _mediaParser.RemoveImageSources(parseAndSavedTempImages); + var parseAndSavedTempImages = _imageSourceParser.FindAndPersistPastedTempImages(editorValue.Value.ToString(), mediaParentId, userId); + var editorValueWithMediaUrlsRemoved = _imageSourceParser.RemoveImageSources(parseAndSavedTempImages); var parsed = MacroTagParser.FormatRichTextContentForPersistence(editorValueWithMediaUrlsRemoved); return parsed; @@ -135,7 +135,7 @@ public IEnumerable GetReferences(object value) { var asString = value == null ? string.Empty : value is string str ? str : value.ToString(); - foreach (var udi in _mediaParser.FindUdisFromDataAttributes(asString)) + foreach (var udi in _imageSourceParser.FindUdisFromDataAttributes(asString)) yield return udi; foreach (var udi in _localLinkParser.FindUdisFromLocalLinks(asString)) diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index e8a2ac11a63e..c62a79d283e8 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -12,13 +12,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { - private readonly LocalLinkParser _localLinkParser; - private readonly UrlParser _urlResolver; + private readonly HtmlLocalLinkParser _localLinkParser; + private readonly HtmlUrlParser _urlParser; - public MarkdownEditorValueConverter(LocalLinkParser localLinkParser, UrlParser urlResolver) + public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser) { _localLinkParser = localLinkParser; - _urlResolver = urlResolver; + _urlParser = urlParser; } public override bool IsConverter(IPublishedPropertyType propertyType) @@ -37,7 +37,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub // ensures string is parsed for {localLink} and urls are resolved correctly sourceString = _localLinkParser.EnsureInternalLinks(sourceString, preview); - sourceString = _urlResolver.EnsureUrls(sourceString); + sourceString = _urlParser.EnsureUrls(sourceString); return sourceString; } diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs index 2caac9e1f483..3ab502742cc5 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs @@ -24,9 +24,9 @@ public class RteMacroRenderingValueConverter : TinyMceValueConverter { private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; - private readonly LocalLinkParser _internalLinkParser; - private readonly UrlParser _urlResolver; - private readonly ImageSourceParser _mediaParser; + private readonly HtmlLocalLinkParser _linkParser; + private readonly HtmlUrlParser _urlParser; + private readonly HtmlImageSourceParser _imageSourceParser; public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) { @@ -36,13 +36,13 @@ public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType } public RteMacroRenderingValueConverter(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, - LocalLinkParser internalLinkParser, UrlParser urlResolver, ImageSourceParser mediaParser) + HtmlLocalLinkParser linkParser, HtmlUrlParser urlParser, HtmlImageSourceParser imageSourceParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; - _internalLinkParser = internalLinkParser; - _urlResolver = urlResolver; - _mediaParser = mediaParser; + _linkParser = linkParser; + _urlParser = urlParser; + _imageSourceParser = imageSourceParser; } // NOT thread-safe over a request because it modifies the @@ -88,9 +88,9 @@ private string Convert(object source, bool preview) var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls and media are resolved correctly - sourceString = _internalLinkParser.EnsureInternalLinks(sourceString, preview); - sourceString = _urlResolver.EnsureUrls(sourceString); - sourceString = _mediaParser.EnsureImageSources(sourceString); + sourceString = _linkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _urlParser.EnsureUrls(sourceString); + sourceString = _imageSourceParser.EnsureImageSources(sourceString); // ensure string is parsed for macros and macros are executed correctly sourceString = RenderRteMacros(sourceString, preview); diff --git a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs index 5efc2ee2db07..939a658407d8 100644 --- a/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs +++ b/src/Umbraco.Web/PropertyEditors/ValueConverters/TextStringValueConverter.cs @@ -11,9 +11,9 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters [DefaultPropertyValueConverter] public class TextStringValueConverter : PropertyValueConverterBase { - public TextStringValueConverter(LocalLinkParser internalLinkParser, UrlParser urlParser) + public TextStringValueConverter(HtmlLocalLinkParser linkParser, HtmlUrlParser urlParser) { - _internalLinkParser = internalLinkParser; + _linkParser = linkParser; _urlParser = urlParser; } @@ -22,8 +22,8 @@ public TextStringValueConverter(LocalLinkParser internalLinkParser, UrlParser ur Constants.PropertyEditors.Aliases.TextBox, Constants.PropertyEditors.Aliases.TextArea }; - private readonly LocalLinkParser _internalLinkParser; - private readonly UrlParser _urlParser; + private readonly HtmlLocalLinkParser _linkParser; + private readonly HtmlUrlParser _urlParser; public override bool IsConverter(IPublishedPropertyType propertyType) => PropertyTypeAliases.Contains(propertyType.EditorAlias); @@ -40,7 +40,7 @@ public override object ConvertSourceToIntermediate(IPublishedElement owner, IPub var sourceString = source.ToString(); // ensures string is parsed for {localLink} and urls are resolved correctly - sourceString = _internalLinkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _linkParser.EnsureInternalLinks(sourceString, preview); sourceString = _urlParser.EnsureUrls(sourceString); return sourceString; diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs index 2f78ac97329b..5ccb16a1a506 100644 --- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs +++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs @@ -107,9 +107,9 @@ public override void Compose(Composition composition) composition.RegisterUnique(); composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); - composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(); // register the umbraco helper - this is Transient! very important! // also, if not level.Run, we cannot really use the helper (during upgrade...) diff --git a/src/Umbraco.Web/Templates/ImageSourceParser.cs b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs similarity index 92% rename from src/Umbraco.Web/Templates/ImageSourceParser.cs rename to src/Umbraco.Web/Templates/HtmlImageSourceParser.cs index 6a0bba499844..b36542167c3b 100644 --- a/src/Umbraco.Web/Templates/ImageSourceParser.cs +++ b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs @@ -14,9 +14,9 @@ namespace Umbraco.Web.Templates { - public sealed class ImageSourceParser + public sealed class HtmlImageSourceParser { - public ImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) + public HtmlImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider) { _umbracoContextAccessor = umbracoContextAccessor; _logger = logger; @@ -99,10 +99,17 @@ public string EnsureImageSources(string text) ///
/// /// - internal string RemoveImageSources(string text) + public string RemoveImageSources(string text) // see comment in ResolveMediaFromTextString for group reference => ResolveImgPattern.Replace(text, "$1$3$4$5"); + /// + /// Used by the RTE (and grid RTE) for drag/drop/persisting images + /// + /// + /// + /// + /// internal string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId) { // Find all img's that has data-tmpimg attribute @@ -199,7 +206,7 @@ internal string FindAndPersistPastedTempImages(string html, Guid mediaParentFold } catch (Exception ex) { - _logger.Error(typeof(ImageSourceParser), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); + _logger.Error(typeof(HtmlImageSourceParser), ex, "Could not delete temp file or folder {FileName}", absoluteTempImagePath); } } } diff --git a/src/Umbraco.Web/Templates/LocalLinkParser.cs b/src/Umbraco.Web/Templates/HtmlLocalLinkParser.cs similarity index 97% rename from src/Umbraco.Web/Templates/LocalLinkParser.cs rename to src/Umbraco.Web/Templates/HtmlLocalLinkParser.cs index f394f56d854e..f65a7183b751 100644 --- a/src/Umbraco.Web/Templates/LocalLinkParser.cs +++ b/src/Umbraco.Web/Templates/HtmlLocalLinkParser.cs @@ -11,7 +11,7 @@ namespace Umbraco.Web.Templates /// /// Utility class used to parse internal links /// - public sealed class LocalLinkParser + public sealed class HtmlLocalLinkParser { private static readonly Regex LocalLinkPattern = new Regex(@"href=""[/]?(?:\{|\%7B)localLink:([a-zA-Z0-9-://]+)(?:\}|\%7D)", @@ -19,7 +19,7 @@ public sealed class LocalLinkParser private readonly IUmbracoContextAccessor _umbracoContextAccessor; - public LocalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) + public HtmlLocalLinkParser(IUmbracoContextAccessor umbracoContextAccessor) { _umbracoContextAccessor = umbracoContextAccessor; } diff --git a/src/Umbraco.Web/Templates/UrlParser.cs b/src/Umbraco.Web/Templates/HtmlUrlParser.cs similarity index 95% rename from src/Umbraco.Web/Templates/UrlParser.cs rename to src/Umbraco.Web/Templates/HtmlUrlParser.cs index e5c40b73655d..5b78477579ed 100644 --- a/src/Umbraco.Web/Templates/UrlParser.cs +++ b/src/Umbraco.Web/Templates/HtmlUrlParser.cs @@ -5,7 +5,7 @@ namespace Umbraco.Web.Templates { - public sealed class UrlParser + public sealed class HtmlUrlParser { private readonly IContentSection _contentSection; private readonly IProfilingLogger _logger; @@ -13,7 +13,7 @@ public sealed class UrlParser private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - public UrlParser(IContentSection contentSection, IProfilingLogger logger) + public HtmlUrlParser(IContentSection contentSection, IProfilingLogger logger) { _contentSection = contentSection; _logger = logger; diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index db0366dbf3d5..bcc669186468 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -15,10 +15,10 @@ namespace Umbraco.Web.Templates { - [Obsolete("This class is obsolete, all methods have been moved to other classes such as InternalLinkHelper, UrlResolver and MediaParser")] + [Obsolete("This class is obsolete, all methods have been moved to other classes: HtmlLocalLinkParser, HtmlUrlParser and HtmlImageSourceParser")] public static class TemplateUtilities { - [Obsolete("Inject and use an instance of InternalLinkParser instead")] + [Obsolete("Inject and use an instance of HtmlLocalLinkParser instead")] internal static string ParseInternalLinks(string text, bool preview, UmbracoContext umbracoContext) { using (umbracoContext.ForcedPreview(preview)) // force for url provider @@ -29,28 +29,28 @@ internal static string ParseInternalLinks(string text, bool preview, UmbracoCont return text; } - [Obsolete("Inject and use an instance of InternalLinkParser instead")] + [Obsolete("Inject and use an instance of HtmlLocalLinkParser instead")] public static string ParseInternalLinks(string text, UrlProvider urlProvider) - => Current.Factory.GetInstance().EnsureInternalLinks(text); + => Current.Factory.GetInstance().EnsureInternalLinks(text); - [Obsolete("Inject and use an instance of UrlResolver")] + [Obsolete("Inject and use an instance of HtmlUrlParser")] public static string ResolveUrlsFromTextString(string text) - => Current.Factory.GetInstance().EnsureUrls(text); + => Current.Factory.GetInstance().EnsureUrls(text); [Obsolete("Use StringExtensions.CleanForXss instead")] public static string CleanForXss(string text, params char[] ignoreFromClean) => text.CleanForXss(ignoreFromClean); - [Obsolete("Use MediaParser.EnsureImageSources instead")] + [Obsolete("Use HtmlImageSourceParser.EnsureImageSources instead")] public static string ResolveMediaFromTextString(string text) - => Current.Factory.GetInstance().EnsureImageSources(text); + => Current.Factory.GetInstance().EnsureImageSources(text); - [Obsolete("Use MediaParser.RemoveImageSources instead")] + [Obsolete("Use HtmlImageSourceParser.RemoveImageSources instead")] internal static string RemoveMediaUrlsFromTextString(string text) - => Current.Factory.GetInstance().RemoveImageSources(text); + => Current.Factory.GetInstance().RemoveImageSources(text); - [Obsolete("Use MediaParser.RemoveImageSources instead")] + [Obsolete("Use HtmlImageSourceParser.FindAndPersistPastedTempImages instead")] internal static string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger) - => Current.Factory.GetInstance().FindAndPersistPastedTempImages(html, mediaParentFolder, userId); + => Current.Factory.GetInstance().FindAndPersistPastedTempImages(html, mediaParentFolder, userId); } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8fc06c75c273..84645f3b2d09 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -247,9 +247,9 @@ - - - + + + diff --git a/src/Umbraco.Web/UmbracoComponentRenderer.cs b/src/Umbraco.Web/UmbracoComponentRenderer.cs index 83c8a7f0faa1..01c696fd2d5a 100644 --- a/src/Umbraco.Web/UmbracoComponentRenderer.cs +++ b/src/Umbraco.Web/UmbracoComponentRenderer.cs @@ -27,14 +27,14 @@ internal class UmbracoComponentRenderer : IUmbracoComponentRenderer private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly IMacroRenderer _macroRenderer; private readonly ITemplateRenderer _templateRenderer; - private readonly LocalLinkParser _internalLinkParser; + private readonly HtmlLocalLinkParser _linkParser; - public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer, LocalLinkParser internalLinkParser) + public UmbracoComponentRenderer(IUmbracoContextAccessor umbracoContextAccessor, IMacroRenderer macroRenderer, ITemplateRenderer templateRenderer, HtmlLocalLinkParser linkParser) { _umbracoContextAccessor = umbracoContextAccessor; _macroRenderer = macroRenderer; _templateRenderer = templateRenderer ?? throw new ArgumentNullException(nameof(templateRenderer)); - _internalLinkParser = internalLinkParser; + _linkParser = linkParser; } /// @@ -159,7 +159,7 @@ private IHtmlString RenderMacro(string alias, IDictionary parame _umbracoContextAccessor.UmbracoContext.HttpContext.Response.ContentType = contentType; //Now, we need to ensure that local links are parsed - html = _internalLinkParser.EnsureInternalLinks(output.ToString()); + html = _linkParser.EnsureInternalLinks(output.ToString()); } } From ba8c1df017d1595abfeee2dbb659b116406fdcd3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Oct 2019 15:38:14 +1100 Subject: [PATCH 14/15] fixing tests --- src/Umbraco.Tests/Testing/UmbracoTestBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 7e72a5aefbd6..ec265bd540c5 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -41,6 +41,7 @@ using Umbraco.Web.Sections; using Current = Umbraco.Core.Composing.Current; using FileSystems = Umbraco.Core.IO.FileSystems; +using Umbraco.Web.Templates; namespace Umbraco.Tests.Testing { @@ -230,6 +231,10 @@ protected virtual void ComposeWeb() .Append(); Composition.RegisterUnique(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); + Composition.RegisterUnique(); + } protected virtual void ComposeMisc() From c831c9de53b59a39734f3f38e06f89031cfb9b69 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 23 Oct 2019 16:30:22 +1100 Subject: [PATCH 15/15] uses nameof in attributes --- src/Umbraco.Web/Templates/TemplateUtilities.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web/Templates/TemplateUtilities.cs b/src/Umbraco.Web/Templates/TemplateUtilities.cs index bcc669186468..62d25fa7941c 100644 --- a/src/Umbraco.Web/Templates/TemplateUtilities.cs +++ b/src/Umbraco.Web/Templates/TemplateUtilities.cs @@ -15,10 +15,10 @@ namespace Umbraco.Web.Templates { - [Obsolete("This class is obsolete, all methods have been moved to other classes: HtmlLocalLinkParser, HtmlUrlParser and HtmlImageSourceParser")] + [Obsolete("This class is obsolete, all methods have been moved to other classes: " + nameof(HtmlLocalLinkParser) + ", " + nameof(HtmlUrlParser) + " and " + nameof(HtmlImageSourceParser))] public static class TemplateUtilities { - [Obsolete("Inject and use an instance of HtmlLocalLinkParser instead")] + [Obsolete("Inject and use an instance of " + nameof(HtmlLocalLinkParser) + " instead")] internal static string ParseInternalLinks(string text, bool preview, UmbracoContext umbracoContext) { using (umbracoContext.ForcedPreview(preview)) // force for url provider @@ -29,27 +29,27 @@ internal static string ParseInternalLinks(string text, bool preview, UmbracoCont return text; } - [Obsolete("Inject and use an instance of HtmlLocalLinkParser instead")] + [Obsolete("Inject and use an instance of " + nameof(HtmlLocalLinkParser) + " instead")] public static string ParseInternalLinks(string text, UrlProvider urlProvider) => Current.Factory.GetInstance().EnsureInternalLinks(text); - [Obsolete("Inject and use an instance of HtmlUrlParser")] + [Obsolete("Inject and use an instance of " + nameof(HtmlUrlParser))] public static string ResolveUrlsFromTextString(string text) => Current.Factory.GetInstance().EnsureUrls(text); - [Obsolete("Use StringExtensions.CleanForXss instead")] + [Obsolete("Use " + nameof(StringExtensions) + "." + nameof(StringExtensions.CleanForXss) + " instead")] public static string CleanForXss(string text, params char[] ignoreFromClean) => text.CleanForXss(ignoreFromClean); - [Obsolete("Use HtmlImageSourceParser.EnsureImageSources instead")] + [Obsolete("Use " + nameof(HtmlImageSourceParser) + "." + nameof(HtmlImageSourceParser.EnsureImageSources) + " instead")] public static string ResolveMediaFromTextString(string text) => Current.Factory.GetInstance().EnsureImageSources(text); - [Obsolete("Use HtmlImageSourceParser.RemoveImageSources instead")] + [Obsolete("Use " + nameof(HtmlImageSourceParser) + "." + nameof(HtmlImageSourceParser.RemoveImageSources) + " instead")] internal static string RemoveMediaUrlsFromTextString(string text) => Current.Factory.GetInstance().RemoveImageSources(text); - [Obsolete("Use HtmlImageSourceParser.FindAndPersistPastedTempImages instead")] + [Obsolete("Use " + nameof(HtmlImageSourceParser) + "." + nameof(HtmlImageSourceParser.FindAndPersistPastedTempImages) + " instead")] internal static string FindAndPersistPastedTempImages(string html, Guid mediaParentFolder, int userId, IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, ILogger logger) => Current.Factory.GetInstance().FindAndPersistPastedTempImages(html, mediaParentFolder, userId); }