diff --git a/src/Umbraco.Core/Events/ContentSavingEventArgs.cs b/src/Umbraco.Core/Events/ContentSavingEventArgs.cs
deleted file mode 100644
index b1cded2eb4..0000000000
--- a/src/Umbraco.Core/Events/ContentSavingEventArgs.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System.Collections.Generic;
-using Umbraco.Cms.Core.Models;
-
-namespace Umbraco.Cms.Core.Events
-{
- ///
- /// Represent event data for the Saving event.
- ///
- public class ContentSavingEventArgs : SaveEventArgs
- {
- #region Factory Methods
-
- ///
- /// Converts to while preserving all args state
- ///
- ///
- public ContentSavedEventArgs ToContentSavedEventArgs()
- {
- return new ContentSavedEventArgs(EventObject, Messages, AdditionalData)
- {
- EventState = EventState
- };
- }
-
- ///
- /// Converts to while preserving all args state
- ///
- ///
- public ContentPublishedEventArgs ToContentPublishedEventArgs()
- {
- return new ContentPublishedEventArgs(EventObject, false, Messages)
- {
- EventState = EventState,
- AdditionalData = AdditionalData
- };
- }
-
- ///
- /// Converts to while preserving all args state
- ///
- ///
- public ContentPublishingEventArgs ToContentPublishingEventArgs()
- {
- return new ContentPublishingEventArgs(EventObject, Messages)
- {
- EventState = EventState,
- AdditionalData = AdditionalData
- };
- }
-
- #endregion
-
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- public ContentSavingEventArgs(IEnumerable eventObject, EventMessages eventMessages)
- : base(eventObject, eventMessages)
- { }
-
- ///
- /// Initializes a new instance of the class.
- ///
- public ContentSavingEventArgs(IContent eventObject, EventMessages eventMessages)
- : base(eventObject, eventMessages)
- { }
-
- #endregion
-
- ///
- /// Determines whether a culture is being saved, during a Saving event.
- ///
- public bool IsSavingCulture(IContent content, string culture)
- => content.CultureInfos.TryGetValue(culture, out var cultureInfo) && cultureInfo.IsDirty();
- }
-}
diff --git a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs
index 99c1d2b0ee..43dc1d0fcd 100644
--- a/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs
+++ b/src/Umbraco.Infrastructure/Cache/DistributedCacheBinder_Handlers.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -126,8 +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
@@ -182,19 +180,6 @@ namespace Umbraco.Cms.Core.Cache
{
}
- ///
- /// Handles cache refreshing for when content is saved (not published)
- ///
- ///
- ///
- ///
- /// 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.
- ///
- private void ContentService_Saved(IContentService sender, SaveEventArgs e)
- {
- }
-
private void ContentService_TreeChanged(IContentService sender, TreeChange.EventArgs args)
{
_distributedCache.RefreshContentCache(args.Changes.ToArray());
diff --git a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs
index 068a8bceea..92e7d6c4d7 100644
--- a/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs
+++ b/src/Umbraco.Infrastructure/Compose/NotificationsComponent.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -18,10 +18,162 @@ using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Implement;
+using Umbraco.Cms.Infrastructure.Services;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Compose
{
+ public sealed class NotificationsHandler :
+ INotificationHandler>,
+ INotificationHandler>
+ {
+ private readonly Notifier _notifier;
+ private readonly ActionCollection _actions;
+ private readonly IContentService _contentService;
+
+ public void Handle(SavedNotification notification)
+ {
+ var newEntities = new List();
+ var updatedEntities = new List();
+
+ //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(), newEntities.ToArray());
+ _notifier.Notify(_actions.GetAction(), updatedEntities.ToArray());
+ }
+
+ public void Handle(SortedNotification 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(), new[] { parent });
+ }
+
+ ///
+ /// This class is used to send the notifications
+ ///
+ 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 _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Notifier(
+ IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
+ IHostingEnvironment hostingEnvironment,
+ INotificationService notificationService,
+ IUserService userService,
+ ILocalizedTextService textService,
+ IOptions globalSettings,
+ ILogger 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 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
+ }));
+ }
+ }
+
+ }
+ }
+
public sealed class NotificationsComponent : IComponent
{
private readonly Notifier _notifier;
@@ -41,10 +193,10 @@ namespace Umbraco.Cms.Core.Compose
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 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
@@ -65,8 +217,8 @@ namespace Umbraco.Cms.Core.Compose
{
ContentService.SentToPublish -= ContentService_SentToPublish;
ContentService.Published -= ContentService_Published;
- ContentService.Sorted -= ContentService_Sorted;
- ContentService.Saved -= ContentService_Saved;
+ //ContentService.Sorted -= ContentService_Sorted;
+ //ContentService.Saved -= ContentService_Saved;
ContentService.Unpublished -= ContentService_Unpublished;
ContentService.Moved -= ContentService_Moved;
ContentService.Trashed -= ContentService_Trashed;
diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs
index 8098a5f8d4..02b7d53108 100644
--- a/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs
+++ b/src/Umbraco.Infrastructure/PropertyEditors/ComplexPropertyEditorContentEventHandler.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
@@ -7,10 +7,53 @@ using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Services.Implement;
+using Umbraco.Cms.Infrastructure.Services;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.PropertyEditors
{
+ // TODO: add copying handling
+ public abstract class ComplexPropertyEditorContentNotificationHandler
+ : INotificationHandler>
+ {
+ private readonly string _editorAlias;
+ private readonly Func _formatPropertyValue;
+
+ protected ComplexPropertyEditorContentNotificationHandler(string editorAlias, Func formatPropertyValue)
+ {
+ _editorAlias = editorAlias;
+ _formatPropertyValue = formatPropertyValue;
+ }
+
+ public void Handle(SavingNotification notification)
+ {
+ foreach (var entity in notification.SavedEntities)
+ {
+ var props = entity.GetPropertiesByEditor(_editorAlias);
+ UpdatePropertyValues(props, true);
+ }
+ }
+
+ private void UpdatePropertyValues(IEnumerable 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;
+ }
+ }
+ }
+ }
+
///
/// Utility class for dealing with Copying/Saving events for complex editors
///
@@ -26,7 +69,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
_editorAlias = editorAlias;
_formatPropertyValue = formatPropertyValue;
ContentService.Copying += ContentService_Copying;
- ContentService.Saving += ContentService_Saving;
+ //ContentService.Saving += ContentService_Saving;
}
///
@@ -84,7 +127,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
if (disposing)
{
ContentService.Copying -= ContentService_Copying;
- ContentService.Saving -= ContentService_Saving;
+ //ContentService.Saving -= ContentService_Saving;
}
_disposedValue = true;
}
diff --git a/src/Umbraco.Infrastructure/Services/CancelableNotification.cs b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs
new file mode 100644
index 0000000000..178ce3df9b
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Services/CancelableNotification.cs
@@ -0,0 +1,124 @@
+using System.Collections.Generic;
+using Umbraco.Cms.Core.Events;
+
+namespace Umbraco.Cms.Infrastructure.Services
+{
+ // TODO split this file into several small classes and move to another namespace
+
+ public interface ICancelableNotification
+ {
+ bool Cancel { get; set; }
+ }
+
+ public abstract class ObjectNotification : INotification where T : class
+ {
+ protected ObjectNotification(T target, EventMessages messages)
+ {
+ Messages = messages;
+ Target = target;
+ }
+
+ public EventMessages Messages { get; }
+
+ protected T Target { get; }
+ }
+
+ public abstract class EnumerableObjectNotification : ObjectNotification>
+ {
+ protected EnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages)
+ {
+ }
+
+ protected EnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages)
+ {
+ }
+ }
+
+ public abstract class CancelableObjectNotification : ObjectNotification 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);
+ }
+ }
+
+ public abstract class CancelableEnumerableObjectNotification : CancelableObjectNotification>
+ {
+ protected CancelableEnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages)
+ {
+ }
+ protected CancelableEnumerableObjectNotification(IEnumerable target, EventMessages messages) : base(target, messages)
+ {
+ }
+ }
+
+ public class DeletingNotification : CancelableEnumerableObjectNotification
+ {
+ public DeletingNotification(T target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public IEnumerable DeletedEntities => Target;
+ }
+
+ public class DeletedNotification : EnumerableObjectNotification
+ {
+ public DeletedNotification(T target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public IEnumerable DeletedEntities => Target;
+ }
+
+ public class SortingNotification : CancelableEnumerableObjectNotification
+ {
+ public SortingNotification(IEnumerable target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public IEnumerable SortedEntities => Target;
+ }
+
+ public class SortedNotification : EnumerableObjectNotification
+ {
+ public SortedNotification(IEnumerable target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public IEnumerable SortedEntities => Target;
+ }
+
+ public class SavingNotification : CancelableEnumerableObjectNotification
+ {
+ public SavingNotification(T target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public SavingNotification(IEnumerable target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public IEnumerable SavedEntities => Target;
+ }
+
+ public class SavedNotification : EnumerableObjectNotification
+ {
+ public SavedNotification(T target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public SavedNotification(IEnumerable target, EventMessages messages) : base(target, messages)
+ {
+ }
+
+ public IEnumerable SavedEntities => Target;
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs
index 017540ae3f..379ff9a0a2 100644
--- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs
@@ -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;
using Umbraco.Extensions;
namespace Umbraco.Cms.Core.Services.Implement
@@ -32,6 +33,7 @@ namespace Umbraco.Cms.Core.Services.Implement
private readonly IShortStringHelper _shortStringHelper;
private readonly ILogger _logger;
private IQuery _queryNotTrashed;
+ private readonly IEventAggregator _eventAggregator;
#region Constructors
@@ -39,7 +41,7 @@ namespace Umbraco.Cms.Core.Services.Implement
IEventMessagesFactory eventMessagesFactory,
IDocumentRepository documentRepository, IEntityRepository entityRepository, IAuditRepository auditRepository,
IContentTypeRepository contentTypeRepository, IDocumentBlueprintRepository documentBlueprintRepository, ILanguageRepository languageRepository,
- Lazy propertyValidationService, IShortStringHelper shortStringHelper)
+ Lazy propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator)
: base(provider, loggerFactory, eventMessagesFactory)
{
_documentRepository = documentRepository;
@@ -50,6 +52,7 @@ namespace Umbraco.Cms.Core.Services.Implement
_languageRepository = languageRepository;
_propertyValidationService = propertyValidationService;
_shortStringHelper = shortStringHelper;
+ _eventAggregator = eventAggregator;
_logger = loggerFactory.CreateLogger();
}
@@ -747,11 +750,15 @@ 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)))
+ if (raiseEvents)
{
- scope.Complete();
- return OperationResult.Cancel(evtMsgs);
+ var notification = new SavingNotification(content, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
+ scope.Complete();
+ return OperationResult.Cancel(evtMsgs);
+ }
}
scope.WriteLock(Cms.Core.Constants.Locks.ContentTree);
@@ -773,7 +780,7 @@ namespace Umbraco.Cms.Core.Services.Implement
if (raiseEvents)
{
- scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved));
+ _eventAggregator.Publish(new SavedNotification(content, evtMsgs));
}
var changeType = TreeChangeTypes.RefreshNode;
scope.Events.Dispatch(TreeChanged, this, new TreeChange(content, changeType).ToEventArgs());
@@ -802,11 +809,15 @@ 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)))
+ if (raiseEvents)
{
- scope.Complete();
- return OperationResult.Cancel(evtMsgs);
+ var notification = new SavingNotification(contentsA, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
+ scope.Complete();
+ return OperationResult.Cancel(evtMsgs);
+ }
}
var treeChanges = contentsA.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode));
@@ -823,7 +834,7 @@ namespace Umbraco.Cms.Core.Services.Implement
if (raiseEvents)
{
- scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved));
+ _eventAggregator.Publish(new SavedNotification(contentsA, evtMsgs));
}
scope.Events.Dispatch(TreeChanged, this, treeChanges.ToEventArgs());
Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content");
@@ -867,9 +878,12 @@ 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 notification = new SavingNotification(content, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
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 +895,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, userId, raiseEvents);
scope.Complete();
return result;
}
@@ -905,9 +919,16 @@ 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)))
- return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
+
+ if (raiseEvents)
+ {
+ var notification = new SavingNotification(content, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
+ return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
+ }
+ }
var varies = content.ContentType.VariesByCulture();
@@ -927,7 +948,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, userId, raiseEvents);
scope.Complete();
return result;
}
@@ -969,9 +990,12 @@ 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 notification = new SavingNotification(content, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
+ }
// all cultures = unpublish whole
if (culture == "*" || (!content.ContentType.VariesByCulture() && culture == null))
@@ -982,7 +1006,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, userId);
scope.Complete();
return result;
}
@@ -996,7 +1020,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, userId);
scope.Complete();
@@ -1039,13 +1063,16 @@ 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 notification = new SavingNotification(content, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
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, userId, raiseEvents);
scope.Complete();
return result;
}
@@ -1069,15 +1096,13 @@ namespace Umbraco.Cms.Core.Services.Implement
///
///
private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content,
- ContentSavingEventArgs saveEventArgs, IReadOnlyCollection allLangs,
+ EventMessages evtMsgs, IReadOnlyCollection allLangs,
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;
@@ -1210,7 +1235,7 @@ namespace Umbraco.Cms.Core.Services.Implement
// raise the Saved event, always
if (raiseEvents)
{
- scope.Events.Dispatch(Saved, this, saveEventArgs.ToContentSavedEventArgs(), nameof(Saved));
+ _eventAggregator.Publish(new SavedNotification(content, evtMsgs));
}
if (unpublishing) // we have tried to unpublish - won't happen in a branch
@@ -1375,8 +1400,9 @@ 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 notification = new SavingNotification(d, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
{
results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d));
continue;
@@ -1390,7 +1416,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, 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 +1462,12 @@ 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 notification = new SavingNotification(d, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
{
results.Add(new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, d));
- continue; // this document is canceled move next
+ continue;
}
var publishing = true;
@@ -1470,7 +1497,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, d.WriterId);
if (result.Success == false)
_logger.LogError(null, "Failed to publish document id={DocumentId}, reason={Reason}.", d.Id, result.Result);
@@ -1718,9 +1745,12 @@ 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 notification = new SavingNotification(document, evtMsgs);
+ _eventAggregator.Publish(notification);
+ if (notification.Cancel)
+ {
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, document);
+ }
// publish & check if values are valid
if (!publishCultures(document, culturesToPublish, allLangs))
@@ -1729,7 +1759,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, userId, branchOne: true, branchRoot: isRoot);
if (result.Success)
publishedDocuments.Add(document);
return result;
@@ -1746,6 +1776,9 @@ namespace Umbraco.Cms.Core.Services.Implement
using (var scope = ScopeProvider.CreateScope())
{
+ var deleteNotification = new DeletingNotification(content, evtMsgs);
+ _eventAggregator.Publish(deleteNotification);
+
var deleteEventArgs = new DeleteEventArgs(content, evtMsgs);
if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs, nameof(Deleting)))
{
@@ -2322,16 +2355,23 @@ 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);
if (raiseEvents)
{
- //raise cancelable sorting event
- if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Sorting)))
+ // raise cancelable sorting event
+ var sortingNotification = new SortingNotification(itemsA, evtMsgs);
+ _eventAggregator.Publish(sortingNotification);
+ if (sortingNotification.Cancel)
+ {
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
+ var savingNotification = new SavingNotification(itemsA, evtMsgs);
+ _eventAggregator.Publish(savingNotification);
+ if (savingNotification.Cancel)
+ {
+ return OperationResult.Cancel(evtMsgs);
+ }
}
var published = new List();
@@ -2364,10 +2404,9 @@ 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));
+ _eventAggregator.Publish(new SavedNotification(itemsA, evtMsgs));
+ _eventAggregator.Publish(new SortedNotification(itemsA, evtMsgs));
}
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
@@ -2483,11 +2522,6 @@ namespace Umbraco.Cms.Core.Services.Implement
///
public static event TypedEventHandler> Sorted;
- ///
- /// Occurs before Save
- ///
- public static event TypedEventHandler Saving;
-
///
/// Occurs after Save
///