Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Admin page #324

Merged
merged 23 commits into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b68f044
Updates based on documentation
efleming18 Oct 18, 2019
88d23d5
Getting the build passing
efleming18 Oct 18, 2019
4d540b5
Getting app functioning
efleming18 Oct 18, 2019
4fd8123
A few cleanups to confirm it's working as expected
efleming18 Oct 18, 2019
8aec301
Fixing functional tests
efleming18 Oct 18, 2019
bb184b7
Updating dockerfile for 3.0
efleming18 Oct 18, 2019
ebeb9ae
Functional Tests now run sequentially
efleming18 Oct 21, 2019
2a1b588
Updating to latest version of moq
efleming18 Oct 24, 2019
80210ab
Adding migration for post 3.0 upgrades
efleming18 Oct 31, 2019
be79a01
Removing commented out lines
efleming18 Nov 1, 2019
0450c12
Moving address and catalogitemordered configuration in to classes tha…
efleming18 Nov 5, 2019
5d000df
Adding admin user
efleming18 Oct 24, 2019
f740193
Adding admin catalog screen
efleming18 Oct 24, 2019
22c25cf
WIP - squash this
efleming18 Oct 29, 2019
ce32209
Allow user to edit a catalog item
efleming18 Oct 31, 2019
0c1ecaa
Adding entry for new service
efleming18 Oct 31, 2019
3142118
Invalidating cache after catalog item update
efleming18 Nov 1, 2019
a9de2bb
Merge branch 'master' into admin-page
efleming18 Nov 7, 2019
7a7285a
Fixing bad merge
efleming18 Nov 7, 2019
c0ab691
Removing Picture Uri and making Id readonly
efleming18 Nov 27, 2019
81d74ba
Adjusting style in menu dropdown so all options are shown
efleming18 Nov 27, 2019
0bd3bd9
Creating Cache helpers with unit tests
efleming18 Dec 11, 2019
c5c5761
Merge branch 'master' into admin-page
efleming18 Dec 11, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/ApplicationCore/Constants/AuthorizationConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Microsoft.eShopWeb.ApplicationCore.Constants
{
public class AuthorizationConstants
{
public static class Roles
{
public const string ADMINISTRATORS = "Administrators";
}

public const string DEFAULT_PASSWORD = "Pass@word1";
}
}
13 changes: 11 additions & 2 deletions src/Infrastructure/Identity/AppIdentityDbContextSeed.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.eShopWeb.ApplicationCore.Constants;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Infrastructure.Identity
{
public class AppIdentityDbContextSeed
{
public static async Task SeedAsync(UserManager<ApplicationUser> userManager)
public static async Task SeedAsync(UserManager<ApplicationUser> userManager, RoleManager<IdentityRole> roleManager)
{
await roleManager.CreateAsync(new IdentityRole(AuthorizationConstants.Roles.ADMINISTRATORS));

var defaultUser = new ApplicationUser { UserName = "demouser@microsoft.com", Email = "demouser@microsoft.com" };
await userManager.CreateAsync(defaultUser, "Pass@word1");
await userManager.CreateAsync(defaultUser, AuthorizationConstants.DEFAULT_PASSWORD);

string adminUserName = "admin@microsoft.com";
var adminUser = new ApplicationUser { UserName = adminUserName, Email = adminUserName };
await userManager.CreateAsync(adminUser, AuthorizationConstants.DEFAULT_PASSWORD);
adminUser = await userManager.FindByNameAsync(adminUserName);
await userManager.AddToRoleAsync(adminUser, AuthorizationConstants.Roles.ADMINISTRATORS);
}
}
}
25 changes: 25 additions & 0 deletions src/Web/Extensions/CacheHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;

namespace Microsoft.eShopWeb.Web.Extensions
{
public static class CacheHelpers
{
public static readonly TimeSpan DefaultCacheDuration = TimeSpan.FromSeconds(30);
private static readonly string _itemsKeyTemplate = "items-{0}-{1}-{2}-{3}";

public static string GenerateCatalogItemCacheKey(int pageIndex, int itemsPage, int? brandId, int? typeId)
{
return string.Format(_itemsKeyTemplate, pageIndex, itemsPage, brandId, typeId);
}

public static string GenerateBrandsCacheKey()
{
return "brands";
}

public static string GenerateTypesCacheKey()
{
return "types";
}
}
}
10 changes: 10 additions & 0 deletions src/Web/Interfaces/ICatalogItemViewModelService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.eShopWeb.Web.ViewModels;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Interfaces
{
public interface ICatalogItemViewModelService
{
Task UpdateCatalogItem(CatalogItemViewModel viewModel);
}
}
38 changes: 38 additions & 0 deletions src/Web/Pages/Admin/EditCatalogItem.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@page
@{
ViewData["Title"] = "Admin - Edit Catalog";
@model EditCatalogItemModel
}

<div class="container">
<div class="row">
<div class="col-md-8">
<img class="esh-catalog-thumbnail" src="@Model.CatalogModel.PictureUri" />
<form method="post">
<div class="form-group">
<label asp-for="CatalogModel.Name"></label>
<input asp-for="CatalogModel.Name" class="form-control" />
<span asp-validation-for="CatalogModel.Name"></span>
</div>
<div class="form-group">
<label asp-for="CatalogModel.Price"></label>
<input asp-for="CatalogModel.Price" class="form-control" />
<span asp-validation-for="CatalogModel.Price"></span>
</div>
<div class="form-group">
<label asp-for="CatalogModel.Id"></label>
<input asp-for="CatalogModel.Id" readonly class="form-control" />
<span asp-validation-for="CatalogModel.Id"></span>
</div>
<div class="col-md-2">
<input type="submit" value="Save" class="esh-catalog-button" />
</div>
</form>
</div>
</div>
</div>


@section Scripts {
<partial name="_ValidationScriptsPartial" />
}
39 changes: 39 additions & 0 deletions src/Web/Pages/Admin/EditCatalogItem.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.ApplicationCore.Constants;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Pages.Admin
{
[Authorize(Roles = AuthorizationConstants.Roles.ADMINISTRATORS)]
public class EditCatalogItemModel : PageModel
{
private readonly ICatalogItemViewModelService _catalogItemViewModelService;

public EditCatalogItemModel(ICatalogItemViewModelService catalogItemViewModelService)
{
_catalogItemViewModelService = catalogItemViewModelService;
}

[BindProperty]
public CatalogItemViewModel CatalogModel { get; set; } = new CatalogItemViewModel();

public async Task OnGet(CatalogItemViewModel catalogModel)
{
CatalogModel = catalogModel;
}

public async Task<IActionResult> OnPostAsync()
{
if (ModelState.IsValid)
{
await _catalogItemViewModelService.UpdateCatalogItem(CatalogModel);
}

return RedirectToPage("/Admin/Index");
}
}
}
45 changes: 45 additions & 0 deletions src/Web/Pages/Admin/Index.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
@page
@{
ViewData["Title"] = "Admin - Catalog";
@model IndexModel
}
<section class="esh-catalog-hero">
<div class="container">
<img class="esh-catalog-title" src="~/images/main_banner_text.png" />
</div>
</section>
<section class="esh-catalog-filters">
<div class="container">
<form method="get">
<label class="esh-catalog-label" data-title="brand">
<select asp-for="@Model.CatalogModel.BrandFilterApplied" asp-items="@Model.CatalogModel.Brands" class="esh-catalog-filter"></select>
</label>
<label class="esh-catalog-label" data-title="type">
<select asp-for="@Model.CatalogModel.TypesFilterApplied" asp-items="@Model.CatalogModel.Types" class="esh-catalog-filter"></select>
</label>
<input class="esh-catalog-send" type="image" src="images/arrow-right.svg" />
</form>
</div>
</section>
<div class="container">
@if (Model.CatalogModel.CatalogItems.Any())
{
<partial name="_pagination" for="CatalogModel.PaginationInfo" />

<div class="esh-catalog-items row">
@foreach (var catalogItem in Model.CatalogModel.CatalogItems)
{
<div class="esh-catalog-item col-md-4">
<partial name="_editCatalog" for="@catalogItem" />
</div>
}
</div>
<partial name="_pagination" for="CatalogModel.PaginationInfo" />
}
else
{
<div class="esh-catalog-items row">
THERE ARE NO RESULTS THAT MATCH YOUR SEARCH
</div>
}
</div>
35 changes: 35 additions & 0 deletions src/Web/Pages/Admin/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.ApplicationCore.Constants;
using Microsoft.eShopWeb.Web.Extensions;
using Microsoft.eShopWeb.Web.Services;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.Extensions.Caching.Memory;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Pages.Admin
{
[Authorize(Roles = AuthorizationConstants.Roles.ADMINISTRATORS)]
public class IndexModel : PageModel
{
private readonly ICatalogViewModelService _catalogViewModelService;
private readonly IMemoryCache _cache;

public IndexModel(ICatalogViewModelService catalogViewModelService, IMemoryCache cache)
{
_catalogViewModelService = catalogViewModelService;
_cache = cache;
}

public CatalogIndexViewModel CatalogModel { get; set; } = new CatalogIndexViewModel();

public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId)
{
var cacheKey = CacheHelpers.GenerateCatalogItemCacheKey(pageId.GetValueOrDefault(), Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied);

_cache.Remove(cacheKey);

CatalogModel = await _catalogViewModelService.GetCatalogItems(pageId.GetValueOrDefault(), Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied);
}
}
}
50 changes: 24 additions & 26 deletions src/Web/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.Web.Services;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Pages
{
public class IndexModel : PageModel
{
private readonly ICatalogViewModelService _catalogViewModelService;

public IndexModel(ICatalogViewModelService catalogViewModelService)
{
_catalogViewModelService = catalogViewModelService;
}

public CatalogIndexViewModel CatalogModel { get; set; } = new CatalogIndexViewModel();

public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId)
{
CatalogModel = await _catalogViewModelService.GetCatalogItems(pageId ?? 0, Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied);
}


}
}
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.Web.Services;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Pages
{
public class IndexModel : PageModel
{
private readonly ICatalogViewModelService _catalogViewModelService;

public IndexModel(ICatalogViewModelService catalogViewModelService)
{
_catalogViewModelService = catalogViewModelService;
}

public CatalogIndexViewModel CatalogModel { get; set; } = new CatalogIndexViewModel();

public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId)
{
CatalogModel = await _catalogViewModelService.GetCatalogItems(pageId ?? 0, Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied);
}
}
}
15 changes: 15 additions & 0 deletions src/Web/Pages/Shared/_editCatalog.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@model CatalogItemViewModel

<form asp-page="/Admin/EditCatalogItem" method="get">
<div>
<img class="esh-catalog-thumbnail" src="@Model.PictureUri" />
<div class="esh-catalog-name">
<span>@Model.Name</span>
</div>
<input class="esh-catalog-button" type="submit" value="[ Edit ]" />
<input type="hidden" asp-for="@Model.Id" name="id" />
<input type="hidden" asp-for="@Model.Name" name="name" />
<input type="hidden" asp-for="@Model.PictureUri" name="pictureUri" />
<input type="hidden" asp-for="@Model.Price" name="price" />
</div>
</form>
3 changes: 2 additions & 1 deletion src/Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public async static Task Main(string[] args)
await CatalogContextSeed.SeedAsync(catalogContext, loggerFactory);

var userManager = services.GetRequiredService<UserManager<ApplicationUser>>();
await AppIdentityDbContextSeed.SeedAsync(userManager);
var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
await AppIdentityDbContextSeed.SeedAsync(userManager, roleManager);
}
catch (Exception ex)
{
Expand Down
19 changes: 8 additions & 11 deletions src/Web/Services/CachedCatalogViewModelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.Extensions.Caching.Memory;
using System;
using Microsoft.eShopWeb.Web.Extensions;

namespace Microsoft.eShopWeb.Web.Services
{
public class CachedCatalogViewModelService : ICatalogViewModelService
{
private readonly IMemoryCache _cache;
private readonly CatalogViewModelService _catalogViewModelService;
private static readonly string _brandsKey = "brands";
private static readonly string _typesKey = "types";
private static readonly string _itemsKeyTemplate = "items-{0}-{1}-{2}-{3}";
private static readonly TimeSpan _defaultCacheDuration = TimeSpan.FromSeconds(30);

public CachedCatalogViewModelService(IMemoryCache cache,
CatalogViewModelService catalogViewModelService)
Expand All @@ -25,28 +21,29 @@ public CachedCatalogViewModelService(IMemoryCache cache,

public async Task<IEnumerable<SelectListItem>> GetBrands()
{
return await _cache.GetOrCreateAsync(_brandsKey, async entry =>
return await _cache.GetOrCreateAsync(CacheHelpers.GenerateBrandsCacheKey(), async entry =>
{
entry.SlidingExpiration = _defaultCacheDuration;
entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration;
return await _catalogViewModelService.GetBrands();
});
}

public async Task<CatalogIndexViewModel> GetCatalogItems(int pageIndex, int itemsPage, int? brandId, int? typeId)
{
string cacheKey = String.Format(_itemsKeyTemplate, pageIndex, itemsPage, brandId, typeId);
var cacheKey = CacheHelpers.GenerateCatalogItemCacheKey(pageIndex, Constants.ITEMS_PER_PAGE, brandId, typeId);

return await _cache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.SlidingExpiration = _defaultCacheDuration;
entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration;
return await _catalogViewModelService.GetCatalogItems(pageIndex, itemsPage, brandId, typeId);
});
}

public async Task<IEnumerable<SelectListItem>> GetTypes()
{
return await _cache.GetOrCreateAsync(_typesKey, async entry =>
return await _cache.GetOrCreateAsync(CacheHelpers.GenerateTypesCacheKey(), async entry =>
{
entry.SlidingExpiration = _defaultCacheDuration;
entry.SlidingExpiration = CacheHelpers.DefaultCacheDuration;
return await _catalogViewModelService.GetTypes();
});
}
Expand Down
Loading