Remove a bunch more events from ContentService
This commit is contained in:
@@ -126,8 +126,6 @@ namespace Umbraco.Cms.Core.Cache
|
||||
() => MediaService.TreeChanged -= MediaService_TreeChanged);
|
||||
|
||||
// bind to content events
|
||||
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);
|
||||
|
||||
|
||||
@@ -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
|
||||
{ }
|
||||
}
|
||||
@@ -13,167 +13,15 @@ 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;
|
||||
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
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this component must be removed entirely - there is some code duplication in NotificationsHandler in anticipation of this component being deleted
|
||||
public sealed class NotificationsComponent : IComponent
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
@@ -189,24 +37,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;
|
||||
|
||||
@@ -215,15 +45,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;
|
||||
}
|
||||
@@ -234,72 +55,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();
|
||||
@@ -310,22 +65,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();
|
||||
|
||||
217
src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs
Normal file
217
src/Umbraco.Infrastructure/Compose/NotificationsHandler.cs
Normal file
@@ -0,0 +1,217 @@
|
||||
// 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.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;
|
||||
using Umbraco.Cms.Infrastructure.Services;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
// TODO: insert these notification handlers in core composition
|
||||
public sealed class NotificationsHandler :
|
||||
INotificationHandler<SavedNotification<IContent>>,
|
||||
INotificationHandler<SortedNotification<IContent>>,
|
||||
INotificationHandler<PublishedNotification<IContent>>,
|
||||
INotificationHandler<MovedNotification<IContent>>,
|
||||
INotificationHandler<TrashedNotification<IContent>>,
|
||||
INotificationHandler<CopiedNotification<IContent>>,
|
||||
INotificationHandler<RolledBackNotification<IContent>>,
|
||||
INotificationHandler<SentToPublishNotification<IContent>>,
|
||||
INotificationHandler<UnpublishedNotification<IContent>>
|
||||
{
|
||||
private readonly Notifier _notifier;
|
||||
private readonly ActionCollection _actions;
|
||||
private readonly IContentService _contentService;
|
||||
|
||||
public NotificationsHandler(Notifier notifier, ActionCollection actions, IContentService contentService)
|
||||
{
|
||||
_notifier = notifier;
|
||||
_actions = actions;
|
||||
_contentService = 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 });
|
||||
}
|
||||
|
||||
public void Handle(PublishedNotification<IContent> notification) => _notifier.Notify(_actions.GetAction<ActionPublish>(), notification.PublishedEntities.ToArray());
|
||||
|
||||
public void Handle(MovedNotification<IContent> 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(TrashedNotification<IContent> notification) => _notifier.Notify(_actions.GetAction<ActionDelete>(), notification.MoveInfoCollection.Select(m => m.Entity).ToArray());
|
||||
|
||||
public void Handle(CopiedNotification<IContent> notification) => _notifier.Notify(_actions.GetAction<ActionCopy>(), notification.Original);
|
||||
|
||||
public void Handle(RolledBackNotification<IContent> notification) => _notifier.Notify(_actions.GetAction<ActionRollback>(), notification.Entity);
|
||||
|
||||
public void Handle(SentToPublishNotification<IContent> notification) => _notifier.Notify(_actions.GetAction<ActionToPublish>(), notification.Entity);
|
||||
|
||||
public void Handle(UnpublishedNotification<IContent> 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,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,15 +1,20 @@
|
||||
using System.Linq;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Events;
|
||||
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;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.Compose
|
||||
{
|
||||
public sealed class RelateOnTrashComponent : IComponent
|
||||
// TODO: insert these notification handlers in core composition
|
||||
// TODO: lots of duplicate code in this one, refactor
|
||||
public sealed class RelateOnTrashHandler :
|
||||
INotificationHandler<MovedNotification<IContent>>,
|
||||
INotificationHandler<TrashedNotification<IContent>>
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IEntityService _entityService;
|
||||
@@ -17,7 +22,7 @@ namespace Umbraco.Cms.Core.Compose
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
public RelateOnTrashComponent(
|
||||
public RelateOnTrashHandler(
|
||||
IRelationService relationService,
|
||||
IEntityService entityService,
|
||||
ILocalizedTextService textService,
|
||||
@@ -31,27 +36,10 @@ namespace Umbraco.Cms.Core.Compose
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Handle(MovedNotification<IContent> 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(Cms.Core.Constants.System.RecycleBinContentString)))
|
||||
{
|
||||
|
||||
const string relationTypeAlias = Cms.Core.Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
|
||||
var relations = _relationService.GetByChildId(item.Entity.Id);
|
||||
|
||||
@@ -62,20 +50,7 @@ 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(TrashedNotification<IContent> notification)
|
||||
{
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
@@ -94,7 +69,7 @@ namespace Umbraco.Cms.Core.Compose
|
||||
_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
|
||||
@@ -124,6 +99,55 @@ namespace Umbraco.Cms.Core.Compose
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public sealed class RelateOnTrashComponent : IComponent
|
||||
{
|
||||
private readonly IRelationService _relationService;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IAuditService _auditService;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
public RelateOnTrashComponent(
|
||||
IRelationService relationService,
|
||||
IEntityService entityService,
|
||||
ILocalizedTextService textService,
|
||||
IAuditService auditService,
|
||||
IScopeProvider scopeProvider)
|
||||
{
|
||||
_relationService = relationService;
|
||||
_entityService = entityService;
|
||||
_textService = textService;
|
||||
_auditService = auditService;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
MediaService.Moved += MediaService_Moved;
|
||||
MediaService.Trashed += MediaService_Trashed;
|
||||
}
|
||||
|
||||
public void Terminate()
|
||||
{
|
||||
MediaService.Moved -= MediaService_Moved;
|
||||
MediaService.Trashed -= MediaService_Trashed;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MediaService_Trashed(IMediaService sender, MoveEventArgs<IMedia> e)
|
||||
{
|
||||
|
||||
@@ -1,36 +1,28 @@
|
||||
using System;
|
||||
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
|
||||
// TODO: insert these notification handlers in core composition
|
||||
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,145 +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.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>
|
||||
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,55 @@
|
||||
// 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;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
// TODO: insert these notification handlers in core composition
|
||||
public abstract class ComplexPropertyEditorContentNotificationHandler :
|
||||
INotificationHandler<SavingNotification<IContent>>,
|
||||
INotificationHandler<CopyingNotification<IContent>>
|
||||
{
|
||||
protected abstract string EditorAlias { get; }
|
||||
|
||||
protected abstract string FormatPropertyValue(string rawJson, bool onlyMissingKeys);
|
||||
|
||||
public void Handle(SavingNotification<IContent> notification)
|
||||
{
|
||||
foreach (var entity in notification.SavedEntities)
|
||||
{
|
||||
var props = entity.GetPropertiesByEditor(EditorAlias);
|
||||
UpdatePropertyValues(props, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(CopyingNotification<IContent> 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;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
@@ -24,7 +25,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
"fileupload",
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-download-alt")]
|
||||
public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator
|
||||
// TODO: insert these notification handlers in core composition
|
||||
public class FileUploadPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler<CopiedNotification<IContent>>, INotificationHandler<DeletedNotification<IContent>>
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
@@ -32,6 +34,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 +45,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 +55,7 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
_localizationService = localizationService;
|
||||
_localizedTextService = localizedTextService;
|
||||
_uploadAutoFillProperties = uploadAutoFillProperties;
|
||||
_contentService = contentService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,37 +124,6 @@ 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)
|
||||
{
|
||||
// get the upload field properties with a value
|
||||
var properties = args.Original.Properties.Where(IsUploadField);
|
||||
|
||||
// copy files
|
||||
var isUpdated = false;
|
||||
foreach (var property in properties)
|
||||
{
|
||||
//copy each of the property values (variants, segments) to the destination
|
||||
foreach (var propertyValue in property.Values)
|
||||
{
|
||||
var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
|
||||
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);
|
||||
isUpdated = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if updated, re-save the copy with the updated value
|
||||
if (isUpdated)
|
||||
sender.Save(args.Copy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After a media has been created, auto-fill the properties.
|
||||
/// </summary>
|
||||
@@ -182,6 +156,40 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
AutoFillProperties(entity);
|
||||
}
|
||||
|
||||
public void Handle(CopiedNotification<IContent> notification)
|
||||
{
|
||||
// get the upload field properties with a value
|
||||
var properties = notification.Original.Properties.Where(IsUploadField);
|
||||
|
||||
// copy files
|
||||
var isUpdated = false;
|
||||
foreach (var property in properties)
|
||||
{
|
||||
//copy each of the property values (variants, segments) to the destination
|
||||
foreach (var propertyValue in property.Values)
|
||||
{
|
||||
var propVal = property.GetValue(propertyValue.Culture, propertyValue.Segment);
|
||||
if (propVal == null || !(propVal is string str) || str.IsNullOrWhiteSpace())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sourcePath = _mediaFileSystem.GetRelativePath(str);
|
||||
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)
|
||||
{
|
||||
_contentService.Save(notification.Copy);
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(DeletedNotification<IContent> notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType<ContentBase>()));
|
||||
|
||||
/// <summary>
|
||||
/// Auto-fill properties (or clear).
|
||||
/// </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;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Core.PropertyEditors
|
||||
@@ -31,7 +32,8 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
HideLabel = false,
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-crop")]
|
||||
public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator
|
||||
// TODO: insert these notification handlers in core composition
|
||||
public class ImageCropperPropertyEditor : DataEditor, IMediaUrlGenerator, INotificationHandler<CopiedNotification<IContent>>, INotificationHandler<DeletedNotification<IContent>>
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly ContentSettings _contentSettings;
|
||||
@@ -39,6 +41,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 +56,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 +65,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>();
|
||||
}
|
||||
|
||||
@@ -175,12 +180,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(CopiedNotification<IContent> 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,19 +194,27 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Handle(DeletedNotification<IContent> notification) => notification.MediaFilesToDelete.AddRange(ServiceDeleted(notification.DeletedEntities.OfType<ContentBase>()));
|
||||
|
||||
/// <summary>
|
||||
/// After a media has been created, auto-fill the properties.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,29 +1,19 @@
|
||||
using System;
|
||||
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
|
||||
// TODO: insert these notification handlers in core composition
|
||||
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 +68,5 @@ namespace Umbraco.Cms.Core.Compose
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
@@ -42,17 +42,11 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
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>()));
|
||||
MemberService.Deleted += memberServiceDeleted;
|
||||
_terminate.Add(() => MemberService.Deleted -= memberServiceDeleted);
|
||||
@@ -62,17 +56,11 @@ namespace Umbraco.Cms.Core.PropertyEditors
|
||||
{
|
||||
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>()));
|
||||
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,38 @@
|
||||
using System;
|
||||
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;
|
||||
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
|
||||
// TODO: insert these notification handlers in core composition
|
||||
public sealed class RedirectTrackingHandler :
|
||||
INotificationHandler<PublishingNotification<IContent>>,
|
||||
INotificationHandler<PublishedNotification<IContent>>,
|
||||
INotificationHandler<MovingNotification<IContent>>,
|
||||
INotificationHandler<MovedNotification<IContent>>
|
||||
{
|
||||
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)
|
||||
public RedirectTrackingHandler(IOptionsMonitor<WebRoutingSettings> webRoutingSettings, IPublishedSnapshotAccessor publishedSnapshotAccessor, IRedirectUrlService redirectUrlService, IVariationContextAccessor variationContextAccessor)
|
||||
{
|
||||
_webRoutingSettings = webRoutingSettings;
|
||||
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||
@@ -38,86 +40,62 @@ namespace Umbraco.Cms.Core.Routing
|
||||
_variationContextAccessor = variationContextAccessor;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
public void Handle(PublishingNotification<IContent> 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();
|
||||
foreach (var entity in notification.PublishedEntities)
|
||||
{
|
||||
StoreOldRoute(entity, oldRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void ContentService_Published(IContentService sender, ContentPublishedEventArgs args)
|
||||
public void Handle(PublishedNotification<IContent> 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();
|
||||
CreateRedirects(oldRoutes);
|
||||
}
|
||||
|
||||
private void ContentService_Moving(IContentService sender, MoveEventArgs<IContent> args)
|
||||
public void Handle(MovingNotification<IContent> 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);
|
||||
foreach (var info in args.MoveInfoCollection)
|
||||
var oldRoutes = GetOldRoutes();
|
||||
foreach (var info in notification.MoveInfoCollection)
|
||||
{
|
||||
StoreOldRoute(info.Entity, oldRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
private void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> args)
|
||||
// TODO refactor (this is duplicate code, see published notification handling above)
|
||||
public void Handle(MovedNotification<IContent> 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();
|
||||
CreateRedirects(oldRoutes);
|
||||
}
|
||||
|
||||
private OldRoutesDictionary GetOldRoutes(IDictionary<string, object> eventState)
|
||||
{
|
||||
if (! eventState.ContainsKey(_eventStateKey))
|
||||
{
|
||||
eventState[_eventStateKey] = new OldRoutesDictionary();
|
||||
}
|
||||
|
||||
return eventState[_eventStateKey] as OldRoutesDictionary;
|
||||
}
|
||||
// TODO: figure out how to do notification state / temp state
|
||||
private OldRoutesDictionary GetOldRoutes() => new OldRoutesDictionary();
|
||||
|
||||
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 +108,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 +122,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 +156,8 @@ namespace Umbraco.Cms.Core.Routing
|
||||
}
|
||||
|
||||
private class OldRoutesDictionary : Dictionary<ContentIdAndCulture, ContentKeyAndOldRoute>
|
||||
{ }
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Cms.Core;
|
||||
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
|
||||
public interface ICancelableNotification : INotification
|
||||
{
|
||||
bool Cancel { get; set; }
|
||||
}
|
||||
@@ -34,7 +39,7 @@ namespace Umbraco.Cms.Infrastructure.Services
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class CancelableObjectNotification<T> : ObjectNotification<T> where T : class
|
||||
public abstract class CancelableObjectNotification<T> : ObjectNotification<T>, ICancelableNotification where T : class
|
||||
{
|
||||
protected CancelableObjectNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
@@ -66,16 +71,33 @@ namespace Umbraco.Cms.Infrastructure.Services
|
||||
{
|
||||
}
|
||||
|
||||
public DeletingNotification(IEnumerable<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 DeletedNotification(T target, EventMessages messages) : base(target, messages) => MediaFilesToDelete = new List<string>();
|
||||
|
||||
public IEnumerable<T> DeletedEntities => Target;
|
||||
|
||||
public List<string> MediaFilesToDelete { get; }
|
||||
}
|
||||
|
||||
public class DeletedBlueprintNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
public DeletedBlueprintNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> DeletedEntities => Target;
|
||||
public DeletedBlueprintNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> DeletedBlueprints => Target;
|
||||
}
|
||||
|
||||
public class SortingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
@@ -121,4 +143,243 @@ namespace Umbraco.Cms.Infrastructure.Services
|
||||
|
||||
public IEnumerable<T> SavedEntities => Target;
|
||||
}
|
||||
|
||||
public class SavedBlueprintNotification<T> : ObjectNotification<T> where T : class
|
||||
{
|
||||
public SavedBlueprintNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public T SavedBlueprint => Target;
|
||||
}
|
||||
|
||||
public class PublishingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
public PublishingNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public PublishingNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> PublishedEntities => Target;
|
||||
}
|
||||
|
||||
public class PublishedNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
public PublishedNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public PublishedNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> PublishedEntities => Target;
|
||||
}
|
||||
|
||||
public class MovingNotification<T> : CancelableObjectNotification<IEnumerable<MoveEventInfo<T>>>
|
||||
{
|
||||
public MovingNotification(MoveEventInfo<T> target, EventMessages messages) : base(new[] {target}, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MovingNotification(IEnumerable<MoveEventInfo<T>> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<MoveEventInfo<T>> MoveInfoCollection => Target;
|
||||
}
|
||||
|
||||
public class MovedNotification<T> : ObjectNotification<IEnumerable<MoveEventInfo<T>>>
|
||||
{
|
||||
public MovedNotification(MoveEventInfo<T> target, EventMessages messages) : base(new[] { target }, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public MovedNotification(IEnumerable<MoveEventInfo<T>> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<MoveEventInfo<T>> MoveInfoCollection => Target;
|
||||
}
|
||||
|
||||
public class TrashingNotification<T> : CancelableObjectNotification<IEnumerable<MoveEventInfo<T>>>
|
||||
{
|
||||
public TrashingNotification(MoveEventInfo<T> target, EventMessages messages) : base(new[] { target }, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public TrashingNotification(IEnumerable<MoveEventInfo<T>> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<MoveEventInfo<T>> MoveInfoCollection => Target;
|
||||
}
|
||||
|
||||
public class TrashedNotification<T> : ObjectNotification<IEnumerable<MoveEventInfo<T>>>
|
||||
{
|
||||
public TrashedNotification(MoveEventInfo<T> target, EventMessages messages) : base(new[] { target }, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public TrashedNotification(IEnumerable<MoveEventInfo<T>> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<MoveEventInfo<T>> MoveInfoCollection => Target;
|
||||
}
|
||||
|
||||
public class CopyingNotification<T> : CancelableObjectNotification<T> where T : class
|
||||
{
|
||||
public CopyingNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
public T Original => Target;
|
||||
|
||||
public T Copy { get; }
|
||||
|
||||
public int ParentId { get; }
|
||||
}
|
||||
|
||||
public class CopiedNotification<T> : ObjectNotification<T> where T : class
|
||||
{
|
||||
public CopiedNotification(T original, T copy, int parentId, EventMessages messages) : base(original, messages)
|
||||
{
|
||||
Copy = copy;
|
||||
ParentId = parentId;
|
||||
}
|
||||
|
||||
public T Original => Target;
|
||||
|
||||
public T Copy { get; }
|
||||
|
||||
public int ParentId { get; }
|
||||
}
|
||||
|
||||
public class RollingBackNotification<T> : CancelableObjectNotification<T> where T : class
|
||||
{
|
||||
public RollingBackNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public T Entity => Target;
|
||||
}
|
||||
|
||||
public class RolledBackNotification<T> : ObjectNotification<T> where T : class
|
||||
{
|
||||
public RolledBackNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public T Entity => Target;
|
||||
}
|
||||
|
||||
public class SendingToPublishNotification<T> : CancelableObjectNotification<T> where T : class
|
||||
{
|
||||
public SendingToPublishNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public T Entity => Target;
|
||||
}
|
||||
|
||||
public class SentToPublishNotification<T> : ObjectNotification<T> where T : class
|
||||
{
|
||||
public SentToPublishNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public T Entity => Target;
|
||||
}
|
||||
|
||||
|
||||
public class UnpublishingNotification<T> : CancelableEnumerableObjectNotification<T>
|
||||
{
|
||||
public UnpublishingNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UnpublishingNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> UnpublishedEntities => Target;
|
||||
}
|
||||
|
||||
public class UnpublishedNotification<T> : EnumerableObjectNotification<T>
|
||||
{
|
||||
public UnpublishedNotification(T target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public UnpublishedNotification(IEnumerable<T> target, EventMessages messages) : base(target, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public IEnumerable<T> UnpublishedEntities => Target;
|
||||
}
|
||||
|
||||
public class EmptiedRecycleBinNotification : INotification
|
||||
{
|
||||
public EmptiedRecycleBinNotification(Guid nodeObjectType, EventMessages messages)
|
||||
{
|
||||
NodeObjectType = nodeObjectType;
|
||||
Messages = messages;
|
||||
}
|
||||
|
||||
public Guid NodeObjectType { get; }
|
||||
|
||||
public EventMessages Messages { get; }
|
||||
|
||||
public bool IsContentRecycleBin => NodeObjectType == Constants.ObjectTypes.Document;
|
||||
|
||||
public bool IsMediaRecycleBin => NodeObjectType == Constants.ObjectTypes.Media;
|
||||
}
|
||||
|
||||
public class EmptyingRecycleBinNotification : EmptiedRecycleBinNotification, ICancelableNotification
|
||||
{
|
||||
public EmptyingRecycleBinNotification(Guid nodeObjectType, EventMessages messages)
|
||||
: base(nodeObjectType, messages)
|
||||
{
|
||||
}
|
||||
|
||||
public bool Cancel { get; set; }
|
||||
}
|
||||
|
||||
public class DeletedVersionsNotification : INotification
|
||||
{
|
||||
public DeletedVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default)
|
||||
{
|
||||
Id = id;
|
||||
Messages = messages;
|
||||
SpecificVersion = specificVersion;
|
||||
DeletePriorVersions = deletePriorVersions;
|
||||
DateToRetain = dateToRetain;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public EventMessages Messages { get; }
|
||||
|
||||
public int SpecificVersion { get; }
|
||||
|
||||
public bool DeletePriorVersions { get; }
|
||||
|
||||
public DateTime DateToRetain { get; }
|
||||
}
|
||||
|
||||
public class DeletingVersionsNotification : DeletedVersionsNotification, ICancelableNotification
|
||||
{
|
||||
public DeletingVersionsNotification(int id, EventMessages messages, int specificVersion = default, bool deletePriorVersions = false, DateTime dateToRetain = default)
|
||||
: base(id, messages, specificVersion, deletePriorVersions, dateToRetain)
|
||||
{
|
||||
}
|
||||
|
||||
public bool Cancel { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
private readonly ILogger<ContentService> _logger;
|
||||
private IQuery<IContent> _queryNotTrashed;
|
||||
private readonly IEventAggregator _eventAggregator;
|
||||
private readonly IRelationService _relationService;
|
||||
|
||||
#region Constructors
|
||||
|
||||
@@ -41,7 +42,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, IEventAggregator eventAggregator)
|
||||
Lazy<IPropertyValidationService> propertyValidationService, IShortStringHelper shortStringHelper, IEventAggregator eventAggregator, IRelationService relationService)
|
||||
: base(provider, loggerFactory, eventMessagesFactory)
|
||||
{
|
||||
_documentRepository = documentRepository;
|
||||
@@ -53,6 +54,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_propertyValidationService = propertyValidationService;
|
||||
_shortStringHelper = shortStringHelper;
|
||||
_eventAggregator = eventAggregator;
|
||||
_relationService = relationService;
|
||||
_logger = loggerFactory.CreateLogger<ContentService>();
|
||||
}
|
||||
|
||||
@@ -1150,7 +1152,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);
|
||||
if (publishResult.Success)
|
||||
{
|
||||
// note: StrategyPublish flips the PublishedState to Publishing!
|
||||
@@ -1243,7 +1245,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");
|
||||
_eventAggregator.Publish(new UnpublishedNotification<IContent>(content, evtMsgs));
|
||||
scope.Events.Dispatch(TreeChanged, this, new TreeChange<IContent>(content, TreeChangeTypes.RefreshBranch).ToEventArgs());
|
||||
|
||||
if (culturesUnpublishing != null)
|
||||
@@ -1298,7 +1300,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));
|
||||
_eventAggregator.Publish(new PublishingNotification<IContent>(content, evtMsgs));
|
||||
}
|
||||
|
||||
// it was not published and now is... descendants that were 'published' (but
|
||||
@@ -1307,7 +1309,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");
|
||||
_eventAggregator.Publish(new PublishedNotification<IContent>(descendants, evtMsgs));
|
||||
}
|
||||
|
||||
switch (publishResult.Result)
|
||||
@@ -1721,7 +1723,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));
|
||||
_eventAggregator.Publish(new PublishedNotification<IContent>(publishedDocuments, evtMsgs));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -1776,11 +1778,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)))
|
||||
var notification = new DeletingNotification<IContent>(content, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -1792,7 +1792,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));
|
||||
{
|
||||
_eventAggregator.Publish(new UnpublishedNotification<IContent>(content, evtMsgs));
|
||||
}
|
||||
|
||||
DeleteLocked(scope, content);
|
||||
|
||||
@@ -1807,11 +1809,12 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
|
||||
private void DeleteLocked(IScope scope, IContent content)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
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));
|
||||
_eventAggregator.Publish(new DeletedNotification<IContent>(c, evtMsgs));
|
||||
|
||||
// media files deleted by QueuingEventDispatcher
|
||||
}
|
||||
@@ -1842,10 +1845,13 @@ 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 notification = new DeletingVersionsNotification(id, evtMsgs, dateToRetain: versionDate);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -1854,8 +1860,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);
|
||||
_eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, dateToRetain: versionDate));
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version date)");
|
||||
|
||||
scope.Complete();
|
||||
@@ -1872,9 +1877,13 @@ 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 notification = new DeletingVersionsNotification(id, evtMsgs, specificVersion: versionId);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -1891,7 +1900,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));
|
||||
_eventAggregator.Publish(new DeletedVersionsNotification(id, evtMsgs, specificVersion: versionId));
|
||||
Audit(AuditType.Delete, userId, Cms.Core.Constants.System.Root, "Delete (by version)");
|
||||
|
||||
scope.Complete();
|
||||
@@ -1914,8 +1923,10 @@ 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 notification = new TrashingNotification<IContent>(moveEventInfo, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs); // causes rollback
|
||||
@@ -1934,9 +1945,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));
|
||||
_eventAggregator.Publish(new TrashedNotification<IContent>(moveInfo, evtMsgs));
|
||||
Audit(AuditType.Move, userId, content.Id, "Moved to recycle bin");
|
||||
|
||||
scope.Complete();
|
||||
@@ -1965,6 +1974,8 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return;
|
||||
}
|
||||
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
var moves = new List<(IContent, string)>();
|
||||
|
||||
using (var scope = ScopeProvider.CreateScope())
|
||||
@@ -1976,8 +1987,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 notification = new MovingNotification<IContent>(moveEventInfo, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return; // causes rollback
|
||||
@@ -2006,9 +2018,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));
|
||||
_eventAggregator.Publish(new MovedNotification<IContent>(moveInfo, evtMsgs));
|
||||
|
||||
Audit(AuditType.Move, userId, content.Id);
|
||||
|
||||
scope.Complete();
|
||||
@@ -2093,8 +2104,9 @@ 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 notification = new EmptyingRecycleBinNotification(nodeObjectType, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -2109,9 +2121,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
deleted.Add(content);
|
||||
}
|
||||
|
||||
recycleBinEventArgs.CanCancel = false;
|
||||
recycleBinEventArgs.RecycleBinEmptiedSuccessfully = true; // oh my?!
|
||||
scope.Events.Dispatch(EmptiedRecycleBin, this, recycleBinEventArgs);
|
||||
_eventAggregator.Publish(new EmptiedRecycleBinNotification(nodeObjectType, evtMsgs));
|
||||
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");
|
||||
|
||||
@@ -2151,13 +2161,16 @@ 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))
|
||||
var notification = new CopyingNotification<IContent>(content, copy, parentId, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return null;
|
||||
@@ -2212,8 +2225,12 @@ 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)))
|
||||
notification = new CopyingNotification<IContent>(descendant, descendantCopy, parentId, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// a copy is not published (but not really unpublishing either)
|
||||
// update the create author and last edit author
|
||||
@@ -2237,7 +2254,14 @@ 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));
|
||||
{
|
||||
if (relateToOriginal)
|
||||
{
|
||||
RelateOnCopy(x.Item1, x.Item2);
|
||||
}
|
||||
|
||||
_eventAggregator.Publish(new CopiedNotification<IContent>(x.Item1, x.Item2, parentId, evtMsgs));
|
||||
}
|
||||
Audit(AuditType.Copy, userId, content.Id);
|
||||
|
||||
scope.Complete();
|
||||
@@ -2246,6 +2270,26 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
return copy;
|
||||
}
|
||||
|
||||
private void RelateOnCopy(IContent original, IContent copy)
|
||||
{
|
||||
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(original.Id, copy.Id, relationType);
|
||||
_relationService.Save(relation);
|
||||
|
||||
Audit(AuditType.Copy, copy.WriterId, copy.Id, $"Copied content with Id: '{copy.Id}' related to original content with Id: '{original.Id}'");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends an <see cref="IContent"/> to Publication, which executes handlers and events for the 'Send to Publication' action.
|
||||
/// </summary>
|
||||
@@ -2254,10 +2298,13 @@ 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 notification = new SendingToPublishNotification<IContent>(content, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return false;
|
||||
@@ -2282,8 +2329,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
if (!saveResult.Success)
|
||||
return saveResult.Success;
|
||||
|
||||
sendToPublishEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(SentToPublish, this, sendToPublishEventArgs);
|
||||
_eventAggregator.Publish(new SentToPublishNotification<IContent>(content, evtMsgs));
|
||||
|
||||
if (culturesChanging != null)
|
||||
Audit(AuditType.SendToPublishVariant, userId, content.Id, $"Send To Publish for cultures: {culturesChanging}", culturesChanging);
|
||||
@@ -2412,7 +2458,9 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
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");
|
||||
{
|
||||
_eventAggregator.Publish(new PublishedNotification<IContent>(published, evtMsgs));
|
||||
}
|
||||
|
||||
Audit(AuditType.Sort, userId, 0, "Sorting content performed by user");
|
||||
return OperationResult.Succeed(evtMsgs);
|
||||
@@ -2492,136 +2540,11 @@ 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 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;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Publishing Strategies
|
||||
@@ -2635,14 +2558,15 @@ 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>
|
||||
/// <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)
|
||||
{
|
||||
// raise Publishing event
|
||||
if (scope.Events.DispatchCancelable(Publishing, this, savingEventArgs.ToContentPublishingEventArgs()))
|
||||
// raise Publishing notification
|
||||
var notification = new PublishingNotification<IContent>(content, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
_logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be published: {Reason}", content.Name, content.Id, "publishing was cancelled");
|
||||
return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content);
|
||||
@@ -2801,8 +2725,10 @@ 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
|
||||
var notification = new UnpublishingNotification<IContent>(content, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
_logger.LogInformation("Document {ContentName} (id={ContentId}) cannot be unpublished: unpublishing was cancelled.", content.Name, content.Id);
|
||||
return new PublishResult(PublishResultType.FailedUnpublishCancelledByEvent, evtMsgs, content);
|
||||
@@ -2871,6 +2797,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,
|
||||
@@ -2883,7 +2810,9 @@ 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)))
|
||||
var notification = new DeletingNotification<IContent>(contents, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return;
|
||||
@@ -2897,7 +2826,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));
|
||||
{
|
||||
_eventAggregator.Publish(new UnpublishedNotification<IContent>(content, evtMsgs));
|
||||
}
|
||||
|
||||
// if current content has children, move them to trash
|
||||
var c = content;
|
||||
@@ -2920,7 +2851,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));
|
||||
{
|
||||
_eventAggregator.Publish(new TrashedNotification<IContent>(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)}");
|
||||
@@ -2997,6 +2930,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;
|
||||
@@ -3017,7 +2952,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");
|
||||
_eventAggregator.Publish(new SavedBlueprintNotification<IContent>(content, evtMsgs));
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
@@ -3025,11 +2960,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));
|
||||
_eventAggregator.Publish(new DeletedBlueprintNotification<IContent>(content, evtMsgs));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -3099,6 +3036,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);
|
||||
@@ -3119,7 +3058,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
_documentBlueprintRepository.Delete(blueprint);
|
||||
}
|
||||
|
||||
scope.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs<IContent>(blueprints), nameof(DeletedBlueprint));
|
||||
_eventAggregator.Publish(new DeletedBlueprintNotification<IContent>(blueprints, evtMsgs));
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -3154,10 +3093,9 @@ 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 notification = new RollingBackNotification<IContent>(content, evtMsgs);
|
||||
_eventAggregator.Publish(notification);
|
||||
if (notification.Cancel)
|
||||
{
|
||||
scope.Complete();
|
||||
return OperationResult.Cancel(evtMsgs);
|
||||
@@ -3177,9 +3115,7 @@ namespace Umbraco.Cms.Core.Services.Implement
|
||||
}
|
||||
else
|
||||
{
|
||||
//Emit RolledBack event aka after
|
||||
rollbackEventArgs.CanCancel = false;
|
||||
scope.Events.Dispatch(RolledBack, this, rollbackEventArgs);
|
||||
_eventAggregator.Publish(new RolledBackNotification<IContent>(content, evtMsgs));
|
||||
|
||||
//Logging & Audit message
|
||||
_logger.LogInformation("User '{UserId}' rolled back content '{ContentId}' to version '{VersionId}'", userId, id, versionId);
|
||||
|
||||
Reference in New Issue
Block a user