From a41335c5bad61c3b34045ca34456fe8218fd8106 Mon Sep 17 00:00:00 2001 From: tmont Date: Fri, 4 Mar 2011 08:20:20 +0000 Subject: [PATCH] * improved default browse page * fixed bug where you couldn't add more categories if none already existed * added ability to delete quotes * added call handler to delete the search index when a quote is deleted --- .../DeleteSearchIndexCallHandler.cs | 31 +++++++++++++++++++ .../Configuration/EnableSearchWithLucene.cs | 16 ++++++++++ .../Controllers/QuoteController.cs | 12 ++++++- Src/VideoGameQuotes.Web/Global.asax.cs | 3 +- .../Models/DefaultBrowseModel.cs | 9 ++++++ .../Services/QuoteService.cs | 18 +++++++++++ .../VideoGameQuotes.Web.csproj | 7 ++++- .../Views/Quote/DefaultBrowse.aspx | 14 +++++---- .../Views/Quote/DeleteSuccess.aspx | 9 ++++++ .../Views/Quote/QuoteNotFound.aspx | 4 +-- .../Views/Shared/SingleQuote.ascx | 5 +-- 11 files changed, 115 insertions(+), 13 deletions(-) create mode 100644 Src/VideoGameQuotes.Web/Configuration/DeleteSearchIndexCallHandler.cs create mode 100644 Src/VideoGameQuotes.Web/Models/DefaultBrowseModel.cs create mode 100644 Src/VideoGameQuotes.Web/Views/Quote/DeleteSuccess.aspx diff --git a/Src/VideoGameQuotes.Web/Configuration/DeleteSearchIndexCallHandler.cs b/Src/VideoGameQuotes.Web/Configuration/DeleteSearchIndexCallHandler.cs new file mode 100644 index 0000000..5ce2d86 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Configuration/DeleteSearchIndexCallHandler.cs @@ -0,0 +1,31 @@ +using Microsoft.Practices.Unity; +using Microsoft.Practices.Unity.InterceptionExtension; +using Portoa.Search; +using VideoGameQuotes.Api; + +namespace VideoGameQuotes.Web.Configuration { + /// + /// Call handler that deletes the search index whenever a quote is deleted + /// + public class DeleteSearchIndexCallHandler : ICallHandler { + private readonly IUnityContainer container; + + public DeleteSearchIndexCallHandler(IUnityContainer container) { + this.container = container; + } + + public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext) { + var quoteId = (int)input.Arguments[0]; + var returnValue = getNext()(input, getNext); + if (returnValue.Exception != null) { + return returnValue; + } + + container.Resolve>().DeleteIndex(new Quote { Id = quoteId }); + + return returnValue; + } + + public int Order { get; set; } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Configuration/EnableSearchWithLucene.cs b/Src/VideoGameQuotes.Web/Configuration/EnableSearchWithLucene.cs index 158d6c4..e573a9a 100644 --- a/Src/VideoGameQuotes.Web/Configuration/EnableSearchWithLucene.cs +++ b/Src/VideoGameQuotes.Web/Configuration/EnableSearchWithLucene.cs @@ -43,6 +43,12 @@ namespace VideoGameQuotes.Web.Configuration { .AddPolicy("UpdateSearchIndexPolicy") .AddCallHandler() .AddMatchingRule(new QuoteUpdatedMatchingRule()); + + Container + .Configure() + .AddPolicy("DeleteSearchIndexPolicy") + .AddCallHandler() + .AddMatchingRule(new QuoteDeletedMatchingRule()); } #region lucene-related factories @@ -60,6 +66,7 @@ namespace VideoGameQuotes.Web.Configuration { } #endregion + #region matching rules private class QuoteUpdatedMatchingRule : IMatchingRule { private static readonly MethodBase saveMethod = typeof(IRepository).GetMethod("Save", new[] { typeof(Quote) }); @@ -67,5 +74,14 @@ namespace VideoGameQuotes.Web.Configuration { return member == saveMethod; } } + + private class QuoteDeletedMatchingRule : IMatchingRule { + private static readonly MethodBase deleteMethod = typeof(IRepository).GetMethod("Delete", new[] { typeof(int) }); + + public bool Matches(MethodBase member) { + return member == deleteMethod; + } + } + #endregion } } \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs b/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs index 0a85d39..5d7de55 100644 --- a/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs +++ b/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs @@ -31,13 +31,23 @@ namespace VideoGameQuotes.Web.Controllers { return this.SerializeToJson(data); } + [HttpPost, VerifyUser(Group = UserGroup.Admin)] + public ActionResult Delete(int id) { + try { + quoteService.Delete(id); + return Json(this.CreateJsonResponse()); + } catch (Exception e) { + return Json(this.CreateJsonErrorResponse(e)); + } + } + public ActionResult Browse(BrowseModel model, int page = 1) { if (page < 1) { return new StatusOverrideResult(View("BadPaging")) { StatusCode = HttpStatusCode.BadRequest }; } if (model.IsEmpty) { - return View("DefaultBrowse"); + return View("DefaultBrowse", quoteService.GetDefaultBrowseModel()); } model.CurrentUser = currentUserProvider.CurrentUser; diff --git a/Src/VideoGameQuotes.Web/Global.asax.cs b/Src/VideoGameQuotes.Web/Global.asax.cs index 51909ee..f3bdd8b 100644 --- a/Src/VideoGameQuotes.Web/Global.asax.cs +++ b/Src/VideoGameQuotes.Web/Global.asax.cs @@ -75,7 +75,8 @@ namespace VideoGameQuotes.Web { //these routes don't work with constraints in mono...? routes.MapSmartRoute("quote-edit", "quote/edit/{id}", new { controller = "Quote", action = "Edit" }); - routes.MapSmartRoute("individual-quote-with-text", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }); + routes.MapSmartRoute("quote-delete", "quote/delete", new { controller = "Quote", action = "Delete" }); + routes.MapSmartRoute("single-quote", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }); routes.MapSmartRoute("dismiss-flag", "dismiss-flag", new { controller = "Quote", action = "DismissFlag" }); routes.MapSmartRoute("default", "", new { controller = "Home", action = "Index" }); diff --git a/Src/VideoGameQuotes.Web/Models/DefaultBrowseModel.cs b/Src/VideoGameQuotes.Web/Models/DefaultBrowseModel.cs new file mode 100644 index 0000000..0544f15 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Models/DefaultBrowseModel.cs @@ -0,0 +1,9 @@ +namespace VideoGameQuotes.Web.Models { + public class DefaultBrowseModel { + public int TotalNumberOfQuotes { get; set; } + public int TotalNumberOfGames { get; set; } + public int TotalNumberOfSystems { get; set; } + public int TotalNumberOfPublishers { get; set; } + public int TotalNumberOfCategories { get; set; } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Services/QuoteService.cs b/Src/VideoGameQuotes.Web/Services/QuoteService.cs index fb86305..7e0b925 100644 --- a/Src/VideoGameQuotes.Web/Services/QuoteService.cs +++ b/Src/VideoGameQuotes.Web/Services/QuoteService.cs @@ -25,6 +25,8 @@ namespace VideoGameQuotes.Web.Services { Vote GetVoteOrCreateNew(Quote quote, User voter); IEnumerable GetBrowsableQuotes(BrowseModel model, int start, int end, out int totalCount); Quote GetQuoteForDayOfYear(int day); + DefaultBrowseModel GetDefaultBrowseModel(); + void Delete(int id); } public class QuoteService : IQuoteService { @@ -53,6 +55,22 @@ namespace VideoGameQuotes.Web.Services { this.publisherRepository = publisherRepository; } + [UnitOfWork] + public DefaultBrowseModel GetDefaultBrowseModel() { + return new DefaultBrowseModel { + TotalNumberOfCategories = categoryRepository.Records.Count(), + TotalNumberOfSystems = systemRepository.Records.Count(), + TotalNumberOfPublishers = publisherRepository.Records.Count(), + TotalNumberOfGames = gameRepository.Records.Count(), + TotalNumberOfQuotes = quoteRepository.Records.Count() + }; + } + + [UnitOfWork] + public void Delete(int id) { + quoteRepository.Delete(id); + } + [UnitOfWork] public Game GetGame(int id) { return gameRepository.FindById(id); diff --git a/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj b/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj index 95cab28..c1a0ef1 100644 --- a/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj +++ b/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj @@ -105,6 +105,7 @@ + @@ -115,6 +116,7 @@ + @@ -210,6 +212,7 @@ + @@ -236,7 +239,9 @@ - + + Designer + Web.config diff --git a/Src/VideoGameQuotes.Web/Views/Quote/DefaultBrowse.aspx b/Src/VideoGameQuotes.Web/Views/Quote/DefaultBrowse.aspx index 0c950ae..ed2d2db 100644 --- a/Src/VideoGameQuotes.Web/Views/Quote/DefaultBrowse.aspx +++ b/Src/VideoGameQuotes.Web/Views/Quote/DefaultBrowse.aspx @@ -1,14 +1,16 @@ -<%@ 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" %> Browse -

Browse

+

+ There are currently <%= Model.TotalNumberOfQuotes %> quotes. Choose wisely. +

diff --git a/Src/VideoGameQuotes.Web/Views/Quote/DeleteSuccess.aspx b/Src/VideoGameQuotes.Web/Views/Quote/DeleteSuccess.aspx new file mode 100644 index 0000000..8c33b0e --- /dev/null +++ b/Src/VideoGameQuotes.Web/Views/Quote/DeleteSuccess.aspx @@ -0,0 +1,9 @@ +<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage" MasterPageFile="~/Views/Shared/Site.Master" %> +Quote Successfully Deleted + +

Wild Success!

+ +

+ The quote and all its votes and flags were deleted. +

+
\ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Views/Quote/QuoteNotFound.aspx b/Src/VideoGameQuotes.Web/Views/Quote/QuoteNotFound.aspx index c65df1c..01312e0 100644 --- a/Src/VideoGameQuotes.Web/Views/Quote/QuoteNotFound.aspx +++ b/Src/VideoGameQuotes.Web/Views/Quote/QuoteNotFound.aspx @@ -16,8 +16,8 @@

- Anyway, try <%= Html.ActionLink("searching", "Search", "Quote") %> for the quote - you were hoping to find. Maybe that will make you less of a stain on humanity. + Anyway, try searching for the quote you were hoping to find using the little search box up + there in the top right corner. Maybe that will make you less of a stain on humanity.

diff --git a/Src/VideoGameQuotes.Web/Views/Shared/SingleQuote.ascx b/Src/VideoGameQuotes.Web/Views/Shared/SingleQuote.ascx index 89a38f4..ce6ad56 100644 --- a/Src/VideoGameQuotes.Web/Views/Shared/SingleQuote.ascx +++ b/Src/VideoGameQuotes.Web/Views/Shared/SingleQuote.ascx @@ -28,11 +28,12 @@