Exercise Files
This commit is contained in:
485
Ch07/07_02_Begin/Website/Controllers/AccountController.cs
Normal file
485
Ch07/07_02_Begin/Website/Controllers/AccountController.cs
Normal file
@ -0,0 +1,485 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.Owin;
|
||||
using Microsoft.Owin.Security;
|
||||
using HPlusSports.Models;
|
||||
|
||||
namespace HPlusSports.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
private ApplicationSignInManager _signInManager;
|
||||
private ApplicationUserManager _userManager;
|
||||
|
||||
public AccountController()
|
||||
{
|
||||
}
|
||||
|
||||
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
|
||||
{
|
||||
UserManager = userManager;
|
||||
SignInManager = signInManager;
|
||||
}
|
||||
|
||||
public ApplicationSignInManager SignInManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
|
||||
}
|
||||
private set
|
||||
{
|
||||
_signInManager = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ApplicationUserManager UserManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
|
||||
}
|
||||
private set
|
||||
{
|
||||
_userManager = value;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Login
|
||||
[AllowAnonymous]
|
||||
public ActionResult Login(string returnUrl)
|
||||
{
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Login
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// This doesn't count login failures towards account lockout
|
||||
// To enable password failures to trigger account lockout, change to shouldLockout: true
|
||||
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
return RedirectToLocal(returnUrl);
|
||||
case SignInStatus.LockedOut:
|
||||
return View("Lockout");
|
||||
case SignInStatus.RequiresVerification:
|
||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
|
||||
case SignInStatus.Failure:
|
||||
default:
|
||||
ModelState.AddModelError("", "Invalid login attempt.");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/VerifyCode
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
|
||||
{
|
||||
// Require that the user has already logged in via username/password or external login
|
||||
if (!await SignInManager.HasBeenVerifiedAsync())
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/VerifyCode
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// The following code protects for brute force attacks against the two factor codes.
|
||||
// If a user enters incorrect codes for a specified amount of time then the user account
|
||||
// will be locked out for a specified amount of time.
|
||||
// You can configure the account lockout settings in IdentityConfig
|
||||
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
return RedirectToLocal(model.ReturnUrl);
|
||||
case SignInStatus.LockedOut:
|
||||
return View("Lockout");
|
||||
case SignInStatus.Failure:
|
||||
default:
|
||||
ModelState.AddModelError("", "Invalid code.");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Register
|
||||
[AllowAnonymous]
|
||||
public ActionResult Register()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Register
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> Register(RegisterViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
|
||||
var result = await UserManager.CreateAsync(user, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
|
||||
|
||||
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
|
||||
// Send an email with this link
|
||||
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
|
||||
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
|
||||
// await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");
|
||||
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
AddErrors(result);
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ConfirmEmail
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> ConfirmEmail(string userId, string code)
|
||||
{
|
||||
if (userId == null || code == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var result = await UserManager.ConfirmEmailAsync(userId, code);
|
||||
return View(result.Succeeded ? "ConfirmEmail" : "Error");
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ForgotPassword
|
||||
[AllowAnonymous]
|
||||
public ActionResult ForgotPassword()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ForgotPassword
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = await UserManager.FindByNameAsync(model.Email);
|
||||
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
|
||||
{
|
||||
// Don't reveal that the user does not exist or is not confirmed
|
||||
return View("ForgotPasswordConfirmation");
|
||||
}
|
||||
|
||||
// For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
|
||||
// Send an email with this link
|
||||
// string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
|
||||
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
|
||||
// await UserManager.SendEmailAsync(user.Id, "Reset Password", "Please reset your password by clicking <a href=\"" + callbackUrl + "\">here</a>");
|
||||
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ForgotPasswordConfirmation
|
||||
[AllowAnonymous]
|
||||
public ActionResult ForgotPasswordConfirmation()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ResetPassword
|
||||
[AllowAnonymous]
|
||||
public ActionResult ResetPassword(string code)
|
||||
{
|
||||
return code == null ? View("Error") : View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ResetPassword
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
var user = await UserManager.FindByNameAsync(model.Email);
|
||||
if (user == null)
|
||||
{
|
||||
// Don't reveal that the user does not exist
|
||||
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||
}
|
||||
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||
}
|
||||
AddErrors(result);
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ResetPasswordConfirmation
|
||||
[AllowAnonymous]
|
||||
public ActionResult ResetPasswordConfirmation()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ExternalLogin
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult ExternalLogin(string provider, string returnUrl)
|
||||
{
|
||||
// Request a redirect to the external login provider
|
||||
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/SendCode
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
|
||||
{
|
||||
var userId = await SignInManager.GetVerifiedUserIdAsync();
|
||||
if (userId == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
|
||||
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
|
||||
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/SendCode
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> SendCode(SendCodeViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// Generate the token and send it
|
||||
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ExternalLoginCallback
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
|
||||
{
|
||||
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
|
||||
if (loginInfo == null)
|
||||
{
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
// Sign in the user with this external login provider if the user already has a login
|
||||
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
|
||||
switch (result)
|
||||
{
|
||||
case SignInStatus.Success:
|
||||
return RedirectToLocal(returnUrl);
|
||||
case SignInStatus.LockedOut:
|
||||
return View("Lockout");
|
||||
case SignInStatus.RequiresVerification:
|
||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
|
||||
case SignInStatus.Failure:
|
||||
default:
|
||||
// If the user does not have an account, then prompt the user to create an account
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
|
||||
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ExternalLoginConfirmation
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
|
||||
{
|
||||
if (User.Identity.IsAuthenticated)
|
||||
{
|
||||
return RedirectToAction("Index", "Manage");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// Get the information about the user from the external login provider
|
||||
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
|
||||
if (info == null)
|
||||
{
|
||||
return View("ExternalLoginFailure");
|
||||
}
|
||||
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
|
||||
var result = await UserManager.CreateAsync(user);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
result = await UserManager.AddLoginAsync(user.Id, info.Login);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
}
|
||||
AddErrors(result);
|
||||
}
|
||||
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/LogOff
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult LogOff()
|
||||
{
|
||||
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ExternalLoginFailure
|
||||
[AllowAnonymous]
|
||||
public ActionResult ExternalLoginFailure()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_userManager != null)
|
||||
{
|
||||
_userManager.Dispose();
|
||||
_userManager = null;
|
||||
}
|
||||
|
||||
if (_signInManager != null)
|
||||
{
|
||||
_signInManager.Dispose();
|
||||
_signInManager = null;
|
||||
}
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
// Used for XSRF protection when adding external logins
|
||||
private const string XsrfKey = "XsrfId";
|
||||
|
||||
private IAuthenticationManager AuthenticationManager
|
||||
{
|
||||
get
|
||||
{
|
||||
return HttpContext.GetOwinContext().Authentication;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddErrors(IdentityResult result)
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError("", error);
|
||||
}
|
||||
}
|
||||
|
||||
private ActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
internal class ChallengeResult : HttpUnauthorizedResult
|
||||
{
|
||||
public ChallengeResult(string provider, string redirectUri)
|
||||
: this(provider, redirectUri, null)
|
||||
{
|
||||
}
|
||||
|
||||
public ChallengeResult(string provider, string redirectUri, string userId)
|
||||
{
|
||||
LoginProvider = provider;
|
||||
RedirectUri = redirectUri;
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
public string LoginProvider { get; set; }
|
||||
public string RedirectUri { get; set; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
public override void ExecuteResult(ControllerContext context)
|
||||
{
|
||||
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
|
||||
if (UserId != null)
|
||||
{
|
||||
properties.Dictionary[XsrfKey] = UserId;
|
||||
}
|
||||
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
132
Ch07/07_02_Begin/Website/Controllers/CartController.cs
Normal file
132
Ch07/07_02_Begin/Website/Controllers/CartController.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using HPlusSports.Models;
|
||||
using System;
|
||||
using System.Data.Entity.Validation;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace HPlusSports.Controllers
|
||||
{
|
||||
public class CartController : Controller
|
||||
{
|
||||
private readonly HPlusSportsDbContext _context;
|
||||
|
||||
public CartController()
|
||||
: this(new HPlusSportsDbContext())
|
||||
{
|
||||
}
|
||||
|
||||
public CartController(HPlusSportsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
var cart = GetCart();
|
||||
|
||||
if (cart.Items.Any())
|
||||
return View("Cart", cart);
|
||||
|
||||
return View("EmptyCart");
|
||||
}
|
||||
|
||||
public ActionResult Add(string sku, int quantity = 1)
|
||||
{
|
||||
var product = _context.Products.FirstOrDefault(x => x.SKU == sku);
|
||||
|
||||
var cart = GetCart();
|
||||
|
||||
var item = cart.Items.FirstOrDefault(x => x.SKU == sku);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
item = new ShoppingCartItem
|
||||
{
|
||||
SKU = product?.SKU,
|
||||
Name = product?.Name,
|
||||
MSRP = (product?.MSRP).GetValueOrDefault(),
|
||||
Price = (product?.Price).GetValueOrDefault(),
|
||||
Quantity = quantity,
|
||||
};
|
||||
|
||||
cart.Items.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.Quantity += quantity;
|
||||
}
|
||||
|
||||
if (item.Quantity <= 0)
|
||||
{
|
||||
cart.Items.Remove(item);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_context.SaveChanges();
|
||||
}
|
||||
catch (DbEntityValidationException ex)
|
||||
{
|
||||
var errors = ex.EntityValidationErrors.SelectMany(x => x.ValidationErrors);
|
||||
|
||||
foreach (var error in errors)
|
||||
{
|
||||
ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
TempData.SuccessMessage($"Successfully added {item.Name} to the cart");
|
||||
|
||||
return RedirectToAction("Index", "Cart");
|
||||
}
|
||||
|
||||
public ActionResult Remove(long id)
|
||||
{
|
||||
var cart = GetCart();
|
||||
|
||||
var item = cart.Items.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
ModelState.AddModelError("", "Missing or invalid cart item");
|
||||
return View();
|
||||
}
|
||||
|
||||
cart.Items.Remove(item);
|
||||
|
||||
_context.SaveChanges();
|
||||
|
||||
return RedirectToAction("Index", "Cart");
|
||||
}
|
||||
|
||||
private ShoppingCart GetCart()
|
||||
{
|
||||
var userId = GetUserId(this);
|
||||
|
||||
var cart =
|
||||
_context.ShoppingCarts
|
||||
.Include("Items")
|
||||
.FirstOrDefault(x => x.UserId == userId);
|
||||
|
||||
if (cart == null)
|
||||
{
|
||||
cart = new ShoppingCart { UserId = userId };
|
||||
_context.ShoppingCarts.Add(cart);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
cart.Recalculate();
|
||||
|
||||
return cart;
|
||||
}
|
||||
|
||||
// Overwriteable function for unit testing
|
||||
internal Func<Controller, string> GetUserId =
|
||||
(controller) => controller.User.Identity.Name;
|
||||
}
|
||||
}
|
||||
57
Ch07/07_02_Begin/Website/Controllers/HomeController.cs
Normal file
57
Ch07/07_02_Begin/Website/Controllers/HomeController.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using HPlusSports.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace HPlusSports.Controllers
|
||||
{
|
||||
[RoutePrefix("home/{name}")]
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly HPlusSportsDbContext _context;
|
||||
|
||||
public HomeController()
|
||||
: this(new HPlusSportsDbContext())
|
||||
{
|
||||
}
|
||||
|
||||
public HomeController(HPlusSportsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
var categories =
|
||||
(
|
||||
from category in _context.Categories
|
||||
let count = _context.Products.Count(x => x.CategoryId == category.Id)
|
||||
select new { category, count }
|
||||
).ToDictionary(x => x.category, x => x.count);
|
||||
|
||||
return View(categories);
|
||||
}
|
||||
|
||||
[Route("about")]
|
||||
public ActionResult About(string name)
|
||||
{
|
||||
ViewBag.Message = "Your application description page, " + name;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
public ActionResult Broken(string error)
|
||||
{
|
||||
throw new Exception(error);
|
||||
}
|
||||
|
||||
public ActionResult Contact()
|
||||
{
|
||||
ViewBag.Message = "Your contact page.";
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Ch07/07_02_Begin/Website/Controllers/ImagesController.cs
Normal file
82
Ch07/07_02_Begin/Website/Controllers/ImagesController.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using HPlusSports.Models;
|
||||
|
||||
namespace HPlusSports.Controllers
|
||||
{
|
||||
public class ImagesController : Controller
|
||||
{
|
||||
private readonly HPlusSportsDbContext _context;
|
||||
|
||||
public ImagesController()
|
||||
: this(new HPlusSportsDbContext())
|
||||
{
|
||||
}
|
||||
|
||||
public ImagesController(HPlusSportsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// /images/category/{SKU}
|
||||
// Redirects to the main image for a category
|
||||
public ActionResult Category(string id)
|
||||
{
|
||||
var imageId =
|
||||
_context.Categories
|
||||
.Where(x => x.Key == id)
|
||||
.Select(x => x.ImageId)
|
||||
.FirstOrDefault();
|
||||
|
||||
return Image(imageId);
|
||||
}
|
||||
|
||||
// /images/product/{SKU}
|
||||
// Redirects to the main image for a product
|
||||
public ActionResult Product(string id)
|
||||
{
|
||||
var imageId =
|
||||
_context.Products
|
||||
.Where(x => x.SKU == id)
|
||||
.SelectMany(x => x.Images.Select(img => img.Id))
|
||||
.FirstOrDefault();
|
||||
|
||||
return Image(imageId);
|
||||
}
|
||||
|
||||
public ActionResult Image(long? id)
|
||||
{
|
||||
var image = _context.Images.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(image.Url))
|
||||
{
|
||||
return RedirectPermanent(image.Url);
|
||||
}
|
||||
|
||||
if (image?.Content == null)
|
||||
{
|
||||
image = GetPlaceholderImage();
|
||||
}
|
||||
|
||||
return File(image.Content, image.ContentType);
|
||||
}
|
||||
|
||||
|
||||
internal static volatile byte[] PlaceholderImageContent;
|
||||
|
||||
private Image GetPlaceholderImage()
|
||||
{
|
||||
if (PlaceholderImageContent == null)
|
||||
{
|
||||
var path = Server.MapPath("~/Content/placeholder.jpg");
|
||||
PlaceholderImageContent = System.IO.File.ReadAllBytes(path);
|
||||
}
|
||||
|
||||
return new Image
|
||||
{
|
||||
Content = PlaceholderImageContent,
|
||||
ContentType = "image/jpeg"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
142
Ch07/07_02_Begin/Website/Controllers/InventoryController.cs
Normal file
142
Ch07/07_02_Begin/Website/Controllers/InventoryController.cs
Normal file
@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Mvc;
|
||||
using HPlusSports.Models;
|
||||
using HPlusSports.Requests;
|
||||
using HPlusSports.Services;
|
||||
using MediatR;
|
||||
|
||||
namespace HPlusSports.Controllers
|
||||
{
|
||||
[Authorize(Roles = UserRoles.Admin)]
|
||||
public class InventoryController : Controller
|
||||
{
|
||||
private HPlusSportsDbContext _context;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public InventoryController(
|
||||
HPlusSportsDbContext context,
|
||||
MediatR.IMediator mediator)
|
||||
{
|
||||
_context = context;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
var products =
|
||||
_context.Products
|
||||
.OrderBy(x => x.CategoryId)
|
||||
.ThenBy(x => x.Name);
|
||||
|
||||
return View(products);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Create(CreateProductRequest request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
var product = new Product
|
||||
{
|
||||
CategoryId = request.CategoryId,
|
||||
Description = request.Description,
|
||||
MSRP = request.MSRP,
|
||||
Name = request.Name,
|
||||
Price = request.Price,
|
||||
SKU = request.SKU,
|
||||
Summary = request.Summary,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
LastUpdatedUserId = GetUserId(this),
|
||||
};
|
||||
|
||||
_context.Products.Add(product);
|
||||
_context.SaveChanges();
|
||||
|
||||
TempData.SuccessMessage($"Successfully created \"{product.Name}\"");
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult Update(long id)
|
||||
{
|
||||
var existing = _context.Products.Find(id);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
TempData.ErrorMessage($"Couldn't update product #\"{id}\": product not found!");
|
||||
}
|
||||
|
||||
return View(existing);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> Update(UpdateProductRequest request)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
request.LastUpdatedUserId = GetUserId(this);
|
||||
|
||||
var response = await _mediator.Send(request);
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
TempData.ErrorMessage(response.Message);
|
||||
return View();
|
||||
}
|
||||
|
||||
TempData.SuccessMessage(response.Message);
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
public ActionResult Delete(long id)
|
||||
{
|
||||
var product = _context.Products.Find(id);
|
||||
|
||||
if (product != null)
|
||||
{
|
||||
_context.Products.Remove(product);
|
||||
_context.SaveChanges();
|
||||
|
||||
TempData.SuccessMessage($"Successfully deleted \"{product.Name}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData.ErrorMessage($"Couldn't delete \"{product.Name}\": product not found!");
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
protected override void OnResultExecuting(ResultExecutingContext filterContext)
|
||||
{
|
||||
ViewData["CategoryId"] =
|
||||
_context.Categories
|
||||
.Select(x => new SelectListItem
|
||||
{
|
||||
Text = x.Name,
|
||||
Value = x.Id.ToString(),
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
// Overwriteable function for unit testing
|
||||
internal Func<Controller, string> GetUserId =
|
||||
(controller) => controller.User.Identity.Name;
|
||||
}
|
||||
}
|
||||
118
Ch07/07_02_Begin/Website/Controllers/ProductsController.cs
Normal file
118
Ch07/07_02_Begin/Website/Controllers/ProductsController.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using HPlusSports.Extensions;
|
||||
using HPlusSports.Models;
|
||||
|
||||
namespace HPlusSports.Controllers
|
||||
{
|
||||
public class ProductsController : Controller
|
||||
{
|
||||
public const int DefaultPageSize = 8;
|
||||
|
||||
private readonly HPlusSportsDbContext _context;
|
||||
|
||||
public ProductsController(HPlusSportsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public ActionResult Index(int? page = null, int? count = null)
|
||||
{
|
||||
return Category(null, page, count);
|
||||
}
|
||||
|
||||
[AllowPartialRendering]
|
||||
public ActionResult Category(string id, int? page = null, int? count = null)
|
||||
{
|
||||
var category = _context.Categories.FirstOrDefault(x => x.Key == id);
|
||||
|
||||
IQueryable<Product> products = _context.Products;
|
||||
|
||||
if (category == null)
|
||||
{
|
||||
products =
|
||||
products
|
||||
.OrderBy(x => x.CategoryId)
|
||||
.ThenBy(x => x.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
products =
|
||||
products
|
||||
.Where(x => x.CategoryId == category.Id)
|
||||
.OrderBy(x => x.Name);
|
||||
}
|
||||
|
||||
var skus = products.Select(x => x.SKU);
|
||||
var ratings = _context.GetProductRatings(skus);
|
||||
|
||||
ViewData["Ratings"] = ratings;
|
||||
ViewData["Category"] = category ?? new Category { Name = "All Categories" };
|
||||
|
||||
/**** Paging Logic ****/
|
||||
var model = products;
|
||||
var resultsCount = model.Count();
|
||||
var pageSize = count.GetValueOrDefault(DefaultPageSize);
|
||||
var currentPage = page.GetValueOrDefault(1);
|
||||
var pageCount = resultsCount / pageSize + (resultsCount % pageSize > 0 ? 1 : 0);
|
||||
var previousPage = (currentPage - 1 > 0) ? currentPage - 1 : (int?)null;
|
||||
var nextPage = (currentPage + 1 <= pageCount) ? currentPage + 1 : (int?)null;
|
||||
|
||||
model = model
|
||||
.Skip((currentPage - 1) * pageSize)
|
||||
.Take(pageSize);
|
||||
|
||||
ViewData["PageSize"] = pageSize;
|
||||
ViewData["ResultsCount"] = resultsCount;
|
||||
ViewData["CurrentPage"] = currentPage;
|
||||
ViewData["PageCount"] = pageCount;
|
||||
ViewData["PreviousPage"] = previousPage;
|
||||
ViewData["NextPage"] = nextPage;
|
||||
|
||||
/**** End Paging Logic ****/
|
||||
|
||||
var vm = new ProductsListViewModel
|
||||
{
|
||||
Products = products.Select(x => new ProductViewModel {
|
||||
MSRP = x.MSRP,
|
||||
Name = x.Name,
|
||||
Price = x.Price,
|
||||
SKU = x.SKU,
|
||||
Rating = ratings.FirstOrDefault(y => x.SKU == y.SKU),
|
||||
})
|
||||
};
|
||||
|
||||
|
||||
return View("ProductList", vm);
|
||||
}
|
||||
|
||||
public ActionResult Product(string id)
|
||||
{
|
||||
var product = _context.FindProductBySku(id);
|
||||
|
||||
if (product == null)
|
||||
return HttpNotFound();
|
||||
|
||||
var imageIds =
|
||||
product
|
||||
.Images
|
||||
.Select(img => img.Id)
|
||||
.ToArray();
|
||||
|
||||
ViewData["Rating"] = _context.GetProductRating(product.SKU);
|
||||
ViewData["Category"] = product.Category;
|
||||
ViewData["Images"] = imageIds;
|
||||
|
||||
return View(product);
|
||||
}
|
||||
|
||||
[ChildActionOnly]
|
||||
public ActionResult MenuCart()
|
||||
{
|
||||
var userId = User.Identity.Name;
|
||||
var cart = _context.ShoppingCarts.FirstOrDefault(x => x.UserId == userId)
|
||||
?? new ShoppingCart { UserId = userId };
|
||||
return PartialView("MenuCart", cart);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user