Exercise Files

This commit is contained in:
Jess Chadwick
2018-06-06 23:57:58 -04:00
commit 20458e435e
4436 changed files with 1359080 additions and 0 deletions

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7709A0D8-F311-4A8E-9E5B-05ED694449AD}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HPlusSports</RootNamespace>
<AssemblyName>HPlusSports.Common</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
</Reference>
<Reference Include="MediatR, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MediatR.4.1.0\lib\net45\MediatR.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Models\Category.cs" />
<Compile Include="Models\EntityNotFoundException.cs" />
<Compile Include="Models\HPlusSportsDbContext.cs" />
<Compile Include="Models\HPlusSportsDbContext.SeedData.cs" />
<Compile Include="Models\Image.cs" />
<Compile Include="Models\Product.cs" />
<Compile Include="Models\ProductRating.cs" />
<Compile Include="Models\Review.cs" />
<Compile Include="Models\ShoppingCart.cs" />
<Compile Include="Models\ShoppingCartItem.cs" />
<Compile Include="Requests\UpdateProductRequest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Services\ProductUpdateService.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TestData.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Models
{
public class Category
{
public long Id { get; set; }
[Required]
public string Key { get; set; }
[Required]
public string Name { get; set; }
public long? ImageId { get; set; }
public virtual Image Image { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using System;
namespace HPlusSports
{
public class EntityNotFoundException : ApplicationException
{
public object Id { get; }
public Type Type { get; }
public EntityNotFoundException(Type type, object id)
: base($"Entity ${id?.ToString()} of type ${type?.Name} not found.")
{
Type = type;
Id = id;
}
}
public class EntityNotFoundException<T> : EntityNotFoundException
{
public EntityNotFoundException(object id) : base(typeof(T), id)
{
}
}
}

View File

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using HPlusSports.Models;
using System.Text.RegularExpressions;
namespace HPlusSports
{
public class HPlusSportsDbContextInitializer
: DropCreateDatabaseIfModelChanges<HPlusSportsDbContext>
{
protected override void Seed(HPlusSportsDbContext context)
{
new SeedData().Populate(context);
}
}
internal class SeedData
{
static readonly Random Random = new Random(1);
public const string TestUserId = "demo@hplussports.com";
static readonly IList<string> UserIds =
Enumerable.Range(0, 10)
.Select(x => $"user{x}")
.Concat(new[] { TestUserId })
.ToArray();
public void Populate(HPlusSportsDbContext context)
{
var doc = ReadTestData();
Populate(context, doc);
}
internal void Populate(HPlusSportsDbContext context, XDocument doc)
{
var categories = doc.Descendants("Categories").Descendants("Category")
.Select(x => new Category
{
Key = ToKey(x.Element("Name").Value),
Name = x.Element("Name").Value,
Image = new Image { Url = x.Element("ImageUrl").Value },
})
.ToArray();
context.Images.AddRange(categories.Select(x => x.Image));
context.SaveChanges();
context.Categories.AddRange(categories);
context.SaveChanges();
var products = doc.Descendants("Product")
.Select(x =>
{
var product = new Product
{
CategoryId = categories.First(cat => cat.Name == x.Element("Category").Value).Id,
Name = x.Element("Name").Value,
Description = StripHtml(x.Element("Description").Value),
MSRP = double.Parse(x.Element("MSRP").Value),
Price = double.Parse(x.Element("Price").Value),
SKU = x.Element("SKU").Value,
Summary = x.Element("Summary").Value,
LastUpdated = DateTime.UtcNow,
LastUpdatedUserId = "admin@hplussports.com",
ThumbnailImage = new Image { Url = x.Element("ThumbnailImageUrl").Value },
};
product.Images.Add(new Image { Url = x.Element("ImageUrl").Value });
return product;
})
.ToArray();
context.Products.AddRange(products);
context.SaveChanges();
var reviews = context.Products.SelectMany(GenerateReviews);
context.Reviews.AddRange(reviews);
context.SaveChanges();
}
IEnumerable<Review> GenerateReviews(Product product)
{
return Enumerable.Range(0, Random.Next(10))
.Select(i =>
new Review
{
SKU = product.SKU,
UserId = UserIds[Random.Next(0, UserIds.Count)],
Rating = Random.Next(3, 5),
});
}
private static string ToKey(string val)
{
return val
.Replace(" ", "-")
.Replace("--", "-")
.Replace("--", "-")
.ToLowerInvariant();
}
private static XDocument ReadTestData()
{
var set = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
using (var stream = typeof(SeedData).Assembly.GetManifestResourceStream("HPlusSports.TestData.xml"))
using (var reader = new StreamReader(stream))
{
return XDocument.Parse(reader.ReadToEnd());
}
}
private static string StripHtml(string source)
{
return
Regex.Replace(
Regex.Replace(
source,
"<[^>]*>",
""
),
"&#[^;]*;",
""
)
.Trim();
}
}
}

View File

@ -0,0 +1,56 @@
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using HPlusSports.Models;
namespace HPlusSports
{
public class HPlusSportsDbContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Image> Images { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<ShoppingCart> ShoppingCarts { get; set; }
public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
public HPlusSportsDbContext()
: this("HPlusSports")
{
}
public HPlusSportsDbContext(string connectionName)
: base(connectionName)
{
}
public Product FindProductBySku(string sku)
=> Products.FirstOrDefault(x => x.SKU == sku);
public ProductRating GetProductRating(string sku)
{
var reviews = Reviews.Where(x => x.SKU == sku);
return new ProductRating
{
SKU = sku,
Rating = reviews.Average(x => (double?)x.Rating),
ReviewCount = reviews.Count(),
};
}
public IQueryable<ProductRating> GetProductRatings(IEnumerable<string> skus)
{
return
Reviews
.Where(x => skus.Distinct().Contains(x.SKU))
.GroupBy(x => x.SKU)
.Select(reviews => new ProductRating
{
SKU = reviews.Key,
Rating = reviews.Average(x => x.Rating),
ReviewCount = reviews.Count(),
});
}
}
}

View File

@ -0,0 +1,13 @@
namespace HPlusSports.Models
{
public class Image
{
public long Id { get; set; }
public string Url { get; set; }
public byte[] Content { get; set; }
public string ContentType { get; set; }
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace HPlusSports.Models
{
public class Product
{
public long Id { get; set; }
[Required]
public long CategoryId { get; set; }
[Required]
public string SKU { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Text)]
public string Summary { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double MSRP { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double Price { get; set; }
[Required]
[Display(Name = "Last Updated By")]
public DateTime LastUpdated { get; set; }
[Required]
[Display(Name = "Last Update Timestamp")]
public string LastUpdatedUserId { get; set; }
[NotMapped]
public virtual Category Category { get; set; }
public long? ThumbnailImageId { get; set; }
public virtual Image ThumbnailImage { get; set; }
public virtual ICollection<Image> Images { get; private set; }
public Product()
{
Images = new List<Image>();
}
}
}

View File

@ -0,0 +1,9 @@
namespace HPlusSports.Models
{
public class ProductRating
{
public string SKU { get; set; }
public double? Rating { get; set; }
public int ReviewCount { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Models
{
public class Review
{
public long Id { get; set; }
public string UserId { get; set; }
public string SKU { get; set; }
[Range(minimum: 1, maximum: 5)]
public int Rating { get; set; }
[DataType(DataType.MultilineText)]
public string Comments { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace HPlusSports.Models
{
public class ShoppingCart
{
public long Id { get; set; }
public string UserId { get; set; }
[DataType(DataType.Currency)]
public double Tax { get; set; }
[DataType(DataType.Currency)]
public double Shipping { get; set; }
[DataType(DataType.Currency)]
public double Total
{
get { return Subtotal + Tax + Shipping; }
}
public string Coupon { get; set; }
public virtual ICollection<ShoppingCartItem> Items { get; private set; }
[DataType(DataType.Currency)]
public double Subtotal
{
get { return Items.Sum(x => x.Total); }
}
public ShoppingCart()
{
Items = new List<ShoppingCartItem>();
}
public void Recalculate()
{
Shipping = Subtotal * 0.1;
Tax = Subtotal * 0.07;
}
}
}

View File

@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Models
{
public class ShoppingCartItem
{
public long Id { get; set; }
[Required]
public string SKU { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Currency)]
[Range(minimum: 0, maximum: double.MaxValue)]
public double MSRP { get; set; }
[DataType(DataType.Currency)]
[Range(minimum: 0, maximum: double.MaxValue)]
public double Price { get; set; }
[Range(minimum: 0, maximum: int.MaxValue)]
public int Quantity { get; set; }
[DataType(DataType.Currency)]
public double Total
{
get { return Price * Quantity; }
}
public ShoppingCartItem()
{
}
}
}

View File

@ -0,0 +1,37 @@
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Models
{
public class UpdateProductRequest
{
[Required]
public long Id { get; set; }
[Required]
public long CategoryId { get; set; }
[Required]
public string SKU { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Text)]
public string Summary { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double MSRP { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double Price { get; set; }
public string LastUpdatedUserId { get; set; }
}
}

View File

@ -0,0 +1,38 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Common")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Common")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7709a0d8-f311-4a8e-9e5b-05ed694449ad")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("HPlusSports.Tests")]

View File

@ -0,0 +1,43 @@
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Requests
{
public class UpdateProductResponse
{
public bool Success { get; set; }
public string Message { get; set; }
}
public class UpdateProductRequest : MediatR.IRequest<UpdateProductResponse>
{
[Required]
public long Id { get; set; }
[Required]
public long CategoryId { get; set; }
[Required]
public string SKU { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Text)]
public string Summary { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double MSRP { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double Price { get; set; }
public string LastUpdatedUserId { get; set; }
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using HPlusSports.Requests;
namespace HPlusSports.Services
{
public class ProductUpdateService
: MediatR.IRequestHandler<UpdateProductRequest, UpdateProductResponse>
{
private readonly HPlusSportsDbContext _context;
public ProductUpdateService(HPlusSportsDbContext context)
{
_context = context;
}
public Task<UpdateProductResponse> Handle(UpdateProductRequest request, CancellationToken cancellationToken)
{
return Task.FromResult(Update(request));
}
public UpdateProductResponse Update(UpdateProductRequest request)
{
var existing = _context.Products.Find(request.Id);
if (existing == null)
{
return new UpdateProductResponse
{
Success = false,
Message = $"Couldn't update product #\"{request.Id}\": product not found!",
};
}
var hasPriceChanged = existing.Price != request.Price;
existing.CategoryId = request.CategoryId;
existing.Description = request.Description;
existing.MSRP = request.MSRP;
existing.Name = request.Name;
existing.Price = request.Price;
existing.SKU = request.SKU;
existing.Summary = request.Summary;
existing.LastUpdated = DateTime.UtcNow;
existing.LastUpdatedUserId = request.LastUpdatedUserId;
_context.SaveChanges();
if (hasPriceChanged)
{
var cartsToUpdate =
_context.ShoppingCarts
.Include("Items")
.Where(cart => cart.Items.Any(x => x.SKU == request.SKU));
foreach (var cart in cartsToUpdate)
{
foreach (var cartItem in cart.Items.Where(x => x.SKU == request.SKU))
{
cartItem.Price = request.Price;
}
cart.Recalculate();
}
}
_context.SaveChanges();
return new UpdateProductResponse
{
Success = true,
Message = $"Successfully updated \"{request.Name}\"",
};
}
}
}

Binary file not shown.

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="6.2.0" targetFramework="net461" />
<package id="MediatR" version="4.1.0" targetFramework="net461" />
</packages>

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.15
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Website", "Website\Website.csproj", "{3BBC2FA4-9C8C-498C-A8B6-178FE7740438}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "Common\Common.csproj", "{7709A0D8-F311-4A8E-9E5B-05ED694449AD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3BBC2FA4-9C8C-498C-A8B6-178FE7740438}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BBC2FA4-9C8C-498C-A8B6-178FE7740438}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BBC2FA4-9C8C-498C-A8B6-178FE7740438}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BBC2FA4-9C8C-498C-A8B6-178FE7740438}.Release|Any CPU.Build.0 = Release|Any CPU
{7709A0D8-F311-4A8E-9E5B-05ED694449AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7709A0D8-F311-4A8E-9E5B-05ED694449AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7709A0D8-F311-4A8E-9E5B-05ED694449AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7709A0D8-F311-4A8E-9E5B-05ED694449AD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F60CE3BA-88D1-4A91-8B96-C2221F081EC1}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,34 @@
using System.Web;
using System.Web.Optimization;
namespace HPlusSports
{
public class BundleConfig
{
// For more information on bundling, visit https://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
// Use the development version of Modernizr to develop with and learn from. Then, when you're
// ready for production, use the build tool at https://modernizr.com to pick only the tests you need.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/respond.js"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
bundles.Add(new ScriptBundle("~/bundles/site").Include(
"~/Scripts/site.js"));
}
}
}

View File

@ -0,0 +1,15 @@
using HPlusSports.Models;
using System.Data.Entity;
namespace HPlusSports.App_Start
{
public class DatabaseConfig
{
public static void Initialize()
{
Database.SetInitializer(new HPlusSportsDbContextInitializer());
var context = new HPlusSportsDbContext();
context.Database.Initialize(true);
}
}
}

View File

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Autofac;
using Autofac.Integration.Mvc;
using MediatR;
namespace HPlusSports.App_Start
{
public class Dependencies
{
public static void Register()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(Dependencies).Assembly);
builder.RegisterType<HPlusSportsDbContext>()
.InstancePerRequest();
builder.RegisterType<HPlusSports.Services.ProductUpdateService>()
.InstancePerRequest();
// mediator itself
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
// request handlers
builder
.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => c.TryResolve(t, out var o) ? o : null;
})
.InstancePerLifetimeScope();
// notification handlers
builder
.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>)c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(typeof(Models.Product).Assembly)
.AsImplementedInterfaces();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
}

View File

@ -0,0 +1,15 @@
using System.Web;
using System.Web.Mvc;
using HPlusSports.Extensions;
namespace HPlusSports
{
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AllowPartialRenderingAttribute());
}
}
}

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using HPlusSports.Models;
namespace HPlusSports
{
public class EmailService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your email service here to send an email.
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
// Plug in your SMS service here to send a text message.
return Task.FromResult(0);
}
}
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
Initialized = Initalize(context);
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
#region DEMO CODE - Do not use in your real site!
const string AdminEmailAddress = "admin@hplussports.com";
static volatile bool Initialized = false;
private static bool Initalize(IOwinContext context)
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
if (!roleManager.RoleExists(UserRoles.Admin))
{
Console.Out.Write($"User {AdminEmailAddress} doesn't exist - creating... ");
var result = roleManager.Create(new IdentityRole(UserRoles.Admin));
if (result.Succeeded)
{
Console.Out.WriteLine("done.");
}
else
{
throw new Exception(String.Join("; ", result.Errors.ToArray()));
}
}
var userManager = context.GetUserManager<ApplicationUserManager>();
if (!userManager.Users.Any(x => x.Email == AdminEmailAddress))
{
Console.Out.Write($"User {AdminEmailAddress} doesn't exist - creating... ");
var adminUser = new ApplicationUser(AdminEmailAddress) { Email = AdminEmailAddress };
var result = userManager.Create(adminUser, "password");
if (result.Succeeded)
{
userManager.AddToRole(adminUser.Id, UserRoles.Admin);
Console.Out.WriteLine("done.");
}
else
{
throw new Exception(String.Join("; ", result.Errors.ToArray()));
}
}
return true;
}
#endregion
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace HPlusSports
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
using HPlusSports.Models;
namespace HPlusSports
{
public partial class Startup
{
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
}
}

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationInsights xmlns="http://schemas.microsoft.com/ApplicationInsights/2013/Settings">
<TelemetryInitializers>
<Add Type="Microsoft.ApplicationInsights.DependencyCollector.HttpDependenciesParsingTelemetryInitializer, Microsoft.AI.DependencyCollector"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.AzureWebAppRoleEnvironmentTelemetryInitializer, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.BuildInfoConfigComponentVersionTelemetryInitializer, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.Web.WebTestTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.SyntheticUserAgentTelemetryInitializer, Microsoft.AI.Web">
<!-- Extended list of bots:
search|spider|crawl|Bot|Monitor|BrowserMob|BingPreview|PagePeeker|WebThumb|URL2PNG|ZooShot|GomezA|Google SketchUp|Read Later|KTXN|KHTE|Keynote|Pingdom|AlwaysOn|zao|borg|oegp|silk|Xenu|zeal|NING|htdig|lycos|slurp|teoma|voila|yahoo|Sogou|CiBra|Nutch|Java|JNLP|Daumoa|Genieo|ichiro|larbin|pompos|Scrapy|snappy|speedy|vortex|favicon|indexer|Riddler|scooter|scraper|scrubby|WhatWeb|WinHTTP|voyager|archiver|Icarus6j|mogimogi|Netvibes|altavista|charlotte|findlinks|Retreiver|TLSProber|WordPress|wsr-agent|http client|Python-urllib|AppEngine-Google|semanticdiscovery|facebookexternalhit|web/snippet|Google-HTTP-Java-Client-->
<Filters>search|spider|crawl|Bot|Monitor|AlwaysOn</Filters>
</Add>
<Add Type="Microsoft.ApplicationInsights.Web.ClientIpHeaderTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.OperationNameTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.OperationCorrelationTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.UserTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.AuthenticatedUserIdTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.AccountIdTelemetryInitializer, Microsoft.AI.Web"/>
<Add Type="Microsoft.ApplicationInsights.Web.SessionTelemetryInitializer, Microsoft.AI.Web"/>
</TelemetryInitializers>
<TelemetryModules>
<Add Type="Microsoft.ApplicationInsights.DependencyCollector.DependencyTrackingTelemetryModule, Microsoft.AI.DependencyCollector"/>
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.PerformanceCollectorModule, Microsoft.AI.PerfCounterCollector">
<!--
Use the following syntax here to collect additional performance counters:
<Counters>
<Add PerformanceCounter="\Process(??APP_WIN32_PROC??)\Handle Count" ReportAs="Process handle count" />
...
</Counters>
PerformanceCounter must be either \CategoryName(InstanceName)\CounterName or \CategoryName\CounterName
NOTE: performance counters configuration will be lost upon NuGet upgrade.
The following placeholders are supported as InstanceName:
??APP_WIN32_PROC?? - instance name of the application process for Win32 counters.
??APP_W3SVC_PROC?? - instance name of the application IIS worker process for IIS/ASP.NET counters.
??APP_CLR_PROC?? - instance name of the application CLR process for .NET counters.
-->
</Add>
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse.QuickPulseTelemetryModule, Microsoft.AI.PerfCounterCollector"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.DeveloperModeWithDebuggerAttachedTelemetryModule, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnhandledExceptionTelemetryModule, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.UnobservedExceptionTelemetryModule, Microsoft.AI.WindowsServer"/>
<Add Type="Microsoft.ApplicationInsights.Web.RequestTrackingTelemetryModule, Microsoft.AI.Web">
<Handlers>
<!--
Add entries here to filter out additional handlers:
NOTE: handler configuration will be lost upon NuGet upgrade.
-->
<Add>System.Web.Handlers.TransferRequestHandler</Add>
<Add>Microsoft.VisualStudio.Web.PageInspector.Runtime.Tracing.RequestDataHttpHandler</Add>
<Add>System.Web.StaticFileHandler</Add>
<Add>System.Web.Handlers.AssemblyResourceLoader</Add>
<Add>System.Web.Optimization.BundleHandler</Add>
<Add>System.Web.Script.Services.ScriptHandlerFactory</Add>
<Add>System.Web.Handlers.TraceHandler</Add>
<Add>System.Web.Services.Discovery.DiscoveryRequestHandler</Add>
<Add>System.Web.HttpDebugHandler</Add>
</Handlers>
</Add>
<Add Type="Microsoft.ApplicationInsights.Web.ExceptionTrackingTelemetryModule, Microsoft.AI.Web"/>
</TelemetryModules>
<TelemetryProcessors>
<Add Type="Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse.QuickPulseTelemetryProcessor, Microsoft.AI.PerfCounterCollector"/>
<Add Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.AdaptiveSamplingTelemetryProcessor, Microsoft.AI.ServerTelemetryChannel">
<MaxTelemetryItemsPerSecond>5</MaxTelemetryItemsPerSecond>
</Add>
</TelemetryProcessors>
<TelemetryChannel Type="Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.ServerTelemetryChannel, Microsoft.AI.ServerTelemetryChannel"/>
<!--
Learn more about Application Insights configuration with ApplicationInsights.config here:
http://go.microsoft.com/fwlink/?LinkID=513840
Note: If not present, please add <InstrumentationKey>Your Key</InstrumentationKey> to the top of this file.
--></ApplicationInsights>

View File

@ -0,0 +1,191 @@
body {
padding-bottom: 20px;
}
body, button, input, textarea {
color: #43454b;
font-family: "Source Sans Pro",HelveticaNeue-Light,"Helvetica Neue Light","Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;
line-height: 1.618;
text-rendering: optimizeLegibility;
font-weight: 400;
}
h1, h2, h3, h4, h5, h6 {
color: #c0af00;
clear: both;
font-weight: 300;
}
a, a:visited, a:hover {
color: #c0af00;
}
.btn {
font-weight: bold
}
.btn-warning, a:visited.btn-warning, a.btn-warning, a:hover.btn-warning {
color: #ffffff;
}
.navbar {
clear: both;
}
.navbar-nav {
padding-top: 2em;
}
.well-lg {
font-size: 120%;
}
.dropbtn.nav-cart {
font-size: 20px;
}
.title h1 {
font-weight: bold;
padding-top: 1em;
}
#main {
padding-left: 15px;
padding-right: 15px;
}
.category {
height: 370px;
}
.category h3 {
margin-top: .5em;
}
.category img {
width: 20em;
}
.pager a {
font-weight: bold;
font-size: 110%;
}
/* Override the default bootstrap behavior where horizontal description lists
will truncate terms that are too long to fit in the left column
*/
.dl-horizontal dt {
white-space: normal;
}
/* Set width on the form input elements since they're 100% wide by default */
input,
select,
textarea {
max-width: 280px;
}
/* Dropdown Button */
.dropbtn {
padding: 16px;
font-size: 16px;
border: none;
}
/* The container <div> - needed to position the dropdown content */
.dropdown {
position: relative;
display: inline-block;
}
/* Dropdown Content (Hidden by Default) */
.dropdown-content {
display: none;
position: absolute;
right: 0;
width: 500px;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
padding:20px;
margin:0;
}
.gi-2x{font-size: 2em;}
.gi-3x{font-size: 3em;}
.gi-4x{font-size: 4em;}
.gi-5x{font-size: 5em;}
.text-large {
font-size: 130%;
}
.text-middle,
.text-middle td {
vertical-align: middle !important;
}
/* Show the dropdown menu on hover */
.dropdown:hover .dropdown-content {
display: block;
}
.nav.navbar-nav .btn {
margin-top: 15px;
}
.rating-star {
color: #f9db2c;
font-weight: bold;
}
.cart .product-image {
width: 5em;
height: 5em;
margin-right: 1em;
}
.product-summary {
margin-bottom: 1em;
height: 24em;
}
.product-summary .product-image {
display: block;
clear: both;
width: 70%;
margin: auto;
}
.product-summary .name {
font-size: 120%;
font-weight: bold;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.product .price,
.product-summary .price {
font-size: 130%;
}
.product .buy-button {
margin-top: 1em;
}
.tablesorter-default .header,
.tablesorter-default .tablesorter-header {
background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAACMtMP///yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==);
background-position: center right;
background-repeat: no-repeat;
cursor: pointer;
white-space: normal;
padding: 4px 20px 4px 4px;
}
th.tablesorter-header[disabled] {
background-image: none !important;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View 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
}
}

View 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;
}
}

View 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();
}
}
}

View 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"
};
}
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Linq;
using System.Web.Mvc;
using HPlusSports.Models;
using HPlusSports.Requests;
using HPlusSports.Services;
namespace HPlusSports.Controllers
{
[Authorize(Roles = UserRoles.Admin)]
public class InventoryController : Controller
{
private HPlusSportsDbContext _context;
private readonly ProductUpdateService _updateService;
public InventoryController(
HPlusSportsDbContext context,
ProductUpdateService updateService)
{
_context = context;
_updateService = updateService;
}
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 ActionResult Update(UpdateProductRequest request)
{
if (!ModelState.IsValid)
{
return View();
}
request.LastUpdatedUserId = GetUserId(this);
var response = _updateService.Update(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;
}
}

View 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);
}
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace HPlusSports.Extensions
{
public class AllowPartialRenderingAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var request = filterContext.HttpContext.Request;
var result = filterContext.Result as ViewResult;
if (request == null)
return;
if (request.IsAjaxRequest())
{
filterContext.Result = new PartialViewResult
{
TempData = result.TempData,
View = result.View,
ViewData = result.ViewData,
ViewEngineCollection = result.ViewEngineCollection,
ViewName = result.ViewName,
};
}
}
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc.Html;
namespace HPlusSports
{
public static class HtmlHelperExtensions
{
public static IHtmlString Rating(this System.Web.Mvc.HtmlHelper html, Models.ProductRating rating)
{
if (rating == null || rating.ReviewCount == 0)
{
return new System.Web.Mvc.MvcHtmlString("<span><em>No Rating</em></span>");
}
return html.Partial("_Rating", rating);
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace HPlusSports
{
public static class TempDataExtensions
{
const string ErrorMessageKey = "ErrorMessage";
const string SuccessMessageKey = "SuccessMessage";
public static bool HasSuccessMessage(this System.Web.Mvc.TempDataDictionary tempData)
{
return !string.IsNullOrWhiteSpace(tempData[SuccessMessageKey] as string);
}
public static string SuccessMessage(this System.Web.Mvc.TempDataDictionary tempData)
{
return tempData[SuccessMessageKey] as string;
}
public static void SuccessMessage(this System.Web.Mvc.TempDataDictionary tempData, string successMessage)
{
tempData[SuccessMessageKey] = successMessage;
}
public static bool HasErrorMessage(this System.Web.Mvc.TempDataDictionary tempData)
{
return !string.IsNullOrWhiteSpace(tempData[ErrorMessageKey] as string);
}
public static string ErrorMessage(this System.Web.Mvc.TempDataDictionary tempData)
{
return tempData[ErrorMessageKey] as string;
}
public static void ErrorMessage(this System.Web.Mvc.TempDataDictionary tempData, string errorMessage)
{
tempData[ErrorMessageKey] = errorMessage;
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using HPlusSports.Models;
namespace HPlusSports
{
public static class UrlHelperExtensions
{
public static string Product(this System.Web.Mvc.UrlHelper urlHelper, Product product)
{
return Product(urlHelper, product?.SKU);
}
public static string Product(this System.Web.Mvc.UrlHelper urlHelper, string sku)
{
return urlHelper.Action("Product", "Products", new { id = sku });
}
}
}

View File

@ -0,0 +1 @@
<%@ Application Codebehind="Global.asax.cs" Inherits="HPlusSports.Application" Language="C#" %>

View File

@ -0,0 +1,23 @@
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using HPlusSports.App_Start;
using HPlusSports.Models;
namespace HPlusSports
{
public class Application : HttpApplication
{
protected void Application_Start()
{
Dependencies.Register();
DatabaseConfig.Initialize();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace HPlusSports.Models
{
public class CreateProductRequest
{
[Required]
public long CategoryId { get; set; }
[Required]
public string SKU { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Text)]
public string Summary { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double MSRP { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double Price { get; set; }
}
}

View File

@ -0,0 +1,111 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Models
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
}
public class ExternalLoginListViewModel
{
public string ReturnUrl { get; set; }
}
public class SendCodeViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
public string ReturnUrl { get; set; }
public bool RememberMe { get; set; }
}
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Remember this browser?")]
public bool RememberBrowser { get; set; }
public bool RememberMe { get; set; }
}
public class ForgotViewModel
{
[Required]
[Display(Name = "Email")]
public string Email { get; set; }
}
public class LoginViewModel
{
[Required]
[Display(Name = "Username")]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
}
}

View File

@ -0,0 +1,47 @@
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace HPlusSports.Models
{
public static class UserRoles
{
public const string Admin = "Admin";
public const string ProductAdmin = "ProductAdmin";
}
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public ApplicationUser() : base()
{
}
public ApplicationUser(string username) : base(username)
{
}
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("HPlusSports_Identity", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}

View File

@ -0,0 +1,86 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
namespace HPlusSports.Models
{
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
public class FactorViewModel
{
public string Purpose { get; set; }
}
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Current password")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "New password")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm new password")]
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string Number { get; set; }
}
public class VerifyPhoneNumberViewModel
{
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
}
public class ConfigureTwoFactorViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace HPlusSports.Models
{
public class ProductsListViewModel
{
public IEnumerable<ProductViewModel> Products { get; set; }
}
public class ProductViewModel
{
public const double MinimumAdvertisableDiscount = 0.05;
public string SKU { get; set; }
public string Name { get; set; }
[DataType(DataType.Currency)]
public double MSRP { get; set; }
[DataType(DataType.Currency)]
public double Price { get; set; }
[DisplayFormat(DataFormatString = "{0:P0}")]
public double? Discount
{
get { return (MSRP - Price) / MSRP; }
}
public bool HasAdvertisableDiscount
{
get { return Discount > MinimumAdvertisableDiscount; }
}
public ProductRating Rating { get; set; }
}
}

View File

@ -0,0 +1,35 @@
using System.ComponentModel.DataAnnotations;
namespace HPlusSports.Models
{
public class UpdateProductRequest
{
[Required]
public long Id { get; set; }
[Required]
public long CategoryId { get; set; }
[Required]
public string SKU { get; set; }
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.Text)]
public string Summary { get; set; }
[Required]
[DataType(DataType.MultilineText)]
public string Description { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double MSRP { get; set; }
[Range(minimum: 0, maximum: double.MaxValue)]
[DataType(DataType.Currency)]
public double Price { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("HPlusSports")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("HPlusSports")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("89121440-93bf-4393-8ae5-8202aa180b65")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: InternalsVisibleTo("HPlusSports.Tests")]

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,429 @@
/* NUGET: BEGIN LICENSE TEXT
*
* Microsoft grants you the right to use these script files for the sole
* purpose of either: (i) interacting through your browser with the Microsoft
* website or online service, subject to the applicable licensing or use
* terms; or (ii) using the files as included with a Microsoft product subject
* to that product's license terms. Microsoft reserves all other rights to the
* files not expressly granted by Microsoft, whether by implication, estoppel
* or otherwise. Insofar as a script file is dual licensed under GPL,
* Microsoft neither took the code under GPL nor distributes it thereunder but
* under the terms set out in this paragraph. All notices and licenses
* below are for informational purposes only.
*
* NUGET: END LICENSE TEXT */
/*!
** Unobtrusive validation support library for jQuery and jQuery Validate
** Copyright (C) Microsoft Corporation. All rights reserved.
*/
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
/*global document: false, jQuery: false */
(function ($) {
var $jQval = $.validator,
adapters,
data_validation = "unobtrusiveValidation";
function setValidationValues(options, ruleName, value) {
options.rules[ruleName] = value;
if (options.message) {
options.messages[ruleName] = options.message;
}
}
function splitAndTrim(value) {
return value.replace(/^\s+|\s+$/g, "").split(/\s*,\s*/g);
}
function escapeAttributeValue(value) {
// As mentioned on http://api.jquery.com/category/selectors/
return value.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g, "\\$1");
}
function getModelPrefix(fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
}
function appendModelPrefix(value, prefix) {
if (value.indexOf("*.") === 0) {
value = value.replace("*.", prefix);
}
return value;
}
function onError(error, inputElement) { // 'this' is the form element
var container = $(this).find("[data-valmsg-for='" + escapeAttributeValue(inputElement[0].name) + "']"),
replaceAttrValue = container.attr("data-valmsg-replace"),
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null;
container.removeClass("field-validation-valid").addClass("field-validation-error");
error.data("unobtrusiveContainer", container);
if (replace) {
container.empty();
error.removeClass("input-validation-error").appendTo(container);
}
else {
error.hide();
}
}
function onErrors(event, validator) { // 'this' is the form element
var container = $(this).find("[data-valmsg-summary=true]"),
list = container.find("ul");
if (list && list.length && validator.errorList.length) {
list.empty();
container.addClass("validation-summary-errors").removeClass("validation-summary-valid");
$.each(validator.errorList, function () {
$("<li />").html(this.message).appendTo(list);
});
}
}
function onSuccess(error) { // 'this' is the form element
var container = error.data("unobtrusiveContainer"),
replaceAttrValue = container.attr("data-valmsg-replace"),
replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) : null;
if (container) {
container.addClass("field-validation-valid").removeClass("field-validation-error");
error.removeData("unobtrusiveContainer");
if (replace) {
container.empty();
}
}
}
function onReset(event) { // 'this' is the form element
var $form = $(this),
key = '__jquery_unobtrusive_validation_form_reset';
if ($form.data(key)) {
return;
}
// Set a flag that indicates we're currently resetting the form.
$form.data(key, true);
try {
$form.data("validator").resetForm();
} finally {
$form.removeData(key);
}
$form.find(".validation-summary-errors")
.addClass("validation-summary-valid")
.removeClass("validation-summary-errors");
$form.find(".field-validation-error")
.addClass("field-validation-valid")
.removeClass("field-validation-error")
.removeData("unobtrusiveContainer")
.find(">*") // If we were using valmsg-replace, get the underlying error
.removeData("unobtrusiveContainer");
}
function validationInfo(form) {
var $form = $(form),
result = $form.data(data_validation),
onResetProxy = $.proxy(onReset, form),
defaultOptions = $jQval.unobtrusive.options || {},
execInContext = function (name, args) {
var func = defaultOptions[name];
func && $.isFunction(func) && func.apply(form, args);
}
if (!result) {
result = {
options: { // options structure passed to jQuery Validate's validate() method
errorClass: defaultOptions.errorClass || "input-validation-error",
errorElement: defaultOptions.errorElement || "span",
errorPlacement: function () {
onError.apply(form, arguments);
execInContext("errorPlacement", arguments);
},
invalidHandler: function () {
onErrors.apply(form, arguments);
execInContext("invalidHandler", arguments);
},
messages: {},
rules: {},
success: function () {
onSuccess.apply(form, arguments);
execInContext("success", arguments);
}
},
attachValidation: function () {
$form
.off("reset." + data_validation, onResetProxy)
.on("reset." + data_validation, onResetProxy)
.validate(this.options);
},
validate: function () { // a validation function that is called by unobtrusive Ajax
$form.validate();
return $form.valid();
}
};
$form.data(data_validation, result);
}
return result;
}
$jQval.unobtrusive = {
adapters: [],
parseElement: function (element, skipAttach) {
/// <summary>
/// Parses a single HTML element for unobtrusive validation attributes.
/// </summary>
/// <param name="element" domElement="true">The HTML element to be parsed.</param>
/// <param name="skipAttach" type="Boolean">[Optional] true to skip attaching the
/// validation to the form. If parsing just this single element, you should specify true.
/// If parsing several elements, you should specify false, and manually attach the validation
/// to the form when you are finished. The default is false.</param>
var $element = $(element),
form = $element.parents("form")[0],
valInfo, rules, messages;
if (!form) { // Cannot do client-side validation without a form
return;
}
valInfo = validationInfo(form);
valInfo.options.rules[element.name] = rules = {};
valInfo.options.messages[element.name] = messages = {};
$.each(this.adapters, function () {
var prefix = "data-val-" + this.name,
message = $element.attr(prefix),
paramValues = {};
if (message !== undefined) { // Compare against undefined, because an empty message is legal (and falsy)
prefix += "-";
$.each(this.params, function () {
paramValues[this] = $element.attr(prefix + this);
});
this.adapt({
element: element,
form: form,
message: message,
params: paramValues,
rules: rules,
messages: messages
});
}
});
$.extend(rules, { "__dummy__": true });
if (!skipAttach) {
valInfo.attachValidation();
}
},
parse: function (selector) {
/// <summary>
/// Parses all the HTML elements in the specified selector. It looks for input elements decorated
/// with the [data-val=true] attribute value and enables validation according to the data-val-*
/// attribute values.
/// </summary>
/// <param name="selector" type="String">Any valid jQuery selector.</param>
// $forms includes all forms in selector's DOM hierarchy (parent, children and self) that have at least one
// element with data-val=true
var $selector = $(selector),
$forms = $selector.parents()
.addBack()
.filter("form")
.add($selector.find("form"))
.has("[data-val=true]");
$selector.find("[data-val=true]").each(function () {
$jQval.unobtrusive.parseElement(this, true);
});
$forms.each(function () {
var info = validationInfo(this);
if (info) {
info.attachValidation();
}
});
}
};
adapters = $jQval.unobtrusive.adapters;
adapters.add = function (adapterName, params, fn) {
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation.</summary>
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
/// <param name="params" type="Array" optional="true">[Optional] An array of parameter names (strings) that will
/// be extracted from the data-val-nnnn-mmmm HTML attributes (where nnnn is the adapter name, and
/// mmmm is the parameter name).</param>
/// <param name="fn" type="Function">The function to call, which adapts the values from the HTML
/// attributes into jQuery Validate rules and/or messages.</param>
/// <returns type="jQuery.validator.unobtrusive.adapters" />
if (!fn) { // Called with no params, just a function
fn = params;
params = [];
}
this.push({ name: adapterName, params: params, adapt: fn });
return this;
};
adapters.addBool = function (adapterName, ruleName) {
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
/// the jQuery Validate validation rule has no parameter values.</summary>
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
/// of adapterName will be used instead.</param>
/// <returns type="jQuery.validator.unobtrusive.adapters" />
return this.add(adapterName, function (options) {
setValidationValues(options, ruleName || adapterName, true);
});
};
adapters.addMinMax = function (adapterName, minRuleName, maxRuleName, minMaxRuleName, minAttribute, maxAttribute) {
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
/// the jQuery Validate validation has three potential rules (one for min-only, one for max-only, and
/// one for min-and-max). The HTML parameters are expected to be named -min and -max.</summary>
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
/// in the data-val-nnnn HTML attribute (where nnnn is the adapter name).</param>
/// <param name="minRuleName" type="String">The name of the jQuery Validate rule to be used when you only
/// have a minimum value.</param>
/// <param name="maxRuleName" type="String">The name of the jQuery Validate rule to be used when you only
/// have a maximum value.</param>
/// <param name="minMaxRuleName" type="String">The name of the jQuery Validate rule to be used when you
/// have both a minimum and maximum value.</param>
/// <param name="minAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
/// contains the minimum value. The default is "min".</param>
/// <param name="maxAttribute" type="String" optional="true">[Optional] The name of the HTML attribute that
/// contains the maximum value. The default is "max".</param>
/// <returns type="jQuery.validator.unobtrusive.adapters" />
return this.add(adapterName, [minAttribute || "min", maxAttribute || "max"], function (options) {
var min = options.params.min,
max = options.params.max;
if (min && max) {
setValidationValues(options, minMaxRuleName, [min, max]);
}
else if (min) {
setValidationValues(options, minRuleName, min);
}
else if (max) {
setValidationValues(options, maxRuleName, max);
}
});
};
adapters.addSingleVal = function (adapterName, attribute, ruleName) {
/// <summary>Adds a new adapter to convert unobtrusive HTML into a jQuery Validate validation, where
/// the jQuery Validate validation rule has a single value.</summary>
/// <param name="adapterName" type="String">The name of the adapter to be added. This matches the name used
/// in the data-val-nnnn HTML attribute(where nnnn is the adapter name).</param>
/// <param name="attribute" type="String">[Optional] The name of the HTML attribute that contains the value.
/// The default is "val".</param>
/// <param name="ruleName" type="String" optional="true">[Optional] The name of the jQuery Validate rule. If not provided, the value
/// of adapterName will be used instead.</param>
/// <returns type="jQuery.validator.unobtrusive.adapters" />
return this.add(adapterName, [attribute || "val"], function (options) {
setValidationValues(options, ruleName || adapterName, options.params[attribute]);
});
};
$jQval.addMethod("__dummy__", function (value, element, params) {
return true;
});
$jQval.addMethod("regex", function (value, element, params) {
var match;
if (this.optional(element)) {
return true;
}
match = new RegExp(params).exec(value);
return (match && (match.index === 0) && (match[0].length === value.length));
});
$jQval.addMethod("nonalphamin", function (value, element, nonalphamin) {
var match;
if (nonalphamin) {
match = value.match(/\W/g);
match = match && match.length >= nonalphamin;
}
return match;
});
if ($jQval.methods.extension) {
adapters.addSingleVal("accept", "mimtype");
adapters.addSingleVal("extension", "extension");
} else {
// for backward compatibility, when the 'extension' validation method does not exist, such as with versions
// of JQuery Validation plugin prior to 1.10, we should use the 'accept' method for
// validating the extension, and ignore mime-type validations as they are not supported.
adapters.addSingleVal("extension", "extension", "accept");
}
adapters.addSingleVal("regex", "pattern");
adapters.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");
adapters.addMinMax("length", "minlength", "maxlength", "rangelength").addMinMax("range", "min", "max", "range");
adapters.addMinMax("minlength", "minlength").addMinMax("maxlength", "minlength", "maxlength");
adapters.add("equalto", ["other"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(fullOtherName) + "']")[0];
setValidationValues(options, "equalTo", element);
});
adapters.add("required", function (options) {
// jQuery Validate equates "required" with "mandatory" for checkbox elements
if (options.element.tagName.toUpperCase() !== "INPUT" || options.element.type.toUpperCase() !== "CHECKBOX") {
setValidationValues(options, "required", true);
}
});
adapters.add("remote", ["url", "type", "additionalfields"], function (options) {
var value = {
url: options.params.url,
type: options.params.type || "GET",
data: {}
},
prefix = getModelPrefix(options.element.name);
$.each(splitAndTrim(options.params.additionalfields || options.element.name), function (i, fieldName) {
var paramName = appendModelPrefix(fieldName, prefix);
value.data[paramName] = function () {
var field = $(options.form).find(":input").filter("[name='" + escapeAttributeValue(paramName) + "']");
// For checkboxes and radio buttons, only pick up values from checked fields.
if (field.is(":checkbox")) {
return field.filter(":checked").val() || field.filter(":hidden").val() || '';
}
else if (field.is(":radio")) {
return field.filter(":checked").val() || '';
}
return field.val();
};
});
setValidationValues(options, "remote", value);
});
adapters.add("password", ["min", "nonalphamin", "regex"], function (options) {
if (options.params.min) {
setValidationValues(options, "minlength", options.params.min);
}
if (options.params.nonalphamin) {
setValidationValues(options, "nonalphamin", options.params.nonalphamin);
}
if (options.params.regex) {
setValidationValues(options, "regex", options.params.regex);
}
});
$(function () {
$jQval.unobtrusive.parse(document);
});
}(jQuery));

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,340 @@
/* NUGET: BEGIN LICENSE TEXT
*
* Microsoft grants you the right to use these script files for the sole
* purpose of either: (i) interacting through your browser with the Microsoft
* website or online service, subject to the applicable licensing or use
* terms; or (ii) using the files as included with a Microsoft product subject
* to that product's license terms. Microsoft reserves all other rights to the
* files not expressly granted by Microsoft, whether by implication, estoppel
* or otherwise. Insofar as a script file is dual licensed under GPL,
* Microsoft neither took the code under GPL nor distributes it thereunder but
* under the terms set out in this paragraph. All notices and licenses
* below are for informational purposes only.
*
* NUGET: END LICENSE TEXT */
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
window.matchMedia = window.matchMedia || (function(doc, undefined){
var bool,
docElem = doc.documentElement,
refNode = docElem.firstElementChild || docElem.firstChild,
// fakeBody required for <FF4 when executed in <head>
fakeBody = doc.createElement('body'),
div = doc.createElement('div');
div.id = 'mq-test-1';
div.style.cssText = "position:absolute;top:-100em";
fakeBody.style.background = "none";
fakeBody.appendChild(div);
return function(q){
div.innerHTML = '&shy;<style media="'+q+'"> #mq-test-1 { width: 42px; }</style>';
docElem.insertBefore(fakeBody, refNode);
bool = div.offsetWidth == 42;
docElem.removeChild(fakeBody);
return { matches: bool, media: q };
};
})(document);
/*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
(function( win ){
//exposed namespace
win.respond = {};
//define update even in native-mq-supporting browsers, to avoid errors
respond.update = function(){};
//expose media query support flag for external use
respond.mediaQueriesSupported = win.matchMedia && win.matchMedia( "only all" ).matches;
//if media queries are supported, exit here
if( respond.mediaQueriesSupported ){ return; }
//define vars
var doc = win.document,
docElem = doc.documentElement,
mediastyles = [],
rules = [],
appendedEls = [],
parsedSheets = {},
resizeThrottle = 30,
head = doc.getElementsByTagName( "head" )[0] || docElem,
base = doc.getElementsByTagName( "base" )[0],
links = head.getElementsByTagName( "link" ),
requestQueue = [],
//loop stylesheets, send text content to translate
ripCSS = function(){
var sheets = links,
sl = sheets.length,
i = 0,
//vars for loop:
sheet, href, media, isCSS;
for( ; i < sl; i++ ){
sheet = sheets[ i ],
href = sheet.href,
media = sheet.media,
isCSS = sheet.rel && sheet.rel.toLowerCase() === "stylesheet";
//only links plz and prevent re-parsing
if( !!href && isCSS && !parsedSheets[ href ] ){
// selectivizr exposes css through the rawCssText expando
if (sheet.styleSheet && sheet.styleSheet.rawCssText) {
translate( sheet.styleSheet.rawCssText, href, media );
parsedSheets[ href ] = true;
} else {
if( (!/^([a-zA-Z:]*\/\/)/.test( href ) && !base)
|| href.replace( RegExp.$1, "" ).split( "/" )[0] === win.location.host ){
requestQueue.push( {
href: href,
media: media
} );
}
}
}
}
makeRequests();
},
//recurse through request queue, get css text
makeRequests = function(){
if( requestQueue.length ){
var thisRequest = requestQueue.shift();
ajax( thisRequest.href, function( styles ){
translate( styles, thisRequest.href, thisRequest.media );
parsedSheets[ thisRequest.href ] = true;
makeRequests();
} );
}
},
//find media blocks in css text, convert to style blocks
translate = function( styles, href, media ){
var qs = styles.match( /@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi ),
ql = qs && qs.length || 0,
//try to get CSS path
href = href.substring( 0, href.lastIndexOf( "/" )),
repUrls = function( css ){
return css.replace( /(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g, "$1" + href + "$2$3" );
},
useMedia = !ql && media,
//vars used in loop
i = 0,
j, fullq, thisq, eachq, eql;
//if path exists, tack on trailing slash
if( href.length ){ href += "/"; }
//if no internal queries exist, but media attr does, use that
//note: this currently lacks support for situations where a media attr is specified on a link AND
//its associated stylesheet has internal CSS media queries.
//In those cases, the media attribute will currently be ignored.
if( useMedia ){
ql = 1;
}
for( ; i < ql; i++ ){
j = 0;
//media attr
if( useMedia ){
fullq = media;
rules.push( repUrls( styles ) );
}
//parse for styles
else{
fullq = qs[ i ].match( /@media *([^\{]+)\{([\S\s]+?)$/ ) && RegExp.$1;
rules.push( RegExp.$2 && repUrls( RegExp.$2 ) );
}
eachq = fullq.split( "," );
eql = eachq.length;
for( ; j < eql; j++ ){
thisq = eachq[ j ];
mediastyles.push( {
media : thisq.split( "(" )[ 0 ].match( /(only\s+)?([a-zA-Z]+)\s?/ ) && RegExp.$2 || "all",
rules : rules.length - 1,
hasquery: thisq.indexOf("(") > -1,
minw : thisq.match( /\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" ),
maxw : thisq.match( /\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/ ) && parseFloat( RegExp.$1 ) + ( RegExp.$2 || "" )
} );
}
}
applyMedia();
},
lastCall,
resizeDefer,
// returns the value of 1em in pixels
getEmValue = function() {
var ret,
div = doc.createElement('div'),
body = doc.body,
fakeUsed = false;
div.style.cssText = "position:absolute;font-size:1em;width:1em";
if( !body ){
body = fakeUsed = doc.createElement( "body" );
body.style.background = "none";
}
body.appendChild( div );
docElem.insertBefore( body, docElem.firstChild );
ret = div.offsetWidth;
if( fakeUsed ){
docElem.removeChild( body );
}
else {
body.removeChild( div );
}
//also update eminpx before returning
ret = eminpx = parseFloat(ret);
return ret;
},
//cached container for 1em value, populated the first time it's needed
eminpx,
//enable/disable styles
applyMedia = function( fromResize ){
var name = "clientWidth",
docElemProp = docElem[ name ],
currWidth = doc.compatMode === "CSS1Compat" && docElemProp || doc.body[ name ] || docElemProp,
styleBlocks = {},
lastLink = links[ links.length-1 ],
now = (new Date()).getTime();
//throttle resize calls
if( fromResize && lastCall && now - lastCall < resizeThrottle ){
clearTimeout( resizeDefer );
resizeDefer = setTimeout( applyMedia, resizeThrottle );
return;
}
else {
lastCall = now;
}
for( var i in mediastyles ){
var thisstyle = mediastyles[ i ],
min = thisstyle.minw,
max = thisstyle.maxw,
minnull = min === null,
maxnull = max === null,
em = "em";
if( !!min ){
min = parseFloat( min ) * ( min.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
}
if( !!max ){
max = parseFloat( max ) * ( max.indexOf( em ) > -1 ? ( eminpx || getEmValue() ) : 1 );
}
// if there's no media query at all (the () part), or min or max is not null, and if either is present, they're true
if( !thisstyle.hasquery || ( !minnull || !maxnull ) && ( minnull || currWidth >= min ) && ( maxnull || currWidth <= max ) ){
if( !styleBlocks[ thisstyle.media ] ){
styleBlocks[ thisstyle.media ] = [];
}
styleBlocks[ thisstyle.media ].push( rules[ thisstyle.rules ] );
}
}
//remove any existing respond style element(s)
for( var i in appendedEls ){
if( appendedEls[ i ] && appendedEls[ i ].parentNode === head ){
head.removeChild( appendedEls[ i ] );
}
}
//inject active styles, grouped by media type
for( var i in styleBlocks ){
var ss = doc.createElement( "style" ),
css = styleBlocks[ i ].join( "\n" );
ss.type = "text/css";
ss.media = i;
//originally, ss was appended to a documentFragment and sheets were appended in bulk.
//this caused crashes in IE in a number of circumstances, such as when the HTML element had a bg image set, so appending beforehand seems best. Thanks to @dvelyk for the initial research on this one!
head.insertBefore( ss, lastLink.nextSibling );
if ( ss.styleSheet ){
ss.styleSheet.cssText = css;
}
else {
ss.appendChild( doc.createTextNode( css ) );
}
//push to appendedEls to track for later removal
appendedEls.push( ss );
}
},
//tweaked Ajax functions from Quirksmode
ajax = function( url, callback ) {
var req = xmlHttp();
if (!req){
return;
}
req.open( "GET", url, true );
req.onreadystatechange = function () {
if ( req.readyState != 4 || req.status != 200 && req.status != 304 ){
return;
}
callback( req.responseText );
}
if ( req.readyState == 4 ){
return;
}
req.send( null );
},
//define ajax obj
xmlHttp = (function() {
var xmlhttpmethod = false;
try {
xmlhttpmethod = new XMLHttpRequest();
}
catch( e ){
xmlhttpmethod = new ActiveXObject( "Microsoft.XMLHTTP" );
}
return function(){
return xmlhttpmethod;
};
})();
//translate CSS
ripCSS();
//expose update for re-running respond later on
respond.update = ripCSS;
//adjust on resize
function callMedia(){
applyMedia( true );
}
if( win.addEventListener ){
win.addEventListener( "resize", callMedia, false );
}
else if( win.attachEvent ){
win.attachEvent( "onresize", callMedia );
}
})(this);

View File

@ -0,0 +1,20 @@
/* NUGET: BEGIN LICENSE TEXT
*
* Microsoft grants you the right to use these script files for the sole
* purpose of either: (i) interacting through your browser with the Microsoft
* website or online service, subject to the applicable licensing or use
* terms; or (ii) using the files as included with a Microsoft product subject
* to that product's license terms. Microsoft reserves all other rights to the
* files not expressly granted by Microsoft, whether by implication, estoppel
* or otherwise. Insofar as a script file is dual licensed under GPL,
* Microsoft neither took the code under GPL nor distributes it thereunder but
* under the terms set out in this paragraph. All notices and licenses
* below are for informational purposes only.
*
* NUGET: END LICENSE TEXT */
/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */
/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */
window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='&shy;<style media="'+h+'"> #mq-test-1 { width: 42px; }</style>';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document);
/*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */
(function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B<y;B++){A=D[B],z=A.href,C=A.media,x=A.rel&&A.rel.toLowerCase()==="stylesheet";if(!!z&&x&&!o[z]){if(A.styleSheet&&A.styleSheet.rawCssText){m(A.styleSheet.rawCssText,z,C);o[z]=true}else{if((!/^([a-zA-Z:]*\/\/)/.test(z)&&!g)||z.replace(RegExp.$1,"").split("/")[0]===e.location.host){d.push({href:z,media:C})}}}}u()},u=function(){if(d.length){var x=d.shift();n(x.href,function(y){m(y,x.href,x.media);o[x.href]=true;u()})}},m=function(I,x,z){var G=I.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),J=G&&G.length||0,x=x.substring(0,x.lastIndexOf("/")),y=function(K){return K.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+x+"$2$3")},A=!J&&z,D=0,C,E,F,B,H;if(x.length){x+="/"}if(A){J=1}for(;D<J;D++){C=0;if(A){E=z;k.push(y(I))}else{E=G[D].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1;k.push(RegExp.$2&&y(RegExp.$2))}B=E.split(",");H=B.length;for(;C<H;C++){F=B[C];i.push({media:F.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:k.length-1,hasquery:F.indexOf("(")>-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l<h){clearTimeout(r);r=setTimeout(j,h);return}else{l=z}for(var E in i){var K=i[E],C=K.minw,J=K.maxw,A=C===null,L=J===null,y="em";if(!!C){C=parseFloat(C)*(C.indexOf(y)>-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this);

View File

@ -0,0 +1,15 @@
function onInit() {
$('table.tablesorter').tablesorter();
var $content = $('#main');
$content.on('click', '[data-partial]', function (evt) {
var url = $(evt.target).attr('href');
$content.load(url);
evt.preventDefault();
evt.stopPropagation();
})
}
$(onInit)

View File

@ -0,0 +1,14 @@
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(HPlusSports.Startup))]
namespace HPlusSports
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}

View File

@ -0,0 +1,10 @@
@{
ViewBag.Title = "Confirm Email";
}
<h2>@ViewBag.Title.</h2>
<div>
<p>
Thank you for confirming your email. Please @Html.ActionLink("Click here to Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
</p>
</div>

View File

@ -0,0 +1,36 @@
@model HPlusSports.Models.ExternalLoginConfirmationViewModel
@{
ViewBag.Title = "Register";
}
<h2>@ViewBag.Title.</h2>
<h3>Associate your @ViewBag.LoginProvider account.</h3>
@using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Association Form</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<p class="text-info">
You've successfully authenticated with <strong>@ViewBag.LoginProvider</strong>.
Please enter a user name for this site below and click the Register button to finish
logging in.
</p>
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,8 @@
@{
ViewBag.Title = "Login Failure";
}
<hgroup>
<h2>@ViewBag.Title.</h2>
<h3 class="text-danger">Unsuccessful login with service.</h3>
</hgroup>

View File

@ -0,0 +1,29 @@
@model HPlusSports.Models.ForgotPasswordViewModel
@{
ViewBag.Title = "Forgot your password?";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("ForgotPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Enter your email.</h4>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Email Link" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,13 @@
@{
ViewBag.Title = "Forgot Password Confirmation";
}
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
</hgroup>
<div>
<p>
Please check your email to reset your password.
</p>
</div>

View File

@ -0,0 +1,53 @@
@using HPlusSports.Models
@model LoginViewModel
@{
ViewBag.Title = "";
}
<h2>User Login</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Email, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
@Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(m => m.RememberMe)
@Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
<p>
@Html.ActionLink("Register as a new user", "Register")
</p>
}
</section>
</div>
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,41 @@
@model HPlusSports.Models.RegisterViewModel
@{
ViewBag.Title = "Register";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,42 @@
@model HPlusSports.Models.ResetPasswordViewModel
@{
ViewBag.Title = "Reset password";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("ResetPassword", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
@Html.AntiForgeryToken()
<h4>Reset your password.</h4>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Code)
<div class="form-group">
@Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.Password, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Reset" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,12 @@
@{
ViewBag.Title = "Reset password confirmation";
}
<hgroup class="title">
<h1>@ViewBag.Title.</h1>
</hgroup>
<div>
<p>
Your password has been reset. Please @Html.ActionLink("click here to log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })
</p>
</div>

View File

@ -0,0 +1,24 @@
@model HPlusSports.Models.SendCodeViewModel
@{
ViewBag.Title = "Send";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("SendCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) {
@Html.AntiForgeryToken()
@Html.Hidden("rememberMe", @Model.RememberMe)
<h4>Send verification code</h4>
<hr />
<div class="row">
<div class="col-md-8">
Select Two-Factor Authentication Provider:
@Html.DropDownListFor(model => model.SelectedProvider, Model.Providers)
<input type="submit" value="Submit" class="btn btn-default" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,38 @@
@model HPlusSports.Models.VerifyCodeViewModel
@{
ViewBag.Title = "Verify";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("VerifyCode", "Account", new { ReturnUrl = Model.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) {
@Html.AntiForgeryToken()
@Html.Hidden("provider", @Model.Provider)
@Html.Hidden("rememberMe", @Model.RememberMe)
<h4>Enter verification code</h4>
<hr />
@Html.ValidationSummary("", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(m => m.Code, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
@Html.TextBoxFor(m => m.Code, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
@Html.CheckBoxFor(m => m.RememberBrowser)
@Html.LabelFor(m => m.RememberBrowser)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Submit" />
</div>
</div>
}
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,28 @@
@model HPlusSports.Models.ExternalLoginListViewModel
@using Microsoft.Owin.Security
<h4>Use another service to log in.</h4>
<hr />
@{
var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
if (loginProviders.Count() == 0) {
<div>
<p>
There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkId=403804">this article</a>
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else {
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl })) {
@Html.AntiForgeryToken()
<div id="socialLoginList">
<p>
@foreach (AuthenticationDescription p in loginProviders) {
<button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Log in using your @p.Caption account">@p.AuthenticationType</button>
}
</p>
</div>
}
}
}

View File

@ -0,0 +1,108 @@
@model HPlusSports.Models.ShoppingCart
@{
ViewBag.Title = "My Cart";
}
@if (TempData.SuccessMessage() != null)
{
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
@TempData.SuccessMessage()
</div>
}
<div class="cart row">
<div>
<hr />
<table class="table">
<thead>
<tr>
<th class="col-md-1">
</th>
<th class="col-md-6">
Product
</th>
<th class="col-md-1">
Quantity
</th>
<th class="col-md-2">
Price
</th>
<th class="col-md-2">
Subtotal
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
<tr class="text-middle">
<td>
<a href="@Url.Action("Remove", "Cart", new { id = item.Id })">
<i class="glyphicon glyphicon-remove text-danger"></i>
</a>
</td>
<td>
<a href="@Url.Product(item.SKU)">
<img class="product-image img-thumbnail" src="@Url.Action("Product", "Images", new { id = item.SKU })" />
<span>@item.Name</span>
</a>
</td>
<td>
<a href="@Url.Action("Add", "Cart", new { item.SKU, quantity = -1 })">
<i class="glyphicon glyphicon-minus"></i>
</a>
@item.Quantity
<a href="@Url.Action("Add", "Cart", new { item.SKU })">
<i class="glyphicon glyphicon-plus"></i>
</a>
</td>
<td>
@item.Price.ToString("C")
</td>
<td>
@item.Total.ToString("C")
</td>
</tr>
}
</tbody>
<tfoot>
<tr class="text-primary">
<td colspan="4" class="text-right">
Subtotal
</td>
<td>
@Model.Subtotal.ToString("C")
</td>
</tr>
<tr class="text-muted">
<td colspan="4" class="text-right">
Tax
</td>
<td>
@Model.Tax.ToString("C")
</td>
</tr>
<tr class="text-muted">
<td colspan="4" class="text-right">
Shipping
</td>
<td>
@Model.Shipping.ToString("C")
</td>
</tr>
<tr class="text-primary text-large">
<th colspan="4" class="text-right">
Total
</th>
<th>
@Model.Total.ToString("C")
</th>
</tr>
</tfoot>
</table>
</div>
</div>

View File

@ -0,0 +1,16 @@
@model HPlusSports.Models.ShoppingCart
@{
ViewBag.Title = "Cart";
}
<div class="row">
<div class="well well-lg">
<p>Your shopping cart is empty!</p>
<p>
Head on over to
@Html.ActionLink("the Product Catalog", "Index", "Products")
to find something!
</p>
</div>
</div>

View File

@ -0,0 +1,6 @@
@{
ViewBag.Title = "About";
}
<h3>@ViewBag.Message</h3>
<p>Use this area to provide additional information.</p>

View File

@ -0,0 +1,16 @@
@{
ViewBag.Title = "Contact";
}
<h3>@ViewBag.Message</h3>
<address>
One Microsoft Way<br />
Redmond, WA 98052-6399<br />
<abbr title="Phone">P:</abbr>
425.555.0100
</address>
<address>
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

View File

@ -0,0 +1,19 @@
@model IDictionary<HPlusSports.Models.Category, int>
@{
ViewBag.Title = "Categories";
}
<div class="row">
@foreach (var category in Model.Keys)
{
<div class="category col-md-4 text-center">
<a href="@Url.Action("Category", "Products", new { id = category.Key })">
<img class="img-thumbnail" src="@Url.Action("Category", "Images", new { id = category.Key })" />
<h3>@category.Name</h3>
</a>
</div>
}
</div>

View File

@ -0,0 +1,85 @@
@model HPlusSports.Models.CreateProductRequest
@{
ViewBag.Title = "Add New Product";
}
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.CategoryId, "CategoryId", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.CategoryId, null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.CategoryId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SKU, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.SKU, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SKU, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Summary, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Summary, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Summary, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.MSRP, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MSRP, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MSRP, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,93 @@
@model IEnumerable<HPlusSports.Models.Product>
@{
ViewBag.Title = "Inventory";
}
@if (TempData.HasSuccessMessage())
{
<div class="alert alert-error alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
@TempData.SuccessMessage()
</div>
}
else if (TempData.HasErrorMessage())
{
<div class="alert alert-error alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
@TempData.ErrorMessage()
</div>
}
<h2>
@ViewBag.Title
<a class="btn btn-primary btn-sm pull-right" href="@Url.Action("Create")">Add Product</a>
</h2>
<table class="table tablesorter">
<thead>
<tr>
<th>
Category
</th>
<th>
@Html.DisplayNameFor(model => model.SKU)
</th>
<th>
@Html.DisplayNameFor(model => model.Name)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th>
@Html.DisplayNameFor(model => model.Description)
</th>
<th>
Last Update
</th>
<th disabled></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Category.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.SKU)
</td>
<td>
@Html.DisplayFor(modelItem => item.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
<span class="clearfix text-muted">
@Html.DisplayFor(modelItem => item.MSRP)
</span>
</td>
<td>
@Html.DisplayFor(modelItem => item.Description)
</td>
<td>
<span class="text-muted">
<span title="@item.LastUpdated.ToString()">
@item.LastUpdated.ToShortDateString()
</span>
by
@item.LastUpdatedUserId
</span>
</td>
<td>
<a href="@Url.Action("Update", new { id = item.Id })">
<i class="glyphicon glyphicon-pencil text-info"></i>
</a>
<a href="@Url.Action("Delete", new { id = item.Id })">
<i class="glyphicon glyphicon-remove text-danger"></i>
</a>
</td>
</tr>
}
</tbody>
</table>

View File

@ -0,0 +1,104 @@
@model HPlusSports.Models.Product
@{
ViewBag.Title = "Update Product";
}
<h2>@ViewBag.Title</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Product</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.Id)
<div class="form-group">
@Html.LabelFor(model => model.CategoryId, "CategoryId", htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DropDownListFor(model => model.CategoryId, null, htmlAttributes: new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.CategoryId, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.SKU, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.SKU, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.SKU, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Summary, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Summary, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Summary, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.MSRP, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.MSRP, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.MSRP, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Price, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.LastUpdated, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DisplayFor(model => model.LastUpdated, new { htmlAttributes = new { @class = "form-control" } })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.LastUpdatedUserId, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.DisplayFor(model => model.LastUpdatedUserId, new { htmlAttributes = new { @class = "form-control" } })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}

View File

@ -0,0 +1,61 @@
@model ShoppingCart
@{
var cart = Model;
}
<a class="dropbtn nav-cart">
<i class="glyphicon glyphicon-shopping-cart"></i>
Cart
@if (cart.Items.Any())
{
<span>
(@cart.Items.Count() items)
</span>
}
</a>
<div class="dropdown-content">
@if (cart.Items.Count == 0)
{
<p>Your shopping cart is empty - keep shopping!</p>
}
else
{
<table class="table table-condensed">
@foreach (var item in cart.Items)
{
<tr class="text-middle">
<td>
<a href="@Url.Action("Remove", "Cart", new { id = item.Id })">
<i class="glyphicon glyphicon-remove text-danger"></i>
</a>
</td>
<td>
<img width="50" height="50" src="@Url.Action("Product", "Images", new { id = item.SKU })" alt="">
</td>
<td>
<a href="@Url.Product(item.SKU)">
@item.Name
</a>
</td>
<td class="text-right">
@item.Quantity
x
@item.Price.ToString("C")
</td>
</tr>
}
<tr>
<th colspan="3" class="text-right">
Subtotal:
</th>
<th class="text-right">
@cart.Subtotal.ToString("C")
</th>
</tr>
</table>
}
<p class="buttons text-right">
<a href="@Url.Action("Index", "Cart")" class="btn btn-default">View Cart</a>
</p>
</div>

View File

@ -0,0 +1,68 @@
@model Product
@{
ViewBag.Title = "";
}
<div class="product row">
<div class="col-md-1">
@if (Model.Images.Count > 1)
{
<ul class="list-group">
@foreach (var image in Model.Images)
{
<li class="list-group-item">
<img class="img img-thumbnail"
src="@Url.Action("Image", "Images", new { id = image.Id })" />
</li>
}
</ul>
}
</div>
<div class="col-md-4">
<img class="img img-thumbnail"
src="@Url.Action("Product", "Images", new { id = Model.SKU })" />
</div>
<div class="col-md-6">
<h2>@Model.Name</h2>
<p>
@{
var rating = (HPlusSports.Models.ProductRating)ViewData["Rating"];
}
@Html.Rating(rating)
</p>
<p>
<span class="price">@Html.DisplayFor(modelItem => modelItem.Price)</span>
<span class="text-muted">@Html.DisplayFor(modelItem => modelItem.MSRP)</span>
@{
var discount = (Model.MSRP - Model.Price) / Model.MSRP;
}
@if (discount > .05)
{
<span class="discount label label-primary">
@discount.ToString("P0") off!
</span>
}
</p>
<p class="buy-button">
<a class="btn btn-sm btn-warning" href="@Url.Action("Add", "Cart", new { Model.SKU })">
Add To Cart
</a>
</p>
<hr />
<p>
@Html.DisplayFor(model => model.Summary)
</p>
</div>
</div>

View File

@ -0,0 +1,103 @@
@model ProductsListViewModel
@{
var category = (Category)ViewData["Category"];
ViewBag.Title = category.Name;
}
<div class="paged-content">
<div class="row">
@foreach (var product in Model.Products)
{
<div class="product-summary col-md-3 text-center">
<h4>
<a href="@Url.Product(product.SKU)">
<img class="product-image img-thumbnail" src="@Url.Action("Product", "Images", new { id = product.SKU })" />
<span class="name">@product.Name</span>
</a>
</h4>
<p>
<span class="price">@Html.DisplayFor(x => product.Price)</span>
<span class="text-muted">@Html.DisplayFor(x => product.MSRP)</span>
@if (product.HasAdvertisableDiscount)
{
<span class="discount label label-primary">
@Html.DisplayFor(x => product.Discount) off!
</span>
}
</p>
<p>
@Html.Rating(product.Rating)
</p>
<p>
<a class="btn btn-sm btn-warning" href="@Url.Action("Add", "Cart", new { product.SKU })">
Add To Cart
</a>
</p>
</div>
}
</div>
<div class="pager row">
<div class="pull-left">
<nav>
<ul class="pager">
<li>
<span>
@ViewBag.ResultsCount results
</span>
</li>
</ul>
</nav>
</div>
<div class="col-md-4 text-right">
@if (ViewBag.PreviousPage != null)
{
<nav>
<ul class="pager">
<li>
<a data-partial href="?page=@ViewBag.PreviousPage&count=@ViewBag.PageSize">
<i class="glyphicon glyphicon-chevron-left"></i>
Prev.
</a>
</li>
</ul>
</nav>
}
</div>
<div class="col-md-2 text-center">
<nav>
<ul class="pager">
<li>
<span>Page @ViewBag.CurrentPage of @ViewBag.PageCount</span>
</li>
</ul>
</nav>
</div>
<div class="col-md-5 text-left">
@if (ViewBag.NextPage != null)
{
<nav>
<ul class="pager">
<li>
<a data-partial href="?page=@ViewBag.NextPage&count=@ViewBag.PageSize">
Next
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
</li>
</ul>
</nav>
}
</div>
</div>
</div>

View File

@ -0,0 +1,10 @@
@model ProductRating
<span title="@Model.Rating.GetValueOrDefault().ToString("N1")">
@for (var i = 1; i <= 5; i++)
{
var starClass = (Model.Rating >= i) ? "star" : "star-empty";
<span class="rating-star glyphicon glyphicon-@starClass"></span>
}
</span>
<span>(@Model.ReviewCount)</span>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Error</title>
</head>
<body>
<hgroup>
<h1>Error.</h1>
<h2>An error occurred while processing your request.</h2>
</hgroup>
</body>
</html>

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title - H+ Sport</title>
<link rel="dns-prefetch" href="//fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro" rel="stylesheet">
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/modernizr")
<link rel="icon" href="https://hplussport.com/wp-content/uploads/2015/12/cropped-HSport_01-32x32.png" sizes="32x32">
</head>
<body>
<div class="navbar">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="~/" class="navbar-brand">
<img src="~/Content/logo.png" />
</a>
</div>
<div class="col-md-6 text-center title navbar-header">
<h1>@ViewData["Title"]</h1>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav pull-right">
<li class="dropdown">
@if (User.IsInRole(HPlusSports.Models.UserRoles.Admin))
{
<a class="dropbtn" href="@Url.Action("Index", "Inventory")">
<i class="glyphicon glyphicon-list-alt"></i>
Inventory
</a>
}
</li>
<li class="dropdown">
@Html.Action("MenuCart", "Products")
</li>
<li>
@if (User.Identity.IsAuthenticated)
{
<form method="post" action="@Url.Action("Logoff", "Account", new { area = "" })">
@Html.AntiForgeryToken()
<button type="submit" class="btn btn-default">Logout</button>
</form>
}
else
{
<div>
<a href="@Url.Action("Login", "Account")" class="btn btn-default">Login</a>
</div>
}
</li>
</ul>
</div>
</div>
</div>
<div id="main" class="container">
@RenderBody()
</div>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/bootstrap")
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.30.1/js/jquery.tablesorter.min.js"></script>
@Scripts.Render("~/bundles/site")
@RenderSection("scripts", required: false)
</body>
</html>

View File

@ -0,0 +1,44 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="HPlusSports"/>
<add namespace="HPlusSports.Models"/>
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
</system.web>
</configuration>

View File

@ -0,0 +1,3 @@
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=301874 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your Web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit https://go.microsoft.com/fwlink/?LinkId=301874 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an attribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your Web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
https://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
</sectionGroup>
</configSections>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="elmah.mvc.disableHandler" value="false" />
<add key="elmah.mvc.disableHandleErrorFilter" value="false" />
<add key="elmah.mvc.requiresAuthentication" value="true" />
<add key="elmah.mvc.IgnoreDefaultRoute" value="false" />
<add key="elmah.mvc.allowedRoles" value="Admin" />
<add key="elmah.mvc.allowedUsers" value="*" />
<add key="elmah.mvc.route" value="elmah" />
<add key="elmah.mvc.UserAuthCaseSensitive" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
</httpModules>
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules>
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
</modules>
</system.webServer>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
</compilers>
</system.codedom>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
<elmah>
</elmah>
</configuration>

View File

@ -0,0 +1,349 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
<Import Project="..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3BBC2FA4-9C8C-498C-A8B6-178FE7740438}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HPlusSports</RootNamespace>
<AssemblyName>HPlusSports</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress>
<Use64BitIISExpress />
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<MvcBuildViews>true</MvcBuildViews>
<DebugSymbols>true</DebugSymbols>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=4.0.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.4.0.1\lib\net45\Autofac.dll</HintPath>
</Reference>
<Reference Include="Autofac.Integration.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll</HintPath>
</Reference>
<Reference Include="Elmah, Version=1.2.13605.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\elmah.corelibrary.1.2\lib\Elmah.dll</HintPath>
</Reference>
<Reference Include="Elmah.Mvc, Version=2.1.2.1389, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Elmah.Mvc.2.1.2\lib\net40\Elmah.Mvc.dll</HintPath>
</Reference>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
</Reference>
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
</Reference>
<Reference Include="MediatR, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MediatR.4.1.0\lib\net45\MediatR.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.EntityFramework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Identity.EntityFramework.2.2.1\lib\net45\Microsoft.AspNet.Identity.EntityFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AspNet.Identity.Owin, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Identity.Owin.2.2.1\lib\net45\Microsoft.AspNet.Identity.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Owin, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Host.SystemWeb, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Host.SystemWeb.3.0.1\lib\net45\Microsoft.Owin.Host.SystemWeb.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Security.2.1.0\lib\net45\Microsoft.Owin.Security.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.Cookies, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Security.Cookies.2.1.0\lib\net45\Microsoft.Owin.Security.Cookies.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin.Security.OAuth, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Owin.Security.OAuth.2.1.0\lib\net45\Microsoft.Owin.Security.OAuth.dll</HintPath>
</Reference>
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Routing" />
<Reference Include="System.Xml" />
<Reference Include="System.Configuration" />
<Reference Include="System.Web.Services" />
<Reference Include="System.EnterpriseServices" />
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
</Reference>
<Reference Include="System.Net.Http.WebRequest">
</Reference>
<Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Optimization">
<HintPath>..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="WebGrease">
<Private>True</Private>
<HintPath>..\packages\WebGrease.1.5.2\lib\WebGrease.dll</HintPath>
</Reference>
<Reference Include="Antlr3.Runtime">
<Private>True</Private>
<HintPath>..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Microsoft.ApplicationInsights">
<HintPath>..\packages\Microsoft.ApplicationInsights.2.2.0\lib\net45\Microsoft.ApplicationInsights.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AI.Agent.Intercept">
<HintPath>..\packages\Microsoft.ApplicationInsights.Agent.Intercept.2.0.6\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AI.DependencyCollector">
<HintPath>..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.2.0\lib\net45\Microsoft.AI.DependencyCollector.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AI.PerfCounterCollector">
<HintPath>..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.2.0\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AI.ServerTelemetryChannel">
<HintPath>..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.2.0\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AI.WindowsServer">
<HintPath>..\packages\Microsoft.ApplicationInsights.WindowsServer.2.2.0\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
</Reference>
<Reference Include="Microsoft.AI.Web">
<HintPath>..\packages\Microsoft.ApplicationInsights.Web.2.2.0\lib\net45\Microsoft.AI.Web.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\BundleConfig.cs" />
<Compile Include="App_Start\DatabaseConfig.cs" />
<Compile Include="App_Start\Dependencies.cs" />
<Compile Include="App_Start\FilterConfig.cs" />
<Compile Include="App_Start\IdentityConfig.cs" />
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\Startup.Auth.cs" />
<Compile Include="Controllers\AccountController.cs" />
<Compile Include="Controllers\CartController.cs" />
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Controllers\ImagesController.cs" />
<Compile Include="Controllers\InventoryController.cs" />
<Compile Include="Controllers\ProductsController.cs" />
<Compile Include="Extensions\AllowPartialRenderingAttribute.cs" />
<Compile Include="Extensions\HtmlHelperExtensions.cs" />
<Compile Include="Extensions\TempDataExtensions.cs" />
<Compile Include="Extensions\UrlHelperExtensions.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Models\Identity\AccountViewModels.cs" />
<Compile Include="Models\Identity\IdentityModels.cs" />
<Compile Include="Models\Identity\ManageViewModels.cs" />
<Compile Include="Models\CreateProductRequest.cs" />
<Compile Include="Models\ProductsListViewModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Startup.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Content\bootstrap.css" />
<Content Include="Content\bootstrap.min.css" />
<Content Include="Content\logo.png" />
<Content Include="Content\placeholder.jpg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="favicon.ico" />
<Content Include="fonts\glyphicons-halflings-regular.svg" />
<Content Include="Global.asax" />
<Content Include="Content\Site.css" />
<Content Include="Scripts\bootstrap.js" />
<Content Include="Scripts\bootstrap.min.js" />
<Content Include="ApplicationInsights.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<None Include="Scripts\jquery-1.10.2.intellisense.js" />
<Content Include="Scripts\jquery-1.10.2.js" />
<Content Include="Scripts\jquery-1.10.2.min.js" />
<None Include="Scripts\jquery.validate-vsdoc.js" />
<Content Include="Scripts\jquery.validate.js" />
<Content Include="Scripts\jquery.validate.min.js" />
<Content Include="Scripts\jquery.validate.unobtrusive.js" />
<Content Include="Scripts\jquery.validate.unobtrusive.min.js" />
<Content Include="Scripts\modernizr-2.6.2.js" />
<Content Include="Scripts\respond.js" />
<Content Include="Scripts\respond.min.js" />
<Content Include="Scripts\site.js" />
<Content Include="Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
<Content Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
<Content Include="Views\Web.config">
<SubType>Designer</SubType>
</Content>
<Content Include="Views\_ViewStart.cshtml" />
<Content Include="Views\Shared\Error.cshtml" />
<Content Include="Views\Shared\_Layout.cshtml" />
<Content Include="Views\Home\About.cshtml" />
<Content Include="Views\Home\Contact.cshtml" />
<Content Include="Views\Home\Index.cshtml" />
<Content Include="Views\Cart\Cart.cshtml" />
<Content Include="Views\Products\ProductList.cshtml" />
<Content Include="Views\Products\Product.cshtml" />
<Content Include="Views\Cart\EmptyCart.cshtml" />
<Content Include="Views\Account\ConfirmEmail.cshtml" />
<Content Include="Views\Account\ExternalLoginConfirmation.cshtml" />
<Content Include="Views\Account\ExternalLoginFailure.cshtml" />
<Content Include="Views\Account\ForgotPassword.cshtml" />
<Content Include="Views\Account\ForgotPasswordConfirmation.cshtml" />
<Content Include="Views\Account\Login.cshtml" />
<Content Include="Views\Account\Register.cshtml" />
<Content Include="Views\Account\ResetPassword.cshtml" />
<Content Include="Views\Account\ResetPasswordConfirmation.cshtml" />
<Content Include="Views\Account\SendCode.cshtml" />
<Content Include="Views\Account\VerifyCode.cshtml" />
<Content Include="Views\Account\_ExternalLoginsListPartial.cshtml" />
<Content Include="Views\Inventory\Index.cshtml" />
<Content Include="Views\Inventory\Update.cshtml" />
<Content Include="Views\Inventory\Create.cshtml" />
<Content Include="Views\Products\_Rating.cshtml" />
<Content Include="Views\Products\MenuCart.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
<ItemGroup>
<Content Include="fonts\glyphicons-halflings-regular.woff" />
</ItemGroup>
<ItemGroup>
<Content Include="fonts\glyphicons-halflings-regular.ttf" />
</ItemGroup>
<ItemGroup>
<Content Include="fonts\glyphicons-halflings-regular.eot" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<Content Include="Scripts\jquery-1.10.2.min.map" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj">
<Project>{7709a0d8-f311-4a8e-9e5b-05ed694449ad}</Project>
<Name>Common</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>65128</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:65128/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.2.1.0\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\build\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target> -->
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Some files were not shown because too many files have changed in this diff Show More