* quote editing, although not quite finished

* flag dismissal
* fixed some mapping issues by adding inverse="true" to some one-to-many relationships
This commit is contained in:
tmont 2011-02-19 09:41:09 +00:00
parent 0dd343cc06
commit b45c82e5fa
15 changed files with 542 additions and 371 deletions

View File

@ -18,13 +18,13 @@
<many-to-many class="VideoGameQuotes.Api.Category" column="category_id" /> <many-to-many class="VideoGameQuotes.Api.Category" column="category_id" />
</set> </set>
<set access="nosetter.camelcase" name="Votes" table="vote" cascade="save-update"> <set access="nosetter.camelcase" name="Votes" table="vote" inverse="true" cascade="all-delete-orphan">
<key column="quote_id" /> <key column="quote_id" not-null="true" />
<one-to-many class="VideoGameQuotes.Api.Vote" /> <one-to-many class="VideoGameQuotes.Api.Vote" />
</set> </set>
<set access="nosetter.camelcase" name="Flags" table="quote_flag" cascade="save-update"> <set access="nosetter.camelcase" name="Flags" inverse="true" table="quote_flag" cascade="all-delete-orphan">
<key column="quote_id" /> <key not-null="true" column="quote_id" />
<one-to-many class="VideoGameQuotes.Api.QuoteFlag" /> <one-to-many class="VideoGameQuotes.Api.QuoteFlag" />
</set> </set>
</class> </class>

View File

@ -47,6 +47,11 @@ namespace VideoGameQuotes.Api {
return this; return this;
} }
public virtual Quote RemoveFlag(QuoteFlag flag) {
flags.Remove(flag);
return this;
}
public virtual void ClearFlags() { public virtual void ClearFlags() {
flags.Clear(); flags.Clear();
} }
@ -131,7 +136,7 @@ namespace VideoGameQuotes.Api {
return quote.Text; return quote.Text;
} }
//ellipses ftw //ellipsis ftw
return quote.Text.Substring(0, 100) + Char.ConvertFromUtf32(0x2026); return quote.Text.Substring(0, 100) + Char.ConvertFromUtf32(0x2026);
} }

View File

@ -1,5 +1,4 @@
using System.Linq; using System.Web.Mvc;
using System.Web.Mvc;
using Portoa.Web; using Portoa.Web;
using Portoa.Web.ErrorHandling; using Portoa.Web.ErrorHandling;
using VideoGameQuotes.Api; using VideoGameQuotes.Api;
@ -8,9 +7,6 @@ using VideoGameQuotes.Web.Security;
using VideoGameQuotes.Web.Services; using VideoGameQuotes.Web.Services;
namespace VideoGameQuotes.Web.Controllers { namespace VideoGameQuotes.Web.Controllers {
[IsValidUser(Group = UserGroup.Admin)] [IsValidUser(Group = UserGroup.Admin)]
public class AdminController : Controller { public class AdminController : Controller {
private readonly ICurrentUserProvider userProvider; private readonly ICurrentUserProvider userProvider;

View File

@ -5,6 +5,7 @@ using System.Net;
using System.Web.Mvc; using System.Web.Mvc;
using JetBrains.Annotations; using JetBrains.Annotations;
using Portoa.Persistence; using Portoa.Persistence;
using Portoa.Validation.DataAnnotations;
using Portoa.Web.Controllers; using Portoa.Web.Controllers;
using Portoa.Web.Results; using Portoa.Web.Results;
using VideoGameQuotes.Api; using VideoGameQuotes.Api;
@ -47,7 +48,7 @@ namespace VideoGameQuotes.Web.Controllers {
quote.AddFlag(model.Comment, model.FlagType, currentUserProvider.CurrentUser); quote.AddFlag(model.Comment, model.FlagType, currentUserProvider.CurrentUser);
quoteService.SaveQuote(quote); quoteService.SaveQuote(quote);
return Json(this.CreateJsonResponse()); return Json(this.CreateJsonResponse());
} catch { } catch (Exception e) {
return Json(this.CreateJsonErrorResponse("Unable to create your report")); return Json(this.CreateJsonErrorResponse("Unable to create your report"));
} }
} }
@ -105,26 +106,68 @@ namespace VideoGameQuotes.Web.Controllers {
return RedirectToAction("Quote", new { id = quote.Id, text = quote.GetUrlFriendlyText() }); return RedirectToAction("Quote", new { id = quote.Id, text = quote.GetUrlFriendlyText() });
} }
[HttpPost, IsValidUser(Group = UserGroup.Admin)]
public ActionResult DismissFlag([GreaterThanZero]int quoteId, [GreaterThanZero]int flagId) {
if (!ModelState.IsValid) {
return Json(this.CreateJsonErrorResponse("quote/flag is invalid"));
}
try {
var quote = quoteService.GetQuote(quoteId);
var flag = quote.Flags.FirstOrDefault(f => f.Id == flagId);
if (flag == null) {
return Json(this.CreateJsonErrorResponse(string.Format("Flag {0} not found for quote {1}", flagId, quote.Id)));
}
quote.RemoveFlag(flag);
quoteService.SaveQuote(quote);
return Json(this.CreateJsonResponse());
} catch (Exception e) {
return Json(this.CreateJsonErrorResponse(e));
}
}
[HttpGet, IsValidUser(Group = UserGroup.Admin)]
public ActionResult Edit(int id) {
try {
var model = new EditQuoteModel(quoteService.GetQuote(id));
ResetEditQuoteModel(model);
return View(model);
} catch (EntityNotFoundException) {
return View("QuoteNotFound");
}
}
[HttpPost, IsValidUser(Group = UserGroup.Admin)]
public ActionResult Edit(EditQuoteModel model) {
return CreateOrEditQuote(model, "Edit");
}
[IsValidUser] [IsValidUser]
public ActionResult Submit() { public ActionResult Submit() {
var model = new QuoteSubmitModel(); var model = new EditQuoteModel();
ResetModel(model); ResetEditQuoteModel(model);
return View(model); return View(model);
} }
[HttpPost, IsValidUser] [HttpPost, IsValidUser]
public ActionResult Submit(QuoteSubmitModel model) { public ActionResult Submit(EditQuoteModel model) {
return CreateOrEditQuote(model, "Submit");
}
private ActionResult CreateOrEditQuote(EditQuoteModel model, string viewName) {
if (!ModelState.IsValid) { if (!ModelState.IsValid) {
ResetModel(model); ResetEditQuoteModel(model);
return View(model); return View(viewName, model);
} }
try { try {
var quote = new Quote { var quote = model.QuoteId > 0
Creator = currentUserProvider.CurrentUser, ? quoteService.GetQuote(model.QuoteId)
Game = GetGameFromModelData(model), : new Quote {Creator = currentUserProvider.CurrentUser};
Text = model.QuoteText
}; quote.Game = GetGameFromModelData(model);
quote.Text = model.QuoteText;
if (model.CategoryIds != null && model.CategoryIds.Count > 0) { if (model.CategoryIds != null && model.CategoryIds.Count > 0) {
quote.ClearCategories(); quote.ClearCategories();
@ -134,22 +177,22 @@ namespace VideoGameQuotes.Web.Controllers {
} }
if (quote.Game == null) { if (quote.Game == null) {
ResetModel(model); ResetEditQuoteModel(model);
return View(model); return View(viewName, model);
} }
quote = quoteService.SaveQuote(quote); quote = quoteService.SaveQuote(quote);
return RedirectToAction("Quote", new { id = quote.Id, text = quote.GetUrlFriendlyText() }); return RedirectToAction("Quote", new {id = quote.Id, text = quote.GetUrlFriendlyText()});
} catch (Exception e) { } catch (Exception e) {
ModelState.AddModelError("save", e.Message); ModelState.AddModelError("save", e.Message);
ResetModel(model); ResetEditQuoteModel(model);
return View(model); return View(viewName, model);
} }
} }
[CanBeNull] [CanBeNull]
private Game GetGameFromModelData(QuoteSubmitModel model) { private Game GetGameFromModelData(EditQuoteModel model) {
if (model.GameId > 0) { if (model.GameId > 0) {
try { try {
return quoteService.GetGame(model.GameId); return quoteService.GetGame(model.GameId);
@ -213,11 +256,11 @@ namespace VideoGameQuotes.Web.Controllers {
return game; return game;
} }
private void ResetModel(QuoteSubmitModel model) { private void ResetEditQuoteModel(EditQuoteModel model) {
model.AllGames = quoteService.GetAllGames().OrderBy(game => game.Name); model.AllGames = quoteService.GetAllGames().OrderBy(game => game.Name);
model.AllSystems = quoteService.GetAllSystems().OrderBy(system => system.ReleaseDate); model.AllSystems = quoteService.GetAllSystems().OrderBy(system => system.ReleaseDate);
model.AllPublishers = quoteService.GetAllPublishers(); model.AllPublishers = quoteService.GetAllPublishers().OrderBy(publisher => publisher.Name);
model.AllCategories = quoteService.GetAllCategories(); model.AllCategories = quoteService.GetAllCategories().OrderBy(category => category.Name);
} }
public ActionResult Quote(int id) { public ActionResult Quote(int id) {
@ -233,7 +276,21 @@ namespace VideoGameQuotes.Web.Controllers {
} }
} }
[HttpPost] [IsValidUser(Group = UserGroup.Admin)]
public ActionResult Flags(int id) {
try {
var model = new QuoteModel {
Quote = quoteService.GetQuote(id),
User = currentUserProvider.CurrentUser
};
return View(model);
} catch (EntityNotFoundException) {
return new StatusOverrideResult(View("QuoteNotFound")) { StatusCode = HttpStatusCode.NotFound };
}
}
[HttpPost, IsValidUser]
public JsonResult CreateCategory(Category category) { public JsonResult CreateCategory(Category category) {
try { try {
category = quoteService.SaveCategory(category); category = quoteService.SaveCategory(category);

View File

@ -75,7 +75,9 @@ namespace VideoGameQuotes.Web {
routes.MapRoute("best", "best/{start}-{end}/", new { controller = "Quote", action = "Best" }, new { start = @"\d+", end = @"\d+" }); routes.MapRoute("best", "best/{start}-{end}/", new { controller = "Quote", action = "Best" }, new { start = @"\d+", end = @"\d+" });
routes.MapRoute("browse", "browse/{*qualifiers}", new { controller = "Quote", action = "Browse" }); routes.MapRoute("browse", "browse/{*qualifiers}", new { controller = "Quote", action = "Browse" });
routes.MapRoute("search", "search/{*searchQuery}", new { controller = "Quote", action = "Search" }); routes.MapRoute("search", "search/{*searchQuery}", new { controller = "Quote", action = "Search" });
routes.MapRoute("quote-task", "{action}/{id}", new { controller = "Quote" }, new { action = "edit|flags", id = @"\d+" });
routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|recent|random|best|vote|report" }); routes.MapRoute("quote", "{action}", new { controller = "Quote" }, new { action = "submit|recent|random|best|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("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("create-category", "category/create", new { controller = "Quote", action = "CreateCategory" });
routes.MapRoute("default", "{controller}", new { controller = "home", action = "index" }); routes.MapRoute("default", "{controller}", new { controller = "home", action = "index" });

View File

@ -3,12 +3,34 @@ using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Web.Mvc; using System.Web.Mvc;
using Portoa.Web.Util;
using VideoGameQuotes.Api; using VideoGameQuotes.Api;
using VideoGameQuotes.Web.Validation; using VideoGameQuotes.Web.Validation;
namespace VideoGameQuotes.Web.Models { namespace VideoGameQuotes.Web.Models {
public class QuoteSubmitModel {
public class EditQuoteModel {
public EditQuoteModel() {
ControllerName = "Quote";
ActionName = "Submit";
}
public EditQuoteModel(Quote quote) : this() {
QuoteId = quote.Id;
Flags = quote.Flags.ToList();
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";
}
public string ActionName { get; set; }
public string ControllerName { get; set; }
public int QuoteId { get; set; }
public List<QuoteFlag> Flags { get; set; }
[NonEmptyText(ErrorMessage = "Quote text must be non-empty"), DisplayName("Quote")] [NonEmptyText(ErrorMessage = "Quote text must be non-empty"), DisplayName("Quote")]
public string QuoteText { get; set; } public string QuoteText { get; set; }
[DisplayName("Categories")] [DisplayName("Categories")]
@ -81,45 +103,52 @@ namespace VideoGameQuotes.Web.Models {
return table.ToString(TagRenderMode.Normal); return table.ToString(TagRenderMode.Normal);
} }
public string MakePublisherTable(HtmlHelper<QuoteSubmitModel> html, int cellsPerRow = 8) { public string MakePublisherTable(HtmlHelper<EditQuoteModel> html, int cellsPerRow = 8) {
return MakeTable( return MakeTable(
"publisher-checkbox-table", "publisher-checkbox-table",
cellsPerRow, cellsPerRow,
AllPublishers, AllPublishers,
publisher => CreateCheckbox(html, "PublisherIds", publisher.Id, "publisher_" + publisher.Id, publisher.Name) publisher => CreateCheckbox("PublisherIds", publisher.Id, "publisher_" + publisher.Id, publisher.Name)
); );
} }
public string MakeSystemTable(HtmlHelper<QuoteSubmitModel> html, int cellsPerRow = 8) { public string MakeSystemTable(HtmlHelper<EditQuoteModel> html, int cellsPerRow = 8) {
return MakeTable( return MakeTable(
"system-checkbox-table", "system-checkbox-table",
cellsPerRow, cellsPerRow,
AllSystems, AllSystems,
system => CreateCheckbox(html, "SystemIds", system.Id, "system_" + system.Id, system.Abbreviation) system => CreateCheckbox("SystemIds", system.Id, "system_" + system.Id, system.Abbreviation)
); );
} }
public string MakeCategoryTable(HtmlHelper<QuoteSubmitModel> html, int cellsPerRow = 5) { public string MakeCategoryTable(HtmlHelper<EditQuoteModel> html, int cellsPerRow = 5) {
var categoryIds = CategoryIds ?? new List<int>();
return MakeTable( return MakeTable(
"category-checkbox-table", "category-checkbox-table",
cellsPerRow, cellsPerRow,
AllCategories, AllCategories,
category => CreateCheckbox(html, "CategoryIds", category.Id, "category_" + category.Id, category.Name) category => CreateCheckbox("CategoryIds", category.Id, "category_" + category.Id, category.Name, categoryIds.Contains(category.Id))
); );
} }
public string MakeRegionTable(HtmlHelper<QuoteSubmitModel> html, int cellsPerRow = 8) { public string MakeRegionTable(int cellsPerRow = 8) {
return MakeTable( return MakeTable(
"region-checkbox-table", "region-checkbox-table",
cellsPerRow, cellsPerRow,
(IEnumerable<Region>)Enum.GetValues(typeof(Region)), (IEnumerable<Region>)Enum.GetValues(typeof(Region)),
region => CreateCheckbox(html, "GameRegions", region, "region_" + (int)region, region.ToString()) region => CreateCheckbox("GameRegions", region, "region_" + (int)region, region.ToString())
); );
} }
private static string CreateCheckbox(HtmlHelper<QuoteSubmitModel> html, string name, object value, string id, string labelText) { private static string CreateCheckbox(string name, object value, string id, string labelText, bool isChecked = false) {
return string.Format("<input type=\"checkbox\" name=\"{0}\" value=\"{1}\" id=\"{2}\"/>", name, value, id) return string.Format(
+ html.Label(labelText, id); "<input type=\"checkbox\" name=\"{0}\" value=\"{1}\" id=\"{2}\"{4}/><label for=\"{2}\">{3}</label>",
name,
value,
id,
labelText,
isChecked ? " checked=\"checked\"" : ""
);
} }
} }
} }

View File

@ -17,7 +17,7 @@ namespace VideoGameQuotes.Web.Security {
public override void OnActionExecuting(ActionExecutingContext filterContext) { public override void OnActionExecuting(ActionExecutingContext filterContext) {
var allowedToExecuteAction = UserProvider != null var allowedToExecuteAction = UserProvider != null
&& UserProvider.CurrentUser != null && UserProvider.CurrentUser != null
&& UserProvider.CurrentUser.Group >= Group; && UserProvider.CurrentUser.Group >= Group;
if (!allowedToExecuteAction) { if (!allowedToExecuteAction) {
filterContext.Result = new ErrorViewResult { filterContext.Result = new ErrorViewResult {

View File

@ -111,7 +111,7 @@
<SubType>ASPXCodeBehind</SubType> <SubType>ASPXCodeBehind</SubType>
</Compile> </Compile>
<Compile Include="Models\ContactModel.cs" /> <Compile Include="Models\ContactModel.cs" />
<Compile Include="Models\QuoteSubmitModel.cs" /> <Compile Include="Models\EditQuoteModel.cs" />
<Compile Include="Security\SessionBasedUserProvider.cs" /> <Compile Include="Security\SessionBasedUserProvider.cs" />
<Compile Include="Global.asax.cs"> <Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon> <DependentUpon>Global.asax</DependentUpon>
@ -141,6 +141,9 @@
<Content Include="Views\Home\ContactSuccess.aspx" /> <Content Include="Views\Home\ContactSuccess.aspx" />
<Content Include="Views\Quote\BadPaging.aspx" /> <Content Include="Views\Quote\BadPaging.aspx" />
<Content Include="Views\Quote\Best.aspx" /> <Content Include="Views\Quote\Best.aspx" />
<Content Include="Views\Quote\Edit.aspx" />
<Content Include="Views\Quote\EditQuoteForm.ascx" />
<Content Include="Views\Quote\Flags.aspx" />
<Content Include="Views\Quote\QualifiedBrowse.aspx" /> <Content Include="Views\Quote\QualifiedBrowse.aspx" />
<Content Include="Views\Quote\DefaultBrowse.aspx" /> <Content Include="Views\Quote\DefaultBrowse.aspx" />
<Content Include="Views\Quote\NoQuotes.aspx" /> <Content Include="Views\Quote\NoQuotes.aspx" />

View File

@ -0,0 +1,8 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.EditQuoteModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Edit</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<h2>Edit Quote</h2>
<%= Html.ValidationSummary("Some errors occurred while trying to save your edit:") %>
<% Html.RenderPartial("EditQuoteForm", Model); %>
</asp:Content>

View File

@ -0,0 +1,139 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<VideoGameQuotes.Web.Models.EditQuoteModel>" %>
<%@ Import Namespace="Portoa.Web.Util" %>
<div id="edit-quote-form">
<% using (Html.BeginForm(Model.ActionName, Model.ControllerName)) { %>
<%= Html.HiddenFor(model => model.QuoteId, new { id = "quote-id" })%>
<p>
<span id="game-select">
<%= Html.Label("Game", "GameId") %>
<br />
<%= Html.DropDownListFor(model => model.GameId, Model.GetGameList()) %>
</span>
<a href="#" id="create-game-link">Create new game</a>
</p>
<div id="create-game-form">
<fieldset>
<legend>Create new game</legend>
<p>
<%= Html.LabelFor(model => model.GameName) %>
<br />
<%= Html.TextBoxFor(model => model.GameName) %>
</p>
<p>
<%= Html.LabelFor(model => model.GameWebsite) %> <small>(link to Wikipedia page or something)</small>
<br />
<%= Html.TextBoxFor(model => model.GameWebsite) %>
</p>
<p>
<%= Html.LabelFor(model => model.GameRegions) %>
<br />
<%= Model.MakeRegionTable() %>
</p>
<p>
<span id="system-select">
<%= Html.LabelFor(model => model.SystemIds) %>
<br />
<%= Model.MakeSystemTable(Html) %>
</span>
<a href="#" id="create-system-link">Create new system</a>
</p>
<div id="create-system-form">
<fieldset>
<legend>Create new system</legend>
<p>
<%= Html.LabelFor(model => model.SystemName) %>
<br />
<%= Html.TextBoxFor(model => model.SystemName) %>
</p>
<p>
<%= Html.LabelFor(model => model.SystemAbbreviation) %>
<br />
<%= Html.TextBoxFor(model => model.SystemAbbreviation) %>
</p>
<p>
<%= Html.LabelFor(model => model.SystemReleaseDate) %>
<br />
<%= Html.TextBox("SystemReleaseDate", DateTime.UtcNow.ToString("yyyy-MM-dd")) %>
</p>
</fieldset>
</div>
<p>
<span id="publisher-select">
<%= Html.LabelFor(model => model.PublisherIds) %>
<br />
<%= Model.MakePublisherTable(Html) %>
</span>
<a href="#" id="create-publisher-link">Create new publisher</a>
</p>
<div id="create-publisher-form">
<fieldset>
<legend>Create new publisher</legend>
<p>
<%= Html.LabelFor(model => model.PublisherName) %>
<br />
<%= Html.TextBoxFor(model => model.PublisherName) %>
</p>
<p>
<%= Html.LabelFor(model => model.PublisherWebsite) %>
<br />
<%= Html.TextBoxFor(model => model.PublisherWebsite) %>
</p>
</fieldset>
</div>
</fieldset>
</div>
<p>
<%= Html.LabelFor(model => model.QuoteText) %>
<br />
<%= Html.TextAreaFor(model => model.QuoteText) %>
</p>
<div>
<p>
<%= Html.LabelFor(model => model.CategoryIds) %>
</p>
<%= Model.MakeCategoryTable(Html) %>
<a href="#" id="create-category-link">Create new category</a>
</div>
<% if (Model.QuoteId > 0) { %>
<div id="quote-flags-container">
<% foreach (var flag in Model.Flags) { %>
<div class="quote-flag">
<input type="hidden" class="quote-flag-id" value="<%= flag.Id %>" />
<p>
Flagged as <strong><%: flag.Type %></strong> on <em><%= flag.Created %></em> by
<strong><%: flag.User.Username ?? flag.User.IpAddress %></strong>.
</p>
<p><%: flag.Comment %></p>
<p><a href="#" class="dismiss-flag-link">dismiss</a></p>
</div>
<% } %>
</div>
<% } %>
<hr />
<%= Html.Submit(Model.QuoteId > 0 ? "Save" : "Submit Quote") %>
<% } %>
</div>

View File

@ -0,0 +1,5 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.QuoteModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Flags</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
</asp:Content>

View File

@ -1,236 +1,8 @@
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.QuoteSubmitModel>" MasterPageFile="~/Views/Shared/Site.Master" %> <%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.EditQuoteModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
<%@ Import Namespace="Portoa.Web.Util" %>
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Submit New Quote</asp:Content> <asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Submit New Quote</asp:Content>
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent"> <asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
<h2>Submit New Quote</h2> <h2>Submit New Quote</h2>
<%= Html.ValidationSummary("Some errors occurred while trying to submit your quote:") %> <%= Html.ValidationSummary("Some errors occurred while trying to submit your quote:") %>
<% Html.RenderPartial("EditQuoteForm", Model); %>
<div id="create-quote-form">
<% using (Html.BeginForm("Submit", "Quote")) { %>
<p>
<span id="game-select">
<%= Html.Label("Game", "GameId") %>
<br />
<%= Html.DropDownListFor(model => model.GameId, Model.GetGameList()) %>
</span>
<a href="#" id="create-game-link">Create new game</a>
</p>
<div id="create-game-form">
<fieldset>
<legend>Create new game</legend>
<p>
<%= Html.LabelFor(model => model.GameName) %>
<br />
<%= Html.TextBoxFor(model => model.GameName) %>
</p>
<p>
<%= Html.LabelFor(model => model.GameWebsite) %> <small>(link to Wikipedia page or something)</small>
<br />
<%= Html.TextBoxFor(model => model.GameWebsite) %>
</p>
<p>
<%= Html.LabelFor(model => model.GameRegions) %>
<br />
<%= Model.MakeRegionTable(Html) %>
</p>
<p>
<span id="system-select">
<%= Html.LabelFor(model => model.SystemIds) %>
<br />
<%= Model.MakeSystemTable(Html) %>
</span>
<a href="#" id="create-system-link">Create new system</a>
</p>
<div id="create-system-form">
<fieldset>
<legend>Create new system</legend>
<p>
<%= Html.LabelFor(model => model.SystemName) %>
<br />
<%= Html.TextBoxFor(model => model.SystemName) %>
</p>
<p>
<%= Html.LabelFor(model => model.SystemAbbreviation) %>
<br />
<%= Html.TextBoxFor(model => model.SystemAbbreviation) %>
</p>
<p>
<%= Html.LabelFor(model => model.SystemReleaseDate) %>
<br />
<%= Html.TextBox("SystemReleaseDate", DateTime.UtcNow.ToString("yyyy-MM-dd")) %>
</p>
</fieldset>
</div>
<p>
<span id="publisher-select">
<%= Html.LabelFor(model => model.PublisherIds) %>
<br />
<%= Model.MakePublisherTable(Html) %>
</span>
<a href="#" id="create-publisher-link">Create new publisher</a>
</p>
<div id="create-publisher-form">
<fieldset>
<legend>Create new publisher</legend>
<p>
<%= Html.LabelFor(model => model.PublisherName) %>
<br />
<%= Html.TextBoxFor(model => model.PublisherName) %>
</p>
<p>
<%= Html.LabelFor(model => model.PublisherWebsite) %>
<br />
<%= Html.TextBoxFor(model => model.PublisherWebsite) %>
</p>
</fieldset>
</div>
</fieldset>
</div>
<p>
<%= Html.LabelFor(model => model.QuoteText) %>
<br />
<%= Html.TextAreaFor(model => model.QuoteText) %>
</p>
<div>
<p>
<%= Html.LabelFor(model => model.CategoryIds) %>
</p>
<%= Model.MakeCategoryTable(Html) %>
<a href="#" id="create-category-link">Create new category</a>
</div>
<hr />
<%= Html.Submit("Submit Quote") %>
<% } %>
</div>
</asp:Content>
<asp:Content runat="server" ID="DeferrableScripts" ContentPlaceHolderID="DeferrableScripts">
<script type="text/javascript">//<![CDATA[
$(document).ready(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");
}
$form.toggle();
$("#game-select").toggle();
return false;
});
$("#create-system-link").click(function() {
var $form = $("#create-system-form");
if ($form.is(":visible")) {
$("#create-system-link").text("Create new system");
} else {
$("#create-system-link").text("Use existing system");
}
$form.toggle();
$("#system-select").toggle();
return false;
});
$("#create-publisher-link").click(function() {
var $form = $("#create-publisher-form");
if ($form.is(":visible")) {
$("#create-publisher-link").text("Create new publisher");
} else {
$("#create-publisher-link").text("Use existing publisher");
}
$form.toggle();
$("#publisher-select").toggle();
return false;
});
$("#create-category-link").click(function() {
var $table = $("#category-checkbox-table");
var $row = $table.find("tr:last");
if ($row.find("#new-category-name").length === 0) {
var $cell = $("<td/>");
if ($row.find("td").length === 8) {
$row = $("<tr/>");
$table.append($row);
}
$row.append($cell);
var $input = $("<input/>").attr("id", "new-category-name").attr("type", "text").val("Category name");
$input.bind("keypress", function(e) {
if (e.which === 13) {
e.preventDefault(); //make sure the parent form doesn't get submitted
$input.attr("disabled", "disabled");
$.ajax("/category/create", {
data: { Name: $input.val() },
type: "POST",
success: function(data, status, $xhr) {
if (data.Error !== null) {
alert("An error occurred: " + data.Error);
return;
}
//add category checkbox to table
var $checkbox = $("<input/>")
.attr({
"id": "category_" + data.Data.categoryId,
"type": "checkbox",
"name": "CategoryIds",
"checked": "checked"
}).val(data.Data.categoryId);
var $label = $("<label/>")
.attr("for", $checkbox.attr("id"))
.text(data.Data.categoryName);
$input.before($checkbox).before($label);
},
error: function($xhr, status, error) {
alert("An error occurred");
},
complete: function() {
$input.remove();
}
});
}
});
$cell.append($input);
$input.select();
$(this).text("Cancel new category");
} else {
$row.find("td:last").remove();
$(this).text("Create new category");
}
return false;
});
});
//]]></script>
</asp:Content> </asp:Content>

View File

@ -1,4 +1,5 @@
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<VideoGameQuotes.Web.Models.QuoteModel>" %> <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<VideoGameQuotes.Web.Models.QuoteModel>" %>
<%@ Import Namespace="Portoa.Util" %>
<%@ Import Namespace="VideoGameQuotes.Api" %> <%@ Import Namespace="VideoGameQuotes.Api" %>
<div class="quote-container"> <div class="quote-container">
@ -24,15 +25,25 @@
</div> </div>
<div class="clearfix"> <div class="clearfix">
<a class="quote-report-link" href="#" title="report this quote as inaccurate, fake, spam, duplicate, etc.">report</a> <div class="quote-links">
<a class="quote-report-link" href="#" title="report this quote as inaccurate, fake, spam, duplicate, etc.">report</a>
<% if (Model.User != null && Model.User.Group >= UserGroup.Admin) { %>
| <%= Html.ActionLink("flags", "flags", "quote", new { id = Model.Quote.Id }, null) %>
| <%= Html.ActionLink("edit", "edit", "quote", new { id = Model.Quote.Id }, null) %>
<% } %>
</div>
</div> </div>
<div class="quote-details"> <div class="quote-details">
<dl> <dl>
<dt>Game</dt> <dt>Game</dt>
<dd><%= Html.ActionLink(Model.Quote.Game.Name, "browse", "Quote", new { game = Model.Quote.Game.Id }, null) %></dd> <dd><%= Html.ActionLink(Model.Quote.Game.Name, "browse", "Quote", new { qualifiers = "game/" + Model.Quote.Game.Id }, null) %></dd>
<dt>Added</dt> <dt>Added</dt>
<dd><%: Model.Quote.GetHumanReadableTimeSinceCreated() %></dd> <dd><%: Model.Quote.GetHumanReadableTimeSinceCreated() %></dd>
<dt>Categories</dt>
<dd>
<%= Model.Quote.Categories.Implode(category => Html.ActionLink(category.Name, "browse", "Quote", new { qualifiers = "category/" + category.Id }, null).ToString(), ", ")%>
</dd>
</dl> </dl>
</div> </div>
</div> </div>

View File

@ -30,7 +30,7 @@
font-size: 20px; font-size: 20px;
} }
.quote-report-link { .quote-links {
float: right; float: right;
} }

View File

@ -38,17 +38,17 @@
$.vgquotes.refresh = refresh; $.vgquotes.refresh = refresh;
}()); }());
$(document).ready(function() { var setupSearch = function() {
(function(){ var submitSearch = function() {
var submitSearch = function() { var searchQuery = $.trim($("#search-query").val());
var searchQuery = $.trim($("#search-query").val()); if (searchQuery.length > 0) {
if (searchQuery.length > 0) { window.location = "/search/" + searchQuery;
window.location = "/search/" + searchQuery; }
}
return false; return false;
}; };
$(document).ready(function() {
$("#search-query").keypress(function(e) { $("#search-query").keypress(function(e) {
if (e.which === 13) { if (e.which === 13) {
submitSearch(); submitSearch();
@ -56,12 +56,10 @@
}); });
$("#search-submit").click(submitSearch); $("#search-submit").click(submitSearch);
}()); });
};
var getQuoteId = function($container) {
return $container.find("input.quote-id").val();
};
var setupVoting = function() {
var voting = false; var voting = false;
$(".vote-for, .vote-against").live("click", function() { $(".vote-for, .vote-against").live("click", function() {
if (voting) { if (voting) {
@ -73,7 +71,7 @@
var $votingLink = $(this); var $votingLink = $(this);
var $container = $votingLink.parents(".quote-container"); var $container = $votingLink.parents(".quote-container");
var direction = $votingLink.hasClass("vote-for") ? 1 : 0; var direction = $votingLink.hasClass("vote-for") ? 1 : 0;
var quoteId = getQuoteId($container); var quoteId = $container.find("input.quote-id").val();
$.ajax("/vote", { $.ajax("/vote", {
type: "POST", type: "POST",
data: { data: {
@ -118,15 +116,16 @@
return false; return false;
}); });
};
//report link var setupReportLink = function() {
$(".quote-report-link").click(function() { $(".quote-report-link").click(function() {
if ($(".report-dialog").length > 0) { if ($(".report-dialog").length > 0) {
return false; return false;
} }
var $link = $(this); var $link = $(this);
var $container = $link.parents(".quote-container"); var $container = $link.parents(".quote-container");
var quoteId = getQuoteId($container); var quoteId = $container.find("input.quote-id").val();
var $row = $("<tr/>"); var $row = $("<tr/>");
var flagTypes = [ [1, "Inaccurate"], [2, "Duplicate"], [3, "Spam"], [4, "Fake"], [0, "Other"] ]; var flagTypes = [ [1, "Inaccurate"], [2, "Duplicate"], [3, "Spam"], [4, "Fake"], [0, "Other"] ];
@ -177,50 +176,195 @@
return false; return false;
}); });
};
//login stuff var setupLogin = function() {
(function(){ var showLoginForm = function() {
var showLoginForm = function() { var $dialog = $("#login-dialog");
var $dialog = $("#login-dialog"); if ($dialog.length > 0) {
if ($dialog.length > 0) { $dialog.remove();
$dialog.remove(); return false;
return false; }
}
var $usernameInput = $("<input/>").attr({ type: "text", id: "login-username" }); var $usernameInput = $("<input/>").attr({ type: "text", id: "login-username" });
var $passwordInput = $("<input/>").attr({ type: "password", id: "login-password" }); var $passwordInput = $("<input/>").attr({ type: "password", id: "login-password" });
var $submit = $("<input/>").attr("type", "submit").css("display", "none"); var $submit = $("<input/>").attr("type", "submit").css("display", "none");
var $form = $("<form/>").attr({ method: "post", action: "/login" }).submit(function() { var $form = $("<form/>").attr({ method: "post", action: "/login" }).submit(function() {
$.ajax("/login", { $.ajax("/login", {
type: "POST", type: "POST",
data: { username: $usernameInput.val(), password: $passwordInput.val() }, data: { username: $usernameInput.val(), password: $passwordInput.val() },
success: function(data, status, $xhr) { success: function(data, status, $xhr) {
if (data.Error !== null) { if (data.Error !== null) {
alert(data.Error); alert(data.Error);
return; return;
}
$.vgquotes.refresh();
} }
});
return false; $.vgquotes.refresh();
}
}); });
var $dialog = $("<div/>").addClass("dialog").attr("id", "login-dialog");
$form.append($usernameInput).append($passwordInput).append($submit);
$dialog.append($form);
$("body").append($dialog);
$dialog.center();
$usernameInput.focus();
return false; return false;
}; });
var $dialog = $("<div/>").addClass("dialog").attr("id", "login-dialog");
$form.append($usernameInput).append($passwordInput).append($submit);
$dialog.append($form);
$("body").append($dialog);
$dialog.center();
$usernameInput.focus();
return false;
};
$(document).ready(function() {
$("#login-link").click(showLoginForm); $("#login-link").click(showLoginForm);
}()); });
}); };
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");
}
$form.toggle();
$("#game-select").toggle();
return false;
});
$("#create-system-link").click(function() {
var $form = $("#create-system-form");
if ($form.is(":visible")) {
$("#create-system-link").text("Create new system");
} else {
$("#create-system-link").text("Use existing system");
}
$form.toggle();
$("#system-select").toggle();
return false;
});
$("#create-publisher-link").click(function() {
var $form = $("#create-publisher-form");
if ($form.is(":visible")) {
$("#create-publisher-link").text("Create new publisher");
} else {
$("#create-publisher-link").text("Use existing publisher");
}
$form.toggle();
$("#publisher-select").toggle();
return false;
});
$(".dismiss-flag-link").click(function() {
var $link = $(this);
var $container = $link.parents(".quote-flag");
var flagId = $container.find(".quote-flag-id").val();
var quoteId = $("#quote-id").val();
$.ajax("/dismiss-flag", {
type: "POST",
data: { quoteId: quoteId, flagId : flagId },
success: function(data, status, $xhr) {
if (data.Error !== null) {
alert(data.Error);
return;
}
$container.remove();
}
});
return false;
});
$("#create-category-link").click(function() {
var $table = $("#category-checkbox-table");
var $row = $table.find("tr:last");
if ($row.find("#new-category-name").length === 0) {
var $cell = $("<td/>");
if ($row.find("td").length === 8) {
$row = $("<tr/>");
$table.append($row);
}
$row.append($cell);
var $input = $("<input/>").attr("id", "new-category-name").attr("type", "text").val("Category name");
$input.bind("keypress", function(e) {
if (e.which === 13) {
e.preventDefault(); //make sure the parent form doesn't get submitted
$input.attr("disabled", "disabled");
$.ajax("/category/create", {
data: { Name: $input.val() },
type: "POST",
success: function(data, status, $xhr) {
if (data.Error !== null) {
alert("An error occurred: " + data.Error);
return;
}
//add category checkbox to table
var $checkbox = $("<input/>")
.attr({
"id": "category_" + data.Data.categoryId,
"type": "checkbox",
"name": "CategoryIds",
"checked": "checked"
}).val(data.Data.categoryId);
var $label = $("<label/>")
.attr("for", $checkbox.attr("id"))
.text(data.Data.categoryName);
$input.before($checkbox).before($label);
},
error: function($xhr, status, error) {
alert("An error occurred");
},
complete: function() {
$input.remove();
}
});
}
});
$cell.append($input);
$input.select();
$(this).text("Cancel new category");
} else {
$row.find("td:last").remove();
$(this).text("Create new category");
}
return false;
});
};
(function(){
setupLogin();
setupSearch();
$(document).ready(function() {
setupReportLink();
setupVoting();
if ($("#edit-quote-form").length > 0) {
setupQuoteForm();
}
});
}());
}(jQuery, window)); }(jQuery, window));