From 9cc0df5980d97a48c0aedd71afe0cd553428b593 Mon Sep 17 00:00:00 2001 From: tmont Date: Mon, 28 Feb 2011 10:34:46 +0000 Subject: [PATCH] added captcha to submit quote form --- .../Controllers/HomeController.cs | 45 ++++++++++------- .../Controllers/QuoteController.cs | 50 ++++++++++++------- Src/VideoGameQuotes.Web/Global.asax.cs | 5 +- ...ditQuoteModel.cs => EditQuoteModelBase.cs} | 39 ++++++++------- .../Models/QuoteCollectionModel.cs | 9 ---- .../VideoGameQuotes.Web.csproj | 3 +- .../Views/Home/Contact.aspx | 2 +- Src/VideoGameQuotes.Web/Views/Quote/Edit.aspx | 2 +- .../Views/Quote/EditQuoteForm.ascx | 28 +++++++---- .../Views/Quote/Submit.aspx | 26 +++++++++- Src/VideoGameQuotes.Web/media/css/global.css | 13 +++-- Src/VideoGameQuotes.Web/media/js/browse.js | 4 +- 12 files changed, 138 insertions(+), 88 deletions(-) rename Src/VideoGameQuotes.Web/Models/{EditQuoteModel.cs => EditQuoteModelBase.cs} (81%) delete mode 100644 Src/VideoGameQuotes.Web/Models/QuoteCollectionModel.cs diff --git a/Src/VideoGameQuotes.Web/Controllers/HomeController.cs b/Src/VideoGameQuotes.Web/Controllers/HomeController.cs index 5eace87..96a2a85 100644 --- a/Src/VideoGameQuotes.Web/Controllers/HomeController.cs +++ b/Src/VideoGameQuotes.Web/Controllers/HomeController.cs @@ -4,7 +4,6 @@ using System.Net.Mail; using System.Security.Cryptography; using System.Text; using System.Web.Mvc; -using Portoa.Web; using Portoa.Web.Controllers; using Portoa.Web.Filters; using Portoa.Web.Security; @@ -12,22 +11,34 @@ using VideoGameQuotes.Api; using VideoGameQuotes.Web.Models; namespace VideoGameQuotes.Web.Controllers { - public class HomeController : Controller { - private readonly IAuthenticationService authenticationService; - private readonly ICurrentUserProvider userProvider; + public static class CaptchaUtil { + private static readonly Random random = new Random(); private static readonly string[] answers = new[] { - "I AM ERROR.", + "I AM ERROR", "shyron", "our princess is in another castle", "the cake is a lie", "all your base", "ganon not gannon", "thunderbird", - "'glad you came, pit!", + "glad you came, pit", "ryu huyabasa" }; + public static string GetRandomAnswer() { + return answers[random.Next(answers.Length)]; + } + + public static string Hash(string value) { + return Convert.ToBase64String(MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(value ?? string.Empty))); + } + } + + public class HomeController : Controller { + private readonly IAuthenticationService authenticationService; + private readonly ICurrentUserProvider userProvider; + public HomeController(IAuthenticationService authenticationService, ICurrentUserProvider userProvider) { this.authenticationService = authenticationService; this.userProvider = userProvider; @@ -41,6 +52,10 @@ namespace VideoGameQuotes.Web.Controllers { return View(); } + public ActionResult Favicon() { + return File("/media/images/favicon.png", "image/png"); + } + [HttpPost] public ActionResult Login([Required]string username, [Required]string password) { if (!ModelState.IsValid) { @@ -66,32 +81,24 @@ namespace VideoGameQuotes.Web.Controllers { } public ActionResult Contact() { - var randomAnswer = GetRandomAnswer(); + var randomAnswer = CaptchaUtil.GetRandomAnswer(); var model = new ContactModel { UnhashedCaptchaAnswer = randomAnswer, - HashedCaptchaAnswer = GetHashedCaptcha(randomAnswer) + HashedCaptchaAnswer = CaptchaUtil.Hash(randomAnswer) }; return View(model); } - private static string GetRandomAnswer() { - return answers[new Random().Next(answers.Length)]; - } - - private static string GetHashedCaptcha(string value) { - return Convert.ToBase64String(MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(value ?? string.Empty))); - } - private static void ResetModel(ContactModel model) { - model.UnhashedCaptchaAnswer = GetRandomAnswer(); - model.HashedCaptchaAnswer = GetHashedCaptcha(model.UnhashedCaptchaAnswer); + model.UnhashedCaptchaAnswer = CaptchaUtil.GetRandomAnswer(); + model.HashedCaptchaAnswer = CaptchaUtil.Hash(model.UnhashedCaptchaAnswer); model.CaptchaAnswer = null; } [HttpPost] public ActionResult Contact(ContactModel model) { - if (GetHashedCaptcha(model.CaptchaAnswer) != model.HashedCaptchaAnswer) { + if (CaptchaUtil.Hash(model.CaptchaAnswer) != model.HashedCaptchaAnswer) { ModelState.AddModelError("CaptchaAnswer", "You are not human"); } diff --git a/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs b/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs index b1ffd9d..cc10a1a 100644 --- a/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs +++ b/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs @@ -1,6 +1,8 @@ using System; using System.Linq; using System.Net; +using System.Security.Cryptography; +using System.Text; using System.Web.Mvc; using Portoa.Persistence; using Portoa.Search; @@ -171,55 +173,56 @@ namespace VideoGameQuotes.Web.Controllers { } try { - var model = new EditQuoteModel(quoteService.GetQuote(id)); - ResetEditQuoteModel(model); - return View(model); + return View(ResetEditQuoteModel(new EditQuoteModel(quoteService.GetQuote(id)))); } catch (EntityNotFoundException) { return View("QuoteNotFound"); } } [HttpPost, VerifyUser(Group = UserGroup.Admin)] - public ActionResult Edit(EditQuoteModel model) { + public ActionResult Edit(EditQuoteModelBase model) { return CreateOrEditQuote(model, "Edit"); } [VerifyUser] public ActionResult Submit() { - var model = new EditQuoteModel(); - ResetEditQuoteModel(model); - return View(model); + return View(ResetEditQuoteModel(new SubmitQuoteModel())); } [HttpPost, VerifyUser] - public ActionResult Submit(EditQuoteModel model) { + public ActionResult Submit(SubmitQuoteModel model) { + if (CaptchaUtil.Hash(model.CaptchaAnswer) != model.HashedCaptchaAnswer) { + ModelState.AddModelError("CaptchaAnswer", "You are not human"); + } + return CreateOrEditQuote(model, "Submit"); } - private ActionResult CreateOrEditQuote(EditQuoteModel model, string viewName) { + private ActionResult CreateOrEditQuote(EditQuoteModelBase model, string viewName) { if (!ModelState.IsValid) { - ResetEditQuoteModel(model); + model = ResetEditQuoteModel(model); return View(viewName, model); } try { - var quote = model.QuoteId > 0 - ? quoteService.GetQuote(model.QuoteId) + var editModel = model as EditQuoteModel; + + var quote = editModel != null && editModel.QuoteId > 0 + ? quoteService.GetQuote(editModel.QuoteId) : new Quote { Creator = currentUserProvider.CurrentUser }; - quote.Game = quoteService.GetGame(model.GameId); + quote.Game = new Game { Id = model.GameId }; quote.Text = model.QuoteText; if (model.CategoryIds != null && model.CategoryIds.Count > 0) { quote.ClearCategories(); foreach (var categoryId in model.CategoryIds) { - quote.AddCategory(quoteService.GetCategory(categoryId)); + quote.AddCategory(new Category { Id = categoryId }); } } if (quote.Game == null) { - ResetEditQuoteModel(model); - return View(viewName, model); + return View(viewName, ResetEditQuoteModel(model)); } quote = quoteService.SaveQuote(quote); @@ -227,17 +230,26 @@ namespace VideoGameQuotes.Web.Controllers { return RedirectToAction("Quote", new { id = quote.Id, text = quote.GetUrlFriendlyText() }); } catch (Exception e) { ModelState.AddModelError("save", e.Message); - ResetEditQuoteModel(model); - return View(viewName, model); + return View(viewName, ResetEditQuoteModel(model)); } } - private void ResetEditQuoteModel(EditQuoteModel model) { + private EditQuoteModelBase ResetEditQuoteModel(EditQuoteModelBase model) { model.CurrentUser = currentUserProvider.CurrentUser; model.AllGames = quoteService.GetAllGames().OrderBy(game => game.Name); model.AllSystems = quoteService.GetAllSystems().OrderBy(system => system.ReleaseDate); model.AllPublishers = quoteService.GetAllPublishers().OrderBy(publisher => publisher.Name); model.AllCategories = quoteService.GetAllCategories().OrderBy(category => category.Name); + + var submitModel = model as SubmitQuoteModel; + if (submitModel != null) { + submitModel.UnhashedCaptchaAnswer = CaptchaUtil.GetRandomAnswer(); + submitModel.HashedCaptchaAnswer = CaptchaUtil.Hash(submitModel.UnhashedCaptchaAnswer); + submitModel.CaptchaAnswer = null; + return submitModel; + } + + return model; } public ActionResult Quote(int id) { diff --git a/Src/VideoGameQuotes.Web/Global.asax.cs b/Src/VideoGameQuotes.Web/Global.asax.cs index 441ab68..3ab4f56 100644 --- a/Src/VideoGameQuotes.Web/Global.asax.cs +++ b/Src/VideoGameQuotes.Web/Global.asax.cs @@ -61,18 +61,17 @@ namespace VideoGameQuotes.Web { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.IgnoreRoute("media/{*anything}"); + routes.MapRoute("favicon", "favicon.ico", new { controller = "Home", action = "Favicon" }); + //bullshit route so that RenderAction works routes.MapRoute("mainmenu", "home/mainmenu", new { controller = "Home", action = "MainMenu" }); routes.MapRoute("quote-of-the-day", "quote/quoteoftheday", new { controller = "Quote", action = "QuoteOfTheDay" }); routes.MapRoute("crud-default", "{controller}/{action}", null, new { controller = "system|publisher|game|category", action = "create|edit|delete" }); - routes.MapRoute("users-paged", "admin/users/{start}-{end}", new { controller = "Admin", action = "Users" }, new { start = @"\d+", end = @"\d+" }); routes.MapRoute("admin", "admin/{action}", new { controller = "Admin", action = "Index" }, new { action = "users|create|flags|password" }); - routes.MapRoute("user-edit", "user/edit/{usernameOrIp}", new { controller = "User", action = "Edit", usernameOrIp = @"\w+" }); routes.MapRoute("user-default", "user/{action}/{id}", new { controller = "User", action = "delete|ban", id = UrlParameter.Optional }); - routes.MapRoute("api", "api/{action}/{id}/{*criteria}", new { controller = "Api" }, new { action = "game|system|category|publisher|quote", id = @"\d+|all" }); routes.MapRoute("home", "{action}", new { controller = "Home", action = "Index" }, new { action = "about|contact|login|logout" }); routes.MapRoute("paged", "{action}/{page}", new { controller = "Quote", page = 1 }, new { action = "best|recent", page = @"\d+" }); diff --git a/Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs b/Src/VideoGameQuotes.Web/Models/EditQuoteModelBase.cs similarity index 81% rename from Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs rename to Src/VideoGameQuotes.Web/Models/EditQuoteModelBase.cs index ddc599b..26250f2 100644 --- a/Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs +++ b/Src/VideoGameQuotes.Web/Models/EditQuoteModelBase.cs @@ -10,13 +10,14 @@ using VideoGameQuotes.Api; namespace VideoGameQuotes.Web.Models { - public class EditQuoteModel { - public EditQuoteModel() { - ControllerName = "Quote"; - ActionName = "Submit"; - } + public class SubmitQuoteModel : EditQuoteModelBase { + public string CaptchaAnswer { get; set; } + public string HashedCaptchaAnswer { get; set; } + public string UnhashedCaptchaAnswer { get; set; } + } - public EditQuoteModel(Quote quote) : this() { + public class EditQuoteModel : EditQuoteModelBase { + public EditQuoteModel(Quote quote) { QuoteId = quote.Id; Flags = quote.Flags.ToList(); QuoteText = quote.Text; @@ -25,29 +26,33 @@ namespace VideoGameQuotes.Web.Models { ActionName = "Edit"; } - public User CurrentUser { get; set; } - - public string ActionName { get; set; } - public string ControllerName { get; set; } - public int QuoteId { get; set; } public List Flags { get; set; } + } + + public abstract class EditQuoteModelBase { + protected EditQuoteModelBase() { + ControllerName = "quote"; + ActionName = "submit"; + } + + public User CurrentUser { get; set; } + public string ActionName { get; set; } + public string ControllerName { get; set; } [Required(ErrorMessage = "Quote text must be non-empty, saddlebags")] [StringLength(1024, ErrorMessage = "Quote can't be longer than 1024 characters, slut")] [DisplayName("Quote")] public string QuoteText { get; set; } - [DisplayName("Categories")] public IList CategoryIds { get; set; } - [GreaterThanZero(ErrorMessage = "Quit screwing with the form, faggle"), DisplayName("Game")] + [Required(ErrorMessage = "Quit screwing with the form, faggle"), DisplayName("Game")] public int GameId { get; set; } public IEnumerable AllGames { get; set; } public IEnumerable AllSystems { get; set; } public IEnumerable AllPublishers { get; set; } public IEnumerable AllCategories { get; set; } - public IEnumerable GetGameList() { return AllGames.Select(game => new SelectListItem { Value = game.Id.ToString(), Text = game.Name }); @@ -83,7 +88,7 @@ namespace VideoGameQuotes.Web.Models { return table.ToString(TagRenderMode.Normal); } - public string MakePublisherTable(HtmlHelper html, int cellsPerRow = 4) { + public string MakePublisherTable(HtmlHelper html, int cellsPerRow = 4) { return MakeTable( "publisher-checkbox-table", cellsPerRow, @@ -92,7 +97,7 @@ namespace VideoGameQuotes.Web.Models { ); } - public string MakeSystemTable(HtmlHelper html, int cellsPerRow = 6) { + public string MakeSystemTable(HtmlHelper html, int cellsPerRow = 6) { return MakeTable( "system-checkbox-table", cellsPerRow, @@ -101,7 +106,7 @@ namespace VideoGameQuotes.Web.Models { ); } - public string MakeCategoryTable(HtmlHelper html, int cellsPerRow = 5) { + public string MakeCategoryTable(HtmlHelper html, int cellsPerRow = 5) { var categoryIds = CategoryIds ?? new List(); return MakeTable( "category-checkbox-table", diff --git a/Src/VideoGameQuotes.Web/Models/QuoteCollectionModel.cs b/Src/VideoGameQuotes.Web/Models/QuoteCollectionModel.cs deleted file mode 100644 index 8ff4ced..0000000 --- a/Src/VideoGameQuotes.Web/Models/QuoteCollectionModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using VideoGameQuotes.Api; - -namespace VideoGameQuotes.Web.Models { - public class QuoteCollectionModel { - public IEnumerable Quotes { get; set; } - public User User { get; set; } - } -} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj b/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj index d4e2f06..e073cfb 100644 --- a/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj +++ b/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj @@ -110,7 +110,6 @@ - @@ -128,7 +127,7 @@ ASPXCodeBehind - + Global.asax diff --git a/Src/VideoGameQuotes.Web/Views/Home/Contact.aspx b/Src/VideoGameQuotes.Web/Views/Home/Contact.aspx index a6f64a6..6240850 100644 --- a/Src/VideoGameQuotes.Web/Views/Home/Contact.aspx +++ b/Src/VideoGameQuotes.Web/Views/Home/Contact.aspx @@ -55,7 +55,7 @@ .addClass("dialog") .css({ top: "20px" }) .append( - $("The answer is ") + $("The answer is: ") .css({ "white-space": "nowrap" }) .find("strong") .text("<%= Model.UnhashedCaptchaAnswer %>") diff --git a/Src/VideoGameQuotes.Web/Views/Quote/Edit.aspx b/Src/VideoGameQuotes.Web/Views/Quote/Edit.aspx index ede0cd5..ee7bf18 100644 --- a/Src/VideoGameQuotes.Web/Views/Quote/Edit.aspx +++ b/Src/VideoGameQuotes.Web/Views/Quote/Edit.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %> +<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %> Edit

Edit Quote

diff --git a/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx b/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx index a21f031..87550fa 100644 --- a/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx +++ b/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx @@ -1,11 +1,10 @@ -<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> +<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%@ Import Namespace="Portoa.Web.Util" %> <%@ Import Namespace="VideoGameQuotes.Api" %> +<%@ Import Namespace="VideoGameQuotes.Web.Models" %>
<% using (Html.BeginForm(Model.ActionName, Model.ControllerName)) { %> -
<%= Html.HiddenFor(model => model.QuoteId, new { id = "quote-id" })%>
-

<%= Html.LabelFor(model => model.GameId, new { @class = "label" })%>
@@ -146,17 +145,18 @@

- <% if (Model.QuoteId > 0) { %> + <% var editModel = Model as EditQuoteModel; if (editModel != null && editModel.QuoteId > 0) { %> + <%= Html.Hidden("QuoteId", editModel.QuoteId, new { id = "quote-id" })%>
- <% foreach (var flag in Model.Flags) { %> + <% foreach (var flag in editModel.Flags) { %>

- Flagged as <%: flag.Type %> on <%= flag.Created %> by - <%: flag.User.Username ?? flag.User.IpAddress %>. + Flagged as <%: flag.Type%> on <%= flag.Created%> by + <%: flag.User.Username ?? flag.User.IpAddress%>.

-

<%: flag.Comment %>

+

<%: flag.Comment%>

dismiss

@@ -166,6 +166,16 @@
-

<%= Html.Submit(Model.QuoteId > 0 ? "Save" : "Submit Quote") %>

+ <% var submitModel = Model as SubmitQuoteModel; if (submitModel != null) { %> + + +

+ Are you human? [click for correct answer] +
+ +

+ <% } %> + +

<%= Html.Submit(editModel != null && editModel.QuoteId > 0 ? "Save" : "Submit Quote") %>

<% } %>
\ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Views/Quote/Submit.aspx b/Src/VideoGameQuotes.Web/Views/Quote/Submit.aspx index 86cfa25..32f82b1 100644 --- a/Src/VideoGameQuotes.Web/Views/Quote/Submit.aspx +++ b/Src/VideoGameQuotes.Web/Views/Quote/Submit.aspx @@ -1,4 +1,4 @@ -<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %> +<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %> Submit New Quote

Submit New Quote

@@ -8,4 +8,28 @@
+ \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/media/css/global.css b/Src/VideoGameQuotes.Web/media/css/global.css index 706a7bc..092bf2b 100644 --- a/Src/VideoGameQuotes.Web/media/css/global.css +++ b/Src/VideoGameQuotes.Web/media/css/global.css @@ -79,6 +79,10 @@ ul { ol { list-style-type: decimal; } +li { + padding: 2px 1px; + margin: 1px 0; +} dt { font-weight: bold; padding: 2px; @@ -471,7 +475,9 @@ a.external { width: 500px; position: relative; } -.quote-game-link { +#captcha-dialog { + position: absolute; + left: 0; } .quote-categories a { border: 1px solid #999999 !important; @@ -482,10 +488,7 @@ a.external { color: #FFFFFF !important; position: relative; } -.quote-categories a.game-link { - padding-left: 4px; -} -.quote-categories a.game-link img { +.quote-categories a img { position: relative; top: 3px; } diff --git a/Src/VideoGameQuotes.Web/media/js/browse.js b/Src/VideoGameQuotes.Web/media/js/browse.js index 85f5945..bda2938 100644 --- a/Src/VideoGameQuotes.Web/media/js/browse.js +++ b/Src/VideoGameQuotes.Web/media/js/browse.js @@ -41,8 +41,8 @@ data[type] = response.Data.records; render(); }, - beforeSend: function() { $link.toggleClass("loading-link"); }, - complete: function() { $link.toggleClass("loading-link"); } + beforeSend: function() { $link.toggleClass("loading-icon"); }, + complete: function() { $link.toggleClass("loading-icon"); } }); } else { render();