WIP on content service events
This commit is contained in:
@@ -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,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
|
||||
{
|
||||
}
|
||||
|
||||
/// <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());
|
||||
|
||||
@@ -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<SavedNotification<IContent>>,
|
||||
INotificationHandler<SortedNotification<IContent>>
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
private readonly ActionCollection _actions;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
public void Handle(SavedNotification<IContent> 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(SortedNotification<IContent> 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 });
|
||||
}
|
||||
|
||||
/// <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
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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<SavingNotification<IContent>>
|
||||
{
|
||||
private readonly string _editorAlias;
|
||||
private readonly Func<string, bool, string> _formatPropertyValue;
|
||||
|
||||
protected ComplexPropertyEditorContentNotificationHandler(string editorAlias, Func<string, bool, string> formatPropertyValue)
|
||||
{
|
||||
_editorAlias = editorAlias;
|
||||
_formatPropertyValue = formatPropertyValue;
|
||||
}
|
||||
|
||||
public void Handle(SavingNotification<IContent> notification)
|
||||
{
|
||||
foreach (var entity in notification.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>
|
||||
/// Utility class for dealing with <see cref="ContentService"/> Copying/Saving events for complex editors
|
||||
/// </summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
124
src/Umbraco.Infrastructure/Services/CancelableNotification.cs
Normal file
124
src/Umbraco.Infrastructure/Services/CancelableNotification.cs
Normal file
@@ -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<T> : 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<T> : ObjectNotification<IEnumerable<T>>
|
||||
{
|
||||
protected EnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages)
|
||||
{
|
||||
}
|
||||
|
||||
protected EnumerableObjectNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CancelableObjectNotification<T> : ObjectNotification<T> 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<T> : CancelableObjectNotification<IEnumerable<T>>
|
||||
{
|
||||
protected CancelableEnumerableObjectNotification(T target, EventMessages messages) : base(new [] {target}, messages)
|
||||
{
|
||||
}
|
||||
protected CancelableEnumerableObjectNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class DeletingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
public DeletingNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> DeletedEntities => Target;
|
||||
}
|
||||
|
||||
public class DeletedNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
public DeletedNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> DeletedEntities => Target;
|
||||
}
|
||||
|
||||
public class SortingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
public SortingNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> SortedEntities => Target;
|
||||
}
|
||||
|
||||
public class SortedNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
public SortedNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> SortedEntities => Target;
|
||||
}
|
||||
|
||||
public class SavingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
public SavingNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public SavingNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> SavedEntities => Target;
|
||||
}
|
||||
|
||||
public class SavedNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
public SavedNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public SavedNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> SavedEntities => Target;
|
||||
}
|
||||
}
|
||||
@@ -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<ContentService> _logger;
|
||||
private IQuery<IContent> _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<IPropertyValidationService> propertyValidationService, IShortStringHelper shortStringHelper)
|
||||
Lazy<IPropertyValidationService> 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<ContentService>();
|
||||
}
|
||||
|
||||
@@ -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<IContent>(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<IContent>(content, evtMsgs));
|
||||
}
|
||||
var changeType = TreeChangeTypes.RefreshNode;
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(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<IContent>(contentsA, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
}
|
||||
}
|
||||
|
||||
var treeChanges = contentsA.Select(x => new TreeChange<IContent>(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<IContent>(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<IContent>(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<IContent>(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<IContent>(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<IContent>(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
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content,
|
||||
ContentSavingEventArgs saveEventArgs, IReadOnlyCollection<ILanguage> allLangs,
|
||||
EventMessages evtMsgs, IReadOnlyCollection<ILanguage> 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<IContent>(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<IContent>(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<IContent>(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<IContent>(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<IContent>(content, evtMsgs);
|
||||
_eventAggregator.Publish(deleteNotification);
|
||||
|
||||
var deleteEventArgs = new DeleteEventArgs<IContent>(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<IContent>(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<IContent>(itemsA, evtMsgs);
|
||||
_eventAggregator.Publish(savingNotification);
|
||||
if (savingNotification.Cancel)
|
||||
{
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
}
|
||||
}
|
||||
|
||||
var published = new List<IContent>();
|
||||
@@ -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<IContent>(itemsA, evtMsgs));
|
||||
_eventAggregator.Publish(new SortedNotification<IContent>(itemsA, evtMsgs));
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange<IContent>(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
|
||||
@@ -2483,11 +2522,6 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
/// </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>
|
||||
|
||||
Reference in New Issue
Block a user