user stuff, restyled SingleQuote to not be so hideous
This commit is contained in:
parent
8522cf50b0
commit
c4b93c46fa
@ -1,15 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace VideoGameQuotes.Api {
|
||||
/// <summary>
|
||||
/// Provides an interface for identifying the current user
|
||||
/// </summary>
|
||||
public interface ICurrentUserProvider {
|
||||
/// <summary>
|
||||
/// Returns the user currently performing actions on the site, or <c>null</c>
|
||||
/// if the user cannot be identified
|
||||
/// </summary>
|
||||
[CanBeNull]
|
||||
User CurrentUser { get; }
|
||||
}
|
||||
}
|
@ -1,12 +1,22 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Portoa.Persistence;
|
||||
|
||||
namespace VideoGameQuotes.Api.Persistence {
|
||||
|
||||
public interface IUserService {
|
||||
User Save(User user);
|
||||
[CanBeNull]
|
||||
User FindByUsername(string name);
|
||||
[CanBeNull]
|
||||
User FindByIpAddress(string ipAddress);
|
||||
void Delete(int id);
|
||||
void Ban(User user);
|
||||
[CanBeNull]
|
||||
User FindByUsernameOrIp(string usernameOrIp);
|
||||
[NotNull]
|
||||
User FindById(int id);
|
||||
}
|
||||
|
||||
public class UserService : IUserService {
|
||||
@ -30,5 +40,27 @@ namespace VideoGameQuotes.Api.Persistence {
|
||||
public User FindByIpAddress(string ipAddress) {
|
||||
return repository.FindByIpAddress(ipAddress);
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public void Delete(int id) {
|
||||
repository.Delete(id);
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public void Ban(User user) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public User FindByUsernameOrIp(string usernameOrIp) {
|
||||
return repository
|
||||
.Records
|
||||
.FirstOrDefault(user => user.Username == usernameOrIp || user.IpAddress == usernameOrIp);
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public User FindById(int id) {
|
||||
return repository.FindById(id);
|
||||
}
|
||||
}
|
||||
}
|
@ -24,4 +24,10 @@ namespace VideoGameQuotes.Api {
|
||||
return passwordProtector.VerifyPassword(potentialPassword);
|
||||
}
|
||||
}
|
||||
|
||||
public static class UserExtensions {
|
||||
public static string GetUsernameOrIp(this User user) {
|
||||
return user.Username ?? user.IpAddress;
|
||||
}
|
||||
}
|
||||
}
|
@ -69,7 +69,6 @@
|
||||
<Compile Include="Category.cs" />
|
||||
<Compile Include="CriterionHandler.cs" />
|
||||
<Compile Include="Game.cs" />
|
||||
<Compile Include="ICurrentUserProvider.cs" />
|
||||
<Compile Include="Search\IQuoteSearcher.cs" />
|
||||
<Compile Include="Search\ISearchIndexLocator.cs" />
|
||||
<Compile Include="Search\Lucene\LuceneQuoteSearcher.cs" />
|
||||
|
@ -1,23 +1,42 @@
|
||||
using System.Web.Mvc;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Web.Mvc;
|
||||
using Portoa.Persistence;
|
||||
using Portoa.Web;
|
||||
using Portoa.Web.ErrorHandling;
|
||||
using Portoa.Web.Results;
|
||||
using VideoGameQuotes.Api;
|
||||
using VideoGameQuotes.Web.Models;
|
||||
using VideoGameQuotes.Web.Security;
|
||||
using VideoGameQuotes.Web.Services;
|
||||
|
||||
namespace VideoGameQuotes.Web.Controllers {
|
||||
[IsValidUser(Group = UserGroup.Admin)]
|
||||
[VerifyUser(Group = UserGroup.Admin)]
|
||||
public class AdminController : Controller {
|
||||
private readonly ICurrentUserProvider userProvider;
|
||||
private readonly ICurrentUserProvider<User> userProvider;
|
||||
private readonly IAdministrationService adminService;
|
||||
|
||||
public AdminController(ICurrentUserProvider userProvider, IAdministrationService adminService) {
|
||||
public AdminController(ICurrentUserProvider<User> userProvider, IAdministrationService adminService) {
|
||||
this.userProvider = userProvider;
|
||||
this.adminService = adminService;
|
||||
}
|
||||
|
||||
public ActionResult Users(int start = 1, int end = 20) {
|
||||
if (start < 1 || start > end || end < 1) {
|
||||
return new StatusOverrideResult(View("BadPaging")) { StatusCode = HttpStatusCode.BadRequest };
|
||||
}
|
||||
|
||||
var model = new PagedModelWithUser<User> {
|
||||
Start = start,
|
||||
End = end,
|
||||
Records = adminService.GetPagedUsers(start, end),
|
||||
TotalCount = adminService.GetAllUsers().Count(),
|
||||
CurrentUser = userProvider.CurrentUser
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult Create() {
|
||||
var model = new CreateAdminModel();
|
||||
|
@ -4,6 +4,7 @@ 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.Security;
|
||||
using VideoGameQuotes.Api;
|
||||
@ -12,7 +13,7 @@ using VideoGameQuotes.Web.Models;
|
||||
namespace VideoGameQuotes.Web.Controllers {
|
||||
public class HomeController : Controller {
|
||||
private readonly IAuthenticationService authenticationService;
|
||||
private readonly ICurrentUserProvider userProvider;
|
||||
private readonly ICurrentUserProvider<User> userProvider;
|
||||
|
||||
private static readonly string[] answers = new[] {
|
||||
"I AM ERROR.",
|
||||
@ -26,7 +27,7 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
"ryu huyabasa"
|
||||
};
|
||||
|
||||
public HomeController(IAuthenticationService authenticationService, ICurrentUserProvider userProvider) {
|
||||
public HomeController(IAuthenticationService authenticationService, ICurrentUserProvider<User> userProvider) {
|
||||
this.authenticationService = authenticationService;
|
||||
this.userProvider = userProvider;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ using System.Web.Mvc;
|
||||
using JetBrains.Annotations;
|
||||
using Portoa.Persistence;
|
||||
using Portoa.Validation.DataAnnotations;
|
||||
using Portoa.Web;
|
||||
using Portoa.Web.Controllers;
|
||||
using Portoa.Web.Results;
|
||||
using VideoGameQuotes.Api;
|
||||
@ -17,10 +18,10 @@ using VideoGameQuotes.Web.Services;
|
||||
namespace VideoGameQuotes.Web.Controllers {
|
||||
public class QuoteController : Controller {
|
||||
private readonly IQuoteService quoteService;
|
||||
private readonly ICurrentUserProvider currentUserProvider;
|
||||
private readonly ICurrentUserProvider<User> currentUserProvider;
|
||||
private readonly IQuoteSearcher quoteSearcher;
|
||||
|
||||
public QuoteController(IQuoteService quoteService, ICurrentUserProvider currentUserProvider, IQuoteSearcher quoteSearcher) {
|
||||
public QuoteController(IQuoteService quoteService, ICurrentUserProvider<User> currentUserProvider, IQuoteSearcher quoteSearcher) {
|
||||
this.quoteService = quoteService;
|
||||
this.currentUserProvider = currentUserProvider;
|
||||
this.quoteSearcher = quoteSearcher;
|
||||
@ -37,7 +38,7 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser]
|
||||
[HttpPost, VerifyUser]
|
||||
public JsonResult Report(ReportModel model) {
|
||||
if (!ModelState.IsValid) {
|
||||
return Json(this.CreateJsonErrorResponse("Invalid request"));
|
||||
@ -48,12 +49,12 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
quote.AddFlag(model.Comment, model.FlagType, currentUserProvider.CurrentUser);
|
||||
quoteService.SaveQuote(quote);
|
||||
return Json(this.CreateJsonResponse());
|
||||
} catch (Exception e) {
|
||||
} catch {
|
||||
return Json(this.CreateJsonErrorResponse("Unable to create your report"));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser]
|
||||
[HttpPost, VerifyUser]
|
||||
public JsonResult Vote(VoteModel model) {
|
||||
if (!ModelState.IsValid) {
|
||||
return Json(this.CreateJsonErrorResponse("Invalid request"));
|
||||
@ -89,9 +90,9 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
return new StatusOverrideResult(View("BadPaging")) { StatusCode = HttpStatusCode.BadRequest };
|
||||
}
|
||||
|
||||
return View(new PagedQuoteCollectionModel {
|
||||
Quotes = quoteService.GetBestQuotes(start, end),
|
||||
User = currentUserProvider.CurrentUser,
|
||||
return View(new PagedModelWithUser<Quote> {
|
||||
Records = quoteService.GetBestQuotes(start, end),
|
||||
CurrentUser = currentUserProvider.CurrentUser,
|
||||
Start = start,
|
||||
End = end
|
||||
});
|
||||
@ -106,7 +107,7 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
return RedirectToAction("Quote", new { id = quote.Id, text = quote.GetUrlFriendlyText() });
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser(Group = UserGroup.Admin)]
|
||||
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult DismissFlag([GreaterThanZero]int quoteId, [GreaterThanZero]int flagId) {
|
||||
if (!ModelState.IsValid) {
|
||||
return Json(this.CreateJsonErrorResponse("quote/flag is invalid"));
|
||||
@ -127,7 +128,7 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet, IsValidUser(Group = UserGroup.Admin)]
|
||||
[HttpGet, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Edit(int id) {
|
||||
try {
|
||||
var model = new EditQuoteModel(quoteService.GetQuote(id));
|
||||
@ -138,19 +139,19 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser(Group = UserGroup.Admin)]
|
||||
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Edit(EditQuoteModel model) {
|
||||
return CreateOrEditQuote(model, "Edit");
|
||||
}
|
||||
|
||||
[IsValidUser]
|
||||
[VerifyUser]
|
||||
public ActionResult Submit() {
|
||||
var model = new EditQuoteModel();
|
||||
ResetEditQuoteModel(model);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser]
|
||||
[HttpPost, VerifyUser]
|
||||
public ActionResult Submit(EditQuoteModel model) {
|
||||
return CreateOrEditQuote(model, "Submit");
|
||||
}
|
||||
@ -276,7 +277,7 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
}
|
||||
}
|
||||
|
||||
[IsValidUser(Group = UserGroup.Admin)]
|
||||
[VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Flags(int id) {
|
||||
try {
|
||||
var model = new QuoteModel {
|
||||
@ -290,7 +291,7 @@ namespace VideoGameQuotes.Web.Controllers {
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, IsValidUser]
|
||||
[HttpPost, VerifyUser]
|
||||
public JsonResult CreateCategory(Category category) {
|
||||
try {
|
||||
category = quoteService.SaveCategory(category);
|
||||
|
91
Src/VideoGameQuotes.Web/Controllers/UserController.cs
Normal file
91
Src/VideoGameQuotes.Web/Controllers/UserController.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Net;
|
||||
using System.Web.Mvc;
|
||||
using Portoa.Persistence;
|
||||
using Portoa.Validation.DataAnnotations;
|
||||
using Portoa.Web.Controllers;
|
||||
using Portoa.Web.Results;
|
||||
using VideoGameQuotes.Api;
|
||||
using VideoGameQuotes.Api.Persistence;
|
||||
using VideoGameQuotes.Web.Models;
|
||||
using VideoGameQuotes.Web.Security;
|
||||
|
||||
namespace VideoGameQuotes.Web.Controllers {
|
||||
|
||||
public class UserController : Controller {
|
||||
private readonly IUserService userService;
|
||||
|
||||
public UserController(IUserService userService) {
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Delete([GreaterThanZero]int id) {
|
||||
if (!ModelState.IsValid) {
|
||||
return Json(this.CreateJsonErrorResponse("Invalid user id"));
|
||||
}
|
||||
|
||||
try {
|
||||
userService.Delete(id);
|
||||
return Json(this.CreateJsonResponse());
|
||||
} catch (Exception e) {
|
||||
return Json(this.CreateJsonErrorResponse(e));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Ban([GreaterThanZero]int id) {
|
||||
if (!ModelState.IsValid) {
|
||||
return Json(this.CreateJsonErrorResponse("Invalid user id"));
|
||||
}
|
||||
|
||||
try {
|
||||
userService.Delete(id);
|
||||
return Json(this.CreateJsonResponse());
|
||||
} catch (Exception e) {
|
||||
return Json(this.CreateJsonErrorResponse(e));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Edit([Required]string usernameOrIp) {
|
||||
if (!ModelState.IsValid) {
|
||||
return GetUsernameNotFoundResult(usernameOrIp);
|
||||
}
|
||||
|
||||
var user = userService.FindByUsernameOrIp(usernameOrIp);
|
||||
if (user == null) {
|
||||
return GetUsernameNotFoundResult(usernameOrIp);
|
||||
}
|
||||
|
||||
return View(new EditUserModel { Id = user.Id, Username = user.Username, IpAddress = user.IpAddress, Group = user.Group });
|
||||
}
|
||||
|
||||
[HttpPost, VerifyUser(Group = UserGroup.Admin)]
|
||||
public ActionResult Edit(EditUserModel model) {
|
||||
if (!ModelState.IsValid) {
|
||||
return View(model);
|
||||
}
|
||||
|
||||
try {
|
||||
var user = userService.FindById(model.Id);
|
||||
user.Username = model.Username;
|
||||
user.IpAddress = model.IpAddress;
|
||||
user.Group = model.Group;
|
||||
user = userService.Save(user);
|
||||
return RedirectToAction("Edit", new { usernameOrIp = user.Username ?? user.IpAddress });
|
||||
} catch (EntityNotFoundException) {
|
||||
ModelState.AddModelError("Id", "Invalid user ID");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
private ActionResult GetUsernameNotFoundResult(string usernameOrIp) {
|
||||
return new StatusOverrideResult(View("InvalidUsername", new InvalidUsernameModel { UsernameOrIp = usernameOrIp })) {
|
||||
StatusCode = HttpStatusCode.NotFound
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
using System.Collections.Specialized;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Microsoft.Practices.Unity;
|
||||
using Portoa.Logging;
|
||||
using Portoa.Web;
|
||||
using Portoa.Web.Controllers;
|
||||
using Portoa.Web.ErrorHandling;
|
||||
using Portoa.Web.Models;
|
||||
using Portoa.Web.Security;
|
||||
using Portoa.Web.Unity;
|
||||
@ -19,7 +23,7 @@ using VideoGameQuotes.Web.Security;
|
||||
using VideoGameQuotes.Web.Services;
|
||||
|
||||
namespace VideoGameQuotes.Web {
|
||||
public class MvcApplication : MvcApplicationBase {
|
||||
public class MvcApplication : MvcApplicationBase<User> {
|
||||
|
||||
protected override void ConfigureModelBinders(ModelBinderDictionary binders) {
|
||||
binders
|
||||
@ -37,8 +41,8 @@ namespace VideoGameQuotes.Web {
|
||||
|
||||
Container
|
||||
.AddNewExtension<LogAllMethodCalls>()
|
||||
.RegisterType<ICurrentUserProvider, SessionBasedUserProvider>()
|
||||
.RegisterType<IsValidUserAttribute>(new InjectionProperty<IsValidUserAttribute>(attr => attr.UserProvider))
|
||||
.RegisterType<ICurrentUserProvider<User>, SessionBasedUserProvider>()
|
||||
.RegisterType<VerifyUserAttribute>(new InjectionProperty<VerifyUserAttribute>(attr => attr.UserProvider))
|
||||
.RegisterType<IUserService, UserService>()
|
||||
.RegisterType<IAdministrationService, AdministrationService>()
|
||||
.RegisterType<IQuoteService, QuoteService>()
|
||||
@ -68,7 +72,11 @@ namespace VideoGameQuotes.Web {
|
||||
//bullshit route so that RenderAction works
|
||||
routes.MapRoute("mainmenu", "home/mainmenu", new { controller = "Home", action = "MainMenu" });
|
||||
|
||||
routes.MapRoute("admin", "admin/{action}", new { controller = "Admin", action = "Index" });
|
||||
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" });
|
||||
|
5
Src/VideoGameQuotes.Web/Models/InvalidUsernameModel.cs
Normal file
5
Src/VideoGameQuotes.Web/Models/InvalidUsernameModel.cs
Normal file
@ -0,0 +1,5 @@
|
||||
namespace VideoGameQuotes.Web.Models {
|
||||
public class InvalidUsernameModel {
|
||||
public string UsernameOrIp { get; set; }
|
||||
}
|
||||
}
|
8
Src/VideoGameQuotes.Web/Models/PagedModelWithUser.cs
Normal file
8
Src/VideoGameQuotes.Web/Models/PagedModelWithUser.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using Portoa.Web.Models;
|
||||
using VideoGameQuotes.Api;
|
||||
|
||||
namespace VideoGameQuotes.Web.Models {
|
||||
public class PagedModelWithUser<T> : PagedModel<T> {
|
||||
public User CurrentUser { get; set; }
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace VideoGameQuotes.Web.Models {
|
||||
public class PagedQuoteCollectionModel : QuoteCollectionModel {
|
||||
public int Start { get; set; }
|
||||
public int End { get; set; }
|
||||
public int PageSize { get { return End - Start + 1; } }
|
||||
public int NextStart { get { return End + 1; } }
|
||||
public int NextEnd { get { return End + PageSize; } }
|
||||
public int PreviousStart { get { return Math.Max(0, Start - PageSize); } }
|
||||
public int PreviousEnd { get { return Math.Max(PageSize - 1, End - PageSize); } }
|
||||
public bool HasPrevious { get { return Start > 0; } }
|
||||
}
|
||||
}
|
@ -2,6 +2,12 @@
|
||||
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; }
|
||||
|
28
Src/VideoGameQuotes.Web/Models/UserModel.cs
Normal file
28
Src/VideoGameQuotes.Web/Models/UserModel.cs
Normal file
@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Web.Mvc;
|
||||
using VideoGameQuotes.Api;
|
||||
|
||||
namespace VideoGameQuotes.Web.Models {
|
||||
public class UserModel {
|
||||
public User CurrentUser { get; set; }
|
||||
public User User { get; set; }
|
||||
}
|
||||
|
||||
public class EditUserModel {
|
||||
[Required]
|
||||
public int Id { get; set; }
|
||||
public string Username { get; set; }
|
||||
[Required]
|
||||
public UserGroup Group { get; set; }
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
public IEnumerable<SelectListItem> GetGroupList(UserGroup selectedGroup) {
|
||||
return Enum.GetValues(typeof(UserGroup))
|
||||
.Cast<UserGroup>()
|
||||
.Select(group => new SelectListItem { Text = group.ToString(), Value = group.ToString()/*, Selected = group == selectedGroup*/ });
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
using System.Web;
|
||||
using Portoa.Web;
|
||||
using Portoa.Web.Session;
|
||||
using VideoGameQuotes.Api;
|
||||
using VideoGameQuotes.Api.Persistence;
|
||||
|
||||
namespace VideoGameQuotes.Web.Security {
|
||||
public class SessionBasedUserProvider : ICurrentUserProvider {
|
||||
public class SessionBasedUserProvider : ICurrentUserProvider<User> {
|
||||
private readonly IUserService userService;
|
||||
private readonly ISessionStore sessionStore;
|
||||
private readonly HttpContextBase httpContext;
|
||||
|
@ -6,12 +6,12 @@ using VideoGameQuotes.Api;
|
||||
|
||||
namespace VideoGameQuotes.Web.Security {
|
||||
[NeedsBuildUp]
|
||||
public class IsValidUserAttribute : ActionFilterAttribute {
|
||||
public IsValidUserAttribute() {
|
||||
public class VerifyUserAttribute : ActionFilterAttribute {
|
||||
public VerifyUserAttribute() {
|
||||
Group = UserGroup.User;
|
||||
}
|
||||
|
||||
public ICurrentUserProvider UserProvider { get; set; }
|
||||
public ICurrentUserProvider<User> UserProvider { get; set; }
|
||||
public UserGroup Group { get; set; }
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext filterContext) {
|
@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Portoa.Persistence;
|
||||
using VideoGameQuotes.Api;
|
||||
@ -9,6 +10,7 @@ namespace VideoGameQuotes.Web.Services {
|
||||
IEnumerable<Quote> GetFlaggedQuotes();
|
||||
User GetUser(int id);
|
||||
IEnumerable<User> GetAllUsers();
|
||||
IEnumerable<User> GetPagedUsers(int start, int end);
|
||||
}
|
||||
|
||||
public class AdministrationService : IAdministrationService {
|
||||
@ -42,5 +44,15 @@ namespace VideoGameQuotes.Web.Services {
|
||||
public IEnumerable<User> GetAllUsers() {
|
||||
return userRepository.Records;
|
||||
}
|
||||
|
||||
[UnitOfWork]
|
||||
public IEnumerable<User> GetPagedUsers(int start, int end) {
|
||||
return userRepository
|
||||
.Records
|
||||
.OrderBy(user => user.Username)
|
||||
.ThenBy(user => user.IpAddress)
|
||||
.Skip(start - 1)
|
||||
.Take(end - start + 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -87,24 +87,27 @@
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Controllers\ApiController.cs" />
|
||||
<Compile Include="Controllers\HomeController.cs" />
|
||||
<Compile Include="Controllers\UserController.cs" />
|
||||
<Compile Include="Models\ApiModel.cs" />
|
||||
<Compile Include="Models\BrowseModel.cs" />
|
||||
<Compile Include="Models\BrowseModelBinder.cs" />
|
||||
<Compile Include="Models\ChangePasswordModel.cs" />
|
||||
<Compile Include="Models\CreateAdminModel.cs" />
|
||||
<Compile Include="Models\InvalidUsernameModel.cs" />
|
||||
<Compile Include="Models\MainMenuModel.cs" />
|
||||
<Compile Include="Models\QualifiedBrowseModel.cs" />
|
||||
<Compile Include="Models\PagedQuoteCollectionModel.cs" />
|
||||
<Compile Include="Models\PagedModelWithUser.cs" />
|
||||
<Compile Include="Models\QuoteCollectionModel.cs" />
|
||||
<Compile Include="Models\QuoteFlagModel.cs" />
|
||||
<Compile Include="Models\QuoteModel.cs" />
|
||||
<Compile Include="Models\ReportModel.cs" />
|
||||
<Compile Include="Models\SearchModel.cs" />
|
||||
<Compile Include="Models\UserModel.cs" />
|
||||
<Compile Include="Models\VoteModel.cs" />
|
||||
<Compile Include="Services\AdministrationService.cs" />
|
||||
<Compile Include="Services\FormsAuthenticationService.cs" />
|
||||
<Compile Include="Validation\NonEmptyText.cs" />
|
||||
<Compile Include="Security\IsValidUserAttribute.cs" />
|
||||
<Compile Include="Security\VerifyUserAttribute.cs" />
|
||||
<Compile Include="Controllers\QuoteController.cs" />
|
||||
<Compile Include="Services\QuoteService.cs" />
|
||||
<Compile Include="Default.aspx.cs">
|
||||
@ -139,10 +142,12 @@
|
||||
<Content Include="Views\Admin\Index.aspx" />
|
||||
<Content Include="Views\Admin\Password.aspx" />
|
||||
<Content Include="Views\Admin\PasswordSuccessfullyChanged.aspx" />
|
||||
<Content Include="Views\Admin\SingleUser.ascx" />
|
||||
<Content Include="Views\Admin\Users.aspx" />
|
||||
<Content Include="Views\Home\About.aspx" />
|
||||
<Content Include="Views\Home\Contact.aspx" />
|
||||
<Content Include="Views\Home\ContactSuccess.aspx" />
|
||||
<Content Include="Views\Quote\BadPaging.aspx" />
|
||||
<Content Include="Views\Shared\BadPaging.aspx" />
|
||||
<Content Include="Views\Quote\Best.aspx" />
|
||||
<Content Include="Views\Quote\Edit.aspx" />
|
||||
<Content Include="Views\Quote\EditQuoteForm.ascx" />
|
||||
@ -163,6 +168,8 @@
|
||||
<Content Include="Views\Shared\RecursiveExceptionView.ascx" />
|
||||
<Content Include="Views\Shared\SingleQuote.ascx" />
|
||||
<Content Include="Views\Shared\Unknown.aspx" />
|
||||
<Content Include="Views\User\Edit.aspx" />
|
||||
<Content Include="Views\User\InvalidUsername.aspx" />
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Web.Debug.config">
|
||||
<DependentUpon>Web.config</DependentUpon>
|
||||
|
26
Src/VideoGameQuotes.Web/Views/Admin/SingleUser.ascx
Normal file
26
Src/VideoGameQuotes.Web/Views/Admin/SingleUser.ascx
Normal file
@ -0,0 +1,26 @@
|
||||
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<VideoGameQuotes.Web.Models.UserModel>" %>
|
||||
<%@ Import Namespace="VideoGameQuotes.Api" %>
|
||||
|
||||
<div class="user-container">
|
||||
<p>
|
||||
<strong><%: Model.User.Username ?? "<no username>" %></strong>
|
||||
[<em><%: Model.User.IpAddress %></em>]
|
||||
</p>
|
||||
|
||||
<dl>
|
||||
<dt>Created</dt>
|
||||
<dd><%: Model.User.Created %></dd>
|
||||
<dt>Group</dt>
|
||||
<dd><%: Model.User.Group %></dd>
|
||||
</dl>
|
||||
|
||||
<% if (Model.CurrentUser.Group == UserGroup.Admin) { %>
|
||||
<div class="user-admin">
|
||||
<%= Html.ActionLink("edit", "edit", "User", new { usernameOrIp = Model.User.Username ?? Model.User.IpAddress }, null) %> |
|
||||
<%= Html.ActionLink("delete", "delete", "User", new { id = Model.User.Id }, null)%> |
|
||||
<%= Html.ActionLink("ban", "ban", "User", new { id = Model.User.Id }, null)%>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<hr />
|
||||
</div>
|
19
Src/VideoGameQuotes.Web/Views/Admin/Users.aspx
Normal file
19
Src/VideoGameQuotes.Web/Views/Admin/Users.aspx
Normal file
@ -0,0 +1,19 @@
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.PagedModelWithUser<VideoGameQuotes.Api.User>>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<%@ Import Namespace="VideoGameQuotes.Web.Models" %>
|
||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Manage Users</asp:Content>
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
<h2>Users</h2>
|
||||
|
||||
<p>
|
||||
<% if (Model.HasPrevious) { %>
|
||||
<%= Html.ActionLink("previous", "users", new { start = Model.PreviousStart, end = Model.PreviousEnd }) %>
|
||||
<% } %>
|
||||
<%= Html.ActionLink("next", "users", new { start = Model.NextStart, end = Model.NextEnd }) %>
|
||||
</p>
|
||||
|
||||
<%
|
||||
foreach (var user in Model.Records) {
|
||||
Html.RenderPartial("SingleUser", new UserModel { CurrentUser = Model.CurrentUser, User = user });
|
||||
}
|
||||
%>
|
||||
</asp:Content>
|
@ -1,4 +1,4 @@
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.PagedQuoteCollectionModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.PagedModelWithUser<VideoGameQuotes.Api.Quote>>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<%@ Import Namespace="VideoGameQuotes.Web.Models" %>
|
||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Best: <%= Model.Start %> – <%= Model.End %></asp:Content>
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
@ -11,8 +11,8 @@
|
||||
</p>
|
||||
|
||||
<%
|
||||
foreach (var quote in Model.Quotes) {
|
||||
Html.RenderPartial("SingleQuote", new QuoteModel { Quote = quote, User = Model.User });
|
||||
foreach (var quote in Model.Records) {
|
||||
Html.RenderPartial("SingleQuote", new QuoteModel { Quote = quote, User = Model.CurrentUser });
|
||||
}
|
||||
%>
|
||||
</asp:Content>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<%@ 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"><%: Model.Quote.Game.Name %></asp:Content>
|
||||
<%@ Import Namespace="VideoGameQuotes.Api" %>
|
||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent"><%: Model.Quote.GetAbbreviatedText() %></asp:Content>
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
<% Html.RenderPartial("SingleQuote", Model); %>
|
||||
</asp:Content>
|
@ -19,31 +19,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="quote-text">
|
||||
<div class="quote-text">
|
||||
<%: Model.Quote.Text %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
<div class="quote-links">
|
||||
<a class="quote-report-link" href="#" title="report this quote as inaccurate, fake, spam, duplicate, etc.">report</a>
|
||||
<p class="quote-details-created">
|
||||
<span title="<%: Model.Quote.Created %>">added <%: Model.Quote.GetHumanReadableTimeSinceCreated() %></span>
|
||||
<% 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) %>
|
||||
by <%= Html.ActionLink(Model.Quote.Creator.GetUsernameOrIp(), "edit", "user", new { usernameOrIp = Model.Quote.Creator.GetUsernameOrIp()}, null) %>
|
||||
<% } %>
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<p class="quote-links">
|
||||
<a class="quote-report-link" href="#" title="report this quote as inaccurate, fake, spam, duplicate, etc.">report</a> |
|
||||
<%= Html.ActionLink("permalink", "quote", "quote", new { id = Model.Quote.Id, text = Model.Quote.GetUrlFriendlyText() }, new { title = "permanent link to this quote" })%>
|
||||
<% if (Model.User != null && Model.User.Group >= UserGroup.Admin) { %>
|
||||
| <%= Html.ActionLink("edit", "edit", "quote", new { id = Model.Quote.Id }, null) %>
|
||||
(<%= Model.Quote.Flags.Count() %> flags)
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="quote-details">
|
||||
<dl>
|
||||
<dt>Game</dt>
|
||||
<dd><%= Html.ActionLink(Model.Quote.Game.Name, "browse", "Quote", new { qualifiers = "game/" + Model.Quote.Game.Id }, null) %></dd>
|
||||
<dt>Added</dt>
|
||||
<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>
|
||||
<div class="quote-categories">
|
||||
<ul class="menu clearfix">
|
||||
<li><%= Html.ActionLink(Model.Quote.Game.Name, "browse", "Quote", new { qualifiers = "game/" + Model.Quote.Game.Id }, new { @class = "quote-game-link", title = string.Format("browse quotes from the game \"{0}\"", Model.Quote.Game.Name) }) %></li>
|
||||
<% foreach (var category in Model.Quote.Categories.OrderBy(category => category.Name)) { %>
|
||||
<li><%= Html.ActionLink(category.Name, "browse", "Quote", new { qualifiers = "category/" + category.Id }, new { title = string.Format("browse quotes categorized as \"{0}\"", category.Name) })%></li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,13 +1,9 @@
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<Portoa.Web.ErrorHandling.ErrorModel<VideoGameQuotes.Api.User>>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<asp:Content runat="server" ID="SubTitle" ContentPlaceHolderID="TitleContent">Unknown Error</asp:Content>
|
||||
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
<h2>WTF⁈</h2>
|
||||
|
||||
<div class="error-image">
|
||||
<img src="/media/images/unknown.png" alt="BLARGH!!" title="BLARGH!!"/>
|
||||
</div>
|
||||
|
||||
<p class="info">
|
||||
WTF⁈ How did this even happen⁈
|
||||
</p>
|
||||
|
31
Src/VideoGameQuotes.Web/Views/User/Edit.aspx
Normal file
31
Src/VideoGameQuotes.Web/Views/User/Edit.aspx
Normal file
@ -0,0 +1,31 @@
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.EditUserModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<%@ Import Namespace="Portoa.Web.Util" %>
|
||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">Edit User</asp:Content>
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
<h2>Edit User</h2>
|
||||
|
||||
<% using (Html.BeginForm()) { %>
|
||||
<%= Html.HiddenFor(user => user.Id) %>
|
||||
|
||||
<p>
|
||||
<%= Html.LabelFor(user => user.Username) %>
|
||||
<br />
|
||||
<%= Html.TextBoxFor(user => user.Username) %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= Html.LabelFor(user => user.IpAddress) %>
|
||||
<br />
|
||||
<%= Html.TextBoxFor(user => user.IpAddress) %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= Html.LabelFor(user => user.Group) %>
|
||||
<br />
|
||||
<%= Html.DropDownListFor(user => user.Group, Model.GetGroupList(Model.Group))%>
|
||||
</p>
|
||||
|
||||
<%= Html.Submit("Save") %>
|
||||
<% } %>
|
||||
|
||||
</asp:Content>
|
11
Src/VideoGameQuotes.Web/Views/User/InvalidUsername.aspx
Normal file
11
Src/VideoGameQuotes.Web/Views/User/InvalidUsername.aspx
Normal file
@ -0,0 +1,11 @@
|
||||
<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<VideoGameQuotes.Web.Models.InvalidUsernameModel>" MasterPageFile="~/Views/Shared/Site.Master" %>
|
||||
<asp:Content runat="server" ID="Title" ContentPlaceHolderID="TitleContent">User Not Found</asp:Content>
|
||||
<asp:Content runat="server" ID="Main" ContentPlaceHolderID="MainContent">
|
||||
<p>
|
||||
No user exists with username or IP address "<%: Model.UsernameOrIp %>".
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<%= Html.ActionLink("list of users", "Users", "Admin") %>
|
||||
</p>
|
||||
</asp:Content>
|
@ -1,6 +1,8 @@
|
||||
.quote-container {
|
||||
width: 500px;
|
||||
margin: auto;
|
||||
/* box shadow stuff from http://nicolasgallagher.com/css-drop-shadows-without-images/demo/ */
|
||||
|
||||
.quote-container {
|
||||
width: 600px;
|
||||
margin: 0 auto 50px auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -8,8 +10,6 @@
|
||||
float: left;
|
||||
font-family: Georgia, serif;
|
||||
padding: 5px;
|
||||
color: #FFFFFF;
|
||||
background-color: #6699FF;
|
||||
}
|
||||
.quote-score {
|
||||
cursor: help;
|
||||
@ -17,19 +17,59 @@
|
||||
.quote-score, .vote-container {
|
||||
text-align: center;
|
||||
}
|
||||
.vote-container {
|
||||
height: 20px;
|
||||
}
|
||||
.quote-data {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.vote-for, .vote-against {
|
||||
cursor: pointer;
|
||||
}
|
||||
.vote-for:hover, .vote-against:hover {
|
||||
color: #FFFF99;
|
||||
color: #46C46E;
|
||||
}
|
||||
|
||||
.quote-container .quote-data .quote-text {
|
||||
.quote-text {
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.quote-score-container {
|
||||
width: 40px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.quote-text {
|
||||
box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
||||
-moz-box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
||||
-webkit-box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;
|
||||
background-color: #FFFFFF;
|
||||
padding: 1.5em 1em;
|
||||
width: 500px;
|
||||
position: relative;
|
||||
}
|
||||
.quote-game-link {
|
||||
}
|
||||
.quote-categories a {
|
||||
border: 1px solid #999999 !important;
|
||||
background-color: #BBBBBB;
|
||||
padding: 5px 8px;
|
||||
display: block;
|
||||
color: #FFFFFF !important;
|
||||
}
|
||||
.quote-categories a:hover {
|
||||
background-color: #999999;
|
||||
border: 1px solid #BBBBBB !important;
|
||||
}
|
||||
.quote-categories li {
|
||||
float: left;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.quote-details-created {
|
||||
float: left;
|
||||
}
|
||||
.quote-links {
|
||||
float: right;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user