diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Controllers/CommentController.cs b/src/Orchard.Web/Modules/Orchard.Comments/Controllers/CommentController.cs index f299d7440..840377360 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Controllers/CommentController.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Controllers/CommentController.cs @@ -117,6 +117,13 @@ namespace Orchard.Comments.Controllers { else { Services.Notifier.Information(T("Your comment has been posted.")); } + + // send email notification + var siteSettings = Services.WorkContext.CurrentSite.As(); + if (siteSettings.NotificationEmail) { + _commentService.SendNotificationEmail(commentPart); + } + } else { Services.TransactionManager.Cancel(); diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Models/CommentSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.Comments/Models/CommentSettingsPart.cs index 05c8974bc..cb43c58ca 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Models/CommentSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Models/CommentSettingsPart.cs @@ -13,5 +13,11 @@ namespace Orchard.Comments.Models { get { return this.Retrieve(x => x.ClosedCommentsDelay); } set { this.Store(x => x.ClosedCommentsDelay, value); } } + + public bool NotificationEmail { + get { return this.Retrieve(x => x.NotificationEmail); } + set { this.Store(x => x.NotificationEmail, value); } + } + } } diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj b/src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj index 280dcfb78..07e2fdf11 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj +++ b/src/Orchard.Web/Modules/Orchard.Comments/Orchard.Comments.csproj @@ -178,6 +178,9 @@ + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Services/CommentService.cs b/src/Orchard.Web/Modules/Orchard.Comments/Services/CommentService.cs index f432fae5a..a4e6efa35 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Services/CommentService.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Services/CommentService.cs @@ -2,16 +2,21 @@ using System.Collections.Generic; using System.Globalization; using System.Text; +using System.Web.Mvc; using System.Xml.Linq; using JetBrains.Annotations; using Orchard.Comments.Models; using Orchard.Comments.Settings; +using Orchard.ContentManagement; using Orchard.Core.Common.Models; +using Orchard.DisplayManagement; using Orchard.Environment.Configuration; using Orchard.Environment.Descriptor; using Orchard.Environment.State; +using Orchard.Localization; using Orchard.Logging; -using Orchard.ContentManagement; +using Orchard.Messaging.Services; +using Orchard.Mvc.Extensions; using Orchard.Security; using Orchard.Services; @@ -25,6 +30,10 @@ namespace Orchard.Comments.Services { private readonly ShellSettings _shellSettings; private readonly IShellDescriptorManager _shellDescriptorManager; private readonly HashSet _processedCommentsParts = new HashSet(); + private readonly IShapeFactory _shapeFactory; + private readonly IShapeDisplay _shapeDisplay; + private readonly IMessageService _messageService; + private readonly IMembershipService _membershipService; public CommentService( IOrchardServices orchardServices, @@ -32,16 +41,28 @@ namespace Orchard.Comments.Services { IEncryptionService encryptionService, IProcessingEngine processingEngine, ShellSettings shellSettings, - IShellDescriptorManager shellDescriptorManager) { + IShellDescriptorManager shellDescriptorManager, + IShapeFactory shapeFactory, + IShapeDisplay shapeDisplay, + IMessageService messageService, + IMembershipService membershipService + ) { _orchardServices = orchardServices; _clock = clock; _encryptionService = encryptionService; _processingEngine = processingEngine; _shellSettings = shellSettings; _shellDescriptorManager = shellDescriptorManager; + _shapeFactory = shapeFactory; + _shapeDisplay = shapeDisplay; + _messageService = messageService; + _membershipService = membershipService; + + T = NullLocalizer.Instance; Logger = NullLogger.Instance; } + public Localizer T { get; set; } public ILogger Logger { get; set; } public CommentPart GetComment(int id) { @@ -196,6 +217,48 @@ namespace Orchard.Comments.Services { return true; } + public void SendNotificationEmail(CommentPart commentPart) { + try { + var commentedOn = _orchardServices.ContentManager.Get(commentPart.CommentedOn); + if (commentedOn == null) { + return; + } + + var owner = commentedOn.As().Owner; + if (owner == null) { + return; + } + + var template = _shapeFactory.Create("Template_Comment_Notification", Arguments.From(new { + CommentPart = commentPart, + CommentApproveUrl = CreateProtectedUrl("Approve", commentPart), + CommentModerateUrl = CreateProtectedUrl("Moderate", commentPart), + CommentDeleteUrl = CreateProtectedUrl("Delete", commentPart) + })); + + var parameters = new Dictionary { + {"Subject", T("Comment notification").Text}, + {"Body", _shapeDisplay.Display(template)}, + {"Recipients", owner.Email} + }; + + _messageService.Send("Email", parameters); + } + catch(Exception e) { + Logger.Error(e, "An unexpected error occured while sending a notification email"); + } + } + + public string CreateProtectedUrl(string action, CommentPart part) { + var workContext = _orchardServices.WorkContext; + if (workContext.HttpContext != null) { + var url = new UrlHelper(workContext.HttpContext.Request.RequestContext); + return url.AbsoluteAction(action, "Comment", new { area = "Orchard.Comments", nonce = CreateNonce(part, TimeSpan.FromDays(7)) }); + } + + return null; + } + private CommentPart GetCommentWithQueryHints(int id) { return _orchardServices.ContentManager.Get(id, VersionOptions.Latest, new QueryHints().ExpandParts()); } diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Services/ICommentService.cs b/src/Orchard.Web/Modules/Orchard.Comments/Services/ICommentService.cs index 507d8c27f..8637e8040 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Services/ICommentService.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Services/ICommentService.cs @@ -22,6 +22,7 @@ namespace Orchard.Comments.Services { string CreateNonce(CommentPart comment, TimeSpan delay); bool CanStillCommentOn(CommentsPart commentsPart); bool CanCreateComment(CommentPart commentPart); - + void SendNotificationEmail(CommentPart commentPart); + string CreateProtectedUrl(string action, CommentPart part); } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Tokens/CommentTokens.cs b/src/Orchard.Web/Modules/Orchard.Comments/Tokens/CommentTokens.cs index 67ba1cbb0..b33fc42ae 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Tokens/CommentTokens.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Tokens/CommentTokens.cs @@ -4,22 +4,18 @@ using Orchard.Comments.Models; using Orchard.Comments.Services; using Orchard.ContentManagement; using Orchard.Localization; -using Orchard.Mvc.Extensions; using Orchard.Tokens; namespace Orchard.Comments.Tokens { public class CommentTokens : ITokenProvider { private readonly IContentManager _contentManager; - private readonly IWorkContextAccessor _workContextAccessor; private readonly ICommentService _commentService; public CommentTokens( IContentManager contentManager, - IWorkContextAccessor workContextAccessor, ICommentService commentService) { _contentManager = contentManager; - _workContextAccessor = workContextAccessor; _commentService = commentService; T = NullLocalizer.Instance; } @@ -51,22 +47,12 @@ namespace Orchard.Comments.Tokens { .Chain("CommentAuthorUrl", "Text", content => content.As().SiteName) .Token("CommentAuthorEmail", content => content.As().Email) .Chain("CommentAuthorEmail", "Text", content => content.As().Email) - .Token("CommentApproveUrl", content => CreateProtectedUrl("Approve", content.As())) - .Token("CommentModerateUrl", content => CreateProtectedUrl("Moderate", content.As())) - .Token("CommentDeleteUrl", content => CreateProtectedUrl("Delete", content.As())) + .Token("CommentApproveUrl", content => _commentService.CreateProtectedUrl("Approve", content.As())) + .Token("CommentModerateUrl", content => _commentService.CreateProtectedUrl("Moderate", content.As())) + .Token("CommentDeleteUrl", content => _commentService.CreateProtectedUrl("Delete", content.As())) ; } - private string CreateProtectedUrl(string action, CommentPart part) { - var workContext = _workContextAccessor.GetContext(); - if (workContext.HttpContext != null) { - var url = new UrlHelper(workContext.HttpContext.Request.RequestContext); - return url.AbsoluteAction(action, "Comment", new {area = "Orchard.Comments", nonce = _commentService.CreateNonce(part, TimeSpan.FromDays(7))}); - } - - return null; - } - private static string CommentAuthor(IContent comment) { var commentPart = comment.As(); return String.IsNullOrWhiteSpace(commentPart.UserName) ? commentPart.Author : commentPart.UserName; diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml b/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml index 859508423..d967c3131 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Comments/Views/EditorTemplates/Parts.Comments.SiteSettings.cshtml @@ -1,4 +1,11 @@ @model Orchard.Comments.Models.CommentSettingsPart +@using Orchard.Messaging.Services; + +@{ + var messageManager = WorkContext.Resolve(); + var emailEnabled = messageManager.GetAvailableChannelServices().Contains("email"); +} +
@T("Comments") @@ -16,4 +23,15 @@ @T("Number of days after comments are automatically closed. Leave to 0 to have them always available.") +
+ + + @Html.ValidationMessage("NotificationEmail", "*") + @T("Check to send comment notification emails to the commented content author at their account’s email address.") + + @if (!emailEnabled) { +
@T("This option is available when an email module is activated.")
+ } +
+
\ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Views/Template.Comment.Notification.cshtml b/src/Orchard.Web/Modules/Orchard.Comments/Views/Template.Comment.Notification.cshtml new file mode 100644 index 000000000..f7034fe6d --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Comments/Views/Template.Comment.Notification.cshtml @@ -0,0 +1,24 @@ +@using Orchard.Comments.Models; + +@{ + CommentPart commentPart = Model.CommentPart; + string approveUrl = Model.CommentApproveUrl; + string moderateUrl = Model.CommentModerateUrl; + string deleteUrl = Model.CommentDeleteUrl; +} +
+ +@T("A new comment is available.")
+@T("From: {0}", commentPart.UserName)
+@T("Site: {0}", commentPart.SiteName)
+@T("Email: {0}", commentPart.Email)
+@T("Comment: {0}", commentPart.CommentText)
+ +@if (commentPart.Status == CommentStatus.Pending) { + @T("The comment is moderated. Approve ", approveUrl) +} +@if (commentPart.Status == CommentStatus.Approved) { + @T("The comment is public. Unapprove ", moderateUrl) +} +@T("Delete ", deleteUrl) +