Merge branch 'v9/dev' into v9/contrib
# Conflicts: # src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Packaging;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Templates;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
@@ -92,6 +92,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
builder.Services.AddUnique<ICreatedPackagesRepository, CreatedPackageSchemaRepository>();
|
||||
builder.Services.AddSingleton<PackageDataInstallation>();
|
||||
builder.Services.AddUnique<IPackageInstallation, PackageInstallation>();
|
||||
builder.Services.AddUnique<IHtmlMacroParameterParser, HtmlMacroParameterParser>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
@@ -46,11 +45,8 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
// Scheduled for removal in V11
|
||||
[Obsolete("Please use constructor that takes an ILogger instead")]
|
||||
protected RecurringHostedServiceBase(TimeSpan period, TimeSpan delay)
|
||||
{
|
||||
_period = period;
|
||||
_delay = delay;
|
||||
_logger = StaticServiceProvider.Instance.GetRequiredService<ILoggerFactory>().CreateLogger(GetType());
|
||||
}
|
||||
: this(null, period, delay)
|
||||
{ }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
@@ -82,7 +78,8 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unhandled exception in recurring hosted service.");
|
||||
ILogger logger = _logger ?? StaticApplicationLogging.CreateLogger(GetType());
|
||||
logger.LogError(ex, "Unhandled exception in recurring hosted service.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -108,7 +105,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_timer?.Dispose();
|
||||
_timer?.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Notifications;
|
||||
@@ -11,6 +15,7 @@ using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using ColumnInfo = Umbraco.Cms.Infrastructure.Persistence.SqlSyntax.ColumnInfo;
|
||||
|
||||
@@ -88,15 +93,33 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
private readonly ILogger<DatabaseSchemaCreator> _logger;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private readonly IOptionsMonitor<InstallDefaultDataSettings> _defaultDataCreationSettings;
|
||||
|
||||
public DatabaseSchemaCreator(IUmbracoDatabase database, ILogger<DatabaseSchemaCreator> logger,
|
||||
ILoggerFactory loggerFactory, IUmbracoVersion umbracoVersion, IEventAggregator eventAggregator)
|
||||
[Obsolete("Please use constructor taking all parameters. Scheduled for removal in V11.")]
|
||||
public DatabaseSchemaCreator(
|
||||
IUmbracoDatabase database,
|
||||
ILogger<DatabaseSchemaCreator> logger,
|
||||
ILoggerFactory loggerFactory,
|
||||
IUmbracoVersion umbracoVersion,
|
||||
IEventAggregator eventAggregator)
|
||||
: this (database, logger, loggerFactory, umbracoVersion, eventAggregator, StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<InstallDefaultDataSettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
public DatabaseSchemaCreator(
|
||||
IUmbracoDatabase database,
|
||||
ILogger<DatabaseSchemaCreator> logger,
|
||||
ILoggerFactory loggerFactory,
|
||||
IUmbracoVersion umbracoVersion,
|
||||
IEventAggregator eventAggregator,
|
||||
IOptionsMonitor<InstallDefaultDataSettings> defaultDataCreationSettings)
|
||||
{
|
||||
_database = database ?? throw new ArgumentNullException(nameof(database));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
_umbracoVersion = umbracoVersion ?? throw new ArgumentNullException(nameof(umbracoVersion));
|
||||
_eventAggregator = eventAggregator;
|
||||
_defaultDataCreationSettings = defaultDataCreationSettings;
|
||||
|
||||
if (_database?.SqlContext?.SqlSyntax == null)
|
||||
{
|
||||
@@ -153,8 +176,11 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
|
||||
if (creatingNotification.Cancel == false)
|
||||
{
|
||||
var dataCreation = new DatabaseDataCreator(_database,
|
||||
_loggerFactory.CreateLogger<DatabaseDataCreator>(), _umbracoVersion);
|
||||
var dataCreation = new DatabaseDataCreator(
|
||||
_database,
|
||||
_loggerFactory.CreateLogger<DatabaseDataCreator>(),
|
||||
_umbracoVersion,
|
||||
_defaultDataCreationSettings);
|
||||
foreach (Type table in OrderedTables)
|
||||
{
|
||||
CreateTable(false, table, dataCreation);
|
||||
@@ -419,9 +445,14 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
where T : new()
|
||||
{
|
||||
Type tableType = typeof(T);
|
||||
CreateTable(overwrite, tableType,
|
||||
new DatabaseDataCreator(_database, _loggerFactory.CreateLogger<DatabaseDataCreator>(),
|
||||
_umbracoVersion));
|
||||
CreateTable(
|
||||
overwrite,
|
||||
tableType,
|
||||
new DatabaseDataCreator(
|
||||
_database,
|
||||
_loggerFactory.CreateLogger<DatabaseDataCreator>(),
|
||||
_umbracoVersion,
|
||||
_defaultDataCreationSettings));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
{
|
||||
@@ -14,22 +19,35 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IOptionsMonitor<InstallDefaultDataSettings> _installDefaultDataSettings;
|
||||
|
||||
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in V11.")]
|
||||
public DatabaseSchemaCreatorFactory(
|
||||
ILogger<DatabaseSchemaCreator> logger,
|
||||
ILoggerFactory loggerFactory,
|
||||
IUmbracoVersion umbracoVersion,
|
||||
IEventAggregator eventAggregator)
|
||||
: this(logger, loggerFactory, umbracoVersion, eventAggregator, StaticServiceProvider.Instance.GetRequiredService<IOptionsMonitor<InstallDefaultDataSettings>>())
|
||||
{
|
||||
}
|
||||
|
||||
public DatabaseSchemaCreatorFactory(
|
||||
ILogger<DatabaseSchemaCreator> logger,
|
||||
ILoggerFactory loggerFactory,
|
||||
IUmbracoVersion umbracoVersion,
|
||||
IEventAggregator eventAggregator,
|
||||
IOptionsMonitor<InstallDefaultDataSettings> installDefaultDataSettings)
|
||||
{
|
||||
_logger = logger;
|
||||
_loggerFactory = loggerFactory;
|
||||
_umbracoVersion = umbracoVersion;
|
||||
_eventAggregator = eventAggregator;
|
||||
_installDefaultDataSettings = installDefaultDataSettings;
|
||||
}
|
||||
|
||||
public DatabaseSchemaCreator Create(IUmbracoDatabase database)
|
||||
{
|
||||
return new DatabaseSchemaCreator(database, _logger, _loggerFactory, _umbracoVersion, _eventAggregator);
|
||||
return new DatabaseSchemaCreator(database, _logger, _loggerFactory, _umbracoVersion, _eventAggregator, _installDefaultDataSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,6 +659,12 @@ namespace Umbraco.Extensions
|
||||
return sql;
|
||||
}
|
||||
|
||||
public static Sql<ISqlContext> SelectDistinct(this Sql<ISqlContext> sql, params object[] columns)
|
||||
{
|
||||
sql.Append("SELECT DISTINCT " + string.Join(", ", columns));
|
||||
return sql;
|
||||
}
|
||||
|
||||
//this.Append("SELECT " + string.Join(", ", columns), new object[0]);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -18,14 +18,16 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
{
|
||||
internal class MacroRepository : EntityRepositoryBase<int, IMacro>, IMacroRepository
|
||||
internal class MacroRepository : EntityRepositoryBase<int, IMacro>, IMacroWithAliasRepository
|
||||
{
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly IRepositoryCachePolicy<IMacro, string> _macroByAliasCachePolicy;
|
||||
|
||||
public MacroRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger<MacroRepository> logger, IShortStringHelper shortStringHelper)
|
||||
: base(scopeAccessor, cache, logger)
|
||||
{
|
||||
_shortStringHelper = shortStringHelper;
|
||||
_macroByAliasCachePolicy = new DefaultRepositoryCachePolicy<IMacro, string>(GlobalIsolatedCache, ScopeAccessor, DefaultOptions);
|
||||
}
|
||||
|
||||
protected override IMacro PerformGet(int id)
|
||||
@@ -68,6 +70,38 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
return Get(id) != null;
|
||||
}
|
||||
|
||||
public IMacro GetByAlias(string alias)
|
||||
{
|
||||
return _macroByAliasCachePolicy.Get(alias, PerformGetByAlias, PerformGetAllByAlias);
|
||||
}
|
||||
|
||||
public IEnumerable<IMacro> GetAllByAlias(string[] aliases)
|
||||
{
|
||||
if (aliases.Any() is false)
|
||||
{
|
||||
return base.GetMany();
|
||||
}
|
||||
|
||||
return _macroByAliasCachePolicy.GetAll(aliases, PerformGetAllByAlias);
|
||||
}
|
||||
|
||||
private IMacro PerformGetByAlias(string alias)
|
||||
{
|
||||
var query = Query<IMacro>().Where(x => x.Alias.Equals(alias));
|
||||
return PerformGetByQuery(query).FirstOrDefault();
|
||||
}
|
||||
|
||||
private IEnumerable<IMacro> PerformGetAllByAlias(params string[] aliases)
|
||||
{
|
||||
if (aliases.Any() is false)
|
||||
{
|
||||
return base.GetMany();
|
||||
}
|
||||
|
||||
var query = Query<IMacro>().Where(x => aliases.Contains(x.Alias));
|
||||
return PerformGetByQuery(query);
|
||||
}
|
||||
|
||||
protected override IEnumerable<IMacro> PerformGetAll(params int[] ids)
|
||||
{
|
||||
return ids.Length > 0 ? ids.Select(Get) : GetAllNoIds();
|
||||
|
||||
@@ -60,14 +60,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
var urlHash = url.GenerateHash<SHA1>();
|
||||
Sql<ISqlContext> sql = GetBaseQuery(false)
|
||||
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash &&
|
||||
(x.Culture == culture.ToLower() || x.Culture == string.Empty))
|
||||
(x.Culture == culture.ToLower() || x.Culture == null || x.Culture == string.Empty))
|
||||
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc);
|
||||
List<RedirectUrlDto> dtos = Database.Fetch<RedirectUrlDto>(sql);
|
||||
RedirectUrlDto dto = dtos.FirstOrDefault(f => f.Culture == culture.ToLower());
|
||||
|
||||
if (dto == null)
|
||||
{
|
||||
dto = dtos.FirstOrDefault(f => f.Culture == string.Empty);
|
||||
dto = dtos.FirstOrDefault(f => string.IsNullOrWhiteSpace(f.Culture));
|
||||
}
|
||||
|
||||
return dto == null ? null : Map(dto);
|
||||
|
||||
@@ -198,22 +198,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<IUmbracoEntity> GetPagedParentEntitiesByChildIds(int[] childIds, long pageIndex, int pageSize, out long totalRecords, int[] relationTypes, params Guid[] entityTypes)
|
||||
{
|
||||
return _entityRepository.GetPagedResultsByQuery(Query<IUmbracoEntity>(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql =>
|
||||
{
|
||||
SqlJoinRelations(sql);
|
||||
|
||||
sql.WhereIn<RelationDto>(rel => rel.ChildId, childIds);
|
||||
sql.WhereAny(s => s.WhereIn<RelationDto>(rel => rel.ParentId, childIds), s => s.WhereNotIn<NodeDto>(node => node.NodeId, childIds));
|
||||
|
||||
if (relationTypes != null && relationTypes.Any())
|
||||
{
|
||||
sql.WhereIn<RelationDto>(rel => rel.RelationType, relationTypes);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<IUmbracoEntity> GetPagedChildEntitiesByParentId(int parentId, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
|
||||
{
|
||||
return GetPagedChildEntitiesByParentId(parentId, pageIndex, pageSize, out totalRecords, new int[0], entityTypes);
|
||||
@@ -241,21 +225,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
});
|
||||
}
|
||||
|
||||
public IEnumerable<IUmbracoEntity> GetPagedEntitiesForItemsInRelation(int[] itemIds, long pageIndex, int pageSize, out long totalRecords, params Guid[] entityTypes)
|
||||
{
|
||||
return _entityRepository.GetPagedResultsByQuery(Query<IUmbracoEntity>(), entityTypes, pageIndex, pageSize, out totalRecords, null, null, sql =>
|
||||
{
|
||||
SqlJoinRelations(sql);
|
||||
|
||||
sql.WhereIn<RelationDto>(rel => rel.ChildId, itemIds);
|
||||
sql.Where<RelationDto, NodeDto>((rel, node) => rel.ChildId == node.NodeId);
|
||||
sql.Where<RelationTypeDto>(type => type.IsDependency);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void Save(IEnumerable<IRelation> relations)
|
||||
{
|
||||
foreach (var hasIdentityGroup in relations.GroupBy(r => r.HasIdentity))
|
||||
@@ -475,8 +444,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
[Column(Name = "contentTypeName")]
|
||||
public string ChildContentTypeName { get; set; }
|
||||
|
||||
|
||||
|
||||
[Column(Name = "relationTypeName")]
|
||||
public string RelationTypeName { get; set; }
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
|
||||
@@ -20,10 +19,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
_scopeAccessor = scopeAccessor;
|
||||
}
|
||||
|
||||
public IEnumerable<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize,
|
||||
bool filterMustBeIsDependency, out long totalRecords)
|
||||
/// <summary>
|
||||
/// Gets a page of items used in any kind of relation from selected integer ids.
|
||||
/// </summary>
|
||||
public IEnumerable<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
||||
{
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct(
|
||||
"[pn].[id] as nodeId",
|
||||
"[pn].[uniqueId] as nodeKey",
|
||||
"[pn].[text] as nodeName",
|
||||
@@ -36,12 +37,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
||||
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
||||
.From<RelationDto>("r")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn");
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "pn", aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn");
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
@@ -57,13 +58,15 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
||||
|
||||
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
||||
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
|
||||
return pagedResult.Items.Select(MapDtoToEntity);
|
||||
}
|
||||
|
||||
public IEnumerable<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency,
|
||||
out long totalRecords)
|
||||
/// <summary>
|
||||
/// Gets a page of the descending items that have any references, given a parent id.
|
||||
/// </summary>
|
||||
public IEnumerable<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
||||
{
|
||||
var syntax = _scopeAccessor.AmbientScope.Database.SqlContext.SqlSyntax;
|
||||
|
||||
@@ -73,13 +76,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
.From<NodeDto>("node")
|
||||
.Where<NodeDto>(x => x.NodeId == parentId, "node");
|
||||
|
||||
|
||||
// Gets the descendants of the parent node
|
||||
Sql<ISqlContext> subQuery;
|
||||
|
||||
if (_scopeAccessor.AmbientScope.Database.DatabaseType.IsSqlCe())
|
||||
{
|
||||
// SqlCE do not support nested selects that returns a scalar. So we need to do this in multiple queries
|
||||
// SqlCE does not support nested selects that returns a scalar. So we need to do this in multiple queries
|
||||
|
||||
var pathForLike = _scopeAccessor.AmbientScope.Database.ExecuteScalar<string>(subsubQuery);
|
||||
|
||||
@@ -96,10 +98,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
.WhereLike<NodeDto>(x => x.Path, subsubQuery);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Get all relations where parent is in the sub query
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct(
|
||||
"[pn].[id] as nodeId",
|
||||
"[pn].[uniqueId] as nodeKey",
|
||||
"[pn].[text] as nodeName",
|
||||
@@ -112,29 +112,35 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
||||
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
||||
.From<RelationDto>("r")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"pn", aliasRight:"c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "pn", aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn")
|
||||
.WhereIn((System.Linq.Expressions.Expression<Func<NodeDto, object>>)(x => x.NodeId), subQuery, "pn");
|
||||
|
||||
if (filterMustBeIsDependency)
|
||||
{
|
||||
sql = sql.Where<RelationTypeDto>(rt => rt.IsDependency, "umbracoRelationType");
|
||||
}
|
||||
|
||||
// Ordering is required for paging
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
||||
|
||||
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
||||
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
|
||||
return pagedResult.Items.Select(MapDtoToEntity);
|
||||
}
|
||||
|
||||
public IEnumerable<RelationItem> GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
||||
/// <summary>
|
||||
/// Gets a page of items which are in relation with the current item.
|
||||
/// Basically, shows the items which depend on the current item.
|
||||
/// </summary>
|
||||
public IEnumerable<RelationItem> GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency, out long totalRecords)
|
||||
{
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().Select(
|
||||
var sql = _scopeAccessor.AmbientScope.Database.SqlContext.Sql().SelectDistinct(
|
||||
"[cn].[id] as nodeId",
|
||||
"[cn].[uniqueId] as nodeKey",
|
||||
"[cn].[text] as nodeName",
|
||||
@@ -147,17 +153,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
"[umbracoRelationType].[isDependency] as relationTypeIsDependency",
|
||||
"[umbracoRelationType].[dual] as relationTypeIsBidirectional")
|
||||
.From<RelationDto>("r")
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight:"umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId )), aliasLeft: "r", aliasRight:"cn", aliasOther: "umbracoRelationType" )
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight:"pn", aliasOther:"cn" )
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"cn", aliasRight:"c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft:"c", aliasRight:"ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft:"ct", aliasRight:"ctn");
|
||||
|
||||
if (ids.Any())
|
||||
{
|
||||
sql = sql.Where<NodeDto>(x => ids.Contains(x.NodeId), "pn");
|
||||
}
|
||||
.InnerJoin<RelationTypeDto>("umbracoRelationType").On<RelationDto, RelationTypeDto>((left, right) => left.RelationType == right.Id, aliasLeft: "r", aliasRight: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("cn").On<RelationDto, NodeDto, RelationTypeDto>((r, cn, rt) => (!rt.Dual && r.ParentId == cn.NodeId) || (rt.Dual && (r.ChildId == cn.NodeId || r.ParentId == cn.NodeId)), aliasLeft: "r", aliasRight: "cn", aliasOther: "umbracoRelationType")
|
||||
.InnerJoin<NodeDto>("pn").On<RelationDto, NodeDto, NodeDto>((r, pn, cn) => (pn.NodeId == r.ChildId && cn.NodeId == r.ParentId) || (pn.NodeId == r.ParentId && cn.NodeId == r.ChildId), aliasLeft: "r", aliasRight: "pn", aliasOther: "cn")
|
||||
.LeftJoin<ContentDto>("c").On<NodeDto, ContentDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "cn", aliasRight: "c")
|
||||
.LeftJoin<ContentTypeDto>("ct").On<ContentDto, ContentTypeDto>((left, right) => left.ContentTypeId == right.NodeId, aliasLeft: "c", aliasRight: "ct")
|
||||
.LeftJoin<NodeDto>("ctn").On<ContentTypeDto, NodeDto>((left, right) => left.NodeId == right.NodeId, aliasLeft: "ct", aliasRight: "ctn")
|
||||
.Where<NodeDto>(x => x.NodeId == id, "pn")
|
||||
.Where<RelationDto>(x => x.ChildId == id || x.ParentId == id, "r"); // This last Where is purely to help SqlServer make a smarter query plan. More info https://github.com/umbraco/Umbraco-CMS/issues/12190
|
||||
|
||||
if (filterMustBeIsDependency)
|
||||
{
|
||||
@@ -168,14 +171,13 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement
|
||||
sql = sql.OrderBy<RelationTypeDto>(x => x.Alias);
|
||||
|
||||
var pagedResult = _scopeAccessor.AmbientScope.Database.Page<RelationItemDto>(pageIndex + 1, pageSize, sql);
|
||||
totalRecords = Convert.ToInt32(pagedResult.TotalItems);
|
||||
totalRecords = pagedResult.TotalItems;
|
||||
|
||||
return pagedResult.Items.Select(MapDtoToEntity);
|
||||
}
|
||||
|
||||
private RelationItem MapDtoToEntity(RelationItemDto dto)
|
||||
{
|
||||
var type = ObjectTypes.GetUdiType(dto.ChildNodeObjectType);
|
||||
return new RelationItem()
|
||||
{
|
||||
NodeId = dto.ChildNodeId,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
@@ -14,6 +15,8 @@ using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Core.Templates;
|
||||
using Umbraco.Cms.Infrastructure.Templates;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
@@ -37,6 +40,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
private readonly RichTextEditorPastedImages _pastedImages;
|
||||
private readonly HtmlLocalLinkParser _localLinkParser;
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
private readonly IHtmlMacroParameterParser _macroParameterParser;
|
||||
|
||||
public GridPropertyEditor(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
@@ -45,7 +49,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
HtmlLocalLinkParser localLinkParser,
|
||||
IIOHelper ioHelper,
|
||||
IImageUrlGenerator imageUrlGenerator)
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IHtmlMacroParameterParser macroParameterParser)
|
||||
: base(dataValueEditorFactory)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
@@ -54,6 +59,20 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_pastedImages = pastedImages;
|
||||
_localLinkParser = localLinkParser;
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
_macroParameterParser = macroParameterParser;
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor which takes an IHtmlMacroParameterParser instead")]
|
||||
public GridPropertyEditor(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
HtmlImageSourceParser imageSourceParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
HtmlLocalLinkParser localLinkParser,
|
||||
IIOHelper ioHelper,
|
||||
IImageUrlGenerator imageUrlGenerator)
|
||||
: this (dataValueEditorFactory, backOfficeSecurityAccessor, imageSourceParser, pastedImages, localLinkParser, ioHelper, imageUrlGenerator, StaticServiceProvider.Instance.GetRequiredService<IHtmlMacroParameterParser>())
|
||||
{
|
||||
}
|
||||
|
||||
public override IPropertyIndexValueFactory PropertyIndexValueFactory => new GridPropertyIndexValueFactory();
|
||||
@@ -74,6 +93,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
private readonly RichTextPropertyEditor.RichTextPropertyValueEditor _richTextPropertyValueEditor;
|
||||
private readonly MediaPickerPropertyEditor.MediaPickerPropertyValueEditor _mediaPickerPropertyValueEditor;
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
private readonly IHtmlMacroParameterParser _macroParameterParser;
|
||||
|
||||
public GridPropertyValueEditor(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
@@ -85,7 +105,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
IShortStringHelper shortStringHelper,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper)
|
||||
IIOHelper ioHelper,
|
||||
IHtmlMacroParameterParser macroParameterParser)
|
||||
: base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
@@ -96,6 +117,25 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_mediaPickerPropertyValueEditor =
|
||||
dataValueEditorFactory.Create<MediaPickerPropertyEditor.MediaPickerPropertyValueEditor>(attribute);
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
_macroParameterParser = macroParameterParser;
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor which takes an IHtmlMacroParameterParser instead")]
|
||||
public GridPropertyValueEditor(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
DataEditorAttribute attribute,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
ILocalizedTextService localizedTextService,
|
||||
HtmlImageSourceParser imageSourceParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
IShortStringHelper shortStringHelper,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper)
|
||||
: this (dataValueEditorFactory, attribute, backOfficeSecurityAccessor, localizedTextService,
|
||||
imageSourceParser, pastedImages, shortStringHelper, imageUrlGenerator, jsonSerializer, ioHelper,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IHtmlMacroParameterParser>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +160,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
var mediaParent = config?.MediaParentId;
|
||||
var mediaParentId = mediaParent == null ? Guid.Empty : mediaParent.Guid;
|
||||
|
||||
var grid = DeserializeGridValue(rawJson, out var rtes, out _);
|
||||
var grid = DeserializeGridValue(rawJson, out var rtes, out _, out _);
|
||||
|
||||
var userId = _backOfficeSecurityAccessor?.BackOfficeSecurity?.CurrentUser?.Id ?? Constants.Security.SuperUserId;
|
||||
|
||||
@@ -154,7 +194,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
if (val.IsNullOrWhiteSpace())
|
||||
return string.Empty;
|
||||
|
||||
var grid = DeserializeGridValue(val, out var rtes, out _);
|
||||
var grid = DeserializeGridValue(val, out var rtes, out _, out _);
|
||||
|
||||
//process the rte values
|
||||
foreach (var rte in rtes.ToList())
|
||||
@@ -168,7 +208,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
return grid;
|
||||
}
|
||||
|
||||
private GridValue DeserializeGridValue(string rawJson, out IEnumerable<GridValue.GridControl> richTextValues, out IEnumerable<GridValue.GridControl> mediaValues)
|
||||
private GridValue DeserializeGridValue(string rawJson, out IEnumerable<GridValue.GridControl> richTextValues, out IEnumerable<GridValue.GridControl> mediaValues, out IEnumerable<GridValue.GridControl> macroValues)
|
||||
{
|
||||
var grid = JsonConvert.DeserializeObject<GridValue>(rawJson);
|
||||
|
||||
@@ -177,6 +217,9 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
richTextValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "rte");
|
||||
mediaValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "media");
|
||||
|
||||
// Find all the macros
|
||||
macroValues = controls.Where(x => x.Editor.Alias.ToLowerInvariant() == "macro");
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
@@ -192,7 +235,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
if (rawJson.IsNullOrWhiteSpace())
|
||||
yield break;
|
||||
|
||||
DeserializeGridValue(rawJson, out var richTextEditorValues, out var mediaValues);
|
||||
DeserializeGridValue(rawJson, out var richTextEditorValues, out var mediaValues, out var macroValues);
|
||||
|
||||
foreach (var umbracoEntityReference in richTextEditorValues.SelectMany(x =>
|
||||
_richTextPropertyValueEditor.GetReferences(x.Value)))
|
||||
@@ -201,6 +244,9 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
foreach (var umbracoEntityReference in mediaValues.Where(x => x.Value.HasValues)
|
||||
.SelectMany(x => _mediaPickerPropertyValueEditor.GetReferences(x.Value["udi"])))
|
||||
yield return umbracoEntityReference;
|
||||
|
||||
foreach (var umbracoEntityReference in _macroParameterParser.FindUmbracoEntityReferencesFromGridControlMacros(macroValues))
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Media;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
@@ -16,6 +15,8 @@ using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Core.Templates;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.Infrastructure.Macros;
|
||||
using Umbraco.Cms.Infrastructure.Templates;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
@@ -36,12 +37,13 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly HtmlImageSourceParser _imageSourceParser;
|
||||
private readonly HtmlLocalLinkParser _localLinkParser;
|
||||
private readonly IHtmlMacroParameterParser _macroParameterParser;
|
||||
private readonly RichTextEditorPastedImages _pastedImages;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
|
||||
/// <summary>
|
||||
/// The constructor will setup the property editor based on the attribute if one is found
|
||||
/// The constructor will setup the property editor based on the attribute if one is found.
|
||||
/// </summary>
|
||||
public RichTextPropertyEditor(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
@@ -50,7 +52,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
HtmlLocalLinkParser localLinkParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
IIOHelper ioHelper,
|
||||
IImageUrlGenerator imageUrlGenerator)
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IHtmlMacroParameterParser macroParameterParser)
|
||||
: base(dataValueEditorFactory)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
@@ -59,6 +62,20 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_pastedImages = pastedImages;
|
||||
_ioHelper = ioHelper;
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
_macroParameterParser = macroParameterParser;
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor which takes an IHtmlMacroParameterParser instead")]
|
||||
public RichTextPropertyEditor(
|
||||
IDataValueEditorFactory dataValueEditorFactory,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
HtmlImageSourceParser imageSourceParser,
|
||||
HtmlLocalLinkParser localLinkParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
IIOHelper ioHelper,
|
||||
IImageUrlGenerator imageUrlGenerator)
|
||||
: this (dataValueEditorFactory, backOfficeSecurityAccessor, imageSourceParser, localLinkParser, pastedImages, ioHelper, imageUrlGenerator, StaticServiceProvider.Instance.GetRequiredService<IHtmlMacroParameterParser>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,6 +96,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly HtmlImageSourceParser _imageSourceParser;
|
||||
private readonly HtmlLocalLinkParser _localLinkParser;
|
||||
private readonly IHtmlMacroParameterParser _macroParameterParser;
|
||||
private readonly RichTextEditorPastedImages _pastedImages;
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
private readonly IHtmlSanitizer _htmlSanitizer;
|
||||
@@ -94,7 +112,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper,
|
||||
IHtmlSanitizer htmlSanitizer)
|
||||
IHtmlSanitizer htmlSanitizer,
|
||||
IHtmlMacroParameterParser macroParameterParser)
|
||||
: base(localizedTextService, shortStringHelper, jsonSerializer, ioHelper, attribute)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
@@ -103,6 +122,26 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_pastedImages = pastedImages;
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
_htmlSanitizer = htmlSanitizer;
|
||||
_macroParameterParser = macroParameterParser;
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor which takes an HtmlMacroParameterParser instead")]
|
||||
public RichTextPropertyValueEditor(
|
||||
DataEditorAttribute attribute,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
ILocalizedTextService localizedTextService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
HtmlImageSourceParser imageSourceParser,
|
||||
HtmlLocalLinkParser localLinkParser,
|
||||
RichTextEditorPastedImages pastedImages,
|
||||
IImageUrlGenerator imageUrlGenerator,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IIOHelper ioHelper,
|
||||
IHtmlSanitizer htmlSanitizer)
|
||||
: this(attribute, backOfficeSecurityAccessor, localizedTextService, shortStringHelper, imageSourceParser,
|
||||
localLinkParser, pastedImages, imageUrlGenerator, jsonSerializer, ioHelper, htmlSanitizer,
|
||||
StaticServiceProvider.Instance.GetRequiredService<IHtmlMacroParameterParser>())
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -182,6 +221,10 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
|
||||
//TODO: Detect Macros too ... but we can save that for a later date, right now need to do media refs
|
||||
//UPDATE: We are getting the Macros in 'FindUmbracoEntityReferencesFromEmbeddedMacros' - perhaps we just return the macro Udis here too or do they need their own relationAlias?
|
||||
|
||||
foreach (var umbracoEntityReference in _macroParameterParser.FindUmbracoEntityReferencesFromEmbeddedMacros(asString))
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to check for clear text passwords from members as the first thing. This was possible in v8 :(
|
||||
else if (IsSuccessfulLegacyPassword(hashedPassword, providedPassword))
|
||||
{
|
||||
@@ -138,7 +139,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
}
|
||||
|
||||
var result = LegacyPasswordSecurity.VerifyPassword(Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName, providedPassword, hashedPassword);
|
||||
return result || LegacyPasswordSecurity.VerifyPassword(Constants.Security.AspNetUmbraco4PasswordHashAlgorithmName, providedPassword, hashedPassword);
|
||||
return result || LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword);
|
||||
}
|
||||
|
||||
private static string DecryptLegacyPassword(string encryptedPassword, string algorithmName, string decryptionKey)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
@@ -10,7 +11,6 @@ namespace Umbraco.Cms.Core.Security
|
||||
where TUser: UmbracoIdentityUser
|
||||
{
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly PasswordHasher<TUser> _aspnetV2PasswordHasher = new PasswordHasher<TUser>(new V2PasswordHasherOptions());
|
||||
|
||||
public UmbracoPasswordHasher(LegacyPasswordSecurity legacyPasswordSecurity, IJsonSerializer jsonSerializer)
|
||||
{
|
||||
@@ -43,57 +43,64 @@ namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
if (user is null)
|
||||
{
|
||||
throw new System.ArgumentNullException(nameof(user));
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
}
|
||||
|
||||
if (!user.PasswordConfig.IsNullOrWhiteSpace())
|
||||
try
|
||||
{
|
||||
// check if the (legacy) password security supports this hash algorith and if so then use it
|
||||
var deserialized = _jsonSerializer.Deserialize<PersistedPasswordSettings>(user.PasswordConfig);
|
||||
if (LegacyPasswordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm))
|
||||
// Best case and most likely scenario, a modern hash supported by ASP.Net identity.
|
||||
PasswordVerificationResult upstreamResult = base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
if (upstreamResult != PasswordVerificationResult.Failed)
|
||||
{
|
||||
var result = LegacyPasswordSecurity.VerifyPassword(deserialized.HashAlgorithm, providedPassword, hashedPassword);
|
||||
|
||||
//We need to special handle this case, apparently v8 still saves the user algorithm as {"hashAlgorithm":"HMACSHA256"}, when using legacy encoding and hasinging.
|
||||
if (result == false)
|
||||
{
|
||||
result = LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword);
|
||||
}
|
||||
|
||||
return result
|
||||
? PasswordVerificationResult.SuccessRehashNeeded
|
||||
: PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
// We will explicitly detect names here
|
||||
// The default is PBKDF2.ASPNETCORE.V3:
|
||||
// PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
|
||||
// The underlying class only lets us change 2 things which is the version: options.CompatibilityMode and the iteration count
|
||||
// The PBKDF2.ASPNETCORE.V2 settings are:
|
||||
// PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
|
||||
|
||||
switch (deserialized.HashAlgorithm)
|
||||
{
|
||||
case Constants.Security.AspNetCoreV3PasswordHashAlgorithmName:
|
||||
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
case Constants.Security.AspNetCoreV2PasswordHashAlgorithmName:
|
||||
var legacyResult = _aspnetV2PasswordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
if (legacyResult == PasswordVerificationResult.Success)
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
return legacyResult;
|
||||
return upstreamResult;
|
||||
}
|
||||
}
|
||||
|
||||
// else go the default (v3)
|
||||
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||
}
|
||||
|
||||
private class V2PasswordHasherOptions : IOptions<PasswordHasherOptions>
|
||||
{
|
||||
public PasswordHasherOptions Value => new PasswordHasherOptions
|
||||
catch (FormatException)
|
||||
{
|
||||
CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2
|
||||
};
|
||||
// hash wasn't a valid base64 encoded string, MS concat the salt bytes and hash bytes and base 64 encode both together.
|
||||
// We however historically base 64 encoded the salt bytes and hash bytes separately then concat the strings so we got 2 sets of padding.
|
||||
// both salt bytes and hash bytes lengths were not evenly divisible by 3 hence 2 sets of padding.
|
||||
|
||||
// We could check upfront with TryFromBase64String, but not whilst we target netstandard 2.0
|
||||
// so might as well just deal with the exception.
|
||||
}
|
||||
|
||||
// At this point we either have a legacy password or a bad attempt.
|
||||
|
||||
// Check the supported worst case scenario, a "useLegacyEncoding" password - HMACSHA1 but with password used as key so not unique for users sharing same password
|
||||
// This was the standard for v4.
|
||||
// Do this first because with useLegacyEncoding the algorithm stored in the database is irrelevant.
|
||||
if (LegacyPasswordSecurity.VerifyLegacyHashedPassword(providedPassword, hashedPassword))
|
||||
{
|
||||
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||
}
|
||||
|
||||
// For users we expect to know the historic algorithm.
|
||||
// NOTE: MemberPasswordHasher subclasses this class to deal with the fact that PasswordConfig wasn't stored.
|
||||
if (user.PasswordConfig.IsNullOrWhiteSpace())
|
||||
{
|
||||
return PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
PersistedPasswordSettings deserialized;
|
||||
try
|
||||
{
|
||||
deserialized = _jsonSerializer.Deserialize<PersistedPasswordSettings>(user.PasswordConfig);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
if (!LegacyPasswordSecurity.SupportHashAlgorithm(deserialized.HashAlgorithm))
|
||||
{
|
||||
return PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
// Last chance must be HMACSHA256 or SHA1
|
||||
return LegacyPasswordSecurity.VerifyPassword(deserialized.HashAlgorithm, providedPassword, hashedPassword)
|
||||
? PasswordVerificationResult.SuccessRehashNeeded
|
||||
: PasswordVerificationResult.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <summary>
|
||||
/// Represents the Macro Service, which is an easy access to operations involving <see cref="IMacro"/>
|
||||
/// </summary>
|
||||
internal class MacroService : RepositoryService, IMacroService
|
||||
internal class MacroService : RepositoryService, IMacroWithAliasService
|
||||
{
|
||||
private readonly IMacroRepository _macroRepository;
|
||||
private readonly IAuditRepository _auditRepository;
|
||||
|
||||
public MacroService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory,
|
||||
IMacroRepository macroRepository, IAuditRepository auditRepository)
|
||||
public MacroService(IScopeProvider provider, ILoggerFactory loggerFactory, IEventMessagesFactory eventMessagesFactory, IMacroRepository macroRepository, IAuditRepository auditRepository)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_macroRepository = macroRepository;
|
||||
@@ -33,10 +32,14 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <returns>An <see cref="IMacro"/> object</returns>
|
||||
public IMacro GetByAlias(string alias)
|
||||
{
|
||||
if (_macroRepository is not IMacroWithAliasRepository macroWithAliasRepository)
|
||||
{
|
||||
return GetAll().FirstOrDefault(x => x.Alias == alias);
|
||||
}
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
var q = Query<IMacro>().Where(x => x.Alias == alias);
|
||||
return _macroRepository.Get(q).FirstOrDefault();
|
||||
return macroWithAliasRepository.GetByAlias(alias);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +64,20 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<IMacro> GetAll(params string[] aliases)
|
||||
{
|
||||
if (_macroRepository is not IMacroWithAliasRepository macroWithAliasRepository)
|
||||
{
|
||||
var hashset = new HashSet<string>(aliases);
|
||||
return GetAll().Where(x => hashset.Contains(x.Alias));
|
||||
}
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
return macroWithAliasRepository.GetAllByAlias(aliases);
|
||||
}
|
||||
}
|
||||
|
||||
public IMacro GetById(int id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
@@ -18,22 +17,32 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_entityService = entityService;
|
||||
}
|
||||
|
||||
public PagedResult<RelationItem> GetPagedRelationsForItems(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency)
|
||||
/// <summary>
|
||||
/// Gets a paged result of items which are in relation with the current item.
|
||||
/// Basically, shows the items which depend on the current item.
|
||||
/// </summary>
|
||||
public PagedResult<RelationItem> GetPagedRelationsForItem(int id, long pageIndex, int pageSize, bool filterMustBeIsDependency)
|
||||
{
|
||||
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
|
||||
var items = _trackedReferencesRepository.GetPagedRelationsForItems(ids, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems);
|
||||
var items = _trackedReferencesRepository.GetPagedRelationsForItem(id, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems);
|
||||
|
||||
return new PagedResult<RelationItem>(totalItems, pageIndex+1, pageSize) { Items = items };
|
||||
return new PagedResult<RelationItem>(totalItems, pageIndex + 1, pageSize) { Items = items };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged result of items used in any kind of relation from selected integer ids.
|
||||
/// </summary>
|
||||
public PagedResult<RelationItem> GetPagedItemsWithRelations(int[] ids, long pageIndex, int pageSize, bool filterMustBeIsDependency)
|
||||
{
|
||||
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
|
||||
var items = _trackedReferencesRepository.GetPagedItemsWithRelations(ids, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems);
|
||||
var items = _trackedReferencesRepository.GetPagedItemsWithRelations(ids, pageIndex, pageSize, filterMustBeIsDependency, out var totalItems);
|
||||
|
||||
return new PagedResult<RelationItem>(totalItems, pageIndex+1, pageSize) { Items = items };
|
||||
return new PagedResult<RelationItem>(totalItems, pageIndex + 1, pageSize) { Items = items };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a paged result of the descending items that have any references, given a parent id.
|
||||
/// </summary>
|
||||
public PagedResult<RelationItem> GetPagedDescendantsInReferences(int parentId, long pageIndex, int pageSize, bool filterMustBeIsDependency)
|
||||
{
|
||||
using IScope scope = _scopeProvider.CreateScope(autoComplete: true);
|
||||
@@ -44,7 +53,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
pageSize,
|
||||
filterMustBeIsDependency,
|
||||
out var totalItems);
|
||||
return new PagedResult<RelationItem>(totalItems, pageIndex+1, pageSize) { Items = items };
|
||||
return new PagedResult<RelationItem>(totalItems, pageIndex + 1, pageSize) { Items = items };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
155
src/Umbraco.Infrastructure/Templates/HtmlMacroParameterParser.cs
Normal file
155
src/Umbraco.Infrastructure/Templates/HtmlMacroParameterParser.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Macros;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Templates
|
||||
{
|
||||
public sealed class HtmlMacroParameterParser : IHtmlMacroParameterParser
|
||||
{
|
||||
private readonly IMacroService _macroService;
|
||||
private readonly ILogger<HtmlMacroParameterParser> _logger;
|
||||
private readonly ParameterEditorCollection _parameterEditors;
|
||||
|
||||
public HtmlMacroParameterParser(IMacroService macroService, ILogger<HtmlMacroParameterParser> logger, ParameterEditorCollection parameterEditors)
|
||||
{
|
||||
_macroService = macroService;
|
||||
_logger = logger;
|
||||
_parameterEditors = parameterEditors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses out media UDIs from an HTML string based on embedded macro parameter values.
|
||||
/// </summary>
|
||||
/// <param name="text">HTML string</param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<UmbracoEntityReference> FindUmbracoEntityReferencesFromEmbeddedMacros(string text)
|
||||
{
|
||||
// There may be more than one macro with the same alias on the page so using a tuple
|
||||
var foundMacros = new List<Tuple<string, Dictionary<string, string>>>();
|
||||
|
||||
// This legacy ParseMacros() already finds the macros within a Rich Text Editor using regexes
|
||||
// It seems to lowercase the macro parameter alias - so making the dictionary case insensitive
|
||||
MacroTagParser.ParseMacros(text, textblock => { }, (macroAlias, macroAttributes) => foundMacros.Add(new Tuple<string, Dictionary<string, string>>(macroAlias, new Dictionary<string, string>(macroAttributes, StringComparer.OrdinalIgnoreCase))));
|
||||
foreach (var umbracoEntityReference in GetUmbracoEntityReferencesFromMacros(foundMacros))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses out media UDIs from Macro Grid Control parameters.
|
||||
/// </summary>
|
||||
/// <param name="macroGridControls"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<UmbracoEntityReference> FindUmbracoEntityReferencesFromGridControlMacros(IEnumerable<GridValue.GridControl> macroGridControls)
|
||||
{
|
||||
var foundMacros = new List<Tuple<string, Dictionary<string, string>>>();
|
||||
|
||||
foreach (var macroGridControl in macroGridControls)
|
||||
{
|
||||
// Deserialise JSON of Macro Grid Control to a class
|
||||
var gridMacro = macroGridControl.Value.ToObject<GridMacro>();
|
||||
// Collect any macro parameters that contain the media udi format
|
||||
if (gridMacro is not null && gridMacro.MacroParameters is not null && gridMacro.MacroParameters.Any())
|
||||
{
|
||||
foundMacros.Add(new Tuple<string, Dictionary<string, string>>(gridMacro.MacroAlias, gridMacro.MacroParameters));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var umbracoEntityReference in GetUmbracoEntityReferencesFromMacros(foundMacros))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<UmbracoEntityReference> GetUmbracoEntityReferencesFromMacros(List<Tuple<string, Dictionary<string, string>>> macros)
|
||||
{
|
||||
|
||||
if (_macroService is not IMacroWithAliasService macroWithAliasService)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var uniqueMacroAliases = macros.Select(f => f.Item1).Distinct();
|
||||
// TODO: Tracking Macro references
|
||||
// Here we are finding the used macros' Udis (there should be a Related Macro relation type - but Relations don't accept 'Macro' as an option)
|
||||
var foundMacroUmbracoEntityReferences = new List<UmbracoEntityReference>();
|
||||
// Get all the macro configs in one hit for these unique macro aliases - this is now cached with a custom cache policy
|
||||
var macroConfigs = macroWithAliasService.GetAll(uniqueMacroAliases.ToArray());
|
||||
|
||||
foreach (var macro in macros)
|
||||
{
|
||||
var macroConfig = macroConfigs.FirstOrDefault(f => f.Alias == macro.Item1);
|
||||
if (macroConfig is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
foundMacroUmbracoEntityReferences.Add(new UmbracoEntityReference(Udi.Create(Constants.UdiEntityType.Macro, macroConfig.Key)));
|
||||
// Only do this if the macros actually have parameters
|
||||
if (macroConfig.Properties is not null && macroConfig.Properties.Keys.Any(f => f != "macroAlias"))
|
||||
{
|
||||
foreach (var umbracoEntityReference in GetUmbracoEntityReferencesFromMacroParameters(macro.Item2, macroConfig, _parameterEditors))
|
||||
{
|
||||
yield return umbracoEntityReference;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds media UDIs in Macro Parameter Values by calling the GetReference method for all the Macro Parameter Editors for a particular macro.
|
||||
/// </summary>
|
||||
/// <param name="macroParameters">The parameters for the macro a dictionary of key/value strings</param>
|
||||
/// <param name="macroConfig">The macro configuration for this particular macro - contains the types of editors used for each parameter</param>
|
||||
/// <param name="parameterEditors">A list of all the registered parameter editors used in the Umbraco implmentation - to look up the corresponding property editor for a macro parameter</param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<UmbracoEntityReference> GetUmbracoEntityReferencesFromMacroParameters(Dictionary<string, string> macroParameters, IMacro macroConfig, ParameterEditorCollection parameterEditors)
|
||||
{
|
||||
var foundUmbracoEntityReferences = new List<UmbracoEntityReference>();
|
||||
foreach (var parameter in macroConfig.Properties)
|
||||
{
|
||||
if (macroParameters.TryGetValue(parameter.Alias, out string parameterValue))
|
||||
{
|
||||
var parameterEditorAlias = parameter.EditorAlias;
|
||||
// Lookup propertyEditor from the registered ParameterEditors with the implmementation to avoid looking up for each parameter
|
||||
var parameterEditor = parameterEditors.FirstOrDefault(f => string.Equals(f.Alias, parameterEditorAlias, StringComparison.OrdinalIgnoreCase));
|
||||
if (parameterEditor is not null)
|
||||
{
|
||||
// Get the ParameterValueEditor for this PropertyEditor (where the GetReferences method is implemented) - cast as IDataValueReference to determine if 'it is' implemented for the editor
|
||||
if (parameterEditor.GetValueEditor() is IDataValueReference parameterValueEditor)
|
||||
{
|
||||
foreach (var entityReference in parameterValueEditor.GetReferences(parameterValue))
|
||||
{
|
||||
foundUmbracoEntityReferences.Add(entityReference);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("{0} doesn't have a ValueEditor that implements IDataValueReference", parameterEditor.Alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundUmbracoEntityReferences;
|
||||
}
|
||||
|
||||
// Poco class to deserialise the Json for a Macro Control
|
||||
private class GridMacro
|
||||
{
|
||||
[JsonProperty("macroAlias")]
|
||||
public string MacroAlias { get; set; }
|
||||
|
||||
[JsonProperty("macroParamsDictionary")]
|
||||
public Dictionary<string, string> MacroParameters { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Editors;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Templates
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods to parse referenced entities as Macro parameters.
|
||||
/// </summary>
|
||||
public interface IHtmlMacroParameterParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parses out media UDIs from an HTML string based on embedded macro parameter values.
|
||||
/// </summary>
|
||||
/// <param name="text">HTML string</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<UmbracoEntityReference> FindUmbracoEntityReferencesFromEmbeddedMacros(string text);
|
||||
|
||||
/// <summary>
|
||||
/// Parses out media UDIs from Macro Grid Control parameters.
|
||||
/// </summary>
|
||||
/// <param name="macroGridControls"></param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<UmbracoEntityReference> FindUmbracoEntityReferencesFromGridControlMacros(IEnumerable<GridValue.GridControl> macroGridControls);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user