Merge remote-tracking branch 'origin/v9/dev' into v9/feature/language-keys-cleanup
This commit is contained in:
@@ -42,4 +42,8 @@ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
|
||||
# Needing to set unsafe-perm as root is the user setup
|
||||
# https://docs.npmjs.com/cli/v6/using-npm/config#unsafe-perm
|
||||
# Default: false if running as root, true otherwise (we are ROOT)
|
||||
RUN npm -g config set user vscode && npm -g config set unsafe-perm
|
||||
RUN npm -g config set user vscode && npm -g config set unsafe-perm
|
||||
|
||||
# Generate and trust a local developer certificate for Kestrel
|
||||
# This is needed for Kestrel to bind on https
|
||||
RUN dotnet dev-certs https --trust
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3'
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
|
||||
volumes:
|
||||
- ..:/workspace:cached
|
||||
|
||||
|
||||
# Overrides default command so things don't shut down after the process ends.
|
||||
command: sleep infinity
|
||||
|
||||
@@ -28,7 +28,7 @@ services:
|
||||
# Uncomment the next line to use a non-root user for all processes.
|
||||
# user: vscode
|
||||
|
||||
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
|
||||
# Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
|
||||
# (Adding the "ports" property to this file will not forward from a Codespace.)
|
||||
|
||||
# DotNetCore ENV Variables
|
||||
@@ -41,7 +41,7 @@ services:
|
||||
- Umbraco__CMS__Unattended__UnattendedUserPassword=password1234
|
||||
- Umbraco__CMS__Global__Smtp__Host=smtp4dev
|
||||
- Umbraco__CMS__Global__Smtp__Port=25
|
||||
- Umbraco__CMS__Global__Smtp__From=warren-env@umbraco.com
|
||||
- Umbraco__CMS__Global__Smtp__From=noreply@umbraco.test
|
||||
|
||||
db:
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1,3 +1,4 @@
|
||||
* text=auto
|
||||
*.doc diff=astextplain
|
||||
*.DOC diff=astextplain
|
||||
*.docx diff=astextplain
|
||||
@@ -44,6 +45,7 @@
|
||||
*.json text=auto
|
||||
*.xml text=auto
|
||||
*.resx text=auto
|
||||
*.yml text eol=lf core.whitespace whitespace=tab-in-indent,trailing-space,tabwidth=2
|
||||
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
|
||||
29
build/NuSpecs/tools/Web.config.cloud.xdt
Normal file
29
build/NuSpecs/tools/Web.config.cloud.xdt
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
|
||||
<runtime xdt:Transform="InsertIfMissing" />
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1" xdt:Transform="InsertIfMissing" />
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Collections.Immutable')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Buffers')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Memory')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Numerics.Vectors')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.2.5.0" newVersion="1.2.5.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" />
|
||||
<bindingRedirect oldVersion="4.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" />
|
||||
<bindingRedirect oldVersion="4.0.0.0-4.0.1.1" newVersion="4.0.1.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="System.Numerics.Vectors" publicKeyToken="b03f5f7f11d50a3a" />
|
||||
<bindingRedirect oldVersion="4.0.0.0-4.1.4.0" newVersion="4.1.4.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -92,6 +92,7 @@
|
||||
$src = "$($this.SolutionRoot)\src"
|
||||
$log = "$($this.BuildTemp)\belle.log"
|
||||
|
||||
|
||||
Write-Host "Compile Belle"
|
||||
Write-Host "Logging to $log"
|
||||
|
||||
@@ -555,7 +556,6 @@
|
||||
# run
|
||||
if (-not $get)
|
||||
{
|
||||
cd
|
||||
if ($command.Length -eq 0)
|
||||
{
|
||||
$command = @( "Build" )
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">Umbraco.Cms.Web.UI</RootNamespace>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);App_Plugins/**;</DefaultItemExcludes>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);umbraco/**;</DefaultItemExcludes>
|
||||
<DefaultItemExcludes>$(DefaultItemExcludes);wwwroot/media/**;</DefaultItemExcludes>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -10,42 +14,18 @@
|
||||
<PackageReference Include="Umbraco.SqlServerCE" Version="4.0.0.1" Condition="'$(UseSqlCe)' == 'true'" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\PackageTestSiteName\build\PackageTestSiteName.targets" Condition="'$(PackageTestSiteName)' != ''"/>
|
||||
<Import Project="..\PackageTestSiteName\build\PackageTestSiteName.targets" Condition="'$(PackageTestSiteName)' != ''" />
|
||||
|
||||
<ItemGroup Condition="'$(PackageTestSiteName)' != ''">
|
||||
<ProjectReference Include="..\PackageTestSiteName\PackageTestSiteName.csproj"/>
|
||||
<ProjectReference Include="..\PackageTestSiteName\PackageTestSiteName.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Remove="umbraco\Data\**" />
|
||||
<Content Remove="umbraco\logs\**" />
|
||||
<Content Remove="umbraco\MediaCache\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="umbraco\Data\**" />
|
||||
<Compile Remove="umbraco\logs\**" />
|
||||
<Compile Remove="umbraco\MediaCache\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Remove="umbraco\Data\**" />
|
||||
<EmbeddedResource Remove="umbraco\logs\**" />
|
||||
<EmbeddedResource Remove="umbraco\MediaCache\**" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="config\**\*.*">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
</None>
|
||||
<None Include="umbraco\**\*.*">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
</None>
|
||||
<None Include="App_Plugins\**\*.*">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
<CopyToPublishDirectory>Always</CopyToPublishDirectory>
|
||||
</None>
|
||||
<None Remove="umbraco\Data\**" />
|
||||
<None Remove="umbraco\logs\**" />
|
||||
<None Remove="umbraco\MediaCache\**" />
|
||||
<Content Include="App_Plugins/**" CopyToOutputDirectory="Always" />
|
||||
<Content Include="umbraco/**" CopyToOutputDirectory="Always" />
|
||||
<Content Remove="umbraco/Data/**" />
|
||||
<Content Remove="umbraco/Logs/**" />
|
||||
<Content Remove="umbraco/mediacache/**"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Set this to true if ModelsBuilder mode is not InMemoryAuto-->
|
||||
@@ -53,4 +33,5 @@
|
||||
<RazorCompileOnBuild>false</RazorCompileOnBuild>
|
||||
<RazorCompileOnPublish>false</RazorCompileOnPublish>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
12
src/Umbraco.Core/Cache/IValueEditorCache.cs
Normal file
12
src/Umbraco.Core/Cache/IValueEditorCache.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public interface IValueEditorCache
|
||||
{
|
||||
public IDataValueEditor GetValueEditor(IDataEditor dataEditor, IDataType dataType);
|
||||
public void ClearCache(IEnumerable<int> dataTypeIds);
|
||||
}
|
||||
}
|
||||
61
src/Umbraco.Core/Cache/ValueEditorCache.cs
Normal file
61
src/Umbraco.Core/Cache/ValueEditorCache.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class ValueEditorCache : IValueEditorCache
|
||||
{
|
||||
private readonly Dictionary<string, Dictionary<int, IDataValueEditor>> _valueEditorCache;
|
||||
private readonly object _dictionaryLocker;
|
||||
|
||||
public ValueEditorCache()
|
||||
{
|
||||
_valueEditorCache = new Dictionary<string, Dictionary<int, IDataValueEditor>>();
|
||||
_dictionaryLocker = new object();
|
||||
}
|
||||
|
||||
public IDataValueEditor GetValueEditor(IDataEditor editor, IDataType dataType)
|
||||
{
|
||||
// Lock just in case multiple threads uses the cache at the same time.
|
||||
lock (_dictionaryLocker)
|
||||
{
|
||||
// We try and get the dictionary based on the IDataEditor alias,
|
||||
// this is here just in case a data type can have more than one value data editor.
|
||||
// If this is not the case this could be simplified quite a bit, by just using the inner dictionary only.
|
||||
IDataValueEditor valueEditor;
|
||||
if (_valueEditorCache.TryGetValue(editor.Alias, out Dictionary<int, IDataValueEditor> dataEditorCache))
|
||||
{
|
||||
if (dataEditorCache.TryGetValue(dataType.Id, out valueEditor))
|
||||
{
|
||||
return valueEditor;
|
||||
}
|
||||
|
||||
valueEditor = editor.GetValueEditor(dataType.Configuration);
|
||||
dataEditorCache[dataType.Id] = valueEditor;
|
||||
return valueEditor;
|
||||
}
|
||||
|
||||
valueEditor = editor.GetValueEditor(dataType.Configuration);
|
||||
_valueEditorCache[editor.Alias] = new Dictionary<int, IDataValueEditor> { [dataType.Id] = valueEditor };
|
||||
return valueEditor;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearCache(IEnumerable<int> dataTypeIds)
|
||||
{
|
||||
lock (_dictionaryLocker)
|
||||
{
|
||||
// If a datatype is saved or deleted we have to clear any value editors based on their ID from the cache,
|
||||
// since it could mean that their configuration has changed.
|
||||
foreach (var id in dataTypeIds)
|
||||
{
|
||||
foreach (Dictionary<int, IDataValueEditor> editors in _valueEditorCache.Values)
|
||||
{
|
||||
editors.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/Umbraco.Core/Cache/ValueEditorCacheRefresher.cs
Normal file
57
src/Umbraco.Core/Cache/ValueEditorCacheRefresher.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class ValueEditorCacheRefresher : PayloadCacheRefresherBase<DataTypeCacheRefresherNotification, DataTypeCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IValueEditorCache _valueEditorCache;
|
||||
|
||||
public ValueEditorCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory,
|
||||
IValueEditorCache valueEditorCache) : base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_valueEditorCache = valueEditorCache;
|
||||
}
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("D28A1DBB-2308-4918-9A92-2F8689B6CBFE");
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
public override string Name => "ValueEditorCacheRefresher";
|
||||
|
||||
public override void Refresh(DataTypeCacheRefresher.JsonPayload[] payloads)
|
||||
{
|
||||
IEnumerable<int> ids = payloads.Select(x => x.Id);
|
||||
_valueEditorCache.ClearCache(ids);
|
||||
}
|
||||
|
||||
// these events should never trigger
|
||||
// everything should be PAYLOAD/JSON
|
||||
|
||||
public override void RefreshAll()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Refresh(Guid id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,6 +253,9 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
|
||||
// register a basic/noop published snapshot service to be replaced
|
||||
Services.AddSingleton<IPublishedSnapshotService, InternalPublishedSnapshotService>();
|
||||
|
||||
// Register ValueEditorCache used for validation
|
||||
Services.AddSingleton<IValueEditorCache, ValueEditorCache>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,16 +258,17 @@ namespace Umbraco.Cms.Core.IO
|
||||
// test URL
|
||||
var path = fullPathOrUrl.Replace('\\', '/'); // ensure URL separator char
|
||||
|
||||
// if it starts with the root path, strip it and trim the starting slash to make it relative
|
||||
// eg "c:/websites/test/root/Media/1234/img.jpg" => "1234/img.jpg"
|
||||
// or on unix systems "/var/wwwroot/test/Meia/1234/img.jpg"
|
||||
if (_ioHelper.PathStartsWith(path, _rootPathFwd, '/'))
|
||||
return path.Substring(_rootPathFwd.Length).TrimStart(Constants.CharArrays.ForwardSlash);
|
||||
|
||||
// if it starts with the root URL, strip it and trim the starting slash to make it relative
|
||||
// eg "/Media/1234/img.jpg" => "1234/img.jpg"
|
||||
if (_ioHelper.PathStartsWith(path, _rootUrl, '/'))
|
||||
return path.Substring(_rootUrl.Length).TrimStart(Constants.CharArrays.ForwardSlash);
|
||||
|
||||
// if it starts with the root path, strip it and trim the starting slash to make it relative
|
||||
// eg "c:/websites/test/root/Media/1234/img.jpg" => "1234/img.jpg"
|
||||
if (_ioHelper.PathStartsWith(path, _rootPathFwd, '/'))
|
||||
return path.Substring(_rootPathFwd.Length).TrimStart(Constants.CharArrays.ForwardSlash);
|
||||
|
||||
// unchanged - what else?
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.IO;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
|
||||
namespace Umbraco.Cms.Core.IO
|
||||
{
|
||||
public class SystemFiles
|
||||
{
|
||||
public static string TinyMceConfig => Constants.SystemDirectories.Config + "/tinyMceConfig.config";
|
||||
|
||||
// TODO: Kill this off we don't have umbraco.config XML cache we now have NuCache
|
||||
public static string GetContentCacheXml(IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
return Path.Combine(hostingEnvironment.LocalTempPath, "umbraco.config");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Models
|
||||
@@ -15,9 +15,9 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="name">name of the Media object</param>
|
||||
/// <param name="parent">Parent <see cref="IMedia"/> object</param>
|
||||
/// <param name="contentType">MediaType for the current Media object</param>
|
||||
public Media(string name, IMedia parent, IMediaType contentType)
|
||||
: this(name, parent, contentType, new PropertyCollection())
|
||||
/// <param name="mediaType">MediaType for the current Media object</param>
|
||||
public Media(string name, IMedia parent, IMediaType mediaType)
|
||||
: this(name, parent, mediaType, new PropertyCollection())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
@@ -25,10 +25,10 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="name">name of the Media object</param>
|
||||
/// <param name="parent">Parent <see cref="IMedia"/> object</param>
|
||||
/// <param name="contentType">MediaType for the current Media object</param>
|
||||
/// <param name="mediaType">MediaType for the current Media object</param>
|
||||
/// <param name="properties">Collection of properties</param>
|
||||
public Media(string name, IMedia parent, IMediaType contentType, IPropertyCollection properties)
|
||||
: base(name, parent, contentType, properties)
|
||||
public Media(string name, IMedia parent, IMediaType mediaType, IPropertyCollection properties)
|
||||
: base(name, parent, mediaType, properties)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
@@ -36,9 +36,9 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="name">name of the Media object</param>
|
||||
/// <param name="parentId">Id of the Parent IMedia</param>
|
||||
/// <param name="contentType">MediaType for the current Media object</param>
|
||||
public Media(string name, int parentId, IMediaType contentType)
|
||||
: this(name, parentId, contentType, new PropertyCollection())
|
||||
/// <param name="mediaType">MediaType for the current Media object</param>
|
||||
public Media(string name, int parentId, IMediaType mediaType)
|
||||
: this(name, parentId, mediaType, new PropertyCollection())
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
@@ -46,10 +46,10 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="name">Name of the Media object</param>
|
||||
/// <param name="parentId">Id of the Parent IMedia</param>
|
||||
/// <param name="contentType">MediaType for the current Media object</param>
|
||||
/// <param name="mediaType">MediaType for the current Media object</param>
|
||||
/// <param name="properties">Collection of properties</param>
|
||||
public Media(string name, int parentId, IMediaType contentType, IPropertyCollection properties)
|
||||
: base(name, parentId, contentType, properties)
|
||||
public Media(string name, int parentId, IMediaType mediaType, IPropertyCollection properties)
|
||||
: base(name, parentId, mediaType, properties)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
@@ -57,9 +57,9 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="contentType">New MediaType for this Media</param>
|
||||
/// <remarks>Leaves PropertyTypes intact after change</remarks>
|
||||
internal void ChangeContentType(IMediaType contentType)
|
||||
internal void ChangeContentType(IMediaType mediaType)
|
||||
{
|
||||
ChangeContentType(contentType, false);
|
||||
ChangeContentType(mediaType, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,14 +68,14 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// </summary>
|
||||
/// <param name="contentType">New MediaType for this Media</param>
|
||||
/// <param name="clearProperties">Boolean indicating whether to clear PropertyTypes upon change</param>
|
||||
internal void ChangeContentType(IMediaType contentType, bool clearProperties)
|
||||
internal void ChangeContentType(IMediaType mediaType, bool clearProperties)
|
||||
{
|
||||
ChangeContentType(new SimpleContentType(contentType));
|
||||
ChangeContentType(new SimpleContentType(mediaType));
|
||||
|
||||
if (clearProperties)
|
||||
Properties.EnsureCleanPropertyTypes(contentType.CompositionPropertyTypes);
|
||||
Properties.EnsureCleanPropertyTypes(mediaType.CompositionPropertyTypes);
|
||||
else
|
||||
Properties.EnsurePropertyTypes(contentType.CompositionPropertyTypes);
|
||||
Properties.EnsurePropertyTypes(mediaType.CompositionPropertyTypes);
|
||||
|
||||
Properties.ClearCollectionChangedEvents(); // be sure not to double add
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
public class DataEditor : IDataEditor
|
||||
{
|
||||
private IDictionary<string, object> _defaultConfiguration;
|
||||
private IDataValueEditor _reusableEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataEditor"/> class.
|
||||
@@ -90,8 +89,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
/// simple enough for now.</para>
|
||||
/// </remarks>
|
||||
// TODO: point of that one? shouldn't we always configure?
|
||||
public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? (_reusableEditor ?? (_reusableEditor = CreateValueEditor()));
|
||||
|
||||
public IDataValueEditor GetValueEditor() => ExplicitValueEditor ?? CreateValueEditor();
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
|
||||
@@ -130,6 +130,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
_distributedCache.RefreshDataTypeCache(entity);
|
||||
}
|
||||
_distributedCache.RefreshValueEditorCache(notification.SavedEntities);
|
||||
}
|
||||
|
||||
public void Handle(DataTypeDeletedNotification notification)
|
||||
@@ -138,6 +139,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
_distributedCache.RemoveDataTypeCache(entity);
|
||||
}
|
||||
_distributedCache.RefreshValueEditorCache(notification.DeletedEntities);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -106,6 +107,21 @@ namespace Umbraco.Extensions
|
||||
|
||||
#endregion
|
||||
|
||||
#region ValueEditorCache
|
||||
|
||||
public static void RefreshValueEditorCache(this DistributedCache dc, IEnumerable<IDataType> dataTypes)
|
||||
{
|
||||
if (dataTypes is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = dataTypes.Select(x => new DataTypeCacheRefresher.JsonPayload(x.Id, x.Key, false));
|
||||
dc.RefreshByPayload(ValueEditorCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentCache
|
||||
|
||||
public static void RefreshAllContentCache(this DistributedCache dc)
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeepAlive"/> class.
|
||||
/// </summary>
|
||||
/// <param name="requestAccessor">Accessor for the current request.</param>
|
||||
/// <param name="hostingEnvironment">The current hosting environment</param>
|
||||
/// <param name="mainDom">Representation of the main application domain.</param>
|
||||
/// <param name="keepAliveSettings">The configuration for keep alive settings.</param>
|
||||
/// <param name="logger">The typed logger.</param>
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
|
||||
using (_profilingLogger.DebugDuration<KeepAlive>("Keep alive executing", "Keep alive complete"))
|
||||
{
|
||||
var umbracoAppUrl = _hostingEnvironment.ApplicationMainUrl.ToString();
|
||||
var umbracoAppUrl = _hostingEnvironment.ApplicationMainUrl?.ToString();
|
||||
if (umbracoAppUrl.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.LogWarning("No umbracoApplicationUrl for service (yet), skip.");
|
||||
|
||||
@@ -927,6 +927,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence
|
||||
|
||||
return p.ExitCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
@@ -17,6 +18,12 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
public class BlockEditorPropertyHandler : ComplexPropertyEditorContentNotificationHandler
|
||||
{
|
||||
private readonly BlockListEditorDataConverter _converter = new BlockListEditorDataConverter();
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public BlockEditorPropertyHandler(ILogger<BlockEditorPropertyHandler> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
protected override string EditorAlias => Constants.PropertyEditors.Aliases.BlockList;
|
||||
|
||||
@@ -110,8 +117,22 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
// this gets a little ugly because there could be some other complex editor that contains another block editor
|
||||
// and since we would have no idea how to parse that, all we can do is try JSON Path to find another block editor
|
||||
// of our type
|
||||
var json = JToken.Parse(asString);
|
||||
if (ProcessJToken(json, createGuid, out var result))
|
||||
JToken json = null;
|
||||
try
|
||||
{
|
||||
json = JToken.Parse(asString);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// See issue https://github.com/umbraco/Umbraco-CMS/issues/10879
|
||||
// We are detecting JSON data by seeing if a string is surrounded by [] or {}
|
||||
// If people enter text like [PLACEHOLDER] JToken parsing fails, it's safe to ignore though
|
||||
// Logging this just in case in the future we find values that are not safe to ignore
|
||||
_logger.LogWarning( "The property {PropertyAlias} on content type {ContentTypeKey} has a value of: {BlockItemValue} - this was recognized as JSON but could not be parsed",
|
||||
data.Key, propertyAliasToBlockItemData.Key, asString);
|
||||
}
|
||||
|
||||
if (json != null && ProcessJToken(json, createGuid, out var result))
|
||||
{
|
||||
// need to re-save this back to the RawPropertyValues
|
||||
data.RawPropertyValues[propertyAliasToBlockItemData.Key] = result;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
@@ -13,12 +14,18 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IValueEditorCache _valueEditorCache;
|
||||
|
||||
public PropertyValidationService(PropertyEditorCollection propertyEditors, IDataTypeService dataTypeService, ILocalizedTextService textService)
|
||||
public PropertyValidationService(
|
||||
PropertyEditorCollection propertyEditors,
|
||||
IDataTypeService dataTypeService,
|
||||
ILocalizedTextService textService,
|
||||
IValueEditorCache valueEditorCache)
|
||||
{
|
||||
_propertyEditors = propertyEditors;
|
||||
_dataTypeService = dataTypeService;
|
||||
_textService = textService;
|
||||
_valueEditorCache = valueEditorCache;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -58,7 +65,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_textService.Localize("validation", "invalidPattern"),
|
||||
};
|
||||
|
||||
var valueEditor = editor.GetValueEditor(dataType.Configuration);
|
||||
IDataValueEditor valueEditor = _valueEditorCache.GetValueEditor(editor, dataType);
|
||||
foreach (var validationResult in valueEditor.Validate(postedValue, isRequired, validationRegExp))
|
||||
{
|
||||
// If we've got custom error messages, we'll replace the default ones that will have been applied in the call to Validate().
|
||||
|
||||
@@ -9,6 +9,7 @@ using System.Threading;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -72,6 +73,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
|
||||
private IJsonSerializer Serializer => GetRequiredService<IJsonSerializer>();
|
||||
|
||||
private IValueEditorCache ValueEditorCache => GetRequiredService<IValueEditorCache>();
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => ContentRepositoryBase.ThrowOnWarning = true;
|
||||
|
||||
@@ -1162,7 +1165,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
|
||||
Assert.IsFalse(content.HasIdentity);
|
||||
|
||||
// content cannot publish values because they are invalid
|
||||
var propertyValidationService = new PropertyValidationService(PropertyEditorCollection, DataTypeService, TextService);
|
||||
var propertyValidationService = new PropertyValidationService(PropertyEditorCollection, DataTypeService, TextService, ValueEditorCache);
|
||||
bool isValid = propertyValidationService.IsPropertyDataValid(content, out IProperty[] invalidProperties, CultureImpact.Invariant);
|
||||
Assert.IsFalse(isValid);
|
||||
Assert.IsNotEmpty(invalidProperties);
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Cache
|
||||
{
|
||||
[TestFixture]
|
||||
public class ValueEditorCacheTests
|
||||
{
|
||||
|
||||
[Test]
|
||||
public void Caches_ValueEditor()
|
||||
{
|
||||
var sut = new ValueEditorCache();
|
||||
|
||||
var dataEditor = new FakeDataEditor("TestEditor");
|
||||
IDataType dataType = CreateDataTypeMock(1).Object;
|
||||
|
||||
// Request the same value editor twice
|
||||
IDataValueEditor firstEditor = sut.GetValueEditor(dataEditor, dataType);
|
||||
IDataValueEditor secondEditor = sut.GetValueEditor(dataEditor, dataType);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.AreSame(firstEditor, secondEditor);
|
||||
Assert.AreEqual(1, dataEditor.ValueEditorCount, "GetValueEditor invoked more than once.");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Different_Data_Editors_Returns_Different_Value_Editors()
|
||||
{
|
||||
var sut = new ValueEditorCache();
|
||||
|
||||
var dataEditor1 = new FakeDataEditor("Editor1");
|
||||
var dataEditor2 = new FakeDataEditor("Editor2");
|
||||
IDataType dataType = CreateDataTypeMock(1).Object;
|
||||
|
||||
IDataValueEditor firstEditor = sut.GetValueEditor(dataEditor1, dataType);
|
||||
IDataValueEditor secondEditor = sut.GetValueEditor(dataEditor2, dataType);
|
||||
|
||||
Assert.AreNotSame(firstEditor, secondEditor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Different_Data_Types_Returns_Different_Value_Editors()
|
||||
{
|
||||
var sut = new ValueEditorCache();
|
||||
var dataEditor = new FakeDataEditor("Editor");
|
||||
IDataType dataType1 = CreateDataTypeMock(1).Object;
|
||||
IDataType dataType2 = CreateDataTypeMock(2).Object;
|
||||
|
||||
IDataValueEditor firstEditor = sut.GetValueEditor(dataEditor, dataType1);
|
||||
IDataValueEditor secondEditor = sut.GetValueEditor(dataEditor, dataType2);
|
||||
|
||||
Assert.AreNotSame(firstEditor, secondEditor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Clear_Cache_Removes_Specific_Editors()
|
||||
{
|
||||
var sut = new ValueEditorCache();
|
||||
|
||||
var dataEditor1 = new FakeDataEditor("Editor 1");
|
||||
var dataEditor2 = new FakeDataEditor("Editor 2");
|
||||
|
||||
IDataType dataType1 = CreateDataTypeMock(1).Object;
|
||||
IDataType dataType2 = CreateDataTypeMock(2).Object;
|
||||
|
||||
// Load the editors into cache
|
||||
IDataValueEditor editor1DataType1 = sut.GetValueEditor(dataEditor1, dataType1);
|
||||
IDataValueEditor editor1Datatype2 = sut.GetValueEditor(dataEditor1, dataType2);
|
||||
IDataValueEditor editor2DataType1 = sut.GetValueEditor(dataEditor2, dataType1);
|
||||
IDataValueEditor editor2Datatype2 = sut.GetValueEditor(dataEditor2, dataType2);
|
||||
|
||||
sut.ClearCache(new []{dataType1.Id});
|
||||
|
||||
// New value editor objects should be created after it's cleared
|
||||
Assert.AreNotSame(editor1DataType1, sut.GetValueEditor(dataEditor1, dataType1), "Value editor was not cleared from cache");
|
||||
Assert.AreNotSame(editor2DataType1, sut.GetValueEditor(dataEditor2, dataType1), "Value editor was not cleared from cache");
|
||||
|
||||
// But the value editors for data type 2 should be the same
|
||||
Assert.AreSame(editor1Datatype2, sut.GetValueEditor(dataEditor1, dataType2), "Too many editors was cleared from cache");
|
||||
Assert.AreSame(editor2Datatype2, sut.GetValueEditor(dataEditor2, dataType2), "Too many editors was cleared from cache");
|
||||
}
|
||||
|
||||
|
||||
private Mock<IDataType> CreateDataTypeMock(int id)
|
||||
{
|
||||
var mock = new Mock<IDataType>();
|
||||
mock.Setup(x => x.Id).Returns(id);
|
||||
return mock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A fake IDataEditor
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is necessary to ensure that different objects are returned from GetValueEditor
|
||||
/// </remarks>
|
||||
private class FakeDataEditor : IDataEditor
|
||||
{
|
||||
public FakeDataEditor(string alias)
|
||||
{
|
||||
Alias = alias;
|
||||
}
|
||||
|
||||
public string Alias { get; }
|
||||
public EditorType Type { get; }
|
||||
public string Name { get; }
|
||||
public string Icon { get; }
|
||||
public string Group { get; }
|
||||
public bool IsDeprecated { get; }
|
||||
|
||||
public IDataValueEditor GetValueEditor()
|
||||
{
|
||||
ValueEditorCount++;
|
||||
return Mock.Of<IDataValueEditor>();
|
||||
}
|
||||
public IDataValueEditor GetValueEditor(object configuration) => GetValueEditor();
|
||||
|
||||
public IDictionary<string, object> DefaultConfiguration { get; }
|
||||
public IConfigurationEditor GetConfigurationEditor() => throw new System.NotImplementedException();
|
||||
|
||||
public IPropertyIndexValueFactory PropertyIndexValueFactory { get; }
|
||||
|
||||
public int ValueEditorCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -603,7 +604,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Models
|
||||
return new PropertyValidationService(
|
||||
propertyEditorCollection,
|
||||
dataTypeService,
|
||||
localizedTextService);
|
||||
localizedTextService,
|
||||
new ValueEditorCache());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -28,11 +30,15 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors
|
||||
private const string SubContentGuid1 = "4c44ce6b3a5c4f5f8f15e3dc24819a9e";
|
||||
private const string SubContentGuid2 = "a062c06d6b0b44ac892b35d90309c7f8";
|
||||
private const string SubSettingsGuid1 = "4d998d980ffa4eee8afdc23c4abd6d29";
|
||||
private static readonly ILogger<BlockEditorPropertyHandler> s_logger = Mock.Of<ILogger<BlockEditorPropertyHandler>>();
|
||||
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public void Cannot_Have_Null_Udi()
|
||||
{
|
||||
var component = new BlockEditorPropertyHandler();
|
||||
var component = new BlockEditorPropertyHandler(s_logger);
|
||||
var json = GetBlockListJson(null, string.Empty);
|
||||
Assert.Throws<FormatException>(() => component.ReplaceBlockListUdis(json));
|
||||
}
|
||||
@@ -48,7 +54,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors
|
||||
|
||||
var expected = ReplaceGuids(json, guids, ContentGuid1, ContentGuid2, SettingsGuid1);
|
||||
|
||||
var component = new BlockEditorPropertyHandler();
|
||||
var component = new BlockEditorPropertyHandler(s_logger);
|
||||
var result = component.ReplaceBlockListUdis(json, GuidFactory);
|
||||
|
||||
var expectedJson = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(expected, _serializerSettings), _serializerSettings);
|
||||
@@ -75,7 +81,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors
|
||||
// get the json with the subFeatures as escaped
|
||||
var json = GetBlockListJson(innerJsonEscaped);
|
||||
|
||||
var component = new BlockEditorPropertyHandler();
|
||||
var component = new BlockEditorPropertyHandler(s_logger);
|
||||
var result = component.ReplaceBlockListUdis(json, GuidFactory);
|
||||
|
||||
// the expected result is that the subFeatures data is no longer escaped
|
||||
@@ -119,7 +125,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors
|
||||
SubContentGuid2,
|
||||
SubSettingsGuid1);
|
||||
|
||||
var component = new BlockEditorPropertyHandler();
|
||||
var component = new BlockEditorPropertyHandler(s_logger);
|
||||
var result = component.ReplaceBlockListUdis(json, GuidFactory);
|
||||
|
||||
var expectedJson = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(expected, _serializerSettings), _serializerSettings);
|
||||
@@ -147,7 +153,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors
|
||||
|
||||
var json = GetBlockListJson(complexEditorJsonEscaped);
|
||||
|
||||
var component = new BlockEditorPropertyHandler();
|
||||
var component = new BlockEditorPropertyHandler(s_logger);
|
||||
var result = component.ReplaceBlockListUdis(json, GuidFactory);
|
||||
|
||||
// the expected result is that the subFeatures data is no longer escaped
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Threading;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -45,7 +46,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Services
|
||||
|
||||
var propEditors = new PropertyEditorCollection(new DataEditorCollection(() => new[] { dataEditor }));
|
||||
|
||||
validationService = new PropertyValidationService(propEditors, dataTypeService.Object, Mock.Of<ILocalizedTextService>());
|
||||
validationService = new PropertyValidationService(propEditors, dataTypeService.Object, Mock.Of<ILocalizedTextService>(), new ValueEditorCache());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -22,16 +22,18 @@ namespace Umbraco.Extensions
|
||||
/// </summary>
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="cropAlias">The crop alias e.g. thumbnail.</param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this IPublishedContent mediaItem,
|
||||
string cropAlias) =>
|
||||
mediaItem.GetCropUrl(cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
|
||||
string cropAlias,
|
||||
UrlMode urlMode = UrlMode.Default) =>
|
||||
mediaItem.GetCropUrl(cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode);
|
||||
|
||||
public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string cropAlias)
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaWithCrops, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
|
||||
public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string cropAlias, UrlMode urlMode = UrlMode.Default)
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaWithCrops, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the crop URL by using only the specified <paramref name="imageCropperValue" />.
|
||||
@@ -39,14 +41,16 @@ namespace Umbraco.Extensions
|
||||
/// <param name="mediaItem">The media item.</param>
|
||||
/// <param name="imageCropperValue">The image cropper value.</param>
|
||||
/// <param name="cropAlias">The crop alias.</param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The image crop URL.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this IPublishedContent mediaItem,
|
||||
ImageCropperValue imageCropperValue,
|
||||
string cropAlias)
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, imageCropperValue, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
|
||||
string cropAlias,
|
||||
UrlMode urlMode = UrlMode.Default)
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaItem, imageCropperValue, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item.
|
||||
@@ -54,17 +58,19 @@ namespace Umbraco.Extensions
|
||||
/// <param name="mediaItem">The IPublishedContent item.</param>
|
||||
/// <param name="propertyAlias">The property alias of the property containing the JSON data e.g. umbracoFile.</param>
|
||||
/// <param name="cropAlias">The crop alias e.g. thumbnail.</param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
public static string GetCropUrl(
|
||||
this IPublishedContent mediaItem,
|
||||
string propertyAlias,
|
||||
string cropAlias) =>
|
||||
mediaItem.GetCropUrl(propertyAlias, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
|
||||
string cropAlias,
|
||||
UrlMode urlMode = UrlMode.Default) =>
|
||||
mediaItem.GetCropUrl(propertyAlias, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode);
|
||||
|
||||
public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string propertyAlias, string cropAlias)
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaWithCrops, propertyAlias, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider);
|
||||
public static string GetCropUrl(this MediaWithCrops mediaWithCrops, string propertyAlias, string cropAlias, UrlMode urlMode = UrlMode.Default)
|
||||
=> ImageCropperTemplateCoreExtensions.GetCropUrl(mediaWithCrops, propertyAlias, cropAlias, ImageUrlGenerator, PublishedValueFallback, PublishedUrlProvider, urlMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the IPublishedContent item.
|
||||
@@ -84,6 +90,7 @@ namespace Umbraco.Extensions
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -99,7 +106,8 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null)
|
||||
string furtherOptions = null,
|
||||
UrlMode urlMode = UrlMode.Default)
|
||||
=> mediaItem.GetCropUrl(
|
||||
ImageUrlGenerator,
|
||||
PublishedValueFallback,
|
||||
@@ -114,7 +122,8 @@ namespace Umbraco.Extensions
|
||||
preferFocalPoint,
|
||||
useCropDimensions,
|
||||
cacheBuster,
|
||||
furtherOptions
|
||||
furtherOptions,
|
||||
urlMode
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
@@ -215,7 +224,7 @@ namespace Umbraco.Extensions
|
||||
);
|
||||
|
||||
|
||||
[Obsolete("Use GetCrop to merge local and media crops, get automatic cache buster value and have more parameters.")]
|
||||
[Obsolete("Use GetCropUrl to merge local and media crops, get automatic cache buster value and have more parameters.")]
|
||||
public static string GetLocalCropUrl(
|
||||
this MediaWithCrops mediaWithCrops,
|
||||
string alias,
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Umbraco.Extensions
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -28,14 +29,16 @@ namespace Umbraco.Extensions
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
|
||||
public static string GetCropUrl(
|
||||
this MediaWithCrops mediaWithCrops,
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true);
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
UrlMode urlMode = UrlMode.Default) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the crop URL by using only the specified <paramref name="imageCropperValue" />.
|
||||
@@ -46,6 +49,7 @@ namespace Umbraco.Extensions
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <param name="urlMode">The url mode.s</param>
|
||||
/// <returns>
|
||||
/// The image crop URL.
|
||||
/// </returns>
|
||||
@@ -55,7 +59,8 @@ namespace Umbraco.Extensions
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true);
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, imageCropperValue, true, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL by the crop alias using the specified property containing the image cropper JSON data on the IPublishedContent item.
|
||||
@@ -66,6 +71,7 @@ namespace Umbraco.Extensions
|
||||
/// <param name="imageUrlGenerator">The image URL generator.</param>
|
||||
/// <param name="publishedValueFallback">The published value fallback.</param>
|
||||
/// <param name="publishedUrlProvider">The published URL provider.</param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -75,14 +81,16 @@ namespace Umbraco.Extensions
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
|
||||
public static string GetCropUrl(this MediaWithCrops mediaWithCrops,
|
||||
IPublishedValueFallback publishedValueFallback,
|
||||
IPublishedUrlProvider publishedUrlProvider,
|
||||
string propertyAlias,
|
||||
string cropAlias,
|
||||
IImageUrlGenerator imageUrlGenerator) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
UrlMode urlMode = UrlMode.Default) => mediaWithCrops.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying image processing service URL from the IPublishedContent item.
|
||||
@@ -105,6 +113,7 @@ namespace Umbraco.Extensions
|
||||
/// <example><![CDATA[
|
||||
/// furtherOptions: "bgcolor=fff"
|
||||
/// ]]></example></param>
|
||||
/// <param name="urlMode">The url mode.</param>
|
||||
/// <returns>
|
||||
/// The URL of the cropped image.
|
||||
/// </returns>
|
||||
@@ -123,7 +132,8 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
|
||||
string furtherOptions = null,
|
||||
UrlMode urlMode = UrlMode.Default) => mediaItem.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, null, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, urlMode);
|
||||
|
||||
public static string GetCropUrl(
|
||||
this MediaWithCrops mediaWithCrops,
|
||||
@@ -140,14 +150,15 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null)
|
||||
string furtherOptions = null,
|
||||
UrlMode urlMode = UrlMode.Default)
|
||||
{
|
||||
if (mediaWithCrops == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(mediaWithCrops));
|
||||
}
|
||||
|
||||
return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
|
||||
return mediaWithCrops.Content.GetCropUrl(imageUrlGenerator, publishedValueFallback, publishedUrlProvider, mediaWithCrops.LocalCrops, false, width, height, propertyAlias, cropAlias, quality, imageCropMode, imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, urlMode);
|
||||
}
|
||||
|
||||
private static string GetCropUrl(
|
||||
@@ -167,7 +178,8 @@ namespace Umbraco.Extensions
|
||||
bool preferFocalPoint = false,
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null)
|
||||
string furtherOptions = null,
|
||||
UrlMode urlMode = UrlMode.Default)
|
||||
{
|
||||
if (mediaItem == null)
|
||||
{
|
||||
@@ -179,7 +191,7 @@ namespace Umbraco.Extensions
|
||||
return null;
|
||||
}
|
||||
|
||||
var mediaItemUrl = mediaItem.MediaUrl(publishedUrlProvider, propertyAlias: propertyAlias);
|
||||
var mediaItemUrl = mediaItem.MediaUrl(publishedUrlProvider, propertyAlias: propertyAlias, mode: urlMode);
|
||||
|
||||
// Only get crops from media when required and used
|
||||
if (localCropsOnly == false && (imageCropMode == ImageCropMode.Crop || imageCropMode == null))
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@@ -209,27 +208,27 @@ namespace Umbraco.Extensions
|
||||
return $"{version}.{runtimeMinifier.CacheBuster}".GenerateHash();
|
||||
}
|
||||
|
||||
public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper, IPublishedContent mediaItem, string cropAlias, bool htmlEncode = true)
|
||||
public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper, IPublishedContent mediaItem, string cropAlias, bool htmlEncode = true, UrlMode urlMode = UrlMode.Default)
|
||||
{
|
||||
if (mediaItem == null)
|
||||
{
|
||||
return HtmlString.Empty;
|
||||
}
|
||||
|
||||
var url = mediaItem.GetCropUrl(cropAlias: cropAlias, useCropDimensions: true);
|
||||
var url = mediaItem.GetCropUrl(cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
|
||||
private static IHtmlContent CreateHtmlString(string url, bool htmlEncode) => htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url);
|
||||
|
||||
public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper, IPublishedContent mediaItem, string propertyAlias, string cropAlias, bool htmlEncode = true)
|
||||
public static IHtmlContent GetCropUrl(this IUrlHelper urlHelper, IPublishedContent mediaItem, string propertyAlias, string cropAlias, bool htmlEncode = true, UrlMode urlMode = UrlMode.Default)
|
||||
{
|
||||
if (mediaItem == null)
|
||||
{
|
||||
return HtmlString.Empty;
|
||||
}
|
||||
|
||||
var url = mediaItem.GetCropUrl(propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true);
|
||||
var url = mediaItem.GetCropUrl(propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true, urlMode: urlMode);
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
|
||||
@@ -246,7 +245,8 @@ namespace Umbraco.Extensions
|
||||
bool useCropDimensions = false,
|
||||
bool cacheBuster = true,
|
||||
string furtherOptions = null,
|
||||
bool htmlEncode = true)
|
||||
bool htmlEncode = true,
|
||||
UrlMode urlMode = UrlMode.Default)
|
||||
{
|
||||
if (mediaItem == null)
|
||||
{
|
||||
@@ -254,7 +254,7 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
|
||||
var url = mediaItem.GetCropUrl(width, height, propertyAlias, cropAlias, quality, imageCropMode,
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions);
|
||||
imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, urlMode);
|
||||
|
||||
return CreateHtmlString(url, htmlEncode);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @name umbraco.directives.directive:umbTree
|
||||
* @restrict E
|
||||
**/
|
||||
function umbTreeDirective($q, treeService, navigationService, notificationsService) {
|
||||
function umbTreeDirective($q, treeService, notificationsService) {
|
||||
|
||||
return {
|
||||
restrict: 'E',
|
||||
@@ -357,8 +357,6 @@ function umbTreeDirective($q, treeService, navigationService, notificationsServi
|
||||
defined on the tree
|
||||
*/
|
||||
$scope.select = function (n, ev) {
|
||||
|
||||
navigationService.hideMenu();
|
||||
|
||||
if (n.metaData && n.metaData.noAccess === true) {
|
||||
ev.preventDefault();
|
||||
|
||||
@@ -127,7 +127,7 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
|
||||
var aboveClass = "above-backdrop";
|
||||
var leftColumn = document.getElementById("leftcolumn");
|
||||
|
||||
if(leftColumn) {
|
||||
if (leftColumn) {
|
||||
var isLeftColumnOnTop = leftColumn.classList.contains(aboveClass);
|
||||
|
||||
if (isLeftColumnOnTop) {
|
||||
|
||||
@@ -585,7 +585,6 @@
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
flex:0;
|
||||
}
|
||||
|
||||
.umb-fileupload,
|
||||
|
||||
@@ -2044,4 +2044,8 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="FileWriting">Filskrivning</key>
|
||||
<key alias="MediaFolderCreation">Medie mappeoprettelse</key>
|
||||
</area>
|
||||
<area alias="treeSearch">
|
||||
<key alias="searchResult">resultat</key>
|
||||
<key alias="searchResults">resultater</key>
|
||||
</area>
|
||||
</language>
|
||||
|
||||
Reference in New Issue
Block a user