Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB5168-migrate-macros

This commit is contained in:
elitsa
2020-02-25 13:44:48 +01:00
58 changed files with 555 additions and 177 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -1,5 +1,8 @@
namespace Umbraco.Web.Features
{
/// <summary>
/// This is a marker interface to allow controllers to be disabled if also marked with FeatureAuthorizeAttribute.
/// </summary>
public interface IUmbracoFeature
{
+1
View File
@@ -75,5 +75,6 @@ namespace Umbraco.Core.IO
get;
set; //Only required for unit tests
}
}
}
@@ -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)
/// <summary>
/// Tries to create a directory.
/// </summary>
/// <param name="ioHelper">The IOHelper.</param>
/// <param name="dir">the directory path.</param>
/// <returns>true if the directory was created, false otherwise.</returns>
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);
}
@@ -69,6 +69,13 @@ namespace Umbraco.Core.Services
/// <returns></returns>
bool HasContainerInPath(string contentPath);
/// <summary>
/// Gets a value indicating whether there is a list view content item in the path.
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
bool HasContainerInPath(params int[] ids);
Attempt<OperationResult<OperationResultType, EntityContainer>> CreateContainer(int parentContainerId, string name, int userId = Constants.Security.SuperUserId);
Attempt<OperationResult> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
EntityContainer GetContainer(int containerId);
@@ -1,5 +1,8 @@
namespace Umbraco.Core.Sync
{
/// <summary>
/// An <see cref="IServerMessenger"/> implementation that works by storing messages in the database.
/// </summary>
public interface IBatchedDatabaseServerMessenger : IServerMessenger
{
void FlushBatch();
@@ -2,8 +2,16 @@ using Umbraco.Web.Models.Trees;
namespace Umbraco.Web.Trees
{
/// <summary>
/// Represents a factory to create <see cref="MenuItemCollection"/>.
/// </summary>
public interface IMenuItemCollectionFactory
{
/// <summary>
/// Creates an empty <see cref="MenuItemCollection"/>.
/// </summary>
/// <returns>An empty <see cref="MenuItemCollection"/>.</returns>
MenuItemCollection Create();
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.3</LangVersion>
<LangVersion>8</LangVersion>
<RootNamespace>Umbraco.Core</RootNamespace>
<AssemblyVersion>9.0.0</AssemblyVersion>
<InformationalVersion>9.0.0</InformationalVersion>
-10
View File
@@ -1,10 +0,0 @@
using System;
using Umbraco.Web.Routing;
namespace Umbraco.Core
{
public interface IUmbracoRouteEventSender
{
event EventHandler<RoutableAttemptEventArgs> RouteAttempt;
}
}
@@ -5,6 +5,7 @@
<RootNamespace>Umbraco.Examine</RootNamespace>
<Product>Umbraco CMS</Product>
<Title>Umbraco.Examine.Lucene</Title>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@@ -27,6 +27,13 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns></returns>
bool HasContainerInPath(string contentPath);
/// <summary>
/// Gets a value indicating whether there is a list view content item in the path.
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
bool HasContainerInPath(params int[] ids);
/// <summary>
/// Returns true or false depending on whether content nodes have been created based on the provided content type id.
/// </summary>
@@ -1312,14 +1312,16 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern",
return test;
}
/// <summary>
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
/// </summary>
/// <param name="contentPath"></param>
/// <returns></returns>
/// <inheritdoc />
public bool HasContainerInPath(string contentPath)
{
var ids = contentPath.Split(',').Select(int.Parse);
var ids = contentPath.Split(',').Select(int.Parse).ToArray();
return HasContainerInPath(ids);
}
/// <inheritdoc />
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 });
@@ -16,35 +16,46 @@ namespace Umbraco.Core.PropertyEditors
public IEnumerable<UmbracoEntityReference> GetAllReferences(IPropertyCollection properties, PropertyEditorCollection propertyEditors)
{
var trackedRelations = new List<UmbracoEntityReference>();
var trackedRelations = new HashSet<UmbracoEntityReference>();
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;
@@ -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)
@@ -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<TItem> GetDescendants(int id, bool andSelf)
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>7.3</LangVersion>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -12,7 +12,7 @@
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<LangVersion>7.3</LangVersion>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -364,7 +364,7 @@ namespace Umbraco.Web.PublishedCache.NuCache
public override bool EnsureEnvironment(out IEnumerable<string> 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<string>() : new[] { "NuCache local files." };
return ok;
}
@@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Umbraco.Infrastructure.PublishedCache</RootNamespace>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -12,6 +12,7 @@
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -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<IContentTypeService, SaveEventArgs<IContentType>>(null, Current.Services.ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>()), "Saved"),
new EventDefinition<IContentTypeService, SaveEventArgs<IContentType>>(null, ServiceContext.ContentTypeService, new SaveEventArgs<IContentType>(Enumerable.Empty<IContentType>()), "Saved"),
};
+1 -1
View File
@@ -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;
@@ -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);
}
@@ -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<IDataTypeService>();
private IIOHelper IOHelper { get; } = TestHelper.IOHelper;
ILocalizedTextService LocalizedTextService { get; } = Mock.Of<ILocalizedTextService>();
ILocalizationService LocalizationService { get; } = Mock.Of<ILocalizationService>();
IShortStringHelper ShortStringHelper { get; } = Mock.Of<IShortStringHelper>();
[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<ILogger>(),
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<PropertyValue>
{
// 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<IDataValueReferenceFactory>());
// mediaPicker does implement IDataValueReference
var mediaPicker = new MediaPickerPropertyEditor(
Mock.Of<ILogger>(),
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<PropertyValue>
{
// 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<IDataValueReferenceFactory>());
// mediaPicker does implement IDataValueReference
var mediaPicker = new MediaPickerPropertyEditor(
Mock.Of<ILogger>(),
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<PropertyValue>
{
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<UmbracoEntityReference> 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);
}
}
}
}
}
@@ -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<string>(), Enumerable.Empty<PublishedPropertyType>(), 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);
@@ -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" };
@@ -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,
@@ -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;
}
@@ -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;
}
@@ -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();
@@ -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");
+6 -6
View File
@@ -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<IUmbracoContextFactory>(), Mock.Of<ILogger>());
@@ -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;
}
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Security
var umbracoContext = new UmbracoContext(
httpContextAccessor,
Mock.Of<IPublishedSnapshotService>(),
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<IPublishedSnapshotService>(),
new WebSecurity(httpContextAccessor, Current.Services.UserService, globalSettings, IOHelper),
new WebSecurity(httpContextAccessor, ServiceContext.UserService, globalSettings, IOHelper),
globalSettings,
new TestVariationContextAccessor(),
IOHelper,
@@ -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;
@@ -112,6 +112,7 @@ namespace Umbraco.Tests.Testing
private TypeLoader _featureTypeLoader;
#region Accessors
protected ServiceContext ServiceContext => Factory.GetInstance<ServiceContext>();
protected ILogger Logger => Factory.GetInstance<ILogger>();
protected IJsonSerializer JsonNetSerializer { get; } = new JsonNetSerializer();
+2 -1
View File
@@ -42,7 +42,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<LangVersion>latest</LangVersion>
<LangVersion>8</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@@ -149,6 +149,7 @@
<Compile Include="Persistence\Repositories\DocumentRepositoryTest.cs" />
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
<Compile Include="UmbracoExamine\ExamineExtensions.cs" />
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionTests.cs" />
<Compile Include="PublishedContent\NuCacheChildrenTests.cs" />
<Compile Include="PublishedContent\PublishedContentLanguageVariantTests.cs" />
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.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<int>()))
.Returns(() => null);
@@ -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<IContentTypeBaseService>()
.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<DataEditor>()));
@@ -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<DataEditor>()));
@@ -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<IContent>(), It.IsAny<int>(), It.IsAny<bool>()))
.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<IContent>(), It.IsAny<int>(), It.IsAny<bool>()))
.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<IContent>(), It.IsAny<int>(), It.IsAny<bool>()))
.Returns(new OperationResult(OperationResultType.Success, new Core.Events.EventMessages())); //success
@@ -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<IUser>(), It.IsAny<bool>()))
.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(
@@ -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,
@@ -32,7 +32,7 @@ namespace Umbraco.Tests.Web
var umbCtx = new UmbracoContext(
httpContextAccessor,
Mock.Of<IPublishedSnapshotService>(),
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<IPublishedSnapshotService>(),
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<IPublishedSnapshotService>(),
new WebSecurity(httpContextAccessor, Current.Services.UserService, TestObjects.GetGlobalSettings(), IOHelper),
new WebSecurity(httpContextAccessor, ServiceContext.UserService, TestObjects.GetGlobalSettings(), IOHelper),
TestObjects.GetGlobalSettings(),
new TestVariationContextAccessor(),
IOHelper,
@@ -2,6 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>8</LangVersion>
</PropertyGroup>
<ItemGroup>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 581 KiB

@@ -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>
</ng-form>
@@ -10,7 +10,8 @@
bindings: {
model: "=",
onStartTyping: "&?",
onSearch: "&?"
onSearch: "&?",
onBlur: "&?"
}
});
@@ -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;
@@ -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);
@@ -28,7 +28,7 @@
</umb-editor-sub-header-section>
<umb-editor-sub-header-section>
<umb-mini-search model="vm.usersOptions.filter" on-search="vm.searchUsers()" on-start-typing="vm.searchUsers()">
<umb-mini-search model="vm.usersOptions.filter" on-search="vm.searchUsers()" on-blur="vm.onBlurSearch()">
</umb-mini-search>
</umb-editor-sub-header-section>
+5 -2
View File
@@ -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
/// <returns></returns>
private ContentItemDisplay MapToDisplay(IContent content)
{
var display = Mapper.Map<ContentItemDisplay>(content);
var display = Mapper.Map<ContentItemDisplay>(content, context =>
{
context.Items["CurrentUser"] = Security.CurrentUser;
});
display.AllowPreview = display.AllowPreview && content.Trashed == false && content.ContentType.IsElement == false;
return display;
}
@@ -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;
}
/// <summary>
/// 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<InstallApiController>($"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);
@@ -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;
@@ -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<ContentPropertyBasic> _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)
/// <summary>
/// Checks if the content item is a descendant of a list view
/// </summary>
/// <param name="source"></param>
/// <param name="context"></param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>
/// 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.
/// </remarks>
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<int>();
// 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;
@@ -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;
}
@@ -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<ActionDelete>(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("/")
+1 -44
View File
@@ -111,10 +111,6 @@
<Project>{29aa69d9-b597-4395-8d42-43b1263c240a}</Project>
<Name>Umbraco.Core</Name>
</ProjectReference>
<ProjectReference Include="..\Umbraco.Examine\Umbraco.Examine.csproj">
<Project>{f9b7fe05-0f93-4d0d-9c10-690b33ecbbd8}</Project>
<Name>Umbraco.Examine</Name>
</ProjectReference>
<ProjectReference Include="..\Umbraco.Infrastructure\Umbraco.Infrastructure.csproj">
<Project>{3ae7bf57-966b-45a5-910a-954d7c554441}</Project>
<Name>Umbraco.Infrastructure</Name>
@@ -600,47 +596,8 @@
<None Include="..\Umbraco.Web.UI\Views\web.config">
<Link>Mvc\web.config</Link>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<WebReferenceUrl Include="http://update.umbraco.org/checkforupgrade.asmx">
<UrlBehavior>Dynamic</UrlBehavior>
<RelPath>Web References\org.umbraco.update\</RelPath>
<UpdateFromURL>http://update.umbraco.org/checkforupgrade.asmx</UpdateFromURL>
<ServiceLocationURL>
</ServiceLocationURL>
<CachedDynamicPropName>
</CachedDynamicPropName>
<CachedAppSettingsObjectName>Settings</CachedAppSettingsObjectName>
<CachedSettingsPropName>umbraco_org_umbraco_update_CheckForUpgrade</CachedSettingsPropName>
</WebReferenceUrl>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!--
copied from Microsoft.CSharp.targets
because we have webservices, we need to SGEN
but it's getting confused by us referencing System.ValueTuple which it cannot load
Name="UmbGenerateSerializationAssemblies"
Condition="'$(_SGenGenerateSerializationAssembliesConfig)' == 'On' or ('@(WebReferenceUrl)'!='' and '$(_SGenGenerateSerializationAssembliesConfig)' == 'Auto')"
-->
<Target Name="AfterBuild" DependsOnTargets="AssignTargetPaths;Compile;ResolveKeySource" Inputs="$(MSBuildAllProjects);@(IntermediateAssembly)" Outputs="$(IntermediateOutputPath)$(_SGenDllName)">
<PropertyGroup>
<SGenMSBuildArchitecture Condition="'$(SGenMSBuildArchitecture)' == ''">$(PlatformTargetAsMSBuildArchitecture)</SGenMSBuildArchitecture>
</PropertyGroup>
<ItemGroup>
<!-- we want to exclude all facade references ?! -->
<FixedReferencePath Include="@(ReferencePath)" Condition="'%(ReferencePath.FileName)' != 'System.ValueTuple' and '%(ReferencePath.FileName)' != 'System.Net.Http' and '%(ReferencePath.FileName)' != 'System.Threading.Tasks.Extensions'" />
</ItemGroup>
<Delete Files="$(TargetDir)$(TargetName).XmlSerializers.dll" ContinueOnError="true" />
<!--
ShouldGenerateSerializer="$(SGenShouldGenerateSerializer)"
-->
<SGen BuildAssemblyName="$(TargetFileName)" BuildAssemblyPath="$(IntermediateOutputPath)" References="@(FixedReferencePath)" ShouldGenerateSerializer="true" UseProxyTypes="$(SGenUseProxyTypes)" UseKeep="$(SGenUseKeep)" KeyContainer="$(KeyContainerName)" KeyFile="$(KeyOriginatorFile)" DelaySign="$(DelaySign)" ToolPath="$(SGenToolPath)" SdkToolsPath="$(TargetFrameworkSDKToolsDirectory)" EnvironmentVariables="$(SGenEnvironment)" MSBuildArchitecture="$(SGenMSBuildArchitecture)" SerializationAssembly="$(IntermediateOutputPath)$(_SGenDllName)" Platform="$(SGenPlatformTarget)" Types="$(SGenSerializationTypes)">
<Output TaskParameter="SerializationAssembly" ItemName="SerializationAssembly" />
</SGen>
</Target>
</Project>