mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Adding Approve, Delete and Moderate GET actions driven by tokens
Allow comments tokens to be chained with Text ones (HtmlEncode, ...) --HG-- branch : 1.x
This commit is contained in:
@@ -123,6 +123,36 @@ namespace Orchard.Comments.Controllers {
|
||||
return this.RedirectLocal(returnUrl, "~/");
|
||||
}
|
||||
|
||||
public ActionResult Approve(string nonce) {
|
||||
int id;
|
||||
if (_commentService.DecryptNonce(nonce, out id)) {
|
||||
_commentService.ApproveComment(id);
|
||||
}
|
||||
|
||||
Services.Notifier.Information(T("Comment approved successfully"));
|
||||
return Redirect("~/");
|
||||
}
|
||||
|
||||
public ActionResult Delete(string nonce) {
|
||||
int id;
|
||||
if (_commentService.DecryptNonce(nonce, out id)) {
|
||||
_commentService.DeleteComment(id);
|
||||
}
|
||||
|
||||
Services.Notifier.Information(T("Comment deleted successfully"));
|
||||
return Redirect("~/");
|
||||
}
|
||||
|
||||
public ActionResult Moderate(string nonce) {
|
||||
int id;
|
||||
if (_commentService.DecryptNonce(nonce, out id)) {
|
||||
_commentService.UnapproveComment(id);
|
||||
}
|
||||
|
||||
Services.Notifier.Information(T("Comment moderated successfully"));
|
||||
return Redirect("~/");
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
@@ -9,5 +9,5 @@ Features:
|
||||
Orchard.Comments:
|
||||
Name: Comments
|
||||
Description: Standard content item comments.
|
||||
Dependencies: Settings
|
||||
Dependencies: Settings, Orchard.Tokens
|
||||
Category: Social
|
||||
|
@@ -119,6 +119,10 @@
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Orchard.Tokens\Orchard.Tokens.csproj">
|
||||
<Project>{6f759635-13d7-4e94-bcc9-80445d63f117}</Project>
|
||||
<Name>Orchard.Tokens</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Admin\Edit.cshtml" />
|
||||
|
@@ -1,15 +1,28 @@
|
||||
using JetBrains.Annotations;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Orchard.Comments.Models;
|
||||
using Orchard.Logging;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Security;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Comments.Services {
|
||||
[UsedImplicitly]
|
||||
public class CommentService : ICommentService {
|
||||
private readonly IOrchardServices _orchardServices;
|
||||
private readonly IClock _clock;
|
||||
private readonly IEncryptionService _encryptionService;
|
||||
|
||||
public CommentService(IOrchardServices orchardServices) {
|
||||
public CommentService(
|
||||
IOrchardServices orchardServices,
|
||||
IClock clock,
|
||||
IEncryptionService encryptionService) {
|
||||
_orchardServices = orchardServices;
|
||||
_clock = clock;
|
||||
_encryptionService = encryptionService;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
@@ -64,7 +77,7 @@ namespace Orchard.Comments.Services {
|
||||
}
|
||||
|
||||
public void DeleteComment(int commentId) {
|
||||
_orchardServices.ContentManager.Remove(_orchardServices.ContentManager.Get(commentId));
|
||||
_orchardServices.ContentManager.Remove(_orchardServices.ContentManager.Get<CommentPart>(commentId).ContentItem);
|
||||
}
|
||||
|
||||
public bool CommentsDisabledForCommentedContent(int id) {
|
||||
@@ -79,6 +92,28 @@ namespace Orchard.Comments.Services {
|
||||
_orchardServices.ContentManager.Get<CommentsPart>(id, VersionOptions.Latest).CommentsActive = true;
|
||||
}
|
||||
|
||||
public string CreateNonce(CommentPart comment, TimeSpan delay) {
|
||||
var challengeToken = new XElement("n", new XAttribute("c", comment.Id), new XAttribute("v", _clock.UtcNow.ToUniversalTime().Add(delay).ToString(CultureInfo.InvariantCulture))).ToString();
|
||||
var data = Encoding.UTF8.GetBytes(challengeToken);
|
||||
return Convert.ToBase64String(_encryptionService.Encode(data));
|
||||
}
|
||||
|
||||
public bool DecryptNonce(string nonce, out int id) {
|
||||
id = 0;
|
||||
|
||||
try {
|
||||
var data = _encryptionService.Decode(Convert.FromBase64String(nonce));
|
||||
var xml = Encoding.UTF8.GetString(data);
|
||||
var element = XElement.Parse(xml);
|
||||
id = Convert.ToInt32(element.Attribute("c").Value);
|
||||
var validateByUtc = DateTime.Parse(element.Attribute("v").Value, CultureInfo.InvariantCulture);
|
||||
return _clock.UtcNow <= validateByUtc;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
private CommentPart GetCommentWithQueryHints(int id) {
|
||||
return _orchardServices.ContentManager.Get<CommentPart>(id, VersionOptions.Latest, new QueryHints().ExpandParts<CommentPart>());
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Orchard.Comments.Models;
|
||||
using Orchard.ContentManagement;
|
||||
|
||||
@@ -16,5 +17,7 @@ namespace Orchard.Comments.Services {
|
||||
bool CommentsDisabledForCommentedContent(int id);
|
||||
void DisableCommentsForCommentedContent(int id);
|
||||
void EnableCommentsForCommentedContent(int id);
|
||||
bool DecryptNonce(string nonce, out int id);
|
||||
string CreateNonce(CommentPart comment, TimeSpan delay);
|
||||
}
|
||||
}
|
@@ -1,46 +1,72 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Comments.Models;
|
||||
using Orchard.Comments.Services;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Events;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.Tokens;
|
||||
|
||||
namespace Orchard.Comments.Tokens {
|
||||
public interface ITokenProvider : IEventHandler {
|
||||
void Describe(dynamic context);
|
||||
void Evaluate(dynamic context);
|
||||
}
|
||||
|
||||
public class CommentTokens : ITokenProvider {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IWorkContextAccessor _workContextAccessor;
|
||||
private readonly ICommentService _commentService;
|
||||
|
||||
public CommentTokens(IContentManager contentManager) {
|
||||
public CommentTokens(
|
||||
IContentManager contentManager,
|
||||
IWorkContextAccessor workContextAccessor,
|
||||
ICommentService commentService) {
|
||||
_contentManager = contentManager;
|
||||
_workContextAccessor = workContextAccessor;
|
||||
_commentService = commentService;
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
|
||||
public void Describe(dynamic context) {
|
||||
public void Describe(DescribeContext context) {
|
||||
context.For("Content", T("Comments"), T("Comments"))
|
||||
.Token("CommentedOn", T("Commented On"), T("The content item this comment was created on."))
|
||||
.Token("CommentMessage", T("Comment Message"), T("The text of the comment itself"))
|
||||
.Token("CommentAuthor", T("Comment Author"), T("The author of the comment."))
|
||||
.Token("CommentAuthorUrl", T("Comment Author Url"), T("The url provided by the author of the comment."))
|
||||
.Token("CommentAuthorEmail", T("Comment Author Email"), T("The email provided by the author of the comment."))
|
||||
.Token("CommentApproveUrl", T("Comment approval Url"), T("The absolute url to follow in order to approve this comment."))
|
||||
.Token("CommentModerateUrl", T("Comment moderation Url"), T("The absolute url to follow in order to moderate this comment."))
|
||||
.Token("CommentDeleteUrl", T("Comment deletion Url"), T("The absolute url to follow in order to delete this comment."))
|
||||
;
|
||||
}
|
||||
|
||||
public void Evaluate(dynamic context) {
|
||||
public void Evaluate(EvaluateContext context) {
|
||||
context.For<IContent>("Content")
|
||||
.Token("CommentedOn", (Func<IContent, object>)(content => content.As<CommentPart>().CommentedOn))
|
||||
.Chain("CommentedOn", "Content", (Func<IContent, object>)(content => _contentManager.Get(content.As<CommentPart>().CommentedOn)))
|
||||
.Token("CommentMessage", (Func<IContent, object>)(content => content.As<CommentPart>().CommentText))
|
||||
.Token("CommentAuthor", (Func<IContent, object>)CommentAuthor)
|
||||
.Token("CommentAuthorUrl", (Func<IContent, object>)(content => content.As<CommentPart>().SiteName))
|
||||
.Token("CommentAuthorEmail", (Func<IContent, object>)(content => content.As<CommentPart>().Email))
|
||||
.Token("CommentedOn", content => content.As<CommentPart>().CommentedOn)
|
||||
.Chain("CommentedOn", "Content", content => _contentManager.Get(content.As<CommentPart>().CommentedOn))
|
||||
.Token("CommentMessage", content => content.As<CommentPart>().CommentText)
|
||||
.Chain("CommentMessage", "Text", content => content.As<CommentPart>().CommentText)
|
||||
.Token("CommentAuthor", CommentAuthor)
|
||||
.Chain("CommentAuthor", "Text", CommentAuthor)
|
||||
.Token("CommentAuthorUrl", content => content.As<CommentPart>().SiteName)
|
||||
.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>()))
|
||||
;
|
||||
}
|
||||
|
||||
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;
|
||||
|
Reference in New Issue
Block a user