Implementing comments notifications

This commit is contained in:
Sebastien Ros
2014-04-16 10:32:19 -07:00
parent dd203b3edf
commit ff20eb3124
8 changed files with 128 additions and 20 deletions

View File

@@ -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<CommentSettingsPart>();
if (siteSettings.NotificationEmail) {
_commentService.SendNotificationEmail(commentPart);
}
}
else {
Services.TransactionManager.Cancel();

View File

@@ -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); }
}
}
}

View File

@@ -178,6 +178,9 @@
<ItemGroup>
<Content Include="Views\CommentText.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Template.Comment.Notification.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -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<int> _processedCommentsParts = new HashSet<int>();
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<CommonPart>().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<string, object> {
{"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<CommentPart>(id, VersionOptions.Latest, new QueryHints().ExpandParts<CommentPart>());
}

View File

@@ -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);
}
}

View File

@@ -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<CommentPart>().SiteName)
.Token("CommentAuthorEmail", content => content.As<CommentPart>().Email)
.Chain("CommentAuthorEmail", "Text", content => content.As<CommentPart>().Email)
.Token("CommentApproveUrl", content => CreateProtectedUrl("Approve", content.As<CommentPart>()))
.Token("CommentModerateUrl", content => CreateProtectedUrl("Moderate", content.As<CommentPart>()))
.Token("CommentDeleteUrl", content => CreateProtectedUrl("Delete", content.As<CommentPart>()))
.Token("CommentApproveUrl", content => _commentService.CreateProtectedUrl("Approve", content.As<CommentPart>()))
.Token("CommentModerateUrl", content => _commentService.CreateProtectedUrl("Moderate", content.As<CommentPart>()))
.Token("CommentDeleteUrl", content => _commentService.CreateProtectedUrl("Delete", content.As<CommentPart>()))
;
}
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<CommentPart>();
return String.IsNullOrWhiteSpace(commentPart.UserName) ? commentPart.Author : commentPart.UserName;

View File

@@ -1,4 +1,11 @@
@model Orchard.Comments.Models.CommentSettingsPart
@using Orchard.Messaging.Services;
@{
var messageManager = WorkContext.Resolve<IMessageManager>();
var emailEnabled = messageManager.GetAvailableChannelServices().Contains("email");
}
<fieldset>
<legend>@T("Comments")</legend>
@@ -16,4 +23,15 @@
<span class="hint">@T("Number of days after comments are automatically closed. Leave to 0 to have them always available.")</span>
</div>
<div>
<input type="checkbox" value="true" class="check-box" id="@Html.FieldIdFor(m => m.NotificationEmail)" name="@Html.FieldNameFor(m => m.NotificationEmail)" @(emailEnabled ? "" : "disabled=\"disabled\"") />
<label class="forcheckbox" for="CommentSettings_NotificationEmail">@T("Notification email")</label>
@Html.ValidationMessage("NotificationEmail", "*")
<span class="hint">@T("Check to send comment notification emails to the commented content author at their accounts email address.")</span>
@if (!emailEnabled) {
<div class="message message-Warning">@T("This option is available when an email module is activated.")</div>
}
</div>
</fieldset>

View File

@@ -0,0 +1,24 @@
@using Orchard.Comments.Models;
@{
CommentPart commentPart = Model.CommentPart;
string approveUrl = Model.CommentApproveUrl;
string moderateUrl = Model.CommentModerateUrl;
string deleteUrl = Model.CommentDeleteUrl;
}
<br />
@T("A new comment is available.") <br />
@T("From: {0}", commentPart.UserName) <br/>
@T("Site: {0}", commentPart.SiteName) <br />
@T("Email: {0}", commentPart.Email) <br />
@T("Comment: {0}", commentPart.CommentText) <br />
@if (commentPart.Status == CommentStatus.Pending) {
@T("The comment is moderated. <a href=\"{0}\">Approve</a> ", approveUrl)
}
@if (commentPart.Status == CommentStatus.Approved) {
@T("The comment is public. <a href=\"{0}\">Unapprove</a> ", moderateUrl)
}
@T("<a href=\"{0}\">Delete</a> ", deleteUrl)