Some more blog and commenting cleanup.

--HG--
extra : convert_revision : svn%3A5ff7c347-ad56-4c35-b696-ccb81de16e03/trunk%4042880
This commit is contained in:
ErikPorter
2009-12-02 00:50:38 +00:00
parent 571bf27a21
commit 1ea0489ba1
12 changed files with 193 additions and 91 deletions

View File

@@ -1,5 +1,6 @@
using System.Web.Mvc;
using Orchard.Blogs.Models;
using Orchard.Mvc.Html;
namespace Orchard.Blogs.Extensions {
public static class HtmlHelperExtensions {
@@ -8,8 +9,7 @@ namespace Orchard.Blogs.Extensions {
}
public static string Published(this HtmlHelper htmlHelper, BlogPost blogPost) {
//TODO: (erikpo) Relative time instead would be nice.
return blogPost.Published.HasValue ? blogPost.Published.Value.ToString("{0:M d yyyy h:mm tt}") : "Draft";
return htmlHelper.DateTime(blogPost.Published, "as a Draft");
}
}
}

View File

@@ -10,7 +10,7 @@
<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<h1><%=Html.Encode(Model.Post.Title) %></h1>
<div class="metadata">
<div class="posted">Posted by <%=Html.Encode(Model.Post.Creator.UserName) %> <%=Model.Post.Published.HasValue ? "on" : "as a" %> <%=Html.Published(Model.Post) %></div>
<div class="posted">Posted by <%=Html.Encode(Model.Post.Creator.UserName) %> <%=Html.Published(Model.Post) %></div>
<div><a href="<%=Url.BlogPostEdit(Model.Blog.Slug, Model.Post.Slug) %>">(edit)</a></div>
</div>

View File

@@ -1,34 +1,6 @@
using Orchard.Data;
using Orchard.Models;
using Orchard.Models.Driver;
using Orchard.Models.Records;
using Orchard.Models;
namespace Orchard.Comments.Models {
public class CommentSettings : ContentPart<CommentSettingsRecord> {
}
public class CommentSettingsRecord : ContentPartRecord {
public virtual bool RequireLoginToAddComment { get; set; }
public virtual bool EnableCommentsOnPages { get; set; }
public virtual bool EnableCommentsOnPosts { get; set; }
public virtual bool EnableSpamProtection { get; set; }
public virtual string AkismetKey { get; set; }
public virtual string AkismetUrl { get; set; }
}
public class CommentSettingsProvider : ContentProvider {
private readonly IRepository<CommentSettingsRecord> _commentSettingsRepository;
public CommentSettingsProvider(IRepository<CommentSettingsRecord> repository) {
_commentSettingsRepository = repository;
Filters.Add(new ActivatingFilter<CommentSettings>("site"));
Filters.Add(new StorageFilter<CommentSettingsRecord>(_commentSettingsRepository) { AutomaticallyCreateMissingRecord = true });
Filters.Add(new TemplateFilterForRecord<CommentSettingsRecord>("CommentSettings"));
OnActivated<CommentSettings>(DefaultSettings);
}
private static void DefaultSettings(ActivatedContentContext context, CommentSettings settings) {
settings.Record.EnableCommentsOnPages = true;
settings.Record.EnableCommentsOnPosts = true;
}
}
}

View File

@@ -0,0 +1,21 @@
using Orchard.Data;
using Orchard.Models.Driver;
namespace Orchard.Comments.Models {
public class CommentSettingsProvider : ContentProvider {
private readonly IRepository<CommentSettingsRecord> _commentSettingsRepository;
public CommentSettingsProvider(IRepository<CommentSettingsRecord> repository) {
_commentSettingsRepository = repository;
Filters.Add(new ActivatingFilter<CommentSettings>("site"));
Filters.Add(new StorageFilter<CommentSettingsRecord>(_commentSettingsRepository) { AutomaticallyCreateMissingRecord = true });
Filters.Add(new TemplateFilterForRecord<CommentSettingsRecord>("CommentSettings"));
OnActivated<CommentSettings>(DefaultSettings);
}
private static void DefaultSettings(ActivatedContentContext context, CommentSettings settings) {
settings.Record.EnableCommentsOnPages = true;
settings.Record.EnableCommentsOnPosts = true;
}
}
}

View File

@@ -0,0 +1,12 @@
using Orchard.Models.Records;
namespace Orchard.Comments.Models {
public class CommentSettingsRecord : ContentPartRecord {
public virtual bool RequireLoginToAddComment { get; set; }
public virtual bool EnableCommentsOnPages { get; set; }
public virtual bool EnableCommentsOnPosts { get; set; }
public virtual bool EnableSpamProtection { get; set; }
public virtual string AkismetKey { get; set; }
public virtual string AkismetUrl { get; set; }
}
}

View File

@@ -5,15 +5,6 @@ using Orchard.Models.Driver;
using Orchard.UI.Models;
namespace Orchard.Comments.Models {
public class HasComments : ContentPart {
public HasComments() {
Comments = new List<Comment>();
}
public IEnumerable<Comment> Comments { get; set; }
public bool Closed { get; set; }
}
public class HasCommentsProvider : ContentProvider {
private readonly IRepository<Comment> _commentsRepository;
private readonly IRepository<ClosedComments> _closedCommentsRepository;

View File

@@ -0,0 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using Orchard.Models;
namespace Orchard.Comments.Models {
public class HasComments : ContentPart {
public HasComments() {
Comments = Enumerable.Empty<Comment>();
}
public IEnumerable<Comment> Comments { get; set; }
public bool Closed { get; set; }
}
}

View File

@@ -69,11 +69,15 @@
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Models\Comment.cs" />
<Compile Include="Models\CommentSettings.cs" />
<Compile Include="Models\CommentSettingsProvider.cs" />
<Compile Include="Models\CommentSettingsRecord.cs" />
<Compile Include="Models\CommentsHandler.cs" />
<Compile Include="Models\HasComments.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<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" />

View File

@@ -9,10 +9,6 @@ using Orchard.UI.Notify;
using Joel.Net;
namespace Orchard.Comments.Services {
public interface ICommentValidator : IDependency {
bool ValidateComment(Comment comment);
}
//This uses an akismet api implementation from http://akismetapi.codeplex.com/
//Since the implementation is trivial, it may make sense to implement it to reduce dependencies.
public class AkismetCommentValidator : ICommentValidator {

View File

@@ -0,0 +1,7 @@
using Orchard.Comments.Models;
namespace Orchard.Comments.Services {
public interface ICommentValidator : IDependency {
bool ValidateComment(Comment comment);
}
}

View File

@@ -1,25 +1,28 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<HasComments>" %>
<%@ Import Namespace="Orchard.Mvc.Html"%>
<%@ Import Namespace="Orchard.Comments.Models"%>
<h3><%= Model.Comments.Count() %> Comments</h3>
<ol>
<% foreach (var comment in Model.Comments) {%>
<li>
<%= comment.CommentText %>
</li>
<li>
Posted by <%= comment.UserName %> on <%= comment.CommentDate %>
</li>
<hr />
<% } %>
</ol>
<% if (Model.Closed) { %>
<p>Comments have been disabled for this content.</p>
<% } else { %>
<h3><%=Model.Comments.Count() %> Comment<%=Model.Comments.Count() == 1 ? "" : "s" %></h3><%
foreach (var comment in Model.Comments) { %>
<div class="name">
<div><!-- GRAVATAR --></div>
<p class="comment">
<%--TODO: (erikpo) Need to clean the name and url so nothing dangerous goes out--%>
<strong><%=Html.LinkOrDefault(comment.UserName, comment.SiteName, new { rel = "nofollow" })%></strong>
<span>said<br /><%=Html.Link(Html.DateTime(comment.CommentDate), "#")%></span>
</p>
<div class="text">
<p><%=comment.CommentText %></p>
</div>
</div><%
}
if (Model.Closed) { %>
<p>Comments have been disabled for this content.</p><%
}
else { %>
<% Html.BeginForm("Create", "Admin", new { area = "Orchard.Comments" }); %>
<%= Html.ValidationSummary() %>
<div class="yui-g">
<%=Html.ValidationSummary() %>
<div class="yui-g">
<h2 class="separator">Add a Comment</h2>
<h3>Information</h3>
<ol>
<li>
<%= Html.Hidden("CommentedOn", Model.ContentItem.Id) %>
@@ -32,18 +35,17 @@
<input id="Email" class="inputText inputTextLarge" name="Email" type="text" />
</li>
<li>
<label for="SiteName">SiteName:</label>
<label for="SiteName">Url:</label>
<input id="SiteName" class="inputText inputTextLarge" name="SiteName" type="text" />
</li>
<li>
<label for="CommentText">Leave a comment</label>
<textarea id="CommentText" rows="10" cols="30" name="CommentText">
</textarea>
<textarea id="CommentText" rows="10" cols="30" name="CommentText"></textarea>
</li>
<li>
<input type="submit" class="button" value="Submit Comment" />
</li>
</ol>
</div>
<% Html.EndForm(); %>
<% } %>
</div>
<% Html.EndForm(); %><%
} %>

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.Utility;
namespace Orchard.Mvc.Html {
@@ -84,5 +85,87 @@ namespace Orchard.Mvc.Html {
#endregion
#region Format Date/Time
//TODO: (erikpo) This method needs localized
public static string DateTime(this HtmlHelper htmlHelper, DateTime value)
{
TimeSpan time = System.DateTime.UtcNow - value;
if (time.TotalDays > 7)
//TODO: (erikpo) This format should come from a site setting
return "at " + value.ToString("MMM d yyyy h:mm tt");
if (time.TotalHours > 24)
return string.Format("{0} day{1} ago", time.Days, time.Days == 1 ? "" : "s");
if (time.TotalMinutes > 60)
return string.Format("{0} hour{1} ago", time.Hours, time.Hours == 1 ? "" : "s");
if (time.TotalSeconds > 60)
return string.Format("{0} minute{1} ago", time.Minutes, time.Minutes == 1 ? "" : "s");
else if (time.TotalSeconds > 10)
return string.Format("{0} second{1} ago", time.Seconds, time.Seconds == 1 ? "" : "s");
else
return "a moment ago";
}
public static string DateTime(this HtmlHelper htmlHelper, DateTime? value, string defaultIfNull) {
return value.HasValue ? htmlHelper.DateTime(value.Value) : defaultIfNull;
}
#endregion
#region Link
public static string Link(this HtmlHelper htmlHelper, string linkContents, string href)
{
return htmlHelper.Link(linkContents, href, null);
}
public static string Link(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes)
{
return htmlHelper.Link(linkContents, href, new RouteValueDictionary(htmlAttributes));
}
public static string Link(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary<string, object> htmlAttributes)
{
TagBuilder tagBuilder = new TagBuilder("a")
{
InnerHtml = linkContents
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("href", href);
return tagBuilder.ToString(TagRenderMode.Normal);
}
#endregion
#region LinkOrDefault
public static string LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href)
{
return htmlHelper.LinkOrDefault(linkContents, href, null);
}
public static string LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href, object htmlAttributes)
{
return htmlHelper.LinkOrDefault(linkContents, href, new RouteValueDictionary(htmlAttributes));
}
public static string LinkOrDefault(this HtmlHelper htmlHelper, string linkContents, string href, IDictionary<string, object> htmlAttributes)
{
if (!string.IsNullOrEmpty(href))
{
TagBuilder tagBuilder = new TagBuilder("a")
{
InnerHtml = linkContents
};
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("href", href);
linkContents = tagBuilder.ToString(TagRenderMode.Normal);
}
return linkContents;
}
#endregion
}
}