Refactoring Orchard.Comments

--HG--
branch : 1.x
extra : rebase_source : 53cad738eb99ac7e2ac909821d4246c542284e9b
This commit is contained in:
Sebastien Ros
2012-10-31 15:59:06 -07:00
parent 471b949ed9
commit ce305bf0c8
37 changed files with 582 additions and 419 deletions

View File

@@ -19,24 +19,26 @@ namespace Orchard.Comments.Controllers {
using Orchard.Settings;
[ValidateInput(false)]
public class AdminController : Controller {
public class AdminController : Controller, IUpdateModel {
private readonly IOrchardServices _orchardServices;
private readonly ICommentService _commentService;
private readonly ISiteService _siteService;
private readonly IContentManager _contentManager;
public AdminController(
IOrchardServices services,
ICommentService commentService,
IOrchardServices orchardServices,
ICommentService commentService,
ISiteService siteService,
IShapeFactory shapeFactory) {
_orchardServices = orchardServices;
_commentService = commentService;
_siteService = siteService;
Services = services;
_contentManager = _orchardServices.ContentManager;
Logger = NullLogger.Instance;
T = NullLocalizer.Instance;
Shape = shapeFactory;
}
public IOrchardServices Services { get; set; }
public ILogger Logger { get; set; }
public Localizer T { get; set; }
dynamic Shape { get; set; }
@@ -72,7 +74,7 @@ namespace Orchard.Comments.Controllers {
.OrderByDescending<CommentPartRecord>(cpr => cpr.CommentDateUtc)
.Slice(pager.GetStartIndex(), pager.PageSize)
.ToList()
.Select(comment => CreateCommentEntry(comment.Record));
.Select(comment => CreateCommentEntry(comment));
var model = new CommentsIndexViewModel {
Comments = entries.ToList(),
@@ -94,7 +96,7 @@ namespace Orchard.Comments.Controllers {
case CommentIndexBulkAction.None:
break;
case CommentIndexBulkAction.MarkAsSpam:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
return new HttpUnauthorizedResult();
//TODO: Transaction
foreach (CommentEntry entry in checkedEntries) {
@@ -102,7 +104,7 @@ namespace Orchard.Comments.Controllers {
}
break;
case CommentIndexBulkAction.Unapprove:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
return new HttpUnauthorizedResult();
//TODO: Transaction
foreach (CommentEntry entry in checkedEntries) {
@@ -110,7 +112,7 @@ namespace Orchard.Comments.Controllers {
}
break;
case CommentIndexBulkAction.Approve:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
return new HttpUnauthorizedResult();
//TODO: Transaction
foreach (CommentEntry entry in checkedEntries) {
@@ -118,7 +120,7 @@ namespace Orchard.Comments.Controllers {
}
break;
case CommentIndexBulkAction.Delete:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't delete comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't delete comment")))
return new HttpUnauthorizedResult();
foreach (CommentEntry entry in checkedEntries) {
@@ -156,7 +158,7 @@ namespace Orchard.Comments.Controllers {
default:
throw new ArgumentOutOfRangeException();
}
var entries = comments.List().Select(comment => CreateCommentEntry(comment.Record)).ToList();
var entries = comments.List().Select(comment => CreateCommentEntry(comment)).ToList();
var model = new CommentsDetailsViewModel {
Comments = entries,
Options = options,
@@ -178,7 +180,7 @@ namespace Orchard.Comments.Controllers {
case CommentDetailsBulkAction.None:
break;
case CommentDetailsBulkAction.MarkAsSpam:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
return new HttpUnauthorizedResult();
//TODO: Transaction
foreach (CommentEntry entry in checkedEntries) {
@@ -186,7 +188,7 @@ namespace Orchard.Comments.Controllers {
}
break;
case CommentDetailsBulkAction.Unapprove:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
return new HttpUnauthorizedResult();
foreach (CommentEntry entry in checkedEntries) {
@@ -194,7 +196,7 @@ namespace Orchard.Comments.Controllers {
}
break;
case CommentDetailsBulkAction.Approve:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't moderate comment")))
return new HttpUnauthorizedResult();
foreach (CommentEntry entry in checkedEntries) {
@@ -202,7 +204,7 @@ namespace Orchard.Comments.Controllers {
}
break;
case CommentDetailsBulkAction.Delete:
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't delete comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't delete comment")))
return new HttpUnauthorizedResult();
foreach (CommentEntry entry in checkedEntries) {
@@ -219,7 +221,7 @@ namespace Orchard.Comments.Controllers {
[HttpPost]
public ActionResult Disable(int commentedItemId, string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't disable comments")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't disable comments")))
return new HttpUnauthorizedResult();
_commentService.DisableCommentsForCommentedContent(commentedItemId);
@@ -228,47 +230,49 @@ namespace Orchard.Comments.Controllers {
[HttpPost]
public ActionResult Enable(int commentedItemId, string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't enable comments")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't enable comments")))
return new HttpUnauthorizedResult();
_commentService.EnableCommentsForCommentedContent(commentedItemId);
return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));
}
public ActionResult Edit(int id) {
CommentPart commentPart = _commentService.GetComment(id);
var commentPart = _contentManager.Get<CommentPart>(id);
if (commentPart == null)
return new HttpNotFoundResult();
var viewModel = new CommentsEditViewModel {
CommentText = commentPart.Record.CommentText,
Email = commentPart.Record.Email,
Id = commentPart.Record.Id,
Name = commentPart.Record.Author,
SiteName = commentPart.Record.SiteName,
Status = commentPart.Record.Status,
};
return View(viewModel);
dynamic editorShape = _contentManager.BuildEditor(commentPart);
return View(editorShape);
}
[HttpPost]
public ActionResult Edit(FormCollection input) {
var viewModel = new CommentsEditViewModel();
UpdateModel(viewModel);
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't edit comment")))
public ActionResult Edit(int id, FormCollection input) {
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't edit comment")))
return new HttpUnauthorizedResult();
_commentService.UpdateComment(viewModel.Id, viewModel.Name, viewModel.Email, viewModel.SiteName, viewModel.CommentText, viewModel.Status);
var commentPart = _contentManager.Get<CommentPart>(id);
var editorShape = _contentManager.UpdateEditor(commentPart, this);
if (!ModelState.IsValid) {
foreach (var error in ModelState.Values.SelectMany(m => m.Errors).Select(e => e.ErrorMessage)) {
_orchardServices.Notifier.Error(T(error));
}
TempData["Comments.InvalidCommentEditorShape"] = editorShape;
}
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Approve(int id, string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't approve comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't approve comment")))
return new HttpUnauthorizedResult();
var commentPart = _commentService.GetComment(id);
var commentPart = _contentManager.Get<CommentPart>(id);
if (commentPart == null)
return new HttpNotFoundResult();
@@ -280,10 +284,10 @@ namespace Orchard.Comments.Controllers {
[HttpPost]
public ActionResult Unapprove(int id, string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't unapprove comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't unapprove comment")))
return new HttpUnauthorizedResult();
var commentPart = _commentService.GetComment(id);
var commentPart = _contentManager.Get<CommentPart>(id);
if (commentPart == null)
return new HttpNotFoundResult();
@@ -295,10 +299,10 @@ namespace Orchard.Comments.Controllers {
[HttpPost]
public ActionResult MarkAsSpam(int id, string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't mark comment as spam")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't mark comment as spam")))
return new HttpUnauthorizedResult();
var commentPart = _commentService.GetComment(id);
var commentPart = _contentManager.Get<CommentPart>(id);
if (commentPart == null)
return new HttpNotFoundResult();
@@ -310,10 +314,10 @@ namespace Orchard.Comments.Controllers {
[HttpPost]
public ActionResult Delete(int id, string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't delete comment")))
if (!_orchardServices.Authorizer.Authorize(Permissions.ManageComments, T("Couldn't delete comment")))
return new HttpUnauthorizedResult();
var commentPart = _commentService.GetComment(id);
var commentPart = _contentManager.Get<CommentPart>(id);
if (commentPart == null)
return new HttpNotFoundResult();
@@ -323,10 +327,10 @@ namespace Orchard.Comments.Controllers {
return this.RedirectLocal(returnUrl, () => RedirectToAction("Details", new { id = commentedOn }));
}
private CommentEntry CreateCommentEntry(CommentPartRecord commentPart) {
private CommentEntry CreateCommentEntry(CommentPart item) {
return new CommentEntry {
Comment = commentPart,
CommentedOn = _commentService.GetCommentedContent(commentPart.CommentedOn),
Comment = item.Record,
Shape = _contentManager.BuildDisplay(item, "SummaryAdmin"),
IsChecked = false,
};
}
@@ -343,5 +347,13 @@ namespace Orchard.Comments.Controllers {
return !string.IsNullOrEmpty(value);
}
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
}
}

View File

@@ -10,69 +10,65 @@ using Orchard.Mvc.Extensions;
using Orchard.UI.Notify;
namespace Orchard.Comments.Controllers {
public class CommentController : Controller {
public class CommentController : Controller, IUpdateModel {
public IOrchardServices Services { get; set; }
private readonly ICommentService _commentService;
private readonly INotifier _notifier;
public Localizer T { get; set; }
public CommentController(IOrchardServices services, ICommentService commentService, INotifier notifier) {
Services = services;
_commentService = commentService;
_notifier = notifier;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
[HttpPost, ValidateInput(false)]
public ActionResult Create(string returnUrl) {
if (!Services.Authorizer.Authorize(Permissions.AddComment, T("Couldn't add comment")))
return this.RedirectLocal(returnUrl, "~/");
var viewModel = new CommentsCreateViewModel();
TryUpdateModel(viewModel);
var context = new CreateCommentContext {
Author = viewModel.Name,
CommentText = viewModel.CommentText,
Email = viewModel.Email,
SiteName = viewModel.SiteName,
CommentedOn = viewModel.CommentedOn
};
var comment = Services.ContentManager.New("Comment");
Services.ContentManager.Create(comment);
var editorShape = Services.ContentManager.UpdateEditor(comment, this);
if (ModelState.IsValid) {
if (!String.IsNullOrEmpty(context.SiteName) && !context.SiteName.StartsWith("http://") && !context.SiteName.StartsWith("https://")) {
context.SiteName = "http://" + context.SiteName;
}
if (comment.Has<CommentPart>()) {
var commentPart = comment.As<CommentPart>();
CommentPart commentPart = _commentService.CreateComment(context, Services.WorkContext.CurrentSite.As<CommentSettingsPart>().Record.ModerateComments);
if (commentPart.Record.Status == CommentStatus.Pending) {
// if the user who submitted the comment has the right to moderate, don't make this comment moderated
if (Services.Authorizer.Authorize(Permissions.ManageComments)) {
commentPart.Record.Status = CommentStatus.Approved;
}
else {
Services.Notifier.Information(T("Your comment will appear after the site administrator approves it."));
if (commentPart.Status == CommentStatus.Pending) {
// if the user who submitted the comment has the right to moderate, don't make this comment moderated
if (Services.Authorizer.Authorize(Permissions.ManageComments)) {
commentPart.Status = CommentStatus.Approved;
}
else {
Services.Notifier.Information(T("Your comment will appear after the site administrator approves it."));
}
}
}
}
else {
foreach (var error in ModelState.Values.SelectMany(m => m.Errors).Select( e=> e.ErrorMessage)) {
Services.TransactionManager.Cancel();
foreach (var error in ModelState.Values.SelectMany(m => m.Errors).Select(e => e.ErrorMessage)) {
_notifier.Error(T(error));
}
}
if(!ModelState.IsValid) {
TempData["CreateCommentContext.Name"] = context.Author;
TempData["CreateCommentContext.CommentText"] = context.CommentText;
TempData["CreateCommentContext.Email"] = context.Email;
TempData["CreateCommentContext.SiteName"] = context.SiteName;
TempData["Comments.InvalidCommentEditorShape"] = editorShape;
}
return this.RedirectLocal(returnUrl, "~/");
}
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
}
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
ModelState.AddModelError(key, errorMessage.ToString());
}
}
}

View File

@@ -4,15 +4,84 @@ using JetBrains.Annotations;
using Orchard.Comments.Models;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Aspects;
using Orchard.Services;
using Orchard.Localization;
using Orchard.Comments.Services;
namespace Orchard.Comments.Drivers {
[UsedImplicitly]
public class CommentPartDriver : ContentPartDriver<CommentPart> {
private readonly IContentManager _contentManager;
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IClock _clock;
private readonly ICommentService _commentService;
protected override string Prefix { get { return "Comments"; } }
public CommentPartDriver(IContentManager contentManager) {
public Localizer T { get; set; }
public CommentPartDriver(
IContentManager contentManager,
IWorkContextAccessor workContextAccessor,
IClock clock,
ICommentService commentService) {
_contentManager = contentManager;
_workContextAccessor = workContextAccessor;
_clock = clock;
_commentService = commentService;
T = NullLocalizer.Instance;
}
protected override DriverResult Display(CommentPart part, string displayType, dynamic shapeHelper) {
return Combined(
ContentShape("Parts_Comment", () => shapeHelper.Parts_Comment()),
ContentShape("Parts_Comment_SummaryAdmin", () => shapeHelper.Parts_Comment_SummaryAdmin())
);
}
// GET
protected override DriverResult Editor(CommentPart part, dynamic shapeHelper) {
if (Orchard.UI.Admin.AdminFilter.IsApplied(_workContextAccessor.GetContext().HttpContext.Request.RequestContext)) {
return ContentShape("Parts_Comment_AdminEdit",
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Comment.AdminEdit", Model: part, Prefix: Prefix));
}
else {
return ContentShape("Parts_Comment_Edit",
() => shapeHelper.EditorTemplate(TemplateName: "Parts.Comment", Model: part, Prefix: Prefix));
}
}
// POST
protected override DriverResult Editor(CommentPart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
var workContext = _workContextAccessor.GetContext();
part.CommentDateUtc = _clock.UtcNow;
if (!String.IsNullOrEmpty(part.SiteName) && !part.SiteName.StartsWith("http://") && !part.SiteName.StartsWith("https://")) {
part.SiteName = "http://" + part.SiteName;
}
// TODO: it's very bad how the corresponding user is stored. Needs revision.
var currentUser = workContext.CurrentUser;
part.UserName = (currentUser != null ? currentUser.UserName : null);
if (currentUser != null) part.Author = currentUser.UserName;
if (String.IsNullOrEmpty(part.Author)) updater.AddModelError("NameMissing", T("You didn't specify your name."));
// TODO: needs spam handling
part.Status = workContext.CurrentSite.As<CommentSettingsPart>().ModerateComments ? CommentStatus.Pending : CommentStatus.Approved;
var commentedOn = _contentManager.Get<ICommonPart>(part.CommentedOn);
if (commentedOn != null && commentedOn.Container != null) {
part.CommentedOnContainer = commentedOn.Container.ContentItem.Id;
}
commentedOn.As<CommentsPart>().Record.CommentPartRecords.Add(part.Record);
return Editor(part, shapeHelper);
}
protected override void Importing(CommentPart part, ContentManagement.Handlers.ImportContentContext context) {
@@ -38,7 +107,7 @@ namespace Orchard.Comments.Drivers {
var status = context.Attribute(part.PartDefinition.Name, "Status");
if (status != null) {
part.Record.Status = (CommentStatus) Enum.Parse(typeof(CommentStatus), status);
part.Record.Status = (CommentStatus)Enum.Parse(typeof(CommentStatus), status);
}
var commentDate = context.Attribute(part.PartDefinition.Name, "CommentDateUtc");

View File

@@ -20,7 +20,6 @@ namespace Orchard.Comments.Drivers {
Func<int> approvedCount = () => commentsForCommentedContent.Where(x => x.Status == CommentStatus.Approved).Count();
return Combined(
ContentShape("Parts_Comments_Count",
() => shapeHelper.Parts_Comments_Count(CommentCount: approvedCount(), PendingCount: pendingCount())),
ContentShape("Parts_Comments_Count_SummaryAdmin",

View File

@@ -4,14 +4,19 @@ using Orchard.Comments.Models;
using Orchard.Comments.Services;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using System.Collections.Generic;
namespace Orchard.Comments.Drivers {
[UsedImplicitly]
public class CommentsPartDriver : ContentPartDriver<CommentsPart> {
private readonly ICommentService _commentService;
public CommentsPartDriver(ICommentService commentService) {
private readonly IContentManager _contentManager;
public CommentsPartDriver(
ICommentService commentService,
IContentManager contentManager) {
_commentService = commentService;
_contentManager = contentManager;
}
protected override DriverResult Display(CommentsPart part, string displayType, dynamic shapeHelper) {
@@ -23,10 +28,25 @@ namespace Orchard.Comments.Drivers {
Func<int> approvedCount = () => commentsForCommentedContent.Where(x => x.Status == CommentStatus.Approved).Count();
return Combined(
ContentShape("Parts_Comments",
() => shapeHelper.Parts_Comments()),
ContentShape("Parts_ListOfComments",
() => {
var commentShapes = new List<dynamic>();
foreach (var item in part.Comments) {
commentShapes.Add(_contentManager.BuildDisplay(item.ContentItem, "Summary"));
}
return shapeHelper.Parts_ListOfComments(CommentShapes: commentShapes);
}),
ContentShape("Parts_CommentForm",
() => {
var newComment = _contentManager.New("Comment");
if (newComment.Has<CommentPart>()) newComment.As<CommentPart>().CommentedOn = part.Id;
var editorShape = _contentManager.BuildEditor(newComment);
return shapeHelper.Parts_CommentForm(EditorShape: editorShape);
}),
ContentShape("Parts_Comments_Count",
() => shapeHelper.Parts_Comments_Count(CommentCount: approvedCount(), PendingCount: pendingCount())),
() => shapeHelper.Parts_Comments_Count(CommentCount: approvedCount(), PendingCount: pendingCount())),
ContentShape("Parts_Comments_Count_SummaryAdmin",
() => shapeHelper.Parts_Comments_Count_SummaryAdmin(CommentCount: approvedCount(), PendingCount: pendingCount()))
);

View File

@@ -2,12 +2,29 @@ using JetBrains.Annotations;
using Orchard.Comments.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using Orchard.ContentManagement;
namespace Orchard.Comments.Handlers {
[UsedImplicitly]
public class CommentPartHandler : ContentHandler {
public CommentPartHandler(IRepository<CommentPartRecord> commentsRepository) {
public CommentPartHandler(
IRepository<CommentPartRecord> commentsRepository,
IContentManager contentManager) {
Filters.Add(StorageFilter.For(commentsRepository));
OnLoading<CommentPart>((context, comment) => {
comment.CommentedOnContentItemField.Loader(
item => contentManager.Get(comment.CommentedOn)
);
comment.CommentedOnContentItemMetadataField.Loader(
item => contentManager.GetItemMetadata(comment.CommentedOnContentItem)
);
});
OnIndexing<CommentPart>((context, commentPart) => context.DocumentIndex
.Add("commentText", commentPart.Record.CommentText));
}
}
}

View File

@@ -17,19 +17,19 @@ namespace Orchard.Comments.Handlers {
Filters.Add(StorageFilter.For(commentsRepository));
OnInitializing<CommentsPart>((ctx, x) => {
x.CommentsActive = true;
x.CommentsShown = true;
x.Comments = new List<CommentPart>();
OnInitializing<CommentsPart>((ctx, part) => {
part.CommentsActive = true;
part.CommentsShown = true;
part.Comments = new List<CommentPart>();
});
OnLoading<CommentsPart>((context, comments) => {
comments._comments.Loader(list => contentManager
comments.CommentsField.Loader(list => contentManager
.Query<CommentPart, CommentPartRecord>()
.Where(x => x.CommentsPartRecord == context.ContentItem.As<CommentsPart>().Record && x.Status == CommentStatus.Approved)
.List().ToList());
comments._pendingComments.Loader(list => contentManager
comments.PendingCommentsField.Loader(list => contentManager
.Query<CommentPart, CommentPartRecord>()
.Where(x => x.CommentsPartRecord == context.ContentItem.As<CommentsPart>().Record && x.Status == CommentStatus.Pending)
.List().ToList());

View File

@@ -33,8 +33,6 @@ namespace Orchard.Comments {
.ContentPartRecord()
.Column<bool>("ModerateComments")
.Column<bool>("EnableSpamProtection")
.Column<string>("AkismetKey")
.Column<string>("AkismetUrl")
);
SchemaBuilder.CreateTable("CommentsPartRecord", table => table
@@ -46,7 +44,10 @@ namespace Orchard.Comments {
ContentDefinitionManager.AlterTypeDefinition("Comment",
cfg => cfg
.WithPart("CommentPart")
.WithPart("CommonPart")
.WithPart("CommonPart",
p => p
.WithSetting("OwnerEditorSettings.ShowOwnerEditor", "false")
.WithSetting("DateEditorSettings.ShowDateEditor", "false"))
.WithPart("IdentityPart")
);
@@ -84,5 +85,25 @@ namespace Orchard.Comments {
return 3;
}
public int UpdateFrom3() {
ContentDefinitionManager.AlterTypeDefinition("Comment",
cfg => cfg
.WithPart("CommonPart",
p => p
.WithSetting("OwnerEditorSettings.ShowOwnerEditor", "false")
.WithSetting("DateEditorSettings.ShowDateEditor", "false"))
);
SchemaBuilder.AlterTable("CommentSettingsPartRecord", table => table
.DropColumn("AkismetKey")
);
SchemaBuilder.AlterTable("CommentSettingsPartRecord", table => table
.DropColumn("AkismetUrl")
);
return 4;
}
}
}

View File

@@ -1,6 +1,76 @@
using Orchard.ContentManagement;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using Orchard.ContentManagement.Utilities;
namespace Orchard.Comments.Models {
public class CommentPart : ContentPart<CommentPartRecord> {
private readonly LazyField<ContentItem> _commentedOnContentItem = new LazyField<ContentItem>();
private readonly LazyField<ContentItemMetadata> _commentedOnContentItemMetadata = new LazyField<ContentItemMetadata>();
public LazyField<ContentItem> CommentedOnContentItemField { get { return _commentedOnContentItem; } }
public LazyField<ContentItemMetadata> CommentedOnContentItemMetadataField { get { return _commentedOnContentItemMetadata; } }
public string Author {
get { return Record.Author; }
set { Record.Author = value; }
}
[StringLength(245)]
[DisplayName("Site")]
[RegularExpression(@"^(http(s)?://)?([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}[\S]+$", ErrorMessage = "Invalid url")]
public string SiteName {
get { return Record.SiteName; }
set { Record.SiteName = value; }
}
public string UserName {
get { return Record.UserName; }
set { Record.UserName = value; }
}
[RegularExpression(@"^[^@\s]+@[^@\s]+$", ErrorMessage = "Invalid Email")]
[StringLength(255)]
public string Email {
get { return Record.Email; }
set { Record.Email = value; }
}
public CommentStatus Status {
get { return Record.Status; }
set { Record.Status = value; }
}
public DateTime? CommentDateUtc {
get { return Record.CommentDateUtc; }
set { Record.CommentDateUtc = value; }
}
[Required, DisplayName("Comment")]
public string CommentText {
get { return Record.CommentText; }
set { Record.CommentText = value; }
}
public int CommentedOn {
get { return Record.CommentedOn; }
set { Record.CommentedOn = value; }
}
public ContentItem CommentedOnContentItem {
get { return _commentedOnContentItem.Value; }
set { _commentedOnContentItem.Value = value; }
}
public ContentItemMetadata CommentedOnContentItemMetadata {
get { return _commentedOnContentItemMetadata.Value; }
set { _commentedOnContentItemMetadata.Value = value; }
}
public int CommentedOnContainer {
get { return Record.CommentedOnContainer; }
set { Record.CommentedOnContainer = value; }
}
}
}

View File

@@ -4,7 +4,5 @@ namespace Orchard.Comments.Models {
public class CommentSettingsPartRecord : ContentPartRecord {
public virtual bool ModerateComments { get; set; }
public virtual bool EnableSpamProtection { get; set; }
public virtual string AkismetKey { get; set; }
public virtual string AkismetUrl { get; set; }
}
}

View File

@@ -4,16 +4,21 @@ using Orchard.ContentManagement.Utilities;
namespace Orchard.Comments.Models {
public class CommentsPart : ContentPart<CommentsPartRecord> {
public CommentsPart() {
Comments = new List<CommentPart>();
PendingComments = new List<CommentPart>();
private readonly LazyField<IList<CommentPart>> _comments = new LazyField<IList<CommentPart>>();
private readonly LazyField<IList<CommentPart>> _pendingComments = new LazyField<IList<CommentPart>>();
public LazyField<IList<CommentPart>> CommentsField { get { return _comments; } }
public LazyField<IList<CommentPart>> PendingCommentsField { get { return _pendingComments; } }
public IList<CommentPart> Comments {
get { return _comments.Value; }
set { _comments.Value = value; }
}
public IList<CommentPart> PendingComments {
get { return _pendingComments.Value; }
set { _pendingComments.Value = value; }
}
public readonly LazyField<IList<CommentPart>> _comments = new LazyField<IList<CommentPart>>();
public readonly LazyField<IList<CommentPart>> _pendingComments = new LazyField<IList<CommentPart>>();
public IList<CommentPart> Comments { get { return _comments.Value; } set { _comments.Value = value; } }
public IList<CommentPart> PendingComments { get { return _pendingComments.Value; } set { _pendingComments.Value = value; } }
public bool CommentsShown {
get { return Record.CommentsShown; }

View File

@@ -21,6 +21,10 @@
</UpgradeBackupLocation>
<TargetFrameworkProfile />
<UseIISExpress>false</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -42,11 +46,6 @@
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Joel.Net.Akismet, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\joel.net.akismet\Joel.Net.Akismet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations">
@@ -97,14 +96,12 @@
<Compile Include="Services\CommentService.cs" />
<Compile Include="Services\CommentValidator.cs" />
<Compile Include="Services\ICommentValidator.cs" />
<Compile Include="ViewModels\CommentsCreateViewModel.cs" />
<Compile Include="ViewModels\CommentsDetailsViewModel.cs" />
<Compile Include="ViewModels\CommentsEditViewModel.cs" />
<Compile Include="ViewModels\CommentsIndexViewModel.cs" />
<Compile Include="ViewModels\EditCommentsViewModel.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Module.txt" />
<Content Include="Release notes.txt" />
<Content Include="Styles\images\menu.comments.png" />
<Content Include="Styles\menu.comments-admin.css" />
<Content Include="Styles\orchard-comments-admin.css" />
@@ -124,12 +121,12 @@
<ItemGroup>
<Content Include="Views\Admin\Edit.cshtml" />
<Content Include="Views\Admin\Index.cshtml" />
<Content Include="Views\Parts.Comments.cshtml" />
<Content Include="Views\Parts.CommentForm.cshtml" />
<Content Include="Views\Parts.Comments.Count.cshtml" />
<Content Include="Views\Parts.Comments.Count.SummaryAdmin.cshtml" />
<Content Include="Views\EditorTemplates\Parts.Comments.Comments.cshtml" />
<Content Include="Views\EditorTemplates\Parts.Comments.SiteSettings.cshtml" />
<Content Include="Views\ListOfComments.cshtml" />
<Content Include="Views\Parts.ListOfComments.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Placement.info">
@@ -150,6 +147,17 @@
<ItemGroup>
<Content Include="Views\CommentMetadata.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Parts.Comment.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Parts.Comment.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Parts.Comment.AdminEdit.cshtml" />
<Content Include="Views\Content-Comment.SummaryAdmin.cshtml" />
<None Include="Views\Parts.Comment.SummaryAdmin.cshtml" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>

View File

@@ -1,21 +1,31 @@
<Placement>
<!-- available display shapes -->
<!--
<!-- available display shapes -->
<!--
Parts_Comments
Parts_Comments_Count
Parts_Comments_Count_SummaryAdmin
-->
<!-- widget and edit shapes just get default placement -->
<!-- edit "shapes" -->
<Place Parts_Comments_Enable="Content:10"/>
<Place Parts_Comments_SiteSettings="Content:10"/>
<Match DisplayType="Detail">
<Place Parts_Comments="Content:10" />
</Match>
<Match DisplayType="Summary">
<Place Parts_Comments_Count="Meta:5" />
</Match>
<Match DisplayType="SummaryAdmin">
<Place Parts_Comments_Count_SummaryAdmin="Meta:4" />
</Match>
<!-- widget and edit shapes just get default placement -->
<!-- edit "shapes" -->
<Place Parts_Comments_Enable="Content:10"/>
<Place Parts_Comments_SiteSettings="Content:10"/>
<Place Parts_Comment_Edit="Content:10"/>
<Place Parts_Comment_AdminEdit="Content:10"/>
<Match ContentType="Comment">
<Place Content_SaveButton="-"/><!--Show default save button only on admin-->
</Match>
<Match DisplayType="Detail">
<Place Parts_ListOfComments="Content:10" />
<Place Parts_CommentForm="Content:10" />
</Match>
<Match DisplayType="Summary">
<Place Parts_Comments_Count="Meta:5"
Parts_Comment="Content:10" />
</Match>
<Match DisplayType="SummaryAdmin">
<Place Parts_Comment_SummaryAdmin="Content:10"
Parts_Comments_Count_SummaryAdmin="Meta:4" />
</Match>
</Placement>

View File

@@ -0,0 +1,13 @@
==== RELEASE NOTES ====
== New features ==
- Comment display and edit shapes are dynamically built, so parts can be attached to comments and placement can be configured (also for admin)
== Breaking changes ==
- Css ids of the comment form's fields have changed
TODO: list old ids and new one for migration steps. Changed because of usage of Html.FieldIdFor(...)
- CommentService (and ICommentService) has multiple breaking changes: CreateComment and UpdateComment was removed, use ContentManager methods instead
- Comments used plenty of static viewmodels; some very completely removed, some changed adapting to comment shape building; if you've overridden any comment
shapes, check them

View File

@@ -32,26 +32,18 @@ namespace Orchard.Comments.Services {
}
public IContentQuery<CommentPart, CommentPartRecord> GetComments(CommentStatus status) {
return _orchardServices.ContentManager
.Query<CommentPart, CommentPartRecord>()
return GetComments()
.Where(c => c.Status == status);
}
public IContentQuery<CommentPart, CommentPartRecord> GetCommentsForCommentedContent(int id) {
return _orchardServices.ContentManager
.Query<CommentPart, CommentPartRecord>()
return GetComments()
.Where(c => c.CommentedOn == id || c.CommentedOnContainer == id);
}
public IContentQuery<CommentPart, CommentPartRecord> GetCommentsForCommentedContent(int id, CommentStatus status) {
return _orchardServices.ContentManager
.Query<CommentPart, CommentPartRecord>()
.Where(c => c.CommentedOn == id || c.CommentedOnContainer == id)
.Where(ctx => ctx.Status == status);
}
public CommentPart GetComment(int id) {
return _orchardServices.ContentManager.Get<CommentPart>(id);
return GetCommentsForCommentedContent(id)
.Where(c => c.Status == status);
}
public ContentItemMetadata GetDisplayForCommentedContent(int id) {
@@ -68,47 +60,18 @@ namespace Orchard.Comments.Services {
return result;
}
public CommentPart CreateComment(CreateCommentContext context, bool moderateComments) {
return _orchardServices.ContentManager.Create<CommentPart>("Comment", comment => {
comment.Record.Author = context.Author;
comment.Record.CommentDateUtc = _clock.UtcNow;
comment.Record.CommentText = context.CommentText;
comment.Record.Email = context.Email;
comment.Record.SiteName = context.SiteName;
comment.Record.UserName = (_orchardServices.WorkContext.CurrentUser != null ? _orchardServices.WorkContext.CurrentUser.UserName : null);
comment.Record.CommentedOn = context.CommentedOn;
comment.Record.Status = _commentValidator.ValidateComment(comment)
? moderateComments ? CommentStatus.Pending : CommentStatus.Approved
: CommentStatus.Spam;
var commentedOn = _orchardServices.ContentManager.Get<ICommonPart>(comment.Record.CommentedOn);
if (commentedOn != null && commentedOn.Container != null) {
comment.Record.CommentedOnContainer = commentedOn.Container.ContentItem.Id;
}
commentedOn.As<CommentsPart>().Record.CommentPartRecords.Add(comment.Record);
});
}
public void UpdateComment(int id, string name, string email, string siteName, string commentText, CommentStatus status) {
CommentPart commentPart = GetComment(id);
commentPart.Record.Author = name;
commentPart.Record.Email = email;
commentPart.Record.SiteName = siteName;
commentPart.Record.CommentText = commentText;
commentPart.Record.Status = status;
}
public void ApproveComment(int commentId) {
CommentPart commentPart = GetComment(commentId);
var commentPart = GetCommentWithQueryHints(commentId);
commentPart.Record.Status = CommentStatus.Approved;
}
public void UnapproveComment(int commentId) {
CommentPart commentPart = GetComment(commentId);
var commentPart = GetCommentWithQueryHints(commentId);
commentPart.Record.Status = CommentStatus.Pending;
}
public void MarkCommentAsSpam(int commentId) {
CommentPart commentPart = GetComment(commentId);
var commentPart = GetCommentWithQueryHints(commentId);
commentPart.Record.Status = CommentStatus.Spam;
}
@@ -127,5 +90,9 @@ namespace Orchard.Comments.Services {
public void EnableCommentsForCommentedContent(int id) {
_orchardServices.ContentManager.Get<CommentsPart>(id, VersionOptions.Latest).CommentsActive = true;
}
private CommentPart GetCommentWithQueryHints(int id) {
return _orchardServices.ContentManager.Get<CommentPart>(id, VersionOptions.Latest, new QueryHints().ExpandParts<CommentPart>());
}
}
}

View File

@@ -7,11 +7,8 @@ namespace Orchard.Comments.Services {
IContentQuery<CommentPart, CommentPartRecord> GetComments(CommentStatus status);
IContentQuery<CommentPart, CommentPartRecord> GetCommentsForCommentedContent(int id);
IContentQuery<CommentPart, CommentPartRecord> GetCommentsForCommentedContent(int id, CommentStatus status);
CommentPart GetComment(int id);
ContentItemMetadata GetDisplayForCommentedContent(int id);
ContentItem GetCommentedContent(int id);
CommentPart CreateComment(CreateCommentContext commentRecord, bool moderateComments);
void UpdateComment(int id, string name, string email, string siteName, string commentText, CommentStatus status);
void ApproveComment(int commentId);
void UnapproveComment(int commentId);
void MarkCommentAsSpam(int commentId);

View File

@@ -1,24 +0,0 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace Orchard.Comments.ViewModels {
public class CommentsCreateViewModel {
[Required]
[StringLength(255)]
public string Name { get; set; }
[RegularExpression(@"^[^@\s]+@[^@\s]+$", ErrorMessage = "Invalid Email")]
[StringLength(255)]
public string Email { get; set; }
[StringLength(245)]
[DisplayName("Site")]
[RegularExpression(@"^(http(s)?://)?([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}[\S]+$", ErrorMessage = "Invalid url")]
public string SiteName { get; set; }
[Required, DisplayName("Comment")]
public string CommentText { get; set; }
public int CommentedOn { get; set; }
}
}

View File

@@ -1,12 +0,0 @@
using Orchard.Comments.Models;
namespace Orchard.Comments.ViewModels {
public class CommentsEditViewModel {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string SiteName { get; set; }
public string CommentText { get; set; }
public CommentStatus Status { get; set; }
}
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Web.Mvc;
using Orchard.Comments.Models;
using Orchard.ContentManagement;
namespace Orchard.Comments.ViewModels {
public class CommentsIndexViewModel {
@@ -9,9 +9,10 @@ namespace Orchard.Comments.ViewModels {
public dynamic Pager { get; set; }
}
[Bind(Exclude = "Shape")]
public class CommentEntry {
public CommentPartRecord Comment { get; set; }
public ContentItem CommentedOn { get; set; }
public dynamic Shape { get; set; }
public bool IsChecked { get; set; }
}

View File

@@ -1,4 +0,0 @@
namespace Orchard.Comments.ViewModels {
public class EditCommentsViewModel {
}
}

View File

@@ -1,46 +1,8 @@
@model Orchard.Comments.ViewModels.CommentsEditViewModel
@using Orchard.Comments.Models;
@using Orchard.Comments.Models;
@{ Layout.Title = T("Edit Comment").ToString(); }
@using(Html.BeginFormAntiForgeryPost()) {
@Html.ValidationSummary()
<fieldset class="who">
<div>
<label for="Name">@T("Name")</label>
<input id="Name" class="text" name="Name" type="text" value="@Model.Name" />
</div>
<div>
<label for="Email">@T("Email")</label>
<input id="Email" class="text" name="Email" type="text" value="@Model.Email" />
</div>
<div>
<label for="SiteName">@T("Url")</label>
<input id="SiteName" class="text" name="SiteName" type="text" value="@Model.SiteName" />
</div>
</fieldset>
<fieldset class="what">
<div>
<label for="CommentText">@T("Body")</label>
<textarea id="CommentText" rows="10" cols="30" name="CommentText">@Model.CommentText</textarea>
<input id="CommentId" name="Id" type="hidden" value="@Model.Id" />
</div>
</fieldset>
<fieldset>
<div>
@Html.RadioButton("Status", "Pending", (Model.Status == CommentStatus.Pending), new { id = "Status_Pending" })
<label class="forcheckbox" for="Status_Pending">@T("Pending")</label>
</div>
<div>
@Html.RadioButton("Status", "Approved", (Model.Status == CommentStatus.Approved), new { id = "Status_Approved" })
<label class="forcheckbox" for="Status_Approved">@T("Approved")</label>
</div>
<div>
@Html.RadioButton("Status", "Spam", (Model.Status == CommentStatus.Spam), new { id = "Status_Spam" })
<label class="forcheckbox" for="Status_Spam">@T("Mark as spam")</label>
</div>
</fieldset>
<fieldset>
<button class="primaryAction" type="submit">@T("Save")</button>
</fieldset>
@Display(Model);
}

View File

@@ -3,6 +3,7 @@
@using Orchard.Comments.ViewModels;
@using Orchard.Mvc.Html;
@using Orchard.Utility.Extensions;
@{
Style.Require("Admin");
Script.Require("ShapesBase");
@@ -64,45 +65,7 @@
<input type="hidden" value="@Model.Comments[commentIndex].Comment.Id" name="@Html.NameOf(m => m.Comments[commentIndex].Comment.Id)"/>
<input type="checkbox" value="true" name="@Html.NameOf(m => m.Comments[commentIndex].IsChecked)"/>
</td>
<td>
@if (commentEntry.Comment.Status == CommentStatus.Spam) { @T("Spam") }
else if (commentEntry.Comment.Status == CommentStatus.Pending) { @T("Pending") }
else { @T("Approved") }
</td>
<td>
<div>@commentEntry.Comment.Author</div>
@if (HasText(commentEntry.Comment.UserName) && commentEntry.Comment.Author != commentEntry.Comment.UserName) {
<div class="authenticated-commenter-id">@commentEntry.Comment.UserName</div>
}
</td>
<td>
@* would ideally have permalinks for individual comments *@
<p><a href="@Url.ItemDisplayUrl(commentEntry.CommentedOn)#comments"><time>@Display.DateTime(DateTimeUtc: commentEntry.Comment.CommentDateUtc.GetValueOrDefault())</time></a></p>
@if (commentEntry.Comment.CommentText != null) {
var ellipsized = Html.Ellipsize(commentEntry.Comment.CommentText, 500);
var paragraphed = new HtmlString(ellipsized.ToHtmlString().Replace("\r\n", "</p><p>"));
<p>@paragraphed</p>
}
else {
@T("[Empty]")
}
</td>
<td>@Html.ItemDisplayLink(commentEntry.CommentedOn)</td>
<td>
<div class="actions">
@if (commentEntry.Comment.Status != CommentStatus.Spam) {
<a href="@Url.Action("MarkAsSpam", new {commentEntry.Comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString()})" itemprop="RemoveUrl UnsafeUrl">@T("Spam")</a>@T(" | ")
}
@if (commentEntry.Comment.Status != CommentStatus.Approved) {
<a href="@Url.Action("Approve", new {commentEntry.Comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString()})" itemprop="ApproveUrl UnsafeUrl">@T("Approve")</a>@T(" | ")
}
else {
<a href="@Url.Action("Unapprove", new {commentEntry.Comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString()})" itemprop="UnapproveUrl UnsafeUrl">@T("Unapprove")</a>@T(" | ")
}
<a href="@Url.Action("Edit", new {commentEntry.Comment.Id})" title="@T("Edit")">@T("Edit")</a>@T(" | ")
<a href="@Url.Action("Delete", new {commentEntry.Comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString()})" itemprop="RemoveUrl UnsafeUrl">@T("Delete")</a>
</div>
</td>
@Display(commentEntry.Shape)
</tr>
commentIndex = commentIndex + 1;
}

View File

@@ -1,13 +1,5 @@
@*
Model:
ContentPart
*@
@using Orchard.Comments.Models;
@using Orchard.Utility.Extensions;
@{
@{
var comment = (Orchard.Comments.Models.CommentPart)Model.ContentPart;
}
@Html.LinkOrDefault(comment.Record.Author, comment.Record.SiteName, new { rel = "nofollow" })
@Html.LinkOrDefault(comment.Author, comment.SiteName, new { rel = "nofollow" })

View File

@@ -1,12 +1,5 @@
@*
Model:
ContentPart
*@
@using Orchard.Comments.Models;
@using Orchard.Utility.Extensions;
@{
@{
var comment = (Orchard.Comments.Models.CommentPart)Model.ContentPart;
}
@T("said") <time datetime="@comment.Record.CommentDateUtc.GetValueOrDefault()">@Html.Link((string)Display.DateTimeRelative(dateTimeUtc: comment.Record.CommentDateUtc.GetValueOrDefault()).ToString(), "#comment-" + comment.Id)</time>
@T("said") <time datetime="@comment.CommentDateUtc.GetValueOrDefault()">@Html.Link((string)Display.DateTimeRelative(dateTimeUtc: comment.CommentDateUtc.GetValueOrDefault()).ToString(), @Url.RouteUrl(comment.CommentedOnContentItemMetadata.DisplayRouteValues) + "#comment-" + comment.Id)</time>

View File

@@ -0,0 +1,3 @@
@if (Model.Content != null) {
@Display(Model.Content)
}

View File

@@ -0,0 +1,44 @@
@model Orchard.Comments.Models.CommentPart
@using Orchard.Comments.Models;
<fieldset class="who">
<ol>
<li>
@Html.LabelFor(m => m.Author, T("Name"))
@Html.TextBoxFor(m => m.Author)
</li>
<li>
@Html.LabelFor(m => m.Email, T("Email"))
@Html.TextBoxFor(m => m.Email)
</li>
<li>
@Html.LabelFor(m => m.SiteName, T("Url"))
@Html.TextBoxFor(m => m.SiteName)
</li>
</ol>
</fieldset>
<fieldset class="what">
<div>
@Html.LabelFor(m => m.CommentText, T("Comment"))
@Html.TextAreaFor(m => m.CommentText, new { rows = 10, cols = 30, @class = "comment-text" })
@Html.HiddenFor(m => m.Id)
</div>
</fieldset>
<fieldset>
<div>
@Html.RadioButtonFor(m => m.Status, CommentStatus.Pending)
<label class="forcheckbox" for="Status_Pending">@T("Pending")</label>
</div>
<div>
@Html.RadioButtonFor(m => m.Status, CommentStatus.Approved)
<label class="forcheckbox" for="Status_Approved">@T("Approved")</label>
</div>
<div>
@Html.RadioButtonFor(m => m.Status, CommentStatus.Spam)
<label class="forcheckbox" for="Status_Spam">@T("Mark as spam")</label>
</div>
</fieldset>
<fieldset>
<button class="primaryAction" type="submit">@T("Save")</button>
</fieldset>

View File

@@ -0,0 +1,40 @@
@model Orchard.Comments.Models.CommentPart
@if (WorkContext.CurrentUser == null) {
<fieldset class="who">
<legend id="add-comment">@T("Add a Comment")</legend>
<ol>
<li>
@Html.LabelFor(m => m.Author, T("Name"))
@Html.TextBoxFor(m => m.Author)
</li>
<li>
@Html.LabelFor(m => m.Email, T("Email"))
@Html.TextBoxFor(m => m.Email)
</li>
<li>
@Html.LabelFor(m => m.SiteName, T("Url"))
@Html.TextBoxFor(m => m.SiteName)
</li>
</ol>
</fieldset>
}
else {
@Html.Hidden("Name", WorkContext.CurrentUser.UserName ?? "")
@Html.Hidden("Email", WorkContext.CurrentUser.Email ?? "")
}
@if (WorkContext.CurrentUser != null) { <h2 id="commenter">@if (WorkContext.CurrentUser != null) { @T("Hi, {0}!", Convert.ToString(Html.ItemDisplayText(WorkContext.CurrentUser)))}</h2> }
<fieldset class="what">
<ol>
<li>
@Html.LabelFor(m => m.CommentText, T("Comment"))
@Html.TextAreaFor(m => m.CommentText, new { rows = 10, cols = 30, @class = "comment-text" })
</li>
<li>
<button class="primaryAction" type="submit">@T("Submit Comment")</button>
@Html.Hidden("CommentedOn", Model.CommentedOn)
</li>
</ol>
</fieldset>

View File

@@ -1,5 +1,4 @@
@model Orchard.Comments.Models.CommentsPart
@using Orchard.Comments.Models;
@using Orchard.Localization;
<fieldset>

View File

@@ -1,5 +1,4 @@
@model Orchard.Comments.Models.CommentSettingsPartRecord
@using Orchard.Comments.Models;
<fieldset>
<legend>@T("Comments")</legend>

View File

@@ -1,19 +0,0 @@
@model IEnumerable<Orchard.Comments.Models.CommentPart>
@using Orchard.Comments.Models;
@using Orchard.Utility.Extensions;
<ul class="comments">
@foreach (var comment in Model) {
<li>
<article class="comment" id="comment-@comment.Id">
<header>
<h4>
<span class="who">@Display.CommentAuthor(ContentPart: comment)</span>
<span class="when">@Display.CommentMetadata(ContentPart: comment)</span>
</h4>
</header>
<p class="text">@(new MvcHtmlString(Html.Encode(comment.Record.CommentText).Replace("\r\n", "<br />\r\n")))</p>
</article>
</li>
}
</ul>

View File

@@ -0,0 +1,46 @@
@using Orchard.Comments.Models;
@using Orchard.Utility.Extensions;
@{
var comment = (Orchard.Comments.Models.CommentPart)Model.ContentPart;
}
<td>
@if (comment.Status == CommentStatus.Spam) { @T("Spam") }
else if (comment.Status == CommentStatus.Pending) { @T("Pending") }
else { @T("Approved") }
</td>
<td>
<div>@comment.Author</div>
@if (HasText(comment.UserName) && comment.Author != comment.UserName) {
<div class="authenticated-commenter-id">@comment.UserName</div>
}
</td>
<td>
@* would ideally have permalinks for individual comments *@
<p><a href="@Url.RouteUrl(comment.CommentedOnContentItemMetadata.DisplayRouteValues)#comments"><time>@Display.DateTime(DateTimeUtc: comment.CommentDateUtc.GetValueOrDefault())</time></a></p>
@if (comment.CommentText != null) {
var ellipsized = Html.Ellipsize(comment.CommentText, 500);
var paragraphed = new HtmlString(ellipsized.ToHtmlString().Replace("\r\n", "</p><p>"));
<p>@paragraphed</p>
}
else {
@T("[Empty]")
}
</td>
<td>@Html.Link(comment.CommentedOnContentItemMetadata.DisplayText, @Url.RouteUrl(comment.CommentedOnContentItemMetadata.DisplayRouteValues))</td>
<td>
<div class="actions">
@if (comment.Status != CommentStatus.Spam) {
<a href="@Url.Action("MarkAsSpam", new { comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString() })" itemprop="RemoveUrl UnsafeUrl">@T("Spam")</a>@T(" | ")
}
@if (comment.Status != CommentStatus.Approved) {
<a href="@Url.Action("Approve", new { comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString() })" itemprop="ApproveUrl UnsafeUrl">@T("Approve")</a>@T(" | ")
}
else {
<a href="@Url.Action("Unapprove", new { comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString() })" itemprop="UnapproveUrl UnsafeUrl">@T("Unapprove")</a>@T(" | ")
}
<a href="@Url.Action("Edit", new { comment.Id })" title="@T("Edit")">@T("Edit")</a>@T(" | ")
<a href="@Url.Action("Delete", new { comment.Id, returnUrl = ViewContext.RequestContext.HttpContext.Request.ToUrlString() })" itemprop="RemoveUrl UnsafeUrl">@T("Delete")</a>
</div>
</td>

View File

@@ -0,0 +1,13 @@
@{
var comment = (Orchard.Comments.Models.CommentPart)Model.ContentPart;
}
@using Orchard.Utility.Extensions;
<header>
<h4>
<span class="who">@Display.CommentAuthor(ContentPart: comment)</span>
<span class="when">@Display.CommentMetadata(ContentPart: comment)</span>
</h4>
</header>
<p class="text">@(new MvcHtmlString(Html.Encode(comment.Record.CommentText).Replace("\r\n", "<br />\r\n")))</p>

View File

@@ -0,0 +1,26 @@
@using Orchard.Comments;
@using Orchard.Utility.Extensions;
@if (!Model.ContentPart.CommentsActive) {
if (Model.ContentPart.Comments.Count > 0) {
<div id="comments">
<p class="comment-disabled">@T("Comments have been disabled for this content.")</p>
</div>
}
}
else if (WorkContext.CurrentUser == null && !AuthorizedFor(Permissions.AddComment)) {
<h2 id="add-comment">@T("Add a Comment")</h2>
<p class="info message">@T("You must {0} to comment.", Html.ActionLink(T("log on").ToString(), "LogOn",
new { Controller = "Account", Area = "Orchard.Users", ReturnUrl = string.Format("{0}#addacomment", Context.Request.RawUrl) }))</p>
}
else {
@Html.ValidationSummary()
using (Html.BeginFormAntiForgeryPost(Url.Action("Create", "Comment", new { Area = "Orchard.Comments", ReturnUrl = Context.Request.ToUrlString() }), FormMethod.Post, new { @class = "comment-form" })) {
if (TempData.ContainsKey("Comments.InvalidCommentEditorShape")) {
@Display(TempData["Comments.InvalidCommentEditorShape"]);
}
else {
@Display(Model.EditorShape)
}
}
}

View File

@@ -1,6 +1,6 @@
<ul class="pageStatus">
<li>
@Display.CommentSummaryLinks(item: Model.ContentPart.ContentItem, count: Model.CommentCount, pendingCount: Model.PendingCount)
@T(" | ")&nbsp;
</li>
<li>
@Display.CommentSummaryLinks(item: Model.ContentPart.ContentItem, count: Model.CommentCount, pendingCount: Model.PendingCount)
@T(" | ")&nbsp;
</li>
</ul>

View File

@@ -1,2 +1 @@
@using Orchard.Comments.ViewModels;
<span class="commentcount">@T.Plural("1 Comment", "{0} Comments", (int)Model.CommentCount)</span>
<span class="commentcount">@T.Plural("1 Comment", "{0} Comments", (int)Model.CommentCount)</span>

View File

@@ -1,74 +0,0 @@
@using Orchard.Comments.Models;
@using Orchard.Comments;
@using Orchard.Security;
@using Orchard.Utility.Extensions;
@{
var contextExists = TempData["CreateCommentContext.Name"] != null;
var name = Convert.ToString(TempData["CreateCommentContext.Name"]);
var commentText = Convert.ToString(TempData["CreateCommentContext.CommentText"]);
var email = Convert.ToString(TempData["CreateCommentContext.Email"]);
var siteName = Convert.ToString(TempData["CreateCommentContext.SiteName"]);
}
@if (Model.ContentPart.Comments.Count > 0) {
<div id="comments">
<h2 class="comment-count">@T.Plural("1 Comment", "{0} Comments", (int)Model.ContentPart.Comments.Count)</h2>
@{Html.RenderPartial("ListOfComments", (IEnumerable<Orchard.Comments.Models.CommentPart>)Model.ContentPart.Comments);}
</div>
}
@if (Model.ContentPart.CommentsActive == false) {
if (Model.ContentPart.Comments.Count > 0) {
<div id="comments">
<p class="comment-disabled">@T("Comments have been disabled for this content.")</p>
</div>
}
}
else if (WorkContext.CurrentUser == null && !AuthorizedFor(Permissions.AddComment)) {
<h2 id="add-comment">@T("Add a Comment")</h2>
<p class="info message">@T("You must {0} to comment.", Html.ActionLink(T("log on").ToString(), "LogOn", new { Controller = "Account", Area = "Orchard.Users", ReturnUrl = string.Format("{0}#addacomment", Context.Request.RawUrl) }))</p>
} else {
using (Html.BeginForm("Create", "Comment", new { area = "Orchard.Comments" }, FormMethod.Post, new { @class = "comment-form" })) {
@Html.ValidationSummary()
if (WorkContext.CurrentUser == null) {
<fieldset class="who">
<legend id="add-comment">@T("Add a Comment")</legend>
<ol>
<li>
<label for="Name">@T("Name")</label>
<input id="Name" class="text" name="Name" type="text" value="@(contextExists ? name : String.Empty)" />
</li>
<li>
<label for="Email">@T("Email")</label>
<input id="Email" class="text" name="Email" type="text" value="@(contextExists ? email : String.Empty)"/>
</li>
<li>
<label for="SiteName">@T("Url")</label>
<input id="SiteName" class="text" name="SiteName" type="text" value="@(contextExists ? siteName : String.Empty)"/>
</li>
</ol>
</fieldset>
} else {
@Html.Hidden("Name", WorkContext.CurrentUser.UserName ?? "")
@Html.Hidden("Email", WorkContext.CurrentUser.Email ?? "")
}
<h2 id="commenter">@if (WorkContext.CurrentUser != null) { @T("Hi, {0}!", Convert.ToString(Html.ItemDisplayText(WorkContext.CurrentUser)))}</h2>
<fieldset class="what">
<ol>
<li>
<label for="comment-text">@T("Comment")</label>
<textarea id="comment-text" rows="10" cols="30" name="CommentText">@(contextExists ? commentText : String.Empty)</textarea>
</li>
<li>
<button class="primaryAction" type="submit">@T("Submit Comment")</button>
@Html.Hidden("CommentedOn", (int)Model.ContentPart.ContentItem.Id)
@Html.Hidden("ReturnUrl", Context.Request.ToUrlString())
@Html.AntiForgeryTokenOrchard()
</li>
</ol>
</fieldset>
}
}

View File

@@ -0,0 +1,14 @@
@if (Model.ContentPart.Comments.Count > 0) {
<div id="comments">
<h2 class="comment-count">@T.Plural("1 Comment", "{0} Comments", (int)Model.ContentPart.Comments.Count)</h2>
<ul class="comments">
@foreach (var comment in Model.CommentShapes) {
<li>
<article class="comment" id="comment-@comment.ContentItem.Id">
@Display(comment)
</article>
</li>
}
</ul>
</div>
}