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