2019-02-14 09:15:47 +01:00
using System ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Linq ;
2016-09-01 19:06:08 +02:00
using Umbraco.Core ;
2019-02-14 09:15:47 +01:00
using Umbraco.Core.Composing ;
using Umbraco.Core.Configuration ;
2018-10-24 23:55:55 +11:00
using Umbraco.Core.Logging ;
2016-09-01 19:06:08 +02:00
using Umbraco.Core.Models ;
2018-01-15 11:32:30 +01:00
using Umbraco.Core.Models.Entities ;
2019-02-14 09:15:47 +01:00
using Umbraco.Core.Models.Membership ;
2018-03-27 10:04:07 +02:00
using Umbraco.Core.Services ;
2017-12-28 09:18:09 +01:00
using Umbraco.Core.Services.Implement ;
2018-10-29 17:27:33 +11:00
using Umbraco.Web.Actions ;
2016-09-01 19:06:08 +02:00
2019-02-14 09:15:47 +01:00
namespace Umbraco.Web.Compose
2016-09-01 19:06:08 +02:00
{
2019-01-03 21:00:28 +01:00
public sealed class NotificationsComponent : IComponent
2016-09-01 19:06:08 +02:00
{
2019-01-07 09:30:47 +01:00
private readonly Notifier _notifier ;
private readonly ActionCollection _actions ;
2019-04-23 21:55:42 +02:00
private readonly IContentService _contentService ;
2019-01-07 09:30:47 +01:00
2019-04-23 21:55:42 +02:00
public NotificationsComponent ( Notifier notifier , ActionCollection actions , IContentService contentService )
2019-01-07 09:30:47 +01:00
{
_notifier = notifier ;
_actions = actions ;
2019-04-23 21:55:42 +02:00
_contentService = contentService ;
2019-01-07 09:30:47 +01:00
}
public void Initialize ( )
2018-10-24 23:55:55 +11:00
{
//Send notifications for the send to publish action
2020-09-04 00:28:38 +10:00
ContentService . SentToPublish + = ContentService_SentToPublish ;
2016-09-01 19:06:08 +02:00
//Send notifications for the published action
2020-09-04 00:28:38 +10:00
ContentService . Published + = ContentService_Published ;
2018-10-24 23:55:55 +11:00
//Send notifications for the saved action
2020-09-04 00:28:38 +10:00
ContentService . Sorted + = ContentService_Sorted ;
2016-09-01 19:06:08 +02:00
//Send notifications for the update and created actions
2020-09-04 00:28:38 +10:00
ContentService . Saved + = ContentService_Saved ;
2018-10-24 23:55:55 +11:00
//Send notifications for the unpublish action
2020-09-04 00:28:38 +10:00
ContentService . Unpublished + = ContentService_Unpublished ;
2019-04-23 14:24:45 +02:00
//Send notifications for the move/move to recycle bin and restore actions
2020-09-04 00:28:38 +10:00
ContentService . Moved + = ContentService_Moved ;
2019-04-23 14:24:45 +02:00
//Send notifications for the delete action when content is moved to the recycle bin
2020-09-04 00:28:38 +10:00
ContentService . Trashed + = ContentService_Trashed ;
2019-04-23 14:24:45 +02:00
//Send notifications for the copy action
2020-09-04 00:28:38 +10:00
ContentService . Copied + = ContentService_Copied ;
2019-04-20 12:24:53 +02:00
//Send notifications for the rollback action
2020-09-04 00:28:38 +10:00
ContentService . RolledBack + = ContentService_RolledBack ;
2019-04-23 21:55:42 +02:00
//Send notifications for the public access changed action
2020-09-04 00:28:38 +10:00
PublicAccessService . Saved + = PublicAccessService_Saved ;
2019-11-05 12:54:22 +01:00
2020-09-04 00:28:38 +10:00
UserService . UserGroupPermissionsAssigned + = UserService_UserGroupPermissionsAssigned ;
2018-10-24 23:55:55 +11:00
}
2019-01-07 09:30:47 +01:00
public void Terminate ( )
2020-09-04 00:28:38 +10:00
{
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 ;
}
private void UserService_UserGroupPermissionsAssigned ( IUserService sender , Core . Events . SaveEventArgs < EntityPermission > args )
= > UserServiceUserGroupPermissionsAssigned ( args , _contentService ) ;
private void PublicAccessService_Saved ( IPublicAccessService sender , Core . Events . SaveEventArgs < PublicAccessEntry > args )
= > PublicAccessServiceSaved ( args , _contentService ) ;
private void ContentService_RolledBack ( IContentService sender , Core . Events . RollbackEventArgs < IContent > args )
= > _notifier . Notify ( _actions . GetAction < ActionRollback > ( ) , args . Entity ) ;
private void ContentService_Copied ( IContentService sender , Core . Events . CopyEventArgs < IContent > args )
= > _notifier . Notify ( _actions . GetAction < ActionCopy > ( ) , args . Original ) ;
private void ContentService_Trashed ( IContentService sender , Core . Events . MoveEventArgs < IContent > args )
= > _notifier . Notify ( _actions . GetAction < ActionDelete > ( ) , args . MoveInfoCollection . Select ( m = > m . Entity ) . ToArray ( ) ) ;
private void ContentService_Moved ( IContentService sender , Core . Events . MoveEventArgs < IContent > args )
= > ContentServiceMoved ( args ) ;
private void ContentService_Unpublished ( IContentService sender , Core . Events . PublishEventArgs < IContent > args )
= > _notifier . Notify ( _actions . GetAction < ActionUnpublish > ( ) , args . PublishedEntities . ToArray ( ) ) ;
private void ContentService_Saved ( IContentService sender , Core . Events . ContentSavedEventArgs args )
= > ContentServiceSaved ( args ) ;
private void ContentService_Sorted ( IContentService sender , Core . Events . SaveEventArgs < IContent > args )
= > ContentServiceSorted ( sender , args ) ;
private void ContentService_Published ( IContentService sender , Core . Events . ContentPublishedEventArgs args )
= > _notifier . Notify ( _actions . GetAction < ActionPublish > ( ) , args . PublishedEntities . ToArray ( ) ) ;
private void ContentService_SentToPublish ( IContentService sender , Core . Events . SendToPublishEventArgs < IContent > args )
= > _notifier . Notify ( _actions . GetAction < ActionToPublish > ( ) , args . Entity ) ;
2019-01-07 09:30:47 +01:00
2020-09-04 00:28:38 +10:00
private void ContentServiceSorted ( IContentService sender , Core . Events . SaveEventArgs < IContent > args )
2018-10-24 23:55:55 +11:00
{
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.
2018-11-28 11:05:41 +01:00
if ( parentId [ 0 ] < = 0 ) return ;
2018-10-24 23:55:55 +11:00
var parent = sender . GetById ( parentId [ 0 ] ) ;
if ( parent = = null ) return ; // this shouldn't happen
2020-09-04 00:28:38 +10:00
_notifier . Notify ( _actions . GetAction < ActionSort > ( ) , new [ ] { parent } ) ;
2018-10-24 23:55:55 +11:00
}
2020-09-04 00:28:38 +10:00
private void ContentServiceSaved ( Core . Events . SaveEventArgs < IContent > args )
2018-10-24 23:55:55 +11:00
{
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 )
2016-09-01 19:06:08 +02:00
{
2018-10-24 23:55:55 +11:00
var dirty = ( IRememberBeingDirty ) entity ;
if ( dirty . WasPropertyDirty ( "Id" ) )
{
//it's new
newEntities . Add ( entity ) ;
}
else
{
//it's updating
updatedEntities . Add ( entity ) ;
}
}
2020-09-04 00:28:38 +10:00
_notifier . Notify ( _actions . GetAction < ActionNew > ( ) , newEntities . ToArray ( ) ) ;
_notifier . Notify ( _actions . GetAction < ActionUpdate > ( ) , updatedEntities . ToArray ( ) ) ;
2018-10-24 23:55:55 +11:00
}
2019-04-23 22:00:35 +02:00
2020-09-04 00:28:38 +10:00
private void UserServiceUserGroupPermissionsAssigned ( Core . Events . SaveEventArgs < EntityPermission > args , IContentService contentService )
2019-04-23 22:00:35 +02:00
{
var entities = contentService . GetByIds ( args . SavedEntities . Select ( e = > e . EntityId ) ) . ToArray ( ) ;
if ( entities . Any ( ) = = false )
{
return ;
}
2020-09-04 00:28:38 +10:00
_notifier . Notify ( _actions . GetAction < ActionRights > ( ) , entities ) ;
2019-04-23 22:00:35 +02:00
}
2019-11-05 12:54:22 +01:00
2020-09-04 00:28:38 +10:00
private void ContentServiceMoved ( Core . Events . MoveEventArgs < IContent > args )
2019-04-23 14:24:45 +02:00
{
// 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
2019-11-05 13:45:42 +01:00
. Where ( m = > m . OriginalPath . Contains ( Constants . System . RecycleBinContentString ) )
2019-04-23 14:24:45 +02:00
. Select ( m = > m . Entity )
. ToArray ( ) ;
if ( restoredEntities . Any ( ) )
{
_notifier . Notify ( _actions . GetAction < ActionRestore > ( ) , restoredEntities ) ;
}
}
2018-10-24 23:55:55 +11:00
2020-09-04 00:28:38 +10:00
private void PublicAccessServiceSaved ( Core . Events . SaveEventArgs < PublicAccessEntry > args , IContentService contentService )
2019-04-23 21:55:42 +02:00
{
var entities = contentService . GetByIds ( args . SavedEntities . Select ( e = > e . ProtectedNodeId ) ) . ToArray ( ) ;
if ( entities . Any ( ) = = false )
{
return ;
}
2020-09-04 00:28:38 +10:00
_notifier . Notify ( _actions . GetAction < ActionProtect > ( ) , entities ) ;
2019-04-23 21:55:42 +02:00
}
2019-11-05 12:54:22 +01:00
2018-10-24 23:55:55 +11:00
/// <summary>
/// This class is used to send the notifications
/// </summary>
public sealed class Notifier
{
2020-02-19 15:38:22 +11:00
private readonly IUmbracoContextAccessor _umbracoContextAccessor ;
2020-05-07 09:34:16 +02:00
private readonly IRequestAccessor _requestAccessor ;
2018-10-24 23:55:55 +11:00
private readonly INotificationService _notificationService ;
private readonly IUserService _userService ;
private readonly ILocalizedTextService _textService ;
private readonly IGlobalSettings _globalSettings ;
private readonly ILogger _logger ;
2016-09-01 19:06:08 +02:00
2018-10-24 23:55:55 +11:00
/// <summary>
/// Constructor
/// </summary>
/// <param name="umbracoContextAccessor"></param>
/// <param name="notificationService"></param>
/// <param name="userService"></param>
/// <param name="textService"></param>
/// <param name="globalSettings"></param>
/// <param name="contentConfig"></param>
/// <param name="logger"></param>
2020-05-07 09:34:16 +02:00
public Notifier ( IUmbracoContextAccessor umbracoContextAccessor , IRequestAccessor requestAccessor , INotificationService notificationService , IUserService userService , ILocalizedTextService textService , IGlobalSettings globalSettings , ILogger logger )
2018-10-24 23:55:55 +11:00
{
2020-02-19 15:38:22 +11:00
_umbracoContextAccessor = umbracoContextAccessor ;
2020-05-07 09:34:16 +02:00
_requestAccessor = requestAccessor ;
2018-10-24 23:55:55 +11:00
_notificationService = notificationService ;
_userService = userService ;
_textService = textService ;
_globalSettings = globalSettings ;
_logger = logger ;
}
2018-10-25 00:28:37 +11:00
public void Notify ( IAction action , params IContent [ ] entities )
2018-10-24 23:55:55 +11:00
{
2020-02-19 15:38:22 +11:00
var user = _umbracoContextAccessor . UmbracoContext ? . Security ? . CurrentUser ;
2018-10-24 23:55:55 +11:00
//if there is no current user, then use the admin
if ( user = = null )
{
_logger . Debug ( typeof ( Notifier ) , "There is no current Umbraco user logged in, the notifications will be sent from the administrator" ) ;
2019-11-05 13:45:42 +01:00
user = _userService . GetUserById ( Constants . Security . SuperUserId ) ;
2018-10-24 23:55:55 +11:00
if ( user = = null )
2016-09-01 19:06:08 +02:00
{
2020-09-14 10:17:32 +02:00
_logger . LogWarning ( typeof ( Notifier ) , "Notifications can not be sent, no admin user with id {SuperUserId} could be resolved" , Constants . Security . SuperUserId ) ;
2018-10-24 23:55:55 +11:00
return ;
2016-09-01 19:06:08 +02:00
}
}
2020-05-07 09:34:16 +02:00
SendNotification ( user , entities , action , _requestAccessor . GetApplicationUrl ( ) ) ;
2018-10-24 23:55:55 +11:00
}
2017-07-20 11:21:28 +02:00
2018-10-25 00:28:37 +11:00
private void SendNotification ( IUser sender , IEnumerable < IContent > entities , IAction action , Uri siteUri )
2018-05-03 15:09:56 +02:00
{
2018-10-24 23:55:55 +11:00
if ( sender = = null ) throw new ArgumentNullException ( nameof ( sender ) ) ;
2019-10-30 14:36:15 +00:00
if ( siteUri = = null )
{
2020-09-14 10:17:32 +02:00
_logger . LogWarning ( typeof ( Notifier ) , "Notifications can not be sent, no site url is set (might be during boot process?)" ) ;
2019-10-30 14:36:15 +00:00
return ;
}
2018-10-24 23:55:55 +11:00
2018-10-25 17:21:10 +11:00
//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
} ) ) ;
}
2018-10-24 23:55:55 +11:00
}
2016-09-01 19:06:08 +02:00
}
}
2018-10-24 23:55:55 +11:00
2018-11-28 11:05:41 +01:00
2016-09-01 19:06:08 +02:00
}