* edit/create/delete categories

* also made error views user-aware
This commit is contained in:
tmont 2011-02-24 08:07:50 +00:00
parent dc9a415428
commit ff3499b3b9
14 changed files with 165 additions and 28 deletions

View File

@ -0,0 +1,78 @@
using System.Web.Mvc;
using Portoa.Persistence;
using Portoa.Web.Controllers;
using VideoGameQuotes.Api;
using VideoGameQuotes.Web.Models;
using VideoGameQuotes.Web.Security;
using VideoGameQuotes.Web.Services;
namespace VideoGameQuotes.Web.Controllers {
public class CategoryController : Controller {
private readonly ICategoryService categoryService;
public CategoryController(ICategoryService categoryService) {
this.categoryService = categoryService;
}
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
public JsonResult Delete(int id) {
if (id < 1) {
return Json(this.CreateJsonResponse("Invalid ID"));
}
try {
var category = categoryService.FindById(id);
var numQuotes = categoryService.GetQuotesForCategory(category);
if (numQuotes > 0) {
return Json(this.CreateJsonErrorResponse(
string.Format("There are {0} quotes that are still using this category", numQuotes)
));
}
} catch (EntityNotFoundException) {
return Json(this.CreateJsonErrorResponse("No category exists for ID " + id));
}
categoryService.Delete(id);
return Json(this.CreateJsonResponse());
}
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
public JsonResult Edit(EditCategoryModel model) {
if (model.CategoryId < 1) {
ModelState.AddModelError("CategoryId", "Invalid category ID");
}
if (!ModelState.IsValid) {
return Json(this.CreateJsonErrorResponse("Some errors occurred"));
}
try {
var category = categoryService.FindById(model.CategoryId);
if (categoryService.FindByName(model.CategoryName) != null) {
return Json(this.CreateJsonErrorResponse("A category already exists for that name"));
}
category.Name = model.CategoryName;
category = categoryService.Save(category);
return Json(this.CreateJsonResponse(data: category.ToDto()));
} catch (EntityNotFoundException) {
return Json(this.CreateJsonErrorResponse("No category exists for ID " + model.CategoryId));
}
}
[HttpPost, VerifyUser]
public JsonResult Create(EditCategoryModel model) {
if (!ModelState.IsValid) {
return Json(this.CreateJsonErrorResponse("Some errors occurred."));
}
if (categoryService.FindByName(model.CategoryName) != null) {
return Json(this.CreateJsonErrorResponse("A category already exists for that name"));
}
var category = categoryService.Save(new Category { Name = model.CategoryName });
return Json(this.CreateJsonResponse(data: category.ToDto()));
}
}
}

View File

@ -233,17 +233,6 @@ namespace VideoGameQuotes.Web.Controllers {
}
}
[HttpPost, VerifyUser]
public JsonResult CreateCategory(Category category) {
try {
category = quoteService.SaveCategory(category);
var data = new Dictionary<string, string> { { "categoryId", category.Id.ToString() }, { "categoryName", category.Name } };
return Json(this.CreateJsonResponse(null, data));
} catch (Exception e) {
return Json(this.CreateJsonErrorResponse(e));
}
}
public ActionResult Search(string searchQuery) {
var model = new SearchModel {
User = currentUserProvider.CurrentUser,

View File

@ -43,6 +43,7 @@ namespace VideoGameQuotes.Web {
.RegisterType<IAdministrationService, AdministrationService>()
.RegisterType<IQuoteService, QuoteService>()
.RegisterType<ISystemService, SystemService>()
.RegisterType<ICategoryService, CategoryService>()
.RegisterType<IPublisherService, PublisherService>()
.RegisterType<IGameService, GameService>()
.RegisterType<IApiService, ApiService>()
@ -71,7 +72,7 @@ namespace VideoGameQuotes.Web {
//bullshit route so that RenderAction works
routes.MapRoute("mainmenu", "home/mainmenu", new { controller = "Home", action = "MainMenu" });
routes.MapRoute("crud-default", "{controller}/{action}", null, new { controller = "system|publisher|game", action = "create|edit|delete" });
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" });
@ -88,7 +89,6 @@ namespace VideoGameQuotes.Web {
routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|recent|random|vote|report" });
routes.MapRoute("dismiss-flag", "dismiss-flag", new { controller = "Quote", action = "DismissFlag" });
routes.MapRoute("individual-quote", "quote/{id}/{*text}", new { controller = "Quote", action = "Quote" }, new { id = @"\d+" });
routes.MapRoute("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
routes.MapRoute("default", "{controller}", new { controller = "home", action = "index" });
}
}

View File

@ -0,0 +1,9 @@
using System.ComponentModel.DataAnnotations;
namespace VideoGameQuotes.Web.Models {
public class EditCategoryModel {
public int CategoryId { get; set; }
[Required(ErrorMessage = "The category must have a name, queerball")]
public string CategoryName { get; set; }
}
}

View File

@ -22,6 +22,7 @@ namespace VideoGameQuotes.Web.Security {
if (!allowedToExecuteAction) {
filterContext.Result = new ErrorViewResult {
Message = "You are not a verified user (are you hiding your IP address?)",
ModelCreator = exception => new ErrorModel<User> { Exception = exception, User = UserProvider.CurrentUser },
StatusCode = HttpStatusCode.Forbidden,
ViewName = "Forbidden"
};

View File

@ -0,0 +1,53 @@
using System.Linq;
using JetBrains.Annotations;
using Portoa.Persistence;
using VideoGameQuotes.Api;
namespace VideoGameQuotes.Web.Services {
public interface ICategoryService {
[CanBeNull]
Category FindByName(string name);
Category Save(Category category);
Category FindById(int id);
void Delete(int id);
int GetQuotesForCategory(Category category);
}
public class CategoryService : ICategoryService {
private readonly IRepository<Category> categoryRepository;
private readonly IRepository<Quote> quoteRepository;
public CategoryService(IRepository<Category> categoryRepository, IRepository<Quote> quoteRepository) {
this.categoryRepository = categoryRepository;
this.quoteRepository = quoteRepository;
}
[UnitOfWork]
public Category FindByName(string name) {
return categoryRepository.Records.FirstOrDefault(category => category.Name == name);
}
[UnitOfWork]
public Category Save(Category category) {
return categoryRepository.Save(category);
}
[UnitOfWork]
public Category FindById(int id) {
return categoryRepository.FindById(id);
}
[UnitOfWork]
public void Delete(int id) {
categoryRepository.Delete(id);
}
[UnitOfWork]
public int GetQuotesForCategory(Category category) {
return quoteRepository
.Records
.Where(quote => quote.Categories.Contains(category))
.Count();
}
}
}

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Portoa.Persistence;

View File

@ -86,12 +86,15 @@
<ItemGroup>
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\ApiController.cs" />
<Compile Include="Controllers\CategoryController.cs" />
<Compile Include="Controllers\GameController.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\PublisherController.cs" />
<Compile Include="Controllers\SystemController.cs" />
<Compile Include="Models\EditCategoryModel.cs" />
<Compile Include="Models\EditGameModel.cs" />
<Compile Include="Models\EditPublisherModel.cs" />
<Compile Include="Services\CategoryService.cs" />
<Compile Include="Services\GameService.cs" />
<Compile Include="Services\PublisherService.cs" />
<Compile Include="Services\SystemService.cs" />
@ -140,7 +143,9 @@
<Content Include="media\css\global.css" />
<Content Include="media\css\quote.css" />
<Content Include="media\css\reset.css" />
<Content Include="media\images\accept.png" />
<Content Include="media\images\add.png" />
<Content Include="media\images\cancel.png" />
<Content Include="media\images\delete.png" />
<Content Include="media\images\favicon.png" />
<Content Include="media\images\loading.gif" />

View File

@ -1,12 +1,15 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Portoa.Web.ErrorHandling.ErrorModel>" %>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Portoa.Web.ErrorHandling.ErrorModel<VideoGameQuotes.Api.User>>" %>
<%@ Import Namespace="VideoGameQuotes.Api" %>
<%
if (true) {
if (Model.Exception != null) { %>
<h2>Exception Details</h2> <%
Html.RenderPartial("RecursiveExceptionView", Model.Exception);
} else { %>
<p class="info">No exception was thrown.</p>
<% }
}
if (Model.User == null || Model.User.Group < UserGroup.Admin) {
return;
}
if (Model.Exception != null) { %>
<h2>Exception Details</h2> <%
Html.RenderPartial("RecursiveExceptionView", Model.Exception);
} else { %>
<p class="info">No exception was thrown.</p>
<% }
%>

View File

@ -1,4 +1,4 @@
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel<VideoGameQuotes.Api.User>>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">Forbidden</asp:Content>

View File

@ -1,4 +1,4 @@
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel<VideoGameQuotes.Api.User>>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content ContentPlaceHolderID="TitleContent" runat="server">404</asp:Content>

View File

@ -1,9 +1,9 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Exception>" %>
<%@ Import Namespace="Portoa.Util" %>
<p class="exception-message"><%= Model.GetType().GetFriendlyName(false) %>: <code style="white-space: pre"><%= Html.Encode(Model.Message) %></code></p>
<p class="exception-message"><%= Model.GetType().GetFriendlyName(false) %>: <code style="white-space: pre"><%: Model.Message %></code></p>
<pre><%= Model.StackTrace %></pre>
<pre><%: Model.StackTrace %></pre>
<hr />

Binary file not shown.

After

Width:  |  Height:  |  Size: 781 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B