V10: fix build warnings infrastructure (#12369)
* Run code cleanup * Run dotnet format * Start manual fixes * Manual fixing of warnings * Fix nullability in columnalias * Fix tests * Fix up after merge * Start updating after review * Update editorconfig to contain new static & const rules * Fix up editorconfig to not contain duplicate rules * Fix up static member names * Fix up according to review * Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.DistributedCache.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Repositories.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentIndexPopulator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ContentValueSetValidator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/ExamineUmbracoIndexingHandler.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Examine/PublishedContentIndexPopulator.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Extensions/InstanceIdentifiableExtensions.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/HostedServices/RecurringHostedServiceBase.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/HostedServices/ReportSiteTask.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Macros/MacroTagParser.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Macros/MacroTagParser.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Macros/MacroTagParser.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Migrations/Expressions/Alter/Table/IAlterTableColumnOptionBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Migrations/Upgrade/V_10_0_0/AddMemberPropertiesAsColumns.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/ModelsBuilder/Building/TextBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/AccessMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/AuditEntryMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/MediaMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/MemberMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyGroupMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyGroupMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/PropertyTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/RelationTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Mappers/RelationTypeMapper.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/NPocoMapperCollectionBuilder.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Querying/ExpressionVisitorBase.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Update src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs Co-authored-by: Mole <nikolajlauridsen@protonmail.ch> * Fix [..] to substring * Fix after merge with 10/dev * Fox ContentValueSetValidator.cs * Update LoggerConfigExtensions Co-authored-by: Nikolaj Geisle <niko737@edu.ucl.dk> Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
This commit is contained in:
@@ -282,7 +282,7 @@ dotnet_naming_style.internal_error_style.required_suffix = ____INTERNAL_ERROR___
|
||||
|
||||
# All public/protected/protected_internal constant fields must be PascalCase
|
||||
# https://docs.microsoft.com/dotnet/standard/design-guidelines/field
|
||||
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal
|
||||
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_accessibilities = public, protected, protected_internal, internal, private
|
||||
dotnet_naming_symbols.public_protected_constant_fields_group.required_modifiers = const
|
||||
dotnet_naming_symbols.public_protected_constant_fields_group.applicable_kinds = field
|
||||
dotnet_naming_rule.public_protected_constant_fields_must_be_pascal_case_rule.symbols = public_protected_constant_fields_group
|
||||
@@ -356,24 +356,13 @@ dotnet_naming_rule.parameters_rule.symbols = parameters_group
|
||||
dotnet_naming_rule.parameters_rule.style = camel_case_style
|
||||
dotnet_naming_rule.parameters_rule.severity = warning
|
||||
|
||||
# Private static fields use camelCase and start with s_
|
||||
dotnet_naming_symbols.private_static_field_symbols.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.private_static_field_symbols.required_modifiers = static, shared
|
||||
dotnet_naming_symbols.private_static_field_symbols.applicable_kinds = field
|
||||
dotnet_naming_rule.private_static_fields_must_be_camel_cased_and_prefixed_with_s_underscore.symbols = private_static_field_symbols
|
||||
dotnet_naming_rule.private_static_fields_must_be_camel_cased_and_prefixed_with_s_underscore.style = camel_case_and_prefix_with_s_underscore_style
|
||||
dotnet_naming_rule.private_static_fields_must_be_camel_cased_and_prefixed_with_s_underscore.severity = warning
|
||||
dotnet_naming_style.camel_case_and_prefix_with_s_underscore_style.required_prefix = s_
|
||||
dotnet_naming_style.camel_case_and_prefix_with_s_underscore_style.capitalization = camel_case
|
||||
|
||||
# Instance fields use camelCase and are prefixed with '_'
|
||||
dotnet_naming_symbols.private_field_symbols.applicable_accessibilities = private
|
||||
dotnet_naming_symbols.private_field_symbols.applicable_kinds = field
|
||||
dotnet_naming_rule.private_instance_fields_must_be_camel_cased_and_prefixed_with_underscore.symbols = private_field_symbols
|
||||
dotnet_naming_rule.private_instance_fields_must_be_camel_cased_and_prefixed_with_underscore.style = camel_case_and_prefix_with_underscore_style
|
||||
dotnet_naming_rule.private_instance_fields_must_be_camel_cased_and_prefixed_with_underscore.severity = warning
|
||||
dotnet_naming_style.camel_case_and_prefix_with_underscore_style.required_prefix = _
|
||||
dotnet_naming_style.camel_case_and_prefix_with_underscore_style.capitalization = camel_case
|
||||
dotnet_naming_rule.instance_fields_should_be_camel_case.severity = warning
|
||||
dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
|
||||
dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
|
||||
dotnet_naming_symbols.instance_fields.applicable_kinds = field
|
||||
dotnet_naming_style.instance_field_style.capitalization = camel_case
|
||||
dotnet_naming_style.instance_field_style.required_prefix = _
|
||||
|
||||
##########################################
|
||||
# License
|
||||
|
||||
@@ -180,8 +180,7 @@ public class SqliteSyntaxProvider : SqlSyntaxProviderBase<SqliteSyntaxProvider>
|
||||
return string.Join(" || ", args.AsEnumerable());
|
||||
}
|
||||
|
||||
public override string GetColumn(DatabaseType dbType, string tableName, string columnName, string columnAlias,
|
||||
string? referenceName = null, bool forInsert = false)
|
||||
public override string GetColumn(DatabaseType dbType, string tableName, string columnName, string? columnAlias, string? referenceName = null, bool forInsert = false)
|
||||
{
|
||||
if (forInsert)
|
||||
{
|
||||
|
||||
@@ -8,16 +8,17 @@ using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Cache;
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that distributed cache events are setup and the <see cref="IServerMessenger" /> is initialized
|
||||
/// </summary>
|
||||
public sealed class DatabaseServerMessengerNotificationHandler : INotificationHandler<UmbracoApplicationStartingNotification>, INotificationHandler<UmbracoRequestEndNotification>
|
||||
public sealed class DatabaseServerMessengerNotificationHandler :
|
||||
INotificationHandler<UmbracoApplicationStartingNotification>, INotificationHandler<UmbracoRequestEndNotification>
|
||||
{
|
||||
private readonly IServerMessenger _messenger;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly ILogger<DatabaseServerMessengerNotificationHandler> _logger;
|
||||
private readonly IServerMessenger _messenger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
/// <summary>
|
||||
@@ -45,7 +46,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
if (_databaseFactory.CanConnect == false)
|
||||
{
|
||||
_logger.LogWarning("Cannot connect to the database, distributed calls will not be enabled for this server.");
|
||||
_logger.LogWarning(
|
||||
"Cannot connect to the database, distributed calls will not be enabled for this server.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,4 +60,3 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
public void Handle(UmbracoRequestEndNotification notification) => _messenger?.SendMessages();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Cache;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the default cache policy.
|
||||
/// </summary>
|
||||
@@ -25,66 +21,22 @@ namespace Umbraco.Cms.Core.Cache
|
||||
public class DefaultRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
private static readonly TEntity[] s_emptyEntities = new TEntity[0]; // const
|
||||
private static readonly TEntity[] _emptyEntities = new TEntity[0]; // const
|
||||
private readonly RepositoryCachePolicyOptions _options;
|
||||
|
||||
public DefaultRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
: base(cache, scopeAccessor)
|
||||
{
|
||||
: base(cache, scopeAccessor) =>
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
protected string GetEntityCacheKey(int id) => EntityTypeCacheKey + id;
|
||||
|
||||
protected string GetEntityCacheKey(TId? id)
|
||||
{
|
||||
if (EqualityComparer<TId>.Default.Equals(id, default))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (typeof(TId).IsValueType)
|
||||
{
|
||||
return EntityTypeCacheKey + id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EntityTypeCacheKey + id?.ToString()?.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
protected string EntityTypeCacheKey { get; } = $"uRepo_{typeof(TEntity).Name}_";
|
||||
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
=> Cache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
|
||||
protected virtual void InsertEntities(TId[]? ids, TEntity[]? entities)
|
||||
{
|
||||
if (ids?.Length == 0 && entities?.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.Insert(EntityTypeCacheKey, () => s_emptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entities is not null)
|
||||
{
|
||||
// individually cache each item
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var capture = entity;
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -116,7 +68,10 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -148,7 +103,10 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -159,6 +117,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
// whatever happens, clear the cache
|
||||
var cacheKey = GetEntityCacheKey(entity.Id);
|
||||
Cache.Clear(cacheKey);
|
||||
|
||||
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
|
||||
Cache.Clear(EntityTypeCacheKey);
|
||||
}
|
||||
@@ -168,7 +127,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
public override TEntity? Get(TId? id, Func<TId?, TEntity?> performGet, Func<TId[]?, IEnumerable<TEntity>?> performGetAll)
|
||||
{
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
TEntity? fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
|
||||
// if found in cache then return else fetch and cache
|
||||
if (fromCache != null)
|
||||
@@ -176,7 +135,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
return fromCache;
|
||||
}
|
||||
|
||||
var entity = performGet(id);
|
||||
TEntity? entity = performGet(id);
|
||||
|
||||
if (entity != null && entity.HasIdentity)
|
||||
{
|
||||
@@ -198,7 +157,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
// if found in cache the return else check
|
||||
var cacheKey = GetEntityCacheKey(id);
|
||||
var fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
TEntity? fromCache = Cache.GetCacheItem<TEntity>(cacheKey);
|
||||
return fromCache != null || performExists(id);
|
||||
}
|
||||
|
||||
@@ -209,17 +168,19 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
// try to get each entity from the cache
|
||||
// if we can find all of them, return
|
||||
var entities = ids.Select(GetCached).WhereNotNull().ToArray();
|
||||
TEntity[] entities = ids.Select(GetCached).WhereNotNull().ToArray();
|
||||
if (ids.Length.Equals(entities.Length))
|
||||
{
|
||||
return entities; // no need for null checks, we are not caching nulls
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// get everything we have
|
||||
var entities = Cache.GetCacheItemsByKeySearch<TEntity>(EntityTypeCacheKey)?
|
||||
TEntity?[] entities = Cache.GetCacheItemsByKeySearch<TEntity>(EntityTypeCacheKey)
|
||||
.ToArray(); // no need for null checks, we are not caching nulls
|
||||
|
||||
if (entities?.Length > 0)
|
||||
if (entities.Length > 0)
|
||||
{
|
||||
// if some of them were in the cache...
|
||||
if (_options.GetAllCacheValidateCount)
|
||||
@@ -229,9 +190,11 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
var totalCount = _options.PerformCount();
|
||||
if (entities.Length == totalCount)
|
||||
{
|
||||
return entities.WhereNotNull().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no need to validate, just return what we have and assume it's all there is
|
||||
@@ -242,13 +205,16 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
// if none of them were in the cache
|
||||
// and we allow zero count - check for the special (empty) entry
|
||||
var empty = Cache.GetCacheItem<TEntity[]>(EntityTypeCacheKey);
|
||||
if (empty != null) return empty;
|
||||
TEntity[]? empty = Cache.GetCacheItem<TEntity[]>(EntityTypeCacheKey);
|
||||
if (empty != null)
|
||||
{
|
||||
return empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache failed, get from repo and cache
|
||||
var repoEntities = performGetAll(ids)?
|
||||
TEntity[]? repoEntities = performGetAll(ids)?
|
||||
.WhereNotNull() // exclude nulls!
|
||||
.Where(x => x.HasIdentity) // be safe, though would be weird...
|
||||
.ToArray();
|
||||
@@ -260,9 +226,48 @@ namespace Umbraco.Cms.Core.Cache
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
public override void ClearAll() => Cache.ClearByKey(EntityTypeCacheKey);
|
||||
|
||||
protected string GetEntityCacheKey(int id) => EntityTypeCacheKey + id;
|
||||
|
||||
protected string GetEntityCacheKey(TId? id)
|
||||
{
|
||||
Cache.ClearByKey(EntityTypeCacheKey);
|
||||
if (EqualityComparer<TId>.Default.Equals(id, default))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (typeof(TId).IsValueType)
|
||||
{
|
||||
return EntityTypeCacheKey + id;
|
||||
}
|
||||
|
||||
return EntityTypeCacheKey + id?.ToString()?.ToUpperInvariant();
|
||||
}
|
||||
|
||||
protected virtual void InsertEntity(string cacheKey, TEntity entity)
|
||||
=> Cache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
|
||||
|
||||
protected virtual void InsertEntities(TId[]? ids, TEntity[]? entities)
|
||||
{
|
||||
if (ids?.Length == 0 && entities?.Length == 0 && _options.GetAllCacheAllowZeroCount)
|
||||
{
|
||||
// getting all of them, and finding nothing.
|
||||
// if we can cache a zero count, cache an empty array,
|
||||
// for as long as the cache is not cleared (no expiration)
|
||||
Cache.Insert(EntityTypeCacheKey, () => _emptyEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entities is not null)
|
||||
{
|
||||
// individually cache each item
|
||||
foreach (TEntity entity in entities)
|
||||
{
|
||||
TEntity capture = entity;
|
||||
Cache.Insert(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Default <see cref="IDistributedCacheBinder"/> implementation.
|
||||
/// </summary>
|
||||
namespace Umbraco.Cms.Core.Cache;
|
||||
public class DistributedCacheBinder :
|
||||
INotificationHandler<DictionaryItemDeletedNotification>,
|
||||
INotificationHandler<DictionaryItemSavedNotification>,
|
||||
@@ -62,30 +56,12 @@ namespace Umbraco.Cms.Core.Cache
|
||||
_distributedCache.RefreshPublicAccess();
|
||||
}
|
||||
|
||||
public void Handle(PublicAccessEntryDeletedNotification notification)
|
||||
{
|
||||
_distributedCache.RefreshPublicAccess();
|
||||
|
||||
}
|
||||
public void Handle(PublicAccessEntryDeletedNotification notification) => _distributedCache.RefreshPublicAccess();
|
||||
|
||||
#endregion
|
||||
|
||||
#region ContentService
|
||||
|
||||
/// <summary>
|
||||
/// Handles cache refreshing for when content is copied
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>
|
||||
/// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the
|
||||
/// case then we need to clear all user permissions cache.
|
||||
/// </remarks>
|
||||
private void ContentService_Copied(IContentService sender, CopyEventArgs<IContent> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void Handle(ContentTreeChangeNotification notification)
|
||||
{
|
||||
_distributedCache.RefreshContentCache(notification.Changes.ToArray());
|
||||
@@ -100,7 +76,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
// {
|
||||
// _distributedCache.RemoveUnpublishedPageCache(e.DeletedEntities.ToArray());
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region LocalizationService / Dictionary
|
||||
@@ -130,6 +105,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
_distributedCache.RefreshDataTypeCache(entity);
|
||||
}
|
||||
|
||||
_distributedCache.RefreshValueEditorCache(notification.SavedEntities);
|
||||
}
|
||||
|
||||
@@ -139,6 +115,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
_distributedCache.RemoveDataTypeCache(entity);
|
||||
}
|
||||
|
||||
_distributedCache.RefreshValueEditorCache(notification.DeletedEntities);
|
||||
}
|
||||
|
||||
@@ -362,4 +339,3 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// 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;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="DistributedCache"/>.
|
||||
/// </summary>
|
||||
@@ -93,15 +91,23 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshDataTypeCache(this DistributedCache dc, IDataType dataType)
|
||||
{
|
||||
if (dataType == null) return;
|
||||
var payloads = new[] { new DataTypeCacheRefresher.JsonPayload(dataType.Id, dataType.Key, false) };
|
||||
if (dataType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeCacheRefresher.JsonPayload[] payloads = new[] { new DataTypeCacheRefresher.JsonPayload(dataType.Id, dataType.Key, false) };
|
||||
dc.RefreshByPayload(DataTypeCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
public static void RemoveDataTypeCache(this DistributedCache dc, IDataType dataType)
|
||||
{
|
||||
if (dataType == null) return;
|
||||
var payloads = new[] { new DataTypeCacheRefresher.JsonPayload(dataType.Id, dataType.Key, true) };
|
||||
if (dataType == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DataTypeCacheRefresher.JsonPayload[] payloads = new[] { new DataTypeCacheRefresher.JsonPayload(dataType.Id, dataType.Key, true) };
|
||||
dc.RefreshByPayload(DataTypeCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
@@ -116,7 +122,7 @@ namespace Umbraco.Extensions
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = dataTypes.Select(x => new DataTypeCacheRefresher.JsonPayload(x.Id, x.Key, false));
|
||||
IEnumerable<DataTypeCacheRefresher.JsonPayload> payloads = dataTypes.Select(x => new DataTypeCacheRefresher.JsonPayload(x.Id, x.Key, false));
|
||||
dc.RefreshByPayload(ValueEditorCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
@@ -126,7 +132,7 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshAllContentCache(this DistributedCache dc)
|
||||
{
|
||||
var payloads = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
ContentCacheRefresher.JsonPayload[] payloads = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
|
||||
// note: refresh all content cache does refresh content types too
|
||||
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
|
||||
@@ -134,9 +140,12 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshContentCache(this DistributedCache dc, TreeChange<IContent>[] changes)
|
||||
{
|
||||
if (changes.Length == 0) return;
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = changes
|
||||
IEnumerable<ContentCacheRefresher.JsonPayload> payloads = changes
|
||||
.Select(x => new ContentCacheRefresher.JsonPayload(x.Item.Id, x.Item.Key, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
|
||||
@@ -148,13 +157,21 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshMemberCache(this DistributedCache dc, params IMember[] members)
|
||||
{
|
||||
if (members.Length == 0) return;
|
||||
if (members.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dc.RefreshByPayload(MemberCacheRefresher.UniqueId, members.Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, false)));
|
||||
}
|
||||
|
||||
public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members)
|
||||
{
|
||||
if (members.Length == 0) return;
|
||||
if (members.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dc.RefreshByPayload(MemberCacheRefresher.UniqueId, members.Select(x => new MemberCacheRefresher.JsonPayload(x.Id, x.Username, true)));
|
||||
}
|
||||
|
||||
@@ -178,7 +195,7 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshAllMediaCache(this DistributedCache dc)
|
||||
{
|
||||
var payloads = new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
MediaCacheRefresher.JsonPayload[] payloads = new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
|
||||
// note: refresh all media cache does refresh content types too
|
||||
dc.RefreshByPayload(MediaCacheRefresher.UniqueId, payloads);
|
||||
@@ -186,9 +203,12 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshMediaCache(this DistributedCache dc, TreeChange<IMedia>[] changes)
|
||||
{
|
||||
if (changes.Length == 0) return;
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = changes
|
||||
IEnumerable<MediaCacheRefresher.JsonPayload> payloads = changes
|
||||
.Select(x => new MediaCacheRefresher.JsonPayload(x.Item.Id, x.Item.Key, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(MediaCacheRefresher.UniqueId, payloads);
|
||||
@@ -212,15 +232,23 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshMacroCache(this DistributedCache dc, IMacro macro)
|
||||
{
|
||||
if (macro == null) return;
|
||||
var payloads = new[] { new MacroCacheRefresher.JsonPayload(macro.Id, macro.Alias) };
|
||||
if (macro == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MacroCacheRefresher.JsonPayload[] payloads = new[] { new MacroCacheRefresher.JsonPayload(macro.Id, macro.Alias) };
|
||||
dc.RefreshByPayload(MacroCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
public static void RemoveMacroCache(this DistributedCache dc, IMacro macro)
|
||||
{
|
||||
if (macro == null) return;
|
||||
var payloads = new[] { new MacroCacheRefresher.JsonPayload(macro.Id, macro.Alias) };
|
||||
if (macro == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MacroCacheRefresher.JsonPayload[] payloads = new[] { new MacroCacheRefresher.JsonPayload(macro.Id, macro.Alias) };
|
||||
dc.RefreshByPayload(MacroCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
@@ -230,9 +258,12 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshContentTypeCache(this DistributedCache dc, ContentTypeChange<IContentType>[] changes)
|
||||
{
|
||||
if (changes.Length == 0) return;
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = changes
|
||||
IEnumerable<ContentTypeCacheRefresher.JsonPayload> payloads = changes
|
||||
.Select(x => new ContentTypeCacheRefresher.JsonPayload(typeof(IContentType).Name, x.Item.Id, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(ContentTypeCacheRefresher.UniqueId, payloads);
|
||||
@@ -240,9 +271,12 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshContentTypeCache(this DistributedCache dc, ContentTypeChange<IMediaType>[] changes)
|
||||
{
|
||||
if (changes.Length == 0) return;
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = changes
|
||||
IEnumerable<ContentTypeCacheRefresher.JsonPayload> payloads = changes
|
||||
.Select(x => new ContentTypeCacheRefresher.JsonPayload(typeof(IMediaType).Name, x.Item.Id, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(ContentTypeCacheRefresher.UniqueId, payloads);
|
||||
@@ -250,9 +284,12 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshContentTypeCache(this DistributedCache dc, ContentTypeChange<IMemberType>[] changes)
|
||||
{
|
||||
if (changes.Length == 0) return;
|
||||
if (changes.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payloads = changes
|
||||
IEnumerable<ContentTypeCacheRefresher.JsonPayload> payloads = changes
|
||||
.Select(x => new ContentTypeCacheRefresher.JsonPayload(typeof(IMemberType).Name, x.Item.Id, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(ContentTypeCacheRefresher.UniqueId, payloads);
|
||||
@@ -264,21 +301,29 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshDomainCache(this DistributedCache dc, IDomain domain)
|
||||
{
|
||||
if (domain == null) return;
|
||||
var payloads = new[] { new DomainCacheRefresher.JsonPayload(domain.Id, DomainChangeTypes.Refresh) };
|
||||
if (domain == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DomainCacheRefresher.JsonPayload[] payloads = new[] { new DomainCacheRefresher.JsonPayload(domain.Id, DomainChangeTypes.Refresh) };
|
||||
dc.RefreshByPayload(DomainCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
public static void RemoveDomainCache(this DistributedCache dc, IDomain domain)
|
||||
{
|
||||
if (domain == null) return;
|
||||
var payloads = new[] { new DomainCacheRefresher.JsonPayload(domain.Id, DomainChangeTypes.Remove) };
|
||||
if (domain == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DomainCacheRefresher.JsonPayload[] payloads = new[] { new DomainCacheRefresher.JsonPayload(domain.Id, DomainChangeTypes.Remove) };
|
||||
dc.RefreshByPayload(DomainCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
public static void RefreshAllDomainCache(this DistributedCache dc)
|
||||
{
|
||||
var payloads = new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) };
|
||||
DomainCacheRefresher.JsonPayload[] payloads = new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) };
|
||||
dc.RefreshByPayload(DomainCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
|
||||
@@ -288,19 +333,25 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static void RefreshLanguageCache(this DistributedCache dc, ILanguage language)
|
||||
{
|
||||
if (language == null) return;
|
||||
if (language == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode,
|
||||
language.WasPropertyDirty(nameof(ILanguage.IsoCode))
|
||||
? LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture
|
||||
: LanguageCacheRefresher.JsonPayload.LanguageChangeType.Update);
|
||||
var payload = new LanguageCacheRefresher.JsonPayload(
|
||||
language.Id,
|
||||
language.IsoCode,
|
||||
language.WasPropertyDirty(nameof(ILanguage.IsoCode)) ? LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture : LanguageCacheRefresher.JsonPayload.LanguageChangeType.Update);
|
||||
|
||||
dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload });
|
||||
}
|
||||
|
||||
public static void RemoveLanguageCache(this DistributedCache dc, ILanguage language)
|
||||
{
|
||||
if (language == null) return;
|
||||
if (language == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode, LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove);
|
||||
dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload });
|
||||
@@ -322,6 +373,4 @@ namespace Umbraco.Extensions
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Collections;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Cache;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a caching policy that caches the entire entities set as a single collection.
|
||||
/// </summary>
|
||||
@@ -19,13 +15,16 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>Caches the entire set of entities as a single collection.</para>
|
||||
/// <para>Used by Content-, Media- and MemberTypeRepository, DataTypeRepository, DomainRepository,
|
||||
/// <para>
|
||||
/// Used by Content-, Media- and MemberTypeRepository, DataTypeRepository, DomainRepository,
|
||||
/// LanguageRepository, PublicAccessRepository, TemplateRepository... things that make sense to
|
||||
/// keep as a whole in memory.</para>
|
||||
/// keep as a whole in memory.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : RepositoryCachePolicyBase<TEntity, TId>
|
||||
where TEntity : class, IEntity
|
||||
{
|
||||
protected static readonly TId[] EmptyIds = new TId[0]; // const
|
||||
private readonly Func<TEntity, TId> _entityGetId;
|
||||
private readonly bool _expires;
|
||||
|
||||
@@ -36,13 +35,26 @@ namespace Umbraco.Cms.Core.Cache
|
||||
_expires = expires;
|
||||
}
|
||||
|
||||
protected static readonly TId[] EmptyIds = new TId[0]; // const
|
||||
|
||||
protected string GetEntityTypeCacheKey()
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
return $"uRepo_{typeof (TEntity).Name}_";
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
persistNew(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetEntityTypeCacheKey() => $"uRepo_{typeof(TEntity).Name}_";
|
||||
|
||||
protected void InsertEntities(TEntity[]? entities)
|
||||
{
|
||||
if (entities is null)
|
||||
@@ -60,7 +72,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
// cache as that would not be efficient for Get(id). so the DeepCloneableList is
|
||||
// set to ListCloneBehavior.CloneOnce ie it will clone *once* when inserting,
|
||||
// and then will *not* clone when retrieving.
|
||||
|
||||
var key = GetEntityTypeCacheKey();
|
||||
|
||||
if (_expires)
|
||||
@@ -73,25 +84,13 @@ namespace Umbraco.Cms.Core.Cache
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Create(TEntity entity, Action<TEntity> persistNew)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
|
||||
try
|
||||
{
|
||||
persistNew(entity);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(TEntity entity, Action<TEntity> persistUpdated)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -106,7 +105,10 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public override void Delete(TEntity entity, Action<TEntity> persistDeleted)
|
||||
{
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
if (entity == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(entity));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
@@ -122,8 +124,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
public override TEntity? Get(TId? id, Func<TId?, TEntity?> performGet, Func<TId[]?, IEnumerable<TEntity>?> performGetAll)
|
||||
{
|
||||
// get all from the cache, then look for the entity
|
||||
var all = GetAllCached(performGetAll);
|
||||
var entity = all.FirstOrDefault(x => _entityGetId(x)?.Equals(id) ?? false);
|
||||
IEnumerable<TEntity> all = GetAllCached(performGetAll);
|
||||
TEntity? entity = all.FirstOrDefault(x => _entityGetId(x)?.Equals(id) ?? false);
|
||||
|
||||
// see note in InsertEntities - what we get here is the original
|
||||
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
|
||||
@@ -134,8 +136,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
public override TEntity? GetCached(TId id)
|
||||
{
|
||||
// get all from the cache -- and only the cache, then look for the entity
|
||||
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
var entity = all?.FirstOrDefault(x => _entityGetId(x)?.Equals(id) ?? false);
|
||||
DeepCloneableList<TEntity>? all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
TEntity? entity = all?.FirstOrDefault(x => _entityGetId(x)?.Equals(id) ?? false);
|
||||
|
||||
// see note in InsertEntities - what we get here is the original
|
||||
// cached entity, not a clone, so we need to manually ensure it is deep-cloned.
|
||||
@@ -146,7 +148,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
public override bool Exists(TId id, Func<TId, bool> performExits, Func<TId[], IEnumerable<TEntity>?> performGetAll)
|
||||
{
|
||||
// get all as one set, then look for the entity
|
||||
var all = GetAllCached(performGetAll);
|
||||
IEnumerable<TEntity> all = GetAllCached(performGetAll);
|
||||
return all.Any(x => _entityGetId(x)?.Equals(id) ?? false);
|
||||
}
|
||||
|
||||
@@ -154,10 +156,13 @@ namespace Umbraco.Cms.Core.Cache
|
||||
public override TEntity[] GetAll(TId[]? ids, Func<TId[], IEnumerable<TEntity>?> performGetAll)
|
||||
{
|
||||
// get all as one set, from cache if possible, else repo
|
||||
var all = GetAllCached(performGetAll);
|
||||
IEnumerable<TEntity> all = GetAllCached(performGetAll);
|
||||
|
||||
// if ids have been specified, filter
|
||||
if (ids?.Length > 0) all = all.Where(x => ids.Contains(_entityGetId(x)));
|
||||
if (ids?.Length > 0)
|
||||
{
|
||||
all = all.Where(x => ids.Contains(_entityGetId(x)));
|
||||
}
|
||||
|
||||
// and return
|
||||
// see note in SetCacheActionToInsertEntities - what we get here is the original
|
||||
@@ -165,23 +170,22 @@ namespace Umbraco.Cms.Core.Cache
|
||||
return all.Select(x => (TEntity)x.DeepClone()).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll() => Cache.Clear(GetEntityTypeCacheKey());
|
||||
|
||||
// does NOT clone anything, so be nice with the returned values
|
||||
internal IEnumerable<TEntity> GetAllCached(Func<TId[], IEnumerable<TEntity>?> performGetAll)
|
||||
{
|
||||
// try the cache first
|
||||
var all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
if (all != null) return all.ToArray();
|
||||
DeepCloneableList<TEntity>? all = Cache.GetCacheItem<DeepCloneableList<TEntity>>(GetEntityTypeCacheKey());
|
||||
if (all != null)
|
||||
{
|
||||
return all.ToArray();
|
||||
}
|
||||
|
||||
// else get from repo and cache
|
||||
var entities = performGetAll(EmptyIds)?.WhereNotNull().ToArray();
|
||||
TEntity[]? entities = performGetAll(EmptyIds)?.WhereNotNull().ToArray();
|
||||
InsertEntities(entities); // may be an empty array...
|
||||
return entities ?? Enumerable.Empty<TEntity>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ClearAll()
|
||||
{
|
||||
Cache.Clear(GetEntityTypeCacheKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
using IScope = Umbraco.Cms.Infrastructure.Scoping.IScope;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class for repository cache policies.
|
||||
/// </summary>
|
||||
@@ -30,7 +29,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
get
|
||||
{
|
||||
var ambientScope = _scopeAccessor.AmbientScope;
|
||||
IScope? ambientScope = _scopeAccessor.AmbientScope;
|
||||
switch (ambientScope?.RepositoryCacheMode)
|
||||
{
|
||||
case RepositoryCacheMode.Default:
|
||||
@@ -40,7 +39,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
case RepositoryCacheMode.None:
|
||||
return NoAppCache.Instance;
|
||||
default:
|
||||
throw new NotSupportedException($"Repository cache mode {ambientScope?.RepositoryCacheMode} is not supported.");
|
||||
throw new NotSupportedException(
|
||||
$"Repository cache mode {ambientScope?.RepositoryCacheMode} is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,4 +69,3 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <inheritdoc />
|
||||
public abstract void ClearAll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,20 @@
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Infrastructure.Scoping;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Cache;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a special policy that does not cache the result of GetAll.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntity">The type of the entity.</typeparam>
|
||||
/// <typeparam name="TId">The type of the identifier.</typeparam>
|
||||
/// <remarks>
|
||||
/// <para>Overrides the default repository cache policy and does not writes the result of GetAll
|
||||
/// to cache, but only the result of individual Gets. It does read the cache for GetAll, though.</para>
|
||||
/// <para>
|
||||
/// Overrides the default repository cache policy and does not writes the result of GetAll
|
||||
/// to cache, but only the result of individual Gets. It does read the cache for GetAll, though.
|
||||
/// </para>
|
||||
/// <para>Used by DictionaryRepository.</para>
|
||||
/// </remarks>
|
||||
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
|
||||
@@ -22,11 +23,11 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public SingleItemsOnlyRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
|
||||
: base(cache, scopeAccessor, options)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
protected override void InsertEntities(TId[]? ids, TEntity[]? entities)
|
||||
{
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using NCrontab;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Configuration;
|
||||
|
||||
public class NCronTabParser : ICronTabParser
|
||||
{
|
||||
public bool IsValidCronTab(string cronTab)
|
||||
@@ -20,5 +18,3 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
return result.GetNextOccurrence(time);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Mappers;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods to the <see cref="IUmbracoBuilder" /> class.
|
||||
/// </summary>
|
||||
@@ -20,13 +20,10 @@ namespace Umbraco.Extensions
|
||||
public static NPocoMapperCollectionBuilder? NPocoMappers(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<NPocoMapperCollectionBuilder>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package migration plans collection builder.
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
public static PackageMigrationPlanCollectionBuilder? PackageMigrationPlans(this IUmbracoBuilder builder)
|
||||
=> builder.WithCollectionBuilder<PackageMigrationPlanCollectionBuilder>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Serilog;
|
||||
using SixLabors.ImageSharp;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
@@ -55,9 +56,10 @@ using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Cms.Infrastructure.Serialization;
|
||||
using Umbraco.Cms.Infrastructure.Services.Implement;
|
||||
using Umbraco.Extensions;
|
||||
using IScopeProvider = Umbraco.Cms.Infrastructure.Scoping.IScopeProvider;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -97,7 +99,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
// register the scope provider
|
||||
builder.Services.AddSingleton<ScopeProvider>(); // implements IScopeProvider, IScopeAccessor
|
||||
builder.Services.AddSingleton<ICoreScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<Infrastructure.Scoping.IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<Core.Scoping.IScopeProvider>(f => f.GetRequiredService<ScopeProvider>());
|
||||
builder.Services.AddSingleton<IScopeAccessor>(f => f.GetRequiredService<ScopeProvider>());
|
||||
|
||||
@@ -124,7 +126,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
builder.Services.AddSingleton<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
|
||||
|
||||
builder.Services.AddSingleton<IShortStringHelper>(factory
|
||||
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(factory.GetRequiredService<IOptionsMonitor<RequestHandlerSettings>>().CurrentValue)));
|
||||
=> new DefaultShortStringHelper(new DefaultShortStringHelperConfig().WithDefault(
|
||||
factory.GetRequiredService<IOptionsMonitor<RequestHandlerSettings>>().CurrentValue)));
|
||||
|
||||
builder.Services.AddSingleton<IMigrationPlanExecutor, MigrationPlanExecutor>();
|
||||
builder.Services.AddSingleton<IMigrationBuilder>(factory => new MigrationBuilder(factory));
|
||||
@@ -172,13 +175,14 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
|
||||
builder.Services.AddSingleton<IUmbracoTreeSearcherFields, UmbracoTreeSearcherFields>();
|
||||
builder.Services.AddSingleton<IPublishedContentQueryAccessor, PublishedContentQueryAccessor>(sp =>
|
||||
new PublishedContentQueryAccessor(sp.GetRequiredService<IScopedServiceProvider>())
|
||||
);
|
||||
new PublishedContentQueryAccessor(sp.GetRequiredService<IScopedServiceProvider>()));
|
||||
builder.Services.AddScoped<IPublishedContentQuery>(factory =>
|
||||
{
|
||||
var umbCtx = factory.GetRequiredService<IUmbracoContextAccessor>();
|
||||
var umbracoContext = umbCtx.GetRequiredUmbracoContext();
|
||||
return new PublishedContentQuery(umbracoContext.PublishedSnapshot, factory.GetRequiredService<IVariationContextAccessor>(), factory.GetRequiredService<IExamineManager>());
|
||||
IUmbracoContextAccessor umbCtx = factory.GetRequiredService<IUmbracoContextAccessor>();
|
||||
IUmbracoContext umbracoContext = umbCtx.GetRequiredUmbracoContext();
|
||||
return new PublishedContentQuery(
|
||||
umbracoContext.PublishedSnapshot,
|
||||
factory.GetRequiredService<IVariationContextAccessor>(), factory.GetRequiredService<IExamineManager>());
|
||||
});
|
||||
|
||||
// register accessors for cultures
|
||||
@@ -195,7 +199,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
builder.Services.AddSingleton<ICronTabParser, NCronTabParser>();
|
||||
|
||||
// Add default ImageSharp configuration and service implementations
|
||||
builder.Services.AddSingleton(SixLabors.ImageSharp.Configuration.Default);
|
||||
builder.Services.AddSingleton(Configuration.Default);
|
||||
builder.Services.AddSingleton<IImageDimensionExtractor, ImageSharpDimensionExtractor>();
|
||||
|
||||
builder.Services.AddTransient<INodeCountService, NodeCountService>();
|
||||
@@ -206,6 +210,21 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IUmbracoBuilder AddLogViewer(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILogViewerConfig, LogViewerConfig>();
|
||||
builder.Services.AddSingleton<ILogLevelLoader, LogLevelLoader>();
|
||||
builder.SetLogViewer<SerilogJsonLogViewer>();
|
||||
builder.Services.AddSingleton<ILogViewer>(factory => new SerilogJsonLogViewer(
|
||||
factory.GetRequiredService<ILogger<SerilogJsonLogViewer>>(),
|
||||
factory.GetRequiredService<ILogViewerConfig>(),
|
||||
factory.GetRequiredService<ILoggingConfiguration>(),
|
||||
factory.GetRequiredService<ILogLevelLoader>(),
|
||||
Log.Logger));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds logging requirements for Umbraco
|
||||
/// </summary>
|
||||
@@ -223,15 +242,17 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
builder.Services.AddSingleton<IMainDomKeyGenerator, DefaultMainDomKeyGenerator>();
|
||||
builder.Services.AddSingleton<IMainDomLock>(factory =>
|
||||
{
|
||||
var globalSettings = factory.GetRequiredService<IOptions<GlobalSettings>>();
|
||||
var connectionStrings = factory.GetRequiredService<IOptionsMonitor<ConnectionStrings>>();
|
||||
var hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
|
||||
IOptions<GlobalSettings> globalSettings = factory.GetRequiredService<IOptions<GlobalSettings>>();
|
||||
IOptionsMonitor<ConnectionStrings> connectionStrings =
|
||||
factory.GetRequiredService<IOptionsMonitor<ConnectionStrings>>();
|
||||
IHostingEnvironment hostingEnvironment = factory.GetRequiredService<IHostingEnvironment>();
|
||||
|
||||
var dbCreator = factory.GetRequiredService<IDbProviderFactoryCreator>();
|
||||
var databaseSchemaCreatorFactory = factory.GetRequiredService<DatabaseSchemaCreatorFactory>();
|
||||
var loggerFactory = factory.GetRequiredService<ILoggerFactory>();
|
||||
var npocoMappers = factory.GetRequiredService<NPocoMapperCollection>();
|
||||
var mainDomKeyGenerator = factory.GetRequiredService<IMainDomKeyGenerator>();
|
||||
IDbProviderFactoryCreator dbCreator = factory.GetRequiredService<IDbProviderFactoryCreator>();
|
||||
DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory =
|
||||
factory.GetRequiredService<DatabaseSchemaCreatorFactory>();
|
||||
ILoggerFactory loggerFactory = factory.GetRequiredService<ILoggerFactory>();
|
||||
NPocoMapperCollection npocoMappers = factory.GetRequiredService<NPocoMapperCollection>();
|
||||
IMainDomKeyGenerator mainDomKeyGenerator = factory.GetRequiredService<IMainDomKeyGenerator>();
|
||||
|
||||
switch (globalSettings.Value.MainDomLock)
|
||||
{
|
||||
@@ -246,18 +267,22 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
npocoMappers);
|
||||
|
||||
case "MainDomSemaphoreLock":
|
||||
return new MainDomSemaphoreLock(loggerFactory.CreateLogger<MainDomSemaphoreLock>(), hostingEnvironment);
|
||||
return new MainDomSemaphoreLock(
|
||||
loggerFactory.CreateLogger<MainDomSemaphoreLock>(),
|
||||
hostingEnvironment);
|
||||
|
||||
case "FileSystemMainDomLock":
|
||||
default:
|
||||
return new FileSystemMainDomLock(loggerFactory.CreateLogger<FileSystemMainDomLock>(), mainDomKeyGenerator, hostingEnvironment, factory.GetRequiredService<IOptionsMonitor<GlobalSettings>>());
|
||||
return new FileSystemMainDomLock(
|
||||
loggerFactory.CreateLogger<FileSystemMainDomLock>(),
|
||||
mainDomKeyGenerator, hostingEnvironment,
|
||||
factory.GetRequiredService<IOptionsMonitor<GlobalSettings>>());
|
||||
}
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
private static IUmbracoBuilder AddPreValueMigrators(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.WithCollectionBuilder<PreValueMigratorCollectionBuilder>()
|
||||
@@ -276,20 +301,6 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IUmbracoBuilder AddLogViewer(this IUmbracoBuilder builder)
|
||||
{
|
||||
builder.Services.AddSingleton<ILogViewerConfig, LogViewerConfig>();
|
||||
builder.Services.AddSingleton<ILogLevelLoader, LogLevelLoader>();
|
||||
builder.SetLogViewer<SerilogJsonLogViewer>();
|
||||
builder.Services.AddSingleton<ILogViewer>(factory => new SerilogJsonLogViewer(factory.GetRequiredService<ILogger<SerilogJsonLogViewer>>(),
|
||||
factory.GetRequiredService<ILogViewerConfig>(),
|
||||
factory.GetRequiredService<ILoggingConfiguration>(),
|
||||
factory.GetRequiredService<ILogLevelLoader>(),
|
||||
Log.Logger));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IUmbracoBuilder AddCoreNotifications(this IUmbracoBuilder builder)
|
||||
{
|
||||
// add handlers for sending user notifications (i.e. emails)
|
||||
@@ -371,6 +382,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
.AddNotificationHandler<MemberTypeChangedNotification, DistributedCacheBinder>()
|
||||
.AddNotificationHandler<ContentTreeChangeNotification, DistributedCacheBinder>()
|
||||
;
|
||||
|
||||
// add notification handlers for auditing
|
||||
builder
|
||||
.AddNotificationHandler<MemberSavedNotification, AuditNotificationsHandler>()
|
||||
@@ -386,4 +398,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
@@ -7,8 +6,8 @@ using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Infrastructure.Sync;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods to the <see cref="IUmbracoBuilder" /> class.
|
||||
/// </summary>
|
||||
@@ -48,7 +47,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a server registrar.</param>
|
||||
public static IUmbracoBuilder SetServerRegistrar(this IUmbracoBuilder builder, Func<IServiceProvider, IServerRoleAccessor> factory)
|
||||
public static IUmbracoBuilder SetServerRegistrar(
|
||||
this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IServerRoleAccessor> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -82,7 +83,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a server messenger.</param>
|
||||
public static IUmbracoBuilder SetServerMessenger(this IUmbracoBuilder builder, Func<IServiceProvider, IServerMessenger> factory)
|
||||
public static IUmbracoBuilder SetServerMessenger(
|
||||
this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IServerMessenger> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -99,4 +102,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -12,8 +10,8 @@ using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods to the <see cref="IUmbracoBuilder" /> class.
|
||||
/// </summary>
|
||||
@@ -63,4 +61,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.IO.MediaPathSchemes;
|
||||
using Umbraco.Extensions;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
/*
|
||||
@@ -49,7 +49,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
ILogger<PhysicalFileSystem> logger = factory.GetRequiredService<ILogger<PhysicalFileSystem>>();
|
||||
GlobalSettings globalSettings = factory.GetRequiredService<IOptions<GlobalSettings>>().Value;
|
||||
|
||||
var rootPath = Path.IsPathRooted(globalSettings.UmbracoMediaPhysicalRootPath) ? globalSettings.UmbracoMediaPhysicalRootPath : hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPhysicalRootPath);
|
||||
var rootPath = Path.IsPathRooted(globalSettings.UmbracoMediaPhysicalRootPath)
|
||||
? globalSettings.UmbracoMediaPhysicalRootPath
|
||||
: hostingEnvironment.MapPathWebRoot(globalSettings.UmbracoMediaPhysicalRootPath);
|
||||
var rootUrl = hostingEnvironment.ToAbsolute(globalSettings.UmbracoMediaPath);
|
||||
return new PhysicalFileSystem(ioHelper, hostingEnvironment, logger, rootPath, rootUrl);
|
||||
});
|
||||
@@ -57,4 +59,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,9 @@ using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Core.Telemetry;
|
||||
using Umbraco.Cms.Infrastructure.Install;
|
||||
using Umbraco.Cms.Infrastructure.Install.InstallSteps;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -42,4 +41,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ using Umbraco.Cms.Core.Models.Mapping;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -41,4 +41,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
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;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Composes repositories.
|
||||
/// </summary>
|
||||
@@ -74,4 +73,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -8,7 +7,7 @@ using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Extensions;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
@@ -26,10 +25,9 @@ using Umbraco.Cms.Infrastructure.Telemetry.Providers;
|
||||
using Umbraco.Cms.Infrastructure.Templates;
|
||||
using Umbraco.Extensions;
|
||||
using CacheInstructionService = Umbraco.Cms.Core.Services.Implement.CacheInstructionService;
|
||||
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -64,16 +62,15 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
||||
private static PackagesRepository CreatePackageRepository(IServiceProvider factory, string packageRepoFileName)
|
||||
=> new PackagesRepository(
|
||||
=> new(
|
||||
factory.GetRequiredService<IContentService>(),
|
||||
factory.GetRequiredService<IContentTypeService>(),
|
||||
factory.GetRequiredService<IDataTypeService>(),
|
||||
factory.GetRequiredService<IFileService>(),
|
||||
factory.GetRequiredService<IMacroService>(),
|
||||
factory.GetRequiredService<ILocalizationService>(),
|
||||
factory.GetRequiredService<Core.Hosting.IHostingEnvironment>(),
|
||||
factory.GetRequiredService<IHostingEnvironment>(),
|
||||
factory.GetRequiredService<IEntityXmlSerializer>(),
|
||||
factory.GetRequiredService<IOptions<GlobalSettings>>(),
|
||||
factory.GetRequiredService<IMediaService>(),
|
||||
@@ -84,7 +81,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
|
||||
// Factory registration is only required because of ambiguous constructor
|
||||
private static PackageDataInstallation CreatePackageDataInstallation(IServiceProvider factory)
|
||||
=> new PackageDataInstallation(
|
||||
=> new(
|
||||
factory.GetRequiredService<IDataValueEditorFactory>(),
|
||||
factory.GetRequiredService<ILogger<PackageDataInstallation>>(),
|
||||
factory.GetRequiredService<IFileService>(),
|
||||
@@ -101,9 +98,10 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
factory.GetRequiredService<IMediaService>(),
|
||||
factory.GetRequiredService<IMediaTypeService>());
|
||||
|
||||
private static LocalizedTextServiceFileSources CreateLocalizedTextServiceFileSourcesFactory(IServiceProvider container)
|
||||
private static LocalizedTextServiceFileSources CreateLocalizedTextServiceFileSourcesFactory(
|
||||
IServiceProvider container)
|
||||
{
|
||||
var hostingEnvironment = container.GetRequiredService<IHostingEnvironment>();
|
||||
IHostingEnvironment hostingEnvironment = container.GetRequiredService<IHostingEnvironment>();
|
||||
var subPath = WebPath.Combine(Constants.SystemDirectories.Umbraco, "config", "lang");
|
||||
var mainLangFolder = new DirectoryInfo(hostingEnvironment.MapPathContentRoot(subPath));
|
||||
|
||||
@@ -112,7 +110,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
container.GetRequiredService<AppCaches>(),
|
||||
mainLangFolder,
|
||||
container.GetServices<LocalizedTextServiceSupplementaryFileSource>(),
|
||||
new EmbeddedFileProvider(typeof(IAssemblyProvider).Assembly, "Umbraco.Cms.Core.EmbeddedResources.Lang").GetDirectoryContents(string.Empty));
|
||||
}
|
||||
new EmbeddedFileProvider(typeof(IAssemblyProvider).Assembly, "Umbraco.Cms.Core.EmbeddedResources.Lang")
|
||||
.GetDirectoryContents(string.Empty));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Infrastructure.Telemetry.Interfaces;
|
||||
using Umbraco.Cms.Infrastructure.Telemetry.Providers;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
public static class UmbracoBuilder_TelemetryProviders
|
||||
{
|
||||
public static IUmbracoBuilder AddTelemetryProviders(this IUmbracoBuilder builder)
|
||||
@@ -22,4 +22,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Dictionary;
|
||||
@@ -8,8 +7,8 @@ using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods to the <see cref="IUmbracoBuilder" /> class.
|
||||
/// </summary>
|
||||
@@ -45,7 +44,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a culture dictionary factory.</param>
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder, Func<IServiceProvider, ICultureDictionaryFactory> factory)
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory(
|
||||
this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, ICultureDictionaryFactory> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -56,7 +57,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A factory.</param>
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory(this IUmbracoBuilder builder, ICultureDictionaryFactory factory)
|
||||
public static IUmbracoBuilder SetCultureDictionaryFactory(
|
||||
this IUmbracoBuilder builder,
|
||||
ICultureDictionaryFactory factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -79,7 +82,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a published content model factory.</param>
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder, Func<IServiceProvider, IPublishedModelFactory> factory)
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory(
|
||||
this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IPublishedModelFactory> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -90,7 +95,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A published content model factory.</param>
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory(this IUmbracoBuilder builder, IPublishedModelFactory factory)
|
||||
public static IUmbracoBuilder SetPublishedContentModelFactory(
|
||||
this IUmbracoBuilder builder,
|
||||
IPublishedModelFactory factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -113,7 +120,9 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">The builder.</param>
|
||||
/// <param name="factory">A function creating a short string helper.</param>
|
||||
public static IUmbracoBuilder SetShortStringHelper(this IUmbracoBuilder builder, Func<IServiceProvider, IShortStringHelper> factory)
|
||||
public static IUmbracoBuilder SetShortStringHelper(
|
||||
this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IShortStringHelper> factory)
|
||||
{
|
||||
builder.Services.AddUnique(factory);
|
||||
return builder;
|
||||
@@ -135,13 +144,15 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// </summary>
|
||||
/// <param name="builder">A builder.</param>
|
||||
/// <param name="filesystemFactory">Factory method to create an IFileSystem implementation used in the MediaFileManager</param>
|
||||
public static IUmbracoBuilder SetMediaFileSystem(this IUmbracoBuilder builder,
|
||||
public static IUmbracoBuilder SetMediaFileSystem(
|
||||
this IUmbracoBuilder builder,
|
||||
Func<IServiceProvider, IFileSystem> filesystemFactory)
|
||||
{
|
||||
builder.Services.AddUnique(
|
||||
provider =>
|
||||
{
|
||||
IFileSystem filesystem = filesystemFactory(provider);
|
||||
|
||||
// We need to use the Filesystems to create a shadow wrapper,
|
||||
// because shadow wrapper requires the IsScoped delegate from the FileSystems.
|
||||
// This is used by the scope provider when taking control of the filesystems.
|
||||
@@ -160,7 +171,8 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
/// <param name="configure">Method that configures the <see cref="FileSystems" />.</param>
|
||||
/// <exception cref="ArgumentNullException">Throws exception if <paramref name="configure" /> is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">Throws exception if full path can't be resolved successfully.</exception>
|
||||
public static IUmbracoBuilder ConfigureFileSystems(this IUmbracoBuilder builder,
|
||||
public static IUmbracoBuilder ConfigureFileSystems(
|
||||
this IUmbracoBuilder builder,
|
||||
Action<IServiceProvider, FileSystems> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
@@ -212,4 +224,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Infrastructure.WebAssets;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection;
|
||||
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
internal static IUmbracoBuilder AddWebAssets(this IUmbracoBuilder builder)
|
||||
@@ -13,4 +12,3 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Deploy
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Deploy;
|
||||
|
||||
/// <summary>
|
||||
/// Defines methods that can convert a grid cell value to / from an environment-agnostic string.
|
||||
/// </summary>
|
||||
/// <remarks>Grid cell values may contain values such as content identifiers, that would be local
|
||||
/// to one environment, and need to be converted in order to be deployed.</remarks>
|
||||
/// <remarks>
|
||||
/// Grid cell values may contain values such as content identifiers, that would be local
|
||||
/// to one environment, and need to be converted in order to be deployed.
|
||||
/// </remarks>
|
||||
public interface IGridCellValueConnector
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connector supports a specified grid editor view.
|
||||
/// </summary>
|
||||
/// <param name="view">The grid editor view. It needs to be the view instead of the alias as the view is really what identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases using the same kind of view.</param>
|
||||
/// <param name="view">
|
||||
/// The grid editor view. It needs to be the view instead of the alias as the view is really what
|
||||
/// identifies what kind of connector should be used. Alias can be anything and you can have multiple different aliases
|
||||
/// using the same kind of view.
|
||||
/// </param>
|
||||
/// <remarks>A value indicating whether the connector supports the grid editor view.</remarks>
|
||||
/// <remarks>Note that <paramref name="view" /> can be string.Empty to indicate the "default" connector.</remarks>
|
||||
bool IsConnector(string view);
|
||||
@@ -31,8 +36,9 @@ namespace Umbraco.Cms.Core.Deploy
|
||||
/// Allows you to modify the value of a control being deployed.
|
||||
/// </summary>
|
||||
/// <param name="gridControl">The control being deployed.</param>
|
||||
/// <remarks>Follows the pattern of the property value connectors (<see cref="IValueConnector"/>). The SetValue method is used to modify the value of the <paramref name="gridControl"/>.</remarks>
|
||||
|
||||
/// <remarks>
|
||||
/// Follows the pattern of the property value connectors (<see cref="IValueConnector" />). The SetValue method is
|
||||
/// used to modify the value of the <paramref name="gridControl" />.
|
||||
/// </remarks>
|
||||
void SetValue(GridValue.GridControl gridControl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.DistributedLocking;
|
||||
@@ -10,12 +6,12 @@ namespace Umbraco.Cms.Infrastructure.DistributedLocking;
|
||||
|
||||
public class DefaultDistributedLockingMechanismFactory : IDistributedLockingMechanismFactory
|
||||
{
|
||||
private object _lock = new();
|
||||
private bool _initialized;
|
||||
private IDistributedLockingMechanism _distributedLockingMechanism = null!;
|
||||
private readonly IEnumerable<IDistributedLockingMechanism> _distributedLockingMechanisms;
|
||||
|
||||
private readonly IOptionsMonitor<GlobalSettings> _globalSettings;
|
||||
private readonly IEnumerable<IDistributedLockingMechanism> _distributedLockingMechanisms;
|
||||
private IDistributedLockingMechanism _distributedLockingMechanism = null!;
|
||||
private bool _initialized;
|
||||
private object _lock = new();
|
||||
|
||||
public DefaultDistributedLockingMechanismFactory(
|
||||
IOptionsMonitor<GlobalSettings> globalSettings,
|
||||
@@ -49,7 +45,8 @@ public class DefaultDistributedLockingMechanismFactory : IDistributedLockingMech
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw new InvalidOperationException($"Couldn't find DistributedLockingMechanism specified by global config: {configured}");
|
||||
throw new InvalidOperationException(
|
||||
$"Couldn't find DistributedLockingMechanism specified by global config: {configured}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +56,6 @@ public class DefaultDistributedLockingMechanismFactory : IDistributedLockingMech
|
||||
return defaultMechanism;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Couldn't find an appropriate default distributed locking mechanism.");
|
||||
throw new InvalidOperationException("Couldn't find an appropriate default distributed locking mechanism.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Semver;
|
||||
using Umbraco.Cms.Core.Semver;
|
||||
using Umbraco.Cms.Infrastructure.Migrations;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Events;
|
||||
|
||||
public class MigrationEventArgs : CancellableObjectEventArgs<IList<Type>>, IEquatable<MigrationEventArgs>
|
||||
{
|
||||
public MigrationEventArgs(IList<Type> migrationTypes, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel)
|
||||
: this(migrationTypes, null, configuredVersion, targetVersion, productName, canCancel)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
public MigrationEventArgs(IList<Type> migrationTypes, SemVersion configuredVersion, SemVersion targetVersion, string productName)
|
||||
: this(migrationTypes, null, configuredVersion, targetVersion, productName, false)
|
||||
{
|
||||
}
|
||||
|
||||
internal MigrationEventArgs(IList<Type> migrationTypes, IMigrationContext? migrationContext, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel)
|
||||
: base(migrationTypes, canCancel)
|
||||
@@ -20,10 +24,6 @@ namespace Umbraco.Cms.Core.Events
|
||||
ProductName = productName;
|
||||
}
|
||||
|
||||
public MigrationEventArgs(IList<Type> migrationTypes, SemVersion configuredVersion, SemVersion targetVersion, string productName)
|
||||
: this(migrationTypes, null, configuredVersion, targetVersion, productName, false)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Returns all migrations that were used in the migration runner
|
||||
/// </summary>
|
||||
@@ -50,18 +50,44 @@ namespace Umbraco.Cms.Core.Events
|
||||
/// <remarks>Is only available after migrations have run, for post-migrations.</remarks>
|
||||
internal IMigrationContext? MigrationContext { get; }
|
||||
|
||||
public static bool operator ==(MigrationEventArgs left, MigrationEventArgs right) => Equals(left, right);
|
||||
|
||||
public static bool operator !=(MigrationEventArgs left, MigrationEventArgs right) => !Equals(left, right);
|
||||
|
||||
public bool Equals(MigrationEventArgs? other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && ConfiguredSemVersion.Equals(other.ConfiguredSemVersion) && (MigrationContext?.Equals(other.MigrationContext) ?? false) && string.Equals(ProductName, other.ProductName) && TargetSemVersion.Equals(other.TargetSemVersion);
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.Equals(other) && ConfiguredSemVersion.Equals(other.ConfiguredSemVersion) &&
|
||||
(MigrationContext?.Equals(other.MigrationContext) ?? false) &&
|
||||
string.Equals(ProductName, other.ProductName) && TargetSemVersion.Equals(other.TargetSemVersion);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != this.GetType()) return false;
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((MigrationEventArgs)obj);
|
||||
}
|
||||
|
||||
@@ -69,26 +95,16 @@ namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
var hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ ConfiguredSemVersion.GetHashCode();
|
||||
if (MigrationContext is not null)
|
||||
{
|
||||
hashCode = (hashCode * 397) ^ MigrationContext.GetHashCode();
|
||||
}
|
||||
|
||||
hashCode = (hashCode * 397) ^ ProductName.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ TargetSemVersion.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(MigrationEventArgs left, MigrationEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(MigrationEventArgs left, MigrationEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using IScope = Umbraco.Cms.Infrastructure.Scoping.IScope;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
// TODO: lots of duplicate code in this one, refactor
|
||||
public sealed class RelateOnTrashNotificationHandler :
|
||||
INotificationHandler<ContentMovedNotification>,
|
||||
@@ -18,11 +18,11 @@ namespace Umbraco.Cms.Core.Events
|
||||
INotificationHandler<MediaMovedNotification>,
|
||||
INotificationHandler<MediaMovedToRecycleBinNotification>
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
public RelateOnTrashNotificationHandler(
|
||||
IRelationService relationService,
|
||||
@@ -40,12 +40,14 @@ namespace Umbraco.Cms.Core.Events
|
||||
|
||||
public void Handle(ContentMovedNotification notification)
|
||||
{
|
||||
foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
|
||||
foreach (MoveEventInfo<IContent> item in notification.MoveInfoCollection.Where(x =>
|
||||
x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
|
||||
{
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
var relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
IEnumerable<IRelation> relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
|
||||
foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
|
||||
foreach (IRelation relation in
|
||||
relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
|
||||
{
|
||||
_relationService.Delete(relation);
|
||||
}
|
||||
@@ -54,43 +56,44 @@ namespace Umbraco.Cms.Core.Events
|
||||
|
||||
public void Handle(ContentMovedToRecycleBinNotification notification)
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
using (IScope scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
|
||||
IRelationType? relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
|
||||
|
||||
// check that the relation-type exists, if not, then recreate it
|
||||
if (relationType == null)
|
||||
{
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
Guid documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
|
||||
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType, false);
|
||||
_relationService.Save(relationType);
|
||||
}
|
||||
|
||||
foreach (var item in notification.MoveInfoCollection)
|
||||
foreach (MoveEventInfo<IContent> item in notification.MoveInfoCollection)
|
||||
{
|
||||
var originalPath = item.OriginalPath.ToDelimitedList();
|
||||
IList<string> originalPath = item.OriginalPath.ToDelimitedList();
|
||||
var originalParentId = originalPath.Count > 2
|
||||
? int.Parse(originalPath[originalPath.Count - 2], CultureInfo.InvariantCulture)
|
||||
: Constants.System.Root;
|
||||
|
||||
// before we can create this relation, we need to ensure that the original parent still exists which
|
||||
// may not be the case if the encompassing transaction also deleted it when this item was moved to the bin
|
||||
|
||||
if (_entityService.Exists(originalParentId))
|
||||
{
|
||||
// Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later
|
||||
var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? new Relation(originalParentId, item.Entity.Id, relationType);
|
||||
IRelation relation =
|
||||
_relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ??
|
||||
new Relation(originalParentId, item.Entity.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
|
||||
_auditService.Add(AuditType.Delete,
|
||||
_auditService.Add(
|
||||
AuditType.Delete,
|
||||
item.Entity.WriterId,
|
||||
item.Entity.Id,
|
||||
ObjectTypes.GetName(UmbracoObjectTypes.Document),
|
||||
string.Format(_textService.Localize("recycleBin","contentTrashed"), item.Entity.Id, originalParentId)
|
||||
);
|
||||
UmbracoObjectTypes.Document.GetName(),
|
||||
string.Format(_textService.Localize("recycleBin", "contentTrashed"), item.Entity.Id, originalParentId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,58 +103,61 @@ namespace Umbraco.Cms.Core.Events
|
||||
|
||||
public void Handle(MediaMovedNotification notification)
|
||||
{
|
||||
foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString)))
|
||||
foreach (MoveEventInfo<IMedia> item in notification.MoveInfoCollection.Where(x =>
|
||||
x.OriginalPath.Contains(Constants.System.RecycleBinMediaString)))
|
||||
{
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
|
||||
var relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
|
||||
IEnumerable<IRelation> relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
foreach (IRelation relation in
|
||||
relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
|
||||
{
|
||||
_relationService.Delete(relation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Handle(MediaMovedToRecycleBinNotification notification)
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
using (IScope scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
|
||||
var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
|
||||
IRelationType? relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
|
||||
|
||||
// check that the relation-type exists, if not, then recreate it
|
||||
if (relationType == null)
|
||||
{
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
Guid documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType, false);
|
||||
_relationService.Save(relationType);
|
||||
}
|
||||
|
||||
foreach (var item in notification.MoveInfoCollection)
|
||||
foreach (MoveEventInfo<IMedia> item in notification.MoveInfoCollection)
|
||||
{
|
||||
var originalPath = item.OriginalPath.ToDelimitedList();
|
||||
IList<string> originalPath = item.OriginalPath.ToDelimitedList();
|
||||
var originalParentId = originalPath.Count > 2
|
||||
? int.Parse(originalPath[originalPath.Count - 2], CultureInfo.InvariantCulture)
|
||||
: Constants.System.Root;
|
||||
|
||||
// before we can create this relation, we need to ensure that the original parent still exists which
|
||||
// may not be the case if the encompassing transaction also deleted it when this item was moved to the bin
|
||||
if (_entityService.Exists(originalParentId))
|
||||
{
|
||||
// Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later
|
||||
var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? new Relation(originalParentId, item.Entity.Id, relationType);
|
||||
IRelation relation =
|
||||
_relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ??
|
||||
new Relation(originalParentId, item.Entity.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
_auditService.Add(AuditType.Delete,
|
||||
_auditService.Add(
|
||||
AuditType.Delete,
|
||||
item.Entity.CreatorId,
|
||||
item.Entity.Id,
|
||||
ObjectTypes.GetName(UmbracoObjectTypes.Media),
|
||||
string.Format(_textService.Localize("recycleBin","mediaTrashed"), item.Entity.Id, originalParentId)
|
||||
);
|
||||
UmbracoObjectTypes.Media.GetName(),
|
||||
string.Format(_textService.Localize("recycleBin", "mediaTrashed"), item.Entity.Id, originalParentId));
|
||||
}
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract class BaseValueSetBuilder<TContent> : IValueSetBuilder<TContent>
|
||||
where TContent : IContentBase
|
||||
{
|
||||
protected bool PublishedValuesOnly { get; }
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
|
||||
protected BaseValueSetBuilder(PropertyEditorCollection propertyEditors, bool publishedValuesOnly)
|
||||
{
|
||||
PublishedValuesOnly = publishedValuesOnly;
|
||||
_propertyEditors = propertyEditors ?? throw new System.ArgumentNullException(nameof(propertyEditors));
|
||||
_propertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors));
|
||||
}
|
||||
|
||||
protected bool PublishedValuesOnly { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IEnumerable<ValueSet> GetValueSets(params TContent[] content);
|
||||
|
||||
protected void AddPropertyValue(IProperty property, string? culture, string? segment, IDictionary<string, IEnumerable<object?>>? values)
|
||||
{
|
||||
var editor = _propertyEditors[property.PropertyType.PropertyEditorAlias];
|
||||
if (editor == null) return;
|
||||
|
||||
var indexVals = editor.PropertyIndexValueFactory.GetIndexValues(property, culture, segment, PublishedValuesOnly);
|
||||
foreach (var keyVal in indexVals)
|
||||
IDataEditor? editor = _propertyEditors[property.PropertyType.PropertyEditorAlias];
|
||||
if (editor == null)
|
||||
{
|
||||
if (keyVal.Key.IsNullOrWhiteSpace()) continue;
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<KeyValuePair<string, IEnumerable<object?>>> indexVals =
|
||||
editor.PropertyIndexValueFactory.GetIndexValues(property, culture, segment, PublishedValuesOnly);
|
||||
foreach (KeyValuePair<string, IEnumerable<object?>> keyVal in indexVals)
|
||||
{
|
||||
if (keyVal.Key.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cultureSuffix = culture == null ? string.Empty : "_" + culture;
|
||||
|
||||
@@ -43,22 +50,35 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
continue;
|
||||
case string strVal:
|
||||
{
|
||||
if (strVal.IsNullOrWhiteSpace()) continue;
|
||||
if (strVal.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = $"{keyVal.Key}{cultureSuffix}";
|
||||
if (values?.TryGetValue(key, out var v) ?? false)
|
||||
if (values?.TryGetValue(key, out IEnumerable<object?>? v) ?? false)
|
||||
{
|
||||
values[key] = new List<object?>(v) { val }.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
values?.Add($"{keyVal.Key}{cultureSuffix}", val.Yield());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
{
|
||||
var key = $"{keyVal.Key}{cultureSuffix}";
|
||||
if (values?.TryGetValue(key, out var v) ?? false)
|
||||
if (values?.TryGetValue(key, out IEnumerable<object?>? v) ?? false)
|
||||
{
|
||||
values[key] = new List<object?>(v) { val }.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
values?.Add($"{keyVal.Key}{cultureSuffix}", val.Yield());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -66,5 +86,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +1,33 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Querying;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Performs the data lookups required to rebuild a content index
|
||||
/// </summary>
|
||||
public class ContentIndexPopulator : IndexPopulator<IUmbracoContentIndex>
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory;
|
||||
private readonly IValueSetBuilder<IContent> _contentValueSetBuilder;
|
||||
private readonly ILogger<ContentIndexPopulator> _logger;
|
||||
private readonly int? _parentId;
|
||||
|
||||
private readonly bool _publishedValuesOnly;
|
||||
private readonly IUmbracoDatabaseFactory _umbracoDatabaseFactory;
|
||||
|
||||
/// <summary>
|
||||
/// This is a static query, it's parameters don't change so store statically
|
||||
/// </summary>
|
||||
private IQuery<IContent>? _publishedQuery;
|
||||
private IQuery<IContent> PublishedQuery => _publishedQuery ??= _umbracoDatabaseFactory.SqlContext.Query<IContent>().Where(x => x.Published);
|
||||
|
||||
private readonly bool _publishedValuesOnly;
|
||||
private readonly int? _parentId;
|
||||
private readonly ILogger<ContentIndexPopulator> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor to lookup all content data
|
||||
/// </summary>
|
||||
/// <param name="contentService"></param>
|
||||
/// <param name="sqlContext"></param>
|
||||
/// <param name="contentValueSetBuilder"></param>
|
||||
public ContentIndexPopulator(
|
||||
ILogger<ContentIndexPopulator> logger,
|
||||
IContentService contentService,
|
||||
@@ -64,11 +56,13 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
_parentId = parentId;
|
||||
}
|
||||
|
||||
public override bool IsRegistered(IUmbracoContentIndex index)
|
||||
{
|
||||
private IQuery<IContent> PublishedQuery => _publishedQuery ??=
|
||||
_umbracoDatabaseFactory.SqlContext.Query<IContent>().Where(x => x.Published);
|
||||
|
||||
public override bool IsRegistered(IUmbracoContentIndex index) =>
|
||||
|
||||
// check if it should populate based on published values
|
||||
return _publishedValuesOnly == index.PublishedValuesOnly;
|
||||
}
|
||||
_publishedValuesOnly == index.PublishedValuesOnly;
|
||||
|
||||
protected override void PopulateIndexes(IReadOnlyList<IIndex> indexes)
|
||||
{
|
||||
@@ -110,18 +104,18 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
var valueSets = _contentValueSetBuilder.GetValueSets(content).ToList();
|
||||
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
foreach (var index in indexes)
|
||||
foreach (IIndex index in indexes)
|
||||
{
|
||||
index.IndexItems(valueSets);
|
||||
}
|
||||
}
|
||||
|
||||
pageIndex++;
|
||||
} while (content.Length == pageSize);
|
||||
}
|
||||
while (content.Length == pageSize);
|
||||
}
|
||||
|
||||
protected void IndexPublishedContent(int contentParentId, int pageIndex, int pageSize,
|
||||
IReadOnlyList<IIndex> indexes)
|
||||
protected void IndexPublishedContent(int contentParentId, int pageIndex, int pageSize, IReadOnlyList<IIndex> indexes)
|
||||
{
|
||||
IContent[] content;
|
||||
|
||||
@@ -131,15 +125,13 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
// add the published filter
|
||||
// note: We will filter for published variants in the validator
|
||||
content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _, PublishedQuery,
|
||||
Ordering.By("Path", Direction.Ascending)).ToArray();
|
||||
|
||||
content = _contentService.GetPagedDescendants(contentParentId, pageIndex, pageSize, out _, PublishedQuery, Ordering.By("Path")).ToArray();
|
||||
|
||||
if (content.Length > 0)
|
||||
{
|
||||
var indexableContent = new List<IContent>();
|
||||
|
||||
foreach (var item in content)
|
||||
foreach (IContent item in content)
|
||||
{
|
||||
if (item.Level == 1)
|
||||
{
|
||||
@@ -167,9 +159,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
|
||||
pageIndex++;
|
||||
} while (content.Length == pageSize);
|
||||
}
|
||||
while (content.Length == pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
@@ -8,21 +6,24 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
using IScope = Umbraco.Cms.Infrastructure.Scoping.IScope;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds <see cref="ValueSet" />s for <see cref="IContent" /> items
|
||||
/// </summary>
|
||||
public class ContentValueSetBuilder : BaseValueSetBuilder<IContent>, IContentValueSetBuilder, IPublishedContentValueSetBuilder
|
||||
public class ContentValueSetBuilder : BaseValueSetBuilder<IContent>, IContentValueSetBuilder,
|
||||
IPublishedContentValueSetBuilder
|
||||
{
|
||||
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public ContentValueSetBuilder(PropertyEditorCollection propertyEditors,
|
||||
public ContentValueSetBuilder(
|
||||
PropertyEditorCollection propertyEditors,
|
||||
UrlSegmentProviderCollection urlSegmentProviders,
|
||||
IUserService userService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
@@ -44,7 +45,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
// We can lookup all of the creator/writer names at once which can save some
|
||||
// processing below instead of one by one.
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
using (IScope scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
creatorIds = _userService.GetProfilesById(content.Select(x => x.CreatorId).ToArray())
|
||||
.ToDictionary(x => x.Id, x => x);
|
||||
@@ -62,8 +63,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
// but I wonder if there's a way to reduce the boxing that we have to do or if it will matter in the end since
|
||||
// Lucene will do it no matter what? One idea was to create a `FieldValue` struct which would contain `object`, `object[]`, `ValueType` and `ValueType[]`
|
||||
// references and then each array is an array of `FieldValue[]` and values are assigned accordingly. Not sure if it will make a difference or not.
|
||||
|
||||
foreach (var c in content)
|
||||
foreach (IContent c in content)
|
||||
{
|
||||
var isVariant = c.ContentType.VariesByCulture();
|
||||
|
||||
@@ -71,7 +71,9 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
var values = new Dictionary<string, IEnumerable<object?>>
|
||||
{
|
||||
{ "icon", c.ContentType.Icon?.Yield() ?? Enumerable.Empty<string>() },
|
||||
{UmbracoExamineFieldNames.PublishedFieldName, new object[] {c.Published ? "y" : "n"}}, //Always add invariant published value
|
||||
{
|
||||
UmbracoExamineFieldNames.PublishedFieldName, new object[] { c.Published ? "y" : "n" }
|
||||
}, // Always add invariant published value
|
||||
{ "id", new object[] { c.Id } },
|
||||
{ UmbracoExamineFieldNames.NodeKeyFieldName, new object[] { c.Key } },
|
||||
{ "parentID", new object[] { c.Level > 1 ? c.ParentId : -1 } },
|
||||
@@ -80,14 +82,24 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{ "sortOrder", new object[] { c.SortOrder } },
|
||||
{ "createDate", new object[] { c.CreateDate } }, // Always add invariant createDate
|
||||
{ "updateDate", new object[] { c.UpdateDate } }, // Always add invariant updateDate
|
||||
{UmbracoExamineFieldNames.NodeNameFieldName, (PublishedValuesOnly //Always add invariant nodeName
|
||||
{
|
||||
UmbracoExamineFieldNames.NodeNameFieldName, (PublishedValuesOnly // Always add invariant nodeName
|
||||
? c.PublishName?.Yield()
|
||||
: c.Name?.Yield()) ?? Enumerable.Empty<string>()},
|
||||
: c.Name?.Yield()) ?? Enumerable.Empty<string>()
|
||||
},
|
||||
{ "urlName", urlValue?.Yield() ?? Enumerable.Empty<string>() }, // Always add invariant urlName
|
||||
{"path", c.Path?.Yield() ?? Enumerable.Empty<string>()},
|
||||
{"nodeType", c.ContentType.Id.ToString().Yield() ?? Enumerable.Empty<string>()},
|
||||
{"creatorName", (creatorIds.TryGetValue(c.CreatorId, out var creatorProfile) ? creatorProfile.Name! : "??").Yield() },
|
||||
{"writerName", (writerIds.TryGetValue(c.WriterId, out var writerProfile) ? writerProfile.Name! : "??").Yield() },
|
||||
{ "path", c.Path.Yield() },
|
||||
{ "nodeType", c.ContentType.Id.ToString().Yield() },
|
||||
{
|
||||
"creatorName",
|
||||
(creatorIds.TryGetValue(c.CreatorId, out IProfile? creatorProfile) ? creatorProfile.Name! : "??")
|
||||
.Yield()
|
||||
},
|
||||
{
|
||||
"writerName",
|
||||
(writerIds.TryGetValue(c.WriterId, out IProfile? writerProfile) ? writerProfile.Name! : "??")
|
||||
.Yield()
|
||||
},
|
||||
{ "writerID", new object[] { c.WriterId } },
|
||||
{ "templateID", new object[] { c.TemplateId ?? 0 } },
|
||||
{ UmbracoExamineFieldNames.VariesByCultureFieldName, new object[] { "n" } },
|
||||
@@ -105,14 +117,15 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
values[$"nodeName_{lowerCulture}"] = (PublishedValuesOnly
|
||||
? c.GetPublishName(culture)?.Yield()
|
||||
: c.GetCultureName(culture)?.Yield()) ?? Enumerable.Empty<string>();
|
||||
values[$"{UmbracoExamineFieldNames.PublishedFieldName}_{lowerCulture}"] = (c.IsCulturePublished(culture) ? "y" : "n").Yield<object>();
|
||||
values[$"{UmbracoExamineFieldNames.PublishedFieldName}_{lowerCulture}"] =
|
||||
(c.IsCulturePublished(culture) ? "y" : "n").Yield<object>();
|
||||
values[$"updateDate_{lowerCulture}"] = (PublishedValuesOnly
|
||||
? c.GetPublishDate(culture)
|
||||
: c.GetUpdateDate(culture))?.Yield<object>() ?? Enumerable.Empty<object>();
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var property in c.Properties)
|
||||
foreach (IProperty property in c.Properties)
|
||||
{
|
||||
if (!property.PropertyType.VariesByCulture())
|
||||
{
|
||||
@@ -121,9 +134,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
else
|
||||
{
|
||||
foreach (var culture in c.AvailableCultures)
|
||||
{
|
||||
AddPropertyValue(property, culture.ToLowerInvariant(), null, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var vs = new ValueSet(c.Id.ToInvariantString(), IndexTypes.Content, c.ContentType.Alias, values);
|
||||
|
||||
@@ -131,5 +146,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,23 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Used to validate a ValueSet for content/media - based on permissions, parent id, etc....
|
||||
/// </summary>
|
||||
public class ContentValueSetValidator : ValueSetValidator, IContentValueSetValidator
|
||||
{
|
||||
private readonly IPublicAccessService? _publicAccessService;
|
||||
private readonly IScopeProvider? _scopeProvider;
|
||||
private const string PathKey = "path";
|
||||
private static readonly IEnumerable<string> ValidCategories = new[] {IndexTypes.Content, IndexTypes.Media};
|
||||
private readonly IPublicAccessService? _publicAccessService;
|
||||
private readonly IScopeProvider? _scopeProvider;
|
||||
|
||||
// used for tests
|
||||
public ContentValueSetValidator(bool publishedValuesOnly, int? parentId = null, IEnumerable<string>? includeItemTypes = null, IEnumerable<string>? excludeItemTypes = null)
|
||||
: this(publishedValuesOnly, true, null, null, parentId, includeItemTypes, excludeItemTypes)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentValueSetValidator(
|
||||
bool publishedValuesOnly,
|
||||
bool supportProtectedContent,
|
||||
IPublicAccessService? publicAccessService,
|
||||
IScopeProvider? scopeProvider,
|
||||
int? parentId = null,
|
||||
IEnumerable<string>? includeItemTypes = null,
|
||||
IEnumerable<string>? excludeItemTypes = null)
|
||||
: base(includeItemTypes, excludeItemTypes, null, null)
|
||||
{
|
||||
PublishedValuesOnly = publishedValuesOnly;
|
||||
SupportProtectedContent = supportProtectedContent;
|
||||
ParentId = parentId;
|
||||
_publicAccessService = publicAccessService;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
protected override IEnumerable<string> ValidIndexCategories => ValidCategories;
|
||||
|
||||
public bool PublishedValuesOnly { get; }
|
||||
@@ -32,22 +54,29 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
// we cannot return FAILED here because we need the value set to get into the indexer and then deal with it from there
|
||||
// because we need to remove anything that doesn't pass by parent Id in the cases that umbraco data is moved to an illegal parent.
|
||||
if (!path.Contains(string.Concat(",", ParentId.Value.ToString(CultureInfo.InvariantCulture), ",")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ValidateRecycleBin(string path, string category)
|
||||
{
|
||||
var recycleBinId = category == IndexTypes.Content ? Constants.System.RecycleBinContentString : Constants.System.RecycleBinMediaString;
|
||||
var recycleBinId = category == IndexTypes.Content
|
||||
? Constants.System.RecycleBinContentString
|
||||
: Constants.System.RecycleBinMediaString;
|
||||
|
||||
//check for recycle bin
|
||||
if (PublishedValuesOnly)
|
||||
{
|
||||
if (path.Contains(string.Concat(",", recycleBinId, ",")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -74,33 +103,14 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
return true;
|
||||
}
|
||||
|
||||
// used for tests
|
||||
public ContentValueSetValidator(bool publishedValuesOnly, int? parentId = null,
|
||||
IEnumerable<string>? includeItemTypes = null, IEnumerable<string>? excludeItemTypes = null)
|
||||
: this(publishedValuesOnly, true, null, null, parentId, includeItemTypes, excludeItemTypes)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentValueSetValidator(bool publishedValuesOnly, bool supportProtectedContent,
|
||||
IPublicAccessService? publicAccessService,
|
||||
IScopeProvider? scopeProvider,
|
||||
int? parentId = null,
|
||||
IEnumerable<string>? includeItemTypes = null, IEnumerable<string>? excludeItemTypes = null)
|
||||
: base(includeItemTypes, excludeItemTypes, null, null)
|
||||
{
|
||||
PublishedValuesOnly = publishedValuesOnly;
|
||||
SupportProtectedContent = supportProtectedContent;
|
||||
ParentId = parentId;
|
||||
_publicAccessService = publicAccessService;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
public override ValueSetValidationResult Validate(ValueSet valueSet)
|
||||
{
|
||||
var baseValidate = base.Validate(valueSet);
|
||||
ValueSetValidationResult baseValidate = base.Validate(valueSet);
|
||||
valueSet = baseValidate.ValueSet;
|
||||
if (baseValidate.Status == ValueSetValidationStatus.Failed)
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
var isFiltered = baseValidate.Status == ValueSetValidationStatus.Filtered;
|
||||
|
||||
@@ -108,7 +118,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
//check for published content
|
||||
if (valueSet.Category == IndexTypes.Content && PublishedValuesOnly)
|
||||
{
|
||||
if (!valueSet.Values.TryGetValue(UmbracoExamineFieldNames.PublishedFieldName, out var published))
|
||||
if (!valueSet.Values.TryGetValue(UmbracoExamineFieldNames.PublishedFieldName, out IReadOnlyList<object>? published))
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
@@ -119,17 +129,19 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
|
||||
//deal with variants, if there are unpublished variants than we need to remove them from the value set
|
||||
if (valueSet.Values.TryGetValue(UmbracoExamineFieldNames.VariesByCultureFieldName, out var variesByCulture)
|
||||
if (valueSet.Values.TryGetValue(UmbracoExamineFieldNames.VariesByCultureFieldName, out IReadOnlyList<object>? variesByCulture)
|
||||
&& variesByCulture.Count > 0 && variesByCulture[0].Equals("y"))
|
||||
{
|
||||
//so this valueset is for a content that varies by culture, now check for non-published cultures and remove those values
|
||||
foreach (var publishField in valueSet.Values.Where(x => x.Key.StartsWith($"{UmbracoExamineFieldNames.PublishedFieldName}_")).ToList())
|
||||
foreach (KeyValuePair<string, IReadOnlyList<object>> publishField in valueSet.Values
|
||||
.Where(x => x.Key.StartsWith($"{UmbracoExamineFieldNames.PublishedFieldName}_")).ToList())
|
||||
{
|
||||
if (publishField.Value.Count <= 0 || !publishField.Value[0].Equals("y"))
|
||||
{
|
||||
//this culture is not published, so remove all of these culture values
|
||||
var cultureSuffix = publishField.Key.Substring(publishField.Key.LastIndexOf('_'));
|
||||
foreach (var cultureField in valueSet.Values.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList())
|
||||
foreach (KeyValuePair<string, IReadOnlyList<object>> cultureField in valueSet.Values
|
||||
.Where(x => x.Key.InvariantEndsWith(cultureSuffix)).ToList())
|
||||
{
|
||||
filteredValues.Remove(cultureField.Key);
|
||||
isFiltered = true;
|
||||
@@ -140,10 +152,26 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
|
||||
//must have a 'path'
|
||||
if (!valueSet.Values.TryGetValue(PathKey, out var pathValues)) return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
if (pathValues.Count == 0) return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
if (pathValues[0] == null) return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
if (pathValues[0].ToString().IsNullOrWhiteSpace()) return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
if (!valueSet.Values.TryGetValue(PathKey, out IReadOnlyList<object>? pathValues))
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
if (pathValues.Count == 0)
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
if (pathValues[0] == null)
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
if (pathValues[0].ToString().IsNullOrWhiteSpace())
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
var path = pathValues[0].ToString();
|
||||
|
||||
var filteredValueSet = new ValueSet(valueSet.Id, valueSet.Category, valueSet.ItemType, filteredValues.ToDictionary(x => x.Key, x => (IEnumerable<object>)x.Value));
|
||||
@@ -153,9 +181,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
if (!ValidatePath(path!, valueSet.Category)
|
||||
|| !ValidateRecycleBin(path!, valueSet.Category)
|
||||
|| !ValidateProtectedContent(path!, valueSet.Category))
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Filtered, filteredValueSet);
|
||||
}
|
||||
|
||||
return new ValueSetValidationResult(isFiltered ? ValueSetValidationStatus.Filtered : ValueSetValidationStatus.Valid, filteredValueSet);
|
||||
}
|
||||
return new ValueSetValidationResult(
|
||||
isFiltered ? ValueSetValidationStatus.Filtered : ValueSetValidationStatus.Valid, filteredValueSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for Examine.
|
||||
/// </summary>
|
||||
public static class ExamineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content from the <paramref name="cache" />.
|
||||
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content from the
|
||||
/// <paramref name="cache" />.
|
||||
/// </summary>
|
||||
/// <param name="results">The search results.</param>
|
||||
/// <param name="cache">The cache to fetch the content from.</param>
|
||||
@@ -25,26 +24,35 @@ namespace Umbraco.Extensions
|
||||
/// <remarks>
|
||||
/// Search results are skipped if it can't be fetched from the <paramref name="cache" /> by its integer id.
|
||||
/// </remarks>
|
||||
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(this IEnumerable<ISearchResult> results, IPublishedCache? cache)
|
||||
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(
|
||||
this IEnumerable<ISearchResult> results,
|
||||
IPublishedCache? cache)
|
||||
{
|
||||
if (cache == null) throw new ArgumentNullException(nameof(cache));
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cache));
|
||||
}
|
||||
|
||||
var publishedSearchResults = new List<PublishedSearchResult>();
|
||||
|
||||
foreach (var result in results)
|
||||
foreach (ISearchResult result in results)
|
||||
{
|
||||
if (int.TryParse(result.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var contentId) &&
|
||||
cache.GetById(contentId) is IPublishedContent content)
|
||||
if (int.TryParse(result.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var contentId))
|
||||
{
|
||||
IPublishedContent? content = cache.GetById(contentId);
|
||||
if (content is not null)
|
||||
{
|
||||
publishedSearchResults.Add(new PublishedSearchResult(content, result.Score));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return publishedSearchResults;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content, media or members from the <paramref name="snapshot" />.
|
||||
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content, media or members from the
|
||||
/// <paramref name="snapshot" />.
|
||||
/// </summary>
|
||||
/// <param name="results">The search results.</param>
|
||||
/// <param name="snapshot">The snapshot.</param>
|
||||
@@ -55,13 +63,18 @@ namespace Umbraco.Extensions
|
||||
/// <remarks>
|
||||
/// Search results are skipped if it can't be fetched from the respective cache by its integer id.
|
||||
/// </remarks>
|
||||
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(this IEnumerable<ISearchResult> results, IPublishedSnapshot snapshot)
|
||||
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(
|
||||
this IEnumerable<ISearchResult> results,
|
||||
IPublishedSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null) throw new ArgumentNullException(nameof(snapshot));
|
||||
if (snapshot == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(snapshot));
|
||||
}
|
||||
|
||||
var publishedSearchResults = new List<PublishedSearchResult>();
|
||||
|
||||
foreach (var result in results)
|
||||
foreach (ISearchResult result in results)
|
||||
{
|
||||
if (int.TryParse(result.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var contentId) &&
|
||||
result.Values.TryGetValue(ExamineFieldNames.CategoryFieldName, out var indexType))
|
||||
@@ -91,4 +104,3 @@ namespace Umbraco.Extensions
|
||||
return publishedSearchResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
[DataContract(Name = "indexer", Namespace = "")]
|
||||
public class ExamineIndexModel
|
||||
{
|
||||
@@ -20,6 +19,4 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
[DataMember(Name = "canRebuild")]
|
||||
public bool CanRebuild { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Examine;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -13,17 +8,17 @@ using Umbraco.Cms.Core.Runtime;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public class ExamineIndexRebuilder : IIndexRebuilder
|
||||
{
|
||||
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly ILogger<ExamineIndexRebuilder> _logger;
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly ILogger<ExamineIndexRebuilder> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IEnumerable<IIndexPopulator> _populators;
|
||||
private readonly object _rebuildLocker = new();
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExamineIndexRebuilder" /> class.
|
||||
@@ -125,7 +120,8 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
if (!Monitor.TryEnter(_rebuildLocker))
|
||||
{
|
||||
_logger.LogWarning("Call was made to RebuildIndexes but the task runner for rebuilding is already running");
|
||||
_logger.LogWarning(
|
||||
"Call was made to RebuildIndexes but the task runner for rebuilding is already running");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -166,13 +162,15 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
if (!Monitor.TryEnter(_rebuildLocker))
|
||||
{
|
||||
_logger.LogWarning($"Call was made to {nameof(RebuildIndexes)} but the task runner for rebuilding is already running");
|
||||
_logger.LogWarning(
|
||||
$"Call was made to {nameof(RebuildIndexes)} but the task runner for rebuilding is already running");
|
||||
}
|
||||
else
|
||||
{
|
||||
// If an index exists but it has zero docs we'll consider it empty and rebuild
|
||||
IIndex[] indexes = (onlyEmptyIndexes
|
||||
? _examineManager.Indexes.Where(x => !x.IndexExists() || (x is IIndexStats stats && stats.GetDocumentCount() == 0))
|
||||
? _examineManager.Indexes.Where(x =>
|
||||
!x.IndexExists() || (x is IIndexStats stats && stats.GetDocumentCount() == 0))
|
||||
: _examineManager.Indexes).ToArray();
|
||||
|
||||
if (indexes.Length == 0)
|
||||
@@ -213,4 +211,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
[DataContract(Name = "searcher", Namespace = "")]
|
||||
public class ExamineSearcherModel
|
||||
{
|
||||
public ExamineSearcherModel()
|
||||
{
|
||||
}
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Examine;
|
||||
using Examine.Search;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -14,8 +10,8 @@ using Umbraco.Cms.Infrastructure.HostedServices;
|
||||
using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Indexing handler for Examine indexes
|
||||
/// </summary>
|
||||
@@ -25,17 +21,17 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
// enlist with a lower priority to ensure that anything "default" runs after us
|
||||
// but greater that SafeXmlReaderWriter priority which is 60
|
||||
private const int EnlistPriority = 80;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly ILogger<ExamineUmbracoIndexingHandler> _logger;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
|
||||
private readonly IContentValueSetBuilder _contentValueSetBuilder;
|
||||
private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder;
|
||||
private readonly Lazy<bool> _enabled;
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly ILogger<ExamineUmbracoIndexingHandler> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IValueSetBuilder<IMedia> _mediaValueSetBuilder;
|
||||
private readonly IValueSetBuilder<IMember> _memberValueSetBuilder;
|
||||
private readonly Lazy<bool> _enabled;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IPublishedContentValueSetBuilder _publishedContentValueSetBuilder;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
|
||||
public ExamineUmbracoIndexingHandler(
|
||||
IMainDom mainDom,
|
||||
@@ -62,45 +58,6 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
_enabled = new Lazy<bool>(IsEnabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to lazily check if Examine Index handling is enabled
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool IsEnabled()
|
||||
{
|
||||
//let's deal with shutting down Examine with MainDom
|
||||
var examineShutdownRegistered = _mainDom.Register(release: () =>
|
||||
{
|
||||
using (_profilingLogger.TraceDuration<ExamineUmbracoIndexingHandler>("Examine shutting down"))
|
||||
{
|
||||
_examineManager.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
if (!examineShutdownRegistered)
|
||||
{
|
||||
_logger.LogInformation("Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled");
|
||||
|
||||
//if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled!
|
||||
Suspendable.ExamineEvents.SuspendIndexers(_logger);
|
||||
return false; //exit, do not continue
|
||||
}
|
||||
|
||||
_logger.LogDebug("Examine shutdown registered with MainDom");
|
||||
|
||||
var registeredIndexers = _examineManager.Indexes.OfType<IUmbracoIndex>().Count(x => x.EnableDefaultEventHandler);
|
||||
|
||||
_logger.LogInformation("Adding examine event handlers for {RegisteredIndexers} index providers.", registeredIndexers);
|
||||
|
||||
// don't bind event handlers if we're not suppose to listen
|
||||
if (registeredIndexers == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Enabled => _enabled.Value;
|
||||
|
||||
@@ -182,14 +139,14 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
//Delete all content of this content/media/member type that is in any content indexer by looking up matched examine docs
|
||||
foreach (var id in removedContentTypes)
|
||||
{
|
||||
foreach (var index in _examineManager.Indexes.OfType<IUmbracoIndex>())
|
||||
foreach (IUmbracoIndex index in _examineManager.Indexes.OfType<IUmbracoIndex>())
|
||||
{
|
||||
var page = 0;
|
||||
var total = long.MaxValue;
|
||||
while (page * pageSize < total)
|
||||
{
|
||||
//paging with examine, see https://shazwazza.com/post/paging-with-examine/
|
||||
var results = index.Searcher
|
||||
ISearchResults? results = index.Searcher
|
||||
.CreateQuery()
|
||||
.Field("nodeType", id.ToInvariantString())
|
||||
.Execute(QueryOptions.SkipTake(page * pageSize, pageSize));
|
||||
@@ -197,7 +154,8 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
foreach (ISearchResult item in results)
|
||||
{
|
||||
if (int.TryParse(item.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out int contentId))
|
||||
if (int.TryParse(item.Id, NumberStyles.Integer, CultureInfo.InvariantCulture,
|
||||
out var contentId))
|
||||
{
|
||||
DeleteIndexForEntity(contentId, false);
|
||||
}
|
||||
@@ -209,10 +167,53 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to lazily check if Examine Index handling is enabled
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool IsEnabled()
|
||||
{
|
||||
//let's deal with shutting down Examine with MainDom
|
||||
var examineShutdownRegistered = _mainDom.Register(release: () =>
|
||||
{
|
||||
using (_profilingLogger.TraceDuration<ExamineUmbracoIndexingHandler>("Examine shutting down"))
|
||||
{
|
||||
_examineManager.Dispose();
|
||||
}
|
||||
});
|
||||
|
||||
if (!examineShutdownRegistered)
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"Examine shutdown not registered, this AppDomain is not the MainDom, Examine will be disabled");
|
||||
|
||||
//if we could not register the shutdown examine ourselves, it means we are not maindom! in this case all of examine should be disabled!
|
||||
Suspendable.ExamineEvents.SuspendIndexers(_logger);
|
||||
return false; //exit, do not continue
|
||||
}
|
||||
|
||||
_logger.LogDebug("Examine shutdown registered with MainDom");
|
||||
|
||||
var registeredIndexers =
|
||||
_examineManager.Indexes.OfType<IUmbracoIndex>().Count(x => x.EnableDefaultEventHandler);
|
||||
|
||||
_logger.LogInformation("Adding examine event handlers for {RegisteredIndexers} index providers.",
|
||||
registeredIndexers);
|
||||
|
||||
// don't bind event handlers if we're not suppose to listen
|
||||
if (registeredIndexers == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Deferred Actions
|
||||
|
||||
private class DeferedActions
|
||||
{
|
||||
private readonly List<DeferedAction> _actions = new List<DeferedAction>();
|
||||
private readonly List<DeferedAction> _actions = new();
|
||||
|
||||
public static DeferedActions? Get(ICoreScopeProvider scopeProvider)
|
||||
{
|
||||
@@ -246,7 +247,8 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
private abstract class DeferedAction
|
||||
{
|
||||
public virtual void Execute()
|
||||
{ }
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -255,11 +257,12 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
private class DeferedReIndexForContent : DeferedAction
|
||||
{
|
||||
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
|
||||
private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler;
|
||||
private readonly IContent _content;
|
||||
private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler;
|
||||
private readonly bool _isPublished;
|
||||
|
||||
public DeferedReIndexForContent(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IContent content, bool isPublished)
|
||||
public DeferedReIndexForContent(IBackgroundTaskQueue backgroundTaskQueue,
|
||||
ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IContent content, bool isPublished)
|
||||
{
|
||||
_backgroundTaskQueue = backgroundTaskQueue;
|
||||
_examineUmbracoIndexingHandler = examineUmbracoIndexingHandler;
|
||||
@@ -267,23 +270,27 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
_isPublished = isPublished;
|
||||
}
|
||||
|
||||
public override void Execute() => Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _content, _isPublished);
|
||||
public override void Execute() =>
|
||||
Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _content, _isPublished);
|
||||
|
||||
public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IContent content, bool isPublished)
|
||||
public static void Execute(IBackgroundTaskQueue backgroundTaskQueue,
|
||||
ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IContent content, bool isPublished)
|
||||
=> backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken =>
|
||||
{
|
||||
using ICoreScope scope = examineUmbracoIndexingHandler._scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
using ICoreScope scope =
|
||||
examineUmbracoIndexingHandler._scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
|
||||
// for content we have a different builder for published vs unpublished
|
||||
// we don't want to build more value sets than is needed so we'll lazily build 2 one for published one for non-published
|
||||
var builders = new Dictionary<bool, Lazy<List<ValueSet>>>
|
||||
{
|
||||
[true] = new Lazy<List<ValueSet>>(() => examineUmbracoIndexingHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()),
|
||||
[false] = new Lazy<List<ValueSet>>(() => examineUmbracoIndexingHandler._contentValueSetBuilder.GetValueSets(content).ToList())
|
||||
[true] = new(() => examineUmbracoIndexingHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()),
|
||||
[false] = new(() => examineUmbracoIndexingHandler._contentValueSetBuilder.GetValueSets(content).ToList())
|
||||
};
|
||||
|
||||
// This is only for content - so only index items for IUmbracoContentIndex (to exlude members)
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType<IUmbracoContentIndex>()
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes
|
||||
.OfType<IUmbracoContentIndex>()
|
||||
//filter the indexers
|
||||
.Where(x => isPublished || !x.PublishedValuesOnly)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
@@ -308,10 +315,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
|
||||
private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler;
|
||||
private readonly IMedia _media;
|
||||
private readonly bool _isPublished;
|
||||
private readonly IMedia _media;
|
||||
|
||||
public DeferedReIndexForMedia(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMedia media, bool isPublished)
|
||||
public DeferedReIndexForMedia(IBackgroundTaskQueue backgroundTaskQueue,
|
||||
ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMedia media, bool isPublished)
|
||||
{
|
||||
_backgroundTaskQueue = backgroundTaskQueue;
|
||||
_examineUmbracoIndexingHandler = examineUmbracoIndexingHandler;
|
||||
@@ -319,18 +327,22 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
_isPublished = isPublished;
|
||||
}
|
||||
|
||||
public override void Execute() => Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _media, _isPublished);
|
||||
public override void Execute() =>
|
||||
Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _media, _isPublished);
|
||||
|
||||
public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMedia media, bool isPublished) =>
|
||||
public static void Execute(IBackgroundTaskQueue backgroundTaskQueue,
|
||||
ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMedia media, bool isPublished) =>
|
||||
// perform the ValueSet lookup on a background thread
|
||||
backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken =>
|
||||
{
|
||||
using ICoreScope scope = examineUmbracoIndexingHandler._scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
using ICoreScope scope =
|
||||
examineUmbracoIndexingHandler._scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
|
||||
var valueSet = examineUmbracoIndexingHandler._mediaValueSetBuilder.GetValueSets(media).ToList();
|
||||
|
||||
// This is only for content - so only index items for IUmbracoContentIndex (to exlude members)
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType<IUmbracoContentIndex>()
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes
|
||||
.OfType<IUmbracoContentIndex>()
|
||||
//filter the indexers
|
||||
.Where(x => isPublished || !x.PublishedValuesOnly)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
@@ -347,11 +359,12 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// </summary>
|
||||
private class DeferedReIndexForMember : DeferedAction
|
||||
{
|
||||
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
|
||||
private readonly ExamineUmbracoIndexingHandler _examineUmbracoIndexingHandler;
|
||||
private readonly IMember _member;
|
||||
private readonly IBackgroundTaskQueue _backgroundTaskQueue;
|
||||
|
||||
public DeferedReIndexForMember(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMember member)
|
||||
public DeferedReIndexForMember(IBackgroundTaskQueue backgroundTaskQueue,
|
||||
ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMember member)
|
||||
{
|
||||
_examineUmbracoIndexingHandler = examineUmbracoIndexingHandler;
|
||||
_member = member;
|
||||
@@ -360,16 +373,19 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
public override void Execute() => Execute(_backgroundTaskQueue, _examineUmbracoIndexingHandler, _member);
|
||||
|
||||
public static void Execute(IBackgroundTaskQueue backgroundTaskQueue, ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMember member) =>
|
||||
public static void Execute(IBackgroundTaskQueue backgroundTaskQueue,
|
||||
ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IMember member) =>
|
||||
// perform the ValueSet lookup on a background thread
|
||||
backgroundTaskQueue.QueueBackgroundWorkItem(cancellationToken =>
|
||||
{
|
||||
using ICoreScope scope = examineUmbracoIndexingHandler._scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
using ICoreScope scope =
|
||||
examineUmbracoIndexingHandler._scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
|
||||
var valueSet = examineUmbracoIndexingHandler._memberValueSetBuilder.GetValueSets(member).ToList();
|
||||
|
||||
// only process for IUmbracoMemberIndex (not content indexes)
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType<IUmbracoMemberIndex>()
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes
|
||||
.OfType<IUmbracoMemberIndex>()
|
||||
//filter the indexers
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
{
|
||||
@@ -387,14 +403,16 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
private readonly IReadOnlyCollection<int>? _ids;
|
||||
private readonly bool _keepIfUnpublished;
|
||||
|
||||
public DeferedDeleteIndex(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, int id, bool keepIfUnpublished)
|
||||
public DeferedDeleteIndex(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, int id,
|
||||
bool keepIfUnpublished)
|
||||
{
|
||||
_examineUmbracoIndexingHandler = examineUmbracoIndexingHandler;
|
||||
_id = id;
|
||||
_keepIfUnpublished = keepIfUnpublished;
|
||||
}
|
||||
|
||||
public DeferedDeleteIndex(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IReadOnlyCollection<int> ids, bool keepIfUnpublished)
|
||||
public DeferedDeleteIndex(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler,
|
||||
IReadOnlyCollection<int> ids, bool keepIfUnpublished)
|
||||
{
|
||||
_examineUmbracoIndexingHandler = examineUmbracoIndexingHandler;
|
||||
_ids = ids;
|
||||
@@ -413,9 +431,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
|
||||
public static void Execute(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, int id, bool keepIfUnpublished)
|
||||
public static void Execute(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, int id,
|
||||
bool keepIfUnpublished)
|
||||
{
|
||||
foreach (var index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes
|
||||
.OfType<IUmbracoIndex>()
|
||||
.Where(x => x.PublishedValuesOnly || !keepIfUnpublished)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
{
|
||||
@@ -423,9 +443,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
|
||||
public static void Execute(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler, IReadOnlyCollection<int> ids, bool keepIfUnpublished)
|
||||
public static void Execute(ExamineUmbracoIndexingHandler examineUmbracoIndexingHandler,
|
||||
IReadOnlyCollection<int> ids, bool keepIfUnpublished)
|
||||
{
|
||||
foreach (var index in examineUmbracoIndexingHandler._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
foreach (IUmbracoIndex index in examineUmbracoIndexingHandler._examineManager.Indexes
|
||||
.OfType<IUmbracoIndex>()
|
||||
.Where(x => x.PublishedValuesOnly || !keepIfUnpublished)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
{
|
||||
@@ -433,6 +455,6 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using Examine;
|
||||
using Examine.Search;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Used to return diagnostic data for any index
|
||||
/// </summary>
|
||||
public class GenericIndexDiagnostics : IIndexDiagnostics
|
||||
{
|
||||
private readonly IIndex _index;
|
||||
private static readonly string[] s_ignoreProperties = { "Description" };
|
||||
private static readonly string[] _ignoreProperties = { "Description" };
|
||||
|
||||
private readonly ISet<string> _idOnlyFieldSet = new HashSet<string> { "id" };
|
||||
private readonly IIndex _index;
|
||||
|
||||
public GenericIndexDiagnostics(IIndex index) => _index = index;
|
||||
|
||||
public int DocumentCount => -1; // unknown
|
||||
|
||||
public int FieldCount => -1; // unknown
|
||||
|
||||
public IReadOnlyDictionary<string, object?> Metadata
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = new Dictionary<string, object?>();
|
||||
|
||||
IOrderedEnumerable<PropertyInfo> props = TypeHelper
|
||||
.CachedDiscoverableProperties(_index.GetType(), mustWrite: false)
|
||||
.Where(x => _ignoreProperties.InvariantContains(x.Name) == false)
|
||||
.OrderBy(x => x.Name);
|
||||
|
||||
foreach (PropertyInfo p in props)
|
||||
{
|
||||
var val = p.GetValue(_index, null) ?? string.Empty;
|
||||
|
||||
result.Add(p.Name, val);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public Attempt<string?> IsHealthy()
|
||||
{
|
||||
if (!_index.IndexExists())
|
||||
{
|
||||
return Attempt.Fail("Does not exist");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = _index.Searcher.CreateQuery().ManagedQuery("test").SelectFields(_idOnlyFieldSet).Execute(new QueryOptions(0, 1));
|
||||
_index.Searcher.CreateQuery().ManagedQuery("test").SelectFields(_idOnlyFieldSet)
|
||||
.Execute(new QueryOptions(0, 1));
|
||||
return Attempt<string?>.Succeed(); // if we can search we'll assume it's healthy
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -45,26 +67,4 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
public long GetDocumentCount() => -1L;
|
||||
|
||||
public IEnumerable<string> GetFieldNames() => Enumerable.Empty<string>();
|
||||
|
||||
public IReadOnlyDictionary<string, object?> Metadata
|
||||
{
|
||||
get
|
||||
{
|
||||
var result = new Dictionary<string, object?>();
|
||||
|
||||
var props = TypeHelper.CachedDiscoverableProperties(_index.GetType(), mustWrite: false)
|
||||
.Where(x => s_ignoreProperties.InvariantContains(x.Name) == false)
|
||||
.OrderBy(x => x.Name);
|
||||
|
||||
foreach (var p in props)
|
||||
{
|
||||
var val = p.GetValue(_index, null) ?? string.Empty;
|
||||
|
||||
result.Add(p.Name, val);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Used to search the back office for Examine indexed entities (Documents, Media and Members)
|
||||
/// </summary>
|
||||
public interface IBackOfficeExamineSearcher
|
||||
{
|
||||
IEnumerable<ISearchResult> Search(string query,
|
||||
IEnumerable<ISearchResult> Search(
|
||||
string query,
|
||||
UmbracoEntityTypes entityType,
|
||||
int pageSize,
|
||||
long pageIndex, out long totalFound, string? searchFrom = null, bool ignoreUserStartNodes = false);
|
||||
}
|
||||
long pageIndex,
|
||||
out long totalFound,
|
||||
string? searchFrom = null,
|
||||
bool ignoreUserStartNodes = false);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Marker interface for a <see cref="T:Examine.ValueSet" /> builder for supporting unpublished content
|
||||
@@ -9,4 +9,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
public interface IContentValueSetBuilder : IValueSetBuilder<IContent>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Examine;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
/// <summary>
|
||||
/// An extended <see cref="IValueSetValidator" /> for content indexes
|
||||
/// </summary>
|
||||
@@ -25,7 +25,8 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
int? ParentId { get; }
|
||||
|
||||
bool ValidatePath(string path, string category);
|
||||
|
||||
bool ValidateRecycleBin(string path, string category);
|
||||
|
||||
bool ValidateProtectedContent(string path, string category);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Exposes diagnostic information about an index
|
||||
/// </summary>
|
||||
public interface IIndexDiagnostics : IIndexStats
|
||||
{
|
||||
/// <summary>
|
||||
/// If the index can be open/read
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A successful attempt if it is healthy, else a failed attempt with a message if unhealthy
|
||||
/// </returns>
|
||||
Attempt<string?> IsHealthy();
|
||||
|
||||
/// <summary>
|
||||
/// A key/value collection of diagnostic properties for the index
|
||||
/// </summary>
|
||||
@@ -26,5 +15,12 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// Used to display in the UI
|
||||
/// </remarks>
|
||||
IReadOnlyDictionary<string, object?> Metadata { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the index can be open/read
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A successful attempt if it is healthy, else a failed attempt with a message if unhealthy
|
||||
/// </returns>
|
||||
Attempt<string?> IsHealthy();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Examine;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Creates <see cref="IIndexDiagnostics" /> for an index if it doesn't implement <see cref="IIndexDiagnostics" />
|
||||
@@ -10,4 +9,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
IIndexDiagnostics Create(IIndex index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Examine;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
public interface IIndexPopulator
|
||||
{
|
||||
/// <summary>
|
||||
@@ -17,4 +17,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// <param name="indexes"></param>
|
||||
void Populate(params IIndex[] indexes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Examine;
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
public interface IIndexRebuilder
|
||||
{
|
||||
bool CanRebuild(string indexName);
|
||||
|
||||
void RebuildIndex(string indexName, TimeSpan? delay = null, bool useBackgroundThread = true);
|
||||
|
||||
void RebuildIndexes(bool onlyEmptyIndexes, TimeSpan? delay = null, bool useBackgroundThread = true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using Examine;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for a <see cref="ValueSet" /> builder for only published content
|
||||
/// </summary>
|
||||
public interface IPublishedContentValueSetBuilder : IValueSetBuilder<IContent>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
using Examine;
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for indexes of Umbraco content
|
||||
/// </summary>
|
||||
public interface IUmbracoContentIndex : IUmbracoIndex
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// A Marker interface for defining an Umbraco indexer
|
||||
/// </summary>
|
||||
@@ -23,4 +22,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// </remarks>
|
||||
bool PublishedValuesOnly { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public interface IUmbracoIndexConfig
|
||||
{
|
||||
IContentValueSetValidator GetContentValueSetValidator();
|
||||
IContentValueSetValidator GetPublishedContentValueSetValidator();
|
||||
IValueSetValidator GetMemberValueSetValidator();
|
||||
|
||||
}
|
||||
IContentValueSetValidator GetPublishedContentValueSetValidator();
|
||||
|
||||
IValueSetValidator GetMemberValueSetValidator();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using Examine;
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
public interface IUmbracoMemberIndex : IUmbracoIndex
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to propagate hardcoded internal Field lists
|
||||
/// </summary>
|
||||
@@ -28,8 +26,10 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
IEnumerable<string> GetBackOfficeDocumentFields();
|
||||
|
||||
ISet<string> GetBackOfficeFieldsToLoad();
|
||||
|
||||
ISet<string> GetBackOfficeMembersFieldsToLoad();
|
||||
|
||||
ISet<string> GetBackOfficeDocumentFieldsToLoad();
|
||||
|
||||
ISet<string> GetBackOfficeMediaFieldsToLoad();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a collection of <see cref="ValueSet" /> to be indexed based on a collection of <see cref="T" />
|
||||
/// </summary>
|
||||
@@ -16,5 +15,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// <returns></returns>
|
||||
IEnumerable<ValueSet> GetValueSets(params T[] content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation of <see cref="IIndexDiagnosticsFactory"/> which returns <see cref="GenericIndexDiagnostics"/> for indexes that don't have an implementation
|
||||
/// Default implementation of <see cref="IIndexDiagnosticsFactory" /> which returns
|
||||
/// <see cref="GenericIndexDiagnostics" /> for indexes that don't have an implementation
|
||||
/// </summary>
|
||||
public class IndexDiagnosticsFactory : IIndexDiagnosticsFactory
|
||||
{
|
||||
@@ -17,4 +18,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
return indexDiag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Collections;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IIndexPopulator" /> that is automatically associated to any index of type <see cref="TIndex" />
|
||||
/// </summary>
|
||||
/// <typeparam name="TIndex"></typeparam>
|
||||
public abstract class IndexPopulator<TIndex> : IndexPopulator where TIndex : IIndex
|
||||
public abstract class IndexPopulator<TIndex> : IndexPopulator
|
||||
where TIndex : IIndex
|
||||
{
|
||||
public override bool IsRegistered(IIndex index)
|
||||
{
|
||||
if (base.IsRegistered(index))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(index is TIndex casted))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsRegistered(casted);
|
||||
}
|
||||
@@ -27,27 +30,17 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
public abstract class IndexPopulator : IIndexPopulator
|
||||
{
|
||||
private readonly ConcurrentHashSet<string> _registeredIndexes = new ConcurrentHashSet<string>();
|
||||
private readonly ConcurrentHashSet<string> _registeredIndexes = new();
|
||||
|
||||
public virtual bool IsRegistered(IIndex index)
|
||||
{
|
||||
return _registeredIndexes.Contains(index.Name);
|
||||
}
|
||||
public virtual bool IsRegistered(IIndex index) => _registeredIndexes.Contains(index.Name);
|
||||
|
||||
public void Populate(params IIndex[] indexes) => PopulateIndexes(indexes.Where(IsRegistered).ToList());
|
||||
|
||||
/// <summary>
|
||||
/// Registers an index for this populator
|
||||
/// </summary>
|
||||
/// <param name="indexName"></param>
|
||||
public void RegisterIndex(string indexName)
|
||||
{
|
||||
_registeredIndexes.Add(indexName);
|
||||
}
|
||||
|
||||
public void Populate(params IIndex[] indexes)
|
||||
{
|
||||
PopulateIndexes(indexes.Where(IsRegistered).ToList());
|
||||
}
|
||||
public void RegisterIndex(string indexName) => _registeredIndexes.Add(indexName);
|
||||
|
||||
protected abstract void PopulateIndexes(IReadOnlyList<IIndex> indexes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// The index types stored in the Lucene Index
|
||||
/// </summary>
|
||||
public static class IndexTypes
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The content index type
|
||||
/// </summary>
|
||||
@@ -30,4 +29,3 @@
|
||||
/// </remarks>
|
||||
public const string Member = "member";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Performs the data lookups required to rebuild a media index
|
||||
/// </summary>
|
||||
public class MediaIndexPopulator : IndexPopulator<IUmbracoContentIndex>
|
||||
{
|
||||
private readonly ILogger<MediaIndexPopulator> _logger;
|
||||
private readonly int? _parentId;
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IValueSetBuilder<IMedia> _mediaValueSetBuilder;
|
||||
private readonly int? _parentId;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor to lookup all content data
|
||||
/// </summary>
|
||||
/// <param name="mediaService"></param>
|
||||
/// <param name="mediaValueSetBuilder"></param>
|
||||
public MediaIndexPopulator(ILogger<MediaIndexPopulator> logger, IMediaService mediaService, IValueSetBuilder<IMedia> mediaValueSetBuilder)
|
||||
: this(logger, null, mediaService, mediaValueSetBuilder)
|
||||
{
|
||||
@@ -30,9 +26,6 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// <summary>
|
||||
/// Optional constructor allowing specifying custom query parameters
|
||||
/// </summary>
|
||||
/// <param name="parentId"></param>
|
||||
/// <param name="mediaService"></param>
|
||||
/// <param name="mediaValueSetBuilder"></param>
|
||||
public MediaIndexPopulator(ILogger<MediaIndexPopulator> logger, int? parentId, IMediaService mediaService, IValueSetBuilder<IMedia> mediaValueSetBuilder)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -45,7 +38,8 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
if (indexes.Count == 0)
|
||||
{
|
||||
_logger.LogDebug($"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator.");
|
||||
_logger.LogDebug(
|
||||
$"{nameof(PopulateIndexes)} called with no indexes to populate. Typically means no index is registered with this populator.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -63,18 +57,19 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
|
||||
do
|
||||
{
|
||||
media = _mediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out var total).ToArray();
|
||||
media = _mediaService.GetPagedDescendants(mediaParentId, pageIndex, pageSize, out _).ToArray();
|
||||
|
||||
if (media.Length > 0)
|
||||
{
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
foreach (var index in indexes)
|
||||
foreach (IIndex index in indexes)
|
||||
{
|
||||
index.IndexItems(_mediaValueSetBuilder.GetValueSets(media));
|
||||
}
|
||||
}
|
||||
|
||||
pageIndex++;
|
||||
} while (media.Length == pageSize);
|
||||
}
|
||||
|
||||
while (media.Length == pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public class MediaValueSetBuilder : BaseValueSetBuilder<IMedia>
|
||||
{
|
||||
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
|
||||
private readonly MediaUrlGeneratorCollection _mediaUrlGenerators;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
private readonly MediaUrlGeneratorCollection _mediaUrlGenerators;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public MediaValueSetBuilder(
|
||||
PropertyEditorCollection propertyEditors,
|
||||
@@ -46,7 +38,6 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
foreach (IMedia m in media)
|
||||
{
|
||||
|
||||
var urlValue = m.GetUrlSegment(_shortStringHelper, _urlSegmentProviders);
|
||||
|
||||
IEnumerable<string?> mediaFiles = m.GetUrls(_contentSettings, _mediaUrlGenerators)
|
||||
@@ -66,13 +57,13 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{ "updateDate", new object[] { m.UpdateDate } },
|
||||
{ UmbracoExamineFieldNames.NodeNameFieldName, m.Name?.Yield() ?? Enumerable.Empty<string>() },
|
||||
{ "urlName", urlValue?.Yield() ?? Enumerable.Empty<string>() },
|
||||
{"path", m.Path?.Yield() ?? Enumerable.Empty<string>()},
|
||||
{ "path", m.Path.Yield() },
|
||||
{ "nodeType", m.ContentType.Id.ToString().Yield() },
|
||||
{ "creatorName", (m.GetCreatorProfile(_userService)?.Name ?? "??").Yield() },
|
||||
{UmbracoExamineFieldNames.UmbracoFileFieldName, mediaFiles}
|
||||
{ UmbracoExamineFieldNames.UmbracoFileFieldName, mediaFiles },
|
||||
};
|
||||
|
||||
foreach (var property in m.Properties)
|
||||
foreach (IProperty property in m.Properties)
|
||||
{
|
||||
AddPropertyValue(property, null, null, values);
|
||||
}
|
||||
@@ -83,5 +74,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public class MemberIndexPopulator : IndexPopulator<IUmbracoMemberIndex>
|
||||
{
|
||||
private readonly IMemberService _memberService;
|
||||
@@ -16,9 +14,13 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
_memberService = memberService;
|
||||
_valueSetBuilder = valueSetBuilder;
|
||||
}
|
||||
|
||||
protected override void PopulateIndexes(IReadOnlyList<IIndex> indexes)
|
||||
{
|
||||
if (indexes.Count == 0) return;
|
||||
if (indexes.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int pageSize = 1000;
|
||||
var pageIndex = 0;
|
||||
@@ -33,12 +35,14 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
if (members.Length > 0)
|
||||
{
|
||||
// ReSharper disable once PossibleMultipleEnumeration
|
||||
foreach (var index in indexes)
|
||||
foreach (IIndex index in indexes)
|
||||
{
|
||||
index.IndexItems(_valueSetBuilder.GetValueSets(members));
|
||||
}
|
||||
}
|
||||
|
||||
pageIndex++;
|
||||
} while (members.Length == pageSize);
|
||||
}
|
||||
}
|
||||
while (members.Length == pageSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public class MemberValueSetBuilder : BaseValueSetBuilder<IMember>
|
||||
{
|
||||
@@ -18,7 +15,7 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<ValueSet> GetValueSets(params IMember[] members)
|
||||
{
|
||||
foreach (var m in members)
|
||||
foreach (IMember m in members)
|
||||
{
|
||||
var values = new Dictionary<string, IEnumerable<object?>>
|
||||
{
|
||||
@@ -32,13 +29,13 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{ "createDate", new object[] { m.CreateDate } },
|
||||
{ "updateDate", new object[] { m.UpdateDate } },
|
||||
{ UmbracoExamineFieldNames.NodeNameFieldName, m.Name?.Yield() ?? Enumerable.Empty<string>() },
|
||||
{"path", m.Path?.Yield() ?? Enumerable.Empty<string>()},
|
||||
{ "path", m.Path.Yield() },
|
||||
{ "nodeType", m.ContentType.Id.ToString().Yield() },
|
||||
{"loginName", m.Username?.Yield() ?? Enumerable.Empty<string>()},
|
||||
{"email", m.Email?.Yield() ?? Enumerable.Empty<string>()},
|
||||
{ "loginName", m.Username.Yield() },
|
||||
{ "email", m.Email.Yield() },
|
||||
};
|
||||
|
||||
foreach (var property in m.Properties)
|
||||
foreach (IProperty property in m.Properties)
|
||||
{
|
||||
AddPropertyValue(property, null, null, values);
|
||||
}
|
||||
@@ -49,5 +46,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
public class MemberValueSetValidator : ValueSetValidator
|
||||
{
|
||||
public MemberValueSetValidator() : base(null, null, DefaultMemberIndexFields, null)
|
||||
/// <summary>
|
||||
/// By default these are the member fields we index
|
||||
/// </summary>
|
||||
public static readonly string[] DefaultMemberIndexFields =
|
||||
{
|
||||
"id", UmbracoExamineFieldNames.NodeNameFieldName, "updateDate", "loginName", "email",
|
||||
UmbracoExamineFieldNames.NodeKeyFieldName,
|
||||
};
|
||||
|
||||
private static readonly IEnumerable<string> _validCategories = new[] { IndexTypes.Member };
|
||||
|
||||
public MemberValueSetValidator()
|
||||
: base(null, null, DefaultMemberIndexFields, null)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -18,13 +28,5 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// By default these are the member fields we index
|
||||
/// </summary>
|
||||
public static readonly string[] DefaultMemberIndexFields = { "id", UmbracoExamineFieldNames.NodeNameFieldName, "updateDate", "loginName", "email", UmbracoExamineFieldNames.NodeKeyFieldName };
|
||||
|
||||
private static readonly IEnumerable<string> ValidCategories = new[] { IndexTypes.Member };
|
||||
protected override IEnumerable<string> ValidIndexCategories => ValidCategories;
|
||||
|
||||
}
|
||||
protected override IEnumerable<string> ValidIndexCategories => _validCategories;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public class NoopBackOfficeExamineSearcher : IBackOfficeExamineSearcher
|
||||
{
|
||||
public IEnumerable<ISearchResult> Search(string query, UmbracoEntityTypes entityType, int pageSize, long pageIndex, out long totalFound,
|
||||
string? searchFrom = null, bool ignoreUserStartNodes = false)
|
||||
public IEnumerable<ISearchResult> Search(
|
||||
string query,
|
||||
UmbracoEntityTypes entityType,
|
||||
int pageSize,
|
||||
long pageIndex,
|
||||
out long totalFound,
|
||||
string? searchFrom = null,
|
||||
bool ignoreUserStartNodes = false)
|
||||
{
|
||||
totalFound = 0;
|
||||
return Enumerable.Empty<ISearchResult>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,25 @@ using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Performs the data lookups required to rebuild a content index containing only published content
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The published (external) index will still rebuild just fine using the default <see cref="ContentIndexPopulator"/> which is what
|
||||
/// is used when rebuilding all indexes, but this will be used when the single index is rebuilt and will go a little bit faster
|
||||
/// The published (external) index will still rebuild just fine using the default <see cref="ContentIndexPopulator" />
|
||||
/// which is what is used when rebuilding all indexes,
|
||||
/// but this will be used when the single index is rebuilt and will go a little bit faster since the data query is more specific.
|
||||
/// since the data query is more specific.
|
||||
/// </remarks>
|
||||
public class PublishedContentIndexPopulator : ContentIndexPopulator
|
||||
{
|
||||
public PublishedContentIndexPopulator(ILogger<PublishedContentIndexPopulator> logger, IContentService contentService, IUmbracoDatabaseFactory umbracoDatabaseFactory, IPublishedContentValueSetBuilder contentValueSetBuilder) :
|
||||
base(logger, true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder)
|
||||
public PublishedContentIndexPopulator(
|
||||
ILogger<PublishedContentIndexPopulator> logger,
|
||||
IContentService contentService,
|
||||
IUmbracoDatabaseFactory umbracoDatabaseFactory,
|
||||
IPublishedContentValueSetBuilder contentValueSetBuilder)
|
||||
: base(logger, true, null, contentService, umbracoDatabaseFactory, contentValueSetBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Notifications;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Handles how the indexes are rebuilt on startup
|
||||
/// </summary>
|
||||
@@ -17,10 +15,6 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// </remarks>
|
||||
public sealed class RebuildOnStartupHandler : INotificationHandler<UmbracoRequestBeginNotification>
|
||||
{
|
||||
private readonly ISyncBootStateAccessor _syncBootStateAccessor;
|
||||
private readonly ExamineIndexRebuilder _backgroundIndexRebuilder;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
|
||||
// These must be static because notification handlers are transient.
|
||||
// this does unfortunatley mean that one RebuildOnStartupHandler instance
|
||||
// will be created for each front-end request even though we only use the first one.
|
||||
@@ -30,6 +24,9 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
private static bool _isReady;
|
||||
private static bool _isReadSet;
|
||||
private static object? _isReadyLock;
|
||||
private readonly ExamineIndexRebuilder _backgroundIndexRebuilder;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly ISyncBootStateAccessor _syncBootStateAccessor;
|
||||
|
||||
public RebuildOnStartupHandler(
|
||||
ISyncBootStateAccessor syncBootStateAccessor,
|
||||
@@ -60,8 +57,8 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
SyncBootState bootState = _syncBootStateAccessor.GetSyncBootState();
|
||||
|
||||
_backgroundIndexRebuilder.RebuildIndexes(
|
||||
// if it's not a cold boot, only rebuild empty ones
|
||||
_backgroundIndexRebuilder.RebuildIndexes(
|
||||
bootState != SyncBootState.ColdBoot,
|
||||
TimeSpan.FromMinutes(1));
|
||||
|
||||
@@ -69,4 +66,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Examine;
|
||||
using Examine.Search;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
public static class UmbracoExamineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -15,7 +13,9 @@ namespace Umbraco.Extensions
|
||||
/// <remarks>
|
||||
/// myFieldName_en-us will match the "en-us"
|
||||
/// </remarks>
|
||||
internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^(?<FieldName>[_\\w]+)_(?<CultureName>[a-z]{2,3}(-[a-z0-9]{2,4})?)$", RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
internal static readonly Regex _cultureIsoCodeFieldNameMatchExpression = new(
|
||||
"^(?<FieldName>[_\\w]+)_(?<CultureName>[a-z]{2,3}(-[a-z0-9]{2,4})?)$",
|
||||
RegexOptions.Compiled | RegexOptions.ExplicitCapture);
|
||||
|
||||
// TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Extensions
|
||||
var results = new List<string>();
|
||||
foreach (var field in allFields)
|
||||
{
|
||||
var match = CultureIsoCodeFieldNameMatchExpression.Match(field);
|
||||
Match match = _cultureIsoCodeFieldNameMatchExpression.Match(field);
|
||||
if (match.Success && culture.InvariantEquals(match.Groups["CultureName"].Value))
|
||||
{
|
||||
results.Add(field);
|
||||
@@ -54,7 +54,7 @@ namespace Umbraco.Extensions
|
||||
|
||||
foreach (var field in allFields)
|
||||
{
|
||||
var match = CultureIsoCodeFieldNameMatchExpression.Match(field);
|
||||
Match match = _cultureIsoCodeFieldNameMatchExpression.Match(field);
|
||||
if (match.Success && culture.InvariantEquals(match.Groups["CultureName"].Value))
|
||||
{
|
||||
yield return field; // matches this culture field
|
||||
@@ -68,7 +68,7 @@ namespace Umbraco.Extensions
|
||||
|
||||
public static IBooleanOperation Id(this IQuery query, int id)
|
||||
{
|
||||
var fieldQuery = query.Id(id.ToInvariantString());
|
||||
IBooleanOperation? fieldQuery = query.Id(id.ToInvariantString());
|
||||
return fieldQuery;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Umbraco.Extensions
|
||||
/// <returns></returns>
|
||||
public static IBooleanOperation ParentId(this IQuery query, int id)
|
||||
{
|
||||
var fieldQuery = query.Field("parentID", id);
|
||||
IBooleanOperation? fieldQuery = query.Field("parentID", id);
|
||||
return fieldQuery;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,9 @@ namespace Umbraco.Extensions
|
||||
/// <returns></returns>
|
||||
public static IBooleanOperation NodeName(this IQuery query, string nodeName)
|
||||
{
|
||||
var fieldQuery = query.Field(UmbracoExamineFieldNames.NodeNameFieldName, (IExamineValue)new ExamineValue(Examineness.Explicit, nodeName));
|
||||
IBooleanOperation? fieldQuery = query.Field(
|
||||
UmbracoExamineFieldNames.NodeNameFieldName,
|
||||
(IExamineValue)new ExamineValue(Examineness.Explicit, nodeName));
|
||||
return fieldQuery;
|
||||
}
|
||||
|
||||
@@ -104,7 +106,7 @@ namespace Umbraco.Extensions
|
||||
/// <returns></returns>
|
||||
public static IBooleanOperation NodeName(this IQuery query, IExamineValue nodeName)
|
||||
{
|
||||
var fieldQuery = query.Field(UmbracoExamineFieldNames.NodeNameFieldName, nodeName);
|
||||
IBooleanOperation? fieldQuery = query.Field(UmbracoExamineFieldNames.NodeNameFieldName, nodeName);
|
||||
return fieldQuery;
|
||||
}
|
||||
|
||||
@@ -116,7 +118,9 @@ namespace Umbraco.Extensions
|
||||
/// <returns></returns>
|
||||
public static IBooleanOperation NodeTypeAlias(this IQuery query, string nodeTypeAlias)
|
||||
{
|
||||
var fieldQuery = query.Field(ExamineFieldNames.ItemTypeFieldName, (IExamineValue)new ExamineValue(Examineness.Explicit, nodeTypeAlias));
|
||||
IBooleanOperation? fieldQuery = query.Field(
|
||||
ExamineFieldNames.ItemTypeFieldName,
|
||||
(IExamineValue)new ExamineValue(Examineness.Explicit, nodeTypeAlias));
|
||||
return fieldQuery;
|
||||
}
|
||||
|
||||
@@ -128,9 +132,7 @@ namespace Umbraco.Extensions
|
||||
/// <returns></returns>
|
||||
public static IBooleanOperation NodeTypeAlias(this IQuery query, IExamineValue nodeTypeAlias)
|
||||
{
|
||||
var fieldQuery = query.Field(ExamineFieldNames.ItemTypeFieldName, nodeTypeAlias);
|
||||
IBooleanOperation? fieldQuery = query.Field(ExamineFieldNames.ItemTypeFieldName, nodeTypeAlias);
|
||||
return fieldQuery;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
using Examine;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
public static class UmbracoExamineFieldNames
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to store the path of a content object
|
||||
/// </summary>
|
||||
public const string IndexPathFieldName = ExamineFieldNames.SpecialFieldPrefix + "Path";
|
||||
|
||||
public const string NodeKeyFieldName = ExamineFieldNames.SpecialFieldPrefix + "Key";
|
||||
public const string UmbracoFileFieldName = "umbracoFileSrc";
|
||||
public const string IconFieldName = ExamineFieldNames.SpecialFieldPrefix + "Icon";
|
||||
@@ -25,4 +26,3 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
public const string CategoryFieldName = "__IndexType";
|
||||
public const string ItemTypeFieldName = "__NodeTypeAlias";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
using Examine;
|
||||
using System.Text.RegularExpressions;
|
||||
using Examine;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Custom <see cref="FieldDefinitionCollection" /> allowing dynamic creation of <see cref="FieldDefinition" />
|
||||
/// </summary>
|
||||
public class UmbracoFieldDefinitionCollection : FieldDefinitionCollection
|
||||
{
|
||||
|
||||
public UmbracoFieldDefinitionCollection()
|
||||
: base(UmbracoIndexFieldDefinitions)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A type that defines the type of index for each Umbraco field (non user defined fields)
|
||||
/// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene
|
||||
@@ -21,31 +16,24 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// </summary>
|
||||
public static readonly FieldDefinition[] UmbracoIndexFieldDefinitions =
|
||||
{
|
||||
new FieldDefinition("parentID", FieldDefinitionTypes.Integer),
|
||||
new FieldDefinition("level", FieldDefinitionTypes.Integer),
|
||||
new FieldDefinition("writerID", FieldDefinitionTypes.Integer),
|
||||
new FieldDefinition("creatorID", FieldDefinitionTypes.Integer),
|
||||
new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer),
|
||||
new FieldDefinition("template", FieldDefinitionTypes.Integer),
|
||||
|
||||
new FieldDefinition("createDate", FieldDefinitionTypes.DateTime),
|
||||
new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime),
|
||||
|
||||
new FieldDefinition(UmbracoExamineFieldNames.NodeKeyFieldName, FieldDefinitionTypes.InvariantCultureIgnoreCase),
|
||||
new FieldDefinition("version", FieldDefinitionTypes.Raw),
|
||||
new FieldDefinition("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase),
|
||||
new FieldDefinition("template", FieldDefinitionTypes.Raw),
|
||||
new FieldDefinition("urlName", FieldDefinitionTypes.InvariantCultureIgnoreCase),
|
||||
new FieldDefinition("path", FieldDefinitionTypes.Raw),
|
||||
|
||||
new FieldDefinition("email", FieldDefinitionTypes.EmailAddress),
|
||||
|
||||
new FieldDefinition(UmbracoExamineFieldNames.PublishedFieldName, FieldDefinitionTypes.Raw),
|
||||
new FieldDefinition(UmbracoExamineFieldNames.IndexPathFieldName, FieldDefinitionTypes.Raw),
|
||||
new FieldDefinition(UmbracoExamineFieldNames.IconFieldName, FieldDefinitionTypes.Raw),
|
||||
new FieldDefinition(UmbracoExamineFieldNames.VariesByCultureFieldName, FieldDefinitionTypes.Raw),
|
||||
new("parentID", FieldDefinitionTypes.Integer), new("level", FieldDefinitionTypes.Integer),
|
||||
new("writerID", FieldDefinitionTypes.Integer), new("creatorID", FieldDefinitionTypes.Integer),
|
||||
new("sortOrder", FieldDefinitionTypes.Integer), new("template", FieldDefinitionTypes.Integer),
|
||||
new("createDate", FieldDefinitionTypes.DateTime), new("updateDate", FieldDefinitionTypes.DateTime),
|
||||
new(UmbracoExamineFieldNames.NodeKeyFieldName, FieldDefinitionTypes.InvariantCultureIgnoreCase),
|
||||
new("version", FieldDefinitionTypes.Raw), new("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase),
|
||||
new("template", FieldDefinitionTypes.Raw), new("urlName", FieldDefinitionTypes.InvariantCultureIgnoreCase),
|
||||
new("path", FieldDefinitionTypes.Raw), new("email", FieldDefinitionTypes.EmailAddress),
|
||||
new(UmbracoExamineFieldNames.PublishedFieldName, FieldDefinitionTypes.Raw),
|
||||
new(UmbracoExamineFieldNames.IndexPathFieldName, FieldDefinitionTypes.Raw),
|
||||
new(UmbracoExamineFieldNames.IconFieldName, FieldDefinitionTypes.Raw),
|
||||
new(UmbracoExamineFieldNames.VariesByCultureFieldName, FieldDefinitionTypes.Raw),
|
||||
};
|
||||
|
||||
public UmbracoFieldDefinitionCollection()
|
||||
: base(UmbracoIndexFieldDefinitions)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to dynamically add field definitions for culture variations
|
||||
@@ -56,39 +44,46 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// <remarks>
|
||||
/// We need to do this so that we don't have to maintain a huge static list of all field names and their definitions
|
||||
/// otherwise we'd have to dynamically add/remove definitions anytime languages are added/removed, etc...
|
||||
/// For example, we have things like `nodeName` and `__Published` which are also used for culture fields like `nodeName_en-us`
|
||||
/// and we don't want to have a full static list of all of these definitions when we can just define the one definition and then
|
||||
/// For example, we have things like `nodeName` and `__Published` which are also used for culture fields like
|
||||
/// `nodeName_en-us`
|
||||
/// and we don't want to have a full static list of all of these definitions when we can just define the one definition
|
||||
/// and then
|
||||
/// dynamically apply that to culture specific fields.
|
||||
///
|
||||
/// There is a caveat to this however, when a field definition is found for a non-culture field we will create and store a new field
|
||||
/// definition for that culture so that the next time it needs to be looked up and used we are not allocating more objects. This does mean
|
||||
/// however that if a language is deleted, the field definitions for that language will still exist in memory. This isn't going to cause any
|
||||
/// There is a caveat to this however, when a field definition is found for a non-culture field we will create and
|
||||
/// store a new field
|
||||
/// definition for that culture so that the next time it needs to be looked up and used we are not allocating more
|
||||
/// objects. This does mean
|
||||
/// however that if a language is deleted, the field definitions for that language will still exist in memory. This
|
||||
/// isn't going to cause any
|
||||
/// problems and the mem will be cleared on next site restart but it's worth pointing out.
|
||||
/// </remarks>
|
||||
public override bool TryGetValue(string fieldName, out FieldDefinition fieldDefinition)
|
||||
{
|
||||
if (base.TryGetValue(fieldName, out fieldDefinition))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// before we use regex to match do some faster simple matching since this is going to execute quite a lot
|
||||
if (!fieldName.Contains("_"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var match = UmbracoExamineExtensions.CultureIsoCodeFieldNameMatchExpression.Match(fieldName);
|
||||
Match match = UmbracoExamineExtensions._cultureIsoCodeFieldNameMatchExpression.Match(fieldName);
|
||||
if (match.Success)
|
||||
{
|
||||
var nonCultureFieldName = match.Groups["FieldName"].Value;
|
||||
|
||||
// check if there's a definition for this and if so return the field definition for the culture field based on the non-culture field
|
||||
if (base.TryGetValue(nonCultureFieldName, out var existingFieldDefinition))
|
||||
if (base.TryGetValue(nonCultureFieldName, out FieldDefinition existingFieldDefinition))
|
||||
{
|
||||
// now add a new field def
|
||||
fieldDefinition = GetOrAdd(fieldName, s => new FieldDefinition(s, existingFieldDefinition.Type));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@ using Examine;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
public class UmbracoIndexConfig : IUmbracoIndexConfig
|
||||
{
|
||||
|
||||
public UmbracoIndexConfig(IPublicAccessService publicAccessService, IScopeProvider scopeProvider)
|
||||
{
|
||||
ScopeProvider = scopeProvider;
|
||||
@@ -14,24 +13,18 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
}
|
||||
|
||||
protected IPublicAccessService PublicAccessService { get; }
|
||||
protected IScopeProvider ScopeProvider { get; }
|
||||
public IContentValueSetValidator GetContentValueSetValidator()
|
||||
{
|
||||
return new ContentValueSetValidator(false, true, PublicAccessService, ScopeProvider);
|
||||
}
|
||||
|
||||
public IContentValueSetValidator GetPublishedContentValueSetValidator()
|
||||
{
|
||||
return new ContentValueSetValidator(true, false, PublicAccessService, ScopeProvider);
|
||||
}
|
||||
protected IScopeProvider ScopeProvider { get; }
|
||||
|
||||
public IContentValueSetValidator GetContentValueSetValidator() =>
|
||||
new ContentValueSetValidator(false, true, PublicAccessService, ScopeProvider);
|
||||
|
||||
public IContentValueSetValidator GetPublishedContentValueSetValidator() =>
|
||||
new ContentValueSetValidator(true, false, PublicAccessService, ScopeProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IValueSetValidator" /> for the member indexer
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IValueSetValidator GetMemberValueSetValidator()
|
||||
{
|
||||
return new MemberValueSetValidator();
|
||||
}
|
||||
}
|
||||
public IValueSetValidator GetMemberValueSetValidator() => new MemberValueSetValidator();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Examine
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Examine;
|
||||
|
||||
/// <summary>
|
||||
/// Performing basic validation of a value set
|
||||
/// </summary>
|
||||
@@ -23,8 +21,6 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
ValidIndexCategories = null;
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<string>? ValidIndexCategories { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional inclusion list of content types to index
|
||||
/// </summary>
|
||||
@@ -57,22 +53,31 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
/// </remarks>
|
||||
public IEnumerable<string>? ExcludeFields { get; }
|
||||
|
||||
protected virtual IEnumerable<string>? ValidIndexCategories { get; }
|
||||
|
||||
public virtual ValueSetValidationResult Validate(ValueSet valueSet)
|
||||
{
|
||||
if (ValidIndexCategories != null && !ValidIndexCategories.InvariantContains(valueSet.Category))
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
// check if this document is of a correct type of node type alias
|
||||
if (IncludeItemTypes != null && !IncludeItemTypes.InvariantContains(valueSet.ItemType))
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
// if this node type is part of our exclusion list
|
||||
if (ExcludeItemTypes != null && ExcludeItemTypes.InvariantContains(valueSet.ItemType))
|
||||
{
|
||||
return new ValueSetValidationResult(ValueSetValidationStatus.Failed, valueSet);
|
||||
}
|
||||
|
||||
var isFiltered = false;
|
||||
|
||||
var filteredValues = valueSet.Values.ToDictionary(x => x.Key, x => x.Value.ToList());
|
||||
|
||||
// filter based on the fields provided (if any)
|
||||
if (IncludeFields != null || ExcludeFields != null)
|
||||
{
|
||||
@@ -89,12 +94,11 @@ namespace Umbraco.Cms.Infrastructure.Examine
|
||||
filteredValues.Remove(key); // remove any value with a key that matches the exclusion list
|
||||
isFiltered = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
var filteredValueSet = new ValueSet(valueSet.Id, valueSet.Category, valueSet.ItemType, filteredValues.ToDictionary(x => x.Key, x => (IEnumerable<object>)x.Value));
|
||||
return new ValueSetValidationResult(isFiltered ? ValueSetValidationStatus.Filtered : ValueSetValidationStatus.Valid, filteredValueSet);
|
||||
}
|
||||
return new ValueSetValidationResult(
|
||||
isFiltered ? ValueSetValidationStatus.Filtered : ValueSetValidationStatus.Valid, filteredValueSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MimeKit;
|
||||
using MimeKit.Text;
|
||||
using Umbraco.Cms.Core.Models.Email;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Extensions
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Extensions;
|
||||
|
||||
internal static class EmailMessageExtensions
|
||||
{
|
||||
public static MimeMessage ToMimeMessage(this EmailMessage mailMessage, string configuredFromAddress)
|
||||
@@ -14,16 +12,13 @@ namespace Umbraco.Cms.Infrastructure.Extensions
|
||||
|
||||
if (!InternetAddress.TryParse(fromEmail, out InternetAddress fromAddress))
|
||||
{
|
||||
throw new ArgumentException($"Email could not be sent. Could not parse from address {fromEmail} as a valid email address.");
|
||||
throw new ArgumentException(
|
||||
$"Email could not be sent. Could not parse from address {fromEmail} as a valid email address.");
|
||||
}
|
||||
|
||||
var messageToSend = new MimeMessage
|
||||
{
|
||||
From = { fromAddress },
|
||||
Subject = mailMessage.Subject,
|
||||
};
|
||||
var messageToSend = new MimeMessage { From = { fromAddress }, Subject = mailMessage.Subject };
|
||||
|
||||
AddAddresses(messageToSend, mailMessage.To, x => x.To, throwIfNoneValid: true);
|
||||
AddAddresses(messageToSend, mailMessage.To, x => x.To, true);
|
||||
AddAddresses(messageToSend, mailMessage.Cc, x => x.Cc);
|
||||
AddAddresses(messageToSend, mailMessage.Bcc, x => x.Bcc);
|
||||
AddAddresses(messageToSend, mailMessage.ReplyTo, x => x.ReplyTo);
|
||||
@@ -49,12 +44,33 @@ namespace Umbraco.Cms.Infrastructure.Extensions
|
||||
}
|
||||
else
|
||||
{
|
||||
messageToSend.Body = new TextPart(mailMessage.IsBodyHtml ? TextFormat.Html : TextFormat.Plain) { Text = mailMessage.Body };
|
||||
messageToSend.Body =
|
||||
new TextPart(mailMessage.IsBodyHtml ? TextFormat.Html : TextFormat.Plain) { Text = mailMessage.Body };
|
||||
}
|
||||
|
||||
return messageToSend;
|
||||
}
|
||||
|
||||
public static NotificationEmailModel ToNotificationEmail(
|
||||
this EmailMessage emailMessage,
|
||||
string? configuredFromAddress)
|
||||
{
|
||||
var fromEmail = string.IsNullOrEmpty(emailMessage.From) ? configuredFromAddress : emailMessage.From;
|
||||
|
||||
NotificationEmailAddress? from = ToNotificationAddress(fromEmail);
|
||||
|
||||
return new NotificationEmailModel(
|
||||
from,
|
||||
GetNotificationAddresses(emailMessage.To),
|
||||
GetNotificationAddresses(emailMessage.Cc),
|
||||
GetNotificationAddresses(emailMessage.Bcc),
|
||||
GetNotificationAddresses(emailMessage.ReplyTo),
|
||||
emailMessage.Subject,
|
||||
emailMessage.Body,
|
||||
emailMessage.Attachments,
|
||||
emailMessage.IsBodyHtml);
|
||||
}
|
||||
|
||||
private static void AddAddresses(MimeMessage message, string?[]? addresses, Func<MimeMessage, InternetAddressList> addressListGetter, bool throwIfNoneValid = false)
|
||||
{
|
||||
var foundValid = false;
|
||||
@@ -72,29 +88,10 @@ namespace Umbraco.Cms.Infrastructure.Extensions
|
||||
|
||||
if (throwIfNoneValid && foundValid == false)
|
||||
{
|
||||
throw new InvalidOperationException($"Email could not be sent. Could not parse a valid recipient address.");
|
||||
throw new InvalidOperationException("Email could not be sent. Could not parse a valid recipient address.");
|
||||
}
|
||||
}
|
||||
|
||||
public static NotificationEmailModel ToNotificationEmail(this EmailMessage emailMessage,
|
||||
string? configuredFromAddress)
|
||||
{
|
||||
var fromEmail = string.IsNullOrEmpty(emailMessage.From) ? configuredFromAddress : emailMessage.From;
|
||||
|
||||
NotificationEmailAddress? from = ToNotificationAddress(fromEmail);
|
||||
|
||||
return new NotificationEmailModel(
|
||||
from,
|
||||
GetNotificationAddresses(emailMessage.To),
|
||||
GetNotificationAddresses(emailMessage.Cc),
|
||||
GetNotificationAddresses(emailMessage.Bcc),
|
||||
GetNotificationAddresses(emailMessage.ReplyTo),
|
||||
emailMessage.Subject,
|
||||
emailMessage.Body,
|
||||
emailMessage.Attachments,
|
||||
emailMessage.IsBodyHtml);
|
||||
}
|
||||
|
||||
private static NotificationEmailAddress? ToNotificationAddress(string? address)
|
||||
{
|
||||
if (InternetAddress.TryParse(address, out InternetAddress internetAddress))
|
||||
@@ -129,4 +126,3 @@ namespace Umbraco.Cms.Infrastructure.Extensions
|
||||
return notificationAddresses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
public static class InfrastuctureTypeLoaderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -12,7 +10,6 @@ namespace Umbraco.Extensions
|
||||
/// </summary>
|
||||
/// <param name="mgr"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<Type> GetPackageMigrationPlans(this TypeLoader mgr) => mgr.GetTypes<PackageMigrationPlan>();
|
||||
|
||||
}
|
||||
public static IEnumerable<Type> GetPackageMigrationPlans(this TypeLoader mgr) =>
|
||||
mgr.GetTypes<PackageMigrationPlan>();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
internal static class InstanceIdentifiableExtensions
|
||||
{
|
||||
public static string GetDebugInfo(this IInstanceIdentifiable instance)
|
||||
public static string GetDebugInfo(this IInstanceIdentifiable? instance)
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
@@ -17,4 +14,3 @@ namespace Umbraco.Extensions
|
||||
return $"(id: {instance.InstanceId.ToString("N").Substring(0, 8)} from thread: {instance.CreatedThreadId})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
public static class MediaPicker3ConfigurationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies the configuration to ensure only valid crops are kept and have the correct width/height.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
public static void ApplyConfiguration(this ImageCropperValue imageCropperValue, MediaPicker3Configuration? configuration)
|
||||
{
|
||||
var crops = new List<ImageCropperValue.ImageCropperCrop>();
|
||||
|
||||
var configuredCrops = configuration?.Crops;
|
||||
MediaPicker3Configuration.CropConfiguration[]? configuredCrops = configuration?.Crops;
|
||||
if (configuredCrops != null)
|
||||
{
|
||||
foreach (var configuredCrop in configuredCrops)
|
||||
foreach (MediaPicker3Configuration.CropConfiguration configuredCrop in configuredCrops)
|
||||
{
|
||||
var crop = imageCropperValue.Crops?.FirstOrDefault(x => x.Alias == configuredCrop.Alias);
|
||||
ImageCropperValue.ImageCropperCrop? crop =
|
||||
imageCropperValue.Crops?.FirstOrDefault(x => x.Alias == configuredCrop.Alias);
|
||||
|
||||
crops.Add(new ImageCropperValue.ImageCropperCrop
|
||||
{
|
||||
Alias = configuredCrop.Alias,
|
||||
Width = configuredCrop.Width,
|
||||
Height = configuredCrop.Height,
|
||||
Coordinates = crop?.Coordinates
|
||||
Coordinates = crop?.Coordinates,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -40,4 +38,3 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides object extension methods.
|
||||
/// </summary>
|
||||
public static class ObjectJsonExtensions
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Type, Dictionary<string, object>> ToObjectTypes = new ConcurrentDictionary<Type, Dictionary<string, object>>();
|
||||
private static readonly ConcurrentDictionary<Type, Dictionary<string, object>> _toObjectTypes = new();
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object's properties into a dictionary.
|
||||
@@ -23,30 +20,37 @@ namespace Umbraco.Extensions
|
||||
/// <returns>A dictionary containing each properties.</returns>
|
||||
public static Dictionary<string, object> ToObjectDictionary<T>(T obj, Func<PropertyInfo, string>? namer = null)
|
||||
{
|
||||
if (obj == null) return new Dictionary<string, object>();
|
||||
if (obj == null)
|
||||
{
|
||||
return new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
string DefaultNamer(PropertyInfo property)
|
||||
{
|
||||
var jsonProperty = property.GetCustomAttribute<JsonPropertyAttribute>();
|
||||
JsonPropertyAttribute? jsonProperty = property.GetCustomAttribute<JsonPropertyAttribute>();
|
||||
return jsonProperty?.PropertyName ?? property.Name;
|
||||
}
|
||||
|
||||
var t = obj.GetType();
|
||||
Type t = obj.GetType();
|
||||
|
||||
if (namer == null) namer = DefaultNamer;
|
||||
if (namer == null)
|
||||
{
|
||||
namer = DefaultNamer;
|
||||
}
|
||||
|
||||
if (!ToObjectTypes.TryGetValue(t, out var properties))
|
||||
if (!_toObjectTypes.TryGetValue(t, out Dictionary<string, object>? properties))
|
||||
{
|
||||
properties = new Dictionary<string, object>();
|
||||
|
||||
foreach (var p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy))
|
||||
foreach (PropertyInfo p in t.GetProperties(BindingFlags.Public | BindingFlags.Instance |
|
||||
BindingFlags.FlattenHierarchy))
|
||||
{
|
||||
properties[namer(p)] = ReflectionUtilities.EmitPropertyGetter<T, object>(p);
|
||||
}
|
||||
|
||||
ToObjectTypes[t] = properties;
|
||||
_toObjectTypes[t] = properties;
|
||||
}
|
||||
|
||||
return properties.ToDictionary(x => x.Key, x => ((Func<T, object>)x.Value)(obj));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
namespace Umbraco.Extensions;
|
||||
|
||||
public static class ScopeExtensions
|
||||
{
|
||||
public static void ReadLock(this IScope scope, ICollection<int> lockIds)
|
||||
@@ -21,4 +20,3 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using HeyRed.MarkdownSharp;
|
||||
using HeyRed.MarkdownSharp;
|
||||
using Umbraco.Cms.Core.HealthChecks;
|
||||
using Umbraco.Cms.Core.HealthChecks.NotificationMethods;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HealthChecks
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HealthChecks;
|
||||
|
||||
public class MarkdownToHtmlConverter : IMarkdownToHtmlConverter
|
||||
{
|
||||
public string ToHtml(HealthCheckResults results, HealthCheckNotificationVerbosity verbosity)
|
||||
@@ -16,19 +16,16 @@ namespace Umbraco.Cms.Infrastructure.HealthChecks
|
||||
|
||||
private string ApplyHtmlHighlighting(string html)
|
||||
{
|
||||
const string SuccessHexColor = "5cb85c";
|
||||
const string WarningHexColor = "f0ad4e";
|
||||
const string ErrorHexColor = "d9534f";
|
||||
const string successHexColor = "5cb85c";
|
||||
const string warningHexColor = "f0ad4e";
|
||||
const string errorHexColor = "d9534f";
|
||||
|
||||
html = ApplyHtmlHighlightingForStatus(html, StatusResultType.Success, SuccessHexColor);
|
||||
html = ApplyHtmlHighlightingForStatus(html, StatusResultType.Warning, WarningHexColor);
|
||||
return ApplyHtmlHighlightingForStatus(html, StatusResultType.Error, ErrorHexColor);
|
||||
html = ApplyHtmlHighlightingForStatus(html, StatusResultType.Success, successHexColor);
|
||||
html = ApplyHtmlHighlightingForStatus(html, StatusResultType.Warning, warningHexColor);
|
||||
return ApplyHtmlHighlightingForStatus(html, StatusResultType.Error, errorHexColor);
|
||||
}
|
||||
|
||||
private string ApplyHtmlHighlightingForStatus(string html, StatusResultType status, string color)
|
||||
{
|
||||
return html
|
||||
private string ApplyHtmlHighlightingForStatus(string html, StatusResultType status, string color) =>
|
||||
html
|
||||
.Replace("Result: '" + status + "'", "Result: <span style=\"color: #" + color + "\">" + status + "</span>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// A Background Task Queue, to enqueue tasks for executing in the background.
|
||||
/// </summary>
|
||||
@@ -13,10 +10,9 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
/// </remarks>
|
||||
public class BackgroundTaskQueue : IBackgroundTaskQueue
|
||||
{
|
||||
private readonly ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
|
||||
new ConcurrentQueue<Func<CancellationToken, Task>>();
|
||||
private readonly SemaphoreSlim _signal = new(0);
|
||||
|
||||
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
|
||||
private readonly ConcurrentQueue<Func<CancellationToken, Task>> _workItems = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)
|
||||
@@ -39,4 +35,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
return workItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -8,19 +6,19 @@ using Umbraco.Cms.Core.Runtime;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Recurring hosted service that executes the content history cleanup.
|
||||
/// </summary>
|
||||
public class ContentVersionCleanup : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly ILogger<ContentVersionCleanup> _logger;
|
||||
private readonly IOptionsMonitor<ContentSettings> _settingsMonitor;
|
||||
private readonly IContentVersionService _service;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IServerRoleAccessor _serverRoleAccessor;
|
||||
private readonly IContentVersionService _service;
|
||||
private readonly IOptionsMonitor<ContentSettings> _settingsMonitor;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentVersionCleanup" /> class.
|
||||
@@ -48,7 +46,8 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
// Globally disabled by feature flag
|
||||
if (!_settingsMonitor.CurrentValue.ContentVersionCleanupPolicy.EnableCleanup)
|
||||
{
|
||||
_logger.LogInformation("ContentVersionCleanup task will not run as it has been globally disabled via configuration");
|
||||
_logger.LogInformation(
|
||||
"ContentVersionCleanup task will not run as it has been globally disabled via configuration");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -93,4 +92,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -19,22 +15,22 @@ using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Hosted service implementation for recurring health check notifications.
|
||||
/// </summary>
|
||||
public class HealthCheckNotifier : RecurringHostedServiceBase
|
||||
{
|
||||
private HealthChecksSettings _healthChecksSettings;
|
||||
private readonly HealthCheckCollection _healthChecks;
|
||||
private readonly HealthCheckNotificationMethodCollection _notifications;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IServerRoleAccessor _serverRegistrar;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly ILogger<HealthCheckNotifier> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly HealthCheckNotificationMethodCollection _notifications;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IServerRoleAccessor _serverRegistrar;
|
||||
private HealthChecksSettings _healthChecksSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="HealthCheckNotifier" /> class.
|
||||
@@ -114,7 +110,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
// Ensure we use an explicit scope since we are running on a background thread and plugin health
|
||||
// checks can be making service/database calls so we want to ensure the CallContext/Ambient scope
|
||||
// isn't used since that can be problematic.
|
||||
using (ICoreScope scope = _scopeProvider.CreateCoreScope())
|
||||
using (ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true))
|
||||
using (_profilingLogger.DebugDuration<HealthCheckNotifier>("Health checks executing", "Health checks complete"))
|
||||
{
|
||||
// Don't notify for any checks that are disabled, nor for any disabled just for notifications.
|
||||
@@ -128,7 +124,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
IEnumerable<HealthCheck> checks = _healthChecks
|
||||
.Where(x => disabledCheckIds.Contains(x.Id) == false);
|
||||
|
||||
var results = await HealthCheckResults.Create(checks);
|
||||
HealthCheckResults results = await HealthCheckResults.Create(checks);
|
||||
results.LogResults();
|
||||
|
||||
// Send using registered notification methods that are enabled.
|
||||
@@ -139,4 +135,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
/// <summary>
|
||||
/// A Background Task Queue, to enqueue tasks for executing in the background.
|
||||
/// </summary>
|
||||
@@ -22,4 +18,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
/// </summary>
|
||||
Task<Func<CancellationToken, Task>?> DequeueAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -16,20 +12,20 @@ using Umbraco.Cms.Core.Runtime;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Hosted service implementation for keep alive feature.
|
||||
/// </summary>
|
||||
public class KeepAlive : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IMainDom _mainDom;
|
||||
private KeepAliveSettings _keepAliveSettings;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private readonly ILogger<KeepAlive> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IServerRoleAccessor _serverRegistrar;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
private KeepAliveSettings _keepAliveSettings;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeepAlive" /> class.
|
||||
@@ -97,7 +93,9 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
|
||||
// If the config is an absolute path, just use it
|
||||
string keepAlivePingUrl = WebPath.Combine(umbracoAppUrl!, _hostingEnvironment.ToAbsolute(_keepAliveSettings.KeepAlivePingUrl));
|
||||
var keepAlivePingUrl = WebPath.Combine(
|
||||
umbracoAppUrl!,
|
||||
_hostingEnvironment.ToAbsolute(_keepAliveSettings.KeepAlivePingUrl));
|
||||
|
||||
try
|
||||
{
|
||||
@@ -112,4 +110,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -12,8 +10,8 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Log scrubbing hosted service.
|
||||
/// </summary>
|
||||
@@ -22,13 +20,13 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
/// </remarks>
|
||||
public class LogScrubber : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IServerRoleAccessor _serverRegistrar;
|
||||
private readonly IAuditService _auditService;
|
||||
private LoggingSettings _settings;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly ILogger<LogScrubber> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IServerRoleAccessor _serverRegistrar;
|
||||
private LoggingSettings _settings;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LogScrubber" /> class.
|
||||
@@ -90,4 +88,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// A queue based hosted service used to executing tasks on a background thread.
|
||||
@@ -17,7 +13,8 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
private readonly ILogger<QueuedHostedService> _logger;
|
||||
|
||||
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
|
||||
public QueuedHostedService(
|
||||
IBackgroundTaskQueue taskQueue,
|
||||
ILogger<QueuedHostedService> logger)
|
||||
{
|
||||
TaskQueue = taskQueue;
|
||||
@@ -26,11 +23,16 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
|
||||
public IBackgroundTaskQueue TaskQueue { get; }
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
public override async Task StopAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await BackgroundProcessing(stoppingToken);
|
||||
_logger.LogInformation("Queued Hosted Service is stopping.");
|
||||
|
||||
await base.StopAsync(stoppingToken);
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken) =>
|
||||
await BackgroundProcessing(stoppingToken);
|
||||
|
||||
private async Task BackgroundProcessing(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
@@ -46,17 +48,10 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex,
|
||||
_logger.LogError(
|
||||
ex,
|
||||
"Error occurred executing {WorkItem}.", nameof(workItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task StopAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
_logger.LogInformation("Queued Hosted Service is stopping.");
|
||||
|
||||
await base.StopAsync(stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a base class for recurring background tasks implemented as hosted services.
|
||||
/// </summary>
|
||||
@@ -19,22 +16,27 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
public abstract class RecurringHostedServiceBase : IHostedService, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The default delay to use for recurring tasks for the first run after application start-up if no alternative is configured.
|
||||
/// The default delay to use for recurring tasks for the first run after application start-up if no alternative is
|
||||
/// configured.
|
||||
/// </summary>
|
||||
protected static readonly TimeSpan DefaultDelay = TimeSpan.FromMinutes(3);
|
||||
|
||||
private readonly ILogger? _logger;
|
||||
private TimeSpan _period;
|
||||
private readonly TimeSpan _delay;
|
||||
private Timer? _timer;
|
||||
|
||||
private readonly ILogger? _logger;
|
||||
private bool _disposedValue;
|
||||
private TimeSpan _period;
|
||||
private Timer? _timer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RecurringHostedServiceBase" /> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Logger.</param>
|
||||
/// <param name="period">Timespan representing how often the task should recur.</param>
|
||||
/// <param name="delay">Timespan representing the initial delay after application start-up before the first run of the task occurs.</param>
|
||||
/// <param name="delay">
|
||||
/// Timespan representing the initial delay after application start-up before the first run of the task
|
||||
/// occurs.
|
||||
/// </param>
|
||||
protected RecurringHostedServiceBase(ILogger? logger, TimeSpan period, TimeSpan delay)
|
||||
{
|
||||
_logger = logger;
|
||||
@@ -46,13 +48,15 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
[Obsolete("Please use constructor that takes an ILogger instead")]
|
||||
protected RecurringHostedServiceBase(TimeSpan period, TimeSpan delay)
|
||||
: this(null, period, delay)
|
||||
{ }
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the period between operations.
|
||||
/// </summary>
|
||||
/// <param name="newPeriod">The new period between tasks</param>
|
||||
protected void ChangePeriod(TimeSpan newPeriod) => _period = newPeriod;
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
@@ -65,6 +69,14 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_period = Timeout.InfiniteTimeSpan;
|
||||
_timer?.Change(Timeout.Infinite, 0);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the task.
|
||||
/// </summary>
|
||||
@@ -97,13 +109,11 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
|
||||
public abstract Task PerformExecuteAsync(object? state);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_period = Timeout.InfiniteTimeSpan;
|
||||
_timer?.Change(Timeout.Infinite, 0);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
/// <summary>
|
||||
/// Change the period between operations.
|
||||
/// </summary>
|
||||
/// <param name="newPeriod">The new period between tasks</param>
|
||||
protected void ChangePeriod(TimeSpan newPeriod) => _period = newPeriod;
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
@@ -117,12 +127,4 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
@@ -12,13 +9,13 @@ using Umbraco.Cms.Core.Telemetry;
|
||||
using Umbraco.Cms.Core.Telemetry.Models;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
public class ReportSiteTask : RecurringHostedServiceBase
|
||||
{
|
||||
private static HttpClient _httpClient = new();
|
||||
private readonly ILogger<ReportSiteTask> _logger;
|
||||
private readonly ITelemetryService _telemetryService;
|
||||
private static HttpClient s_httpClient = new();
|
||||
|
||||
public ReportSiteTask(
|
||||
ILogger<ReportSiteTask> logger,
|
||||
@@ -27,7 +24,7 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
_logger = logger;
|
||||
_telemetryService = telemetryService;
|
||||
s_httpClient = new HttpClient();
|
||||
_httpClient = new HttpClient();
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor that takes ITelemetryService instead, scheduled for removal in V11")]
|
||||
@@ -54,28 +51,27 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
|
||||
try
|
||||
{
|
||||
if (s_httpClient.BaseAddress is null)
|
||||
if (_httpClient.BaseAddress is null)
|
||||
{
|
||||
// Send data to LIVE telemetry
|
||||
s_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
|
||||
_httpClient.BaseAddress = new Uri("https://telemetry.umbraco.com/");
|
||||
|
||||
#if DEBUG
|
||||
// Send data to DEBUG telemetry service
|
||||
s_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
|
||||
_httpClient.BaseAddress = new Uri("https://telemetry.rainbowsrock.net/");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
s_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
|
||||
_httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json");
|
||||
|
||||
using (var request = new HttpRequestMessage(HttpMethod.Post, "installs/"))
|
||||
{
|
||||
request.Content = new StringContent(JsonConvert.SerializeObject(telemetryReportData), Encoding.UTF8, "application/json"); //CONTENT-TYPE header
|
||||
request.Content = new StringContent(JsonConvert.SerializeObject(telemetryReportData), Encoding.UTF8, "application/json");
|
||||
|
||||
// Make a HTTP Post to telemetry service
|
||||
// https://telemetry.umbraco.com/installs/
|
||||
// Fire & Forget, do not need to know if its a 200, 500 etc
|
||||
using (HttpResponseMessage response = await s_httpClient.SendAsync(request))
|
||||
using (await _httpClient.SendAsync(request))
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -89,4 +85,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Runtime;
|
||||
@@ -13,23 +8,23 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Hosted service implementation for scheduled publishing feature.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Runs only on non-replica servers.</remarks>
|
||||
/// Runs only on non-replica servers.
|
||||
/// </remarks>
|
||||
public class ScheduledPublishing : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly ILogger<ScheduledPublishing> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IServerMessenger _serverMessenger;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly IServerMessenger _serverMessenger;
|
||||
private readonly IServerRoleAccessor _serverRegistrar;
|
||||
private readonly IUmbracoContextFactory _umbracoContextFactory;
|
||||
|
||||
@@ -98,7 +93,6 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
// - batched messenger should not depend on a current HttpContext
|
||||
// but then what should be its "scope"? could we attach it to scopes?
|
||||
// - and we should definitively *not* have to flush it here (should be auto)
|
||||
|
||||
using UmbracoContextReference contextReference = _umbracoContextFactory.EnsureUmbracoContext();
|
||||
using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true);
|
||||
|
||||
@@ -138,4 +132,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -10,16 +8,16 @@ using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration;
|
||||
|
||||
/// <summary>
|
||||
/// Implements periodic database instruction processing as a hosted service.
|
||||
/// </summary>
|
||||
public class InstructionProcessTask : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IServerMessenger _messenger;
|
||||
private readonly ILogger<InstructionProcessTask> _logger;
|
||||
private readonly IServerMessenger _messenger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private bool _disposedValue;
|
||||
|
||||
/// <summary>
|
||||
@@ -71,4 +69,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -12,20 +8,19 @@ using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration;
|
||||
|
||||
/// <summary>
|
||||
/// Implements periodic server "touching" (to mark as active/deactive) as a hosted service.
|
||||
/// </summary>
|
||||
public class TouchServerTask : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IServerRegistrationService _serverRegistrationService;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly ILogger<TouchServerTask> _logger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IServerRegistrationService _serverRegistrationService;
|
||||
private readonly IServerRoleAccessor _serverRoleAccessor;
|
||||
private GlobalSettings _globalSettings;
|
||||
|
||||
@@ -34,9 +29,10 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
/// </summary>
|
||||
/// <param name="runtimeState">Representation of the state of the Umbraco runtime.</param>
|
||||
/// <param name="serverRegistrationService">Services for server registrations.</param>
|
||||
/// <param name="requestAccessor">Accessor for the current request.</param>
|
||||
/// <param name="logger">The typed logger.</param>
|
||||
/// <param name="globalSettings">The configuration for global settings.</param>
|
||||
/// <param name="hostingEnvironment">The hostingEnviroment.</param>
|
||||
/// <param name="serverRoleAccessor">The accessor for the server role</param>
|
||||
public TouchServerTask(
|
||||
IRuntimeState runtimeState,
|
||||
IServerRegistrationService serverRegistrationService,
|
||||
@@ -47,7 +43,8 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
: base(logger, globalSettings.CurrentValue.DatabaseServerRegistrar.WaitTimeBetweenCalls, TimeSpan.FromSeconds(15))
|
||||
{
|
||||
_runtimeState = runtimeState;
|
||||
_serverRegistrationService = serverRegistrationService ?? throw new ArgumentNullException(nameof(serverRegistrationService));
|
||||
_serverRegistrationService = serverRegistrationService ??
|
||||
throw new ArgumentNullException(nameof(serverRegistrationService));
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_logger = logger;
|
||||
_globalSettings = globalSettings.CurrentValue;
|
||||
@@ -83,7 +80,9 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
|
||||
try
|
||||
{
|
||||
_serverRegistrationService.TouchServer(serverAddress!, _globalSettings.DatabaseServerRegistrar.StaleServerTimeout);
|
||||
_serverRegistrationService.TouchServer(
|
||||
serverAddress!,
|
||||
_globalSettings.DatabaseServerRegistrar.StaleServerTimeout);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -93,4 +92,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices.ServerRegistration
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Cms.Core.Runtime;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.HostedServices;
|
||||
|
||||
/// <summary>
|
||||
/// Used to cleanup temporary file locations.
|
||||
/// </summary>
|
||||
@@ -19,12 +16,12 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
/// </remarks>
|
||||
public class TempFileCleanup : RecurringHostedServiceBase
|
||||
{
|
||||
private readonly TimeSpan _age = TimeSpan.FromDays(1);
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly ILogger<TempFileCleanup> _logger;
|
||||
private readonly IMainDom _mainDom;
|
||||
|
||||
private readonly DirectoryInfo[] _tempFolders;
|
||||
private readonly TimeSpan _age = TimeSpan.FromDays(1);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TempFileCleanup" /> class.
|
||||
@@ -70,7 +67,8 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
case CleanFolderResultStatus.FailedWithException:
|
||||
foreach (CleanFolderResult.Error error in result.Errors!)
|
||||
{
|
||||
_logger.LogError(error.Exception, "Could not delete temp file {FileName}", error.ErroringFile.FullName);
|
||||
_logger.LogError(error.Exception, "Could not delete temp file {FileName}",
|
||||
error.ErroringFile.FullName);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -101,4 +99,3 @@ namespace Umbraco.Cms.Infrastructure.HostedServices
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.XPath;
|
||||
using Examine.Search;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Xml;
|
||||
|
||||
namespace Umbraco.Cms.Core
|
||||
{
|
||||
namespace Umbraco.Cms.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Query methods used for accessing strongly typed content in templates.
|
||||
/// </summary>
|
||||
@@ -58,8 +56,14 @@ namespace Umbraco.Cms.Core
|
||||
/// <param name="take">The amount of results to take/return.</param>
|
||||
/// <param name="totalRecords">The total amount of records.</param>
|
||||
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
|
||||
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
|
||||
/// <param name="loadedFields">This parameter is no longer used, because the results are loaded from the published snapshot using the single item ID field.</param>
|
||||
/// <param name="indexName">
|
||||
/// The name of the index to search (defaults to
|
||||
/// <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).
|
||||
/// </param>
|
||||
/// <param name="loadedFields">
|
||||
/// This parameter is no longer used, because the results are loaded from the published snapshot
|
||||
/// using the single item ID field.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The search results.
|
||||
/// </returns>
|
||||
@@ -67,18 +71,29 @@ namespace Umbraco.Cms.Core
|
||||
/// <para>
|
||||
/// When the <paramref name="culture" /> is not specified or is *, all cultures are searched.
|
||||
/// To search for only invariant documents and fields use null.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all
|
||||
/// invariant fields for all documents.
|
||||
/// </para>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName, ISet<string>? loadedFields = null);
|
||||
IEnumerable<PublishedSearchResult> Search(
|
||||
string term,
|
||||
int skip,
|
||||
int take,
|
||||
out long totalRecords,
|
||||
string culture = "*",
|
||||
string indexName = Constants.UmbracoIndexes.ExternalIndexName,
|
||||
ISet<string>? loadedFields = null);
|
||||
|
||||
/// <summary>
|
||||
/// Searches content.
|
||||
/// </summary>
|
||||
/// <param name="term">The term to search.</param>
|
||||
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
|
||||
/// <param name="indexName">The name of the index to search (defaults to <see cref="Cms.Core.Constants.UmbracoIndexes.ExternalIndexName" />).</param>
|
||||
/// <param name="indexName">
|
||||
/// The name of the index to search (defaults to
|
||||
/// <see cref="Cms.Core.Constants.UmbracoIndexes.ExternalIndexName" />).
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The search results.
|
||||
/// </returns>
|
||||
@@ -86,7 +101,8 @@ namespace Umbraco.Cms.Core
|
||||
/// <para>
|
||||
/// When the <paramref name="culture" /> is not specified or is *, all cultures are searched.
|
||||
/// To search for only invariant documents and fields use null.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all
|
||||
/// invariant fields for all documents.
|
||||
/// </para>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
@@ -113,4 +129,3 @@ namespace Umbraco.Cms.Core
|
||||
/// </returns>
|
||||
IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip, int take, out long totalRecords);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Umbraco.Cms.Core
|
||||
{
|
||||
namespace Umbraco.Cms.Core;
|
||||
|
||||
/// <remarks>
|
||||
/// Not intended for use in background threads where you should make use of <see cref="Umbraco.Cms.Core.Web.IUmbracoContextFactory.EnsureUmbracoContext"/>
|
||||
/// and instead resolve IPublishedContentQuery from a <see cref="Microsoft.Extensions.DependencyInjection.IServiceScope"/>
|
||||
/// Not intended for use in background threads where you should make use of
|
||||
/// <see cref="Umbraco.Cms.Core.Web.IUmbracoContextFactory.EnsureUmbracoContext" />
|
||||
/// and instead resolve IPublishedContentQuery from a
|
||||
/// <see cref="Microsoft.Extensions.DependencyInjection.IServiceScope" />
|
||||
/// e.g. using <see cref="Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.CreateScope" />
|
||||
/// <example>
|
||||
/// <code>
|
||||
@@ -19,4 +21,3 @@ namespace Umbraco.Cms.Core
|
||||
{
|
||||
bool TryGetValue([MaybeNullWhen(false)] out IPublishedContentQuery publishedContentQuery);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.AccessControl;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
@@ -14,26 +11,29 @@ using Umbraco.Cms.Core.Install;
|
||||
using Umbraco.Cms.Core.IO;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
/// <inheritdoc />
|
||||
public class FilePermissionHelper : IFilePermissionHelper
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
|
||||
private readonly string[] _packagesPermissionsDirs;
|
||||
|
||||
// ensure that these directories exist and Umbraco can write to them
|
||||
private readonly string[] _permissionDirs;
|
||||
private readonly string[] _packagesPermissionsDirs;
|
||||
|
||||
// ensure Umbraco can write to these files (the directories must exist)
|
||||
private readonly string[] _permissionFiles = Array.Empty<string>();
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private string _basePath;
|
||||
private readonly string _basePath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilePermissionHelper" /> class.
|
||||
/// </summary>
|
||||
public FilePermissionHelper(IOptions<GlobalSettings> globalSettings, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment)
|
||||
public FilePermissionHelper(IOptions<GlobalSettings> globalSettings, IIOHelper ioHelper,
|
||||
IHostingEnvironment hostingEnvironment)
|
||||
{
|
||||
_globalSettings = globalSettings.Value;
|
||||
_ioHelper = ioHelper;
|
||||
@@ -45,14 +45,14 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Config),
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Data),
|
||||
hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoMediaPhysicalRootPath),
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Preview)
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Preview),
|
||||
};
|
||||
_packagesPermissionsDirs = new[]
|
||||
{
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Bin),
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Umbraco),
|
||||
hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoPath),
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Packages)
|
||||
hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.Packages),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,7 +70,9 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
EnsureFiles(_permissionFiles, out errors);
|
||||
report[FilePermissionTest.FileWriting] = errors.ToList();
|
||||
|
||||
EnsureCanCreateSubDirectory(_hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoMediaPhysicalRootPath), out errors);
|
||||
EnsureCanCreateSubDirectory(
|
||||
_hostingEnvironment.MapPathWebRoot(_globalSettings.UmbracoMediaPhysicalRootPath),
|
||||
out errors);
|
||||
report[FilePermissionTest.MediaFolderCreation] = errors.ToList();
|
||||
|
||||
return report.Sum(x => x.Value.Count()) == 0;
|
||||
@@ -211,12 +213,14 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
var writeAllow = false;
|
||||
var writeDeny = false;
|
||||
var accessControlList = new DirectorySecurity(path, AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
|
||||
var accessControlList = new DirectorySecurity(
|
||||
path,
|
||||
AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
|
||||
|
||||
AuthorizationRuleCollection accessRules;
|
||||
try
|
||||
{
|
||||
accessRules = accessControlList.GetAccessRules(true, true, typeof(System.Security.Principal.SecurityIdentifier));
|
||||
accessRules = accessControlList.GetAccessRules(true, true, typeof(SecurityIdentifier));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -262,4 +266,3 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
|
||||
// Check for current install ID
|
||||
var installCookie = _cookieManager.GetCookieValue(Constants.Web.InstallerCookieName);
|
||||
if (!Guid.TryParse(installCookie, out var installId))
|
||||
if (!Guid.TryParse(installCookie, out Guid installId))
|
||||
{
|
||||
installId = Guid.NewGuid();
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Install.InstallSteps;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Infrastructure.Install.InstallSteps;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
public sealed class InstallStepCollection
|
||||
{
|
||||
private readonly InstallHelper _installHelper;
|
||||
@@ -16,27 +14,22 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
_installHelper = installHelper;
|
||||
|
||||
// TODO: this is ugly but I have a branch where it's nicely refactored - for now we just want to manage ordering
|
||||
var a = installerSteps.ToArray();
|
||||
InstallSetupStep[] a = installerSteps.ToArray();
|
||||
_orderedInstallerSteps = new InstallSetupStep[]
|
||||
{
|
||||
a.OfType<NewInstallStep>().First(),
|
||||
a.OfType<UpgradeStep>().First(),
|
||||
a.OfType<FilePermissionsStep>().First(),
|
||||
a.OfType<TelemetryIdentifierStep>().First(),
|
||||
a.OfType<DatabaseConfigureStep>().First(),
|
||||
a.OfType<DatabaseInstallStep>().First(),
|
||||
a.OfType<NewInstallStep>().First(), a.OfType<UpgradeStep>().First(),
|
||||
a.OfType<FilePermissionsStep>().First(), a.OfType<TelemetryIdentifierStep>().First(),
|
||||
a.OfType<DatabaseConfigureStep>().First(), a.OfType<DatabaseInstallStep>().First(),
|
||||
a.OfType<DatabaseUpgradeStep>().First(),
|
||||
|
||||
// TODO: Add these back once we have a compatible Starter kit
|
||||
// a.OfType<StarterKitDownloadStep>().First(),
|
||||
// a.OfType<StarterKitInstallStep>().First(),
|
||||
// a.OfType<StarterKitCleanupStep>().First(),
|
||||
|
||||
a.OfType<CompleteInstallStep>().First(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the installer steps
|
||||
/// </summary>
|
||||
@@ -44,18 +37,12 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
/// <remarks>
|
||||
/// The step order returned here is how they will appear on the front-end if they have views assigned
|
||||
/// </remarks>
|
||||
public IEnumerable<InstallSetupStep> GetAllSteps()
|
||||
{
|
||||
return _orderedInstallerSteps;
|
||||
}
|
||||
public IEnumerable<InstallSetupStep> GetAllSteps() => _orderedInstallerSteps;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the steps that are used only for the current installation type
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<InstallSetupStep> GetStepsForCurrentInstallType()
|
||||
{
|
||||
return GetAllSteps().Where(x => x.InstallTypeTarget.HasFlag(_installHelper.GetInstallationType()));
|
||||
}
|
||||
}
|
||||
public IEnumerable<InstallSetupStep> GetStepsForCurrentInstallType() => GetAllSteps()
|
||||
.Where(x => x.InstallTypeTarget.HasFlag(_installHelper.GetInstallationType()));
|
||||
}
|
||||
|
||||
@@ -1,31 +1,26 @@
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade,
|
||||
"UmbracoVersion", 50, "Installation is complete! Get ready to be redirected to your new CMS.",
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps;
|
||||
|
||||
[InstallSetupStep(
|
||||
InstallationType.NewInstall | InstallationType.Upgrade,
|
||||
"UmbracoVersion",
|
||||
50,
|
||||
"Installation is complete! Get ready to be redirected to your new CMS.",
|
||||
PerformsAppRestart = true)]
|
||||
public class CompleteInstallStep : InstallSetupStep<object>
|
||||
{
|
||||
private readonly InstallHelper _installHelper;
|
||||
|
||||
public CompleteInstallStep(InstallHelper installHelper)
|
||||
{
|
||||
_installHelper = installHelper;
|
||||
}
|
||||
public CompleteInstallStep(InstallHelper installHelper) => _installHelper = installHelper;
|
||||
|
||||
public override async Task<InstallSetupResult?> ExecuteAsync(object model)
|
||||
{
|
||||
// reports the ended install
|
||||
await _installHelper.SetInstallStatusAsync(true, "");
|
||||
await _installHelper.SetInstallStatusAsync(true, string.Empty);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool RequiresExecution(object model)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public override bool RequiresExecution(object model) => true;
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps;
|
||||
|
||||
[InstallSetupStep(InstallationType.NewInstall, "DatabaseConfigure", "database", 10, "Setting up a database, so Umbraco has a place to store your website", PerformsAppRestart = true)]
|
||||
public class DatabaseConfigureStep : InstallSetupStep<DatabaseModel>
|
||||
{
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
private readonly ILogger<DatabaseConfigureStep> _logger;
|
||||
private readonly IEnumerable<IDatabaseProviderMetadata> _databaseProviderMetadata;
|
||||
private readonly IOptionsMonitor<ConnectionStrings> _connectionStrings;
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
private readonly IEnumerable<IDatabaseProviderMetadata> _databaseProviderMetadata;
|
||||
private readonly ILogger<DatabaseConfigureStep> _logger;
|
||||
|
||||
public DatabaseConfigureStep(
|
||||
DatabaseBuilder databaseBuilder,
|
||||
@@ -29,9 +29,13 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
_databaseProviderMetadata = databaseProviderMetadata;
|
||||
}
|
||||
|
||||
public override object ViewModel => new {databases = _databaseProviderMetadata.GetAvailable().ToList()};
|
||||
|
||||
public override string View => ShouldDisplayView() ? base.View : string.Empty;
|
||||
|
||||
public override Task<InstallSetupResult?> ExecuteAsync(DatabaseModel databaseSettings)
|
||||
{
|
||||
if (!_databaseBuilder.ConfigureDatabaseConnection(databaseSettings, isTrialRun: false))
|
||||
if (!_databaseBuilder.ConfigureDatabaseConnection(databaseSettings, false))
|
||||
{
|
||||
throw new InstallException("Could not connect to the database");
|
||||
}
|
||||
@@ -39,13 +43,6 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
return Task.FromResult<InstallSetupResult?>(null);
|
||||
}
|
||||
|
||||
public override object ViewModel => new
|
||||
{
|
||||
databases = _databaseProviderMetadata.GetAvailable().ToList()
|
||||
};
|
||||
|
||||
public override string View => ShouldDisplayView() ? base.View : string.Empty;
|
||||
|
||||
public override bool RequiresExecution(DatabaseModel model) => ShouldDisplayView();
|
||||
|
||||
private bool ShouldDisplayView()
|
||||
@@ -72,4 +69,3 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Install;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps;
|
||||
|
||||
[InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade, "DatabaseInstall", 11, "")]
|
||||
public class DatabaseInstallStep : InstallSetupStep<object>
|
||||
{
|
||||
private readonly IRuntimeState _runtime;
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
private readonly IRuntimeState _runtime;
|
||||
|
||||
public DatabaseInstallStep(IRuntimeState runtime, DatabaseBuilder databaseBuilder)
|
||||
{
|
||||
@@ -24,14 +21,16 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
public override Task<InstallSetupResult?> ExecuteAsync(object model)
|
||||
{
|
||||
if (_runtime.Level == RuntimeLevel.Run)
|
||||
{
|
||||
throw new Exception("Umbraco is already configured!");
|
||||
}
|
||||
|
||||
if (_runtime.Reason == RuntimeLevelReason.InstallMissingDatabase)
|
||||
{
|
||||
_databaseBuilder.CreateDatabase();
|
||||
}
|
||||
|
||||
var result = _databaseBuilder.CreateSchemaAndData();
|
||||
DatabaseBuilder.Result? result = _databaseBuilder.CreateSchemaAndData();
|
||||
|
||||
if (result?.Success == false)
|
||||
{
|
||||
@@ -44,12 +43,8 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
}
|
||||
|
||||
// Upgrade is required, so set the flag for the next step
|
||||
return Task.FromResult(new InstallSetupResult(new Dictionary<string, object>
|
||||
{
|
||||
{ "upgrade", true}
|
||||
}))!;
|
||||
return Task.FromResult(new InstallSetupResult(new Dictionary<string, object> { { "upgrade", true } }))!;
|
||||
}
|
||||
|
||||
public override bool RequiresExecution(object model) => true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
|
||||
public override Task<InstallSetupResult?> ExecuteAsync(object model)
|
||||
{
|
||||
var installSteps = InstallStatusTracker.GetStatus().ToArray();
|
||||
var previousStep = installSteps.Single(x => x.Name == "DatabaseInstall");
|
||||
InstallTrackingItem[] installSteps = InstallStatusTracker.GetStatus().ToArray();
|
||||
InstallTrackingItem previousStep = installSteps.Single(x => x.Name == "DatabaseInstall");
|
||||
var upgrade = previousStep.AdditionalData.ContainsKey("upgrade");
|
||||
|
||||
if (upgrade)
|
||||
@@ -49,7 +49,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
var plan = new UmbracoPlan(_umbracoVersion);
|
||||
plan.AddPostMigration<ClearCsrfCookies>(); // needed when running installer (back-office)
|
||||
|
||||
var result = _databaseBuilder.UpgradeSchemaAndData(plan);
|
||||
DatabaseBuilder.Result? result = _databaseBuilder.UpgradeSchemaAndData(plan);
|
||||
|
||||
if (result?.Success == false)
|
||||
{
|
||||
@@ -69,7 +69,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
}
|
||||
|
||||
// This step relies on the previous one completed - because it has stored some information we need
|
||||
var installSteps = InstallStatusTracker.GetStatus().ToArray();
|
||||
InstallTrackingItem[] installSteps = InstallStatusTracker.GetStatus().ToArray();
|
||||
if (installSteps.Any(x => x.Name == "DatabaseInstall" && x.AdditionalData.ContainsKey("upgrade")) == false)
|
||||
{
|
||||
return false;
|
||||
@@ -79,7 +79,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
// A connection string was present, determine whether this is an install/upgrade
|
||||
// Return true (upgrade) if there is an installed version, else false (install)
|
||||
var result = _databaseBuilder.ValidateSchema();
|
||||
DatabaseSchemaResult? result = _databaseBuilder.ValidateSchema();
|
||||
return result?.DetermineHasInstalledVersion() ?? false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System.Collections.Specialized;
|
||||
using System.Data.Common;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Install.Models;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Web;
|
||||
@@ -14,6 +17,7 @@ using Umbraco.Cms.Infrastructure.Persistence;
|
||||
using Umbraco.Cms.Web.Common.DependencyInjection;
|
||||
using Umbraco.Extensions;
|
||||
using Constants = Umbraco.Cms.Core.Constants;
|
||||
using HttpResponseMessage = System.Net.Http.HttpResponseMessage;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
{
|
||||
@@ -100,18 +104,18 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
|
||||
public override async Task<InstallSetupResult?> ExecuteAsync(UserModel user)
|
||||
{
|
||||
var admin = _userService.GetUserById(Constants.Security.SuperUserId);
|
||||
IUser? admin = _userService.GetUserById(Constants.Security.SuperUserId);
|
||||
if (admin == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the super user!");
|
||||
}
|
||||
admin.Email = user.Email.Trim();
|
||||
admin.Name = user.Name!.Trim();
|
||||
admin.Name = user.Name.Trim();
|
||||
admin.Username = user.Email.Trim();
|
||||
|
||||
_userService.Save(admin);
|
||||
|
||||
var membershipUser = await _userManager.FindByIdAsync(Constants.Security.SuperUserIdAsString);
|
||||
BackOfficeIdentityUser? membershipUser = await _userManager.FindByIdAsync(Constants.Security.SuperUserIdAsString);
|
||||
if (membershipUser == null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
@@ -121,11 +125,15 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
//To change the password here we actually need to reset it since we don't have an old one to use to change
|
||||
var resetToken = await _userManager.GeneratePasswordResetTokenAsync(membershipUser);
|
||||
if (string.IsNullOrWhiteSpace(resetToken))
|
||||
{
|
||||
throw new InvalidOperationException("Could not reset password: unable to generate internal reset token");
|
||||
}
|
||||
|
||||
var resetResult = await _userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim());
|
||||
IdentityResult resetResult = await _userManager.ChangePasswordWithResetAsync(membershipUser.Id, resetToken, user.Password.Trim());
|
||||
if (!resetResult.Succeeded)
|
||||
{
|
||||
throw new InvalidOperationException("Could not reset password: " + string.Join(", ", resetResult.Errors.ToErrorMessage()));
|
||||
}
|
||||
|
||||
_metricsConsentService.SetConsentLevel(user.TelemetryLevel);
|
||||
|
||||
@@ -138,7 +146,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
|
||||
try
|
||||
{
|
||||
var response = httpClient.PostAsync("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", content).Result;
|
||||
HttpResponseMessage response = httpClient.PostAsync("https://shop.umbraco.com/base/Ecom/SubmitEmail/installer.aspx", content).Result;
|
||||
}
|
||||
catch { /* fail in silence */ }
|
||||
}
|
||||
@@ -192,14 +200,14 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
|
||||
private InstallState GetInstallState()
|
||||
{
|
||||
var installState = InstallState.Unknown;
|
||||
InstallState installState = InstallState.Unknown;
|
||||
|
||||
if (_databaseBuilder.IsDatabaseConfigured)
|
||||
{
|
||||
installState = (installState | InstallState.HasConnectionString) & ~InstallState.Unknown;
|
||||
}
|
||||
|
||||
var umbracoConnectionString = _connectionStrings.CurrentValue;
|
||||
ConnectionStrings? umbracoConnectionString = _connectionStrings.CurrentValue;
|
||||
|
||||
var isConnectionStringConfigured = umbracoConnectionString.IsConnectionStringConfigured();
|
||||
if (isConnectionStringConfigured)
|
||||
@@ -207,7 +215,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
installState = (installState | InstallState.ConnectionStringConfigured) & ~InstallState.Unknown;
|
||||
}
|
||||
|
||||
var factory = _dbProviderFactoryCreator.CreateFactory(umbracoConnectionString.ProviderName);
|
||||
DbProviderFactory? factory = _dbProviderFactoryCreator.CreateFactory(umbracoConnectionString.ProviderName);
|
||||
var isConnectionAvailable = isConnectionStringConfigured && DbConnectionExtensions.IsConnectionAvailable(umbracoConnectionString.ConnectionString, factory);
|
||||
if (isConnectionAvailable)
|
||||
{
|
||||
@@ -231,14 +239,14 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
|
||||
|
||||
private bool ShowView()
|
||||
{
|
||||
var installState = GetInstallState();
|
||||
InstallState installState = GetInstallState();
|
||||
|
||||
return installState.HasFlag(InstallState.Unknown) || !installState.HasFlag(InstallState.UmbracoInstalled);
|
||||
}
|
||||
|
||||
public override bool RequiresExecution(UserModel model)
|
||||
{
|
||||
var installState = GetInstallState();
|
||||
InstallState installState = GetInstallState();
|
||||
if (installState.HasFlag(InstallState.Unknown))
|
||||
{
|
||||
// In this one case when it's a brand new install and nothing has been configured, make sure the
|
||||
|
||||
@@ -1,32 +1,29 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Cms.Core.Migrations;
|
||||
using Umbraco.Cms.Core.Packaging;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Notifications;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs the package migration plans
|
||||
/// </summary>
|
||||
public class PackageMigrationRunner
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IKeyValueService _keyValueService;
|
||||
private readonly IMigrationPlanExecutor _migrationPlanExecutor;
|
||||
private readonly Dictionary<string, PackageMigrationPlan> _packageMigrationPlans;
|
||||
private readonly PendingPackageMigrations _pendingPackageMigrations;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly ICoreScopeProvider _scopeProvider;
|
||||
private readonly PendingPackageMigrations _pendingPackageMigrations;
|
||||
private readonly IMigrationPlanExecutor _migrationPlanExecutor;
|
||||
private readonly IKeyValueService _keyValueService;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly Dictionary<string, PackageMigrationPlan> _packageMigrationPlans;
|
||||
|
||||
public PackageMigrationRunner(
|
||||
IProfilingLogger profilingLogger,
|
||||
@@ -53,7 +50,8 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
/// <returns></returns>
|
||||
public IEnumerable<ExecutedMigrationPlan> RunPackageMigrationsIfPending(string packageName)
|
||||
{
|
||||
IReadOnlyDictionary<string, string?>? keyValues = _keyValueService.FindByKeyPrefix(Constants.Conventions.Migrations.KeyValuePrefix);
|
||||
IReadOnlyDictionary<string, string?>? keyValues =
|
||||
_keyValueService.FindByKeyPrefix(Constants.Conventions.Migrations.KeyValuePrefix);
|
||||
IReadOnlyList<string> pendingMigrations = _pendingPackageMigrations.GetPendingPackageMigrations(keyValues);
|
||||
|
||||
IEnumerable<string> packagePlans = _packageMigrationPlans.Values
|
||||
@@ -93,6 +91,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
"Unattended upgrade completed for " + migrationName))
|
||||
{
|
||||
var upgrader = new Upgrader(plan);
|
||||
|
||||
// This may throw, if so the transaction will be rolled back
|
||||
results.Add(upgrader.Execute(_migrationPlanExecutor, _scopeProvider, _keyValueService));
|
||||
}
|
||||
@@ -105,4 +104,3 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core;
|
||||
@@ -12,18 +9,18 @@ using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
public class UnattendedInstaller : INotificationAsyncHandler<RuntimeUnattendedInstallNotification>
|
||||
{
|
||||
private readonly IOptions<UnattendedSettings> _unattendedSettings;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
|
||||
private readonly DatabaseSchemaCreatorFactory _databaseSchemaCreatorFactory;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IUmbracoDatabaseFactory _databaseFactory;
|
||||
private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ILogger<UnattendedInstaller> _logger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IOptions<UnattendedSettings> _unattendedSettings;
|
||||
|
||||
public UnattendedInstaller(
|
||||
DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory,
|
||||
@@ -34,7 +31,8 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
ILogger<UnattendedInstaller> logger,
|
||||
IRuntimeState runtimeState)
|
||||
{
|
||||
_databaseSchemaCreatorFactory = databaseSchemaCreatorFactory ?? throw new ArgumentNullException(nameof(databaseSchemaCreatorFactory));
|
||||
_databaseSchemaCreatorFactory = databaseSchemaCreatorFactory ??
|
||||
throw new ArgumentNullException(nameof(databaseSchemaCreatorFactory));
|
||||
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
|
||||
_unattendedSettings = unattendedSettings;
|
||||
_databaseFactory = databaseFactory;
|
||||
@@ -60,7 +58,9 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
_runtimeState.DetermineRuntimeLevel();
|
||||
if (_runtimeState.Reason == RuntimeLevelReason.InstallMissingDatabase)
|
||||
{
|
||||
_dbProviderFactoryCreator.CreateDatabase(_databaseFactory.ProviderName!, _databaseFactory.ConnectionString!);
|
||||
_dbProviderFactoryCreator.CreateDatabase(
|
||||
_databaseFactory.ProviderName!,
|
||||
_databaseFactory.ConnectionString!);
|
||||
}
|
||||
|
||||
bool connect;
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
_logger.LogInformation(ex, "Error during unattended install.");
|
||||
|
||||
var innerException = new UnattendedInstallException("Unattended installation failed.", ex);
|
||||
_runtimeState.Configure(Core.RuntimeLevel.BootFailed, Core.RuntimeLevelReason.BootFailedOnException, innerException);
|
||||
_runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
using (database = _databaseFactory.CreateDatabase())
|
||||
{
|
||||
var hasUmbracoTables = database?.IsUmbracoInstalled() ?? false;
|
||||
var hasUmbracoTables = database.IsUmbracoInstalled();
|
||||
|
||||
// database has umbraco tables, assume Umbraco is already installed
|
||||
if (hasUmbracoTables)
|
||||
@@ -110,10 +110,10 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
// all conditions fulfilled, do the install
|
||||
_logger.LogInformation("Starting unattended install.");
|
||||
|
||||
database?.BeginTransaction();
|
||||
database.BeginTransaction();
|
||||
DatabaseSchemaCreator creator = _databaseSchemaCreatorFactory.Create(database);
|
||||
creator.InitializeDatabaseSchema();
|
||||
database?.CompleteTransaction();
|
||||
database.CompleteTransaction();
|
||||
_logger.LogInformation("Unattended install completed.");
|
||||
|
||||
// Emit an event with EventAggregator that unattended install completed
|
||||
@@ -131,10 +131,9 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
+ "\n Please check log file for additional information (can be found in '/Umbraco/Data/Logs/')",
|
||||
ex);
|
||||
|
||||
_runtimeState.Configure(Core.RuntimeLevel.BootFailed, Core.RuntimeLevelReason.BootFailedOnException, innerException);
|
||||
_runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Exceptions;
|
||||
@@ -12,22 +9,20 @@ using Umbraco.Cms.Infrastructure.Migrations.Install;
|
||||
using Umbraco.Cms.Infrastructure.Migrations.Upgrade;
|
||||
using Umbraco.Cms.Infrastructure.Runtime;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Infrastructure.Migrations;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Install
|
||||
{
|
||||
namespace Umbraco.Cms.Infrastructure.Install;
|
||||
|
||||
/// <summary>
|
||||
/// Handles <see cref="RuntimeUnattendedUpgradeNotification" /> to execute the unattended Umbraco upgrader
|
||||
/// or the unattended Package migrations runner.
|
||||
/// </summary>
|
||||
public class UnattendedUpgrader : INotificationAsyncHandler<RuntimeUnattendedUpgradeNotification>
|
||||
{
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
private readonly DatabaseBuilder _databaseBuilder;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly PackageMigrationRunner _packageMigrationRunner;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly IUmbracoVersion _umbracoVersion;
|
||||
|
||||
public UnattendedUpgrader(
|
||||
IProfilingLogger profilingLogger,
|
||||
@@ -59,38 +54,49 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
DatabaseBuilder.Result? result = _databaseBuilder.UpgradeSchemaAndData(plan);
|
||||
if (result?.Success == false)
|
||||
{
|
||||
var innerException = new UnattendedInstallException("An error occurred while running the unattended upgrade.\n" + result.Message);
|
||||
_runtimeState.Configure(Core.RuntimeLevel.BootFailed, Core.RuntimeLevelReason.BootFailedOnException, innerException);
|
||||
var innerException = new UnattendedInstallException(
|
||||
"An error occurred while running the unattended upgrade.\n" + result.Message);
|
||||
_runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException);
|
||||
}
|
||||
|
||||
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete;
|
||||
notification.UnattendedUpgradeResult =
|
||||
RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case RuntimeLevelReason.UpgradePackageMigrations:
|
||||
{
|
||||
if (!_runtimeState.StartupState.TryGetValue(RuntimeState.PendingPackageMigrationsStateKey, out var pm)
|
||||
if (!_runtimeState.StartupState.TryGetValue(
|
||||
RuntimeState.PendingPackageMigrationsStateKey,
|
||||
out var pm)
|
||||
|| pm is not IReadOnlyList<string> pendingMigrations)
|
||||
{
|
||||
throw new InvalidOperationException($"The required key {RuntimeState.PendingPackageMigrationsStateKey} does not exist in startup state");
|
||||
throw new InvalidOperationException(
|
||||
$"The required key {RuntimeState.PendingPackageMigrationsStateKey} does not exist in startup state");
|
||||
}
|
||||
|
||||
if (pendingMigrations.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No pending migrations found but the runtime level reason is " + Core.RuntimeLevelReason.UpgradePackageMigrations);
|
||||
throw new InvalidOperationException(
|
||||
"No pending migrations found but the runtime level reason is " +
|
||||
RuntimeLevelReason.UpgradePackageMigrations);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IEnumerable<ExecutedMigrationPlan> result = _packageMigrationRunner.RunPackagePlans(pendingMigrations);
|
||||
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.PackageMigrationComplete;
|
||||
_packageMigrationRunner.RunPackagePlans(pendingMigrations);
|
||||
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult
|
||||
.PackageMigrationComplete;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SetRuntimeError(ex);
|
||||
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors;
|
||||
notification.UnattendedUpgradeResult =
|
||||
RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException("Invalid reason " + _runtimeState.Reason);
|
||||
@@ -106,4 +112,3 @@ namespace Umbraco.Cms.Infrastructure.Install
|
||||
RuntimeLevelReason.BootFailedOnException,
|
||||
exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Parsing;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Core.Logging
|
||||
{
|
||||
namespace Umbraco.Cms.Core.Logging;
|
||||
|
||||
public class MessageTemplates : IMessageTemplates
|
||||
{
|
||||
// Umbraco now uses Message Templates (https://messagetemplates.org/) for logging, which means
|
||||
@@ -17,18 +13,19 @@ namespace Umbraco.Cms.Core.Logging
|
||||
// means we cannot get rid of Serilog entirely. We may want to revisit this at some point.
|
||||
|
||||
// TODO: Do we still need this, is there a non-pre release package shipped?
|
||||
|
||||
private static readonly Lazy<global::Serilog.ILogger> MinimalLogger = new Lazy<global::Serilog.ILogger>(() => new LoggerConfiguration().CreateLogger());
|
||||
private static readonly Lazy<ILogger> _minimalLogger = new(() => new LoggerConfiguration().CreateLogger());
|
||||
|
||||
public string Render(string messageTemplate, params object[] args)
|
||||
{
|
||||
// resolve a minimal logger instance which is used to bind message templates
|
||||
var logger = MinimalLogger.Value;
|
||||
ILogger logger = _minimalLogger.Value;
|
||||
|
||||
var bound = logger.BindMessageTemplate(messageTemplate, args, out var parsedTemplate, out var boundProperties);
|
||||
var bound = logger.BindMessageTemplate(messageTemplate, args, out MessageTemplate? parsedTemplate, out IEnumerable<LogEventProperty>? boundProperties);
|
||||
|
||||
if (!bound)
|
||||
{
|
||||
throw new FormatException($"Could not format message \"{messageTemplate}\" with {args.Length} args.");
|
||||
}
|
||||
|
||||
var values = boundProperties.ToDictionary(x => x.Name, x => x.Value);
|
||||
|
||||
@@ -37,16 +34,20 @@ namespace Umbraco.Cms.Core.Logging
|
||||
|
||||
// this does not
|
||||
var tw = new StringWriter();
|
||||
foreach (var t in parsedTemplate.Tokens)
|
||||
foreach (MessageTemplateToken? t in parsedTemplate.Tokens)
|
||||
{
|
||||
if (t is PropertyToken pt &&
|
||||
values.TryGetValue(pt.PropertyName, out var propVal) &&
|
||||
values.TryGetValue(pt.PropertyName, out LogEventPropertyValue? propVal) &&
|
||||
(propVal as ScalarValue)?.Value is string s)
|
||||
{
|
||||
tw.Write(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
t.Render(values, tw);
|
||||
}
|
||||
}
|
||||
|
||||
return tw.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user