Merge branch 'netcore/dev' into netcore/members-roles-save
# Conflicts: # .gitignore # src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberIdentityUserManagerTests.cs # src/Umbraco.Web.BackOffice/DependencyInjection/UmbracoBuilderExtensions.cs # src/Umbraco.Web.Common/Security/MemberManager.cs
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -202,4 +202,5 @@ src/Umbraco.Tests/TEMP/
|
||||
/src/Umbraco.Web.UI/config/umbracoSettings.config
|
||||
/src/Umbraco.Web.UI.NetCore/Umbraco/models/*
|
||||
src/Umbraco.Tests.UnitTests/umbraco/Data/TEMP/
|
||||
/src/Umbraco.Web.UI.NetCore/appsettings.Local.json
|
||||
src/Umbraco.Tests.Integration/DatabaseContextTests.sdf
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class ApplicationCacheRefresher : CacheRefresherBase<ApplicationCacheRefresher>
|
||||
public sealed class ApplicationCacheRefresher : CacheRefresherBase<ApplicationCacheRefresherNotification>
|
||||
{
|
||||
public ApplicationCacheRefresher(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
{ }
|
||||
public ApplicationCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{
|
||||
}
|
||||
|
||||
#region Define
|
||||
|
||||
protected override ApplicationCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("B15F34A1-BC1D-4F8B-8369-3222728AB4C8");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class ApplicationCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public ApplicationCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
@@ -10,33 +10,22 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstanceType">The actual cache refresher type.</typeparam>
|
||||
/// <remarks>The actual cache refresher type is used for strongly typed events.</remarks>
|
||||
public abstract class CacheRefresherBase<TInstanceType> : ICacheRefresher
|
||||
where TInstanceType : class, ICacheRefresher
|
||||
public abstract class CacheRefresherBase<TNotification> : ICacheRefresher
|
||||
where TNotification : CacheRefresherNotification
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CacheRefresherBase{TInstanceType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected CacheRefresherBase(AppCaches appCaches)
|
||||
protected CacheRefresherBase(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
{
|
||||
AppCaches = appCaches;
|
||||
EventAggregator = eventAggregator;
|
||||
NotificationFactory = factory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when the cache is updated on the server.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Triggers on each server configured for an Umbraco project whenever a cache refresher is updated.
|
||||
/// </remarks>
|
||||
public static event TypedEventHandler<TInstanceType, CacheRefresherEventArgs> CacheUpdated;
|
||||
|
||||
#region Define
|
||||
|
||||
/// <summary>
|
||||
/// Gets the typed 'this' for events.
|
||||
/// </summary>
|
||||
protected abstract TInstanceType This { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique identifier of the refresher.
|
||||
/// </summary>
|
||||
@@ -47,6 +36,11 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TNotificationFactory"/> for <see cref="TNotification"/>
|
||||
/// </summary>
|
||||
protected ICacheRefresherNotificationFactory NotificationFactory { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Refresher
|
||||
@@ -56,7 +50,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
public virtual void RefreshAll()
|
||||
{
|
||||
OnCacheUpdated(This, new CacheRefresherEventArgs(null, MessageType.RefreshAll));
|
||||
OnCacheUpdated(NotificationFactory.Create<TNotification>(null, MessageType.RefreshAll));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,7 +59,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <param name="id">The entity's identifier.</param>
|
||||
public virtual void Refresh(int id)
|
||||
{
|
||||
OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RefreshById));
|
||||
OnCacheUpdated(NotificationFactory.Create<TNotification>(id, MessageType.RefreshById));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,7 +68,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <param name="id">The entity's identifier.</param>
|
||||
public virtual void Refresh(Guid id)
|
||||
{
|
||||
OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RefreshById));
|
||||
OnCacheUpdated(NotificationFactory.Create<TNotification>(id, MessageType.RefreshById));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,7 +77,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <param name="id">The entity's identifier.</param>
|
||||
public virtual void Remove(int id)
|
||||
{
|
||||
OnCacheUpdated(This, new CacheRefresherEventArgs(id, MessageType.RemoveById));
|
||||
OnCacheUpdated(NotificationFactory.Create<TNotification>(id, MessageType.RemoveById));
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -95,6 +89,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
protected AppCaches AppCaches { get; }
|
||||
|
||||
protected IEventAggregator EventAggregator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cache for all repository entities of a specified type.
|
||||
/// </summary>
|
||||
@@ -110,9 +106,9 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
protected static void OnCacheUpdated(TInstanceType sender, CacheRefresherEventArgs args)
|
||||
protected void OnCacheUpdated(CacheRefresherNotification notification)
|
||||
{
|
||||
CacheUpdated?.Invoke(sender, args);
|
||||
EventAggregator.Publish(notification);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args for cache refresher updates
|
||||
/// </summary>
|
||||
public class CacheRefresherEventArgs : EventArgs
|
||||
{
|
||||
public CacheRefresherEventArgs(object msgObject, MessageType type)
|
||||
{
|
||||
MessageType = type;
|
||||
MessageObject = msgObject;
|
||||
}
|
||||
public object MessageObject { get; private set; }
|
||||
public MessageType MessageType { get; private set; }
|
||||
}
|
||||
}
|
||||
22
src/Umbraco.Core/Cache/CacheRefresherNotification.cs
Normal file
22
src/Umbraco.Core/Cache/CacheRefresherNotification.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for cache refresher notifications
|
||||
/// </summary>
|
||||
public abstract class CacheRefresherNotification : INotification
|
||||
{
|
||||
public CacheRefresherNotification(object messageObject, MessageType messageType)
|
||||
{
|
||||
MessageObject = messageObject ?? throw new ArgumentNullException(nameof(messageObject));
|
||||
MessageType = messageType;
|
||||
}
|
||||
|
||||
public object MessageObject { get; }
|
||||
public MessageType MessageType { get; }
|
||||
}
|
||||
}
|
||||
23
src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs
Normal file
23
src/Umbraco.Core/Cache/CacheRefresherNotificationFactory.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using Umbraco.Extensions;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="ICacheRefresherNotificationFactory"/> that uses ActivatorUtilities to create the <see cref="CacheRefresherNotification"/> instances
|
||||
/// </summary>
|
||||
public sealed class CacheRefresherNotificationFactory : ICacheRefresherNotificationFactory
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public CacheRefresherNotificationFactory(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="CacheRefresherNotification"/> using ActivatorUtilities
|
||||
/// </summary>
|
||||
/// <typeparam name="TNotification">The <see cref="CacheRefresherNotification"/> to create</typeparam>
|
||||
public TNotification Create<TNotification>(object msgObject, MessageType type) where TNotification : CacheRefresherNotification
|
||||
=> _serviceProvider.CreateInstance<TNotification>(new object[] { msgObject, type });
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
@@ -11,14 +12,21 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCacheRefresher, ContentCacheRefresher.JsonPayload>
|
||||
public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCacheRefresherNotification, ContentCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
private readonly IDomainService _domainService;
|
||||
|
||||
public ContentCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IDomainService domainService)
|
||||
: base(appCaches, serializer)
|
||||
public ContentCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IIdKeyMap idKeyMap,
|
||||
IDomainService domainService,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_idKeyMap = idKeyMap;
|
||||
@@ -27,8 +35,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region Define
|
||||
|
||||
protected override ContentCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("900A4FBE-DF3C-41E6-BB77-BE896CD158EA");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/ContentCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class ContentCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public ContentCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
@@ -11,15 +12,23 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase<ContentTypeCacheRefresher, ContentTypeCacheRefresher.JsonPayload>
|
||||
public sealed class ContentTypeCacheRefresher : PayloadCacheRefresherBase<ContentTypeCacheRefresherNotification, ContentTypeCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||
private readonly IContentTypeCommonRepository _contentTypeCommonRepository;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
public ContentTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository)
|
||||
: base(appCaches, serializer)
|
||||
public ContentTypeCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IPublishedModelFactory publishedModelFactory,
|
||||
IIdKeyMap idKeyMap,
|
||||
IContentTypeCommonRepository contentTypeCommonRepository,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_publishedModelFactory = publishedModelFactory;
|
||||
@@ -29,8 +38,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region Define
|
||||
|
||||
protected override ContentTypeCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("6902E22C-9C10-483C-91F3-66B7CAE9E2F5");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class ContentTypeCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public ContentTypeCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PropertyEditors.ValueConverters;
|
||||
@@ -9,14 +10,21 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class DataTypeCacheRefresher : PayloadCacheRefresherBase<DataTypeCacheRefresher, DataTypeCacheRefresher.JsonPayload>
|
||||
public sealed class DataTypeCacheRefresher : PayloadCacheRefresherBase<DataTypeCacheRefresherNotification, DataTypeCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
public DataTypeCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IPublishedModelFactory publishedModelFactory, IIdKeyMap idKeyMap)
|
||||
: base(appCaches, serializer)
|
||||
public DataTypeCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IPublishedModelFactory publishedModelFactory,
|
||||
IIdKeyMap idKeyMap,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_publishedModelFactory = publishedModelFactory;
|
||||
@@ -25,8 +33,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region Define
|
||||
|
||||
protected override DataTypeCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("35B16C25-A17E-45D7-BC8F-EDAB1DCC28D2");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/DataTypeCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class DataTypeCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public DataTypeCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class DictionaryCacheRefresher : CacheRefresherBase<DictionaryCacheRefresher>
|
||||
public sealed class DictionaryCacheRefresher : CacheRefresherBase<DictionaryCacheRefresherNotification>
|
||||
{
|
||||
public DictionaryCacheRefresher(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
public DictionaryCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator , factory)
|
||||
{ }
|
||||
|
||||
#region Define
|
||||
|
||||
protected override DictionaryCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("D1D7E227-F817-4816-BFE9-6C39B6152884");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class DictionaryCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public DictionaryCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
@@ -6,20 +7,23 @@ using Umbraco.Cms.Core.Services.Changes;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class DomainCacheRefresher : PayloadCacheRefresherBase<DomainCacheRefresher, DomainCacheRefresher.JsonPayload>
|
||||
public sealed class DomainCacheRefresher : PayloadCacheRefresherBase<DomainCacheRefresherNotification, DomainCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
|
||||
public DomainCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService)
|
||||
: base(appCaches, serializer)
|
||||
public DomainCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
}
|
||||
|
||||
#region Define
|
||||
|
||||
protected override DomainCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("11290A79-4B57-4C99-AD72-7748A3CF38AF");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/DomainCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class DomainCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public DomainCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs
Normal file
16
src/Umbraco.Core/Cache/ICacheRefresherNotificationFactory.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// Factory for creating cache refresher notification instances
|
||||
/// </summary>
|
||||
public interface ICacheRefresherNotificationFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a <see cref="ICacheRefresherNotification"/>
|
||||
/// </summary>
|
||||
/// <typeparam name="TNotification">The <see cref="ICacheRefresherNotification"/> to create</typeparam>
|
||||
TNotification Create<TNotification>(object msgObject, MessageType type) where TNotification : CacheRefresherNotification;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
@@ -8,8 +9,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
/// <typeparam name="TInstanceType">The actual cache refresher type.</typeparam>
|
||||
/// <remarks>The actual cache refresher type is used for strongly typed events.</remarks>
|
||||
public abstract class JsonCacheRefresherBase<TInstanceType, TJsonPayload> : CacheRefresherBase<TInstanceType>, IJsonCacheRefresher
|
||||
where TInstanceType : class, ICacheRefresher
|
||||
public abstract class JsonCacheRefresherBase<TNotification, TJsonPayload> : CacheRefresherBase<TNotification>, IJsonCacheRefresher
|
||||
where TNotification : CacheRefresherNotification
|
||||
{
|
||||
protected IJsonSerializer JsonSerializer { get; }
|
||||
|
||||
@@ -17,7 +18,12 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// Initializes a new instance of the <see cref="JsonCacheRefresherBase{TInstanceType}"/>.
|
||||
/// </summary>
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
protected JsonCacheRefresherBase(AppCaches appCaches, IJsonSerializer jsonSerializer) : base(appCaches)
|
||||
protected JsonCacheRefresherBase(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{
|
||||
JsonSerializer = jsonSerializer;
|
||||
}
|
||||
@@ -28,7 +34,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <param name="json">The json payload.</param>
|
||||
public virtual void Refresh(string json)
|
||||
{
|
||||
OnCacheUpdated(This, new CacheRefresherEventArgs(json, MessageType.RefreshByJson));
|
||||
OnCacheUpdated(NotificationFactory.Create<TNotification>(json, MessageType.RefreshByJson));
|
||||
}
|
||||
|
||||
#region Json
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
@@ -7,18 +8,21 @@ using static Umbraco.Cms.Core.Cache.LanguageCacheRefresher.JsonPayload;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase<LanguageCacheRefresher, LanguageCacheRefresher.JsonPayload>
|
||||
public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase<LanguageCacheRefresherNotification, LanguageCacheRefresher.JsonPayload>
|
||||
{
|
||||
public LanguageCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService)
|
||||
: base(appCaches, serializer)
|
||||
public LanguageCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer serializer,
|
||||
IPublishedSnapshotService publishedSnapshotService,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
}
|
||||
|
||||
#region Define
|
||||
|
||||
protected override LanguageCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("3E0F95D8-0BE5-44B8-8394-2B8750B62654");
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
|
||||
|
||||
11
src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/LanguageCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class LanguageCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public LanguageCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,26 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class MacroCacheRefresher : PayloadCacheRefresherBase<MacroCacheRefresher, MacroCacheRefresher.JsonPayload>
|
||||
public sealed class MacroCacheRefresher : PayloadCacheRefresherBase<MacroCacheRefresherNotification, MacroCacheRefresher.JsonPayload>
|
||||
{
|
||||
public MacroCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer)
|
||||
: base(appCaches, jsonSerializer)
|
||||
public MacroCacheRefresher(
|
||||
AppCaches appCaches,
|
||||
IJsonSerializer jsonSerializer,
|
||||
IEventAggregator eventAggregator,
|
||||
ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, jsonSerializer, eventAggregator, factory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region Define
|
||||
|
||||
protected override MacroCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("7B1E683C-5F34-43dd-803D-9699EA1E98CA");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/MacroCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class MacroCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public MacroCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
@@ -9,13 +10,13 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class MediaCacheRefresher : PayloadCacheRefresherBase<MediaCacheRefresher, MediaCacheRefresher.JsonPayload>
|
||||
public sealed class MediaCacheRefresher : PayloadCacheRefresherBase<MediaCacheRefresherNotification, MediaCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap)
|
||||
: base(appCaches, serializer)
|
||||
public MediaCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IPublishedSnapshotService publishedSnapshotService, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_idKeyMap = idKeyMap;
|
||||
@@ -23,8 +24,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region Define
|
||||
|
||||
protected override MediaCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("B29286DD-2D40-4DDB-B325-681226589FEC");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/MediaCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class MediaCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public MediaCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//using Newtonsoft.Json;
|
||||
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
@@ -9,12 +10,12 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCacheRefresher, MemberCacheRefresher.JsonPayload>
|
||||
public sealed class MemberCacheRefresher : PayloadCacheRefresherBase<MemberCacheRefresherNotification, MemberCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
|
||||
public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap)
|
||||
: base(appCaches, serializer)
|
||||
public MemberCacheRefresher(AppCaches appCaches, IJsonSerializer serializer, IIdKeyMap idKeyMap, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
_idKeyMap = idKeyMap;
|
||||
}
|
||||
@@ -36,8 +37,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region Define
|
||||
|
||||
protected override MemberCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("E285DF34-ACDC-4226-AE32-C0CB5CF388DA");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/MemberCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class MemberCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public MemberCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class MemberGroupCacheRefresher : PayloadCacheRefresherBase<MemberGroupCacheRefresher, MemberGroupCacheRefresher.JsonPayload>
|
||||
public sealed class MemberGroupCacheRefresher : PayloadCacheRefresherBase<MemberGroupCacheRefresherNotification, MemberGroupCacheRefresher.JsonPayload>
|
||||
{
|
||||
public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer)
|
||||
: base(appCaches, jsonSerializer)
|
||||
public MemberGroupCacheRefresher(AppCaches appCaches, IJsonSerializer jsonSerializer, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, jsonSerializer, eventAggregator, factory)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#region Define
|
||||
|
||||
protected override MemberGroupCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("187F236B-BD21-4C85-8A7C-29FBA3D6C00C");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class MemberGroupCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public MemberGroupCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
@@ -9,8 +10,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <typeparam name="TInstanceType">The actual cache refresher type.</typeparam>
|
||||
/// <typeparam name="TPayload">The payload type.</typeparam>
|
||||
/// <remarks>The actual cache refresher type is used for strongly typed events.</remarks>
|
||||
public abstract class PayloadCacheRefresherBase<TInstanceType, TPayload> : JsonCacheRefresherBase<TInstanceType, TPayload>, IPayloadCacheRefresher<TPayload>
|
||||
where TInstanceType : class, ICacheRefresher
|
||||
public abstract class PayloadCacheRefresherBase<TNotification, TPayload> : JsonCacheRefresherBase<TNotification, TPayload>, IPayloadCacheRefresher<TPayload>
|
||||
where TNotification : CacheRefresherNotification
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
@@ -18,7 +19,8 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// </summary>
|
||||
/// <param name="appCaches">A cache helper.</param>
|
||||
/// <param name="serializer"></param>
|
||||
protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer) : base(appCaches, serializer)
|
||||
protected PayloadCacheRefresherBase(AppCaches appCaches, IJsonSerializer serializer, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, serializer, eventAggregator, factory)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,7 +39,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <param name="payloads">The payload.</param>
|
||||
public virtual void Refresh(TPayload[] payloads)
|
||||
{
|
||||
OnCacheUpdated(This, new CacheRefresherEventArgs(payloads, MessageType.RefreshByPayload));
|
||||
OnCacheUpdated(NotificationFactory.Create<TNotification>(payloads, MessageType.RefreshByPayload));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class PublicAccessCacheRefresher : CacheRefresherBase<PublicAccessCacheRefresher>
|
||||
public sealed class PublicAccessCacheRefresher : CacheRefresherBase<PublicAccessCacheRefresherNotification>
|
||||
{
|
||||
public PublicAccessCacheRefresher(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
public PublicAccessCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{ }
|
||||
|
||||
#region Define
|
||||
|
||||
protected override PublicAccessCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("1DB08769-B104-4F8B-850E-169CAC1DF2EC");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class PublicAccessCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public PublicAccessCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class RelationTypeCacheRefresher : CacheRefresherBase<RelationTypeCacheRefresher>
|
||||
public sealed class RelationTypeCacheRefresher : CacheRefresherBase<RelationTypeCacheRefresherNotification>
|
||||
{
|
||||
public RelationTypeCacheRefresher(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
public RelationTypeCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{ }
|
||||
|
||||
#region Define
|
||||
|
||||
protected override RelationTypeCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("D8375ABA-4FB3-4F86-B505-92FBA1B6F7C9");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class RelationTypeCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public RelationTypeCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,18 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class TemplateCacheRefresher : CacheRefresherBase<TemplateCacheRefresher>
|
||||
public sealed class TemplateCacheRefresher : CacheRefresherBase<TemplateCacheRefresherNotification>
|
||||
{
|
||||
private readonly IIdKeyMap _idKeyMap;
|
||||
private readonly IContentTypeCommonRepository _contentTypeCommonRepository;
|
||||
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository)
|
||||
: base(appCaches)
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IIdKeyMap idKeyMap, IContentTypeCommonRepository contentTypeCommonRepository, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{
|
||||
_idKeyMap = idKeyMap;
|
||||
_contentTypeCommonRepository = contentTypeCommonRepository;
|
||||
@@ -19,8 +20,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
|
||||
#region Define
|
||||
|
||||
protected override TemplateCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("DD12B6A0-14B9-46e8-8800-C154F74047C8");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
11
src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/TemplateCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class TemplateCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public TemplateCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public sealed class UserCacheRefresher : CacheRefresherBase<UserCacheRefresher>
|
||||
public sealed class UserCacheRefresher : CacheRefresherBase<UserCacheRefresherNotification>
|
||||
{
|
||||
public UserCacheRefresher(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
public UserCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{ }
|
||||
|
||||
#region Define
|
||||
|
||||
protected override UserCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("E057AF6D-2EE6-41F4-8045-3694010F0AA6");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
@@ -47,7 +46,7 @@ namespace Umbraco.Cms.Core.Cache
|
||||
userCache.Result.ClearByKey(CacheKeys.UserAllContentStartNodesPrefix + id);
|
||||
userCache.Result.ClearByKey(CacheKeys.UserAllMediaStartNodesPrefix + id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
11
src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs
Normal file
11
src/Umbraco.Core/Cache/UserCacheRefresherNotification.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class UserCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public UserCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Persistence.Repositories;
|
||||
|
||||
@@ -10,16 +11,14 @@ namespace Umbraco.Cms.Core.Cache
|
||||
/// <remarks>
|
||||
/// This also needs to clear the user cache since IReadOnlyUserGroup's are attached to IUser objects
|
||||
/// </remarks>
|
||||
public sealed class UserGroupCacheRefresher : CacheRefresherBase<UserGroupCacheRefresher>
|
||||
public sealed class UserGroupCacheRefresher : CacheRefresherBase<UserGroupCacheRefresherNotification>
|
||||
{
|
||||
public UserGroupCacheRefresher(AppCaches appCaches)
|
||||
: base(appCaches)
|
||||
public UserGroupCacheRefresher(AppCaches appCaches, IEventAggregator eventAggregator, ICacheRefresherNotificationFactory factory)
|
||||
: base(appCaches, eventAggregator, factory)
|
||||
{ }
|
||||
|
||||
#region Define
|
||||
|
||||
protected override UserGroupCacheRefresher This => this;
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("45178038-B232-4FE8-AA1A-F2B949C44762");
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using Umbraco.Cms.Core.Sync;
|
||||
|
||||
namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
public class UserGroupCacheRefresherNotification : CacheRefresherNotification
|
||||
{
|
||||
public UserGroupCacheRefresherNotification(object messageObject, MessageType messageType) : base(messageObject, messageType)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,6 +189,7 @@ namespace Umbraco.Cms.Core.DependencyInjection
|
||||
|
||||
// register distributed cache
|
||||
Services.AddUnique(f => new DistributedCache(f.GetRequiredService<IServerMessenger>(), f.GetRequiredService<CacheRefresherCollection>()));
|
||||
Services.AddUnique<ICacheRefresherNotificationFactory, CacheRefresherNotificationFactory>();
|
||||
|
||||
// register the http context and umbraco context accessors
|
||||
// we *should* use the HttpContextUmbracoContextAccessor, however there are cases when
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents event data for the Published event.
|
||||
/// </summary>
|
||||
public class ContentPublishedEventArgs : PublishEventArgs<IContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPublishedEventArgs"/> class.
|
||||
/// </summary>
|
||||
public ContentPublishedEventArgs(IEnumerable<IContent> eventObject, bool canCancel, EventMessages eventMessages)
|
||||
: base(eventObject, canCancel, eventMessages)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a culture has been published, during a Published event.
|
||||
/// </summary>
|
||||
public bool HasPublishedCulture(IContent content, string culture)
|
||||
=> content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.ChangedCulture + culture);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a culture has been unpublished, during a Published event.
|
||||
/// </summary>
|
||||
public bool HasUnpublishedCulture(IContent content, string culture)
|
||||
=> content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture);
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents event data for the Publishing event.
|
||||
/// </summary>
|
||||
public class ContentPublishingEventArgs : PublishEventArgs<IContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentPublishingEventArgs"/> class.
|
||||
/// </summary>
|
||||
public ContentPublishingEventArgs(IEnumerable<IContent> eventObject, EventMessages eventMessages)
|
||||
: base(eventObject, eventMessages)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a culture is being published, during a Publishing event.
|
||||
/// </summary>
|
||||
public bool IsPublishingCulture(IContent content, string culture)
|
||||
=> content.PublishCultureInfos.TryGetValue(culture, out var cultureInfo) && cultureInfo.IsDirty();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a culture is being unpublished, during a Publishing event.
|
||||
/// </summary>
|
||||
public bool IsUnpublishingCulture(IContent content, string culture)
|
||||
=> content.IsPropertyDirty(ContentBase.ChangeTrackingPrefix.UnpublishedCulture + culture); //bit of a hack since we know that the content implementation tracks changes this way
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents event data for the Saved event.
|
||||
/// </summary>
|
||||
public class ContentSavedEventArgs : SaveEventArgs<IContent>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentSavedEventArgs"/> class.
|
||||
/// </summary>
|
||||
public ContentSavedEventArgs(IEnumerable<IContent> eventObject, EventMessages messages, IDictionary<string, object> additionalData)
|
||||
: base(eventObject, false, messages, additionalData)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a culture has been saved, during a Saved event.
|
||||
/// </summary>
|
||||
public bool HasSavedCulture(IContent content, string culture)
|
||||
=> content.WasPropertyDirty(ContentBase.ChangeTrackingPrefix.UpdatedCulture + culture);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent event data for the Saving event.
|
||||
/// </summary>
|
||||
public class ContentSavingEventArgs : SaveEventArgs<IContent>
|
||||
{
|
||||
#region Factory Methods
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="ContentSavingEventArgs"/> to <see cref="ContentSavedEventArgs"/> while preserving all args state
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ContentSavedEventArgs ToContentSavedEventArgs()
|
||||
{
|
||||
return new ContentSavedEventArgs(EventObject, Messages, AdditionalData)
|
||||
{
|
||||
EventState = EventState
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="ContentSavingEventArgs"/> to <see cref="ContentPublishedEventArgs"/> while preserving all args state
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ContentPublishedEventArgs ToContentPublishedEventArgs()
|
||||
{
|
||||
return new ContentPublishedEventArgs(EventObject, false, Messages)
|
||||
{
|
||||
EventState = EventState,
|
||||
AdditionalData = AdditionalData
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts <see cref="ContentSavingEventArgs"/> to <see cref="ContentPublishingEventArgs"/> while preserving all args state
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ContentPublishingEventArgs ToContentPublishingEventArgs()
|
||||
{
|
||||
return new ContentPublishingEventArgs(EventObject, Messages)
|
||||
{
|
||||
EventState = EventState,
|
||||
AdditionalData = AdditionalData
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentSavingEventArgs"/> class.
|
||||
/// </summary>
|
||||
public ContentSavingEventArgs(IEnumerable<IContent> eventObject, EventMessages eventMessages)
|
||||
: base(eventObject, eventMessages)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentSavingEventArgs"/> class.
|
||||
/// </summary>
|
||||
public ContentSavingEventArgs(IContent eventObject, EventMessages eventMessages)
|
||||
: base(eventObject, eventMessages)
|
||||
{ }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a culture is being saved, during a Saving event.
|
||||
/// </summary>
|
||||
public bool IsSavingCulture(IContent content, string culture)
|
||||
=> content.CultureInfos.TryGetValue(culture, out var cultureInfo) && cultureInfo.IsDirty();
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class DeleteRevisionsEventArgs : DeleteEventArgs, IEquatable<DeleteRevisionsEventArgs>
|
||||
{
|
||||
public DeleteRevisionsEventArgs(int id, bool canCancel, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default)
|
||||
: base(id, canCancel)
|
||||
{
|
||||
DeletePriorVersions = deletePriorVersions;
|
||||
SpecificVersion = specificVersion;
|
||||
DateToRetain = dateToRetain;
|
||||
}
|
||||
|
||||
public DeleteRevisionsEventArgs(int id, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default)
|
||||
: base(id)
|
||||
{
|
||||
DeletePriorVersions = deletePriorVersions;
|
||||
SpecificVersion = specificVersion;
|
||||
DateToRetain = dateToRetain;
|
||||
}
|
||||
|
||||
public bool DeletePriorVersions { get; }
|
||||
public int SpecificVersion { get; }
|
||||
public DateTime DateToRetain { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if we are deleting a specific revision
|
||||
/// </summary>
|
||||
public bool IsDeletingSpecificRevision => SpecificVersion != default;
|
||||
|
||||
public bool Equals(DeleteRevisionsEventArgs other)
|
||||
{
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return base.Equals(other) && DateToRetain.Equals(other.DateToRetain) && DeletePriorVersions == other.DeletePriorVersions && SpecificVersion.Equals(other.SpecificVersion);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj)) return false;
|
||||
if (ReferenceEquals(this, obj)) return true;
|
||||
if (obj.GetType() != GetType()) return false;
|
||||
return Equals((DeleteRevisionsEventArgs) obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hashCode = base.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ DateToRetain.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ DeletePriorVersions.GetHashCode();
|
||||
hashCode = (hashCode * 397) ^ SpecificVersion.GetHashCode();
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
public static bool operator !=(DeleteRevisionsEventArgs left, DeleteRevisionsEventArgs right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/Umbraco.Core/Events/ICancelableNotification.cs
Normal file
10
src/Umbraco.Core/Events/ICancelableNotification.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public interface ICancelableNotification : INotification
|
||||
{
|
||||
bool Cancel { get; set; }
|
||||
}
|
||||
}
|
||||
37
src/Umbraco.Core/Events/IScopedNotificationPublisher.cs
Normal file
37
src/Umbraco.Core/Events/IScopedNotificationPublisher.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public interface IScopedNotificationPublisher
|
||||
{
|
||||
/// <summary>
|
||||
/// Publishes a cancelable notification to the notification subscribers
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
/// <returns>True if the notification was cancelled by a subscriber, false otherwise</returns>
|
||||
bool PublishCancelable(ICancelableNotification notification);
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a cancelable notification to the notification subscribers
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
/// <returns>True if the notification was cancelled by a subscriber, false otherwise</returns>
|
||||
Task<bool> PublishCancelableAsync(ICancelableNotification notification);
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a notification to the notification subscribers
|
||||
/// </summary>
|
||||
/// <param name="notification"></param>
|
||||
/// <remarks>The notification is published upon successful completion of the current scope, i.e. when things have been saved/published/deleted etc.</remarks>
|
||||
void Publish(INotification notification);
|
||||
|
||||
/// <summary>
|
||||
/// Invokes publishing of all pending notifications within the current scope
|
||||
/// </summary>
|
||||
/// <param name="completed"></param>
|
||||
void ScopeExit(bool completed);
|
||||
}
|
||||
}
|
||||
9
src/Umbraco.Core/Events/IStatefulNotification.cs
Normal file
9
src/Umbraco.Core/Events/IStatefulNotification.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public interface IStatefulNotification : INotification
|
||||
{
|
||||
IDictionary<string, object> State { get; set; }
|
||||
}
|
||||
}
|
||||
17
src/Umbraco.Core/Events/NotificationExtensions.cs
Normal file
17
src/Umbraco.Core/Events/NotificationExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public static class NotificationExtensions
|
||||
{
|
||||
public static T WithState<T>(this T notification, IDictionary<string, object> state) where T : IStatefulNotification
|
||||
{
|
||||
notification.State = state;
|
||||
return notification;
|
||||
}
|
||||
|
||||
public static T WithStateFrom<T, TSource>(this T notification, TSource source)
|
||||
where T : IStatefulNotification where TSource : IStatefulNotification
|
||||
=> notification.WithState(source.State);
|
||||
}
|
||||
}
|
||||
71
src/Umbraco.Core/Events/ScopedNotificationPublisher.cs
Normal file
71
src/Umbraco.Core/Events/ScopedNotificationPublisher.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class ScopedNotificationPublisher : IScopedNotificationPublisher
|
||||
{
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly List<INotification> _notificationOnScopeCompleted;
|
||||
|
||||
public ScopedNotificationPublisher(IEventAggregator eventAggregator)
|
||||
{
|
||||
_eventAggregator = eventAggregator;
|
||||
_notificationOnScopeCompleted = new List<INotification>();
|
||||
}
|
||||
|
||||
public bool PublishCancelable(ICancelableNotification notification)
|
||||
{
|
||||
if (notification == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(notification));
|
||||
}
|
||||
|
||||
_eventAggregator.Publish(notification);
|
||||
return notification.Cancel;
|
||||
}
|
||||
|
||||
public async Task<bool> PublishCancelableAsync(ICancelableNotification notification)
|
||||
{
|
||||
if (notification == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(notification));
|
||||
}
|
||||
|
||||
await _eventAggregator.PublishAsync(notification);
|
||||
return notification.Cancel;
|
||||
}
|
||||
|
||||
public void Publish(INotification notification)
|
||||
{
|
||||
if (notification == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(notification));
|
||||
}
|
||||
|
||||
_notificationOnScopeCompleted.Add(notification);
|
||||
}
|
||||
|
||||
public void ScopeExit(bool completed)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
foreach (var notification in _notificationOnScopeCompleted)
|
||||
{
|
||||
_eventAggregator.Publish(notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_notificationOnScopeCompleted.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,6 +101,12 @@ namespace Umbraco.Extensions
|
||||
/// <returns>True if ClaimsIdentity</returns>
|
||||
public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity verifiedIdentity)
|
||||
{
|
||||
if (identity is null)
|
||||
{
|
||||
verifiedIdentity = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate that all required claims exist
|
||||
foreach (var claimType in RequiredBackOfficeClaimTypes)
|
||||
{
|
||||
@@ -112,7 +118,7 @@ namespace Umbraco.Extensions
|
||||
}
|
||||
}
|
||||
|
||||
verifiedIdentity = new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType);
|
||||
verifiedIdentity = identity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType ? identity : new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,31 +7,53 @@ using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
|
||||
namespace Umbraco.Extensions
|
||||
{
|
||||
public static class ClaimsPrincipalExtensions
|
||||
{
|
||||
|
||||
public static bool IsBackOfficeAuthenticationType(this ClaimsIdentity claimsIdentity)
|
||||
{
|
||||
if (claimsIdentity is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return claimsIdentity.IsAuthenticated && claimsIdentity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType;
|
||||
}
|
||||
/// <summary>
|
||||
/// This will return the current back office identity if the IPrincipal is the correct type and authenticated.
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="principal"></param>
|
||||
/// <returns></returns>
|
||||
public static ClaimsIdentity GetUmbracoIdentity(this IPrincipal user)
|
||||
public static ClaimsIdentity GetUmbracoIdentity(this IPrincipal principal)
|
||||
{
|
||||
// Check if the identity is a ClaimsIdentity, and that's it's authenticated and has all required claims.
|
||||
if (user.Identity is ClaimsIdentity claimsIdentity
|
||||
&& claimsIdentity.IsAuthenticated
|
||||
&& claimsIdentity.VerifyBackOfficeIdentity(out ClaimsIdentity umbracoIdentity))
|
||||
//If it's already a UmbracoBackOfficeIdentity
|
||||
if (principal.Identity is ClaimsIdentity claimsIdentity
|
||||
&& claimsIdentity.IsBackOfficeAuthenticationType()
|
||||
&& claimsIdentity.VerifyBackOfficeIdentity(out var backOfficeIdentity))
|
||||
{
|
||||
if (claimsIdentity.AuthenticationType == Constants.Security.BackOfficeAuthenticationType)
|
||||
{
|
||||
return claimsIdentity;
|
||||
}
|
||||
return umbracoIdentity;
|
||||
return backOfficeIdentity;
|
||||
}
|
||||
|
||||
//Check if there's more than one identity assigned and see if it's a UmbracoBackOfficeIdentity and use that
|
||||
// We can have assigned more identities if it is a preview request.
|
||||
if (principal is ClaimsPrincipal claimsPrincipal )
|
||||
{
|
||||
claimsIdentity = claimsPrincipal.Identities.FirstOrDefault(x=>x.IsBackOfficeAuthenticationType());
|
||||
if (claimsIdentity.VerifyBackOfficeIdentity(out backOfficeIdentity))
|
||||
{
|
||||
return backOfficeIdentity;
|
||||
}
|
||||
}
|
||||
|
||||
//Otherwise convert to a UmbracoBackOfficeIdentity if it's auth'd
|
||||
if (principal.Identity is ClaimsIdentity claimsIdentity2
|
||||
&& claimsIdentity2.VerifyBackOfficeIdentity(out backOfficeIdentity))
|
||||
{
|
||||
return backOfficeIdentity;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -126,10 +126,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
() => MediaService.TreeChanged -= MediaService_TreeChanged);
|
||||
|
||||
// bind to content events
|
||||
Bind(() => ContentService.Saved += ContentService_Saved, // needed for permissions
|
||||
() => ContentService.Saved -= ContentService_Saved);
|
||||
Bind(() => ContentService.Copied += ContentService_Copied, // needed for permissions
|
||||
() => ContentService.Copied -= ContentService_Copied);
|
||||
Bind(() => ContentService.TreeChanged += ContentService_TreeChanged,// handles all content changes
|
||||
() => ContentService.TreeChanged -= ContentService_TreeChanged);
|
||||
|
||||
@@ -182,32 +178,11 @@ namespace Umbraco.Cms.Core.Cache
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles cache refreshing for when content is saved (not published)
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
/// <remarks>
|
||||
/// When an entity is saved we need to notify other servers about the change in order for the Examine indexes to
|
||||
/// stay up-to-date for unpublished content.
|
||||
/// </remarks>
|
||||
private void ContentService_Saved(IContentService sender, SaveEventArgs<IContent> e)
|
||||
{
|
||||
}
|
||||
|
||||
private void ContentService_TreeChanged(IContentService sender, TreeChange<IContent>.EventArgs args)
|
||||
{
|
||||
_distributedCache.RefreshContentCache(args.Changes.ToArray());
|
||||
}
|
||||
|
||||
// TODO: our weird events handling wants this for now
|
||||
private void ContentService_Deleted(IContentService sender, DeleteEventArgs<IContent> e) { }
|
||||
private void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> e) { }
|
||||
private void ContentService_Trashed(IContentService sender, MoveEventArgs<IContent> e) { }
|
||||
private void ContentService_EmptiedRecycleBin(IContentService sender, RecycleBinEventArgs e) { }
|
||||
private void ContentService_Published(IContentService sender, PublishEventArgs<IContent> e) { }
|
||||
private void ContentService_Unpublished(IContentService sender, PublishEventArgs<IContent> e) { }
|
||||
|
||||
//private void ContentService_SavedBlueprint(IContentService sender, SaveEventArgs<IContent> e)
|
||||
//{
|
||||
// _distributedCache.RefreshUnpublishedPageCache(e.SavedEntities.ToArray());
|
||||
@@ -413,13 +388,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
_distributedCache.RefreshMediaCache(args.Changes.ToArray());
|
||||
}
|
||||
|
||||
// TODO: our weird events handling wants this for now
|
||||
private void MediaService_Saved(IMediaService sender, SaveEventArgs<IMedia> e) { }
|
||||
private void MediaService_Deleted(IMediaService sender, DeleteEventArgs<IMedia> e) { }
|
||||
private void MediaService_Moved(IMediaService sender, MoveEventArgs<IMedia> e) { }
|
||||
private void MediaService_Trashed(IMediaService sender, MoveEventArgs<IMedia> e) { }
|
||||
private void MediaService_EmptiedRecycleBin(IMediaService sender, RecycleBinEventArgs e) { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region MemberService
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
/// <summary>
|
||||
/// A composer for Block editors to run a component
|
||||
/// </summary>
|
||||
public class BlockEditorComposer : ComponentComposer<BlockEditorComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
/// <summary>
|
||||
/// A composer for nested content to run a component
|
||||
/// </summary>
|
||||
public class NestedContentPropertyComposer : ComponentComposer<NestedContentPropertyComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -13,7 +13,6 @@ using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
@@ -22,6 +21,9 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
/// <remarks>
|
||||
/// TODO: this component must be removed entirely - there is some code duplication in <see cref="UserNotificationsHandler"/> in anticipation of this component being deleted
|
||||
/// </remarks>
|
||||
public sealed class NotificationsComponent : IComponent
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
@@ -37,24 +39,6 @@ namespace Umbraco.Cms.Core.Compose
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
//Send notifications for the send to publish action
|
||||
ContentService.SentToPublish += ContentService_SentToPublish;
|
||||
//Send notifications for the published action
|
||||
ContentService.Published += ContentService_Published;
|
||||
//Send notifications for the saved action
|
||||
ContentService.Sorted += ContentService_Sorted;
|
||||
//Send notifications for the update and created actions
|
||||
ContentService.Saved += ContentService_Saved;
|
||||
//Send notifications for the unpublish action
|
||||
ContentService.Unpublished += ContentService_Unpublished;
|
||||
//Send notifications for the move/move to recycle bin and restore actions
|
||||
ContentService.Moved += ContentService_Moved;
|
||||
//Send notifications for the delete action when content is moved to the recycle bin
|
||||
ContentService.Trashed += ContentService_Trashed;
|
||||
//Send notifications for the copy action
|
||||
ContentService.Copied += ContentService_Copied;
|
||||
//Send notifications for the rollback action
|
||||
ContentService.RolledBack += ContentService_RolledBack;
|
||||
//Send notifications for the public access changed action
|
||||
PublicAccessService.Saved += PublicAccessService_Saved;
|
||||
|
||||
@@ -63,15 +47,6 @@ namespace Umbraco.Cms.Core.Compose
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ContentService.SentToPublish -= ContentService_SentToPublish;
|
||||
ContentService.Published -= ContentService_Published;
|
||||
ContentService.Sorted -= ContentService_Sorted;
|
||||
ContentService.Saved -= ContentService_Saved;
|
||||
ContentService.Unpublished -= ContentService_Unpublished;
|
||||
ContentService.Moved -= ContentService_Moved;
|
||||
ContentService.Trashed -= ContentService_Trashed;
|
||||
ContentService.Copied -= ContentService_Copied;
|
||||
ContentService.RolledBack -= ContentService_RolledBack;
|
||||
PublicAccessService.Saved -= PublicAccessService_Saved;
|
||||
UserService.UserGroupPermissionsAssigned -= UserService_UserGroupPermissionsAssigned;
|
||||
}
|
||||
@@ -82,72 +57,6 @@ namespace Umbraco.Cms.Core.Compose
|
||||
private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs<PublicAccessEntry> args)
|
||||
=> PublicAccessServiceSaved(args, _contentService);
|
||||
|
||||
private void ContentService_RolledBack(IContentService sender, RollbackEventArgs<IContent> args)
|
||||
=> _notifier.Notify(_actions.GetAction<ActionRollback>(), args.Entity);
|
||||
|
||||
private void ContentService_Copied(IContentService sender, CopyEventArgs<IContent> args)
|
||||
=> _notifier.Notify(_actions.GetAction<ActionCopy>(), args.Original);
|
||||
|
||||
private void ContentService_Trashed(IContentService sender, MoveEventArgs<IContent> args)
|
||||
=> _notifier.Notify(_actions.GetAction<ActionDelete>(), args.MoveInfoCollection.Select(m => m.Entity).ToArray());
|
||||
|
||||
private void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> args)
|
||||
=> ContentServiceMoved(args);
|
||||
|
||||
private void ContentService_Unpublished(IContentService sender, PublishEventArgs<IContent> args)
|
||||
=> _notifier.Notify(_actions.GetAction<ActionUnpublish>(), args.PublishedEntities.ToArray());
|
||||
|
||||
private void ContentService_Saved(IContentService sender, ContentSavedEventArgs args)
|
||||
=> ContentServiceSaved(args);
|
||||
|
||||
private void ContentService_Sorted(IContentService sender, SaveEventArgs<IContent> args)
|
||||
=> ContentServiceSorted(sender, args);
|
||||
|
||||
private void ContentService_Published(IContentService sender, ContentPublishedEventArgs args)
|
||||
=> _notifier.Notify(_actions.GetAction<ActionPublish>(), args.PublishedEntities.ToArray());
|
||||
|
||||
private void ContentService_SentToPublish(IContentService sender, SendToPublishEventArgs<IContent> args)
|
||||
=> _notifier.Notify(_actions.GetAction<ActionToPublish>(), args.Entity);
|
||||
|
||||
private void ContentServiceSorted(IContentService sender, SaveEventArgs<IContent> args)
|
||||
{
|
||||
var parentId = args.SavedEntities.Select(x => x.ParentId).Distinct().ToList();
|
||||
if (parentId.Count != 1) return; // this shouldn't happen, for sorting all entities will have the same parent id
|
||||
|
||||
// in this case there's nothing to report since if the root is sorted we can't report on a fake entity.
|
||||
// this is how it was in v7, we can't report on root changes because you can't subscribe to root changes.
|
||||
if (parentId[0] <= 0) return;
|
||||
|
||||
var parent = sender.GetById(parentId[0]);
|
||||
if (parent == null) return; // this shouldn't happen
|
||||
|
||||
_notifier.Notify(_actions.GetAction<ActionSort>(), new[] { parent });
|
||||
}
|
||||
|
||||
private void ContentServiceSaved(SaveEventArgs<IContent> args)
|
||||
{
|
||||
var newEntities = new List<IContent>();
|
||||
var updatedEntities = new List<IContent>();
|
||||
|
||||
//need to determine if this is updating or if it is new
|
||||
foreach (var entity in args.SavedEntities)
|
||||
{
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
if (dirty.WasPropertyDirty("Id"))
|
||||
{
|
||||
//it's new
|
||||
newEntities.Add(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
//it's updating
|
||||
updatedEntities.Add(entity);
|
||||
}
|
||||
}
|
||||
_notifier.Notify(_actions.GetAction<ActionNew>(), newEntities.ToArray());
|
||||
_notifier.Notify(_actions.GetAction<ActionUpdate>(), updatedEntities.ToArray());
|
||||
}
|
||||
|
||||
private void UserServiceUserGroupPermissionsAssigned(SaveEventArgs<EntityPermission> args, IContentService contentService)
|
||||
{
|
||||
var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.EntityId)).ToArray();
|
||||
@@ -158,22 +67,6 @@ namespace Umbraco.Cms.Core.Compose
|
||||
_notifier.Notify(_actions.GetAction<ActionRights>(), entities);
|
||||
}
|
||||
|
||||
private void ContentServiceMoved(MoveEventArgs<IContent> args)
|
||||
{
|
||||
// notify about the move for all moved items
|
||||
_notifier.Notify(_actions.GetAction<ActionMove>(), args.MoveInfoCollection.Select(m => m.Entity).ToArray());
|
||||
|
||||
// for any items being moved from the recycle bin (restored), explicitly notify about that too
|
||||
var restoredEntities = args.MoveInfoCollection
|
||||
.Where(m => m.OriginalPath.Contains(Constants.System.RecycleBinContentString))
|
||||
.Select(m => m.Entity)
|
||||
.ToArray();
|
||||
if (restoredEntities.Any())
|
||||
{
|
||||
_notifier.Notify(_actions.GetAction<ActionRestore>(), restoredEntities);
|
||||
}
|
||||
}
|
||||
|
||||
private void PublicAccessServiceSaved(SaveEventArgs<PublicAccessEntry> args, IContentService contentService)
|
||||
{
|
||||
var entities = contentService.GetByIds(args.SavedEntities.Select(e => e.ProtectedNodeId)).ToArray();
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Routing;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
@@ -11,6 +19,49 @@ namespace Umbraco.Cms.Core.Compose
|
||||
base.Compose(builder);
|
||||
|
||||
builder.Services.AddUnique<NotificationsComponent.Notifier>();
|
||||
|
||||
// add handlers for sending user notifications (i.e. emails)
|
||||
builder.Services.AddUnique<UserNotificationsHandler.Notifier>();
|
||||
builder
|
||||
.AddNotificationHandler<ContentSavedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentSortedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentPublishedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentMovedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentMovedToRecycleBinNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentCopiedNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentRolledBackNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentSentToPublishNotification, UserNotificationsHandler>()
|
||||
.AddNotificationHandler<ContentUnpublishedNotification, UserNotificationsHandler>();
|
||||
|
||||
// add handlers for building content relations
|
||||
builder
|
||||
.AddNotificationHandler<ContentCopiedNotification, RelateOnCopyNotificationHandler>()
|
||||
.AddNotificationHandler<ContentMovedNotification, RelateOnTrashNotificationHandler>()
|
||||
.AddNotificationHandler<ContentMovedToRecycleBinNotification, RelateOnTrashNotificationHandler>()
|
||||
.AddNotificationHandler<MediaMovedNotification, RelateOnTrashNotificationHandler>()
|
||||
.AddNotificationHandler<MediaMovedToRecycleBinNotification, RelateOnTrashNotificationHandler>();
|
||||
|
||||
// add notification handlers for property editors
|
||||
builder
|
||||
.AddNotificationHandler<ContentSavingNotification, BlockEditorPropertyHandler>()
|
||||
.AddNotificationHandler<ContentCopyingNotification, BlockEditorPropertyHandler>()
|
||||
.AddNotificationHandler<ContentSavingNotification, NestedContentPropertyHandler>()
|
||||
.AddNotificationHandler<ContentCopyingNotification, NestedContentPropertyHandler>()
|
||||
.AddNotificationHandler<ContentCopiedNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<ContentDeletedNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<MediaDeletedNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<MediaSavingNotification, FileUploadPropertyEditor>()
|
||||
.AddNotificationHandler<ContentCopiedNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<ContentDeletedNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<MediaDeletedNotification, ImageCropperPropertyEditor>()
|
||||
.AddNotificationHandler<MediaSavingNotification, ImageCropperPropertyEditor>();
|
||||
|
||||
// add notification handlers for redirect tracking
|
||||
builder
|
||||
.AddNotificationHandler<ContentPublishingNotification, RedirectTrackingHandler>()
|
||||
.AddNotificationHandler<ContentPublishedNotification, RedirectTrackingHandler>()
|
||||
.AddNotificationHandler<ContentMovingNotification, RedirectTrackingHandler>()
|
||||
.AddNotificationHandler<ContentMovedNotification, RedirectTrackingHandler>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
// TODO: This should just exist in the content service/repo!
|
||||
public sealed class RelateOnCopyComponent : IComponent
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IAuditService _auditService;
|
||||
|
||||
public RelateOnCopyComponent(IRelationService relationService, IAuditService auditService)
|
||||
{
|
||||
_relationService = relationService;
|
||||
_auditService = auditService;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
ContentService.Copied += ContentServiceCopied;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ContentService.Copied -= ContentServiceCopied;
|
||||
}
|
||||
|
||||
private void ContentServiceCopied(IContentService sender, CopyEventArgs<IContent> e)
|
||||
{
|
||||
if (e.RelateToOriginal == false) return;
|
||||
|
||||
|
||||
var relationType = _relationService.GetRelationTypeByAlias(Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias);
|
||||
|
||||
if (relationType == null)
|
||||
{
|
||||
relationType = new RelationType(Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
|
||||
Cms.Core.Constants.Conventions.RelationTypes.RelateDocumentOnCopyName,
|
||||
true,
|
||||
Cms.Core.Constants.ObjectTypes.Document,
|
||||
Cms.Core.Constants.ObjectTypes.Document);
|
||||
|
||||
_relationService.Save(relationType);
|
||||
}
|
||||
|
||||
var relation = new Relation(e.Original.Id, e.Copy.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
|
||||
_auditService.Add(
|
||||
AuditType.Copy,
|
||||
e.Copy.WriterId,
|
||||
e.Copy.Id, ObjectTypes.GetName(UmbracoObjectTypes.Document),
|
||||
$"Copied content with Id: '{e.Copy.Id}' related to original content with Id: '{e.Original.Id}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class RelateOnCopyComposer : ComponentComposer<RelateOnCopyComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class RelateOnTrashComposer : ComponentComposer<RelateOnTrashComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -5,12 +5,12 @@ using Microsoft.Extensions.Configuration.Json;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration
|
||||
{
|
||||
public class JsonConfigManipulator : IConfigManipulator
|
||||
{
|
||||
private static readonly object s_locker = new object();
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public JsonConfigManipulator(IConfiguration configuration)
|
||||
@@ -162,22 +162,26 @@ namespace Umbraco.Cms.Core.Configuration
|
||||
token?.Parent?.Remove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void SaveJson(JsonConfigurationProvider provider, JObject json)
|
||||
{
|
||||
if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider)
|
||||
lock (s_locker)
|
||||
{
|
||||
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
|
||||
if (provider.Source.FileProvider is PhysicalFileProvider physicalFileProvider)
|
||||
{
|
||||
var jsonFilePath = Path.Combine(physicalFileProvider.Root, provider.Source.Path);
|
||||
|
||||
using (var sw = new StreamWriter(jsonFilePath, false))
|
||||
using (var jsonTextWriter = new JsonTextWriter(sw)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
})
|
||||
{
|
||||
json?.WriteTo(jsonTextWriter);
|
||||
using (var sw = new StreamWriter(jsonFilePath, false))
|
||||
using (var jsonTextWriter = new JsonTextWriter(sw)
|
||||
{
|
||||
Formatting = Formatting.Indented,
|
||||
})
|
||||
{
|
||||
json?.WriteTo(jsonTextWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static JObject GetJson(JsonConfigurationProvider provider)
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.DependencyInjection;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Examine;
|
||||
using Umbraco.Cms.Infrastructure.Search;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Search
|
||||
namespace Umbraco.Cms.Infrastructure.DependencyInjection
|
||||
{
|
||||
/// <summary>
|
||||
/// Configures and installs Examine.
|
||||
/// Provides extension methods to the <see cref="IUmbracoBuilder"/> class.
|
||||
/// </summary>
|
||||
public sealed class ExamineComposer : ComponentComposer<ExamineComponent>, ICoreComposer
|
||||
public static partial class UmbracoBuilderExtensions
|
||||
{
|
||||
public override void Compose(IUmbracoBuilder builder)
|
||||
public static IUmbracoBuilder AddExamine(this IUmbracoBuilder builder)
|
||||
{
|
||||
base.Compose(builder);
|
||||
|
||||
// populators are not a collection: one cannot remove ours, and can only add more
|
||||
// the container can inject IEnumerable<IIndexPopulator> and get them all
|
||||
builder.Services.AddSingleton<IIndexPopulator, MemberIndexPopulator>();
|
||||
@@ -49,6 +49,15 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
builder.Services.AddUnique<IValueSetBuilder<IMedia>, MediaValueSetBuilder>();
|
||||
builder.Services.AddUnique<IValueSetBuilder<IMember>, MemberValueSetBuilder>();
|
||||
builder.Services.AddUnique<BackgroundIndexRebuilder>();
|
||||
|
||||
builder.AddNotificationHandler<UmbracoApplicationStarting, ExamineNotificationHandler>();
|
||||
builder.AddNotificationHandler<ContentCacheRefresherNotification, ExamineNotificationHandler>();
|
||||
builder.AddNotificationHandler<ContentTypeCacheRefresherNotification, ExamineNotificationHandler>();
|
||||
builder.AddNotificationHandler<MediaCacheRefresherNotification, ExamineNotificationHandler>();
|
||||
builder.AddNotificationHandler<MemberCacheRefresherNotification, ExamineNotificationHandler>();
|
||||
builder.AddNotificationHandler<LanguageCacheRefresherNotification, ExamineNotificationHandler>();
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public class RelateOnCopyNotificationHandler : INotificationHandler<ContentCopiedNotification>
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IAuditService _auditService;
|
||||
|
||||
public RelateOnCopyNotificationHandler(IRelationService relationService, IAuditService auditService)
|
||||
{
|
||||
_relationService = relationService;
|
||||
_auditService = auditService;
|
||||
}
|
||||
|
||||
public void Handle(ContentCopiedNotification notification)
|
||||
{
|
||||
if (notification.RelateToOriginal == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var relationType = _relationService.GetRelationTypeByAlias(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias);
|
||||
|
||||
if (relationType == null)
|
||||
{
|
||||
relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyName,
|
||||
true,
|
||||
Constants.ObjectTypes.Document,
|
||||
Constants.ObjectTypes.Document);
|
||||
|
||||
_relationService.Save(relationType);
|
||||
}
|
||||
|
||||
var relation = new Relation(notification.Original.Id, notification.Copy.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
|
||||
_auditService.Add(
|
||||
AuditType.Copy,
|
||||
notification.Copy.WriterId,
|
||||
notification.Copy.Id, ObjectTypes.GetName(UmbracoObjectTypes.Document),
|
||||
$"Copied content with Id: '{notification.Copy.Id}' related to original content with Id: '{notification.Original.Id}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,21 @@
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public sealed class RelateOnTrashComponent : IComponent
|
||||
// TODO: lots of duplicate code in this one, refactor
|
||||
public sealed class RelateOnTrashNotificationHandler :
|
||||
INotificationHandler<ContentMovedNotification>,
|
||||
INotificationHandler<ContentMovedToRecycleBinNotification>,
|
||||
INotificationHandler<MediaMovedNotification>,
|
||||
INotificationHandler<MediaMovedToRecycleBinNotification>
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IEntityService _entityService;
|
||||
@@ -17,7 +23,7 @@ namespace Umbraco.Cms.Core.Compose
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
public RelateOnTrashComponent(
|
||||
public RelateOnTrashNotificationHandler(
|
||||
IRelationService relationService,
|
||||
IEntityService entityService,
|
||||
ILocalizedTextService textService,
|
||||
@@ -31,28 +37,11 @@ namespace Umbraco.Cms.Core.Compose
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Handle(ContentMovedNotification notification)
|
||||
{
|
||||
ContentService.Moved += ContentService_Moved;
|
||||
ContentService.Trashed += ContentService_Trashed;
|
||||
MediaService.Moved += MediaService_Moved;
|
||||
MediaService.Trashed += MediaService_Trashed;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ContentService.Moved -= ContentService_Moved;
|
||||
ContentService.Trashed -= ContentService_Trashed;
|
||||
MediaService.Moved -= MediaService_Moved;
|
||||
MediaService.Trashed -= MediaService_Trashed;
|
||||
}
|
||||
|
||||
private void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> e)
|
||||
{
|
||||
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinContentString)))
|
||||
foreach (var item in notification.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
|
||||
{
|
||||
|
||||
const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
var relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
|
||||
foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
|
||||
@@ -62,44 +51,29 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaService_Moved(IMediaService sender, MoveEventArgs<IMedia> e)
|
||||
{
|
||||
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Cms.Core.Constants.System.RecycleBinMediaString)))
|
||||
{
|
||||
const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
|
||||
var relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)))
|
||||
{
|
||||
_relationService.Delete(relation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ContentService_Trashed(IContentService sender, MoveEventArgs<IContent> e)
|
||||
public void Handle(ContentMovedToRecycleBinNotification notification)
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
|
||||
|
||||
// check that the relation-type exists, if not, then recreate it
|
||||
if (relationType == null)
|
||||
{
|
||||
var documentObjectType = Cms.Core.Constants.ObjectTypes.Document;
|
||||
const string relationTypeName =
|
||||
Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
|
||||
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType,
|
||||
documentObjectType);
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
|
||||
_relationService.Save(relationType);
|
||||
}
|
||||
|
||||
foreach (var item in e.MoveInfoCollection)
|
||||
foreach (var item in notification.MoveInfoCollection)
|
||||
{
|
||||
var originalPath = item.OriginalPath.ToDelimitedList();
|
||||
var originalParentId = originalPath.Count > 2
|
||||
? int.Parse(originalPath[originalPath.Count - 2])
|
||||
: Cms.Core.Constants.System.Root;
|
||||
: 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
|
||||
@@ -107,17 +81,15 @@ namespace Umbraco.Cms.Core.Compose
|
||||
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);
|
||||
var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? new Relation(originalParentId, item.Entity.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
|
||||
_auditService.Add(AuditType.Delete,
|
||||
item.Entity.WriterId,
|
||||
item.Entity.Id,
|
||||
ObjectTypes.GetName(UmbracoObjectTypes.Document),
|
||||
string.Format(_textService.Localize(
|
||||
"recycleBin/contentTrashed"),
|
||||
item.Entity.Id, originalParentId));
|
||||
string.Format(_textService.Localize("recycleBin/contentTrashed"), item.Entity.Id, originalParentId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,51 +97,60 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
|
||||
public void MediaService_Trashed(IMediaService sender, MoveEventArgs<IMedia> e)
|
||||
public void Handle(MediaMovedNotification notification)
|
||||
{
|
||||
foreach (var 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)))
|
||||
{
|
||||
_relationService.Delete(relation);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Handle(MediaMovedToRecycleBinNotification notification)
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
const string relationTypeAlias =
|
||||
Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
|
||||
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
|
||||
var relationType = _relationService.GetRelationTypeByAlias(relationTypeAlias);
|
||||
// check that the relation-type exists, if not, then recreate it
|
||||
if (relationType == null)
|
||||
{
|
||||
var documentObjectType = Cms.Core.Constants.ObjectTypes.Document;
|
||||
const string relationTypeName =
|
||||
Cms.Core.Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType,
|
||||
documentObjectType);
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
|
||||
_relationService.Save(relationType);
|
||||
}
|
||||
|
||||
foreach (var item in e.MoveInfoCollection)
|
||||
foreach (var item in notification.MoveInfoCollection)
|
||||
{
|
||||
var originalPath = item.OriginalPath.ToDelimitedList();
|
||||
var originalParentId = originalPath.Count > 2
|
||||
? int.Parse(originalPath[originalPath.Count - 2])
|
||||
: Cms.Core.Constants.System.Root;
|
||||
: 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);
|
||||
var relation = _relationService.GetByParentAndChildId(originalParentId, item.Entity.Id, relationType) ?? new Relation(originalParentId, item.Entity.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
_auditService.Add(AuditType.Delete,
|
||||
item.Entity.CreatorId,
|
||||
item.Entity.Id,
|
||||
ObjectTypes.GetName(UmbracoObjectTypes.Media),
|
||||
string.Format(_textService.Localize(
|
||||
"recycleBin/mediaTrashed"),
|
||||
item.Entity.Id, originalParentId));
|
||||
string.Format(_textService.Localize("recycleBin/mediaTrashed"), item.Entity.Id, originalParentId)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
215
src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs
Normal file
215
src/Umbraco.Infrastructure/Events/UserNotificationsHandler.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Hosting;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Events
|
||||
{
|
||||
public sealed class UserNotificationsHandler :
|
||||
INotificationHandler<ContentSavedNotification>,
|
||||
INotificationHandler<ContentSortedNotification>,
|
||||
INotificationHandler<ContentPublishedNotification>,
|
||||
INotificationHandler<ContentMovedNotification>,
|
||||
INotificationHandler<ContentMovedToRecycleBinNotification>,
|
||||
INotificationHandler<ContentCopiedNotification>,
|
||||
INotificationHandler<ContentRolledBackNotification>,
|
||||
INotificationHandler<ContentSentToPublishNotification>,
|
||||
INotificationHandler<ContentUnpublishedNotification>
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
private readonly ActionCollection _actions;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
public UserNotificationsHandler(Notifier notifier, ActionCollection actions, IContentService contentService)
|
||||
{
|
||||
_notifier = notifier;
|
||||
_actions = actions;
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
public void Handle(ContentSavedNotification notification)
|
||||
{
|
||||
var newEntities = new List<IContent>();
|
||||
var updatedEntities = new List<IContent>();
|
||||
|
||||
//need to determine if this is updating or if it is new
|
||||
foreach (var entity in notification.SavedEntities)
|
||||
{
|
||||
var dirty = (IRememberBeingDirty)entity;
|
||||
if (dirty.WasPropertyDirty("Id"))
|
||||
{
|
||||
//it's new
|
||||
newEntities.Add(entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
//it's updating
|
||||
updatedEntities.Add(entity);
|
||||
}
|
||||
}
|
||||
_notifier.Notify(_actions.GetAction<ActionNew>(), newEntities.ToArray());
|
||||
_notifier.Notify(_actions.GetAction<ActionUpdate>(), updatedEntities.ToArray());
|
||||
}
|
||||
|
||||
public void Handle(ContentSortedNotification notification)
|
||||
{
|
||||
var parentId = notification.SortedEntities.Select(x => x.ParentId).Distinct().ToList();
|
||||
if (parentId.Count != 1)
|
||||
return; // this shouldn't happen, for sorting all entities will have the same parent id
|
||||
|
||||
// in this case there's nothing to report since if the root is sorted we can't report on a fake entity.
|
||||
// this is how it was in v7, we can't report on root changes because you can't subscribe to root changes.
|
||||
if (parentId[0] <= 0)
|
||||
return;
|
||||
|
||||
var parent = _contentService.GetById(parentId[0]);
|
||||
if (parent == null)
|
||||
return; // this shouldn't happen
|
||||
|
||||
_notifier.Notify(_actions.GetAction<ActionSort>(), new[] { parent });
|
||||
}
|
||||
|
||||
public void Handle(ContentPublishedNotification notification) => _notifier.Notify(_actions.GetAction<ActionPublish>(), notification.PublishedEntities.ToArray());
|
||||
|
||||
public void Handle(ContentMovedNotification notification)
|
||||
{
|
||||
// notify about the move for all moved items
|
||||
_notifier.Notify(_actions.GetAction<ActionMove>(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray());
|
||||
|
||||
// for any items being moved from the recycle bin (restored), explicitly notify about that too
|
||||
var restoredEntities = notification.MoveInfoCollection
|
||||
.Where(m => m.OriginalPath.Contains(Constants.System.RecycleBinContentString))
|
||||
.Select(m => m.Entity)
|
||||
.ToArray();
|
||||
if (restoredEntities.Any())
|
||||
{
|
||||
_notifier.Notify(_actions.GetAction<ActionRestore>(), restoredEntities);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ContentMovedToRecycleBinNotification notification) => _notifier.Notify(_actions.GetAction<ActionDelete>(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray());
|
||||
|
||||
public void Handle(ContentCopiedNotification notification) => _notifier.Notify(_actions.GetAction<ActionCopy>(), notification.Original);
|
||||
|
||||
public void Handle(ContentRolledBackNotification notification) => _notifier.Notify(_actions.GetAction<ActionRollback>(), notification.Entity);
|
||||
|
||||
public void Handle(ContentSentToPublishNotification notification) => _notifier.Notify(_actions.GetAction<ActionToPublish>(), notification.Entity);
|
||||
|
||||
public void Handle(ContentUnpublishedNotification notification) => _notifier.Notify(_actions.GetAction<ActionUnpublish>(), notification.UnpublishedEntities.ToArray());
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to send the notifications
|
||||
/// </summary>
|
||||
public sealed class Notifier
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly ILogger<Notifier> _logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Notifier"/> class.
|
||||
/// </summary>
|
||||
public Notifier(
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
IHostingEnvironment hostingEnvironment,
|
||||
INotificationService notificationService,
|
||||
IUserService userService,
|
||||
ILocalizedTextService textService,
|
||||
IOptions<GlobalSettings> globalSettings,
|
||||
ILogger<Notifier> logger)
|
||||
{
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_hostingEnvironment = hostingEnvironment;
|
||||
_notificationService = notificationService;
|
||||
_userService = userService;
|
||||
_textService = textService;
|
||||
_globalSettings = globalSettings.Value;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void Notify(IAction action, params IContent[] entities)
|
||||
{
|
||||
var user = _backOfficeSecurityAccessor?.BackOfficeSecurity?.CurrentUser;
|
||||
|
||||
//if there is no current user, then use the admin
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogDebug("There is no current Umbraco user logged in, the notifications will be sent from the administrator");
|
||||
user = _userService.GetUserById(Constants.Security.SuperUserId);
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogWarning("Notifications can not be sent, no admin user with id {SuperUserId} could be resolved", Constants.Security.SuperUserId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SendNotification(user, entities, action, _hostingEnvironment.ApplicationMainUrl);
|
||||
}
|
||||
|
||||
private void SendNotification(IUser sender, IEnumerable<IContent> entities, IAction action, Uri siteUri)
|
||||
{
|
||||
if (sender == null)
|
||||
throw new ArgumentNullException(nameof(sender));
|
||||
if (siteUri == null)
|
||||
{
|
||||
_logger.LogWarning("Notifications can not be sent, no site URL is set (might be during boot process?)");
|
||||
return;
|
||||
}
|
||||
|
||||
//group by the content type variation since the emails will be different
|
||||
foreach (var contentVariantGroup in entities.GroupBy(x => x.ContentType.Variations))
|
||||
{
|
||||
_notificationService.SendNotifications(
|
||||
sender,
|
||||
contentVariantGroup,
|
||||
action.Letter.ToString(CultureInfo.InvariantCulture),
|
||||
_textService.Localize("actions", action.Alias),
|
||||
siteUri,
|
||||
((IUser user, NotificationEmailSubjectParams subject) x)
|
||||
=> _textService.Localize(
|
||||
"notifications/mailSubject",
|
||||
x.user.GetUserCulture(_textService, _globalSettings),
|
||||
new[] { x.subject.SiteUrl, x.subject.Action, x.subject.ItemName }),
|
||||
((IUser user, NotificationEmailBodyParams body, bool isHtml) x)
|
||||
=> _textService.Localize(
|
||||
x.isHtml ? "notifications/mailBodyHtml" : "notifications/mailBody",
|
||||
x.user.GetUserCulture(_textService, _globalSettings),
|
||||
new[]
|
||||
{
|
||||
x.body.RecipientName,
|
||||
x.body.Action,
|
||||
x.body.ItemName,
|
||||
x.body.EditedUser,
|
||||
x.body.SiteUrl,
|
||||
x.body.ItemId,
|
||||
//format the summary depending on if it's variant or not
|
||||
contentVariantGroup.Key == ContentVariation.Culture
|
||||
? (x.isHtml ? _textService.Localize("notifications/mailBodyVariantHtmlSummary", new[]{ x.body.Summary }) : _textService.Localize("notifications/mailBodyVariantSummary", new []{ x.body.Summary }))
|
||||
: x.body.Summary,
|
||||
x.body.ItemUrl
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
@@ -66,8 +65,6 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
Debugger.Launch();
|
||||
Debugger.Break();
|
||||
CreateDatabaseTable();
|
||||
MigrateFileContentToDB();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,10 @@ using Umbraco.Extensions;
|
||||
namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
{
|
||||
// supports LiveAppData - but not PureLive
|
||||
public sealed class LiveModelsProvider : INotificationHandler<UmbracoApplicationStarting>, INotificationHandler<UmbracoRequestEnd>
|
||||
public sealed class LiveModelsProvider : INotificationHandler<UmbracoApplicationStarting>,
|
||||
INotificationHandler<UmbracoRequestEnd>,
|
||||
INotificationHandler<ContentTypeCacheRefresherNotification>,
|
||||
INotificationHandler<DataTypeCacheRefresherNotification>
|
||||
{
|
||||
private static int s_req;
|
||||
private readonly ILogger<LiveModelsProvider> _logger;
|
||||
@@ -53,16 +56,6 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Must register with maindom in order to function.
|
||||
// If registration is not successful then events are not bound
|
||||
// and we also don't generate models.
|
||||
_mainDom.Register(() =>
|
||||
{
|
||||
// anything changes, and we want to re-generate models.
|
||||
ContentTypeCacheRefresher.CacheUpdated += RequestModelsGeneration;
|
||||
DataTypeCacheRefresher.CacheUpdated += RequestModelsGeneration;
|
||||
});
|
||||
}
|
||||
|
||||
// NOTE
|
||||
@@ -72,8 +65,13 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
// need to be generated. Could be by another request. Anyway. We could
|
||||
// have collisions but... you know the risk.
|
||||
|
||||
private void RequestModelsGeneration(object sender, EventArgs args)
|
||||
private void RequestModelsGeneration()
|
||||
{
|
||||
if (!_mainDom.IsMainDom)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_logger.LogDebug("Requested to generate models.");
|
||||
Interlocked.Exchange(ref s_req, 1);
|
||||
}
|
||||
@@ -121,5 +119,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
GenerateModelsIfRequested();
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ContentTypeCacheRefresherNotification notification) => RequestModelsGeneration();
|
||||
|
||||
public void Handle(DataTypeCacheRefresherNotification notification) => RequestModelsGeneration();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
/// <summary>
|
||||
/// Used to track if ModelsBuilder models are out of date/stale
|
||||
/// </summary>
|
||||
public sealed class OutOfDateModelsStatus : INotificationHandler<UmbracoApplicationStarting>
|
||||
public sealed class OutOfDateModelsStatus : INotificationHandler<ContentTypeCacheRefresherNotification>,
|
||||
INotificationHandler<DataTypeCacheRefresherNotification>
|
||||
{
|
||||
private readonly ModelsBuilderSettings _config;
|
||||
private readonly IHostingEnvironment _hostingEnvironment;
|
||||
@@ -47,22 +48,6 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="UmbracoApplicationStarting"/> notification
|
||||
/// </summary>
|
||||
public void Handle(UmbracoApplicationStarting notification) => Install();
|
||||
|
||||
private void Install()
|
||||
{
|
||||
// don't run if not configured
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ContentTypeCacheRefresher.CacheUpdated += (sender, args) => Write();
|
||||
DataTypeCacheRefresher.CacheUpdated += (sender, args) => Write();
|
||||
}
|
||||
|
||||
private string GetFlagPath()
|
||||
{
|
||||
@@ -77,6 +62,12 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
|
||||
private void Write()
|
||||
{
|
||||
// don't run if not configured
|
||||
if (!IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var path = GetFlagPath();
|
||||
if (path == null || File.Exists(path))
|
||||
{
|
||||
@@ -101,5 +92,9 @@ namespace Umbraco.Cms.Infrastructure.ModelsBuilder
|
||||
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
public void Handle(ContentTypeCacheRefresherNotification notification) => Write();
|
||||
|
||||
public void Handle(DataTypeCacheRefresherNotification notification) => Write();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
using System;
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Models.Blocks;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// A component for Block editors used to bind to events
|
||||
/// A handler for Block editors used to bind to notifications
|
||||
/// </summary>
|
||||
public class BlockEditorComponent : IComponent
|
||||
public class BlockEditorPropertyHandler : ComplexPropertyEditorContentNotificationHandler
|
||||
{
|
||||
private ComplexPropertyEditorContentEventHandler _handler;
|
||||
private readonly BlockListEditorDataConverter _converter = new BlockListEditorDataConverter();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_handler = new ComplexPropertyEditorContentEventHandler(
|
||||
Constants.PropertyEditors.Aliases.BlockList,
|
||||
ReplaceBlockListUdis);
|
||||
}
|
||||
protected override string EditorAlias => Constants.PropertyEditors.Aliases.BlockList;
|
||||
|
||||
public void Terminate() => _handler?.Dispose();
|
||||
|
||||
private string ReplaceBlockListUdis(string rawJson, bool onlyMissingUdis)
|
||||
protected override string FormatPropertyValue(string rawJson, bool onlyMissingKeys)
|
||||
{
|
||||
// the block editor doesn't ever have missing UDIs so when this is true there's nothing to process
|
||||
if (onlyMissingUdis) return rawJson;
|
||||
if (onlyMissingKeys)
|
||||
return rawJson;
|
||||
|
||||
return ReplaceBlockListUdis(rawJson, null);
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class for dealing with <see cref="ContentService"/> Copying/Saving events for complex editors
|
||||
/// </summary>
|
||||
public class ComplexPropertyEditorContentEventHandler : IDisposable
|
||||
{
|
||||
private readonly string _editorAlias;
|
||||
private readonly Func<string, bool, string> _formatPropertyValue;
|
||||
private bool _disposedValue;
|
||||
|
||||
public ComplexPropertyEditorContentEventHandler(string editorAlias,
|
||||
Func<string, bool, string> formatPropertyValue)
|
||||
{
|
||||
_editorAlias = editorAlias;
|
||||
_formatPropertyValue = formatPropertyValue;
|
||||
ContentService.Copying += ContentService_Copying;
|
||||
ContentService.Saving += ContentService_Saving;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ContentService"/> Copying event handler
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ContentService_Copying(IContentService sender, CopyEventArgs<IContent> e)
|
||||
{
|
||||
var props = e.Copy.GetPropertiesByEditor(_editorAlias);
|
||||
UpdatePropertyValues(props, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="ContentService"/> Saving event handler
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ContentService_Saving(IContentService sender, ContentSavingEventArgs e)
|
||||
{
|
||||
foreach (var entity in e.SavedEntities)
|
||||
{
|
||||
var props = entity.GetPropertiesByEditor(_editorAlias);
|
||||
UpdatePropertyValues(props, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePropertyValues(IEnumerable<IProperty> props, bool onlyMissingKeys)
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
// A Property may have one or more values due to cultures
|
||||
var propVals = prop.Values;
|
||||
foreach (var cultureVal in propVals)
|
||||
{
|
||||
// Remove keys from published value & any nested properties
|
||||
var updatedPublishedVal = _formatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys);
|
||||
cultureVal.PublishedValue = updatedPublishedVal;
|
||||
|
||||
// Remove keys from edited/draft value & any nested properties
|
||||
var updatedEditedVal = _formatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys);
|
||||
cultureVal.EditedValue = updatedEditedVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unbinds from events
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
ContentService.Copying -= ContentService_Copying;
|
||||
ContentService.Saving -= ContentService_Saving;
|
||||
}
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unbinds from events
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
public abstract class ComplexPropertyEditorContentNotificationHandler :
|
||||
INotificationHandler<ContentSavingNotification>,
|
||||
INotificationHandler<ContentCopyingNotification>
|
||||
{
|
||||
protected abstract string EditorAlias { get; }
|
||||
|
||||
protected abstract string FormatPropertyValue(string rawJson, bool onlyMissingKeys);
|
||||
|
||||
public void Handle(ContentSavingNotification notification)
|
||||
{
|
||||
foreach (var entity in notification.SavedEntities)
|
||||
{
|
||||
var props = entity.GetPropertiesByEditor(EditorAlias);
|
||||
UpdatePropertyValues(props, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(ContentCopyingNotification notification)
|
||||
{
|
||||
var props = notification.Copy.GetPropertiesByEditor(EditorAlias);
|
||||
UpdatePropertyValues(props, false);
|
||||
}
|
||||
|
||||
private void UpdatePropertyValues(IEnumerable<IProperty> props, bool onlyMissingKeys)
|
||||
{
|
||||
foreach (var prop in props)
|
||||
{
|
||||
// A Property may have one or more values due to cultures
|
||||
var propVals = prop.Values;
|
||||
foreach (var cultureVal in propVals)
|
||||
{
|
||||
// Remove keys from published value & any nested properties
|
||||
var updatedPublishedVal = FormatPropertyValue(cultureVal.PublishedValue?.ToString(), onlyMissingKeys);
|
||||
cultureVal.PublishedValue = updatedPublishedVal;
|
||||
|
||||
// Remove keys from edited/draft value & any nested properties
|
||||
var updatedEditedVal = FormatPropertyValue(cultureVal.EditedValue?.ToString(), onlyMissingKeys);
|
||||
cultureVal.EditedValue = updatedEditedVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
@@ -24,7 +25,9 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
"fileupload",
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-download-alt")]
|
||||
public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator
|
||||
public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator,
|
||||
INotificationHandler<ContentCopiedNotification>, INotificationHandler<ContentDeletedNotification>,
|
||||
INotificationHandler<MediaDeletedNotification>, INotificationHandler<MediaSavingNotification>
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
@@ -32,6 +35,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
private readonly IDataTypeService _dataTypeService;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILocalizedTextService _localizedTextService;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
public FileUploadPropertyEditor(
|
||||
ILoggerFactory loggerFactory,
|
||||
@@ -42,7 +46,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
ILocalizedTextService localizedTextService,
|
||||
IShortStringHelper shortStringHelper,
|
||||
UploadAutoFillProperties uploadAutoFillProperties,
|
||||
IJsonSerializer jsonSerializer)
|
||||
IJsonSerializer jsonSerializer,
|
||||
IContentService contentService)
|
||||
: base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer)
|
||||
{
|
||||
_mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem));
|
||||
@@ -51,6 +56,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_localizationService = localizationService;
|
||||
_localizedTextService = localizedTextService;
|
||||
_uploadAutoFillProperties = uploadAutoFillProperties;
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,16 +92,17 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures any files associated are removed
|
||||
/// The paths to all file upload property files contained within a collection of content entities
|
||||
/// </summary>
|
||||
/// <param name="deletedEntities"></param>
|
||||
internal IEnumerable<string> ServiceDeleted(IEnumerable<ContentBase> deletedEntities)
|
||||
{
|
||||
return deletedEntities.SelectMany(x => x.Properties)
|
||||
.Where(IsUploadField)
|
||||
.SelectMany(GetFilePathsFromPropertyValues)
|
||||
.Distinct();
|
||||
}
|
||||
/// <param name="entities"></param>
|
||||
/// <remarks>
|
||||
/// This method must be made private once MemberService events have been replaced by notifications
|
||||
/// </remarks>
|
||||
internal IEnumerable<string> ContainedFilePaths(IEnumerable<IContentBase> entities) => entities
|
||||
.SelectMany(x => x.Properties)
|
||||
.Where(IsUploadField)
|
||||
.SelectMany(GetFilePathsFromPropertyValues)
|
||||
.Distinct();
|
||||
|
||||
/// <summary>
|
||||
/// Look through all property values stored against the property and resolve any file paths stored
|
||||
@@ -119,15 +126,10 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a content has been copied, also copy uploaded files.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
internal void ContentServiceCopied(IContentService sender, CopyEventArgs<IContent> args)
|
||||
public void Handle(ContentCopiedNotification notification)
|
||||
{
|
||||
// get the upload field properties with a value
|
||||
var properties = args.Original.Properties.Where(IsUploadField);
|
||||
var properties = notification.Original.Properties.Where(IsUploadField);
|
||||
|
||||
// copy files
|
||||
var isUpdated = false;
|
||||
@@ -137,49 +139,41 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
foreach (var propertyValue in property.Values)
|
||||
{
|
||||
var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
|
||||
if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace()) continue;
|
||||
if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sourcePath = _mediaFileSystem.GetRelativePath(str);
|
||||
var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath);
|
||||
args.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment);
|
||||
var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath);
|
||||
notification.Copy.SetValue(property.Alias, _mediaFileSystem.GetUrl(copyPath), propertyValue.Culture, propertyValue.Segment);
|
||||
isUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if updated, re-save the copy with the updated value
|
||||
if (isUpdated)
|
||||
sender.Save(args.Copy);
|
||||
{
|
||||
_contentService.Save(notification.Copy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a media has been created, auto-fill the properties.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
internal void MediaServiceCreated(IMediaService sender, NewEventArgs<IMedia> args)
|
||||
public void Handle(ContentDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
private void DeleteContainedFiles(IEnumerable<IContentBase> deletedEntities)
|
||||
{
|
||||
AutoFillProperties(args.Entity);
|
||||
var filePathsToDelete = ContainedFilePaths(deletedEntities);
|
||||
_mediaFileSystem.DeleteMediaFiles(filePathsToDelete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a media has been saved, auto-fill the properties.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
public void MediaServiceSaving(IMediaService sender, SaveEventArgs<IMedia> args)
|
||||
public void Handle(MediaSavingNotification notification)
|
||||
{
|
||||
foreach (var entity in args.SavedEntities)
|
||||
AutoFillProperties(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a content item has been saved, auto-fill the properties.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
public void ContentServiceSaving(IContentService sender, SaveEventArgs<IContent> args)
|
||||
{
|
||||
foreach (var entity in args.SavedEntities)
|
||||
foreach (var entity in notification.SavedEntities)
|
||||
{
|
||||
AutoFillProperties(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -16,6 +16,7 @@ using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
@@ -31,7 +32,9 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
HideLabel = false,
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-crop")]
|
||||
public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator
|
||||
public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator,
|
||||
INotificationHandler<ContentCopiedNotification>, INotificationHandler<ContentDeletedNotification>,
|
||||
INotificationHandler<MediaDeletedNotification>, INotificationHandler<MediaSavingNotification>
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
@@ -39,6 +42,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private readonly UploadAutoFillProperties _autoFillProperties;
|
||||
private readonly ILogger<ImageCropperPropertyEditor> _logger;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ImageCropperPropertyEditor"/> class.
|
||||
@@ -53,7 +57,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
IShortStringHelper shortStringHelper,
|
||||
ILocalizedTextService localizedTextService,
|
||||
UploadAutoFillProperties uploadAutoFillProperties,
|
||||
IJsonSerializer jsonSerializer)
|
||||
IJsonSerializer jsonSerializer,
|
||||
IContentService contentService)
|
||||
: base(loggerFactory, dataTypeService, localizationService, localizedTextService, shortStringHelper, jsonSerializer)
|
||||
{
|
||||
_mediaFileSystem = mediaFileSystem ?? throw new ArgumentNullException(nameof(mediaFileSystem));
|
||||
@@ -61,6 +66,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_dataTypeService = dataTypeService ?? throw new ArgumentNullException(nameof(dataTypeService));
|
||||
_ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper));
|
||||
_autoFillProperties = uploadAutoFillProperties ?? throw new ArgumentNullException(nameof(uploadAutoFillProperties));
|
||||
_contentService = contentService;
|
||||
_logger = loggerFactory.CreateLogger<ImageCropperPropertyEditor>();
|
||||
}
|
||||
|
||||
@@ -122,16 +128,17 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures any files associated are removed
|
||||
/// The paths to all image cropper property files contained within a collection of content entities
|
||||
/// </summary>
|
||||
/// <param name="deletedEntities"></param>
|
||||
internal IEnumerable<string> ServiceDeleted(IEnumerable<ContentBase> deletedEntities)
|
||||
{
|
||||
return deletedEntities.SelectMany(x => x.Properties)
|
||||
.Where(IsCropperField)
|
||||
.SelectMany(GetFilePathsFromPropertyValues)
|
||||
.Distinct();
|
||||
}
|
||||
/// <param name="entities"></param>
|
||||
/// <remarks>
|
||||
/// This method must be made private once MemberService events have been replaced by notifications
|
||||
/// </remarks>
|
||||
internal IEnumerable<string> ContainedFilePaths(IEnumerable<IContentBase> entities) => entities
|
||||
.SelectMany(x => x.Properties)
|
||||
.Where(IsCropperField)
|
||||
.SelectMany(GetFilePathsFromPropertyValues)
|
||||
.Distinct();
|
||||
|
||||
/// <summary>
|
||||
/// Look through all property values stored against the property and resolve any file paths stored
|
||||
@@ -175,12 +182,10 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
/// <summary>
|
||||
/// After a content has been copied, also copy uploaded files.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
public void ContentServiceCopied(IContentService sender, CopyEventArgs<IContent> args)
|
||||
public void Handle(ContentCopiedNotification notification)
|
||||
{
|
||||
// get the image cropper field properties
|
||||
var properties = args.Original.Properties.Where(IsCropperField);
|
||||
var properties = notification.Original.Properties.Where(IsCropperField);
|
||||
|
||||
// copy files
|
||||
var isUpdated = false;
|
||||
@@ -191,49 +196,40 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
|
||||
var src = GetFileSrcFromPropertyValue(propVal, out var jo);
|
||||
if (src == null) continue;
|
||||
if (src == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var sourcePath = _mediaFileSystem.GetRelativePath(src);
|
||||
var copyPath = _mediaFileSystem.CopyFile(args.Copy, property.PropertyType, sourcePath);
|
||||
var copyPath = _mediaFileSystem.CopyFile(notification.Copy, property.PropertyType, sourcePath);
|
||||
jo["src"] = _mediaFileSystem.GetUrl(copyPath);
|
||||
args.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment);
|
||||
notification.Copy.SetValue(property.Alias, jo.ToString(), propertyValue.Culture, propertyValue.Segment);
|
||||
isUpdated = true;
|
||||
}
|
||||
}
|
||||
// if updated, re-save the copy with the updated value
|
||||
if (isUpdated)
|
||||
sender.Save(args.Copy);
|
||||
{
|
||||
_contentService.Save(notification.Copy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a media has been created, auto-fill the properties.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
public void MediaServiceCreated(IMediaService sender, NewEventArgs<IMedia> args)
|
||||
public void Handle(ContentDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
public void Handle(MediaDeletedNotification notification) => DeleteContainedFiles(notification.DeletedEntities);
|
||||
|
||||
private void DeleteContainedFiles(IEnumerable<IContentBase> deletedEntities)
|
||||
{
|
||||
AutoFillProperties(args.Entity);
|
||||
var filePathsToDelete = ContainedFilePaths(deletedEntities);
|
||||
_mediaFileSystem.DeleteMediaFiles(filePathsToDelete);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a media has been saved, auto-fill the properties.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
public void MediaServiceSaving(IMediaService sender, SaveEventArgs<IMedia> args)
|
||||
public void Handle(MediaSavingNotification notification)
|
||||
{
|
||||
foreach (var entity in args.SavedEntities)
|
||||
AutoFillProperties(entity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a content item has been saved, auto-fill the properties.
|
||||
/// </summary>
|
||||
/// <param name="sender">The event sender.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
public void ContentServiceSaving(IContentService sender, SaveEventArgs<IContent> args)
|
||||
{
|
||||
foreach (var entity in args.SavedEntities)
|
||||
foreach (var entity in notification.SavedEntities)
|
||||
{
|
||||
AutoFillProperties(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
using System;
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.PropertyEditors;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// A component for NestedContent used to bind to events
|
||||
/// A handler for NestedContent used to bind to notifications
|
||||
/// </summary>
|
||||
public class NestedContentPropertyComponent : IComponent
|
||||
public class NestedContentPropertyHandler : ComplexPropertyEditorContentNotificationHandler
|
||||
{
|
||||
private ComplexPropertyEditorContentEventHandler _handler;
|
||||
protected override string EditorAlias => Constants.PropertyEditors.Aliases.NestedContent;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_handler = new ComplexPropertyEditorContentEventHandler(
|
||||
Constants.PropertyEditors.Aliases.NestedContent,
|
||||
CreateNestedContentKeys);
|
||||
}
|
||||
|
||||
public void Terminate() => _handler?.Dispose();
|
||||
|
||||
private string CreateNestedContentKeys(string rawJson, bool onlyMissingKeys) => CreateNestedContentKeys(rawJson, onlyMissingKeys, null);
|
||||
protected override string FormatPropertyValue(string rawJson, bool onlyMissingKeys) => CreateNestedContentKeys(rawJson, onlyMissingKeys, null);
|
||||
|
||||
// internal for tests
|
||||
internal string CreateNestedContentKeys(string rawJson, bool onlyMissingKeys, Func<Guid> createGuid = null)
|
||||
@@ -78,6 +70,5 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -12,6 +12,7 @@ using Umbraco.Cms.Core.Services.Implement;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
// TODO: delete this component and make the "ContainedFilePaths" methods on FileUploadPropertyEditor and ImageCropperPropertyEditor private once MemberService uses notifications instead of static events
|
||||
public sealed class PropertyEditorsComponent : IComponent
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
@@ -40,40 +41,14 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
|
||||
private void Initialize(FileUploadPropertyEditor fileUpload)
|
||||
{
|
||||
MediaService.Saving += fileUpload.MediaServiceSaving;
|
||||
_terminate.Add(() => MediaService.Saving -= fileUpload.MediaServiceSaving);
|
||||
ContentService.Copied += fileUpload.ContentServiceCopied;
|
||||
_terminate.Add(() => ContentService.Copied -= fileUpload.ContentServiceCopied);
|
||||
|
||||
void mediaServiceDeleted(IMediaService sender, DeleteEventArgs<IMedia> args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
||||
MediaService.Deleted += mediaServiceDeleted;
|
||||
_terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted);
|
||||
|
||||
void contentServiceDeleted(IContentService sender, DeleteEventArgs<IContent> args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
||||
ContentService.Deleted += contentServiceDeleted;
|
||||
_terminate.Add(() => ContentService.Deleted -= contentServiceDeleted);
|
||||
|
||||
void memberServiceDeleted(IMemberService sender, DeleteEventArgs<IMember> args) => args.MediaFilesToDelete.AddRange(fileUpload.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
||||
void memberServiceDeleted(IMemberService sender, DeleteEventArgs<IMember> args) => args.MediaFilesToDelete.AddRange(fileUpload.ContainedFilePaths(args.DeletedEntities.Cast<ContentBase>()));
|
||||
MemberService.Deleted += memberServiceDeleted;
|
||||
_terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
|
||||
}
|
||||
|
||||
private void Initialize(ImageCropperPropertyEditor imageCropper)
|
||||
{
|
||||
MediaService.Saving += imageCropper.MediaServiceSaving;
|
||||
_terminate.Add(() => MediaService.Saving -= imageCropper.MediaServiceSaving);
|
||||
ContentService.Copied += imageCropper.ContentServiceCopied;
|
||||
_terminate.Add(() => ContentService.Copied -= imageCropper.ContentServiceCopied);
|
||||
|
||||
void mediaServiceDeleted(IMediaService sender, DeleteEventArgs<IMedia> args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
||||
MediaService.Deleted += mediaServiceDeleted;
|
||||
_terminate.Add(() => MediaService.Deleted -= mediaServiceDeleted);
|
||||
|
||||
void contentServiceDeleted(IContentService sender, DeleteEventArgs<IContent> args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
||||
ContentService.Deleted += contentServiceDeleted;
|
||||
_terminate.Add(() => ContentService.Deleted -= contentServiceDeleted);
|
||||
|
||||
void memberServiceDeleted(IMemberService sender, DeleteEventArgs<IMember> args) => args.MediaFilesToDelete.AddRange(imageCropper.ServiceDeleted(args.DeletedEntities.Cast<ContentBase>()));
|
||||
void memberServiceDeleted(IMemberService sender, DeleteEventArgs<IMember> args) => args.MediaFilesToDelete.AddRange(imageCropper.ContainedFilePaths(args.DeletedEntities.Cast<ContentBase>()));
|
||||
MemberService.Deleted += memberServiceDeleted;
|
||||
_terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
|
||||
}
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
|
||||
namespace Umbraco.Cms.Core.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements an Application Event Handler for managing redirect URLs tracking.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>when content is renamed or moved, we want to create a permanent 301 redirect from it's old URL</para>
|
||||
/// <para>not managing domains because we don't know how to do it - changing domains => must create a higher level strategy using rewriting rules probably</para>
|
||||
/// <para>recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same</para>
|
||||
/// </remarks>
|
||||
public class RedirectTrackingComposer : ComponentComposer<RedirectTrackingComponent>, ICoreComposer
|
||||
{ }
|
||||
}
|
||||
@@ -1,36 +1,42 @@
|
||||
using System;
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.PublishedContent;
|
||||
using Umbraco.Cms.Core.PublishedCache;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Core.Services.Implement;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Routing
|
||||
{
|
||||
/// Implements an Application Event Handler for managing redirect URLs tracking.
|
||||
/// Implements a notification handler for managing redirect URLs tracking.
|
||||
/// <para>when content is renamed or moved, we want to create a permanent 301 redirect from it's old URL</para>
|
||||
/// <para>
|
||||
/// not managing domains because we don't know how to do it - changing domains => must create a higher level
|
||||
/// strategy using rewriting rules probably
|
||||
/// </para>
|
||||
/// <para>recycle bin = moving to and from does nothing: to = the node is gone, where would we redirect? from = same</para>
|
||||
public sealed class RedirectTrackingComponent : IComponent
|
||||
public sealed class RedirectTrackingHandler :
|
||||
INotificationHandler<ContentPublishingNotification>,
|
||||
INotificationHandler<ContentPublishedNotification>,
|
||||
INotificationHandler<ContentMovingNotification>,
|
||||
INotificationHandler<ContentMovedNotification>
|
||||
{
|
||||
private const string _eventStateKey = "Umbraco.Web.Redirects.RedirectTrackingEventHandler";
|
||||
|
||||
private readonly IOptionsMonitor<WebRoutingSettings> _webRoutingSettings;
|
||||
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||
private readonly IRedirectUrlService _redirectUrlService;
|
||||
private readonly IVariationContextAccessor _variationContextAccessor;
|
||||
|
||||
public RedirectTrackingComponent(IOptionsMonitor<WebRoutingSettings> webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor)
|
||||
private const string NotificationStateKey = "Umbraco.Cms.Core.Routing.RedirectTrackingHandler";
|
||||
|
||||
public RedirectTrackingHandler(IOptionsMonitor<WebRoutingSettings> webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor)
|
||||
{
|
||||
_webRoutingSettings = webRoutingSettings;
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
@@ -38,86 +44,53 @@ namespace Umbraco.Cms.Core.Routing
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Handle(ContentPublishingNotification notification) => StoreOldRoutes(notification.PublishedEntities, notification);
|
||||
|
||||
public void Handle(ContentPublishedNotification notification) => CreateRedirectsForOldRoutes(notification);
|
||||
|
||||
public void Handle(ContentMovingNotification notification) => StoreOldRoutes(notification.MoveInfoCollection.Select(m => m.Entity), notification);
|
||||
|
||||
public void Handle(ContentMovedNotification notification) => CreateRedirectsForOldRoutes(notification);
|
||||
|
||||
private void StoreOldRoutes(IEnumerable<IContent> entities, IStatefulNotification notification)
|
||||
{
|
||||
ContentService.Publishing += ContentService_Publishing;
|
||||
ContentService.Published += ContentService_Published;
|
||||
ContentService.Moving += ContentService_Moving;
|
||||
ContentService.Moved += ContentService_Moved;
|
||||
// don't let the notification handlers kick in if Redirect Tracking is turned off in the config
|
||||
if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking)
|
||||
return;
|
||||
|
||||
// kill all redirects once a content is deleted
|
||||
//ContentService.Deleted += ContentService_Deleted;
|
||||
// BUT, doing it here would prevent content deletion due to FK
|
||||
// so the rows are actually deleted by the ContentRepository (see GetDeleteClauses)
|
||||
|
||||
// rolled back items have to be published, so publishing will take care of that
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ContentService.Publishing -= ContentService_Publishing;
|
||||
ContentService.Published -= ContentService_Published;
|
||||
ContentService.Moving -= ContentService_Moving;
|
||||
ContentService.Moved -= ContentService_Moved;
|
||||
}
|
||||
|
||||
private void ContentService_Publishing(IContentService sender, PublishEventArgs<IContent> args)
|
||||
{
|
||||
// don't let the event handlers kick in if Redirect Tracking is turned off in the config
|
||||
if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return;
|
||||
|
||||
var oldRoutes = GetOldRoutes(args.EventState);
|
||||
foreach (var entity in args.PublishedEntities)
|
||||
var oldRoutes = GetOldRoutes(notification);
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
StoreOldRoute(entity, oldRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void ContentService_Published(IContentService sender, ContentPublishedEventArgs args)
|
||||
private void CreateRedirectsForOldRoutes(IStatefulNotification notification)
|
||||
{
|
||||
// don't let the event handlers kick in if Redirect Tracking is turned off in the config
|
||||
if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return;
|
||||
// don't let the notification handlers kick in if Redirect Tracking is turned off in the config
|
||||
if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking)
|
||||
return;
|
||||
|
||||
var oldRoutes = GetOldRoutes(args.EventState);
|
||||
var oldRoutes = GetOldRoutes(notification);
|
||||
CreateRedirects(oldRoutes);
|
||||
}
|
||||
|
||||
private void ContentService_Moving(IContentService sender, MoveEventArgs<IContent> args)
|
||||
private OldRoutesDictionary GetOldRoutes(IStatefulNotification notification)
|
||||
{
|
||||
// don't let the event handlers kick in if Redirect Tracking is turned off in the config
|
||||
if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return;
|
||||
|
||||
var oldRoutes = GetOldRoutes(args.EventState);
|
||||
foreach (var info in args.MoveInfoCollection)
|
||||
if (notification.State.ContainsKey(NotificationStateKey) == false)
|
||||
{
|
||||
StoreOldRoute(info.Entity, oldRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> args)
|
||||
{
|
||||
// don't let the event handlers kick in if Redirect Tracking is turned off in the config
|
||||
if (_webRoutingSettings.CurrentValue.DisableRedirectUrlTracking) return;
|
||||
|
||||
var oldRoutes = GetOldRoutes(args.EventState);
|
||||
CreateRedirects(oldRoutes);
|
||||
}
|
||||
|
||||
private OldRoutesDictionary GetOldRoutes(IDictionary<string, object> eventState)
|
||||
{
|
||||
if (! eventState.ContainsKey(_eventStateKey))
|
||||
{
|
||||
eventState[_eventStateKey] = new OldRoutesDictionary();
|
||||
notification.State[NotificationStateKey] = new OldRoutesDictionary();
|
||||
}
|
||||
|
||||
return eventState[_eventStateKey] as OldRoutesDictionary;
|
||||
return (OldRoutesDictionary)notification.State[NotificationStateKey];
|
||||
}
|
||||
|
||||
private void StoreOldRoute(IContent entity, OldRoutesDictionary oldRoutes)
|
||||
{
|
||||
var contentCache = _publishedSnapshotAccessor.PublishedSnapshot.Content;
|
||||
var entityContent = contentCache.GetById(entity.Id);
|
||||
if (entityContent == null) return;
|
||||
if (entityContent == null)
|
||||
return;
|
||||
|
||||
// get the default affected cultures by going up the tree until we find the first culture variant entity (default to no cultures)
|
||||
var defaultCultures = entityContent.AncestorsOrSelf()?.FirstOrDefault(a => a.Cultures.Any())?.Cultures.Keys.ToArray()
|
||||
@@ -130,7 +103,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
foreach (var culture in cultures)
|
||||
{
|
||||
var route = contentCache.GetRouteById(x.Id, culture);
|
||||
if (IsNotRoute(route)) continue;
|
||||
if (IsNotRoute(route))
|
||||
continue;
|
||||
oldRoutes[new ContentIdAndCulture(x.Id, culture)] = new ContentKeyAndOldRoute(x.Key, route);
|
||||
}
|
||||
}
|
||||
@@ -143,7 +117,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
foreach (var oldRoute in oldRoutes)
|
||||
{
|
||||
var newRoute = contentCache.GetRouteById(oldRoute.Key.ContentId, oldRoute.Key.Culture);
|
||||
if (IsNotRoute(newRoute) || oldRoute.Value.OldRoute == newRoute) continue;
|
||||
if (IsNotRoute(newRoute) || oldRoute.Value.OldRoute == newRoute)
|
||||
continue;
|
||||
_redirectUrlService.Register(oldRoute.Value.OldRoute, oldRoute.Value.ContentKey, oldRoute.Key.Culture);
|
||||
}
|
||||
}
|
||||
@@ -176,6 +151,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
}
|
||||
|
||||
private class OldRoutesDictionary : Dictionary<ContentIdAndCulture, ContentKeyAndOldRoute>
|
||||
{ }
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Infrastructure.Persistence;
|
||||
@@ -30,6 +30,11 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
/// </summary>
|
||||
IEventDispatcher Events { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scope notification publisher
|
||||
/// </summary>
|
||||
IScopedNotificationPublisher Notifications { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the repositories cache mode.
|
||||
/// </summary>
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
private readonly ScopeProvider _scopeProvider;
|
||||
private readonly CoreDebugSettings _coreDebugSettings;
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly ILogger<Scope> _logger;
|
||||
|
||||
private readonly IsolationLevel _isolationLevel;
|
||||
@@ -36,12 +37,15 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
private EventMessages _messages;
|
||||
private ICompletable _fscope;
|
||||
private IEventDispatcher _eventDispatcher;
|
||||
// eventually this may need to be injectable - for now we'll create it explicitly and let future needs determine if it should be injectable
|
||||
private IScopedNotificationPublisher _notificationPublisher;
|
||||
|
||||
// initializes a new scope
|
||||
private Scope(
|
||||
ScopeProvider scopeProvider,
|
||||
CoreDebugSettings coreDebugSettings,
|
||||
IMediaFileSystem mediaFileSystem,
|
||||
IEventAggregator eventAggregator,
|
||||
ILogger<Scope> logger,
|
||||
FileSystems fileSystems,
|
||||
Scope parent,
|
||||
@@ -57,6 +61,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
_scopeProvider = scopeProvider;
|
||||
_coreDebugSettings = coreDebugSettings;
|
||||
_mediaFileSystem = mediaFileSystem;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logger;
|
||||
|
||||
Context = scopeContext;
|
||||
@@ -70,6 +75,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
|
||||
Detachable = detachable;
|
||||
|
||||
|
||||
#if DEBUG_SCOPES
|
||||
_scopeProvider.RegisterScope(this);
|
||||
#endif
|
||||
@@ -146,6 +152,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
ScopeProvider scopeProvider,
|
||||
CoreDebugSettings coreDebugSettings,
|
||||
IMediaFileSystem mediaFileSystem,
|
||||
IEventAggregator eventAggregator,
|
||||
ILogger<Scope> logger,
|
||||
FileSystems fileSystems,
|
||||
bool detachable,
|
||||
@@ -156,7 +163,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false,
|
||||
bool autoComplete = false)
|
||||
: this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
|
||||
: this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, null, scopeContext, detachable, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
|
||||
{ }
|
||||
|
||||
// initializes a new scope in a nested scopes chain, with its parent
|
||||
@@ -164,6 +171,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
ScopeProvider scopeProvider,
|
||||
CoreDebugSettings coreDebugSettings,
|
||||
IMediaFileSystem mediaFileSystem,
|
||||
IEventAggregator eventAggregator,
|
||||
ILogger<Scope> logger,
|
||||
FileSystems fileSystems,
|
||||
Scope parent,
|
||||
@@ -173,7 +181,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
bool? scopeFileSystems = null,
|
||||
bool callContext = false,
|
||||
bool autoComplete = false)
|
||||
: this(scopeProvider, coreDebugSettings, mediaFileSystem, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
|
||||
: this(scopeProvider, coreDebugSettings, mediaFileSystem, eventAggregator, logger, fileSystems, parent, null, false, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete)
|
||||
{ }
|
||||
|
||||
public Guid InstanceId { get; } = Guid.NewGuid();
|
||||
@@ -381,6 +389,16 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
}
|
||||
}
|
||||
|
||||
public IScopedNotificationPublisher Notifications
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureNotDisposed();
|
||||
if (ParentScope != null) return ParentScope.Notifications;
|
||||
return _notificationPublisher ?? (_notificationPublisher = new ScopedNotificationPublisher(_eventAggregator));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Complete()
|
||||
{
|
||||
@@ -556,6 +574,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
if (onException == false)
|
||||
{
|
||||
_eventDispatcher?.ScopeExit(completed);
|
||||
_notificationPublisher?.ScopeExit(completed);
|
||||
}
|
||||
}, () =>
|
||||
{
|
||||
|
||||
@@ -34,8 +34,9 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
private static readonly AsyncLocal<ConcurrentStack<IScopeContext>> s_scopeContextStack = new AsyncLocal<ConcurrentStack<IScopeContext>>();
|
||||
private static readonly string s_scopeItemKey = typeof(Scope).FullName;
|
||||
private static readonly string s_contextItemKey = typeof(ScopeProvider).FullName;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
|
||||
public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions<CoreDebugSettings> coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger<ScopeProvider> logger, ILoggerFactory loggerFactory, IRequestCache requestCache)
|
||||
public ScopeProvider(IUmbracoDatabaseFactory databaseFactory, FileSystems fileSystems, IOptions<CoreDebugSettings> coreDebugSettings, IMediaFileSystem mediaFileSystem, ILogger<ScopeProvider> logger, ILoggerFactory loggerFactory, IRequestCache requestCache, IEventAggregator eventAggregator)
|
||||
{
|
||||
DatabaseFactory = databaseFactory;
|
||||
_fileSystems = fileSystems;
|
||||
@@ -44,6 +45,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
_logger = logger;
|
||||
_loggerFactory = loggerFactory;
|
||||
_requestCache = requestCache;
|
||||
_eventAggregator = eventAggregator;
|
||||
// take control of the FileSystems
|
||||
_fileSystems.IsScoped = () => AmbientScope != null && AmbientScope.ScopedFileSystems;
|
||||
}
|
||||
@@ -379,7 +381,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
RepositoryCacheMode repositoryCacheMode = RepositoryCacheMode.Unspecified,
|
||||
IEventDispatcher eventDispatcher = null,
|
||||
bool? scopeFileSystems = null)
|
||||
=> new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger<Scope>(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
|
||||
=> new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, true, null, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AttachScope(IScope other, bool callContext = false)
|
||||
@@ -458,7 +460,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
{
|
||||
IScopeContext ambientContext = AmbientContext;
|
||||
ScopeContext newContext = ambientContext == null ? new ScopeContext() : null;
|
||||
var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger<Scope>(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
|
||||
var scope = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, false, newContext, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
|
||||
// assign only if scope creation did not throw!
|
||||
PushAmbientScope(scope);
|
||||
if (newContext != null)
|
||||
@@ -468,7 +470,7 @@ namespace Umbraco.Cms.Core.Scoping
|
||||
return scope;
|
||||
}
|
||||
|
||||
var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _loggerFactory.CreateLogger<Scope>(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
|
||||
var nested = new Scope(this, _coreDebugSettings, _mediaFileSystem, _eventAggregator, _loggerFactory.CreateLogger<Scope>(), _fileSystems, ambientScope, isolationLevel, repositoryCacheMode, eventDispatcher, scopeFileSystems, callContext, autoComplete);
|
||||
PushAmbientScope(nested);
|
||||
return nested;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using Examine;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Cache;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Runtime;
|
||||
@@ -20,7 +20,13 @@ using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Search
|
||||
{
|
||||
public sealed class ExamineComponent : IComponent
|
||||
public sealed class ExamineNotificationHandler :
|
||||
INotificationHandler<UmbracoApplicationStarting>,
|
||||
INotificationHandler<ContentCacheRefresherNotification>,
|
||||
INotificationHandler<ContentTypeCacheRefresherNotification>,
|
||||
INotificationHandler<MediaCacheRefresherNotification>,
|
||||
INotificationHandler<MemberCacheRefresherNotification>,
|
||||
INotificationHandler<LanguageCacheRefresherNotification>
|
||||
{
|
||||
private readonly IExamineManager _examineManager;
|
||||
private readonly IContentValueSetBuilder _contentValueSetBuilder;
|
||||
@@ -33,18 +39,19 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
private readonly ServiceContext _services;
|
||||
private readonly IMainDom _mainDom;
|
||||
private readonly IProfilingLogger _profilingLogger;
|
||||
private readonly ILogger<ExamineComponent> _logger;
|
||||
private readonly ILogger<ExamineNotificationHandler> _logger;
|
||||
private readonly IUmbracoIndexesCreator _indexCreator;
|
||||
private static bool s_deactivate_handlers;
|
||||
|
||||
// the default enlist priority is 100
|
||||
// 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;
|
||||
|
||||
public ExamineComponent(IMainDom mainDom,
|
||||
public ExamineNotificationHandler(IMainDom mainDom,
|
||||
IExamineManager examineManager,
|
||||
IProfilingLogger profilingLogger,
|
||||
ILoggerFactory loggerFactory,
|
||||
ILogger<ExamineNotificationHandler> logger,
|
||||
IScopeProvider scopeProvider,
|
||||
IUmbracoIndexesCreator indexCreator,
|
||||
ServiceContext services,
|
||||
@@ -66,16 +73,15 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
_taskHelper = taskHelper;
|
||||
_mainDom = mainDom;
|
||||
_profilingLogger = profilingLogger;
|
||||
_logger = loggerFactory.CreateLogger<ExamineComponent>();
|
||||
_logger = logger;
|
||||
_indexCreator = indexCreator;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Handle(UmbracoApplicationStarting notification)
|
||||
{
|
||||
//let's deal with shutting down Examine with MainDom
|
||||
var examineShutdownRegistered = _mainDom.Register(release: () =>
|
||||
{
|
||||
using (_profilingLogger.TraceDuration<ExamineComponent>("Examine shutting down"))
|
||||
using (_profilingLogger.TraceDuration<ExamineNotificationHandler>("Examine shutting down"))
|
||||
{
|
||||
_examineManager.Dispose();
|
||||
}
|
||||
@@ -105,26 +111,12 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
// don't bind event handlers if we're not suppose to listen
|
||||
if (registeredIndexers == 0)
|
||||
{
|
||||
return;
|
||||
s_deactivate_handlers = true;
|
||||
}
|
||||
|
||||
// bind to distributed cache events - this ensures that this logic occurs on ALL servers
|
||||
// that are taking part in a load balanced environment.
|
||||
ContentCacheRefresher.CacheUpdated += ContentCacheRefresherUpdated;
|
||||
ContentTypeCacheRefresher.CacheUpdated += ContentTypeCacheRefresherUpdated;
|
||||
MediaCacheRefresher.CacheUpdated += MediaCacheRefresherUpdated;
|
||||
MemberCacheRefresher.CacheUpdated += MemberCacheRefresherUpdated;
|
||||
LanguageCacheRefresher.CacheUpdated += LanguageCacheRefresherUpdated;
|
||||
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
ContentCacheRefresher.CacheUpdated -= ContentCacheRefresherUpdated;
|
||||
ContentTypeCacheRefresher.CacheUpdated -= ContentTypeCacheRefresherUpdated;
|
||||
MediaCacheRefresher.CacheUpdated -= MediaCacheRefresherUpdated;
|
||||
MemberCacheRefresher.CacheUpdated -= MemberCacheRefresherUpdated;
|
||||
LanguageCacheRefresher.CacheUpdated -= LanguageCacheRefresherUpdated;
|
||||
}
|
||||
|
||||
#region Cache refresher updated event handlers
|
||||
|
||||
@@ -133,8 +125,12 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private void ContentCacheRefresherUpdated(ContentCacheRefresher sender, CacheRefresherEventArgs args)
|
||||
public void Handle(ContentCacheRefresherNotification args)
|
||||
{
|
||||
if (s_deactivate_handlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Suspendable.ExamineEvents.CanIndex == false)
|
||||
{
|
||||
return;
|
||||
@@ -237,8 +233,13 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
|
||||
private void MemberCacheRefresherUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs args)
|
||||
public void Handle(MemberCacheRefresherNotification args)
|
||||
{
|
||||
if (s_deactivate_handlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Suspendable.ExamineEvents.CanIndex == false)
|
||||
{
|
||||
return;
|
||||
@@ -300,8 +301,13 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
|
||||
private void MediaCacheRefresherUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs args)
|
||||
public void Handle(MediaCacheRefresherNotification args)
|
||||
{
|
||||
if (s_deactivate_handlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Suspendable.ExamineEvents.CanIndex == false)
|
||||
{
|
||||
return;
|
||||
@@ -364,9 +370,14 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
|
||||
private void LanguageCacheRefresherUpdated(LanguageCacheRefresher sender, CacheRefresherEventArgs e)
|
||||
public void Handle(LanguageCacheRefresherNotification args)
|
||||
{
|
||||
if (!(e.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads))
|
||||
if (s_deactivate_handlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(args.MessageObject is LanguageCacheRefresher.JsonPayload[] payloads))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -393,8 +404,13 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
private void ContentTypeCacheRefresherUpdated(ContentTypeCacheRefresher sender, CacheRefresherEventArgs args)
|
||||
public void Handle(ContentTypeCacheRefresherNotification args)
|
||||
{
|
||||
if (s_deactivate_handlers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Suspendable.ExamineEvents.CanIndex == false)
|
||||
{
|
||||
return;
|
||||
@@ -668,34 +684,34 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
private class DeferedReIndexForContent : DeferedAction
|
||||
{
|
||||
private readonly TaskHelper _taskHelper;
|
||||
private readonly ExamineComponent _examineComponent;
|
||||
private readonly ExamineNotificationHandler _ExamineNotificationHandler;
|
||||
private readonly IContent _content;
|
||||
private readonly bool _isPublished;
|
||||
|
||||
public DeferedReIndexForContent(TaskHelper taskHelper, ExamineComponent examineComponent, IContent content, bool isPublished)
|
||||
public DeferedReIndexForContent(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished)
|
||||
{
|
||||
_taskHelper = taskHelper;
|
||||
_examineComponent = examineComponent;
|
||||
_ExamineNotificationHandler = ExamineNotificationHandler;
|
||||
_content = content;
|
||||
_isPublished = isPublished;
|
||||
}
|
||||
|
||||
public override void Execute() => Execute(_taskHelper, _examineComponent, _content, _isPublished);
|
||||
public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _content, _isPublished);
|
||||
|
||||
public static void Execute(TaskHelper taskHelper, ExamineComponent examineComponent, IContent content, bool isPublished)
|
||||
public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IContent content, bool isPublished)
|
||||
=> taskHelper.RunBackgroundTask(() =>
|
||||
{
|
||||
using IScope scope = examineComponent._scopeProvider.CreateScope(autoComplete: true);
|
||||
using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(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>>(() => examineComponent._publishedContentValueSetBuilder.GetValueSets(content).ToList()),
|
||||
[false] = new Lazy<List<ValueSet>>(() => examineComponent._contentValueSetBuilder.GetValueSets(content).ToList())
|
||||
[true] = new Lazy<List<ValueSet>>(() => ExamineNotificationHandler._publishedContentValueSetBuilder.GetValueSets(content).ToList()),
|
||||
[false] = new Lazy<List<ValueSet>>(() => ExamineNotificationHandler._contentValueSetBuilder.GetValueSets(content).ToList())
|
||||
};
|
||||
|
||||
foreach (IUmbracoIndex index in examineComponent._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
//filter the indexers
|
||||
.Where(x => isPublished || !x.PublishedValuesOnly)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
@@ -714,29 +730,29 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
private class DeferedReIndexForMedia : DeferedAction
|
||||
{
|
||||
private readonly TaskHelper _taskHelper;
|
||||
private readonly ExamineComponent _examineComponent;
|
||||
private readonly ExamineNotificationHandler _ExamineNotificationHandler;
|
||||
private readonly IMedia _media;
|
||||
private readonly bool _isPublished;
|
||||
|
||||
public DeferedReIndexForMedia(TaskHelper taskHelper, ExamineComponent examineComponent, IMedia media, bool isPublished)
|
||||
public DeferedReIndexForMedia(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished)
|
||||
{
|
||||
_taskHelper = taskHelper;
|
||||
_examineComponent = examineComponent;
|
||||
_ExamineNotificationHandler = ExamineNotificationHandler;
|
||||
_media = media;
|
||||
_isPublished = isPublished;
|
||||
}
|
||||
|
||||
public override void Execute() => Execute(_taskHelper, _examineComponent, _media, _isPublished);
|
||||
public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _media, _isPublished);
|
||||
|
||||
public static void Execute(TaskHelper taskHelper, ExamineComponent examineComponent, IMedia media, bool isPublished) =>
|
||||
public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMedia media, bool isPublished) =>
|
||||
// perform the ValueSet lookup on a background thread
|
||||
taskHelper.RunBackgroundTask(() =>
|
||||
{
|
||||
using IScope scope = examineComponent._scopeProvider.CreateScope(autoComplete: true);
|
||||
using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true);
|
||||
|
||||
var valueSet = examineComponent._mediaValueSetBuilder.GetValueSets(media).ToList();
|
||||
var valueSet = ExamineNotificationHandler._mediaValueSetBuilder.GetValueSets(media).ToList();
|
||||
|
||||
foreach (IUmbracoIndex index in examineComponent._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
//filter the indexers
|
||||
.Where(x => isPublished || !x.PublishedValuesOnly)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
@@ -753,27 +769,27 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
/// </summary>
|
||||
private class DeferedReIndexForMember : DeferedAction
|
||||
{
|
||||
private readonly ExamineComponent _examineComponent;
|
||||
private readonly ExamineNotificationHandler _ExamineNotificationHandler;
|
||||
private readonly IMember _member;
|
||||
private readonly TaskHelper _taskHelper;
|
||||
|
||||
public DeferedReIndexForMember(TaskHelper taskHelper, ExamineComponent examineComponent, IMember member)
|
||||
public DeferedReIndexForMember(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member)
|
||||
{
|
||||
_examineComponent = examineComponent;
|
||||
_ExamineNotificationHandler = ExamineNotificationHandler;
|
||||
_member = member;
|
||||
_taskHelper = taskHelper;
|
||||
}
|
||||
|
||||
public override void Execute() => Execute(_taskHelper, _examineComponent, _member);
|
||||
public override void Execute() => Execute(_taskHelper, _ExamineNotificationHandler, _member);
|
||||
|
||||
public static void Execute(TaskHelper taskHelper, ExamineComponent examineComponent, IMember member) =>
|
||||
public static void Execute(TaskHelper taskHelper, ExamineNotificationHandler ExamineNotificationHandler, IMember member) =>
|
||||
// perform the ValueSet lookup on a background thread
|
||||
taskHelper.RunBackgroundTask(() =>
|
||||
{
|
||||
using IScope scope = examineComponent._scopeProvider.CreateScope(autoComplete: true);
|
||||
using IScope scope = ExamineNotificationHandler._scopeProvider.CreateScope(autoComplete: true);
|
||||
|
||||
var valueSet = examineComponent._memberValueSetBuilder.GetValueSets(member).ToList();
|
||||
foreach (IUmbracoIndex index in examineComponent._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
var valueSet = ExamineNotificationHandler._memberValueSetBuilder.GetValueSets(member).ToList();
|
||||
foreach (IUmbracoIndex index in ExamineNotificationHandler._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
//filter the indexers
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
{
|
||||
@@ -786,23 +802,23 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
|
||||
private class DeferedDeleteIndex : DeferedAction
|
||||
{
|
||||
private readonly ExamineComponent _examineComponent;
|
||||
private readonly ExamineNotificationHandler _ExamineNotificationHandler;
|
||||
private readonly int _id;
|
||||
private readonly bool _keepIfUnpublished;
|
||||
|
||||
public DeferedDeleteIndex(ExamineComponent examineComponent, int id, bool keepIfUnpublished)
|
||||
public DeferedDeleteIndex(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished)
|
||||
{
|
||||
_examineComponent = examineComponent;
|
||||
_ExamineNotificationHandler = ExamineNotificationHandler;
|
||||
_id = id;
|
||||
_keepIfUnpublished = keepIfUnpublished;
|
||||
}
|
||||
|
||||
public override void Execute() => Execute(_examineComponent, _id, _keepIfUnpublished);
|
||||
public override void Execute() => Execute(_ExamineNotificationHandler, _id, _keepIfUnpublished);
|
||||
|
||||
public static void Execute(ExamineComponent examineComponent, int id, bool keepIfUnpublished)
|
||||
public static void Execute(ExamineNotificationHandler ExamineNotificationHandler, int id, bool keepIfUnpublished)
|
||||
{
|
||||
var strId = id.ToString(CultureInfo.InvariantCulture);
|
||||
foreach (var index in examineComponent._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
foreach (var index in ExamineNotificationHandler._examineManager.Indexes.OfType<IUmbracoIndex>()
|
||||
.Where(x => x.PublishedValuesOnly || !keepIfUnpublished)
|
||||
.Where(x => x.EnableDefaultEventHandler))
|
||||
{
|
||||
@@ -811,7 +827,5 @@ namespace Umbraco.Cms.Infrastructure.Search
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Security.Principal;
|
||||
using Umbraco.Cms.Infrastructure.Security;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
@@ -5,5 +8,8 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// </summary>
|
||||
public interface IBackOfficeUserManager : IUmbracoUserManager<BackOfficeIdentityUser>
|
||||
{
|
||||
void NotifyForgotPasswordRequested(IPrincipal currentUser, string userId);
|
||||
void NotifyForgotPasswordChanged(IPrincipal currentUser, string userId);
|
||||
SignOutSuccessResult NotifyLogoutSuccess(IPrincipal currentUser, string userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Models.Identity;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
@@ -379,16 +376,5 @@ namespace Umbraco.Cms.Core.Security
|
||||
/// A user can only support a phone number if the BackOfficeUserStore is replaced with another that implements IUserPhoneNumberStore
|
||||
/// </remarks>
|
||||
Task<string> GetPhoneNumberAsync(TUser user);
|
||||
|
||||
// TODO: These are raised from outside the signinmanager and usermanager in the auth and user controllers,
|
||||
// let's see if there's a way to avoid that and only have these called within signinmanager and usermanager
|
||||
// which means we can remove these from the interface (things like invite seems like they cannot be moved)
|
||||
// TODO: When we change to not having the crappy static events this will need to be revisited
|
||||
void RaiseForgotPasswordRequestedEvent(IPrincipal currentUser, string userId);
|
||||
void RaiseForgotPasswordChangedSuccessEvent(IPrincipal currentUser, string userId);
|
||||
SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, string userId);
|
||||
UserInviteEventArgs RaiseSendingUserInvite(IPrincipal currentUser, UserInvite invite, IUser createdUser);
|
||||
bool HasSendingUserInviteEventHandler { get; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// Event args used when signing out
|
||||
/// </summary>
|
||||
public class SignOutAuditEventArgs : IdentityAuditEventArgs
|
||||
{
|
||||
public SignOutAuditEventArgs(AuditEvent action, string ipAddress, string comment = null, string performingUser = Cms.Core.Constants.Security.SuperUserIdAsString, string affectedUser = Cms.Core.Constants.Security.SuperUserIdAsString)
|
||||
: base(action, ipAddress, performingUser, comment, affectedUser, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows event handlers to set a GET absolute URL to be redirected to after successful logout out of the back office. This
|
||||
/// can be used for external login providers.
|
||||
/// </summary>
|
||||
public string SignOutRedirectUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Cms.Infrastructure.Security
|
||||
{
|
||||
public class SignOutSuccessResult
|
||||
{
|
||||
public string SignOutRedirectUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Cms.Core.Security
|
||||
{
|
||||
public class UserInviteEventArgs : IdentityAuditEventArgs
|
||||
{
|
||||
public UserInviteEventArgs(string ipAddress, string performingUser, UserInvite invitedUser, IUser localUser, string comment = null)
|
||||
: base(AuditEvent.SendingUserInvite, ipAddress, performingUser, comment, string.Intern(localUser.Id.ToString()), localUser.Name)
|
||||
{
|
||||
InvitedUser = invitedUser ?? throw new System.ArgumentNullException(nameof(invitedUser));
|
||||
User = localUser;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The model used to invite the user
|
||||
/// </summary>
|
||||
public UserInvite InvitedUser { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If event handler sets this to true it indicates that Umbraco will no try to send the invite itself
|
||||
/// </summary>
|
||||
public bool InviteHandled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The local user that has been created that is pending the invite
|
||||
/// </summary>
|
||||
public IUser User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// if set to true will show the edit user button in the UI, else it will not be shown
|
||||
/// </summary>
|
||||
public bool ShowUserResult { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Querying;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
@@ -747,8 +748,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -773,7 +774,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved));
|
||||
scope.Notifications.Publish(new ContentSavedNotification(content, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
var changeType = TreeChangeTypes.RefreshNode;
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(content, changeType).ToEventArgs());
|
||||
@@ -802,8 +803,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new ContentSavingEventArgs(contentsA, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(contentsA, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -823,7 +824,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved));
|
||||
scope.Notifications.Publish(new ContentSavedNotification(contentsA, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs());
|
||||
Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content");
|
||||
@@ -867,9 +868,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
}
|
||||
|
||||
// if culture is specific, first publish the invariant values, then publish the culture itself.
|
||||
// if culture is '*', then publish them all (including variants)
|
||||
@@ -881,7 +884,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// we don't care about the response here, this response will be rechecked below but we need to set the culture info values now.
|
||||
content.PublishCulture(impact);
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
||||
var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -905,9 +908,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
|
||||
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
}
|
||||
|
||||
var varies = content.ContentType.VariesByCulture();
|
||||
|
||||
@@ -927,7 +933,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
foreach (var impact in impacts)
|
||||
content.PublishCulture(impact);
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
||||
var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -969,9 +975,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
}
|
||||
|
||||
// all cultures = unpublish whole
|
||||
if (culture == "*" || (!content.ContentType.VariesByCulture() && culture == null))
|
||||
@@ -982,7 +990,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// to be non-routable so that when it's re-published all variants were as they were.
|
||||
|
||||
content.PublishedState = PublishedState.Unpublishing;
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId);
|
||||
var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -996,7 +1004,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var removed = content.UnpublishCulture(culture);
|
||||
|
||||
//save and publish any changes
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId);
|
||||
var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId);
|
||||
|
||||
scope.Complete();
|
||||
|
||||
@@ -1039,13 +1047,15 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.ContentTree);
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(content, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(content, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
}
|
||||
|
||||
var allLangs = _languageRepository.GetMany().ToList();
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, content, saveEventArgs, allLangs, userId, raiseEvents);
|
||||
var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents);
|
||||
scope.Complete();
|
||||
return result;
|
||||
}
|
||||
@@ -1056,7 +1066,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
/// <param name="scope"></param>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="saveEventArgs"></param>
|
||||
/// <param name="notificationState"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <param name="branchOne"></param>
|
||||
@@ -1069,15 +1079,14 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content,
|
||||
ContentSavingEventArgs saveEventArgs, IReadOnlyCollection<ILanguage> allLangs,
|
||||
EventMessages evtMsgs, IReadOnlyCollection<ILanguage> allLangs,
|
||||
IDictionary<string, object> notificationState,
|
||||
int userId = Cms.Core.Constants.Security.SuperUserId,
|
||||
bool raiseEvents = true, bool branchOne = false, bool branchRoot = false)
|
||||
{
|
||||
if (scope == null) throw new ArgumentNullException(nameof(scope));
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (saveEventArgs == null) throw new ArgumentNullException(nameof(saveEventArgs));
|
||||
|
||||
var evtMsgs = saveEventArgs.Messages;
|
||||
if (evtMsgs == null) throw new ArgumentNullException(nameof(evtMsgs));
|
||||
|
||||
PublishResult publishResult = null;
|
||||
PublishResult unpublishResult = null;
|
||||
@@ -1125,7 +1134,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
: null;
|
||||
|
||||
// ensure that the document can be published, and publish handling events, business rules, etc
|
||||
publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, saveEventArgs, allLangs);
|
||||
publishResult = StrategyCanPublish(scope, content, /*checkPath:*/ (!branchOne || branchRoot), culturesPublishing, culturesUnpublishing, evtMsgs, allLangs, notificationState);
|
||||
if (publishResult.Success)
|
||||
{
|
||||
// note: StrategyPublish flips the PublishedState to Publishing!
|
||||
@@ -1210,7 +1219,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// raise the Saved event, always
|
||||
if (raiseEvents)
|
||||
{
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved));
|
||||
scope.Notifications.Publish(new ContentSavedNotification(content, evtMsgs).WithState(notificationState));
|
||||
}
|
||||
|
||||
if (unpublishing) // we have tried to unpublish - won't happen in a branch
|
||||
@@ -1218,7 +1227,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (unpublishResult.Success) // and succeeded, trigger events
|
||||
{
|
||||
// events and audit
|
||||
scope.Events.Dispatch(Unpublished, this, new PublishEventArgs<IContent>(content, false, false), "Unpublished");
|
||||
scope.Notifications.Publish(new ContentUnpublishedNotification(content, evtMsgs).WithState(notificationState));
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(content, TreeChangeTypes.RefreshBranch).ToEventArgs());
|
||||
|
||||
if (culturesUnpublishing != null)
|
||||
@@ -1273,7 +1282,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (!branchOne) // for branches, handled by SaveAndPublishBranch
|
||||
{
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(content, changeType).ToEventArgs());
|
||||
scope.Events.Dispatch(Published, this, saveEventArgs.ToContentPublishedEventArgs(), nameof(Published));
|
||||
scope.Notifications.Publish(new ContentPublishedNotification(content, evtMsgs).WithState(notificationState));
|
||||
}
|
||||
|
||||
// it was not published and now is... descendants that were 'published' (but
|
||||
@@ -1282,7 +1291,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (!branchOne && isNew == false && previouslyPublished == false && HasChildren(content.Id))
|
||||
{
|
||||
var descendants = GetPublishedDescendantsLocked(content).ToArray();
|
||||
scope.Events.Dispatch(Published, this, new ContentPublishedEventArgs(descendants, false, evtMsgs), "Published");
|
||||
scope.Notifications.Publish(new ContentPublishedNotification(descendants, evtMsgs).WithState(notificationState));
|
||||
}
|
||||
|
||||
switch (publishResult.Result)
|
||||
@@ -1375,8 +1384,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (pendingCultures.Count == 0)
|
||||
continue; //shouldn't happen but no point in processing this document if there's nothing there
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(d, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(d, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d));
|
||||
continue;
|
||||
@@ -1390,7 +1399,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
d.UnpublishCulture(c);
|
||||
}
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs.Value, d.WriterId);
|
||||
var result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, savingNotification.State, d.WriterId);
|
||||
if (result.Success == false)
|
||||
_logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result);
|
||||
results.Add(result);
|
||||
@@ -1436,11 +1445,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (pendingCultures.Count == 0)
|
||||
continue; //shouldn't happen but no point in processing this document if there's nothing there
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(d, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(d, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d));
|
||||
continue; // this document is canceled move next
|
||||
continue;
|
||||
}
|
||||
|
||||
var publishing = true;
|
||||
@@ -1470,7 +1479,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
else if (!publishing)
|
||||
result = new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, d);
|
||||
else
|
||||
result = CommitDocumentChangesInternal(scope, d, saveEventArgs, allLangs.Value, d.WriterId);
|
||||
result = CommitDocumentChangesInternal(scope, d, evtMsgs, allLangs.Value, savingNotification.State, d.WriterId);
|
||||
|
||||
if (result.Success == false)
|
||||
_logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result);
|
||||
@@ -1694,7 +1703,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// trigger events for the entire branch
|
||||
// (SaveAndPublishBranchOne does *not* do it)
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(document, TreeChangeTypes.RefreshBranch).ToEventArgs());
|
||||
scope.Events.Dispatch(Published, this, new ContentPublishedEventArgs(publishedDocuments, false, evtMsgs), nameof(Published));
|
||||
scope.Notifications.Publish(new ContentPublishedNotification(publishedDocuments, evtMsgs));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -1718,9 +1727,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (culturesToPublish.Count == 0) // empty = already published
|
||||
return new PublishResult(PublishResultType.SuccessPublishAlready, evtMsgs, document);
|
||||
|
||||
var saveEventArgs = new ContentSavingEventArgs(document, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Saving)))
|
||||
var savingNotification = new ContentSavingNotification(document, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document);
|
||||
}
|
||||
|
||||
// publish & check if values are valid
|
||||
if (!publishCultures(document, culturesToPublish, allLangs))
|
||||
@@ -1729,7 +1740,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return new PublishResult(PublishResultType.FailedPublishContentInvalid, evtMsgs, document);
|
||||
}
|
||||
|
||||
var result = CommitDocumentChangesInternal(scope, document, saveEventArgs, allLangs, userId, branchOne: true, branchRoot: isRoot);
|
||||
var result = CommitDocumentChangesInternal(scope, document, evtMsgs, allLangs, savingNotification.State, userId, branchOne: true, branchRoot: isRoot);
|
||||
if (result.Success)
|
||||
publishedDocuments.Add(document);
|
||||
return result;
|
||||
@@ -1746,8 +1757,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteEventArgs = new DeleteEventArgs<IContent>(content, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs, nameof(Deleting)))
|
||||
if (scope.Notifications.PublishCancelable(new ContentDeletingNotification(content, evtMsgs)))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -1759,9 +1769,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// but... Unpublishing event makes no sense (not going to cancel?) and no need to save
|
||||
// just raise the event
|
||||
if (content.Trashed == false && content.Published)
|
||||
scope.Events.Dispatch(Unpublished, this, new PublishEventArgs<IContent>(content, false, false), nameof(Unpublished));
|
||||
{
|
||||
scope.Notifications.Publish(new ContentUnpublishedNotification(content, evtMsgs));
|
||||
}
|
||||
|
||||
DeleteLocked(scope, content);
|
||||
DeleteLocked(scope, content, evtMsgs);
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(content, TreeChangeTypes.Remove).ToEventArgs());
|
||||
Audit(AuditType.Delete, userId, content.Id);
|
||||
@@ -1772,13 +1784,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return OperationResult.Succeed(evtMsgs);
|
||||
}
|
||||
|
||||
private void DeleteLocked(IScope scope, IContent content)
|
||||
private void DeleteLocked(IScope scope, IContent content, EventMessages evtMsgs)
|
||||
{
|
||||
void DoDelete(IContent c)
|
||||
{
|
||||
_documentRepository.Delete(c);
|
||||
var args = new DeleteEventArgs<IContent>(c, false); // raise event & get flagged files
|
||||
scope.Events.Dispatch(Deleted, this, args, nameof(Deleted));
|
||||
scope.Notifications.Publish(new ContentDeletedNotification(c, evtMsgs));
|
||||
|
||||
// media files deleted by QueuingEventDispatcher
|
||||
}
|
||||
@@ -1809,10 +1820,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
|
||||
public void DeleteVersions(int id, DateTime versionDate, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var deleteRevisionsEventArgs = new DeleteRevisionsEventArgs(id, dateToRetain: versionDate);
|
||||
if (scope.Events.DispatchCancelable(DeletingVersions, this, deleteRevisionsEventArgs))
|
||||
var deletingVersionsNotification = new ContentDeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate);
|
||||
if (scope.Notifications.PublishCancelable(deletingVersionsNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -1821,8 +1834,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.ContentTree);
|
||||
_documentRepository.DeleteVersions(id, versionDate);
|
||||
|
||||
deleteRevisionsEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(DeletedVersions, this, deleteRevisionsEventArgs);
|
||||
scope.Notifications.Publish(new ContentDeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification));
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)");
|
||||
|
||||
scope.Complete();
|
||||
@@ -1839,9 +1851,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Optional Id of the User deleting versions of a Content object</param>
|
||||
public void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
if (scope.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, /*specificVersion:*/ versionId)))
|
||||
var deletingVersionsNotification = new ContentDeletingVersionsNotification(id, evtMsgs, specificVersion: versionId);
|
||||
if (scope.Notifications.PublishCancelable(deletingVersionsNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -1858,7 +1873,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (c.VersionId != versionId && c.PublishedVersionId != versionId) // don't delete the current or published version
|
||||
_documentRepository.DeleteVersion(versionId);
|
||||
|
||||
scope.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false,/* specificVersion:*/ versionId));
|
||||
scope.Notifications.Publish(new ContentDeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification));
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)");
|
||||
|
||||
scope.Complete();
|
||||
@@ -1881,8 +1896,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
var originalPath = content.Path;
|
||||
var moveEventInfo = new MoveEventInfo<IContent>(content, originalPath, Cms.Core.Constants.System.RecycleBinContent);
|
||||
var moveEventArgs = new MoveEventArgs<IContent>(evtMsgs, moveEventInfo);
|
||||
if (scope.Events.DispatchCancelable(Trashing, this, moveEventArgs, nameof(Trashing)))
|
||||
|
||||
var movingToRecycleBinNotification = new ContentMovingToRecycleBinNotification(moveEventInfo, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(movingToRecycleBinNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs); // causes rollback
|
||||
@@ -1901,9 +1917,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
.Select(x => new MoveEventInfo<IContent>(x.Item1, x.Item2, x.Item1.ParentId))
|
||||
.ToArray();
|
||||
|
||||
moveEventArgs.CanCancel = false;
|
||||
moveEventArgs.MoveInfoCollection = moveInfo;
|
||||
scope.Events.Dispatch(Trashed, this, moveEventArgs, nameof(Trashed));
|
||||
scope.Notifications.Publish(new ContentMovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification));
|
||||
Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin");
|
||||
|
||||
scope.Complete();
|
||||
@@ -1932,6 +1946,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return;
|
||||
}
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
var moves = new List<(IContent, string)>();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -1943,8 +1959,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback
|
||||
|
||||
var moveEventInfo = new MoveEventInfo<IContent>(content, content.Path, parentId);
|
||||
var moveEventArgs = new MoveEventArgs<IContent>(moveEventInfo);
|
||||
if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs, nameof(Moving)))
|
||||
|
||||
var movingNotification = new ContentMovingNotification(moveEventInfo, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(movingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return; // causes rollback
|
||||
@@ -1973,9 +1990,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
.Select(x => new MoveEventInfo<IContent>(x.Item1, x.Item2, x.Item1.ParentId))
|
||||
.ToArray();
|
||||
|
||||
moveEventArgs.MoveInfoCollection = moveInfo;
|
||||
moveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Moved, this, moveEventArgs, nameof(Moved));
|
||||
scope.Notifications.Publish(new ContentMovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification));
|
||||
|
||||
Audit(AuditType.Move, userId, content.Id);
|
||||
|
||||
scope.Complete();
|
||||
@@ -2047,7 +2063,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </summary>
|
||||
public OperationResult EmptyRecycleBin(int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var nodeObjectType = Cms.Core.Constants.ObjectTypes.Document;
|
||||
var deleted = new List<IContent>();
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
@@ -2060,8 +2075,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// are managed by Delete, and not here.
|
||||
|
||||
// no idea what those events are for, keep a simplified version
|
||||
var recycleBinEventArgs = new RecycleBinEventArgs(nodeObjectType, evtMsgs);
|
||||
if (scope.Events.DispatchCancelable(EmptyingRecycleBin, this, recycleBinEventArgs))
|
||||
var emptyingRecycleBinNotification = new ContentEmptyingRecycleBinNotification(evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(emptyingRecycleBinNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -2072,13 +2087,11 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var contents = _documentRepository.Get(query).ToArray();
|
||||
foreach (var content in contents)
|
||||
{
|
||||
DeleteLocked(scope, content);
|
||||
DeleteLocked(scope, content, evtMsgs);
|
||||
deleted.Add(content);
|
||||
}
|
||||
|
||||
recycleBinEventArgs.CanCancel = false;
|
||||
recycleBinEventArgs.RecycleBinEmptiedSuccessfully = true; // oh my?!
|
||||
scope.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs);
|
||||
scope.Notifications.Publish(new ContentEmptiedRecycleBinNotification(evtMsgs).WithStateFrom(emptyingRecycleBinNotification));
|
||||
scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange<IContent>(x, TreeChangeTypes.Remove)).ToEventArgs());
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinContent, "Recycle bin emptied");
|
||||
|
||||
@@ -2118,13 +2131,14 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <returns>The newly created <see cref="IContent"/> object</returns>
|
||||
public IContent Copy(IContent content, int parentId, bool relateToOriginal, bool recursive, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
var copy = content.DeepCloneWithResetIdentities();
|
||||
copy.ParentId = parentId;
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var copyEventArgs = new CopyEventArgs<IContent>(content, copy, true, parentId, relateToOriginal);
|
||||
if (scope.Events.DispatchCancelable(Copying, this, copyEventArgs))
|
||||
if (scope.Notifications.PublishCancelable(new ContentCopyingNotification(content, copy, parentId, evtMsgs)))
|
||||
{
|
||||
scope.Complete();
|
||||
return null;
|
||||
@@ -2179,8 +2193,10 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var descendantCopy = descendant.DeepCloneWithResetIdentities();
|
||||
descendantCopy.ParentId = parentId;
|
||||
|
||||
if (scope.Events.DispatchCancelable(Copying, this, new CopyEventArgs<IContent>(descendant, descendantCopy, parentId)))
|
||||
if (scope.Notifications.PublishCancelable(new ContentCopyingNotification(descendant, descendantCopy, parentId, evtMsgs)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// a copy is not published (but not really unpublishing either)
|
||||
// update the create author and last edit author
|
||||
@@ -2204,7 +2220,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(copy, TreeChangeTypes.RefreshBranch).ToEventArgs());
|
||||
foreach (var x in copies)
|
||||
scope.Events.Dispatch(Copied, this, new CopyEventArgs<IContent>(x.Item1, x.Item2, false, x.Item2.ParentId, relateToOriginal));
|
||||
{
|
||||
scope.Notifications.Publish(new ContentCopiedNotification(x.Item1, x.Item2, parentId, relateToOriginal, evtMsgs));
|
||||
}
|
||||
Audit(AuditType.Copy, userId, content.Id);
|
||||
|
||||
scope.Complete();
|
||||
@@ -2221,10 +2239,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <returns>True if sending publication was successful otherwise false</returns>
|
||||
public bool SendToPublication(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var sendToPublishEventArgs = new SendToPublishEventArgs<IContent>(content);
|
||||
if (scope.Events.DispatchCancelable(SendingToPublish, this, sendToPublishEventArgs))
|
||||
var sendingToPublishNotification = new ContentSendingToPublishNotification(content, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(sendingToPublishNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return false;
|
||||
@@ -2249,8 +2269,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (!saveResult.Success)
|
||||
return saveResult.Success;
|
||||
|
||||
sendToPublishEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SentToPublish, this, sendToPublishEventArgs);
|
||||
scope.Notifications.Publish(new ContentSentToPublishNotification(content, evtMsgs).WithStateFrom(sendingToPublishNotification));
|
||||
|
||||
if (culturesChanging != null)
|
||||
Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging);
|
||||
@@ -2322,16 +2341,21 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages evtMsgs, bool raiseEvents)
|
||||
{
|
||||
var saveEventArgs = new ContentSavingEventArgs(itemsA, evtMsgs);
|
||||
var sortingNotification = new ContentSortingNotification(itemsA, evtMsgs);
|
||||
var savingNotification = new ContentSavingNotification(itemsA, evtMsgs);
|
||||
if (raiseEvents)
|
||||
{
|
||||
//raise cancelable sorting event
|
||||
if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Sorting)))
|
||||
// raise cancelable sorting event
|
||||
if (scope.Notifications.PublishCancelable(sortingNotification))
|
||||
{
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
}
|
||||
|
||||
//raise saving event (this one cannot be canceled)
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saving, this, saveEventArgs, nameof(Saving));
|
||||
// raise cancelable saving event
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
}
|
||||
}
|
||||
|
||||
var published = new List<IContent>();
|
||||
@@ -2364,16 +2388,17 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
var savedEventsArgs = saveEventArgs.ToContentSavedEventArgs();
|
||||
//first saved, then sorted
|
||||
scope.Events.Dispatch(Saved, this, savedEventsArgs, nameof(Saved));
|
||||
scope.Events.Dispatch(Sorted, this, savedEventsArgs, nameof(Sorted));
|
||||
scope.Notifications.Publish(new ContentSavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification));
|
||||
scope.Notifications.Publish(new ContentSortedNotification(itemsA, evtMsgs).WithStateFrom(sortingNotification));
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange<IContent>(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
|
||||
|
||||
if (raiseEvents && published.Any())
|
||||
scope.Events.Dispatch(Published, this, new ContentPublishedEventArgs(published, false, evtMsgs), "Published");
|
||||
{
|
||||
scope.Notifications.Publish(new ContentPublishedNotification(published, evtMsgs));
|
||||
}
|
||||
|
||||
Audit(AuditType.Sort, userId, 0, "Sorting content performed by user");
|
||||
return OperationResult.Succeed(evtMsgs);
|
||||
@@ -2453,140 +2478,13 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, DeleteEventArgs<IContent>> Deleting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, DeleteEventArgs<IContent>> Deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete Versions
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, DeleteRevisionsEventArgs> DeletingVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete Versions
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, DeleteRevisionsEventArgs> DeletedVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Sorting
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SaveEventArgs<IContent>> Sorting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Sorting
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SaveEventArgs<IContent>> Sorted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, ContentSavingEventArgs> Saving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, ContentSavedEventArgs> Saved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Copy
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, CopyEventArgs<IContent>> Copying;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Copy
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, CopyEventArgs<IContent>> Copied;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Content is moved to Recycle Bin
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, MoveEventArgs<IContent>> Trashing;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Content is moved to Recycle Bin
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, MoveEventArgs<IContent>> Trashed;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, MoveEventArgs<IContent>> Moving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, MoveEventArgs<IContent>> Moved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Rollback
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, RollbackEventArgs<IContent>> RollingBack;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Rollback
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, RollbackEventArgs<IContent>> RolledBack;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Send to Publish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SendToPublishEventArgs<IContent>> SendingToPublish;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Send to Publish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SendToPublishEventArgs<IContent>> SentToPublish;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before the Recycle Bin is emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, RecycleBinEventArgs> EmptyingRecycleBin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after the Recycle Bin has been Emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, RecycleBinEventArgs> EmptiedRecycleBin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before publish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, ContentPublishingEventArgs> Publishing;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after publish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, ContentPublishedEventArgs> Published;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before unpublish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, PublishEventArgs<IContent>> Unpublishing;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after unpublish
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, PublishEventArgs<IContent>> Unpublished;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after change.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, TreeChange<IContent>.EventArgs> TreeChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after a blueprint has been saved.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SaveEventArgs<IContent>> SavedBlueprint;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after a blueprint has been deleted.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, DeleteEventArgs<IContent>> DeletedBlueprint;
|
||||
/// <remarks>
|
||||
/// This event needs to be rewritten using notifications instead
|
||||
/// </remarks>
|
||||
internal static event TypedEventHandler<IContentService, TreeChange<IContent>.EventArgs> TreeChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -2601,14 +2499,14 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="culturesUnpublishing"></param>
|
||||
/// <param name="evtMsgs"></param>
|
||||
/// <param name="culturesPublishing"></param>
|
||||
/// <param name="savingEventArgs"></param>
|
||||
/// <param name="allLangs"></param>
|
||||
/// <param name="notificationState"></param>
|
||||
/// <returns></returns>
|
||||
private PublishResult StrategyCanPublish(IScope scope, IContent content, bool checkPath, IReadOnlyList<string> culturesPublishing,
|
||||
IReadOnlyCollection<string> culturesUnpublishing, EventMessages evtMsgs, ContentSavingEventArgs savingEventArgs,
|
||||
IReadOnlyCollection<ILanguage> allLangs)
|
||||
IReadOnlyCollection<string> culturesUnpublishing, EventMessages evtMsgs, IReadOnlyCollection<ILanguage> allLangs, IDictionary<string, object> notificationState)
|
||||
{
|
||||
// raise Publishing event
|
||||
if (scope.Events.DispatchCancelable(Publishing, this, savingEventArgs.ToContentPublishingEventArgs()))
|
||||
// raise Publishing notification
|
||||
if (scope.Notifications.PublishCancelable(new ContentPublishingNotification(content, evtMsgs).WithState(notificationState)))
|
||||
{
|
||||
_logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled");
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
@@ -2767,8 +2665,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <returns></returns>
|
||||
private PublishResult StrategyCanUnpublish(IScope scope, IContent content, EventMessages evtMsgs)
|
||||
{
|
||||
// raise Unpublishing event
|
||||
if (scope.Events.DispatchCancelable(Unpublishing, this, new PublishEventArgs<IContent>(content, evtMsgs)))
|
||||
// raise Unpublishing notification
|
||||
if (scope.Notifications.PublishCancelable(new ContentUnpublishingNotification(content, evtMsgs)))
|
||||
{
|
||||
_logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id);
|
||||
return new PublishResult(PublishResultType.FailedUnpublishCancelledByEvent, evtMsgs, content);
|
||||
@@ -2837,6 +2735,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var changes = new List<TreeChange<IContent>>();
|
||||
var moves = new List<(IContent, string)>();
|
||||
var contentTypeIdsA = contentTypeIds.ToArray();
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
// using an immediate uow here because we keep making changes with
|
||||
// PerformMoveLocked and DeleteLocked that must be applied immediately,
|
||||
@@ -2849,7 +2748,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var query = Query<IContent>().WhereIn(x => x.ContentTypeId, contentTypeIdsA);
|
||||
var contents = _documentRepository.Get(query).ToArray();
|
||||
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IContent>(contents), nameof(Deleting)))
|
||||
if (scope.Notifications.PublishCancelable(new ContentDeletingNotification(contents, evtMsgs)))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -2863,7 +2762,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// but... Unpublishing event makes no sense (not going to cancel?) and no need to save
|
||||
// just raise the event
|
||||
if (content.Trashed == false && content.Published)
|
||||
scope.Events.Dispatch(Unpublished, this, new PublishEventArgs<IContent>(content, false, false), nameof(Unpublished));
|
||||
{
|
||||
scope.Notifications.Publish(new ContentUnpublishedNotification(content, evtMsgs));
|
||||
}
|
||||
|
||||
// if current content has children, move them to trash
|
||||
var c = content;
|
||||
@@ -2878,7 +2779,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
// delete content
|
||||
// triggers the deleted event (and handles the files)
|
||||
DeleteLocked(scope, content);
|
||||
DeleteLocked(scope, content, evtMsgs);
|
||||
changes.Add(new TreeChange<IContent>(content, TreeChangeTypes.Remove));
|
||||
}
|
||||
|
||||
@@ -2886,7 +2787,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
.Select(x => new MoveEventInfo<IContent>(x.Item1, x.Item2, x.Item1.ParentId))
|
||||
.ToArray();
|
||||
if (moveInfos.Length > 0)
|
||||
scope.Events.Dispatch(Trashed, this, new MoveEventArgs<IContent>(false, moveInfos), nameof(Trashed));
|
||||
{
|
||||
scope.Notifications.Publish(new ContentMovedToRecycleBinNotification(moveInfos, evtMsgs));
|
||||
}
|
||||
scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs());
|
||||
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, $"Delete content of type {string.Join(",", contentTypeIdsA)}");
|
||||
@@ -2963,6 +2866,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
public void SaveBlueprint(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
//always ensure the blueprint is at the root
|
||||
if (content.ParentId != -1)
|
||||
content.ParentId = -1;
|
||||
@@ -2983,7 +2888,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
Audit(AuditType.Save, Cms.Core.Constants.Security.SuperUserId, content.Id, $"Saved content template: {content.Name}");
|
||||
|
||||
scope.Events.Dispatch(SavedBlueprint, this, new SaveEventArgs<IContent>(content), "SavedBlueprint");
|
||||
scope.Notifications.Publish(new ContentSavedBlueprintNotification(content, evtMsgs));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -2991,11 +2896,13 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
public void DeleteBlueprint(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.ContentTree);
|
||||
_documentBlueprintRepository.Delete(content);
|
||||
scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs<IContent>(content), nameof(DeletedBlueprint));
|
||||
scope.Notifications.Publish(new ContentDeletedBlueprintNotification(content, evtMsgs));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -3065,6 +2972,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
public void DeleteBlueprintsOfTypes(IEnumerable<int> contentTypeIds, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.ContentTree);
|
||||
@@ -3085,7 +2994,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_documentBlueprintRepository.Delete(blueprint);
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs<IContent>(blueprints), nameof(DeletedBlueprint));
|
||||
scope.Notifications.Publish(new ContentDeletedBlueprintNotification(blueprints, evtMsgs));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -3120,10 +3029,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var rollbackEventArgs = new RollbackEventArgs<IContent>(content);
|
||||
|
||||
//Emit RollingBack event aka before
|
||||
if (scope.Events.DispatchCancelable(RollingBack, this, rollbackEventArgs))
|
||||
var rollingBackNotification = new ContentRollingBackNotification(content, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(rollingBackNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -3143,9 +3050,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
else
|
||||
{
|
||||
//Emit RolledBack event aka after
|
||||
rollbackEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(RolledBack, this, rollbackEventArgs);
|
||||
scope.Notifications.Publish(new ContentRolledBackNotification(content, evtMsgs).WithStateFrom(rollingBackNotification));
|
||||
|
||||
//Logging & Audit message
|
||||
_logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Cms.Core.Scoping;
|
||||
using Umbraco.Cms.Core.Services.Changes;
|
||||
using Umbraco.Cms.Core.Strings;
|
||||
using Umbraco.Cms.Infrastructure.Persistence.Querying;
|
||||
using Umbraco.Cms.Infrastructure.Services.Notifications;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Services.Implement
|
||||
@@ -289,19 +290,21 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
private void CreateMedia(IScope scope, Core.Models.Media media, IMedia parent, int userId, bool withIdentity)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
media.CreatorId = userId;
|
||||
|
||||
if (withIdentity)
|
||||
{
|
||||
// if saving is cancelled, media remains without an identity
|
||||
var saveEventArgs = new SaveEventArgs<IMedia>(media);
|
||||
if (Saving.IsRaisedEventCancelled(saveEventArgs, this))
|
||||
var savingNotification = new MediaSavingNotification(media, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_mediaRepository.Save(media);
|
||||
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new MediaSavedNotification(media, evtMsgs).WithStateFrom(savingNotification));
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IMedia>(media, TreeChangeTypes.RefreshNode).ToEventArgs());
|
||||
}
|
||||
|
||||
@@ -659,8 +662,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMedia>(media, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs))
|
||||
var savingNotification = new MediaSavingNotification(media, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -682,8 +685,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_mediaRepository.Save(media);
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new MediaSavedNotification(media, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
var changeType = TreeChangeTypes.RefreshNode;
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IMedia>(media, changeType).ToEventArgs());
|
||||
@@ -708,8 +710,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var saveEventArgs = new SaveEventArgs<IMedia>(mediasA, evtMsgs);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, new SaveEventArgs<IMedia>(mediasA, evtMsgs)))
|
||||
var savingNotification = new MediaSavingNotification(mediasA, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -727,8 +729,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
saveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, saveEventArgs);
|
||||
scope.Notifications.Publish(new MediaSavedNotification(mediasA, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs());
|
||||
Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media");
|
||||
@@ -754,7 +755,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMedia>(media, evtMsgs)))
|
||||
if (scope.Notifications.PublishCancelable(new MediaDeletingNotification(media, evtMsgs)))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -762,7 +763,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.MediaTree);
|
||||
|
||||
DeleteLocked(scope, media);
|
||||
DeleteLocked(scope, media, evtMsgs);
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IMedia>(media, TreeChangeTypes.Remove).ToEventArgs());
|
||||
Audit(AuditType.Delete, userId, media.Id);
|
||||
@@ -773,13 +774,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return OperationResult.Attempt.Succeed(evtMsgs);
|
||||
}
|
||||
|
||||
private void DeleteLocked(IScope scope, IMedia media)
|
||||
private void DeleteLocked(IScope scope, IMedia media, EventMessages evtMsgs)
|
||||
{
|
||||
void DoDelete(IMedia c)
|
||||
{
|
||||
_mediaRepository.Delete(c);
|
||||
var args = new DeleteEventArgs<IMedia>(c, false); // raise event & get flagged files
|
||||
scope.Events.Dispatch(Deleted, this, args);
|
||||
scope.Notifications.Publish(new MediaDeletedNotification(c, evtMsgs));
|
||||
|
||||
// media files deleted by QueuingEventDispatcher
|
||||
}
|
||||
@@ -815,36 +815,24 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
{
|
||||
DeleteVersions(scope, true, id, versionDate, userId);
|
||||
scope.Complete();
|
||||
|
||||
//if (uow.Events.DispatchCancelable(DeletingVersions, this, new DeleteRevisionsEventArgs(id, dateToRetain: versionDate)))
|
||||
//{
|
||||
// uow.Complete();
|
||||
// return;
|
||||
//}
|
||||
|
||||
//uow.WriteLock(Constants.Locks.MediaTree);
|
||||
//var repository = uow.CreateRepository<IMediaRepository>();
|
||||
//repository.DeleteVersions(id, versionDate);
|
||||
|
||||
//uow.Events.Dispatch(DeletedVersions, this, new DeleteRevisionsEventArgs(id, false, dateToRetain: versionDate));
|
||||
//Audit(uow, AuditType.Delete, "Delete Media by version date, userId, Constants.System.Root);
|
||||
|
||||
//uow.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteVersions(IScope scope, bool wlock, int id, DateTime versionDate, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var args = new DeleteRevisionsEventArgs(id, dateToRetain: versionDate);
|
||||
if (scope.Events.DispatchCancelable(DeletingVersions, this, args))
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
var deletingVersionsNotification = new MediaDeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate);
|
||||
if (scope.Notifications.PublishCancelable(deletingVersionsNotification))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (wlock)
|
||||
scope.WriteLock(Cms.Core.Constants.Locks.MediaTree);
|
||||
_mediaRepository.DeleteVersions(id, versionDate);
|
||||
|
||||
args.CanCancel = false;
|
||||
scope.Events.Dispatch(DeletedVersions, this, args);
|
||||
scope.Notifications.Publish(new MediaDeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate).WithStateFrom(deletingVersionsNotification));
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version date");
|
||||
}
|
||||
|
||||
@@ -858,10 +846,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Optional Id of the User deleting versions of a Media object</param>
|
||||
public void DeleteVersion(int id, int versionId, bool deletePriorVersions, int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var args = new DeleteRevisionsEventArgs(id, /*specificVersion:*/ versionId);
|
||||
if (scope.Events.DispatchCancelable(DeletingVersions, this, args))
|
||||
var deletingVersionsNotification = new MediaDeletingVersionsNotification(id, evtMsgs, specificVersion: versionId);
|
||||
if (scope.Notifications.PublishCancelable(deletingVersionsNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -879,8 +869,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
_mediaRepository.DeleteVersion(versionId);
|
||||
|
||||
args.CanCancel = false;
|
||||
scope.Events.Dispatch(DeletedVersions, this, args);
|
||||
scope.Notifications.Publish(new MediaDeletedVersionsNotification(id, evtMsgs, specificVersion: versionId).WithStateFrom(deletingVersionsNotification));
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete Media by version");
|
||||
|
||||
scope.Complete();
|
||||
@@ -911,8 +900,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var originalPath = media.Path;
|
||||
|
||||
var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, Cms.Core.Constants.System.RecycleBinMedia);
|
||||
var moveEventArgs = new MoveEventArgs<IMedia>(true, evtMsgs, moveEventInfo);
|
||||
if (scope.Events.DispatchCancelable(Trashing, this, moveEventArgs, nameof(Trashing)))
|
||||
|
||||
var movingToRecycleBinNotification = new MediaMovingToRecycleBinNotification(moveEventInfo, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(movingToRecycleBinNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -921,11 +911,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
PerformMoveLocked(media, Cms.Core.Constants.System.RecycleBinMedia, null, userId, moves, true);
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IMedia>(media, TreeChangeTypes.RefreshBranch).ToEventArgs());
|
||||
var moveInfo = moves.Select(x => new MoveEventInfo<IMedia>(x.Item1, x.Item2, x.Item1.ParentId))
|
||||
.ToArray();
|
||||
moveEventArgs.MoveInfoCollection = moveInfo;
|
||||
moveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Trashed, this, moveEventArgs, nameof(Trashed));
|
||||
var moveInfo = moves.Select(x => new MoveEventInfo<IMedia>(x.Item1, x.Item2, x.Item1.ParentId)).ToArray();
|
||||
scope.Notifications.Publish(new MediaMovedToRecycleBinNotification(moveInfo, evtMsgs).WithStateFrom(movingToRecycleBinNotification));
|
||||
Audit(AuditType.Move, userId, media.Id, "Move Media to recycle bin");
|
||||
|
||||
scope.Complete();
|
||||
@@ -962,8 +949,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
throw new InvalidOperationException("Parent does not exist or is trashed."); // causes rollback
|
||||
|
||||
var moveEventInfo = new MoveEventInfo<IMedia>(media, media.Path, parentId);
|
||||
var moveEventArgs = new MoveEventArgs<IMedia>(true, evtMsgs, moveEventInfo);
|
||||
if (scope.Events.DispatchCancelable(Moving, this, moveEventArgs, nameof(Moving)))
|
||||
var movingNotification = new MediaMovingNotification(moveEventInfo, evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(movingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Attempt.Cancel(evtMsgs);
|
||||
@@ -979,9 +966,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var moveInfo = moves //changes
|
||||
.Select(x => new MoveEventInfo<IMedia>(x.Item1, x.Item2, x.Item1.ParentId))
|
||||
.ToArray();
|
||||
moveEventArgs.MoveInfoCollection = moveInfo;
|
||||
moveEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(Moved, this, moveEventArgs, nameof(Moved));
|
||||
scope.Notifications.Publish(new MediaMovedNotification(moveInfo, evtMsgs).WithStateFrom(movingNotification));
|
||||
Audit(AuditType.Move, userId, media.Id);
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -1050,7 +1035,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// <param name="userId">Optional Id of the User emptying the Recycle Bin</param>
|
||||
public OperationResult EmptyRecycleBin(int userId = Cms.Core.Constants.Security.SuperUserId)
|
||||
{
|
||||
var nodeObjectType = Cms.Core.Constants.ObjectTypes.Media;
|
||||
var deleted = new List<IMedia>();
|
||||
var evtMsgs = EventMessagesFactory.Get(); // TODO: and then?
|
||||
|
||||
@@ -1063,23 +1047,22 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
// v7 EmptyingRecycleBin and EmptiedRecycleBin events are greatly simplified since
|
||||
// each deleted items will have its own deleting/deleted events. so, files and such
|
||||
// are managed by Delete, and not here.
|
||||
var args = new RecycleBinEventArgs(nodeObjectType, evtMsgs);
|
||||
|
||||
if (scope.Events.DispatchCancelable(EmptyingRecycleBin, this, args))
|
||||
var emptyingRecycleBinNotification = new MediaEmptyingRecycleBinNotification(evtMsgs);
|
||||
if (scope.Notifications.PublishCancelable(emptyingRecycleBinNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
}
|
||||
|
||||
// emptying the recycle bin means deleting whatever is in there - do it properly!
|
||||
var query = Query<IMedia>().Where(x => x.ParentId == Cms.Core.Constants.System.RecycleBinMedia);
|
||||
var medias = _mediaRepository.Get(query).ToArray();
|
||||
foreach (var media in medias)
|
||||
{
|
||||
DeleteLocked(scope, media);
|
||||
DeleteLocked(scope, media, evtMsgs);
|
||||
deleted.Add(media);
|
||||
}
|
||||
args.CanCancel = false;
|
||||
scope.Events.Dispatch(EmptiedRecycleBin, this, args);
|
||||
scope.Notifications.Publish(new MediaEmptiedRecycleBinNotification(new EventMessages()).WithStateFrom(emptyingRecycleBinNotification));
|
||||
scope.Events.Dispatch(TreeChanged, this, deleted.Select(x => new TreeChange<IMedia>(x, TreeChangeTypes.Remove)).ToEventArgs());
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.RecycleBinMedia, "Empty Media recycle bin");
|
||||
scope.Complete();
|
||||
@@ -1105,10 +1088,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var itemsA = items.ToArray();
|
||||
if (itemsA.Length == 0) return true;
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
var args = new SaveEventArgs<IMedia>(itemsA);
|
||||
if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, args))
|
||||
var savingNotification = new MediaSavingNotification(itemsA, evtMsgs);
|
||||
if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification))
|
||||
{
|
||||
scope.Complete();
|
||||
return false;
|
||||
@@ -1137,8 +1122,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
if (raiseEvents)
|
||||
{
|
||||
args.CanCancel = false;
|
||||
scope.Events.Dispatch(Saved, this, args);
|
||||
scope.Notifications.Publish(new MediaSavedNotification(itemsA, evtMsgs).WithStateFrom(savingNotification));
|
||||
}
|
||||
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange<IMedia>(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
|
||||
Audit(AuditType.Sort, userId, 0);
|
||||
@@ -1216,70 +1200,13 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, DeleteEventArgs<IMedia>> Deleting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, DeleteEventArgs<IMedia>> Deleted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete Versions
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, DeleteRevisionsEventArgs> DeletingVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete Versions
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, DeleteRevisionsEventArgs> DeletedVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, SaveEventArgs<IMedia>> Saving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, SaveEventArgs<IMedia>> Saved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Media is moved to Recycle Bin
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, MoveEventArgs<IMedia>> Trashing;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Media is moved to Recycle Bin
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, MoveEventArgs<IMedia>> Trashed;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, MoveEventArgs<IMedia>> Moving;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Move
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, MoveEventArgs<IMedia>> Moved;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before the Recycle Bin is emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, RecycleBinEventArgs> EmptyingRecycleBin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after the Recycle Bin has been Emptied
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, RecycleBinEventArgs> EmptiedRecycleBin;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after change.
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IMediaService, TreeChange<IMedia>.EventArgs> TreeChanged;
|
||||
/// <remarks>
|
||||
/// This event needs to be rewritten using notifications instead
|
||||
/// </remarks>
|
||||
internal static event TypedEventHandler<IMediaService, TreeChange<IMedia>.EventArgs> TreeChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1307,6 +1234,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var changes = new List<TreeChange<IMedia>>();
|
||||
var moves = new List<(IMedia, string)>();
|
||||
var mediaTypeIdsA = mediaTypeIds.ToArray();
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
{
|
||||
@@ -1315,7 +1243,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
var query = Query<IMedia>().WhereIn(x => x.ContentTypeId, mediaTypeIdsA);
|
||||
var medias = _mediaRepository.Get(query).ToArray();
|
||||
|
||||
if (scope.Events.DispatchCancelable(Deleting, this, new DeleteEventArgs<IMedia>(medias)))
|
||||
if (scope.Notifications.PublishCancelable(new MediaDeletingNotification(medias, evtMsgs)))
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -1338,14 +1266,16 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
// delete media
|
||||
// triggers the deleted event (and handles the files)
|
||||
DeleteLocked(scope, media);
|
||||
DeleteLocked(scope, media, evtMsgs);
|
||||
changes.Add(new TreeChange<IMedia>(media, TreeChangeTypes.Remove));
|
||||
}
|
||||
|
||||
var moveInfos = moves.Select(x => new MoveEventInfo<IMedia>(x.Item1, x.Item2, x.Item1.ParentId))
|
||||
.ToArray();
|
||||
if (moveInfos.Length > 0)
|
||||
scope.Events.Dispatch(Trashed, this, new MoveEventArgs<IMedia>(false, moveInfos), nameof(Trashed));
|
||||
{
|
||||
scope.Notifications.Publish(new MediaMovedToRecycleBinNotification(moveInfos, evtMsgs));
|
||||
}
|
||||
scope.Events.Dispatch(TreeChanged, this, changes.ToEventArgs());
|
||||
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, $"Delete Media of types {string.Join(",", mediaTypeIdsA)}");
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public abstract class CancelableEnumerableObjectNotification<T> : CancelableObjectNotification<IEnumerable<T>>
|
||||
{
|
||||
protected CancelableEnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages)
|
||||
{
|
||||
}
|
||||
protected CancelableEnumerableObjectNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public abstract class CancelableObjectNotification<T> : ObjectNotification<T>, ICancelableNotification where T : class
|
||||
{
|
||||
protected CancelableObjectNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
public void CancelOperation(EventMessage cancelationMessage)
|
||||
{
|
||||
Cancel = true;
|
||||
cancelationMessage.IsDefaultEventMessage = true;
|
||||
Messages.Add(cancelationMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentCopiedNotification : CopiedNotification<IContent>
|
||||
{
|
||||
public ContentCopiedNotification(IContent original, IContent copy, int parentId, bool relateToOriginal, EventMessages messages)
|
||||
: base(original, copy, parentId, relateToOriginal, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentCopyingNotification : CopyingNotification<IContent>
|
||||
{
|
||||
public ContentCopyingNotification(IContent original, IContent copy, int parentId, EventMessages messages)
|
||||
: base(original, copy, parentId, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentDeletedBlueprintNotification : EnumerableObjectNotification<IContent>
|
||||
{
|
||||
public ContentDeletedBlueprintNotification(IContent target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentDeletedBlueprintNotification(IEnumerable<IContent> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<IContent> DeletedBlueprints => Target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentDeletedNotification : DeletedNotification<IContent>
|
||||
{
|
||||
public ContentDeletedNotification(IContent target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentDeletedVersionsNotification : DeletedVersionsNotification<IContent>
|
||||
{
|
||||
public ContentDeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentDeletingNotification : DeletingNotification<IContent>
|
||||
{
|
||||
public ContentDeletingNotification(IContent target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public ContentDeletingNotification(IEnumerable<IContent> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentDeletingVersionsNotification : DeletingVersionsNotification<IContent>
|
||||
{
|
||||
public ContentDeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default) : base(id, messages, specificVersion, deletePriorVersions, dateToRetain)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentEmptiedRecycleBinNotification : EmptiedRecycleBinNotification<IContent>
|
||||
{
|
||||
public ContentEmptiedRecycleBinNotification(EventMessages messages) : base(messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Umbraco.Cms.Core.Events;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Services.Notifications
|
||||
{
|
||||
public sealed class ContentEmptyingRecycleBinNotification : EmptyingRecycleBinNotification<IContent>
|
||||
{
|
||||
public ContentEmptyingRecycleBinNotification(EventMessages messages) : base(messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user