diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index a9f5dcf352..4c2d72fb8c 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -2,7 +2,7 @@ using System.Resources; [assembly: AssemblyCompany("Umbraco")] -[assembly: AssemblyCopyright("Copyright © Umbraco 2019")] +[assembly: AssemblyCopyright("Copyright © Umbraco 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/src/Umbraco.Configuration/Umbraco.Configuration.csproj b/src/Umbraco.Configuration/Umbraco.Configuration.csproj index bc28a2c13a..57fca1dfd6 100644 --- a/src/Umbraco.Configuration/Umbraco.Configuration.csproj +++ b/src/Umbraco.Configuration/Umbraco.Configuration.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 8 diff --git a/src/Umbraco.Core/Features/IUmbracoFeature.cs b/src/Umbraco.Core/Features/IUmbracoFeature.cs index 0187cdecb0..ccb80b0a9f 100644 --- a/src/Umbraco.Core/Features/IUmbracoFeature.cs +++ b/src/Umbraco.Core/Features/IUmbracoFeature.cs @@ -1,5 +1,8 @@ namespace Umbraco.Web.Features { + /// + /// This is a marker interface to allow controllers to be disabled if also marked with FeatureAuthorizeAttribute. + /// public interface IUmbracoFeature { diff --git a/src/Umbraco.Core/IO/IIOHelper.cs b/src/Umbraco.Core/IO/IIOHelper.cs index e8ef6d2973..11f5c6c565 100644 --- a/src/Umbraco.Core/IO/IIOHelper.cs +++ b/src/Umbraco.Core/IO/IIOHelper.cs @@ -75,5 +75,6 @@ namespace Umbraco.Core.IO get; set; //Only required for unit tests } + } } diff --git a/src/Umbraco.Core/Install/FilePermissionDirectoryHelper.cs b/src/Umbraco.Core/IO/IOHelperExtensions.cs similarity index 55% rename from src/Umbraco.Core/Install/FilePermissionDirectoryHelper.cs rename to src/Umbraco.Core/IO/IOHelperExtensions.cs index bc57083cbd..64b57e7dc1 100644 --- a/src/Umbraco.Core/Install/FilePermissionDirectoryHelper.cs +++ b/src/Umbraco.Core/IO/IOHelperExtensions.cs @@ -1,17 +1,17 @@ -using System; +using System; using System.IO; -using Umbraco.Core.IO; -namespace Umbraco.Web.Install +namespace Umbraco.Core.IO { - public class FilePermissionDirectoryHelper + public static class IOHelperExtensions { - - - // tries to create a file - // if successful, the file is deleted - // creates the directory if needed - does not delete it - public static bool TryCreateDirectory(string dir, IIOHelper ioHelper) + /// + /// Tries to create a directory. + /// + /// The IOHelper. + /// the directory path. + /// true if the directory was created, false otherwise. + public static bool TryCreateDirectory(this IIOHelper ioHelper, string dir) { try { @@ -20,7 +20,7 @@ namespace Umbraco.Web.Install if (Directory.Exists(dirPath) == false) Directory.CreateDirectory(dirPath); - var filePath = dirPath + "/" + CreateRandomFileName() + ".tmp"; + var filePath = dirPath + "/" + CreateRandomFileName(ioHelper) + ".tmp"; File.WriteAllText(filePath, "This is an Umbraco internal test file. It is safe to delete it."); File.Delete(filePath); return true; @@ -31,7 +31,7 @@ namespace Umbraco.Web.Install } } - public static string CreateRandomFileName() + public static string CreateRandomFileName(this IIOHelper ioHelper) { return "umbraco-test." + Guid.NewGuid().ToString("N").Substring(0, 8); } diff --git a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs index 6ed3c85e91..82e5c6f171 100644 --- a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -69,6 +69,13 @@ namespace Umbraco.Core.Services /// bool HasContainerInPath(string contentPath); + /// + /// Gets a value indicating whether there is a list view content item in the path. + /// + /// + /// + bool HasContainerInPath(params int[] ids); + Attempt> CreateContainer(int parentContainerId, string name, int userId = Constants.Security.SuperUserId); Attempt SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId); EntityContainer GetContainer(int containerId); diff --git a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs index f1b887ca3f..d63478ef96 100644 --- a/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/IBatchedDatabaseServerMessenger.cs @@ -1,5 +1,8 @@ namespace Umbraco.Core.Sync { + /// + /// An implementation that works by storing messages in the database. + /// public interface IBatchedDatabaseServerMessenger : IServerMessenger { void FlushBatch(); diff --git a/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs b/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs index 64feba8cd8..dc4c53bac9 100644 --- a/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs +++ b/src/Umbraco.Core/Trees/IMenuItemCollectionFactory.cs @@ -2,8 +2,16 @@ using Umbraco.Web.Models.Trees; namespace Umbraco.Web.Trees { + + /// + /// Represents a factory to create . + /// public interface IMenuItemCollectionFactory { + /// + /// Creates an empty . + /// + /// An empty . MenuItemCollection Create(); } } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 52322f979a..7a15e7fbed 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 7.3 + 8 Umbraco.Core 9.0.0 9.0.0 diff --git a/src/Umbraco.Core/UmbracoEvents.cs b/src/Umbraco.Core/UmbracoEvents.cs deleted file mode 100644 index fe138425aa..0000000000 --- a/src/Umbraco.Core/UmbracoEvents.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Umbraco.Web.Routing; - -namespace Umbraco.Core -{ - public interface IUmbracoRouteEventSender - { - event EventHandler RouteAttempt; - } -} diff --git a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj index 726cb4759e..932d6d318b 100644 --- a/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj +++ b/src/Umbraco.Examine.Lucene/Umbraco.Examine.Lucene.csproj @@ -5,6 +5,7 @@ Umbraco.Examine Umbraco CMS Umbraco.Examine.Lucene + 8 diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs index 254e04d2d5..4020244733 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/IContentTypeRepositoryBase.cs @@ -27,6 +27,13 @@ namespace Umbraco.Core.Persistence.Repositories /// bool HasContainerInPath(string contentPath); + /// + /// Gets a value indicating whether there is a list view content item in the path. + /// + /// + /// + bool HasContainerInPath(params int[] ids); + /// /// Returns true or false depending on whether content nodes have been created based on the provided content type id. /// diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index b716a121be..16b9d852fd 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -1312,14 +1312,16 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", return test; } - /// - /// Given the path of a content item, this will return true if the content item exists underneath a list view content item - /// - /// - /// + /// public bool HasContainerInPath(string contentPath) { - var ids = contentPath.Split(',').Select(int.Parse); + var ids = contentPath.Split(',').Select(int.Parse).ToArray(); + return HasContainerInPath(ids); + } + + /// + public bool HasContainerInPath(params int[] ids) + { var sql = new Sql($@"SELECT COUNT(*) FROM cmsContentType INNER JOIN {Constants.DatabaseSchema.Tables.Content} ON cmsContentType.nodeId={Constants.DatabaseSchema.Tables.Content}.contentTypeId WHERE {Constants.DatabaseSchema.Tables.Content}.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true }); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs b/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs index 83f5badb9c..2737dcfef1 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/DataValueReferenceFactoryCollection.cs @@ -16,35 +16,46 @@ namespace Umbraco.Core.PropertyEditors public IEnumerable GetAllReferences(IPropertyCollection properties, PropertyEditorCollection propertyEditors) { - var trackedRelations = new List(); + var trackedRelations = new HashSet(); foreach (var p in properties) { if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue; - //TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here - if (!p.PropertyType.VariesByNothing()) continue; - var val = p.GetValue(); // get the invariant value + //TODO: We will need to change this once we support tracking via variants/segments + // for now, we are tracking values from ALL variants - var valueEditor = editor.GetValueEditor(); - if (valueEditor is IDataValueReference reference) + foreach(var propertyVal in p.Values) { - var refs = reference.GetReferences(val); - trackedRelations.AddRange(refs); + var val = propertyVal.EditedValue; + + var valueEditor = editor.GetValueEditor(); + if (valueEditor is IDataValueReference reference) + { + var refs = reference.GetReferences(val); + foreach(var r in refs) + trackedRelations.Add(r); } - // Loop over collection that may be add to existing property editors - // implementation of GetReferences in IDataValueReference. - // Allows developers to add support for references by a - // package /property editor that did not implement IDataValueReference themselves - foreach (var item in this) - { - // Check if this value reference is for this datatype/editor - // Then call it's GetReferences method - to see if the value stored - // in the dataeditor/property has referecnes to media/content items - if (item.IsForEditor(editor)) - trackedRelations.AddRange(item.GetDataValueReference().GetReferences(val)); + // Loop over collection that may be add to existing property editors + // implementation of GetReferences in IDataValueReference. + // Allows developers to add support for references by a + // package /property editor that did not implement IDataValueReference themselves + foreach (var item in this) + { + // Check if this value reference is for this datatype/editor + // Then call it's GetReferences method - to see if the value stored + // in the dataeditor/property has referecnes to media/content items + if (item.IsForEditor(editor)) + { + foreach(var r in item.GetDataValueReference().GetReferences(val)) + trackedRelations.Add(r); + } + + } } + + } return trackedRelations; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs index 4608b5c5da..4fdc13baa9 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/MarkdownEditorValueConverter.cs @@ -1,12 +1,23 @@ using System; +using HeyRed.MarkdownSharp; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Strings; +using Umbraco.Web.Templates; namespace Umbraco.Core.PropertyEditors.ValueConverters { [DefaultPropertyValueConverter] public class MarkdownEditorValueConverter : PropertyValueConverterBase { + private readonly HtmlLocalLinkParser _localLinkParser; + private readonly HtmlUrlParser _urlParser; + + public MarkdownEditorValueConverter(HtmlLocalLinkParser localLinkParser, HtmlUrlParser urlParser) + { + _localLinkParser = localLinkParser; + _urlParser = urlParser; + } + public override bool IsConverter(IPublishedPropertyType propertyType) => Constants.PropertyEditors.Aliases.MarkdownEditor.Equals(propertyType.EditorAlias); @@ -15,20 +26,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters // PropertyCacheLevel.Content is ok here because that converter does not parse {locallink} nor executes macros public override PropertyCacheLevel GetPropertyCacheLevel(IPublishedPropertyType propertyType) - => PropertyCacheLevel.Element; + => PropertyCacheLevel.Snapshot; public override object ConvertSourceToIntermediate(IPublishedElement owner, IPublishedPropertyType propertyType, object source, bool preview) { - // in xml a string is: string - // in the database a string is: string - // default value is: null - return source; + if (source == null) return null; + var sourceString = source.ToString(); + + // ensures string is parsed for {localLink} and urls are resolved correctly + sourceString = _localLinkParser.EnsureInternalLinks(sourceString, preview); + sourceString = _urlParser.EnsureUrls(sourceString); + + return sourceString; } public override object ConvertIntermediateToObject(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) { + // convert markup to HTML for frontend rendering. // source should come from ConvertSource and be a string (or null) already - return new HtmlEncodedString(inter == null ? string.Empty : (string) inter); + var mark = new Markdown(); + return new HtmlEncodedString(inter == null ? string.Empty : mark.Transform((string)inter)); } public override object ConvertIntermediateToXPath(IPublishedElement owner, IPublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview) diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index 50f12ba73e..7e39894aa3 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -318,6 +318,15 @@ namespace Umbraco.Core.Services.Implement } } + public bool HasContainerInPath(params int[] ids) + { + using (var scope = ScopeProvider.CreateScope(autoComplete: true)) + { + // can use same repo for both content and media + return Repository.HasContainerInPath(ids); + } + } + public IEnumerable GetDescendants(int id, bool andSelf) { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index 27dae62e72..1db36a9c09 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 7.3 + 8 diff --git a/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj b/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj index 5e71b2d9ec..83667bdbd8 100644 --- a/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj +++ b/src/Umbraco.ModelsBuilder.Embedded/Umbraco.ModelsBuilder.Embedded.csproj @@ -12,7 +12,7 @@ v4.7.2 512 true - 7.3 + 8 true diff --git a/src/Umbraco.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj b/src/Umbraco.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj index ae40051129..3174d4a7e2 100644 --- a/src/Umbraco.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj +++ b/src/Umbraco.Persistance.SqlCe/Umbraco.Persistance.SqlCe.csproj @@ -2,6 +2,7 @@ net472 + 8 diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 029978347a..68b7ee596a 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -364,7 +364,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public override bool EnsureEnvironment(out IEnumerable errors) { // must have app_data and be able to write files into it - var ok = FilePermissionDirectoryHelper.TryCreateDirectory(GetLocalFilesPath(), _ioHelper); + var ok = _ioHelper.TryCreateDirectory(GetLocalFilesPath()); errors = ok ? Enumerable.Empty() : new[] { "NuCache local files." }; return ok; } diff --git a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj index 50607130b4..75eeca268b 100644 --- a/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj +++ b/src/Umbraco.PublishedCache.NuCache/Umbraco.PublishedCache.NuCache.csproj @@ -3,6 +3,7 @@ netstandard2.0 Umbraco.Infrastructure.PublishedCache + 8 diff --git a/src/Umbraco.TestData/Umbraco.TestData.csproj b/src/Umbraco.TestData/Umbraco.TestData.csproj index e79ecfd589..963b598f26 100644 --- a/src/Umbraco.TestData/Umbraco.TestData.csproj +++ b/src/Umbraco.TestData/Umbraco.TestData.csproj @@ -12,6 +12,7 @@ v4.7.2 512 true + 8 true diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index c64c0e0da4..7566d8ab85 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -12,6 +12,7 @@ 512 true true + 8 AnyCPU diff --git a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs index 5f5fdaacc1..5fb4b19168 100644 --- a/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs @@ -42,7 +42,7 @@ namespace Umbraco.Tests.Cache // we should really refactor events entirely - in the meantime, let it be an UmbracoTestBase ;( //var testObjects = new TestObjects(null); //var serviceContext = testObjects.GetServiceContextMock(); - var serviceContext = Current.Services; + var serviceContext = ServiceContext; var definitions = new IEventDefinition[] { @@ -150,7 +150,7 @@ namespace Umbraco.Tests.Cache var definitions = new IEventDefinition[] { // works because that event definition maps to an empty handler - new EventDefinition>(null, Current.Services.ContentTypeService, new SaveEventArgs(Enumerable.Empty()), "Saved"), + new EventDefinition>(null, ServiceContext.ContentTypeService, new SaveEventArgs(Enumerable.Empty()), "Saved"), }; diff --git a/src/Umbraco.Tests/Issues/U9560.cs b/src/Umbraco.Tests/Issues/U9560.cs index 92d5045819..c750201b0c 100644 --- a/src/Umbraco.Tests/Issues/U9560.cs +++ b/src/Umbraco.Tests/Issues/U9560.cs @@ -22,7 +22,7 @@ namespace Umbraco.Tests.Issues contentType.Name = "test"; var propertyType = new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext, "prop") { Name = "Prop", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }; contentType.PropertyTypeCollection.Add(propertyType); - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); var aliasName = string.Empty; diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index b982b78c23..5c6d5c6947 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -252,8 +252,8 @@ namespace Umbraco.Tests.Models.Mapping } Assert.AreEqual(contentType.CompositionPropertyGroups.Count(), invariantContent.Tabs.Count() - 1); - Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == Current.Services.TextService.Localize("general/properties"))); - Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == Current.Services.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); + Assert.IsTrue(invariantContent.Tabs.Any(x => x.Label == ServiceContext.TextService.Localize("general/properties"))); + Assert.AreEqual(2, invariantContent.Tabs.Where(x => x.Label == ServiceContext.TextService.Localize("general/properties")).SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); } #region Assertions @@ -348,7 +348,7 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(p.PropertyType.ValidationRegExp, pDto.ValidationRegExp); Assert.AreEqual(p.PropertyType.Description, pDto.Description); Assert.AreEqual(p.PropertyType.Name, pDto.Label); - Assert.AreEqual(Current.Services.DataTypeService.GetDataType(p.PropertyType.DataTypeId), pDto.DataType); + Assert.AreEqual(ServiceContext.DataTypeService.GetDataType(p.PropertyType.DataTypeId), pDto.DataType); Assert.AreEqual(Current.PropertyEditors[p.PropertyType.PropertyEditorAlias], pDto.PropertyEditor); } diff --git a/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs b/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs new file mode 100644 index 0000000000..24ac9cdbf4 --- /dev/null +++ b/src/Umbraco.Tests/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs @@ -0,0 +1,255 @@ +using Moq; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers; +using Umbraco.Web.PropertyEditors; +using static Umbraco.Core.Models.Property; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class DataValueReferenceFactoryCollectionTests + { + IDataTypeService DataTypeService { get; } = Mock.Of(); + private IIOHelper IOHelper { get; } = TestHelper.IOHelper; + ILocalizedTextService LocalizedTextService { get; } = Mock.Of(); + ILocalizationService LocalizationService { get; } = Mock.Of(); + IShortStringHelper ShortStringHelper { get; } = Mock.Of(); + + [Test] + public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory() + { + var collection = new DataValueReferenceFactoryCollection(new TestDataValueReferenceFactory().Yield()); + + + // label does not implement IDataValueReference + var labelEditor = new LabelPropertyEditor( + Mock.Of(), + IOHelper, + DataTypeService, + LocalizedTextService, + LocalizationService, + ShortStringHelper + ); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(labelEditor.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(labelEditor)) + { + Variations = ContentVariation.CultureAndSegment + }) + { + Values = new List + { + // Ignored (no culture) + new PropertyValue + { + EditedValue = trackedUdi1 + }, + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + // Ignored (no culture) + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Culture = "en-US", + Segment = "B", + EditedValue = trackedUdi3 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString()); + } + + [Test] + public void GetAllReferences_All_Variants_With_IDataValueReference_Editor() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + + // mediaPicker does implement IDataValueReference + var mediaPicker = new MediaPickerPropertyEditor( + Mock.Of(), + DataTypeService, + LocalizationService, + IOHelper, + ShortStringHelper, + LocalizedTextService + ); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(mediaPicker)) + { + Variations = ContentVariation.CultureAndSegment + }) + { + Values = new List + { + // Ignored (no culture) + new PropertyValue + { + EditedValue = trackedUdi1 + }, + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + // Ignored (no culture) + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Culture = "en-US", + Segment = "B", + EditedValue = trackedUdi3 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString()); + } + + [Test] + public void GetAllReferences_Invariant_With_IDataValueReference_Editor() + { + var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + + // mediaPicker does implement IDataValueReference + var mediaPicker = new MediaPickerPropertyEditor( + Mock.Of(), + DataTypeService, + LocalizationService, + IOHelper, + ShortStringHelper, + LocalizedTextService + ); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); + var property = new Property(new PropertyType(ShortStringHelper, new DataType(mediaPicker)) + { + Variations = ContentVariation.Nothing | ContentVariation.Segment + }) + { + Values = new List + { + new PropertyValue + { + EditedValue = trackedUdi1 + }, + // Ignored (has culture) + new PropertyValue + { + Culture = "en-US", + EditedValue = trackedUdi2 + }, + // Ignored (has culture) + new PropertyValue + { + Culture = "en-US", + Segment = "A", + EditedValue = trackedUdi3 + }, + new PropertyValue + { + Segment = "A", + EditedValue = trackedUdi4 + }, + // duplicate + new PropertyValue + { + Segment = "B", + EditedValue = trackedUdi4 + } + } + }; + var properties = new PropertyCollection + { + property + }; + var result = collection.GetAllReferences(properties, propertyEditors); + + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(trackedUdi1, result.ElementAt(0).Udi.ToString()); + Assert.AreEqual(trackedUdi4, result.ElementAt(1).Udi.ToString()); + } + + private class TestDataValueReferenceFactory : IDataValueReferenceFactory + { + public IDataValueReference GetDataValueReference() => new TestMediaDataValueReference(); + + public bool IsForEditor(IDataEditor dataEditor) => dataEditor.Alias == Constants.PropertyEditors.Aliases.Label; + + private class TestMediaDataValueReference : IDataValueReference + { + public IEnumerable GetReferences(object value) + { + // This is the same as the media picker, it will just try to parse the value directly as a UDI + + var asString = value is string str ? str : value?.ToString(); + + if (string.IsNullOrEmpty(asString)) yield break; + + if (UdiParser.TryParse(asString, out var udi)) + yield return new UmbracoEntityReference(udi); + } + } + } + } +} diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs index d35cc941da..91e662d46b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs @@ -12,6 +12,7 @@ using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; using Umbraco.Core.Strings; using Umbraco.Tests.TestHelpers; +using Umbraco.Tests.Testing; using Umbraco.Web; using PublishedContentExtensions = Umbraco.Web.PublishedContentExtensions; @@ -78,7 +79,7 @@ namespace Umbraco.Tests.PublishedContent public void To_DataTable() { var doc = GetContent(true, 1); - var dt = doc.ChildrenAsTable(Current.Services); + var dt = doc.ChildrenAsTable(ServiceContext); Assert.AreEqual(11, dt.Columns.Count); Assert.AreEqual(3, dt.Rows.Count); @@ -101,7 +102,7 @@ namespace Umbraco.Tests.PublishedContent var c = (SolidPublishedContent)doc.Children.ElementAt(0); c.ContentType = new PublishedContentType(22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); - var dt = doc.ChildrenAsTable(Current.Services, "Child"); + var dt = doc.ChildrenAsTable(ServiceContext, "Child"); Assert.AreEqual(11, dt.Columns.Count); Assert.AreEqual(2, dt.Rows.Count); @@ -117,7 +118,7 @@ namespace Umbraco.Tests.PublishedContent public void To_DataTable_No_Rows() { var doc = GetContent(false, 1); - var dt = doc.ChildrenAsTable(Current.Services); + var dt = doc.ChildrenAsTable(ServiceContext); //will return an empty data table Assert.AreEqual(0, dt.Columns.Count); Assert.AreEqual(0, dt.Rows.Count); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs index 65da377071..3cff4d4e9d 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs @@ -74,7 +74,7 @@ namespace Umbraco.Tests.PublishedContent _ctx = GetUmbracoContext("/", 1, null, true); if (_createContentTypes) { - var contentTypeService = Current.Services.ContentTypeService; + var contentTypeService = ServiceContext.ContentTypeService; var baseType = new ContentType(ShortStringHelper, -1) { Alias = "base", Name = "Base" }; const string contentTypeAlias = "inherited"; var inheritedType = new ContentType(ShortStringHelper, baseType, contentTypeAlias) { Alias = contentTypeAlias, Name = "Inherited" }; diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs index d36eb0013a..1806493cdd 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs @@ -73,7 +73,7 @@ namespace Umbraco.Tests.PublishedContent var umbracoContext = new UmbracoContext( httpContextAccessor, publishedSnapshotService.Object, - new WebSecurity(httpContextAccessor, Current.Services.UserService, globalSettings, IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), IOHelper, diff --git a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs index ba07bbed82..208ec20517 100644 --- a/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs +++ b/src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -19,7 +19,7 @@ namespace Umbraco.Tests.Routing { var template = new Template(ShortStringHelper, alias, alias); template.Content = ""; // else saving throws with a dirty internal error - Current.Services.FileService.SaveTemplate(template); + ServiceContext.FileService.SaveTemplate(template); return template; } diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs index 02639e593d..d7b4b154eb 100644 --- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs +++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Routing var name = "Template"; var template = new Template(ShortStringHelper, name, alias); template.Content = ""; // else saving throws with a dirty internal error - Current.Services.FileService.SaveTemplate(template); + ServiceContext.FileService.SaveTemplate(template); return template; } diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 1e148d33c6..9cf6b3d773 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -121,7 +121,7 @@ namespace Umbraco.Tests.Scoping var umbracoContext = new UmbracoContext( httpContextAccessor, service, - new WebSecurity(httpContextAccessor, Current.Services.UserService, globalSettings, IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), IOHelper, @@ -146,7 +146,7 @@ namespace Umbraco.Tests.Scoping // create document type, document var contentType = new ContentType(ShortStringHelper, -1) { Alias = "CustomDocument", Name = "Custom Document" }; - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); var item = new Content("name", -1, contentType); // event handler @@ -164,7 +164,7 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - Current.Services.ContentService.SaveAndPublish(item); + ServiceContext.ContentService.SaveAndPublish(item); scope.Complete(); } @@ -178,7 +178,7 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { item.Name = "changed"; - Current.Services.ContentService.SaveAndPublish(item); + ServiceContext.ContentService.SaveAndPublish(item); if (complete) scope.Complete(); diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 7a0b2082b3..d1963a1d2e 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -60,7 +60,7 @@ namespace Umbraco.Tests.Scoping public void DefaultRepositoryCachePolicy(bool complete) { var scopeProvider = ScopeProvider; - var service = Current.Services.UserService; + var service = ServiceContext.UserService; var globalCache = Current.AppCaches.IsolatedCaches.GetOrCreate(typeof(IUser)); var user = (IUser)new User(TestObjects.GetGlobalSettings(), "name", "email", "username", "rawPassword"); @@ -137,7 +137,7 @@ namespace Umbraco.Tests.Scoping public void FullDataSetRepositoryCachePolicy(bool complete) { var scopeProvider = ScopeProvider; - var service = Current.Services.LocalizationService; + var service = ServiceContext.LocalizationService; var globalCache = Current.AppCaches.IsolatedCaches.GetOrCreate(typeof (ILanguage)); var lang = (ILanguage) new Language(TestObjects.GetGlobalSettings(), "fr-FR"); @@ -229,7 +229,7 @@ namespace Umbraco.Tests.Scoping public void SingleItemsOnlyRepositoryCachePolicy(bool complete) { var scopeProvider = ScopeProvider; - var service = Current.Services.LocalizationService; + var service = ServiceContext.LocalizationService; var globalCache = Current.AppCaches.IsolatedCaches.GetOrCreate(typeof (IDictionaryItem)); var lang = (ILanguage)new Language(TestObjects.GetGlobalSettings(), "fr-FR"); diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 24b41d3322..3e62b52689 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -89,7 +89,7 @@ namespace Umbraco.Tests.Scoping // create document type, document var contentType = new ContentType(ShortStringHelper, -1) { Alias = "CustomDocument", Name = "Custom Document" }; - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); var item = new Content("name", -1, contentType); // wire cache refresher @@ -126,9 +126,9 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - Current.Services.ContentService.SaveAndPublish(item); // should create an xml clone + ServiceContext.ContentService.SaveAndPublish(item); // should create an xml clone item.Name = "changed"; - Current.Services.ContentService.SaveAndPublish(item); // should re-use the xml clone + ServiceContext.ContentService.SaveAndPublish(item); // should re-use the xml clone // this should never change Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); @@ -203,7 +203,7 @@ namespace Umbraco.Tests.Scoping // create document type var contentType = new ContentType(ShortStringHelper,-1) { Alias = "CustomDocument", Name = "Custom Document" }; - Current.Services.ContentTypeService.Save(contentType); + ServiceContext.ContentTypeService.Save(contentType); // wire cache refresher _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers), Mock.Of(), Mock.Of()); @@ -225,12 +225,12 @@ namespace Umbraco.Tests.Scoping using (var scope = ScopeProvider.CreateScope()) { - Current.Services.ContentService.SaveAndPublish(item); + ServiceContext.ContentService.SaveAndPublish(item); for (var i = 0; i < count; i++) { var temp = new Content("content_" + i, -1, contentType); - Current.Services.ContentService.SaveAndPublish(temp); + ServiceContext.ContentService.SaveAndPublish(temp); ids[i] = temp.Id; } diff --git a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs index eeff707618..16b8859bed 100644 --- a/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs +++ b/src/Umbraco.Tests/Security/BackOfficeCookieManagerTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Security var umbracoContext = new UmbracoContext( httpContextAccessor, Mock.Of(), - new WebSecurity(httpContextAccessor, Current.Services.UserService, globalSettings, IOHelper), globalSettings, + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), IOHelper, UriUtility, @@ -57,7 +57,7 @@ namespace Umbraco.Tests.Security var umbCtx = new UmbracoContext( httpContextAccessor, Mock.Of(), - new WebSecurity(httpContextAccessor, Current.Services.UserService, globalSettings, IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), IOHelper, diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index 6172c63650..b02af84e09 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -54,8 +54,6 @@ namespace Umbraco.Tests.TestHelpers protected PublishedContentTypeCache ContentTypesCache { get; private set; } protected override ISqlSyntaxProvider SqlSyntax => GetSyntaxProvider(); - - protected ServiceContext ServiceContext => Current.Services; protected IVariationContextAccessor VariationContextAccessor => new TestVariationContextAccessor(); internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider; diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs index 748c810171..c87b92f1c9 100644 --- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs @@ -112,6 +112,7 @@ namespace Umbraco.Tests.Testing private TypeLoader _featureTypeLoader; #region Accessors + protected ServiceContext ServiceContext => Factory.GetInstance(); protected ILogger Logger => Factory.GetInstance(); protected IJsonSerializer JsonNetSerializer { get; } = new JsonNetSerializer(); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index c17f051411..9815c94728 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -42,7 +42,7 @@ prompt 4 false - latest + 8 pdbonly @@ -149,6 +149,7 @@ + diff --git a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs index 2c75b9070d..a176067541 100644 --- a/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/AuthenticationControllerTests.cs @@ -64,7 +64,7 @@ namespace Umbraco.Tests.Web.Controllers ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { //setup some mocks - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceMock.Setup(service => service.GetUserById(It.IsAny())) .Returns(() => null); diff --git a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs index 778819c742..2ed2ff568f 100644 --- a/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/ContentControllerTests.cs @@ -160,10 +160,10 @@ namespace Umbraco.Tests.Web.Controllers if (_contentTypeForMockedContent == null) { _contentTypeForMockedContent = GetMockedContentType(); - Mock.Get(Current.Services.ContentTypeService) + Mock.Get(ServiceContext.ContentTypeService) .Setup(x => x.Get(_contentTypeForMockedContent.Id)) .Returns(_contentTypeForMockedContent); - Mock.Get(Current.Services.ContentTypeService) + Mock.Get(ServiceContext.ContentTypeService) .As() .Setup(x => x.Get(_contentTypeForMockedContent.Id)) .Returns(_contentTypeForMockedContent); @@ -254,7 +254,7 @@ namespace Umbraco.Tests.Web.Controllers { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => null); //do not find it var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); @@ -337,7 +337,7 @@ namespace Umbraco.Tests.Web.Controllers { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => GetMockedContent()); var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); @@ -385,7 +385,7 @@ namespace Umbraco.Tests.Web.Controllers ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success @@ -427,7 +427,7 @@ namespace Umbraco.Tests.Web.Controllers ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success @@ -476,7 +476,7 @@ namespace Umbraco.Tests.Web.Controllers ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { - var contentServiceMock = Mock.Get(Current.Services.ContentService); + var contentServiceMock = Mock.Get(ServiceContext.ContentService); contentServiceMock.Setup(x => x.GetById(123)).Returns(() => content); contentServiceMock.Setup(x => x.Save(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success diff --git a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs index decbd59183..0b0fa8f157 100644 --- a/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs +++ b/src/Umbraco.Tests/Web/Controllers/UsersControllerTests.cs @@ -64,7 +64,7 @@ namespace Umbraco.Tests.Web.Controllers { ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceMock.Setup(service => service.Save(It.IsAny(), It.IsAny())) .Callback((IUser u, bool raiseEvents) => @@ -186,7 +186,7 @@ namespace Umbraco.Tests.Web.Controllers ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { //setup some mocks - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); var users = MockedUser.CreateMulipleUsers(10); long outVal = 10; userServiceMock.Setup(service => service.GetAll( @@ -269,7 +269,7 @@ namespace Umbraco.Tests.Web.Controllers ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper) { //setup some mocks - var userServiceMock = Mock.Get(Current.Services.UserService); + var userServiceMock = Mock.Get(ServiceContext.UserService); userServiceSetup(userServiceMock); var usersController = new UsersController( diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 9b11feb170..bc2b896b49 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -441,7 +441,7 @@ namespace Umbraco.Tests.Web.Mvc var ctx = new UmbracoContext( httpContextAccessor, _service, - new WebSecurity(httpContextAccessor, Current.Services.UserService, globalSettings, IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper), globalSettings, new TestVariationContextAccessor(), IOHelper, diff --git a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs index b126c823cd..08cd84744b 100644 --- a/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs +++ b/src/Umbraco.Tests/Web/WebExtensionMethodTests.cs @@ -32,7 +32,7 @@ namespace Umbraco.Tests.Web var umbCtx = new UmbracoContext( httpContextAccessor, Mock.Of(), - new WebSecurity(httpContextAccessor, Current.Services.UserService, TestObjects.GetGlobalSettings(), IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor(), IOHelper, @@ -53,7 +53,7 @@ namespace Umbraco.Tests.Web var umbCtx = new UmbracoContext( httpContextAccessor, Mock.Of(), - new WebSecurity(httpContextAccessor, Current.Services.UserService, TestObjects.GetGlobalSettings(), IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor(), IOHelper, @@ -84,7 +84,7 @@ namespace Umbraco.Tests.Web var umbCtx = new UmbracoContext( httpContextAccessor, Mock.Of(), - new WebSecurity(httpContextAccessor, Current.Services.UserService, TestObjects.GetGlobalSettings(), IOHelper), + new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper), TestObjects.GetGlobalSettings(), new TestVariationContextAccessor(), IOHelper, diff --git a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj index 3b6456c62b..347e4d5531 100644 --- a/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj +++ b/src/Umbraco.Web.BackOffice/Umbraco.Web.BackOffice.csproj @@ -2,6 +2,7 @@ netstandard2.0 + 8 diff --git a/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg b/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg index 6c0515906f..75bf0d52af 100644 Binary files a/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg and b/src/Umbraco.Web.UI.Client/src/assets/img/installer.jpg differ diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html index 93801f14b8..d4e75908bd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umb-mini-search.html @@ -9,6 +9,7 @@ ng-model="vm.model" ng-change="vm.onChange()" ng-keydown="vm.onKeyDown($event)" + ng-blur="vm.onBlur($event)" prevent-enter-submit no-dirty-check> diff --git a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js index 994129708f..d7aee744e4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js +++ b/src/Umbraco.Web.UI.Client/src/views/components/umb-mini-search/umbminisearch.component.js @@ -10,7 +10,8 @@ bindings: { model: "=", onStartTyping: "&?", - onSearch: "&?" + onSearch: "&?", + onBlur: "&?" } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js index 2e4313ec76..172f9b2249 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js @@ -147,7 +147,7 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en _.each($scope.model.value, function (item){ // we must reload the "document" link URLs to match the current editor culture - if (item.udi.indexOf("/document/") > 0) { + if (item.udi && item.udi.indexOf("/document/") > 0) { item.url = null; entityResource.getUrlByUdi(item.udi).then(function (data) { item.url = data; diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js index 935b1bdfb1..f3c28fdb9d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.controller.js @@ -14,7 +14,7 @@ vm.userStates = []; vm.selection = []; vm.newUser = {}; - vm.usersOptions = {filter:null}; + vm.usersOptions = {}; vm.userSortData = [ { label: "Name (A-Z)", key: "Name", direction: "Ascending" }, { label: "Name (Z-A)", key: "Name", direction: "Descending" }, @@ -112,6 +112,7 @@ vm.selectAll = selectAll; vm.areAllSelected = areAllSelected; vm.searchUsers = searchUsers; + vm.onBlurSearch = onBlurSearch; vm.getFilterName = getFilterName; vm.setUserStatesFilter = setUserStatesFilter; vm.setUserGroupFilter = setUserGroupFilter; @@ -150,10 +151,12 @@ function initViewOptions() { // Start with default view options. + vm.usersOptions.filter = ""; vm.usersOptions.orderBy = "Name"; vm.usersOptions.orderDirection = "Ascending"; // Update from querystring if available. + initViewOptionFromQueryString("filter"); initViewOptionFromQueryString("orderBy"); initViewOptionFromQueryString("orderDirection"); initViewOptionFromQueryString("pageNumber"); @@ -451,7 +454,8 @@ var search = _.debounce(function () { $scope.$apply(function () { - changePageNumber(1); + vm.usersOptions.pageNumber = 1; + getUsers(); }); }, 500); @@ -459,6 +463,10 @@ search(); } + function onBlurSearch() { + updateLocation("filter", vm.usersOptions.filter); + } + function getFilterName(array) { var name = vm.labels.all; var found = false; @@ -547,6 +555,7 @@ } function updateLocation(key, value) { + $location.search("filter", vm.usersOptions.filter);// update filter, but first when something else requests a url update. $location.search(key, value); } @@ -657,7 +666,8 @@ function usersOptionsAsQueryString() { var qs = "?orderBy=" + vm.usersOptions.orderBy + "&orderDirection=" + vm.usersOptions.orderDirection + - "&pageNumber=" + vm.usersOptions.pageNumber; + "&pageNumber=" + vm.usersOptions.pageNumber + + "&filter=" + vm.usersOptions.filter; qs += addUsersOptionsFilterCollectionToQueryString("userStates", vm.usersOptions.userStates); qs += addUsersOptionsFilterCollectionToQueryString("userGroups", vm.usersOptions.userGroups); diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html index 638e6376c3..bb53413060 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/users/users.html @@ -28,7 +28,7 @@ - + diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 12ff3952fd..0b63c94ba8 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -1671,7 +1671,7 @@ namespace Umbraco.Web.Editors [HttpPost] public DomainSave PostSaveLanguageAndDomains(DomainSave model) { - foreach(var domain in model.Domains) + foreach (var domain in model.Domains) { try { @@ -2188,7 +2188,10 @@ namespace Umbraco.Web.Editors /// private ContentItemDisplay MapToDisplay(IContent content) { - var display = Mapper.Map(content); + var display = Mapper.Map(content, context => + { + context.Items["CurrentUser"] = Security.CurrentUser; + }); display.AllowPreview = display.AllowPreview && content.Trashed == false && content.ContentType.IsElement == false; return display; } diff --git a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs index c69766cc9d..0b94058137 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs @@ -91,7 +91,7 @@ namespace Umbraco.Web.Install.Controllers var step = _installSteps.GetAllSteps().Single(x => x.Name == item.Name); // if this step has any instructions then extract them - installModel.Instructions.TryGetValue(item.Name, out var instruction); // else null + var instruction = GetInstruction(installModel, item, step); // if this step doesn't require execution then continue to the next one, this is just a fail-safe check. if (StepRequiresExecution(step, instruction) == false) @@ -153,6 +153,18 @@ namespace Umbraco.Web.Install.Controllers return new InstallProgressResultModel(true, "", ""); } + private static object GetInstruction(InstallInstructions installModel, InstallTrackingItem item, InstallSetupStep step) + { + installModel.Instructions.TryGetValue(item.Name, out var instruction); // else null + + if (instruction is JObject jObject) + { + instruction = jObject?.ToObject(step.StepType); + } + + return instruction; + } + /// /// We'll peek ahead and check if it's RequiresExecution is returning true. If it /// is not, we'll dequeue that step and peek ahead again (recurse) @@ -177,8 +189,7 @@ namespace Umbraco.Web.Install.Controllers var step = _installSteps.GetAllSteps().Single(x => x.Name == item.Name); // if this step has any instructions then extract them - object instruction; - installModel.Instructions.TryGetValue(item.Name, out instruction); // else null + var instruction = GetInstruction(installModel, item, step); // if the step requires execution then return its name if (StepRequiresExecution(step, instruction)) @@ -201,7 +212,15 @@ namespace Umbraco.Web.Install.Controllers // determines whether the step requires execution internal bool StepRequiresExecution(InstallSetupStep step, object instruction) { - var model = Convert.ChangeType(instruction, step.StepType); + if (step == null) throw new ArgumentNullException(nameof(step)); + + var modelAttempt = instruction.TryConvertTo(step.StepType); + if (!modelAttempt.Success) + { + throw new InvalidCastException($"Cannot cast/convert {step.GetType().FullName} into {step.StepType.FullName}"); + } + + var model = modelAttempt.Result; var genericStepType = typeof(InstallSetupStep<>); Type[] typeArgs = { step.StepType }; var typedStepType = genericStepType.MakeGenericType(typeArgs); @@ -222,7 +241,12 @@ namespace Umbraco.Web.Install.Controllers { using (_proflog.TraceDuration($"Executing installation step: '{step.Name}'.", "Step completed")) { - var model = Convert.ChangeType(instruction, step.StepType); + var modelAttempt = instruction.TryConvertTo(step.StepType); + if (!modelAttempt.Success) + { + throw new InvalidCastException($"Cannot cast/convert {step.GetType().FullName} into {step.StepType.FullName}"); + } + var model = modelAttempt.Result; var genericStepType = typeof(InstallSetupStep<>); Type[] typeArgs = { step.StepType }; var typedStepType = genericStepType.MakeGenericType(typeArgs); diff --git a/src/Umbraco.Web/Install/FilePermissionHelper.cs b/src/Umbraco.Web/Install/FilePermissionHelper.cs index be4c2b1dd0..24649b4391 100644 --- a/src/Umbraco.Web/Install/FilePermissionHelper.cs +++ b/src/Umbraco.Web/Install/FilePermissionHelper.cs @@ -141,7 +141,7 @@ namespace Umbraco.Web.Install { try { - var path = _ioHelper.MapPath(dir + "/" + FilePermissionDirectoryHelper.CreateRandomFileName()); + var path = _ioHelper.MapPath(dir + "/" + _ioHelper.CreateRandomFileName()); Directory.CreateDirectory(path); Directory.Delete(path); return true; @@ -171,7 +171,7 @@ namespace Umbraco.Web.Install if (canWrite) { - var filePath = dirPath + "/" + FilePermissionDirectoryHelper.CreateRandomFileName() + ".tmp"; + var filePath = dirPath + "/" + _ioHelper.CreateRandomFileName() + ".tmp"; File.WriteAllText(filePath, "This is an Umbraco internal test file. It is safe to delete it."); File.Delete(filePath); return true; diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs b/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs index d418bf153d..36c117197b 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapDefinition.cs @@ -7,6 +7,7 @@ using Umbraco.Core.Logging; using Umbraco.Core.Mapping; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; +using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Routing; @@ -30,6 +31,7 @@ namespace Umbraco.Web.Models.Mapping private readonly ILocalizationService _localizationService; private readonly ILogger _logger; private readonly IUserService _userService; + private readonly IEntityService _entityService; private readonly IVariationContextAccessor _variationContextAccessor; private readonly IPublishedUrlProvider _publishedUrlProvider; private readonly UriUtility _uriUtility; @@ -38,9 +40,10 @@ namespace Umbraco.Web.Models.Mapping private readonly ContentBasicSavedStateMapper _basicStateMapper; private readonly ContentVariantMapper _contentVariantMapper; + public ContentMapDefinition(CommonMapper commonMapper, ICultureDictionary cultureDictionary, ILocalizedTextService localizedTextService, IContentService contentService, IContentTypeService contentTypeService, IFileService fileService, IUmbracoContextAccessor umbracoContextAccessor, IPublishedRouter publishedRouter, ILocalizationService localizationService, ILogger logger, - IUserService userService, IVariationContextAccessor variationContextAccessor, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider) + IUserService userService, IVariationContextAccessor variationContextAccessor, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider, IEntityService entityService) { _commonMapper = commonMapper; _cultureDictionary = cultureDictionary; @@ -53,6 +56,7 @@ namespace Umbraco.Web.Models.Mapping _localizationService = localizationService; _logger = logger; _userService = userService; + _entityService = entityService; _variationContextAccessor = variationContextAccessor; _uriUtility = uriUtility; _publishedUrlProvider = publishedUrlProvider; @@ -90,7 +94,7 @@ namespace Umbraco.Web.Models.Mapping target.Icon = source.ContentType.Icon; target.Id = source.Id; target.IsBlueprint = source.Blueprint; - target.IsChildOfListView = DetermineIsChildOfListView(source); + target.IsChildOfListView = DetermineIsChildOfListView(source, context); target.IsContainer = source.ContentType.IsContainer; target.IsElement = source.ContentType.IsElement; target.Key = source.Key; @@ -221,13 +225,66 @@ namespace Umbraco.Web.Models.Mapping return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"({source.Name})"; } - private bool DetermineIsChildOfListView(IContent source) + /// + /// Checks if the content item is a descendant of a list view + /// + /// + /// + /// + /// Returns true if the content item is a descendant of a list view and where the content is + /// not a current user's start node. + /// + /// + /// We must check if it's the current user's start node because in that case we will actually be + /// rendering the tree node underneath the list view to visually show context. In this case we return + /// false because the item is technically not being rendered as part of a list view but instead as a + /// real tree node. If we didn't perform this check then tree syncing wouldn't work correctly. + /// + private bool DetermineIsChildOfListView(IContent source, MapperContext context) { - // map the IsChildOfListView (this is actually if it is a descendant of a list view!) + var userStartNodes = Array.Empty(); + + // In cases where a user's start node is below a list view, we will actually render + // out the tree to that start node and in that case for that start node, we want to return + // false here. + if (context.HasItems && context.Items.TryGetValue("CurrentUser", out var usr) && usr is IUser currentUser) + { + userStartNodes = currentUser.CalculateContentStartNodeIds(_entityService); + if (!userStartNodes.Contains(Constants.System.Root)) + { + // return false if this is the user's actual start node, the node will be rendered in the tree + // regardless of if it's a list view or not + if (userStartNodes.Contains(source.Id)) + return false; + } + } + var parent = _contentService.GetParent(source); - return parent != null && (parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(parent.Path)); + + if (parent == null) + return false; + + var pathParts = parent.Path.Split(',').Select(x => int.TryParse(x, out var i) ? i : 0).ToList(); + + // reduce the path parts so we exclude top level content items that + // are higher up than a user's start nodes + foreach (var n in userStartNodes) + { + var index = pathParts.IndexOf(n); + if (index != -1) + { + // now trim all top level start nodes to the found index + for (var i = 0; i < index; i++) + { + pathParts.RemoveAt(0); + } + } + } + + return parent.ContentType.IsContainer || _contentTypeService.HasContainerInPath(pathParts.ToArray()); } + private DateTime? GetScheduledDate(IContent source, ContentScheduleAction action, MapperContext context) { var culture = context.GetCulture() ?? string.Empty; diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 7646a71e7d..4549f47a2f 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -381,7 +381,12 @@ namespace Umbraco.Web.Trees var startNodes = Services.EntityService.GetAll(UmbracoObjectType, UserStartNodes); //if any of these start nodes' parent is current, then we need to render children normally so we need to switch some logic and tell // the UI that this node does have children and that it isn't a container - if (startNodes.Any(x => x.ParentId == e.Id)) + + if (startNodes.Any(x => + { + var pathParts = x.Path.Split(','); + return pathParts.Contains(e.Id.ToInvariantString()); + })) { renderChildren = true; } diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index bacfc820d6..e4bfbc53f7 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -20,11 +20,9 @@ namespace Umbraco.Web.Trees { public abstract class FileSystemTreeController : TreeController { - private readonly IMenuItemCollectionFactory _menuItemCollectionFactory; - protected FileSystemTreeController() { - _menuItemCollectionFactory = Current.MenuItemCollectionFactory; + MenuItemCollectionFactory = Current.MenuItemCollectionFactory; } protected FileSystemTreeController( @@ -41,10 +39,11 @@ namespace Umbraco.Web.Trees IMenuItemCollectionFactory menuItemCollectionFactory) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper, publishedUrlProvider) { - _menuItemCollectionFactory = menuItemCollectionFactory; + MenuItemCollectionFactory = menuItemCollectionFactory; } protected abstract IFileSystem FileSystem { get; } + protected IMenuItemCollectionFactory MenuItemCollectionFactory { get; } protected abstract string[] Extensions { get; } protected abstract string FileIcon { get; } @@ -120,7 +119,7 @@ namespace Umbraco.Web.Trees protected virtual MenuItemCollection GetMenuForRootNode(FormDataCollection queryStrings) { - var menu = _menuItemCollectionFactory.Create(); + var menu = MenuItemCollectionFactory.Create(); //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; @@ -134,7 +133,7 @@ namespace Umbraco.Web.Trees protected virtual MenuItemCollection GetMenuForFolder(string path, FormDataCollection queryStrings) { - var menu = _menuItemCollectionFactory.Create(); + var menu = MenuItemCollectionFactory.Create(); //set the default to create menu.DefaultMenuAlias = ActionNew.ActionAlias; @@ -158,7 +157,7 @@ namespace Umbraco.Web.Trees protected virtual MenuItemCollection GetMenuForFile(string path, FormDataCollection queryStrings) { - var menu = _menuItemCollectionFactory.Create(); + var menu = MenuItemCollectionFactory.Create(); //if it's not a directory then we only allow to delete the item menu.Items.Add(Services.TextService, opensDialog: true); @@ -174,7 +173,7 @@ namespace Umbraco.Web.Trees return GetMenuForRootNode(queryStrings); } - var menu = _menuItemCollectionFactory.Create(); + var menu = MenuItemCollectionFactory.Create(); var path = string.IsNullOrEmpty(id) == false && id != Constants.System.RootString ? HttpUtility.UrlDecode(id).TrimStart("/") diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a66ddf8922..431bffec9e 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -111,10 +111,6 @@ {29aa69d9-b597-4395-8d42-43b1263c240a} Umbraco.Core - - {f9b7fe05-0f93-4d0d-9c10-690b33ecbbd8} - Umbraco.Examine - {3ae7bf57-966b-45a5-910a-954d7c554441} Umbraco.Infrastructure @@ -600,47 +596,8 @@ Mvc\web.config - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - Dynamic - Web References\org.umbraco.update\ - http://update.umbraco.org/checkforupgrade.asmx - - - - - Settings - umbraco_org_umbraco_update_CheckForUpgrade - - - - - $(PlatformTargetAsMSBuildArchitecture) - - - - - - - - - - - + \ No newline at end of file