diff --git a/Src/VideoGameQuotes.Api/Mappings/Game.hbm.xml b/Src/VideoGameQuotes.Api/Mappings/Game.hbm.xml index bd01d88..5d31ce8 100644 --- a/Src/VideoGameQuotes.Api/Mappings/Game.hbm.xml +++ b/Src/VideoGameQuotes.Api/Mappings/Game.hbm.xml @@ -13,12 +13,12 @@ - + - + diff --git a/Src/VideoGameQuotes.Api/Mappings/GamingSystem.hbm.xml b/Src/VideoGameQuotes.Api/Mappings/GamingSystem.hbm.xml index 68b6388..b3b08ea 100644 --- a/Src/VideoGameQuotes.Api/Mappings/GamingSystem.hbm.xml +++ b/Src/VideoGameQuotes.Api/Mappings/GamingSystem.hbm.xml @@ -6,8 +6,8 @@ - - + + diff --git a/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs b/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs index 051a749..b8c9258 100644 --- a/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs +++ b/Src/VideoGameQuotes.Web/Controllers/QuoteController.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Net; using System.Web.Mvc; -using JetBrains.Annotations; using Portoa.Persistence; using Portoa.Validation.DataAnnotations; using Portoa.Web; @@ -173,7 +172,7 @@ namespace VideoGameQuotes.Web.Controllers { ? quoteService.GetQuote(model.QuoteId) : new Quote {Creator = currentUserProvider.CurrentUser}; - quote.Game = GetGameFromModelData(model); + quote.Game = quoteService.GetGame(model.GameId); quote.Text = model.QuoteText; if (model.CategoryIds != null && model.CategoryIds.Count > 0) { @@ -198,71 +197,6 @@ namespace VideoGameQuotes.Web.Controllers { } } - [CanBeNull] - private Game GetGameFromModelData(EditQuoteModel model) { - if (model.GameId > 0) { - try { - return quoteService.GetGame(model.GameId); - } catch { - throw new Exception("Unable to find game with ID " + model.GameId); - } - } - - if (string.IsNullOrEmpty(model.GameName)) { - ModelState.AddModelError("GameName", "Game name must be non-empty"); - return null; - } - - //construct game from model data - var game = new Game { - Name = model.GameName, - Region = model.GameRegions, - Website = model.GameWebsite, - Creator = currentUserProvider.CurrentUser - }; - - //publishers - if ((model.PublisherIds != null && model.PublisherIds.Count > 0) || !string.IsNullOrEmpty(model.PublisherName)) { - //modifying publishers, so remove errthing - game.ClearPublishers(); - - if (model.PublisherIds != null && model.PublisherIds.Count > 0) { - foreach (var publisherId in model.PublisherIds) { - game.AddPublisher(quoteService.GetPublisher(publisherId)); - } - } - - if (!string.IsNullOrEmpty(model.PublisherName)) { - game.AddPublisher(new Publisher { - Name = model.PublisherName, - Website = model.PublisherWebsite - }); - } - } - - //systems - if ((model.SystemIds != null && model.SystemIds.Count > 0) || !string.IsNullOrEmpty(model.SystemName)) { - //modifying systems, so remove errthing - game.ClearSystems(); - - if (model.SystemIds != null && model.SystemIds.Count > 0) { - foreach (var systemId in model.SystemIds) { - game.AddSystem(quoteService.GetSystem(systemId)); - } - } - - if (!string.IsNullOrEmpty(model.SystemName)) { - game.AddSystem(new GamingSystem { - Name = model.SystemName, - Abbreviation = model.SystemAbbreviation, - ReleaseDate = model.SystemReleaseDate - }); - } - } - - return game; - } - private void ResetEditQuoteModel(EditQuoteModel model) { model.AllGames = quoteService.GetAllGames().OrderBy(game => game.Name); model.AllSystems = quoteService.GetAllSystems().OrderBy(system => system.ReleaseDate); diff --git a/Src/VideoGameQuotes.Web/Controllers/SystemController.cs b/Src/VideoGameQuotes.Web/Controllers/SystemController.cs new file mode 100644 index 0000000..1aedfd9 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Controllers/SystemController.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using System.Linq; +using System.Web.Mvc; +using Portoa.Util; +using Portoa.Web; +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 GameController : Controller { + private readonly IGameService gameService; + private readonly ICurrentUserProvider userProvider; + + public GameController(IGameService gameService, ICurrentUserProvider userProvider) { + this.gameService = gameService; + this.userProvider = userProvider; + } + + [HttpPost, VerifyUser] + public ActionResult Create(CreateGameModel model) { + if (!ModelState.IsValid) { + return Json(this.CreateJsonErrorResponse("Invalid request")); + } + + if (gameService.FindByNameAndSystems(model.Name, model.SystemIds) != null) { + return Json(this.CreateJsonResponse("A game with that name for one of those systems already exists")); + } + + var game = new Game { Name = model.Name, Website = model.Website, Region = model.Region, Creator = userProvider.CurrentUser }; + model.SystemIds.Walk(id => game.AddSystem(new GamingSystem { Id = id })); + if (model.PublisherIds != null) { + model.PublisherIds.Walk(id => game.AddPublisher(new Publisher {Id = id})); + } + + game = gameService.Save(game); + return Json(this.CreateJsonResponse(data: game.ToDto())); + } + } + + public class PublisherController : Controller { + private readonly IPublisherService publisherService; + + public PublisherController(IPublisherService publisherService) { + this.publisherService = publisherService; + } + + [HttpPost, VerifyUser] + public ActionResult Create(CreatePublisherModel model) { + if (!ModelState.IsValid) { + return Json(this.CreateJsonErrorResponse("Invalid request")); + } + + if (publisherService.FindByName(model.Name) != null) { + return Json(this.CreateJsonResponse("A publisher with that name already exists")); + } + + var publisher = publisherService.Save(new Publisher { Name = model.Name, Website = model.Website }); + return Json(this.CreateJsonResponse(data: publisher.ToDto())); + } + } + + public class SystemController : Controller { + private readonly ISystemService systemService; + + public SystemController(ISystemService systemService) { + this.systemService = systemService; + } + + [HttpPost, VerifyUser] + public ActionResult Create(CreateSystemModel model) { + if (!ModelState.IsValid) { + return Json(this.CreateJsonErrorResponse("Invalid request")); + } + + if (systemService.FindByNameOrAbbreviation(model.Name, model.Abbreviation).Any()) { + return Json(this.CreateJsonResponse("A system with that name or abbreviation already exists")); + } + + var system = systemService.Save(new GamingSystem { Abbreviation = model.Abbreviation, Name = model.Name, ReleaseDate = model.ReleaseDate }); + return Json(this.CreateJsonResponse(data: system.ToDto())); + } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Global.asax.cs b/Src/VideoGameQuotes.Web/Global.asax.cs index 7e29b00..a10c759 100644 --- a/Src/VideoGameQuotes.Web/Global.asax.cs +++ b/Src/VideoGameQuotes.Web/Global.asax.cs @@ -42,6 +42,9 @@ namespace VideoGameQuotes.Web { .RegisterType() .RegisterType() .RegisterType() + .RegisterType() + .RegisterType() + .RegisterType() .RegisterType() .RegisterType() .RegisterType() @@ -68,6 +71,8 @@ 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" }); + 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" }); diff --git a/Src/VideoGameQuotes.Web/Models/CreateGameModel.cs b/Src/VideoGameQuotes.Web/Models/CreateGameModel.cs new file mode 100644 index 0000000..b1a5b62 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Models/CreateGameModel.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using VideoGameQuotes.Api; + +namespace VideoGameQuotes.Web.Models { + public class CreateGameModel { + [Required] + public string Name { get; set; } + public string Website { get; set; } + public Region Region { get; set; } + [Required] + public List PublisherIds { get; set; } + public List SystemIds { get; set; } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Models/CreatePublisherModel.cs b/Src/VideoGameQuotes.Web/Models/CreatePublisherModel.cs new file mode 100644 index 0000000..1764e92 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Models/CreatePublisherModel.cs @@ -0,0 +1,6 @@ +namespace VideoGameQuotes.Web.Models { + public class CreatePublisherModel { + public string Name { get; set; } + public string Website { get; set; } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Models/CreateSystemModel.cs b/Src/VideoGameQuotes.Web/Models/CreateSystemModel.cs new file mode 100644 index 0000000..78c82b4 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Models/CreateSystemModel.cs @@ -0,0 +1,13 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace VideoGameQuotes.Web.Models { + public class CreateSystemModel { + [Required] + public string Name { get; set; } + [Required, StringLength(12)] + public string Abbreviation { get; set; } + [Required] + public DateTime ReleaseDate { get; set; } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs b/Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs index cb57e8f..ef95ab1 100644 --- a/Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs +++ b/Src/VideoGameQuotes.Web/Models/EditQuoteModel.cs @@ -20,8 +20,6 @@ namespace VideoGameQuotes.Web.Models { QuoteText = quote.Text; CategoryIds = quote.Categories.Select(category => category.Id).ToList(); GameId = quote.Game.Id; - PublisherIds = quote.Game.Publishers.Select(publisher => publisher.Id).ToList(); - SystemIds = quote.Game.Systems.Select(system => system.Id).ToList(); ActionName = "Edit"; } @@ -38,40 +36,13 @@ namespace VideoGameQuotes.Web.Models { public int GameId { get; set; } - //if a new game is created - [DisplayName("Name")] - public string GameName { get; set; } - [DisplayName("Website")] - public string GameWebsite { get; set; } - [DisplayName("Region")] - public Region GameRegions { get; set; } - [DisplayName("Publishers")] - public IList PublisherIds { get; set; } - [DisplayName("Systems")] - public IList SystemIds { get; set; } - - //if a new publisher is created - [DisplayName("Name")] - public string PublisherName { get; set; } - [DisplayName("Website")] - public string PublisherWebsite { get; set; } - - //if a new system is created - [DisplayName("Name")] - public string SystemName { get; set; } - [DisplayName("Abbreviation")] - public string SystemAbbreviation { get; set; } - [DisplayName("Release Date")] - public DateTime SystemReleaseDate { 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 new[] { new SelectListItem { Value = "0", Text = "--none--" } } - .Concat(AllGames.Select(game => new SelectListItem { Value = game.Id.ToString(), Text = game.Name })); + return AllGames.Select(game => new SelectListItem { Value = game.Id.ToString(), Text = game.Name }); } private static string MakeTable(string tableId, int cellsPerRow, IEnumerable items, Func cellDataCallback) { diff --git a/Src/VideoGameQuotes.Web/Models/QuoteModel.cs b/Src/VideoGameQuotes.Web/Models/QuoteModel.cs index 8779f55..78ec05e 100644 --- a/Src/VideoGameQuotes.Web/Models/QuoteModel.cs +++ b/Src/VideoGameQuotes.Web/Models/QuoteModel.cs @@ -2,12 +2,6 @@ using VideoGameQuotes.Api; namespace VideoGameQuotes.Web.Models { - - public class PagedUserModel { - public int Start { get; set; } - public int End { get; set; } - } - public class QuoteModel { [NotNull] public Quote Quote { get; set; } diff --git a/Src/VideoGameQuotes.Web/Services/GameService.cs b/Src/VideoGameQuotes.Web/Services/GameService.cs new file mode 100644 index 0000000..592b12f --- /dev/null +++ b/Src/VideoGameQuotes.Web/Services/GameService.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Portoa.Persistence; +using VideoGameQuotes.Api; + +namespace VideoGameQuotes.Web.Services { + public interface IGameService { + [CanBeNull] + Game FindByNameAndSystems(string name, IEnumerable systemIds); + Game Save(Game game); + } + + public class GameService : IGameService { + private readonly IRepository repository; + + public GameService(IRepository repository) { + this.repository = repository; + } + + [UnitOfWork] + public Game FindByNameAndSystems(string name, IEnumerable systemIds) { + return repository + .Records + .FirstOrDefault(game => game.Name == name && game.Systems.Any(system => systemIds.Contains(system.Id))); + } + + [UnitOfWork] + public Game Save(Game game) { + return repository.Save(game); + } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Services/PublisherService.cs b/Src/VideoGameQuotes.Web/Services/PublisherService.cs new file mode 100644 index 0000000..05ec4b2 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Services/PublisherService.cs @@ -0,0 +1,31 @@ +using System.Linq; +using JetBrains.Annotations; +using Portoa.Persistence; +using VideoGameQuotes.Api; + +namespace VideoGameQuotes.Web.Services { + + public interface IPublisherService { + [CanBeNull] + Publisher FindByName(string name); + Publisher Save(Publisher publisher); + } + + public class PublisherService : IPublisherService { + private readonly IRepository repository; + + public PublisherService(IRepository repository) { + this.repository = repository; + } + + [UnitOfWork] + public Publisher FindByName(string name) { + return repository.Records.FirstOrDefault(publisher => publisher.Name == name); + } + + [UnitOfWork] + public Publisher Save(Publisher publisher) { + return repository.Save(publisher); + } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/Services/SystemService.cs b/Src/VideoGameQuotes.Web/Services/SystemService.cs new file mode 100644 index 0000000..14c9a20 --- /dev/null +++ b/Src/VideoGameQuotes.Web/Services/SystemService.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Linq; +using Portoa.Persistence; +using VideoGameQuotes.Api; + +namespace VideoGameQuotes.Web.Services { + public interface ISystemService { + IEnumerable FindByNameOrAbbreviation(string name, string abbreviation); + GamingSystem Save(GamingSystem system); + } + + public class SystemService : ISystemService { + private readonly IRepository repository; + + public SystemService(IRepository repository) { + this.repository = repository; + } + + [UnitOfWork] + public IEnumerable FindByNameOrAbbreviation(string name, string abbreviation) { + return repository + .Records + .Where(system => system.Abbreviation == abbreviation || system.Name == name); + } + + [UnitOfWork] + public GamingSystem Save(GamingSystem system) { + return repository.Save(system); + } + } +} \ No newline at end of file diff --git a/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj b/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj index 5a1d27b..9f6e933 100644 --- a/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj +++ b/Src/VideoGameQuotes.Web/VideoGameQuotes.Web.csproj @@ -87,12 +87,19 @@ + + + + + + + diff --git a/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx b/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx index f0758bf..3b71b09 100644 --- a/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx +++ b/Src/VideoGameQuotes.Web/Views/Quote/EditQuoteForm.ascx @@ -5,12 +5,10 @@ <% using (Html.BeginForm(Model.ActionName, Model.ControllerName)) { %> <%= Html.HiddenFor(model => model.QuoteId, new { id = "quote-id" })%> -

- - <%= Html.Label("Game", "GameId") %> -
- <%= Html.DropDownListFor(model => model.GameId, Model.GetGameList()) %> -
+

+ <%= Html.Label("Game", "GameId") %> +
+ <%= Html.DropDownListFor(model => model.GameId, Model.GetGameList()) %> Create new game

@@ -20,82 +18,87 @@ Create new game

- <%= Html.LabelFor(model => model.GameName) %> + <%= Html.Label("Name", "GameName") %>
- <%= Html.TextBoxFor(model => model.GameName) %> + <%= Html.TextBox("GameName") %>

- <%= Html.LabelFor(model => model.GameWebsite) %> (link to Wikipedia page or something) + <%= Html.Label("Website", "GameWebsite") %> (link to Wikipedia page or something)
- <%= Html.TextBoxFor(model => model.GameWebsite) %> + <%= Html.TextBox("GameWebsite") %>

- <%= Html.LabelFor(model => model.GameRegions) %> + Regions
<%= Model.MakeRegionTable() %>

-

- - <%= Html.LabelFor(model => model.SystemIds) %> -
- <%= Model.MakeSystemTable(Html) %> -
+

+ Systems +
+ <%= Model.MakeSystemTable(Html) %> Create new system -

+
Create new system

- <%= Html.LabelFor(model => model.SystemName) %> + <%= Html.Label("Name", "SystemName") %>
- <%= Html.TextBoxFor(model => model.SystemName) %> + <%= Html.TextBox("SystemName") %>

- <%= Html.LabelFor(model => model.SystemAbbreviation) %> + <%= Html.Label("Abbreviation", "SystemAbbreviation") %>
- <%= Html.TextBoxFor(model => model.SystemAbbreviation) %> + <%= Html.TextBox("SystemAbbreviation")%>

- <%= Html.LabelFor(model => model.SystemReleaseDate) %> + <%= Html.Label("Release Date", "SystemReleaseDate") %>
- <%= Html.TextBox("SystemReleaseDate", DateTime.UtcNow.ToString("yyyy-MM-dd")) %> + <%= Html.TextBox("SystemReleaseDate", null) %>

+ + <%= Html.Button("Create System", new { id = "create-system-submit" })%> + <%= Html.Button("Cancel", new { id = "create-system-cancel" })%>
-

- - <%= Html.LabelFor(model => model.PublisherIds) %> -
- <%= Model.MakePublisherTable(Html) %> -
+

+ Publishers +
+ <%= Model.MakePublisherTable(Html) %> Create new publisher -

+
Create new publisher

- <%= Html.LabelFor(model => model.PublisherName) %> + <%= Html.Label("Name", "PublisherName") %>
- <%= Html.TextBoxFor(model => model.PublisherName) %> + <%= Html.TextBox("PublisherName") %>

- <%= Html.LabelFor(model => model.PublisherWebsite) %> + <%= Html.Label("Website", "PublisherWebsite") %>
- <%= Html.TextBoxFor(model => model.PublisherWebsite) %> + <%= Html.TextBox("PublisherWebsite") %>

+ + <%= Html.Button("Create Publisher", new { id = "create-publisher-submit" })%> + <%= Html.Button("Cancel", new { id = "create-publisher-cancel" })%>
+ + <%= Html.Button("Create Game", new { id = "create-game-submit" })%> + <%= Html.Button("Cancel", new { id = "create-game-cancel" })%> diff --git a/Src/VideoGameQuotes.Web/media/js/vgquotes.js b/Src/VideoGameQuotes.Web/media/js/vgquotes.js index 3a9303c..e7a6965 100644 --- a/Src/VideoGameQuotes.Web/media/js/vgquotes.js +++ b/Src/VideoGameQuotes.Web/media/js/vgquotes.js @@ -225,43 +225,104 @@ }; var setupQuoteForm = function() { - $("#create-game-link").click(function() { - var $form = $("#create-game-form"); - if ($form.is(":visible")) { - $("#create-game-link").text("Create new game"); - } else { - $("#create-game-link").text("Use existing game"); - $("#GameId").val("0"); + var toggleFormAndLink = function(type) { + return function() { + $("#create-" + type + "-form").toggle(); + $("#" + type + "-select").toggle(); + return false; + } + }; + + var addNewCheckbox = function($table, name, value, text, cellsPerRow) { + var $row = $table.find("tr:last"); + if ($row.find("td").length === cellsPerRow) { + $row = $(""); + $table.append($row); } - $form.toggle(); - $("#game-select").toggle(); + var $cell = $("").append($("").attr({ + type: "checkbox", + name: name, + value: value, + id: name + "_" + value, + checked: "checked" + })).append($("