Enabling admin pages in MusicStore.Spa:
- Updated attribute routing so it works now - Created a Pages folder and PageController for serving pages, I don't like views very much - Worked around an EF issue - Fixed ApiResult to use JsonResult.ExecuteResultAsync - Made PagedList take the sort expression so it can be conditionally applied as calling Count on the query passed causes issues if it contains an OrderBy expression - Made web server ports not conflict with non-SPA MusicStore
This commit is contained in:
parent
e50cb5262a
commit
7055949e7b
|
|
@ -1,5 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using MusicStore.Infrastructure;
|
||||
|
|
@ -23,8 +22,7 @@ namespace MusicStore.Apis
|
|||
var albums = await _storeContext.Albums
|
||||
//.Include(a => a.Genre)
|
||||
//.Include(a => a.Artist)
|
||||
.SortBy(sortBy, a => a.Title)
|
||||
.ToPagedListAsync(page, pageSize);
|
||||
.ToPagedListAsync(page, pageSize, sortBy, a => a.Title);
|
||||
|
||||
return Json(albums);
|
||||
}
|
||||
|
|
@ -72,8 +70,7 @@ namespace MusicStore.Apis
|
|||
}
|
||||
|
||||
[HttpPost]
|
||||
//[Authorize(Roles = "Administrator")]
|
||||
[Authorize(ClaimTypes.Role, "Administrator")]
|
||||
[Authorize("ManageStore", "Allowed")]
|
||||
public async Task<ActionResult> CreateAlbum()
|
||||
{
|
||||
var album = new Album();
|
||||
|
|
@ -99,8 +96,7 @@ namespace MusicStore.Apis
|
|||
}
|
||||
|
||||
[HttpPut("{albumId:int}/update")]
|
||||
//[Authorize(Roles = "Administrator")]
|
||||
[Authorize(ClaimTypes.Role, "Administrator")]
|
||||
[Authorize("ManageStore", "Allowed")]
|
||||
public async Task<ActionResult> UpdateAlbum(int albumId)
|
||||
{
|
||||
var album = _storeContext.Albums.SingleOrDefault(a => a.AlbumId == albumId);
|
||||
|
|
@ -133,11 +129,11 @@ namespace MusicStore.Apis
|
|||
}
|
||||
|
||||
[HttpDelete("{albumId:int}")]
|
||||
//[Authorize(Roles = "Administrator")]
|
||||
[Authorize(ClaimTypes.Role, "Administrator")]
|
||||
[Authorize("ManageStore", "Allowed")]
|
||||
public async Task<ActionResult> DeleteAlbum(int albumId)
|
||||
{
|
||||
var album = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
||||
//var album = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
||||
var album = _storeContext.Albums.SingleOrDefault(a => a.AlbumId == albumId);
|
||||
|
||||
if (album != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using MusicStore.Models;
|
|||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
[Route("account")]
|
||||
[Authorize]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
|
|
@ -20,20 +21,16 @@ namespace MusicStore.Controllers
|
|||
|
||||
public SignInManager<ApplicationUser> SignInManager { get; private set; }
|
||||
|
||||
//
|
||||
// GET: /Account/Login
|
||||
[AllowAnonymous]
|
||||
//Bug: https://github.com/aspnet/WebFx/issues/339
|
||||
[HttpGet]
|
||||
[HttpGet("login")]
|
||||
public IActionResult Login(string returnUrl)
|
||||
{
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Login
|
||||
[HttpPost]
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
|
||||
|
|
@ -59,19 +56,15 @@ namespace MusicStore.Controllers
|
|||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Register
|
||||
[AllowAnonymous]
|
||||
//Bug: https://github.com/aspnet/WebFx/issues/339
|
||||
[HttpGet]
|
||||
[HttpGet("register")]
|
||||
public IActionResult Register()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Register
|
||||
[HttpPost]
|
||||
[HttpPost("register")]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Register(RegisterViewModel model)
|
||||
|
|
@ -84,7 +77,7 @@ namespace MusicStore.Controllers
|
|||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Index", "Home");
|
||||
return RedirectToAction("Home", "Page");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -96,9 +89,7 @@ namespace MusicStore.Controllers
|
|||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Manage
|
||||
[HttpGet]
|
||||
[HttpGet("manage")]
|
||||
public IActionResult Manage(ManageMessageId? message = null)
|
||||
{
|
||||
ViewBag.StatusMessage =
|
||||
|
|
@ -109,9 +100,7 @@ namespace MusicStore.Controllers
|
|||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Manage
|
||||
[HttpPost]
|
||||
[HttpPost("manage")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Manage(ManageUserViewModel model)
|
||||
{
|
||||
|
|
@ -135,14 +124,12 @@ namespace MusicStore.Controllers
|
|||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/LogOff
|
||||
[HttpPost]
|
||||
[HttpPost("logoff")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult LogOff()
|
||||
{
|
||||
SignInManager.SignOut();
|
||||
return RedirectToAction("Index", "Home");
|
||||
return RedirectToAction("Home", "Page");
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
using System.Security.Claims;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MusicStore.Spa.Controllers
|
||||
{
|
||||
[Route("/")]
|
||||
public class PageController : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
public IActionResult Home()
|
||||
{
|
||||
return View("/Pages/Home.cshtml");
|
||||
}
|
||||
|
||||
[HttpGet("admin")]
|
||||
[Authorize("ManageStore", "Allowed")]
|
||||
public IActionResult Admin()
|
||||
{
|
||||
return View("/Pages/Admin.cshtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public IEnumerable<ModelError> ModelErrors { get; set; }
|
||||
|
||||
public override void ExecuteResult(ActionContext context)
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
if (StatusCode.HasValue)
|
||||
{
|
||||
|
|
@ -48,7 +49,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
}
|
||||
|
||||
var json = new JsonResult(this);
|
||||
json.ExecuteResult(context);
|
||||
return json.ExecuteResultAsync(context);
|
||||
}
|
||||
|
||||
public class ModelError
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MusicStore.Infrastructure
|
||||
|
|
@ -64,7 +65,8 @@ namespace MusicStore.Infrastructure
|
|||
return new PagedList<T>(data, pagingConfig.Page, pagingConfig.PageSize, query.Count());
|
||||
}
|
||||
|
||||
public static async Task<IPagedList<T>> ToPagedListAsync<T>(this IQueryable<T> query, int page, int pageSize)
|
||||
public static async Task<IPagedList<TModel>> ToPagedListAsync<TModel, TProperty>(this IQueryable<TModel> query, int page, int pageSize, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection = SortDirection.Ascending)
|
||||
where TModel : class
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
|
|
@ -73,8 +75,15 @@ namespace MusicStore.Infrastructure
|
|||
|
||||
var pagingConfig = new PagingConfig(page, pageSize);
|
||||
var skipCount = ValidatePagePropertiesAndGetSkipCount(pagingConfig);
|
||||
var dataQuery = query;
|
||||
|
||||
var data = await query
|
||||
if (defaultSortExpression != null)
|
||||
{
|
||||
dataQuery = dataQuery
|
||||
.SortBy(sortExpression, defaultSortExpression);
|
||||
}
|
||||
|
||||
var data = await dataQuery
|
||||
.Skip(skipCount)
|
||||
.Take(pagingConfig.PageSize)
|
||||
.ToListAsync();
|
||||
|
|
@ -83,12 +92,14 @@ namespace MusicStore.Infrastructure
|
|||
{
|
||||
// Requested page has no records, just return the first page
|
||||
pagingConfig.Page = 1;
|
||||
data = await query
|
||||
data = await dataQuery
|
||||
.Take(pagingConfig.PageSize)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
return new PagedList<T>(data, pagingConfig.Page, pagingConfig.PageSize, await query.CountAsync());
|
||||
var count = await query.CountAsync();
|
||||
|
||||
return new PagedList<TModel>(data, pagingConfig.Page, pagingConfig.PageSize, count);
|
||||
}
|
||||
|
||||
private static int ValidatePagePropertiesAndGetSkipCount(PagingConfig pagingConfig)
|
||||
|
|
|
|||
|
|
@ -56,22 +56,22 @@ namespace MusicStore.Models
|
|||
private static async Task CreateAdminUser(IServiceProvider serviceProvider)
|
||||
{
|
||||
var options = serviceProvider.GetService<IOptionsAccessor<IdentityDbContextOptions>>().Options;
|
||||
//const string adminRole = "Administrator";
|
||||
const string adminRole = "Administrator";
|
||||
|
||||
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
|
||||
// TODO: Identity SQL does not support roles yet
|
||||
//var roleManager = serviceProvider.GetService<ApplicationRoleManager>();
|
||||
//if (!await roleManager.RoleExistsAsync(adminRole))
|
||||
//{
|
||||
// await roleManager.CreateAsync(new IdentityRole(adminRole));
|
||||
//}
|
||||
var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
|
||||
|
||||
if (!await roleManager.RoleExistsAsync(adminRole))
|
||||
{
|
||||
await roleManager.CreateAsync(new IdentityRole(adminRole));
|
||||
}
|
||||
|
||||
var user = await userManager.FindByNameAsync(options.DefaultAdminUserName);
|
||||
if (user == null)
|
||||
{
|
||||
user = new ApplicationUser { UserName = options.DefaultAdminUserName };
|
||||
await userManager.CreateAsync(user, options.DefaultAdminPassword);
|
||||
//await userManager.AddToRoleAsync(user, adminRole);
|
||||
await userManager.AddToRoleAsync(user, adminRole);
|
||||
await userManager.AddClaimAsync(user, new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<DevelopmentServerPort>5001</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>5101</DevelopmentServerPort>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<ProjectExtensions>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
@model IEnumerable<MusicStore.Models.Album>
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Store Manager";
|
||||
ViewBag.ngApp = "MusicStore.Admin";
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<h1>Store Manager</h1>
|
||||
|
||||
<div class="ng-view"></div>
|
||||
|
||||
@*@Html.InlineData("Lookup", "ArtistsApi")*@
|
||||
@*@Html.InlineData("Lookup", "GenresApi")*@
|
||||
|
||||
@section Scripts {
|
||||
|
||||
<script src="~/js/angular.js"></script>
|
||||
<script src="~/js/angular-route.js"></script>
|
||||
<script src="~/js/ui-bootstrap.js"></script>
|
||||
<script src="~/js/ui-bootstrap-tpls.js"></script>
|
||||
@* TODO: This is currently all the compiled TypeScript, non-minified. Need to explore options
|
||||
for alternate loading schemes, e.g. AMD loader of individual modules, min vs. non-min, etc. *@
|
||||
<script src="~/js/@(ViewBag.ngApp).js"></script>
|
||||
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
@Html.ActionLink("ASP.NET MVC Music Store", "Index", "Home", null, new { @class = "navbar-brand" })
|
||||
@Html.ActionLink("ASP.NET MVC Music Store", "Home", "Page", null, new { @class = "navbar-brand" })
|
||||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>@Html.ActionLink("Home", "Index", "Home")</li>
|
||||
<li>@Html.ActionLink("Home", "Home", "Page")</li>
|
||||
|
||||
@RenderSection("NavBarItems", required: false)
|
||||
|
||||
|
|
@ -35,8 +35,8 @@
|
|||
@RenderBody()
|
||||
<hr />
|
||||
<footer class="navbar navbar-fixed-bottom navbar-default text-center">
|
||||
<p><a href="http://mvcmusicstore.codeplex.com">mvcmusicstore.codeplex.com</a></p>
|
||||
<small>@Html.ActionLink("admin", "Index", "StoreManager")</small>
|
||||
<p><a href="https://github.com/aspnet/musicstore">github.com/aspnet/musicstore</a></p>
|
||||
<small>@Html.ActionLink("admin", "Admin", "Page")</small>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@
|
|||
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*"
|
||||
},
|
||||
"commands": {
|
||||
"WebListener": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5002",
|
||||
"Kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5004",
|
||||
"run": "run server.urls=http://localhost:5003"
|
||||
"WebListener": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5102",
|
||||
"Kestrel": "Microsoft.AspNet.Hosting --server Kestrel --server.urls http://localhost:5104",
|
||||
"run": "run server.urls=http://localhost:5103"
|
||||
},
|
||||
"frameworks": {
|
||||
"aspnet50": { },
|
||||
|
|
|
|||
Loading…
Reference in New Issue