mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Implementing comments notifications
This commit is contained in:
@@ -117,6 +117,13 @@ namespace Orchard.Comments.Controllers {
|
|||||||
else {
|
else {
|
||||||
Services.Notifier.Information(T("Your comment has been posted."));
|
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 {
|
else {
|
||||||
Services.TransactionManager.Cancel();
|
Services.TransactionManager.Cancel();
|
||||||
|
@@ -13,5 +13,11 @@ namespace Orchard.Comments.Models {
|
|||||||
get { return this.Retrieve(x => x.ClosedCommentsDelay); }
|
get { return this.Retrieve(x => x.ClosedCommentsDelay); }
|
||||||
set { this.Store(x => x.ClosedCommentsDelay, value); }
|
set { this.Store(x => x.ClosedCommentsDelay, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool NotificationEmail {
|
||||||
|
get { return this.Retrieve(x => x.NotificationEmail); }
|
||||||
|
set { this.Store(x => x.NotificationEmail, value); }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -178,6 +178,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Views\CommentText.cshtml" />
|
<Content Include="Views\CommentText.cshtml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Views\Template.Comment.Notification.cshtml" />
|
||||||
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||||
|
@@ -2,16 +2,21 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Web.Mvc;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Orchard.Comments.Models;
|
using Orchard.Comments.Models;
|
||||||
using Orchard.Comments.Settings;
|
using Orchard.Comments.Settings;
|
||||||
|
using Orchard.ContentManagement;
|
||||||
using Orchard.Core.Common.Models;
|
using Orchard.Core.Common.Models;
|
||||||
|
using Orchard.DisplayManagement;
|
||||||
using Orchard.Environment.Configuration;
|
using Orchard.Environment.Configuration;
|
||||||
using Orchard.Environment.Descriptor;
|
using Orchard.Environment.Descriptor;
|
||||||
using Orchard.Environment.State;
|
using Orchard.Environment.State;
|
||||||
|
using Orchard.Localization;
|
||||||
using Orchard.Logging;
|
using Orchard.Logging;
|
||||||
using Orchard.ContentManagement;
|
using Orchard.Messaging.Services;
|
||||||
|
using Orchard.Mvc.Extensions;
|
||||||
using Orchard.Security;
|
using Orchard.Security;
|
||||||
using Orchard.Services;
|
using Orchard.Services;
|
||||||
|
|
||||||
@@ -25,6 +30,10 @@ namespace Orchard.Comments.Services {
|
|||||||
private readonly ShellSettings _shellSettings;
|
private readonly ShellSettings _shellSettings;
|
||||||
private readonly IShellDescriptorManager _shellDescriptorManager;
|
private readonly IShellDescriptorManager _shellDescriptorManager;
|
||||||
private readonly HashSet<int> _processedCommentsParts = new HashSet<int>();
|
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(
|
public CommentService(
|
||||||
IOrchardServices orchardServices,
|
IOrchardServices orchardServices,
|
||||||
@@ -32,16 +41,28 @@ namespace Orchard.Comments.Services {
|
|||||||
IEncryptionService encryptionService,
|
IEncryptionService encryptionService,
|
||||||
IProcessingEngine processingEngine,
|
IProcessingEngine processingEngine,
|
||||||
ShellSettings shellSettings,
|
ShellSettings shellSettings,
|
||||||
IShellDescriptorManager shellDescriptorManager) {
|
IShellDescriptorManager shellDescriptorManager,
|
||||||
|
IShapeFactory shapeFactory,
|
||||||
|
IShapeDisplay shapeDisplay,
|
||||||
|
IMessageService messageService,
|
||||||
|
IMembershipService membershipService
|
||||||
|
) {
|
||||||
_orchardServices = orchardServices;
|
_orchardServices = orchardServices;
|
||||||
_clock = clock;
|
_clock = clock;
|
||||||
_encryptionService = encryptionService;
|
_encryptionService = encryptionService;
|
||||||
_processingEngine = processingEngine;
|
_processingEngine = processingEngine;
|
||||||
_shellSettings = shellSettings;
|
_shellSettings = shellSettings;
|
||||||
_shellDescriptorManager = shellDescriptorManager;
|
_shellDescriptorManager = shellDescriptorManager;
|
||||||
|
_shapeFactory = shapeFactory;
|
||||||
|
_shapeDisplay = shapeDisplay;
|
||||||
|
_messageService = messageService;
|
||||||
|
_membershipService = membershipService;
|
||||||
|
|
||||||
|
T = NullLocalizer.Instance;
|
||||||
Logger = NullLogger.Instance;
|
Logger = NullLogger.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Localizer T { get; set; }
|
||||||
public ILogger Logger { get; set; }
|
public ILogger Logger { get; set; }
|
||||||
|
|
||||||
public CommentPart GetComment(int id) {
|
public CommentPart GetComment(int id) {
|
||||||
@@ -196,6 +217,48 @@ namespace Orchard.Comments.Services {
|
|||||||
return true;
|
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) {
|
private CommentPart GetCommentWithQueryHints(int id) {
|
||||||
return _orchardServices.ContentManager.Get<CommentPart>(id, VersionOptions.Latest, new QueryHints().ExpandParts<CommentPart>());
|
return _orchardServices.ContentManager.Get<CommentPart>(id, VersionOptions.Latest, new QueryHints().ExpandParts<CommentPart>());
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ namespace Orchard.Comments.Services {
|
|||||||
string CreateNonce(CommentPart comment, TimeSpan delay);
|
string CreateNonce(CommentPart comment, TimeSpan delay);
|
||||||
bool CanStillCommentOn(CommentsPart commentsPart);
|
bool CanStillCommentOn(CommentsPart commentsPart);
|
||||||
bool CanCreateComment(CommentPart commentPart);
|
bool CanCreateComment(CommentPart commentPart);
|
||||||
|
void SendNotificationEmail(CommentPart commentPart);
|
||||||
|
string CreateProtectedUrl(string action, CommentPart part);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -4,22 +4,18 @@ using Orchard.Comments.Models;
|
|||||||
using Orchard.Comments.Services;
|
using Orchard.Comments.Services;
|
||||||
using Orchard.ContentManagement;
|
using Orchard.ContentManagement;
|
||||||
using Orchard.Localization;
|
using Orchard.Localization;
|
||||||
using Orchard.Mvc.Extensions;
|
|
||||||
using Orchard.Tokens;
|
using Orchard.Tokens;
|
||||||
|
|
||||||
namespace Orchard.Comments.Tokens {
|
namespace Orchard.Comments.Tokens {
|
||||||
|
|
||||||
public class CommentTokens : ITokenProvider {
|
public class CommentTokens : ITokenProvider {
|
||||||
private readonly IContentManager _contentManager;
|
private readonly IContentManager _contentManager;
|
||||||
private readonly IWorkContextAccessor _workContextAccessor;
|
|
||||||
private readonly ICommentService _commentService;
|
private readonly ICommentService _commentService;
|
||||||
|
|
||||||
public CommentTokens(
|
public CommentTokens(
|
||||||
IContentManager contentManager,
|
IContentManager contentManager,
|
||||||
IWorkContextAccessor workContextAccessor,
|
|
||||||
ICommentService commentService) {
|
ICommentService commentService) {
|
||||||
_contentManager = contentManager;
|
_contentManager = contentManager;
|
||||||
_workContextAccessor = workContextAccessor;
|
|
||||||
_commentService = commentService;
|
_commentService = commentService;
|
||||||
T = NullLocalizer.Instance;
|
T = NullLocalizer.Instance;
|
||||||
}
|
}
|
||||||
@@ -51,22 +47,12 @@ namespace Orchard.Comments.Tokens {
|
|||||||
.Chain("CommentAuthorUrl", "Text", content => content.As<CommentPart>().SiteName)
|
.Chain("CommentAuthorUrl", "Text", content => content.As<CommentPart>().SiteName)
|
||||||
.Token("CommentAuthorEmail", content => content.As<CommentPart>().Email)
|
.Token("CommentAuthorEmail", content => content.As<CommentPart>().Email)
|
||||||
.Chain("CommentAuthorEmail", "Text", content => content.As<CommentPart>().Email)
|
.Chain("CommentAuthorEmail", "Text", content => content.As<CommentPart>().Email)
|
||||||
.Token("CommentApproveUrl", content => CreateProtectedUrl("Approve", content.As<CommentPart>()))
|
.Token("CommentApproveUrl", content => _commentService.CreateProtectedUrl("Approve", content.As<CommentPart>()))
|
||||||
.Token("CommentModerateUrl", content => CreateProtectedUrl("Moderate", content.As<CommentPart>()))
|
.Token("CommentModerateUrl", content => _commentService.CreateProtectedUrl("Moderate", content.As<CommentPart>()))
|
||||||
.Token("CommentDeleteUrl", content => CreateProtectedUrl("Delete", 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) {
|
private static string CommentAuthor(IContent comment) {
|
||||||
var commentPart = comment.As<CommentPart>();
|
var commentPart = comment.As<CommentPart>();
|
||||||
return String.IsNullOrWhiteSpace(commentPart.UserName) ? commentPart.Author : commentPart.UserName;
|
return String.IsNullOrWhiteSpace(commentPart.UserName) ? commentPart.Author : commentPart.UserName;
|
||||||
|
@@ -1,4 +1,11 @@
|
|||||||
@model Orchard.Comments.Models.CommentSettingsPart
|
@model Orchard.Comments.Models.CommentSettingsPart
|
||||||
|
@using Orchard.Messaging.Services;
|
||||||
|
|
||||||
|
@{
|
||||||
|
var messageManager = WorkContext.Resolve<IMessageManager>();
|
||||||
|
var emailEnabled = messageManager.GetAvailableChannelServices().Contains("email");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>@T("Comments")</legend>
|
<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>
|
<span class="hint">@T("Number of days after comments are automatically closed. Leave to 0 to have them always available.")</span>
|
||||||
</div>
|
</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 account’s email address.")</span>
|
||||||
|
|
||||||
|
@if (!emailEnabled) {
|
||||||
|
<div class="message message-Warning">@T("This option is available when an email module is activated.")</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
@@ -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)
|
||||||
|
|
Reference in New Issue
Block a user