2014-01-10 17:03:00 +11:00
using System ;
2016-09-01 15:25:40 +02:00
using System.Collections.Concurrent ;
2014-01-10 17:03:00 +11:00
using System.Collections.Generic ;
2014-01-13 13:05:25 +11:00
using System.Globalization ;
using System.Linq ;
using System.Net.Mail ;
using System.Text ;
2014-01-13 13:50:30 +11:00
using System.Threading ;
2014-01-13 13:05:25 +11:00
using Umbraco.Core.Configuration ;
2018-10-24 23:55:55 +11:00
using Umbraco.Core.Configuration.UmbracoSettings ;
2014-01-13 13:05:25 +11:00
using Umbraco.Core.IO ;
using Umbraco.Core.Logging ;
2014-01-10 17:03:00 +11:00
using Umbraco.Core.Models ;
2018-01-15 11:32:30 +01:00
using Umbraco.Core.Models.Entities ;
2014-01-10 17:03:00 +11:00
using Umbraco.Core.Models.Membership ;
using Umbraco.Core.Persistence.Repositories ;
2017-12-12 15:04:13 +01:00
using Umbraco.Core.Scoping ;
2014-01-13 13:05:25 +11:00
using Umbraco.Core.Strings ;
2014-01-10 17:03:00 +11:00
2017-12-28 09:18:09 +01:00
namespace Umbraco.Core.Services.Implement
2014-01-10 17:03:00 +11:00
{
2014-05-08 11:38:05 +10:00
public class NotificationService : INotificationService
2014-01-10 17:03:00 +11:00
{
2017-12-12 15:04:13 +01:00
private readonly IScopeProvider _uowProvider ;
2014-01-13 13:05:25 +11:00
private readonly IUserService _userService ;
private readonly IContentService _contentService ;
2018-10-25 17:21:10 +11:00
private readonly ILocalizationService _localizationService ;
2017-12-12 15:04:13 +01:00
private readonly INotificationsRepository _notificationsRepository ;
2018-04-06 13:51:54 +10:00
private readonly IGlobalSettings _globalSettings ;
2018-10-24 23:55:55 +11:00
private readonly IContentSection _contentSection ;
2015-01-07 17:23:24 +11:00
private readonly ILogger _logger ;
2014-01-10 17:03:00 +11:00
2018-10-25 17:21:10 +11:00
public NotificationService ( IScopeProvider provider , IUserService userService , IContentService contentService , ILocalizationService localizationService ,
2018-10-24 23:55:55 +11:00
ILogger logger , INotificationsRepository notificationsRepository , IGlobalSettings globalSettings , IContentSection contentSection )
2014-01-10 17:03:00 +11:00
{
2017-12-12 15:04:13 +01:00
_notificationsRepository = notificationsRepository ;
2018-04-06 13:51:54 +10:00
_globalSettings = globalSettings ;
2018-10-24 23:55:55 +11:00
_contentSection = contentSection ;
2017-05-12 14:49:44 +02:00
_uowProvider = provider ? ? throw new ArgumentNullException ( nameof ( provider ) ) ;
_userService = userService ? ? throw new ArgumentNullException ( nameof ( userService ) ) ;
_contentService = contentService ? ? throw new ArgumentNullException ( nameof ( contentService ) ) ;
2018-10-25 17:21:10 +11:00
_localizationService = localizationService ;
2017-05-12 14:49:44 +02:00
_logger = logger ? ? throw new ArgumentNullException ( nameof ( logger ) ) ;
2014-01-10 17:03:00 +11:00
}
2016-11-03 10:31:44 +01:00
/// <summary>
/// Gets the previous version to the latest version of the content item if there is one
/// </summary>
/// <param name="contentId"></param>
2016-10-05 18:30:03 +02:00
/// <returns></returns>
private IContentBase GetPreviousVersion ( int contentId )
2016-11-03 10:31:44 +01:00
{
// Regarding this: http://issues.umbraco.org/issue/U4-5180
// we know they are descending from the service so we know that newest is first
// we are only selecting the top 2 rows since that is all we need
var allVersions = _contentService . GetVersionIds ( contentId , 2 ) . ToList ( ) ;
var prevVersionIndex = allVersions . Count > 1 ? 1 : 0 ;
2017-11-30 13:56:29 +01:00
return _contentService . GetVersion ( allVersions [ prevVersionIndex ] ) ;
2014-01-10 17:03:00 +11:00
}
2016-06-28 12:03:36 -06:00
/// <summary>
/// Sends the notifications for the specified user regarding the specified node and action.
/// </summary>
/// <param name="entities"></param>
/// <param name="operatingUser"></param>
/// <param name="action"></param>
/// <param name="actionName"></param>
2018-10-24 23:55:55 +11:00
/// <param name="siteUri"></param>
2016-06-28 12:03:36 -06:00
/// <param name="createSubject"></param>
/// <param name="createBody"></param>
2018-10-25 00:28:37 +11:00
public void SendNotifications ( IUser operatingUser , IEnumerable < IContent > entities , string action , string actionName , Uri siteUri ,
2018-10-24 23:55:55 +11:00
Func < ( IUser user , NotificationEmailSubjectParams subject ) , string > createSubject ,
Func < ( IUser user , NotificationEmailBodyParams body , bool isHtml ) , string > createBody )
2016-06-28 12:03:36 -06:00
{
2018-10-25 00:28:37 +11:00
var entitiesL = entities . ToList ( ) ;
2016-07-05 11:31:53 +02:00
2016-10-05 18:30:03 +02:00
//exit if there are no entities
if ( entitiesL . Count = = 0 ) return ;
2016-06-28 12:03:36 -06:00
2016-10-05 18:30:03 +02:00
//put all entity's paths into a list with the same indicies
var paths = entitiesL . Select ( x = > x . Path . Split ( ',' ) . Select ( int . Parse ) . ToArray ( ) ) . ToArray ( ) ;
2016-07-05 11:31:53 +02:00
2016-09-01 12:13:58 +02:00
// lazily get versions
2016-10-05 18:30:03 +02:00
var prevVersionDictionary = new Dictionary < int , IContentBase > ( ) ;
2016-06-28 12:03:36 -06:00
2016-09-01 12:13:58 +02:00
// see notes above
2018-10-24 23:55:55 +11:00
var id = Constants . Security . SuperUserId ;
2016-09-01 12:13:58 +02:00
const int pagesz = 400 ; // load batches of 400 users
do
2016-06-28 12:03:36 -06:00
{
2016-09-01 12:13:58 +02:00
// users are returned ordered by id, notifications are returned ordered by user id
var users = ( ( UserService ) _userService ) . GetNextUsers ( id , pagesz ) . Where ( x = > x . IsApproved ) . ToList ( ) ;
2017-09-19 18:19:05 +02:00
var notifications = GetUsersNotifications ( users . Select ( x = > x . Id ) , action , Enumerable . Empty < int > ( ) , Constants . ObjectTypes . Document ) . ToList ( ) ;
2016-11-03 10:31:44 +01:00
if ( notifications . Count = = 0 ) break ;
2016-06-28 12:03:36 -06:00
2016-09-01 12:13:58 +02:00
var i = 0 ;
foreach ( var user in users )
2016-06-28 12:03:36 -06:00
{
2016-09-01 12:13:58 +02:00
// continue if there's no notification for this user
if ( notifications [ i ] . UserId ! = user . Id ) continue ; // next user
2014-01-13 13:50:30 +11:00
2016-09-01 12:13:58 +02:00
for ( var j = 0 ; j < entitiesL . Count ; j + + )
2014-01-13 13:05:25 +11:00
{
2016-09-01 12:13:58 +02:00
var content = entitiesL [ j ] ;
2016-10-05 18:30:03 +02:00
var path = paths [ j ] ;
2017-07-20 11:21:28 +02:00
2016-09-01 12:13:58 +02:00
// test if the notification applies to the path ie to this entity
if ( path . Contains ( notifications [ i ] . EntityId ) = = false ) continue ; // next entity
2017-07-20 11:21:28 +02:00
2016-10-05 18:30:03 +02:00
if ( prevVersionDictionary . ContainsKey ( content . Id ) = = false )
{
prevVersionDictionary [ content . Id ] = GetPreviousVersion ( content . Id ) ;
}
2017-07-20 11:21:28 +02:00
2016-09-01 15:25:40 +02:00
// queue notification
2018-10-24 23:55:55 +11:00
var req = CreateNotificationRequest ( operatingUser , user , content , prevVersionDictionary [ content . Id ] , actionName , siteUri , createSubject , createBody ) ;
2016-09-01 15:25:40 +02:00
Enqueue ( req ) ;
2014-01-13 13:05:25 +11:00
}
2016-09-01 12:13:58 +02:00
2016-10-05 18:30:03 +02:00
// skip other notifications for this user, essentially this means moving i to the next index of notifications
// for the next user.
do
2014-01-13 13:05:25 +11:00
{
2016-10-05 18:30:03 +02:00
i + + ;
} while ( i < notifications . Count & & notifications [ i ] . UserId = = user . Id ) ;
2017-07-20 11:21:28 +02:00
2016-09-01 12:13:58 +02:00
if ( i > = notifications . Count ) break ; // break if no more notifications
2014-01-13 13:05:25 +11:00
}
2016-11-03 10:31:44 +01:00
// load more users if any
2016-09-01 12:13:58 +02:00
id = users . Count = = pagesz ? users . Last ( ) . Id + 1 : - 1 ;
} while ( id > 0 ) ;
2014-01-10 17:03:00 +11:00
}
2016-09-01 15:25:40 +02:00
private IEnumerable < Notification > GetUsersNotifications ( IEnumerable < int > userIds , string action , IEnumerable < int > nodeIds , Guid objectType )
2016-09-01 12:13:58 +02:00
{
2017-12-15 16:29:14 +01:00
using ( var scope = _uowProvider . CreateScope ( autoComplete : true ) )
2016-11-03 10:31:44 +01:00
{
2017-12-12 15:04:13 +01:00
return _notificationsRepository . GetUsersNotifications ( userIds , action , nodeIds , objectType ) ;
2016-11-03 10:31:44 +01:00
}
2016-09-01 12:13:58 +02:00
}
2014-01-10 17:03:00 +11:00
/// <summary>
/// Gets the notifications for the user
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public IEnumerable < Notification > GetUserNotifications ( IUser user )
{
2017-12-15 16:29:14 +01:00
using ( var scope = _uowProvider . CreateScope ( autoComplete : true ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
return _notificationsRepository . GetUserNotifications ( user ) ;
2016-04-28 08:48:59 +02:00
}
2014-01-10 17:03:00 +11:00
}
2014-01-13 13:05:25 +11:00
/// <summary>
/// Gets the notifications for the user based on the specified node path
/// </summary>
/// <param name="user"></param>
/// <param name="path"></param>
/// <returns></returns>
/// <remarks>
/// Notifications are inherited from the parent so any child node will also have notifications assigned based on it's parent (ancestors)
/// </remarks>
public IEnumerable < Notification > GetUserNotifications ( IUser user , string path )
{
2016-07-05 11:31:53 +02:00
var userNotifications = GetUserNotifications ( user ) ;
return FilterUserNotificationsByPath ( userNotifications , path ) ;
2014-01-13 13:05:25 +11:00
}
2016-07-08 16:32:06 +02:00
/// <summary>
/// Filters a userNotifications collection by a path
/// </summary>
/// <param name="userNotifications"></param>
/// <param name="path"></param>
2016-06-28 12:03:36 -06:00
/// <returns></returns>
2016-07-08 16:32:06 +02:00
public IEnumerable < Notification > FilterUserNotificationsByPath ( IEnumerable < Notification > userNotifications , string path )
{
2014-01-13 13:05:25 +11:00
var pathParts = path . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) ;
2016-11-03 10:31:44 +01:00
return userNotifications . Where ( r = > pathParts . InvariantContains ( r . EntityId . ToString ( CultureInfo . InvariantCulture ) ) ) . ToList ( ) ;
2014-01-13 13:05:25 +11:00
}
2014-01-10 17:03:00 +11:00
/// <summary>
/// Deletes notifications by entity
/// </summary>
/// <param name="entity"></param>
public IEnumerable < Notification > GetEntityNotifications ( IEntity entity )
{
2017-12-15 16:29:14 +01:00
using ( var scope = _uowProvider . CreateScope ( autoComplete : true ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
return _notificationsRepository . GetEntityNotifications ( entity ) ;
2016-04-28 08:48:59 +02:00
}
2014-01-10 17:03:00 +11:00
}
/// <summary>
/// Deletes notifications by entity
/// </summary>
/// <param name="entity"></param>
public void DeleteNotifications ( IEntity entity )
{
2017-12-12 15:04:13 +01:00
using ( var scope = _uowProvider . CreateScope ( ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
_notificationsRepository . DeleteNotifications ( entity ) ;
scope . Complete ( ) ;
2016-04-28 08:48:59 +02:00
}
2014-01-10 17:03:00 +11:00
}
/// <summary>
/// Deletes notifications by user
/// </summary>
/// <param name="user"></param>
public void DeleteNotifications ( IUser user )
{
2017-12-12 15:04:13 +01:00
using ( var scope = _uowProvider . CreateScope ( ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
_notificationsRepository . DeleteNotifications ( user ) ;
scope . Complete ( ) ;
2016-04-28 08:48:59 +02:00
}
2014-01-10 17:03:00 +11:00
}
/// <summary>
/// Delete notifications by user and entity
/// </summary>
/// <param name="user"></param>
/// <param name="entity"></param>
public void DeleteNotifications ( IUser user , IEntity entity )
{
2017-12-12 15:04:13 +01:00
using ( var scope = _uowProvider . CreateScope ( ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
_notificationsRepository . DeleteNotifications ( user , entity ) ;
scope . Complete ( ) ;
2016-04-28 08:48:59 +02:00
}
2014-01-10 17:03:00 +11:00
}
2016-03-22 14:39:08 +01:00
/// <summary>
/// Sets the specific notifications for the user and entity
/// </summary>
/// <param name="user"></param>
/// <param name="entity"></param>
/// <param name="actions"></param>
/// <remarks>
/// This performs a full replace
/// </remarks>
public IEnumerable < Notification > SetNotifications ( IUser user , IEntity entity , string [ ] actions )
{
2017-12-12 15:04:13 +01:00
using ( var scope = _uowProvider . CreateScope ( ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
var notifications = _notificationsRepository . SetNotifications ( user , entity , actions ) ;
scope . Complete ( ) ;
2016-05-18 10:55:19 +02:00
return notifications ;
2016-04-28 08:48:59 +02:00
}
2016-03-22 14:39:08 +01:00
}
2014-01-10 17:03:00 +11:00
/// <summary>
/// Creates a new notification
/// </summary>
/// <param name="user"></param>
/// <param name="entity"></param>
/// <param name="action">The action letter - note: this is a string for future compatibility</param>
/// <returns></returns>
public Notification CreateNotification ( IUser user , IEntity entity , string action )
{
2017-12-12 15:04:13 +01:00
using ( var scope = _uowProvider . CreateScope ( ) )
2016-04-28 08:48:59 +02:00
{
2017-12-12 15:04:13 +01:00
var notification = _notificationsRepository . CreateNotification ( user , entity , action ) ;
scope . Complete ( ) ;
2016-05-18 10:55:19 +02:00
return notification ;
2016-04-28 08:48:59 +02:00
}
2014-01-10 17:03:00 +11:00
}
2014-01-13 13:05:25 +11:00
#region private methods
/// <summary>
/// Sends the notification
/// </summary>
/// <param name="performingUser"></param>
/// <param name="mailingUser"></param>
/// <param name="content"></param>
2016-10-05 18:30:03 +02:00
/// <param name="oldDoc"></param>
2014-01-13 13:05:25 +11:00
/// <param name="actionName">The action readable name - currently an action is just a single letter, this is the name associated with the letter </param>
2018-10-24 23:55:55 +11:00
/// <param name="siteUri"></param>
2014-01-13 13:05:25 +11:00
/// <param name="createSubject">Callback to create the mail subject</param>
/// <param name="createBody">Callback to create the mail body</param>
2018-10-25 17:21:10 +11:00
private NotificationRequest CreateNotificationRequest ( IUser performingUser , IUser mailingUser , IContent content , IContentBase oldDoc ,
2018-10-24 23:55:55 +11:00
string actionName ,
Uri siteUri ,
Func < ( IUser user , NotificationEmailSubjectParams subject ) , string > createSubject ,
Func < ( IUser user , NotificationEmailBodyParams body , bool isHtml ) , string > createBody )
2014-01-13 13:05:25 +11:00
{
if ( performingUser = = null ) throw new ArgumentNullException ( "performingUser" ) ;
if ( mailingUser = = null ) throw new ArgumentNullException ( "mailingUser" ) ;
if ( content = = null ) throw new ArgumentNullException ( "content" ) ;
2018-10-24 23:55:55 +11:00
if ( siteUri = = null ) throw new ArgumentNullException ( "siteUri" ) ;
2014-01-13 13:05:25 +11:00
if ( createSubject = = null ) throw new ArgumentNullException ( "createSubject" ) ;
2017-07-20 11:21:28 +02:00
if ( createBody = = null ) throw new ArgumentNullException ( "createBody" ) ;
2014-01-13 13:05:25 +11:00
// build summary
var summary = new StringBuilder ( ) ;
2018-10-25 17:21:10 +11:00
if ( content . ContentType . VariesByNothing ( ) )
2014-01-13 13:05:25 +11:00
{
2018-10-25 17:21:10 +11:00
if ( ! _contentSection . DisableHtmlEmail )
{
//create the html summary for invariant content
//list all of the property values like we used to
summary . Append ( "<table style=\"width: 100 %; \">" ) ;
foreach ( var p in content . Properties )
{
//fixme doesn't take into account variants
2018-04-04 13:11:12 +10:00
2018-10-25 17:21:10 +11:00
var newText = p . GetValue ( ) ! = null ? p . GetValue ( ) . ToString ( ) : "" ;
var oldText = newText ;
2014-01-13 13:05:25 +11:00
2018-10-25 17:21:10 +11:00
// check if something was changed and display the changes otherwise display the fields
if ( oldDoc . Properties . Contains ( p . PropertyType . Alias ) )
{
var oldProperty = oldDoc . Properties [ p . PropertyType . Alias ] ;
oldText = oldProperty . GetValue ( ) ! = null ? oldProperty . GetValue ( ) . ToString ( ) : "" ;
2016-09-01 12:13:58 +02:00
2018-10-25 17:21:10 +11:00
// replace html with char equivalent
ReplaceHtmlSymbols ( ref oldText ) ;
ReplaceHtmlSymbols ( ref newText ) ;
}
//show the values
summary . Append ( "<tr>" ) ;
summary . Append ( "<th style='text-align: left; vertical-align: top; width: 25%;border-bottom: 1px solid #CCC'>" ) ;
summary . Append ( p . PropertyType . Name ) ;
summary . Append ( "</th>" ) ;
summary . Append ( "<td style='text-align: left; vertical-align: top;border-bottom: 1px solid #CCC'>" ) ;
summary . Append ( newText ) ;
summary . Append ( "</td>" ) ;
summary . Append ( "</tr>" ) ;
}
summary . Append ( "</table>" ) ;
2014-01-13 13:05:25 +11:00
}
2018-10-25 17:21:10 +11:00
}
else if ( content . ContentType . VariesByCulture ( ) )
{
//it's variant, so detect what cultures have changed
2016-09-01 12:13:58 +02:00
2018-10-25 17:21:10 +11:00
if ( ! _contentSection . DisableHtmlEmail )
{
//Create the html based summary (ul of culture names)
var culturesChanged = content . CultureInfos . Where ( x = > x . Value . WasDirty ( ) )
. Select ( x = > x . Key )
. Select ( _localizationService . GetLanguageByIsoCode )
. WhereNotNull ( )
. Select ( x = > x . CultureName ) ;
summary . Append ( "<ul>" ) ;
foreach ( var culture in culturesChanged )
{
summary . Append ( "<li>" ) ;
summary . Append ( culture ) ;
summary . Append ( "</li>" ) ;
}
summary . Append ( "</ul>" ) ;
}
else
{
//Create the text based summary (csv of culture names)
var culturesChanged = string . Join ( ", " , content . CultureInfos . Where ( x = > x . Value . WasDirty ( ) )
. Select ( x = > x . Key )
. Select ( _localizationService . GetLanguageByIsoCode )
. WhereNotNull ( )
. Select ( x = > x . CultureName ) ) ;
summary . Append ( "'" ) ;
summary . Append ( culturesChanged ) ;
summary . Append ( "'" ) ;
}
}
else
{
//not supported yet...
throw new NotSupportedException ( ) ;
2014-01-13 13:05:25 +11:00
}
2018-10-24 23:55:55 +11:00
var protocol = _globalSettings . UseHttps ? "https" : "http" ;
var subjectVars = new NotificationEmailSubjectParams (
string . Concat ( siteUri . Authority , IOHelper . ResolveUrl ( SystemDirectories . Umbraco ) ) ,
actionName ,
content . Name ) ;
var bodyVars = new NotificationEmailBodyParams (
mailingUser . Name ,
actionName ,
content . Name ,
content . Id . ToString ( CultureInfo . InvariantCulture ) ,
string . Format ( "{2}://{0}/{1}" ,
string . Concat ( siteUri . Authority ) ,
//TODO: RE-enable this so we can have a nice url
/*umbraco.library.NiceUrl(documentObject.Id))*/
string . Concat ( content . Id , ".aspx" ) ,
protocol ) ,
performingUser . Name ,
string . Concat ( siteUri . Authority , IOHelper . ResolveUrl ( SystemDirectories . Umbraco ) ) ,
summary . ToString ( ) ) ;
2014-01-13 13:05:25 +11:00
2016-09-01 12:13:58 +02:00
// create the mail message
2018-10-24 23:55:55 +11:00
var mail = new MailMessage ( _contentSection . NotificationEmailAddress , mailingUser . Email ) ;
2014-01-13 13:05:25 +11:00
// populate the message
2018-10-24 23:55:55 +11:00
mail . Subject = createSubject ( ( mailingUser , subjectVars ) ) ;
if ( _contentSection . DisableHtmlEmail )
2014-01-13 13:05:25 +11:00
{
mail . IsBodyHtml = false ;
2018-10-24 23:55:55 +11:00
mail . Body = createBody ( ( user : mailingUser , body : bodyVars , false ) ) ;
2014-01-13 13:05:25 +11:00
}
else
{
mail . IsBodyHtml = true ;
mail . Body =
2016-10-05 18:30:03 +02:00
string . Concat ( @ "<html><head>
2014-01-13 13:05:25 +11:00
< / head >
< body style = ' font - family : Trebuchet MS , arial , sans - serif ; font - color : black ; ' >
2018-10-24 23:55:55 +11:00
", createBody((user: mailingUser, body: bodyVars, true)));
2014-01-13 13:05:25 +11:00
}
// nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here
// adding the server name to make sure we don't replace external links
2018-04-06 13:51:54 +10:00
if ( _globalSettings . UseHttps & & string . IsNullOrEmpty ( mail . Body ) = = false )
2014-01-13 13:05:25 +11:00
{
2018-10-24 23:55:55 +11:00
string serverName = siteUri . Host ;
2014-01-13 13:05:25 +11:00
mail . Body = mail . Body . Replace (
string . Format ( "http://{0}" , serverName ) ,
string . Format ( "https://{0}" , serverName ) ) ;
}
2016-09-01 15:25:40 +02:00
return new NotificationRequest ( mail , actionName , mailingUser . Name , mailingUser . Email ) ;
2014-01-13 13:05:25 +11:00
}
2018-10-24 23:55:55 +11:00
private string ReplaceLinks ( string text , Uri siteUri )
2014-01-13 13:05:25 +11:00
{
2018-04-06 13:51:54 +10:00
var sb = new StringBuilder ( _globalSettings . UseHttps ? "https://" : "http://" ) ;
2018-10-24 23:55:55 +11:00
sb . Append ( siteUri . Authority ) ;
2016-10-05 18:30:03 +02:00
sb . Append ( "/" ) ;
var domain = sb . ToString ( ) ;
2014-01-13 13:05:25 +11:00
text = text . Replace ( "href=\"/" , "href=\"" + domain ) ;
text = text . Replace ( "src=\"/" , "src=\"" + domain ) ;
return text ;
}
/// <summary>
/// Replaces the HTML symbols with the character equivalent.
/// </summary>
/// <param name="oldString">The old string.</param>
private static void ReplaceHtmlSymbols ( ref string oldString )
{
2018-10-25 00:45:45 +11:00
if ( oldString . IsNullOrWhiteSpace ( ) ) return ;
2014-01-13 13:05:25 +11:00
oldString = oldString . Replace ( " " , " " ) ;
oldString = oldString . Replace ( "’" , "'" ) ;
oldString = oldString . Replace ( "&" , "&" ) ;
oldString = oldString . Replace ( "“" , "“" ) ;
oldString = oldString . Replace ( "”" , "”" ) ;
oldString = oldString . Replace ( """ , "\"" ) ;
}
2018-10-25 00:45:45 +11:00
2016-11-03 10:31:44 +01:00
// manage notifications
// ideally, would need to use IBackgroundTasks - but they are not part of Core!
2016-09-01 15:25:40 +02:00
private static readonly object Locker = new object ( ) ;
private static readonly BlockingCollection < NotificationRequest > Queue = new BlockingCollection < NotificationRequest > ( ) ;
private static volatile bool _running ;
private void Enqueue ( NotificationRequest notification )
{
Queue . Add ( notification ) ;
if ( _running ) return ;
lock ( Locker )
{
if ( _running ) return ;
Process ( Queue ) ;
_running = true ;
}
2016-11-03 10:31:44 +01:00
}
2016-09-01 15:25:40 +02:00
private class NotificationRequest
{
public NotificationRequest ( MailMessage mail , string action , string userName , string email )
{
Mail = mail ;
Action = action ;
UserName = userName ;
Email = email ;
}
public MailMessage Mail { get ; private set ; }
public string Action { get ; private set ; }
public string UserName { get ; private set ; }
public string Email { get ; private set ; }
2014-01-13 13:05:25 +11:00
}
2016-09-01 15:25:40 +02:00
private void Process ( BlockingCollection < NotificationRequest > notificationRequests )
{
ThreadPool . QueueUserWorkItem ( state = >
{
var s = new SmtpClient ( ) ;
try
{
_logger . Debug < NotificationService > ( "Begin processing notifications." ) ;
while ( true )
{
NotificationRequest request ;
2016-11-03 10:31:44 +01:00
while ( notificationRequests . TryTake ( out request , 8 * 1000 ) ) // stay on for 8s
{
try
{
if ( Sendmail ! = null ) Sendmail ( s , request . Mail , _logger ) ; else s . Send ( request . Mail ) ;
2018-08-14 15:08:32 +01:00
_logger . Debug < NotificationService > ( "Notification '{Action}' sent to {Username} ({Email})" , request . Action , request . UserName , request . Email ) ;
2016-09-01 15:25:40 +02:00
}
2016-11-03 10:31:44 +01:00
catch ( Exception ex )
{
2018-08-17 15:41:58 +01:00
_logger . Error < NotificationService > ( ex , "An error occurred sending notification" ) ;
2016-11-03 10:31:44 +01:00
s . Dispose ( ) ;
s = new SmtpClient ( ) ;
}
finally
{
request . Mail . Dispose ( ) ;
}
}
2016-09-01 15:25:40 +02:00
lock ( Locker )
{
2016-11-03 10:31:44 +01:00
if ( notificationRequests . Count > 0 ) continue ; // last chance
2016-09-01 15:25:40 +02:00
_running = false ; // going down
break ;
}
}
}
finally
{
s . Dispose ( ) ;
}
_logger . Debug < NotificationService > ( "Done processing notifications." ) ;
} ) ;
}
// for tests
internal static Action < SmtpClient , MailMessage , ILogger > Sendmail ;
//= (_, msg, logger) => logger.Debug<NotificationService>("Email " + msg.To.ToString());
2014-01-13 13:05:25 +11:00
#endregion
2014-01-10 17:03:00 +11:00
}
2017-07-20 11:21:28 +02:00
}