[Fixes #537] Clear up MusicStore repo
This commit is contained in:
parent
152c842416
commit
2c1f1a277a
|
|
@ -34,8 +34,6 @@ App_Data/
|
|||
bower_components
|
||||
node_modules
|
||||
*.sln.ide
|
||||
*/*.Spa/public/*
|
||||
*/*.Spa/wwwroot/lib
|
||||
*.ng.ts
|
||||
*.sln.ide
|
||||
project.lock.json
|
||||
|
|
|
|||
|
|
@ -1,33 +1,26 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.22803.0
|
||||
VisualStudioVersion = 14.0.23107.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{44621553-AA7D-4893-8834-79582A7D8348}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7D749BDA-4638-4517-B66A-D40DEDEEB141}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
global.json = global.json
|
||||
NuGet.config = NuGet.config
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcMusicStore", "src\MvcMusicStore\MvcMusicStore.csproj", "{25CE8290-EF24-4818-B009-68DC903163D3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcMusicStore.Spa", "src\MvcMusicStore.Spa\MvcMusicStore.Spa.csproj", "{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{363D2681-31A6-48C9-90BB-9ACFF4A41F06}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "E2ETests", "test\E2ETests\E2ETests.xproj", "{A319ACCE-060B-4385-9534-9F2202F6180E}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore", "src\MusicStore\MusicStore.xproj", "{3CFBED5D-2ED8-49DB-96FB-BDAA748DC5A0}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Spa", "src\MusicStore.Spa\MusicStore.Spa.xproj", "{93891170-A8D5-46FD-A291-40F90CF258C2}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Test", "test\MusicStore.Test\MusicStore.Test.xproj", "{CA663205-77DE-4E55-B300-85594181B5A9}"
|
||||
EndProject
|
||||
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "MusicStore.Spa.Test", "test\MusicStore.Spa.Test\MusicStore.Spa.Test.xproj", "{9D3326C4-1F12-4526-9F25-712A1463B3FA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -38,26 +31,6 @@ Global
|
|||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -78,16 +51,6 @@ Global
|
|||
{3CFBED5D-2ED8-49DB-96FB-BDAA748DC5A0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{3CFBED5D-2ED8-49DB-96FB-BDAA748DC5A0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{3CFBED5D-2ED8-49DB-96FB-BDAA748DC5A0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CA663205-77DE-4E55-B300-85594181B5A9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -100,29 +63,13 @@ Global
|
|||
{CA663205-77DE-4E55-B300-85594181B5A9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{CA663205-77DE-4E55-B300-85594181B5A9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{CA663205-77DE-4E55-B300-85594181B5A9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{25CE8290-EF24-4818-B009-68DC903163D3} = {B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}
|
||||
{408AC102-7FB1-4ADD-A16A-9AACBAFFC2F7} = {B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}
|
||||
{A319ACCE-060B-4385-9534-9F2202F6180E} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
|
||||
{3CFBED5D-2ED8-49DB-96FB-BDAA748DC5A0} = {B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}
|
||||
{93891170-A8D5-46FD-A291-40F90CF258C2} = {B7B176B6-8D4D-4EF1-BBD2-DDA650C78FFF}
|
||||
{CA663205-77DE-4E55-B300-85594181B5A9} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
|
||||
{9D3326C4-1F12-4526-9F25-712A1463B3FA} = {363D2681-31A6-48C9-90BB-9ACFF4A41F06}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -1,211 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authorization;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.Data.Entity;
|
||||
using AutoMapper;
|
||||
using MusicStore.Infrastructure;
|
||||
using MusicStore.Models;
|
||||
using MusicStore.Spa.Infrastructure;
|
||||
|
||||
namespace MusicStore.Apis
|
||||
{
|
||||
[Route("api/albums")]
|
||||
public class AlbumsApiController : Controller
|
||||
{
|
||||
private readonly MusicStoreContext _storeContext;
|
||||
|
||||
public AlbumsApiController(MusicStoreContext storeContext)
|
||||
{
|
||||
_storeContext = storeContext;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[NoCache]
|
||||
public async Task<ActionResult> Paged(int page = 1, int pageSize = 50, string sortBy = null)
|
||||
{
|
||||
await _storeContext.Genres.LoadAsync();
|
||||
await _storeContext.Artists.LoadAsync();
|
||||
|
||||
var albums = await _storeContext.Albums
|
||||
// .Include(a => a.Genre)
|
||||
// .Include(a => a.Artist)
|
||||
.ToPagedListAsync(page, pageSize, sortBy,
|
||||
a => a.Title, // sortExpression
|
||||
SortDirection.Ascending, // defaultSortDirection
|
||||
a => Mapper.Map(a, new AlbumResultDto())); // selector
|
||||
|
||||
return Json(albums);
|
||||
}
|
||||
|
||||
[HttpGet("all")]
|
||||
[NoCache]
|
||||
public async Task<ActionResult> All()
|
||||
{
|
||||
var albums = await _storeContext.Albums
|
||||
//.Include(a => a.Genre)
|
||||
//.Include(a => a.Artist)
|
||||
.OrderBy(a => a.Title)
|
||||
.ToListAsync();
|
||||
|
||||
return Json(albums.Select(a => Mapper.Map(a, new AlbumResultDto())));
|
||||
}
|
||||
|
||||
[HttpGet("mostPopular")]
|
||||
[NoCache]
|
||||
public async Task<ActionResult> MostPopular(int count = 6)
|
||||
{
|
||||
count = count > 0 && count < 20 ? count : 6;
|
||||
var albums = await _storeContext.Albums
|
||||
.OrderByDescending(a => a.OrderDetails.Count())
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
|
||||
// TODO: Move the .Select() to end of albums query when EF supports it
|
||||
return Json(albums.Select(a => Mapper.Map(a, new AlbumResultDto())));
|
||||
}
|
||||
|
||||
[HttpGet("{albumId:int}")]
|
||||
[NoCache]
|
||||
public async Task<ActionResult> Details(int albumId)
|
||||
{
|
||||
await _storeContext.Genres.LoadAsync();
|
||||
await _storeContext.Artists.LoadAsync();
|
||||
|
||||
var album = await _storeContext.Albums
|
||||
//.Include(a => a.Artist)
|
||||
//.Include(a => a.Genre)
|
||||
.Where(a => a.AlbumId == albumId)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
var albumResult = Mapper.Map(album, new AlbumResultDto());
|
||||
|
||||
// TODO: Get these from the related entities when EF supports that again, i.e. when .Include() works
|
||||
//album.Artist.Name = (await _storeContext.Artists.SingleOrDefaultAsync(a => a.ArtistId == album.ArtistId)).Name;
|
||||
//album.Genre.Name = (await _storeContext.Genres.SingleOrDefaultAsync(g => g.GenreId == album.GenreId)).Name;
|
||||
|
||||
// TODO: Add null checking and return 404 in that case
|
||||
|
||||
return Json(albumResult);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Authorize("app-ManageStore")]
|
||||
public async Task<ActionResult> CreateAlbum([FromBody]AlbumChangeDto album)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
// Return the model errors
|
||||
return new ApiResult(ModelState);
|
||||
}
|
||||
|
||||
// Save the changes to the DB
|
||||
var dbAlbum = new Album();
|
||||
_storeContext.Albums.Add(Mapper.Map(album, dbAlbum));
|
||||
await _storeContext.SaveChangesAsync();
|
||||
|
||||
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
||||
|
||||
return new ApiResult
|
||||
{
|
||||
Data = dbAlbum.AlbumId,
|
||||
Message = "Album created successfully."
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPut("{albumId:int}/update")]
|
||||
[Authorize("app-ManageStore")]
|
||||
public async Task<ActionResult> UpdateAlbum(int albumId, [FromBody]AlbumChangeDto album)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
// Return the model errors
|
||||
return new ApiResult(ModelState);
|
||||
}
|
||||
|
||||
var dbAlbum = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
||||
|
||||
if (dbAlbum == null)
|
||||
{
|
||||
return new ApiResult
|
||||
{
|
||||
StatusCode = 404,
|
||||
Message = string.Format("The album with ID {0} was not found.", albumId)
|
||||
};
|
||||
}
|
||||
|
||||
// Save the changes to the DB
|
||||
Mapper.Map(album, dbAlbum);
|
||||
await _storeContext.SaveChangesAsync();
|
||||
|
||||
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
||||
|
||||
return new ApiResult
|
||||
{
|
||||
Message = "Album updated successfully."
|
||||
};
|
||||
}
|
||||
|
||||
[HttpDelete("{albumId:int}")]
|
||||
[Authorize("app-ManageStore")]
|
||||
public async Task<ActionResult> DeleteAlbum(int albumId)
|
||||
{
|
||||
var album = await _storeContext.Albums.SingleOrDefaultAsync(a => a.AlbumId == albumId);
|
||||
//var album = _storeContext.Albums.SingleOrDefault(a => a.AlbumId == albumId);
|
||||
|
||||
if (album != null)
|
||||
{
|
||||
_storeContext.Albums.Remove(album);
|
||||
|
||||
// Save the changes to the DB
|
||||
await _storeContext.SaveChangesAsync();
|
||||
|
||||
// TODO: Handle missing record, key violations, concurrency issues, etc.
|
||||
}
|
||||
|
||||
return new ApiResult
|
||||
{
|
||||
Message = "Album deleted successfully."
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[ModelMetadataType(typeof(Album))]
|
||||
public class AlbumChangeDto
|
||||
{
|
||||
public int GenreId { get; set; }
|
||||
|
||||
public int ArtistId { get; set; }
|
||||
|
||||
public string Title { get; set; }
|
||||
|
||||
public decimal Price { get; set; }
|
||||
|
||||
public string AlbumArtUrl { get; set; }
|
||||
}
|
||||
|
||||
public class AlbumResultDto : AlbumChangeDto
|
||||
{
|
||||
public AlbumResultDto()
|
||||
{
|
||||
Artist = new ArtistResultDto();
|
||||
Genre = new GenreResultDto();
|
||||
}
|
||||
|
||||
public int AlbumId { get; set; }
|
||||
|
||||
public ArtistResultDto Artist { get; private set; }
|
||||
|
||||
public GenreResultDto Genre { get; private set; }
|
||||
}
|
||||
|
||||
public class ArtistResultDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
|
||||
public class GenreResultDto
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.Data.Entity;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Apis
|
||||
{
|
||||
[Route("api/artists")]
|
||||
public class ArtistsApiController : Controller
|
||||
{
|
||||
private readonly MusicStoreContext _storeContext;
|
||||
|
||||
public ArtistsApiController(MusicStoreContext storeContext)
|
||||
{
|
||||
_storeContext = storeContext;
|
||||
}
|
||||
|
||||
[HttpGet("lookup")]
|
||||
public async Task<ActionResult> Lookup()
|
||||
{
|
||||
var artists = await _storeContext.Artists
|
||||
.OrderBy(a => a.Name)
|
||||
.ToListAsync();
|
||||
|
||||
return Json(artists);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using Microsoft.Data.Entity;
|
||||
using MusicStore.Models;
|
||||
using MusicStore.Spa.Infrastructure;
|
||||
|
||||
namespace MusicStore.Apis
|
||||
{
|
||||
[Route("api/genres")]
|
||||
public class GenresApiController : Controller
|
||||
{
|
||||
private readonly MusicStoreContext _storeContext;
|
||||
|
||||
public GenresApiController(MusicStoreContext storeContext)
|
||||
{
|
||||
_storeContext = storeContext;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult> GenreList()
|
||||
{
|
||||
var genres = await _storeContext.Genres
|
||||
//.Include(g => g.Albums)
|
||||
.OrderBy(g => g.Name)
|
||||
.ToListAsync();
|
||||
|
||||
return Json(genres);
|
||||
}
|
||||
|
||||
[HttpGet("lookup")]
|
||||
public async Task<ActionResult> Lookup()
|
||||
{
|
||||
var genres = await _storeContext.Genres
|
||||
.Select(g => new { g.GenreId, g.Name })
|
||||
.ToListAsync();
|
||||
|
||||
return Json(genres);
|
||||
}
|
||||
|
||||
[HttpGet("menu")]
|
||||
public async Task<ActionResult> GenreMenuList(int count = 9)
|
||||
{
|
||||
count = count > 0 && count < 20 ? count : 9;
|
||||
|
||||
var genres = await _storeContext.Genres
|
||||
.OrderByDescending(g =>
|
||||
g.Albums.Sum(a =>
|
||||
a.OrderDetails.Sum(od => od.Quantity)))
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
|
||||
return Json(genres);
|
||||
}
|
||||
|
||||
[HttpGet("{genreId:int}/albums")]
|
||||
[NoCache]
|
||||
public async Task<ActionResult> GenreAlbums(int genreId)
|
||||
{
|
||||
var albums = await _storeContext.Albums
|
||||
.Where(a => a.GenreId == genreId)
|
||||
//.Include(a => a.Genre)
|
||||
//.Include(a => a.Artist)
|
||||
//.OrderBy(a => a.Genre.Name)
|
||||
.ToListAsync();
|
||||
|
||||
return Json(albums);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
{
|
||||
"DefaultAdminUsername": "Administrator",
|
||||
"DefaultAdminPassword": "YouShouldChangeThisPassword1!",
|
||||
"Data": {
|
||||
"DefaultConnection": {
|
||||
"Connectionstring": "Server=(localdb)\\MSSQLLocalDB;Database=MusicStoreSpa;Trusted_Connection=True;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authorization;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
[Route("account")]
|
||||
[Authorize]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
[FromServices]
|
||||
public UserManager<ApplicationUser> UserManager { get; set; }
|
||||
|
||||
[FromServices]
|
||||
public SignInManager<ApplicationUser> SignInManager { get; set; }
|
||||
|
||||
[AllowAnonymous]
|
||||
//Bug: https://github.com/aspnet/WebFx/issues/339
|
||||
[HttpGet("login")]
|
||||
public IActionResult Login(string returnUrl)
|
||||
{
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl)
|
||||
{
|
||||
if (ModelState.IsValid == true)
|
||||
{
|
||||
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: false);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
ModelState.AddModelError("", "User is locked out, try again later.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError("", "Invalid username or password.");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
//Bug: https://github.com/aspnet/WebFx/issues/339
|
||||
[HttpGet("register")]
|
||||
public IActionResult Register()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Register(RegisterViewModel model)
|
||||
{
|
||||
//Bug: https://github.com/aspnet/WebFx/issues/247
|
||||
//if (ModelState.IsValid == true)
|
||||
{
|
||||
var user = new ApplicationUser { UserName = model.UserName };
|
||||
var result = await UserManager.CreateAsync(user, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Home", "Page");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddErrors(result);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet("manage")]
|
||||
public IActionResult Manage(ManageMessageId? message = null)
|
||||
{
|
||||
ViewBag.StatusMessage =
|
||||
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
|
||||
: message == ManageMessageId.Error ? "An error has occurred."
|
||||
: "";
|
||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("manage")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Manage(ManageUserViewModel model)
|
||||
{
|
||||
ViewBag.ReturnUrl = Url.Action("Manage");
|
||||
//Bug: https://github.com/aspnet/WebFx/issues/247
|
||||
//if (ModelState.IsValid == true)
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToAction("Manage", new { Message = ManageMessageId.ChangePasswordSuccess });
|
||||
}
|
||||
else
|
||||
{
|
||||
AddErrors(result);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost("logoff")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> LogOff()
|
||||
{
|
||||
await SignInManager.SignOutAsync();
|
||||
return RedirectToAction("Home", "Page");
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private void AddErrors(IdentityResult result)
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError("", error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ApplicationUser> GetCurrentUserAsync()
|
||||
{
|
||||
return await UserManager.FindByIdAsync(Context.User.GetUserId());
|
||||
}
|
||||
|
||||
public enum ManageMessageId
|
||||
{
|
||||
ChangePasswordSuccess,
|
||||
Error
|
||||
}
|
||||
|
||||
private IActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
using Microsoft.AspNet.Authorization;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MusicStore.Spa.Controllers
|
||||
{
|
||||
[Route("/")]
|
||||
public class PageController : Controller
|
||||
{
|
||||
[HttpGet]
|
||||
public IActionResult Home()
|
||||
{
|
||||
return View("/Pages/Home.cshtml");
|
||||
}
|
||||
|
||||
[HttpGet("admin")]
|
||||
[Authorize("app-ManageStore")]
|
||||
public IActionResult Admin()
|
||||
{
|
||||
return View("/Pages/Admin.cshtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Mvc;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
public class TemplateController : Controller
|
||||
{
|
||||
private static readonly string _templateBasePath = "/ng-apps/";
|
||||
|
||||
// GET: Template
|
||||
[Route("ng-apps/{*path}")]
|
||||
public ActionResult Index(string path)
|
||||
{
|
||||
if (!IsValidPath(path))
|
||||
{
|
||||
return HttpNotFound();
|
||||
}
|
||||
|
||||
return View(_templateBasePath + path);
|
||||
}
|
||||
|
||||
private static bool IsValidPath(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var last = '\0';
|
||||
for (var i = 0; i < path.Length; i++)
|
||||
{
|
||||
var c = path[i];
|
||||
if (Char.IsLetterOrDigit(c)
|
||||
|| (c == '/' && last != '/')
|
||||
|| c == '-'
|
||||
|| c == '_'
|
||||
|| (c == '.' && last != '.'))
|
||||
{
|
||||
last = c;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return path.EndsWith(".cshtml", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
/// <vs Clean='clean' />
|
||||
// node-debug (Resolve-Path ~\AppData\Roaming\npm\node_modules\grunt-cli\bin\grunt) task:target
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
grunt.loadNpmTasks("grunt-bower-task");
|
||||
|
||||
grunt.initConfig({
|
||||
staticFilePattern: "**/*.{js,css,map,html,htm,ico,jpg,jpeg,png,gif,eot,svg,ttf,woff}",
|
||||
bower: {
|
||||
install: {
|
||||
options: {
|
||||
targetDir: "wwwroot/lib",
|
||||
layout: "byComponent",
|
||||
cleanTargetDir: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask("ts", ["tslint", "tsng", "typescript:dev", "clean:tsng"]);
|
||||
grunt.registerTask("dev", ["clean:assets", "copy", "bower:install", "less:dev", "ts"]);
|
||||
grunt.registerTask("release", ["clean", "copy", "uglify", "less:release", "typescript:release"]);
|
||||
grunt.registerTask("default", ["dev"]);
|
||||
|
||||
require("grunt-ide-support")(grunt);
|
||||
};
|
||||
|
|
@ -1,328 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Html.Abstractions;
|
||||
using Microsoft.AspNet.Mvc.ViewFeatures;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public static class AngularExtensions
|
||||
{
|
||||
public static IHtmlContent ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
|
||||
{
|
||||
return html.ngPasswordFor(expression, null);
|
||||
}
|
||||
|
||||
public static IHtmlContent ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
|
||||
{
|
||||
return html.ngPasswordFor(expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
|
||||
}
|
||||
|
||||
public static IHtmlContent ngPasswordFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
return html.ngTextBoxFor(expression, MergeAttributes(
|
||||
new RouteValueDictionary { { "type", "password" } },
|
||||
htmlAttributes));
|
||||
}
|
||||
|
||||
public static IHtmlContent ngTextBoxFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression)
|
||||
{
|
||||
return html.ngTextBoxFor(expression, new RouteValueDictionary());
|
||||
}
|
||||
|
||||
public static IHtmlContent ngTextBoxFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
|
||||
{
|
||||
return html.ngTextBoxFor(expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
|
||||
}
|
||||
|
||||
public static IHtmlContent ngTextBoxFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||
var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
|
||||
var ngAttributes = new Dictionary<string, object>();
|
||||
|
||||
ngAttributes["type"] = "text";
|
||||
|
||||
// Angular binding to client-side model (scope). This is required for Angular validation to work.
|
||||
var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
||||
ngAttributes["name"] = valueFieldName;
|
||||
ngAttributes["ng-model"] = valueFieldName;
|
||||
|
||||
// Set input type
|
||||
if (string.Equals(modelExplorer.Metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.EmailAddress), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ngAttributes["type"] = "email";
|
||||
}
|
||||
else if (modelExplorer.ModelType == typeof(Uri)
|
||||
|| string.Equals(modelExplorer.Metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.Url), StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(modelExplorer.Metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.ImageUrl), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ngAttributes["type"] = "url";
|
||||
}
|
||||
else if (IsNumberType(modelExplorer.ModelType))
|
||||
{
|
||||
ngAttributes["type"] = "number";
|
||||
if (IsIntegerType(modelExplorer.ModelType))
|
||||
{
|
||||
ngAttributes["step"] = "1";
|
||||
}
|
||||
else
|
||||
{
|
||||
ngAttributes["step"] = "any";
|
||||
}
|
||||
}
|
||||
else if (modelExplorer.ModelType == typeof(DateTime))
|
||||
{
|
||||
if (string.Equals(modelExplorer.Metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.Date), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ngAttributes["type"] = "date";
|
||||
}
|
||||
else if (string.Equals(modelExplorer.Metadata.DataTypeName, Enum.GetName(typeof(DataType), DataType.DateTime), StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ngAttributes["type"] = "datetime";
|
||||
}
|
||||
}
|
||||
|
||||
// Add attributes for Angular validation
|
||||
var clientValidators = html.GetClientValidationRules(modelExplorer, null);
|
||||
|
||||
foreach (var validator in clientValidators)
|
||||
{
|
||||
if (string.Equals(validator.ValidationType, "length"))
|
||||
{
|
||||
if (validator.ValidationParameters.ContainsKey("min"))
|
||||
{
|
||||
ngAttributes["ng-minlength"] = validator.ValidationParameters["min"];
|
||||
}
|
||||
if (validator.ValidationParameters.ContainsKey("max"))
|
||||
{
|
||||
ngAttributes["ng-maxlength"] = validator.ValidationParameters["max"];
|
||||
}
|
||||
}
|
||||
else if (string.Equals(validator.ValidationType, "required"))
|
||||
{
|
||||
ngAttributes["required"] = null;
|
||||
}
|
||||
else if (string.Equals(validator.ValidationType, "range"))
|
||||
{
|
||||
if (validator.ValidationParameters.ContainsKey("min"))
|
||||
{
|
||||
ngAttributes["min"] = validator.ValidationParameters["min"];
|
||||
}
|
||||
if (validator.ValidationParameters.ContainsKey("max"))
|
||||
{
|
||||
ngAttributes["max"] = validator.ValidationParameters["max"];
|
||||
}
|
||||
}
|
||||
else if (string.Equals(validator.ValidationType, "equalto"))
|
||||
{
|
||||
// CompareAttribute validator
|
||||
var fieldToCompare = validator.ValidationParameters["other"]; // e.g. *.NewPassword
|
||||
var other = validator.ValidationParameters["other"].ToString();
|
||||
if (other.StartsWith("*."))
|
||||
{
|
||||
// The built-in CompareAttributeAdapter prepends *. to the property name so we strip it off here
|
||||
other = other.Substring("*.".Length);
|
||||
}
|
||||
ngAttributes["app-equal-to"] = other;
|
||||
// TODO: Actually write the Angular directive to use this
|
||||
}
|
||||
// TODO: Regex, Phone(regex)
|
||||
}
|
||||
|
||||
// Render!
|
||||
if (modelExplorer.Model != null)
|
||||
{
|
||||
ngAttributes.Add("value", modelExplorer.Model.ToString());
|
||||
}
|
||||
|
||||
var tag = new TagBuilder("input");
|
||||
tag.MergeAttributes(MergeAttributes(ngAttributes, htmlAttributes));
|
||||
tag.TagRenderMode = TagRenderMode.SelfClosing;
|
||||
return tag;
|
||||
}
|
||||
|
||||
private static bool IsNumberType(Type type)
|
||||
{
|
||||
if (type == typeof(Int16) ||
|
||||
type == typeof(Int32) ||
|
||||
type == typeof(Int64) ||
|
||||
type == typeof(UInt16) ||
|
||||
type == typeof(UInt32) ||
|
||||
type == typeof(UInt64) ||
|
||||
type == typeof(Decimal) ||
|
||||
type == typeof(Double) ||
|
||||
type == typeof(Single))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//private static bool IsIntegerType(Type type)
|
||||
//{
|
||||
// switch (Type.GetTypeCode(type))
|
||||
// {
|
||||
// case TypeCode.Int16:
|
||||
// case TypeCode.Int32:
|
||||
// case TypeCode.Int64:
|
||||
// case TypeCode.UInt16:
|
||||
// case TypeCode.UInt32:
|
||||
// case TypeCode.UInt64:
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
//}
|
||||
|
||||
private static bool IsIntegerType(Type type)
|
||||
{
|
||||
if (type == typeof(Int16) ||
|
||||
type == typeof(Int32) ||
|
||||
type == typeof(Int64) ||
|
||||
type == typeof(UInt16) ||
|
||||
type == typeof(UInt32) ||
|
||||
type == typeof(UInt64))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IHtmlContent ngDropDownListFor<TModel, TProperty, TDisplayProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, Expression<Func<TModel, TDisplayProperty>> displayExpression, string source, string nullOption, object htmlAttributes)
|
||||
{
|
||||
return ngDropDownListFor(html, propertyExpression, displayExpression, source, nullOption, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
|
||||
}
|
||||
|
||||
public static IHtmlContent ngDropDownListFor<TModel, TProperty, TDisplayProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertyExpression, Expression<Func<TModel, TDisplayProperty>> displayExpression, string source, string nullOption, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
var propertyExpressionText = ExpressionHelper.GetExpressionText(propertyExpression);
|
||||
var displayExpressionText = ExpressionHelper.GetExpressionText(displayExpression);
|
||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(propertyExpression, html.ViewData, html.MetadataProvider);
|
||||
var tag = new TagBuilder("select");
|
||||
|
||||
var valueFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(propertyExpressionText);
|
||||
var displayFieldName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(displayExpressionText);
|
||||
|
||||
var displayFieldNameParts = displayFieldName.Split('.');
|
||||
displayFieldName = displayFieldNameParts[displayFieldNameParts.Length - 1];
|
||||
|
||||
tag.Attributes["id"] = html.Id(propertyExpressionText);
|
||||
tag.Attributes["name"] = valueFieldName;
|
||||
tag.Attributes["ng-model"] = valueFieldName;
|
||||
|
||||
var ngOptionsFormat = "a.{0} as a.{1} for a in {2}";
|
||||
var ngOptions = string.Format(ngOptionsFormat, valueFieldName, displayFieldName, source);
|
||||
tag.Attributes["ng-options"] = ngOptions;
|
||||
|
||||
if (nullOption != null)
|
||||
{
|
||||
var nullOptionTag = new TagBuilder("option");
|
||||
nullOptionTag.Attributes["value"] = string.Empty;
|
||||
nullOptionTag.InnerHtml.SetContent(nullOption);
|
||||
tag.InnerHtml.SetContent(nullOptionTag);
|
||||
}
|
||||
|
||||
var clientValidators = html.GetClientValidationRules(metadata, null);
|
||||
var isRequired = clientValidators.SingleOrDefault(cv => string.Equals(cv.ValidationType, "required", StringComparison.OrdinalIgnoreCase)) != null;
|
||||
if (isRequired)
|
||||
{
|
||||
tag.Attributes["required"] = string.Empty;
|
||||
}
|
||||
|
||||
tag.MergeAttributes(htmlAttributes, replaceExisting: true);
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static IHtmlContent ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string formName)
|
||||
{
|
||||
return ngValidationMessageFor(htmlHelper, expression, formName, ((IDictionary<string, object>)new RouteValueDictionary()));
|
||||
}
|
||||
|
||||
public static IHtmlContent ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, string formName, object htmlAttributes)
|
||||
{
|
||||
return ngValidationMessageFor(htmlHelper, expression, formName, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
|
||||
}
|
||||
|
||||
public static IHtmlContent ngValidationMessageFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string formName, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
|
||||
var modelName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
||||
|
||||
//var clientValidators = metadata.GetValidators(html.ViewContext.Controller.ControllerContext)
|
||||
// .SelectMany(v => v.GetClientValidationRules());
|
||||
var clientValidators = html.GetClientValidationRules(metadata, null);
|
||||
var tags = new List<TagBuilder>();
|
||||
|
||||
// Get validation messages from data type
|
||||
// TODO: How to get validation messages from model metadata? All methods/properties required seem protected internal :(
|
||||
|
||||
foreach (var validator in clientValidators)
|
||||
{
|
||||
var tag = new TagBuilder("span");
|
||||
tag.Attributes["ng-cloak"] = string.Empty;
|
||||
|
||||
if (string.Equals(validator.ValidationType, "required"))
|
||||
{
|
||||
tag.Attributes["ng-show"] = string.Format("({0}.submitAttempted || {0}.{1}.$dirty || {0}.{1}.visited) && {0}.{1}.$error.{2}", formName, modelName, "required");
|
||||
tag.InnerHtml.SetContent(validator.ErrorMessage);
|
||||
}
|
||||
else if (string.Equals(validator.ValidationType, "length"))
|
||||
{
|
||||
tag.Attributes["ng-show"] = string.Format("({0}.submitAttempted || {0}.{1}.$dirty || {0}.{1}.visited) && ({0}.{1}.$error.minlength || {0}.{1}.$error.maxlength)",
|
||||
formName, modelName);
|
||||
tag.InnerHtml.SetContent(validator.ErrorMessage);
|
||||
}
|
||||
else if (string.Equals(validator.ValidationType, "range"))
|
||||
{
|
||||
tag.Attributes["ng-show"] = string.Format("({0}.submitAttempted || {0}.{1}.$dirty || {0}.{1}.visited) && ({0}.{1}.$error.min || {0}.{1}.$error.max)",
|
||||
formName, modelName);
|
||||
tag.InnerHtml.SetContent(validator.ErrorMessage);
|
||||
}
|
||||
// TODO: Regex, equalto, remote
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tag.MergeAttributes(htmlAttributes);
|
||||
tags.Add(tag);
|
||||
}
|
||||
|
||||
return new HtmlString(String.Concat(tags.Select(t => t.ToString())));
|
||||
}
|
||||
|
||||
public static string ngValidationClassFor<TModel, TProperty>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> expression, string formName, string className)
|
||||
{
|
||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||
var metadata = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
|
||||
var modelName = html.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);
|
||||
var ngClassFormat = "{{ '{0}' : ({1}.submitAttempted || {1}.{2}.$dirty || {1}.{2}.visited) && {1}.{2}.$invalid }}";
|
||||
|
||||
return string.Format(ngClassFormat, className, formName, modelName);
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> MergeAttributes(IDictionary<string, object> source, IDictionary<string, object> target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
// Keys in target win over keys in source
|
||||
foreach (var pair in source)
|
||||
{
|
||||
if (!target.ContainsKey(pair.Key))
|
||||
{
|
||||
target[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Newtonsoft.Json;
|
||||
using Microsoft.AspNet.Html.Abstractions;
|
||||
using Microsoft.AspNet.Mvc.ViewFeatures;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Rendering
|
||||
{
|
||||
public static class JsonExtensions
|
||||
{
|
||||
public static IHtmlContent Json<T, TData>(this IHtmlHelper<T> helper, TData data)
|
||||
{
|
||||
return Json(helper, data, new RouteValueDictionary());
|
||||
}
|
||||
|
||||
public static IHtmlContent Json<T, TData>(this IHtmlHelper<T> helper, TData data, object htmlAttributes)
|
||||
{
|
||||
return Json(helper, data, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
|
||||
}
|
||||
|
||||
public static IHtmlContent Json<T, TData>(this IHtmlHelper<T> helper, TData data, IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
var builder = new TagBuilder("script");
|
||||
builder.Attributes["type"] = "application/json";
|
||||
builder.MergeAttributes(htmlAttributes);
|
||||
|
||||
var innerContent = data is JsonString ? data.ToString() : JsonConvert.SerializeObject(data);
|
||||
innerContent.Replace("<", "\u003C").Replace(">", "\u003E");
|
||||
builder.InnerHtml.SetContentEncoded(innerContent);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IHtmlContent InlineData<T>(this IHtmlHelper<T> helper, string actionName, string controllerName)
|
||||
{
|
||||
//var result = helper.Action(actionName, controllerName);
|
||||
//var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
|
||||
//var url = urlHelper.Action(actionName, controllerName);
|
||||
|
||||
//return helper.Json(new JsonString(result), new RouteValueDictionary {
|
||||
// { "app-inline-data", null },
|
||||
// { "for", url }
|
||||
//});
|
||||
|
||||
return helper.Json(new JsonString(new object()), null);
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonString
|
||||
{
|
||||
public JsonString(object value)
|
||||
: this(value.ToString())
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public JsonString(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public string Value { get; private set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class ApiResult : ActionResult
|
||||
{
|
||||
public ApiResult(ModelStateDictionary modelState)
|
||||
: this()
|
||||
{
|
||||
if (modelState.Any(m => m.Value.Errors.Count > 0))
|
||||
{
|
||||
StatusCode = 400;
|
||||
Message = "The model submitted was invalid. Please correct the specified errors and try again.";
|
||||
ModelErrors = modelState
|
||||
.SelectMany(m => m.Value.Errors.Select(me => new ModelError
|
||||
{
|
||||
FieldName = m.Key,
|
||||
ErrorMessage = me.ErrorMessage
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public ApiResult()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public int? StatusCode { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public object Data { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public IEnumerable<ModelError> ModelErrors { get; set; }
|
||||
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
if (StatusCode.HasValue)
|
||||
{
|
||||
context.HttpContext.Response.StatusCode = StatusCode.Value;
|
||||
}
|
||||
|
||||
var json = new JsonResult(this);
|
||||
return json.ExecuteResultAsync(context);
|
||||
}
|
||||
|
||||
public class ModelError
|
||||
{
|
||||
public string FieldName { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace System.ComponentModel.DataAnnotations
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
|
||||
public class ForcedModelErrorAttribute : ValidationAttribute
|
||||
{
|
||||
public ForcedModelErrorAttribute(object failValue)
|
||||
{
|
||||
FailValue = failValue;
|
||||
}
|
||||
|
||||
public object FailValue { get; private set; }
|
||||
|
||||
public override string FormatErrorMessage(string name)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, "The field {0} was forced to fail model validation.", name);
|
||||
}
|
||||
|
||||
public override bool IsValid(object value)
|
||||
{
|
||||
#if DEBUG
|
||||
return value == null || !value.Equals(FailValue);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Mvc.Filters;
|
||||
|
||||
namespace MusicStore.Spa.Infrastructure
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||
public sealed class NoCacheAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnResultExecuting(ResultExecutingContext context)
|
||||
{
|
||||
context.HttpContext.Response.Headers["Cache-Control"] = "no-cache, no-store, max-age=0";
|
||||
context.HttpContext.Response.Headers["Pragma"] = "no-cache";
|
||||
context.HttpContext.Response.Headers["Expires"] = "-1";
|
||||
|
||||
base.OnResultExecuting(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Spa.Infrastructure
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
|
||||
internal sealed class NotNullAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Data.Entity;
|
||||
|
||||
namespace MusicStore.Infrastructure
|
||||
{
|
||||
public interface IPagedList<T>
|
||||
{
|
||||
IEnumerable<T> Data { get; }
|
||||
|
||||
int Page { get; }
|
||||
|
||||
int PageSize { get; }
|
||||
|
||||
int TotalCount { get; }
|
||||
}
|
||||
|
||||
internal class PagedList<T> : IPagedList<T>
|
||||
{
|
||||
public PagedList(IEnumerable<T> data, int page, int pageSize, int totalCount)
|
||||
{
|
||||
Data = data;
|
||||
Page = page;
|
||||
PageSize = pageSize;
|
||||
TotalCount = totalCount;
|
||||
}
|
||||
|
||||
public IEnumerable<T> Data { get; private set; }
|
||||
|
||||
public int Page { get; private set; }
|
||||
|
||||
public int PageSize { get; private set; }
|
||||
|
||||
public int TotalCount{get; private set; }
|
||||
}
|
||||
|
||||
public static class PagedListExtensions
|
||||
{
|
||||
public static IPagedList<T> ToPagedList<T>(this IQueryable<T> query, int page, int pageSize)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException("query");
|
||||
}
|
||||
|
||||
var pagingConfig = new PagingConfig(page, pageSize);
|
||||
var skipCount = ValidatePagePropertiesAndGetSkipCount(pagingConfig);
|
||||
|
||||
var data = query
|
||||
.Skip(skipCount)
|
||||
.Take(pagingConfig.PageSize)
|
||||
.ToList();
|
||||
|
||||
if (skipCount > 0 && data.Count == 0)
|
||||
{
|
||||
// Requested page has no records, just return the first page
|
||||
pagingConfig.Page = 1;
|
||||
data = query
|
||||
.Take(pagingConfig.PageSize)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return new PagedList<T>(data, pagingConfig.Page, pagingConfig.PageSize, query.Count());
|
||||
}
|
||||
|
||||
public static Task<IPagedList<TModel>> ToPagedListAsync<TModel, TProperty>(this IQueryable<TModel> query, int page, int pageSize, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection = SortDirection.Ascending)
|
||||
where TModel : class
|
||||
{
|
||||
return ToPagedListAsync<TModel, TProperty, TModel>(query, page, pageSize, sortExpression, defaultSortExpression, defaultSortDirection, null);
|
||||
}
|
||||
|
||||
public static async Task<IPagedList<TResult>> ToPagedListAsync<TModel, TProperty, TResult>(this IQueryable<TModel> query, int page, int pageSize, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection, Func<TModel, TResult> selector)
|
||||
where TModel : class
|
||||
where TResult : class
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException("query");
|
||||
}
|
||||
|
||||
var pagingConfig = new PagingConfig(page, pageSize);
|
||||
var skipCount = ValidatePagePropertiesAndGetSkipCount(pagingConfig);
|
||||
var dataQuery = query;
|
||||
|
||||
if (defaultSortExpression != null)
|
||||
{
|
||||
dataQuery = dataQuery
|
||||
.SortBy(sortExpression, defaultSortExpression);
|
||||
}
|
||||
|
||||
var data = await dataQuery
|
||||
.Skip(skipCount)
|
||||
.Take(pagingConfig.PageSize)
|
||||
.ToListAsync();
|
||||
|
||||
if (skipCount > 0 && data.Count == 0)
|
||||
{
|
||||
// Requested page has no records, just return the first page
|
||||
pagingConfig.Page = 1;
|
||||
data = await dataQuery
|
||||
.Take(pagingConfig.PageSize)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
var count = await query.CountAsync();
|
||||
|
||||
var resultData = selector != null
|
||||
? data.Select(selector)
|
||||
: data.Cast<TResult>();
|
||||
|
||||
return new PagedList<TResult>(resultData, pagingConfig.Page, pagingConfig.PageSize, count);
|
||||
}
|
||||
|
||||
private static int ValidatePagePropertiesAndGetSkipCount(PagingConfig pagingConfig)
|
||||
{
|
||||
if (pagingConfig.Page < 1)
|
||||
{
|
||||
pagingConfig.Page = 1;
|
||||
}
|
||||
|
||||
if (pagingConfig.PageSize < 10)
|
||||
{
|
||||
pagingConfig.PageSize = 10;
|
||||
}
|
||||
|
||||
if (pagingConfig.PageSize > 100)
|
||||
{
|
||||
pagingConfig.PageSize = 100;
|
||||
}
|
||||
|
||||
return pagingConfig.PageSize * (pagingConfig.Page - 1);
|
||||
}
|
||||
|
||||
internal class PagingConfig
|
||||
{
|
||||
public PagingConfig(int page, int pageSize)
|
||||
{
|
||||
Page = page;
|
||||
PageSize = pageSize;
|
||||
}
|
||||
|
||||
public int Page { get; set; }
|
||||
|
||||
public int PageSize { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class SmartJsonResult : ActionResult
|
||||
{
|
||||
public SmartJsonResult() : base()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public JsonSerializerSettings Settings { get; set; }
|
||||
|
||||
public object Data { get; set; }
|
||||
|
||||
public int? StatusCode { get; set; }
|
||||
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
//if (!context.IsChildAction)
|
||||
//{
|
||||
// if (StatusCode.HasValue)
|
||||
// {
|
||||
// context.HttpContext.Response.StatusCode = StatusCode.Value;
|
||||
// }
|
||||
// context.HttpContext.Response.ContentType = "application/json";
|
||||
// context.HttpContext.Response.ContentEncoding = Encoding.UTF8;
|
||||
//}
|
||||
|
||||
return context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(Data, Settings ?? new JsonSerializerSettings()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Infrastructure
|
||||
{
|
||||
public enum SortDirection
|
||||
{
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.AspNet.Mvc.ViewFeatures;
|
||||
|
||||
namespace MusicStore.Infrastructure
|
||||
{
|
||||
public static class SortExpression
|
||||
{
|
||||
private const string SORT_DIRECTION_DESC = " DESC";
|
||||
|
||||
public static IQueryable<TModel> SortBy<TModel, TProperty>(this IQueryable<TModel> query, string sortExpression, Expression<Func<TModel, TProperty>> defaultSortExpression, SortDirection defaultSortDirection = SortDirection.Ascending) where TModel : class
|
||||
{
|
||||
return SortBy(query, sortExpression ?? Create(defaultSortExpression, defaultSortDirection));
|
||||
}
|
||||
|
||||
public static string Create<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression, SortDirection sortDirection = SortDirection.Ascending) where TModel : class
|
||||
{
|
||||
var expressionText = ExpressionHelper.GetExpressionText(expression);
|
||||
// TODO: Validate the expression depth, etc.
|
||||
|
||||
var sortExpression = expressionText;
|
||||
|
||||
if (sortDirection == SortDirection.Descending)
|
||||
{
|
||||
sortExpression += SORT_DIRECTION_DESC;
|
||||
}
|
||||
|
||||
return sortExpression;
|
||||
}
|
||||
|
||||
public static IQueryable<T> SortBy<T>(this IQueryable<T> source, string sortExpression) where T : class
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException("source");
|
||||
}
|
||||
|
||||
if (String.IsNullOrWhiteSpace(sortExpression))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
sortExpression = sortExpression.Trim();
|
||||
var isDescending = false;
|
||||
|
||||
// DataSource control passes the sort parameter with a direction
|
||||
// if the direction is descending
|
||||
if (sortExpression.EndsWith(SORT_DIRECTION_DESC, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
isDescending = true;
|
||||
var descIndex = sortExpression.Length - SORT_DIRECTION_DESC.Length;
|
||||
sortExpression = sortExpression.Substring(0, descIndex).Trim();
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(sortExpression))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
ParameterExpression parameter = Expression.Parameter(source.ElementType, String.Empty);
|
||||
|
||||
// Build up the property expression, e.g.: (m => m.Foo.Bar)
|
||||
var sortExpressionParts = sortExpression.Split('.');
|
||||
Expression propertyExpression = parameter;
|
||||
foreach (var property in sortExpressionParts)
|
||||
{
|
||||
propertyExpression = Expression.Property(propertyExpression, property);
|
||||
}
|
||||
|
||||
LambdaExpression lambda = Expression.Lambda(propertyExpression, parameter);
|
||||
|
||||
var methodName = (isDescending) ? "OrderByDescending" : "OrderBy";
|
||||
|
||||
Expression methodCallExpression = Expression.Call(
|
||||
typeof(Queryable),
|
||||
methodName,
|
||||
new [] { source.ElementType, propertyExpression.Type },
|
||||
source.Expression,
|
||||
Expression.Quote(lambda));
|
||||
|
||||
return (IQueryable<T>)source.Provider.CreateQuery(methodCallExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace MusicStore.Spa
|
||||
{
|
||||
public static class MessageServices
|
||||
{
|
||||
public static Task SendEmailAsync(string email, string subject, string message)
|
||||
{
|
||||
// Plug in your email service
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public static Task SendSmsAsync(string number, string message)
|
||||
{
|
||||
// Plug in your sms service
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class ExternalLoginConfirmationViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "User name")]
|
||||
public string UserName { get; set; }
|
||||
}
|
||||
|
||||
public class ManageUserViewModel
|
||||
{
|
||||
[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 LoginViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "User name")]
|
||||
public string UserName { 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]
|
||||
[Display(Name = "User name")]
|
||||
public string UserName { 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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class Album
|
||||
{
|
||||
public Album()
|
||||
{
|
||||
// TODO: Temporary hack to populate the orderdetails until EF does this automatically.
|
||||
OrderDetails = new List<OrderDetail>();
|
||||
}
|
||||
|
||||
[ScaffoldColumn(false)]
|
||||
public int AlbumId { get; set; }
|
||||
|
||||
public int GenreId { get; set; }
|
||||
|
||||
public int ArtistId { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(160, MinimumLength = 2)]
|
||||
[ForcedModelError("fail")]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(0.01, 100.00)]
|
||||
[DataType(DataType.Currency)]
|
||||
public decimal Price { get; set; }
|
||||
|
||||
[Display(Name = "Album Art URL")]
|
||||
[StringLength(1024)]
|
||||
public string AlbumArtUrl { get; set; }
|
||||
|
||||
public virtual Genre Genre { get; set; }
|
||||
|
||||
public virtual Artist Artist { get; set; }
|
||||
|
||||
public virtual ICollection<OrderDetail> OrderDetails { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class Artist
|
||||
{
|
||||
public int ArtistId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class CartItem
|
||||
{
|
||||
[Key]
|
||||
public int CartItemId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string CartId { get; set; }
|
||||
public int AlbumId { get; set; }
|
||||
public int Count { get; set; }
|
||||
|
||||
[DataType(DataType.DateTime)]
|
||||
public DateTime DateCreated { get; set; }
|
||||
|
||||
public virtual Album Album { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class Genre
|
||||
{
|
||||
public Genre()
|
||||
{
|
||||
Albums = new List<Album>();
|
||||
}
|
||||
|
||||
public int GenreId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual ICollection<Album> Albums { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.EntityFramework;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Data.Entity.Metadata;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class ApplicationUser : IdentityUser { }
|
||||
|
||||
public class MusicStoreContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public MusicStoreContext()
|
||||
{
|
||||
}
|
||||
|
||||
public DbSet<Album> Albums { get; set; }
|
||||
public DbSet<Artist> Artists { get; set; }
|
||||
public DbSet<Order> Orders { get; set; }
|
||||
public DbSet<Genre> Genres { get; set; }
|
||||
public DbSet<CartItem> CartItems { get; set; }
|
||||
public DbSet<OrderDetail> OrderDetails { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
// Configure pluralization
|
||||
builder.Entity<Album>().ToTable("Albums");
|
||||
builder.Entity<Artist>().ToTable("Artists");
|
||||
builder.Entity<Order>().ToTable("Orders");
|
||||
builder.Entity<Genre>().ToTable("Genres");
|
||||
builder.Entity<CartItem>().ToTable("CartItems");
|
||||
builder.Entity<OrderDetail>().ToTable("OrderDetails");
|
||||
|
||||
base.OnModelCreating(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
//[Bind(Include = "FirstName,LastName,Address,City,State,PostalCode,Country,Phone,Email")]
|
||||
public class Order
|
||||
{
|
||||
public Order()
|
||||
{
|
||||
OrderDetails = new List<OrderDetail>();
|
||||
}
|
||||
|
||||
[ScaffoldColumn(false)]
|
||||
public int OrderId { get; set; }
|
||||
|
||||
[ScaffoldColumn(false)]
|
||||
public DateTime OrderDate { get; set; }
|
||||
|
||||
[Required]
|
||||
[ScaffoldColumn(false)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "First Name")]
|
||||
[StringLength(160)]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Last Name")]
|
||||
[StringLength(160)]
|
||||
public string LastName { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(70, MinimumLength = 3)]
|
||||
public string Address { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(40)]
|
||||
public string City { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(40)]
|
||||
public string State { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Postal Code")]
|
||||
[StringLength(10, MinimumLength = 5)]
|
||||
public string PostalCode { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(40)]
|
||||
public string Country { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(24)]
|
||||
[DataType(DataType.PhoneNumber)]
|
||||
public string Phone { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Email Address")]
|
||||
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}",
|
||||
ErrorMessage = "Email is not valid.")]
|
||||
[DataType(DataType.EmailAddress)]
|
||||
public string Email { get; set; }
|
||||
|
||||
[ScaffoldColumn(false)]
|
||||
public decimal Total { get; set; }
|
||||
|
||||
public ICollection<OrderDetail> OrderDetails { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
namespace MusicStore.Models
|
||||
{
|
||||
public class OrderDetail
|
||||
{
|
||||
public int OrderDetailId { get; set; }
|
||||
public int OrderId { get; set; }
|
||||
public int AlbumId { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal UnitPrice { get; set; }
|
||||
|
||||
public virtual Album Album { get; set; }
|
||||
public virtual Order Order { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,939 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.AspNet.Identity.EntityFramework;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Data.Entity.Storage;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
using MusicStore.Spa;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public static class SampleData
|
||||
{
|
||||
const string imgUrl = "/images/placeholder.png";
|
||||
|
||||
public static async Task InitializeMusicStoreDatabaseAsync(IServiceProvider serviceProvider)
|
||||
{
|
||||
using (var db = serviceProvider.GetService<MusicStoreContext>())
|
||||
{
|
||||
if (await db.Database.EnsureCreatedAsync())
|
||||
{
|
||||
await InsertTestData(serviceProvider);
|
||||
await CreateAdminUser(serviceProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task CreateAdminUser(IServiceProvider serviceProvider)
|
||||
{
|
||||
var settings = serviceProvider.GetService<IOptions<SiteSettings>>().Value;
|
||||
const string adminRole = "Administrator";
|
||||
|
||||
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
|
||||
var roleManager = serviceProvider.GetService<RoleManager<IdentityRole>>();
|
||||
|
||||
if (!await roleManager.RoleExistsAsync(adminRole))
|
||||
{
|
||||
await roleManager.CreateAsync(new IdentityRole(adminRole));
|
||||
}
|
||||
|
||||
var user = await userManager.FindByNameAsync(settings.DefaultAdminUsername);
|
||||
if (user == null)
|
||||
{
|
||||
user = new ApplicationUser { UserName = settings.DefaultAdminUsername };
|
||||
await userManager.CreateAsync(user, settings.DefaultAdminPassword);
|
||||
await userManager.AddToRoleAsync(user, adminRole);
|
||||
await userManager.AddClaimAsync(user, new Claim("app-ManageStore", "Allowed"));
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task InsertTestData(IServiceProvider serviceProvider)
|
||||
{
|
||||
var albums = GetAlbums(imgUrl, Genres, Artists);
|
||||
|
||||
await AddOrUpdateAsync(serviceProvider, g => g.GenreId, Genres.Select(genre => genre.Value));
|
||||
await AddOrUpdateAsync(serviceProvider, a => a.ArtistId, Artists.Select(artist => artist.Value));
|
||||
await AddOrUpdateAsync(serviceProvider, a => a.AlbumId, albums);
|
||||
}
|
||||
|
||||
// TODO [EF] This may be replaced by a first class mechanism in EF
|
||||
private static async Task AddOrUpdateAsync<TEntity>(
|
||||
IServiceProvider serviceProvider,
|
||||
Func<TEntity, object> propertyToMatch, IEnumerable<TEntity> entities)
|
||||
where TEntity : class
|
||||
{
|
||||
// Query in a separate context so that we can attach existing entities as modified
|
||||
List<TEntity> existingData;
|
||||
using (var db = serviceProvider.GetService<MusicStoreContext>())
|
||||
{
|
||||
existingData = db.Set<TEntity>().ToList();
|
||||
}
|
||||
|
||||
using (var db = serviceProvider.GetService<MusicStoreContext>())
|
||||
{
|
||||
foreach (var item in entities)
|
||||
{
|
||||
db.Entry(item).State = existingData.Any(g => propertyToMatch(g).Equals(propertyToMatch(item)))
|
||||
? EntityState.Modified
|
||||
: EntityState.Added;
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private static Album[] GetAlbums(string imgUrl, Dictionary<string, Genre> genres, Dictionary<string, Artist> artists)
|
||||
{
|
||||
var albums = new Album[]
|
||||
{
|
||||
new Album { Title = "The Best Of The Men At Work", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Men At Work"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "...And Justice For All", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "עד גבול האור", Genre = genres["World"], Price = 8.99M, Artist = artists["אריק אינשטיין"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Black Light Syndrome", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Terry Bozzio, Tony Levin & Steve Stevens"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "10,000 Days", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "11i", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Supreme Beings of Leisure"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "1960", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Soul-Junk"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "4x4=12 ", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["deadmau5"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Copland Celebration, Vol. I", Genre = genres["Classical"], Price = 8.99M, Artist = artists["London Symphony Orchestra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Lively Mind", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Paul Oakenfold"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Matter of Life and Death", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Real Dead One", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Real Live One", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Rush of Blood to the Head", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Coldplay"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Soprano Inspired", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Britten Sinfonia, Ivor Bolton & Lesley Garrett"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A Winter Symphony", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Abbey Road", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Beatles"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ace Of Spades", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Motörhead"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Achtung Baby", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Acústico MTV", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Os Paralamas Do Sucesso"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Adams, John: The Chairman Dances", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Edo de Waart & San Francisco Symphony"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Adrenaline", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deftones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ænima", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Afrociberdelia", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Chico Science & Nação Zumbi"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "After the Goldrush", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Neil Young"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Airdrawn Dagger", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Sasha"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Album Title Goes Here", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["deadmau5"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Alcohol Fueled Brewtality Live! [Disc 1]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Label Society"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Alcohol Fueled Brewtality Live! [Disc 2]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Label Society"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Alive 2007", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Daft Punk"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "All I Ask of You", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Amen (So Be It)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Paddy Casey"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Animal Vehicle", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Axis of Awesome"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ao Vivo [IMPORT]", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Zeca Pagodinho"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Apocalyptic Love", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Slash"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Appetite for Destruction", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Are You Experienced?", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Jimi Hendrix"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Arquivo II", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Os Paralamas Do Sucesso"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Arquivo Os Paralamas Do Sucesso", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Os Paralamas Do Sucesso"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "A-Sides", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Soundgarden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Audioslave", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Audioslave"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Automatic for the People", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["R.E.M."], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Axé Bahia 2001", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Various Artists"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Babel", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Mumford & Sons"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bach: Goldberg Variations", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Wilhelm Kempff"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bach: The Brandenburg Concertos", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Orchestra of The Age of Enlightenment"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bach: The Cello Suites", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Yo-Yo Ma"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bach: Toccata & Fugue in D Minor", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Ton Koopman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bad Motorfinger", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Soundgarden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Balls to the Wall", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Accept"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Banadeek Ta'ala", Genre = genres["World"], Price = 8.99M, Artist = artists["Amr Diab"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Barbie Girl", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Aqua"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bark at the Moon (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bartok: Violin & Viola Concertos", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Yehudi Menuhin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Barulhinho Bom", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Marisa Monte"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "BBC Sessions [Disc 1] [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "BBC Sessions [Disc 2] [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Be Here Now", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Oasis"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bedrock 11 Compiled & Mixed", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["John Digweed"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Berlioz: Symphonie Fantastique", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Michael Tilson Thomas"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Beyond Good And Evil", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Cult"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Big Bad Wolf ", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Armand Van Helden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Big Ones", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Aerosmith"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Black Album", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Black Sabbath Vol. 4 (Remaster)", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Sabbath"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Black Sabbath", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Black Sabbath"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Black", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Blackwater Park", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Opeth"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Blizzard of Ozz", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Blood", Genre = genres["Rock"], Price = 8.99M, Artist = artists["In This Moment"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Blue Moods", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Incognito"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Blue", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Weezer"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bongo Fury", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Frank Zappa & Captain Beefheart"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Boys & Girls", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alabama Shakes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Brave New World", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "B-Sides 1980-1990", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Bunkka", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Paul Oakenfold"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "By The Way", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Red Hot Chili Peppers"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Cake: B-Sides and Rarities", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Cake"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Californication", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Red Hot Chili Peppers"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Carmina Burana", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Boston Symphony Orchestra & Seiji Ozawa"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Carried to Dust (Bonus Track Version)", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Calexico"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Carry On", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Chris Cornell"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Cássia Eller - Sem Limite [Disc 1]", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Cássia Eller"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Chemical Wedding", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Bruce Dickinson"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Chill: Brazil (Disc 1)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Marcos Valle"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Chill: Brazil (Disc 2)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Antônio Carlos Jobim"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Chocolate Starfish And The Hot Dog Flavored Water", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Limp Bizkit"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Chronicle, Vol. 1", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Creedence Clearwater Revival"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Chronicle, Vol. 2", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Creedence Clearwater Revival"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ciao, Baby", Genre = genres["Rock"], Price = 8.99M, Artist = artists["TheStart"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Cidade Negra - Hits", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Cidade Negra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Classic Munkle: Turbo Edition", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Munkle"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Classics: The Best of Sarah Brightman", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Coda", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Come Away With Me", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Norah Jones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Come Taste The Band", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Comfort Eagle", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Cake"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Common Reaction", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Uh Huh Her "], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Compositores", Genre = genres["Rock"], Price = 8.99M, Artist = artists["O Terço"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Contraband", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Velvet Revolver"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Core", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Stone Temple Pilots"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Cornerstone", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Styx"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Cosmicolor", Genre = genres["Rap"], Price = 8.99M, Artist = artists["M-Flo"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Cross", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Justice"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Culture of Fear", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Thievery Corporation"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Da Lama Ao Caos", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Chico Science & Nação Zumbi"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Dakshina", Genre = genres["World"], Price = 8.99M, Artist = artists["Deva Premal"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Dark Side of the Moon", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Death Magnetic", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Deep End of Down", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Above the Fold"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Deep Purple In Rock", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Deixa Entrar", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Falamansa"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Deja Vu", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Crosby, Stills, Nash, and Young"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Di Korpu Ku Alma", Genre = genres["World"], Price = 8.99M, Artist = artists["Lura"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Diary of a Madman (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Diary of a Madman", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Dirt", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alice in Chains"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Diver Down", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Djavan Ao Vivo - Vol. 02", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Djavan"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Djavan Ao Vivo - Vol. 1", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Djavan"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Drum'n'bass for Papa", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Plug"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Duluth", Genre = genres["Country"], Price = 8.99M, Artist = artists["Trampled By Turtles"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Dummy", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Portishead"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Duos II", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Luciana Souza/Romero Lubambo"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Earl Scruggs and Friends", Genre = genres["Country"], Price = 8.99M, Artist = artists["Earl Scruggs"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Eden", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "El Camino", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Black Keys"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Elegant Gypsy", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Al di Meola"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Elements Of Life", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Tiësto"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Elis Regina-Minha História", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Elis Regina"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Emergency On Planet Earth", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Jamiroquai"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Emotion", Genre = genres["World"], Price = 8.99M, Artist = artists["Papa Wemba"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "English Renaissance", Genre = genres["Classical"], Price = 8.99M, Artist = artists["The King's Singers"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Every Kind of Light", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Posies"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Faceless", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Godsmack"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Facelift", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alice in Chains"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Fair Warning", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Fear of a Black Planet", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Public Enemy"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Fear Of The Dark", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Feels Like Home", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Norah Jones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Fireball", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Fly", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "For Those About To Rock We Salute You", Genre = genres["Rock"], Price = 8.99M, Artist = artists["AC/DC"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Four", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Blues Traveler"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Frank", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Amy Winehouse"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Further Down the Spiral", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Nine Inch Nails"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Garage Inc. (Disc 1)", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Garage Inc. (Disc 2)", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Garbage", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Garbage"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Good News For People Who Love Bad News", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Modest Mouse"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Gordon", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Barenaked Ladies"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Górecki: Symphony No. 3", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Adrian Leaper & Doreen de Feis"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greatest Hits I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greatest Hits II", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greatest Hits", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Duck Sauce"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greatest Hits", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Lenny Kravitz"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greatest Hits", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Lenny Kravitz"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greatest Kiss", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Kiss"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Greetings from Michigan", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Sufjan Stevens"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Group Therapy", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Above & Beyond"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Handel: The Messiah (Highlights)", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Scholars Baroque Ensemble"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Haydn: Symphonies 99 - 104", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Royal Philharmonic Orchestra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Heart of the Night", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Spyro Gyra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Heart On", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Eagles of Death Metal"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Holy Diver", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Dio"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Homework", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Daft Punk"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Hot Rocks, 1964-1971 (Disc 1)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Rolling Stones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Houses Of The Holy", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "How To Dismantle An Atomic Bomb", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Human", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Projected"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Hunky Dory", Genre = genres["Rock"], Price = 8.99M, Artist = artists["David Bowie"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Hymns", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Projected"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Hysteria", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Def Leppard"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Absentia", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Porcupine Tree"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Between", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Paul Van Dyk"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Rainbows", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Radiohead"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Step", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Stevie Ray Vaughan & Double Trouble"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In the court of the Crimson King", Genre = genres["Rock"], Price = 8.99M, Artist = artists["King Crimson"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Through The Out Door", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Your Honor [Disc 1]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Foo Fighters"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "In Your Honor [Disc 2]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Foo Fighters"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Indestructible", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Rancid"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Infinity", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Journey"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Into The Light", Genre = genres["Rock"], Price = 8.99M, Artist = artists["David Coverdale"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Introspective", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Pet Shop Boys"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Iron Maiden", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "ISAM", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Amon Tobin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "IV", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Jagged Little Pill", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Alanis Morissette"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Jagged Little Pill", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Alanis Morissette"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Jorge Ben Jor 25 Anos", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Jorge Ben"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Jota Quest-1995", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Jota Quest"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Kick", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["INXS"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Kill 'Em All", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Kind of Blue", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "King For A Day Fool For A Lifetime", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Faith No More"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Kiss", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Carly Rae Jepsen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Last Call", Genre = genres["Country"], Price = 8.99M, Artist = artists["Cayouche"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Le Freak", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Chic"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Le Tigre", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Le Tigre"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Led Zeppelin I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Led Zeppelin II", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Led Zeppelin III", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Let There Be Rock", Genre = genres["Rock"], Price = 8.99M, Artist = artists["AC/DC"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Little Earthquakes", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Tori Amos"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live [Disc 1]", Genre = genres["Blues"], Price = 8.99M, Artist = artists["The Black Crowes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live [Disc 2]", Genre = genres["Blues"], Price = 8.99M, Artist = artists["The Black Crowes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live After Death", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live At Donington 1992 (Disc 1)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live At Donington 1992 (Disc 2)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live on Earth", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["The Cat Empire"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Live On Two Legs [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Living After Midnight", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Judas Priest"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Living", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Paddy Casey"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Load", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Love Changes Everything", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "MacArthur Park Suite", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Donna Summer"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Machine Head", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Magical Mystery Tour", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Beatles"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mais Do Mesmo", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Legião Urbana"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Maquinarama", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Skank"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Marasim", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Jagjit Singh"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mascagni: Cavalleria Rusticana", Genre = genres["Classical"], Price = 8.99M, Artist = artists["James Levine"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Master of Puppets", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mechanics & Mathematics", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Venus Hum"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mental Jewelry", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Live"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Metallics", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "meteora", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Linkin Park"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Meus Momentos", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Gonzaguinha"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mezmerize", Genre = genres["Metal"], Price = 8.99M, Artist = artists["System Of A Down"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mezzanine", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Massive Attack"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Miles Ahead", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Milton Nascimento Ao Vivo", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Milton Nascimento"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Minas", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Milton Nascimento"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Minha Historia", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Chico Buarque"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Misplaced Childhood", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Marillion"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "MK III The Final Concerts [Disc 1]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Morning Dance", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Spyro Gyra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Motley Crue Greatest Hits", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Mötley Crüe"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Moving Pictures", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Rush"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mozart: Chamber Music", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Nash Ensemble"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Mozart: Symphonies Nos. 40 & 41", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Berliner Philharmoniker"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Murder Ballads", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Nick Cave and the Bad Seeds"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Music For The Jilted Generation", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["The Prodigy"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "My Generation - The Very Best Of The Who", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Who"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "My Name is Skrillex", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Skrillex"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Na Pista", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Cláudio Zoli"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Nevermind", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Nirvana"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "New Adventures In Hi-Fi", Genre = genres["Rock"], Price = 8.99M, Artist = artists["R.E.M."], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "New Divide", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Linkin Park"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "New York Dolls", Genre = genres["Punk"], Price = 8.99M, Artist = artists["New York Dolls"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "News Of The World", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Nielsen: The Six Symphonies", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Göteborgs Symfoniker & Neeme Järvi"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Night At The Opera", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Queen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Night Castle", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Trans-Siberian Orchestra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Nkolo", Genre = genres["World"], Price = 8.99M, Artist = artists["Lokua Kanza"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "No More Tears (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "No Prayer For The Dying", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "No Security", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Rolling Stones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "O Brother, Where Art Thou?", Genre = genres["Country"], Price = 8.99M, Artist = artists["Alison Krauss"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "O Samba Poconé", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Skank"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "O(+>", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Prince"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Oceania", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Smashing Pumpkins"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Off the Deep End", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Weird Al"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "OK Computer", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Radiohead"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Olodum", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Olodum"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "One Love", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["David Guetta"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Operation: Mindcrime", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Queensrÿche"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Opiate", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Outbreak", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Dennis Chambers"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Pachelbel: Canon & Gigue", Genre = genres["Classical"], Price = 8.99M, Artist = artists["English Concert & Trevor Pinnock"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Paid in Full", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Eric B. and Rakim"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Para Siempre", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Vicente Fernandez"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Pause", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Four Tet"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Peace Sells... but Who's Buying", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Megadeth"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Physical Graffiti [Disc 1]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Physical Graffiti [Disc 2]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Physical Graffiti", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Piece Of Mind", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Pinkerton", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Weezer"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Plays Metallica By Four Cellos", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Apocalyptica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Pop", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Powerslave", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Prenda Minha", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Caetano Veloso"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Presence", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Pretty Hate Machine", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Nine Inch Nails"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Prisoner", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Jezabels"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Privateering", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Mark Knopfler"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Prokofiev: Romeo & Juliet", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Michael Tilson Thomas"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Prokofiev: Symphony No.1", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sergei Prokofiev & Yuri Temirkanov"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "PSY's Best 6th Part 1", Genre = genres["Pop"], Price = 8.99M, Artist = artists["PSY"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Purcell: The Fairy Queen", Genre = genres["Classical"], Price = 8.99M, Artist = artists["London Classical Players"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Purpendicular", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Purple", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Stone Temple Pilots"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Quanta Gente Veio Ver (Live)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Gilberto Gil"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Quanta Gente Veio ver--Bônus De Carnaval", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Gilberto Gil"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Quiet Songs", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Aisha Duo"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Raices", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Los Tigres del Norte"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Raising Hell", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Run DMC"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Raoul and the Kings of Spain ", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tears For Fears"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rattle And Hum", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Raul Seixas", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Raul Seixas"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Recovery [Explicit]", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Eminem"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Reign In Blood", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Slayer"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Relayed", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Yes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "ReLoad", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Respighi:Pines of Rome", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Eugene Ormandy"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Restless and Wild", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Accept"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Retrospective I (1974-1980)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Rush"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Revelations", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Audioslave"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Revolver", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Beatles"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ride the Lighting ", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ride The Lightning", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ring My Bell", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Anita Ward"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Riot Act", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rise of the Phoenix", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Before the Dawn"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rock In Rio [CD1]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rock In Rio [CD2]", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rock In Rio [CD2]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Roda De Funk", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Funk Como Le Gusta"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Room for Squares", Genre = genres["Pop"], Price = 8.99M, Artist = artists["John Mayer"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Root Down", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Jimmy Smith"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rounds", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Four Tet"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rubber Factory", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Black Keys"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Rust in Peace", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Megadeth"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Sambas De Enredo 2001", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Various Artists"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Santana - As Years Go By", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Santana"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Santana Live", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Santana"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Saturday Night Fever", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Bee Gees"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Scary Monsters and Nice Sprites", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Skrillex"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Scheherazade", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Chicago Symphony Orchestra & Fritz Reiner"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "SCRIABIN: Vers la flamme", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Christopher O'Riley"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Second Coming", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Stone Roses"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Serie Sem Limite (Disc 1)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Tim Maia"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Serie Sem Limite (Disc 2)", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Tim Maia"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Serious About Men", Genre = genres["Rap"], Price = 8.99M, Artist = artists["The Rubberbandits"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Seventh Son of a Seventh Son", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Short Bus", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Filter"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Sibelius: Finlandia", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Berliner Philharmoniker"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Singles Collection", Genre = genres["Rock"], Price = 8.99M, Artist = artists["David Bowie"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Six Degrees of Inner Turbulence", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Dream Theater"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Slave To The Empire", Genre = genres["Metal"], Price = 8.99M, Artist = artists["T&N"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Slaves And Masters", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Slouching Towards Bethlehem", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Robert James"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Smash", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Offspring"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Something Special", Genre = genres["Country"], Price = 8.99M, Artist = artists["Dolly Parton"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Somewhere in Time", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Song(s) You Know By Heart", Genre = genres["Country"], Price = 8.99M, Artist = artists["Jimmy Buffett"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Sound of Music", Genre = genres["Punk"], Price = 8.99M, Artist = artists["Adicts"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "South American Getaway", Genre = genres["Classical"], Price = 8.99M, Artist = artists["The 12 Cellists of The Berlin Philharmonic"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Sozinho Remix Ao Vivo", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Caetano Veloso"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Speak of the Devil", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Spiritual State", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Nujabes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "St. Anger", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Metallica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Still Life", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Opeth"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Stop Making Sense", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Talking Heads"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Stormbringer", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Stranger than Fiction", Genre = genres["Punk"], Price = 8.99M, Artist = artists["Bad Religion"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Strauss: Waltzes", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Eugene Ormandy"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Supermodified", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Amon Tobin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Supernatural", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Santana"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Surfing with the Alien (Remastered)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Joe Satriani"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Switched-On Bach", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Wendy Carlos"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Symphony", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Szymanowski: Piano Works, Vol. 1", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Martin Roscoe"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Tchaikovsky: The Nutcracker", Genre = genres["Classical"], Price = 8.99M, Artist = artists["London Symphony Orchestra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ted Nugent", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Ted Nugent"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Teflon Don", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Rick Ross"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Tell Another Joke at the Ol' Choppin' Block", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Danielson Famile"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Temple of the Dog", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Temple of the Dog"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Ten", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Texas Flood", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Stevie Ray Vaughan"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Battle Rages On", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Beast Live", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Paul D'Ianno"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Best Of 1980-1990", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Best of 1990–2000", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Best of Beethoven", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Nicolaus Esterhazy Sinfonia"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Best Of Billy Cobham", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Billy Cobham"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Best of Ed Motta", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Ed Motta"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Best Of Van Halen, Vol. I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Bridge", Genre = genres["R&B"], Price = 8.99M, Artist = artists["Melanie Fiona"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Cage", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tygers of Pan Tang"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Chicago Transit Authority", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Chicago "], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Chronic", Genre = genres["Rap"], Price = 8.99M, Artist = artists["Dr. Dre"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Colour And The Shape", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Foo Fighters"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Crane Wife", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["The Decemberists"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Cream Of Clapton", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Eric Clapton"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Cure", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Cure"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Dark Side Of The Moon", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Divine Conspiracy", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Epica"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Doors", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Doors"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Dream of the Blue Turtles", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Sting"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Essential Miles Davis [Disc 1]", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Essential Miles Davis [Disc 2]", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Miles Davis"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Final Concerts (Disc 2)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deep Purple"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Final Frontier", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Head and the Heart", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Head and the Heart"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Joshua Tree", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Last Night of the Proms", Genre = genres["Classical"], Price = 8.99M, Artist = artists["BBC Concert Orchestra"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Lumineers", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Lumineers"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Number of The Beast", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Number of The Beast", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Police Greatest Hits", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Police"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Song Remains The Same (Disc 1)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Song Remains The Same (Disc 2)", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Southern Harmony and Musical Companion", Genre = genres["Blues"], Price = 8.99M, Artist = artists["The Black Crowes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Spade", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Butch Walker & The Black Widows"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Stone Roses", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Stone Roses"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Suburbs", Genre = genres["Indie"], Price = 8.99M, Artist = artists["Arcade Fire"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Three Tenors Disc1/Disc2", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Carreras, Pavarotti, Domingo"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Trees They Grow So High", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The Wall", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "The X Factor", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Them Crooked Vultures", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Them Crooked Vultures"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "This Is Happening", Genre = genres["Rock"], Price = 8.99M, Artist = artists["LCD Soundsystem"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Thunder, Lightning, Strike", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Go! Team"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Time to Say Goodbye", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sarah Brightman"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Time, Love & Tenderness", Genre = genres["Pop"], Price = 8.99M, Artist = artists["Michael Bolton"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Tomorrow Starts Today", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Mobile"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Tribute", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Ozzy Osbourne"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Tuesday Night Music Club", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Sheryl Crow"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Umoja", Genre = genres["Rock"], Price = 8.99M, Artist = artists["BLØF"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Under the Pink", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Tori Amos"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Undertow", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Tool"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Un-Led-Ed", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Dread Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Unplugged [Live]", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Kiss"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Unplugged", Genre = genres["Blues"], Price = 8.99M, Artist = artists["Eric Clapton"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Unplugged", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Eric Clapton"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Untrue", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Burial"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Use Your Illusion I", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Use Your Illusion II", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Use Your Illusion II", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Guns N' Roses"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Van Halen III", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Van Halen", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Van Halen"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Version 2.0", Genre = genres["Alternative"], Price = 8.99M, Artist = artists["Garbage"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Vinicius De Moraes", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Vinícius De Moraes"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Virtual XI", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Iron Maiden"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Voodoo Lounge", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Rolling Stones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Vozes do MPB", Genre = genres["Latin"], Price = 8.99M, Artist = artists["Various Artists"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Vs.", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pearl Jam"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Wagner: Favourite Overtures", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Sir Georg Solti & Wiener Philharmoniker"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Walking Into Clarksdale", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Page & Plant"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Wapi Yo", Genre = genres["World"], Price = 8.99M, Artist = artists["Lokua Kanza"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "War", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Warner 25 Anos", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Antônio Carlos Jobim"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Wasteland R&Btheque", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Raunchy"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Watermark", Genre = genres["Electronic"], Price = 8.99M, Artist = artists["Enya"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "We Were Exploding Anyway", Genre = genres["Rock"], Price = 8.99M, Artist = artists["65daysofstatic"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Weill: The Seven Deadly Sins", Genre = genres["Classical"], Price = 8.99M, Artist = artists["Orchestre de l'Opéra de Lyon"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "White Pony", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Deftones"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Who's Next", Genre = genres["Rock"], Price = 8.99M, Artist = artists["The Who"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Wish You Were Here", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Pink Floyd"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "With Oden on Our Side", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Amon Amarth"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Worlds", Genre = genres["Jazz"], Price = 8.99M, Artist = artists["Aaron Goldberg"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Worship Music", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Anthrax"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "X&Y", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Coldplay"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Xinti", Genre = genres["World"], Price = 8.99M, Artist = artists["Sara Tavares"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Yano", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Yano"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Yesterday Once More Disc 1/Disc 2", Genre = genres["Pop"], Price = 8.99M, Artist = artists["The Carpenters"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Zooropa", Genre = genres["Rock"], Price = 8.99M, Artist = artists["U2"], AlbumArtUrl = imgUrl },
|
||||
new Album { Title = "Zoso", Genre = genres["Rock"], Price = 8.99M, Artist = artists["Led Zeppelin"], AlbumArtUrl = imgUrl },
|
||||
};
|
||||
|
||||
foreach (var album in albums)
|
||||
{
|
||||
album.ArtistId = album.Artist.ArtistId;
|
||||
album.GenreId = album.Genre.GenreId;
|
||||
}
|
||||
|
||||
return albums;
|
||||
}
|
||||
|
||||
private static Dictionary<string, Artist> artists;
|
||||
public static Dictionary<string, Artist> Artists
|
||||
{
|
||||
get
|
||||
{
|
||||
if (artists == null)
|
||||
{
|
||||
var artistsList = new Artist[]
|
||||
{
|
||||
new Artist { Name = "65daysofstatic" },
|
||||
new Artist { Name = "Aaron Goldberg" },
|
||||
new Artist { Name = "Above & Beyond" },
|
||||
new Artist { Name = "Above the Fold" },
|
||||
new Artist { Name = "AC/DC" },
|
||||
new Artist { Name = "Accept" },
|
||||
new Artist { Name = "Adicts" },
|
||||
new Artist { Name = "Adrian Leaper & Doreen de Feis" },
|
||||
new Artist { Name = "Aerosmith" },
|
||||
new Artist { Name = "Aisha Duo" },
|
||||
new Artist { Name = "Al di Meola" },
|
||||
new Artist { Name = "Alabama Shakes" },
|
||||
new Artist { Name = "Alanis Morissette" },
|
||||
new Artist { Name = "Alberto Turco & Nova Schola Gregoriana" },
|
||||
new Artist { Name = "Alice in Chains" },
|
||||
new Artist { Name = "Alison Krauss" },
|
||||
new Artist { Name = "Amon Amarth" },
|
||||
new Artist { Name = "Amon Tobin" },
|
||||
new Artist { Name = "Amr Diab" },
|
||||
new Artist { Name = "Amy Winehouse" },
|
||||
new Artist { Name = "Anita Ward" },
|
||||
new Artist { Name = "Anthrax" },
|
||||
new Artist { Name = "Antônio Carlos Jobim" },
|
||||
new Artist { Name = "Apocalyptica" },
|
||||
new Artist { Name = "Aqua" },
|
||||
new Artist { Name = "Armand Van Helden" },
|
||||
new Artist { Name = "Arcade Fire" },
|
||||
new Artist { Name = "Audioslave" },
|
||||
new Artist { Name = "Bad Religion" },
|
||||
new Artist { Name = "Barenaked Ladies" },
|
||||
new Artist { Name = "BBC Concert Orchestra" },
|
||||
new Artist { Name = "Bee Gees" },
|
||||
new Artist { Name = "Before the Dawn" },
|
||||
new Artist { Name = "Berliner Philharmoniker" },
|
||||
new Artist { Name = "Billy Cobham" },
|
||||
new Artist { Name = "Black Label Society" },
|
||||
new Artist { Name = "Black Sabbath" },
|
||||
new Artist { Name = "BLØF" },
|
||||
new Artist { Name = "Blues Traveler" },
|
||||
new Artist { Name = "Boston Symphony Orchestra & Seiji Ozawa" },
|
||||
new Artist { Name = "Britten Sinfonia, Ivor Bolton & Lesley Garrett" },
|
||||
new Artist { Name = "Bruce Dickinson" },
|
||||
new Artist { Name = "Buddy Guy" },
|
||||
new Artist { Name = "Burial" },
|
||||
new Artist { Name = "Butch Walker & The Black Widows" },
|
||||
new Artist { Name = "Caetano Veloso" },
|
||||
new Artist { Name = "Cake" },
|
||||
new Artist { Name = "Calexico" },
|
||||
new Artist { Name = "Carly Rae Jepsen" },
|
||||
new Artist { Name = "Carreras, Pavarotti, Domingo" },
|
||||
new Artist { Name = "Cássia Eller" },
|
||||
new Artist { Name = "Cayouche" },
|
||||
new Artist { Name = "Chic" },
|
||||
new Artist { Name = "Chicago " },
|
||||
new Artist { Name = "Chicago Symphony Orchestra & Fritz Reiner" },
|
||||
new Artist { Name = "Chico Buarque" },
|
||||
new Artist { Name = "Chico Science & Nação Zumbi" },
|
||||
new Artist { Name = "Choir Of Westminster Abbey & Simon Preston" },
|
||||
new Artist { Name = "Chris Cornell" },
|
||||
new Artist { Name = "Christopher O'Riley" },
|
||||
new Artist { Name = "Cidade Negra" },
|
||||
new Artist { Name = "Cláudio Zoli" },
|
||||
new Artist { Name = "Coldplay" },
|
||||
new Artist { Name = "Creedence Clearwater Revival" },
|
||||
new Artist { Name = "Crosby, Stills, Nash, and Young" },
|
||||
new Artist { Name = "Daft Punk" },
|
||||
new Artist { Name = "Danielson Famile" },
|
||||
new Artist { Name = "David Bowie" },
|
||||
new Artist { Name = "David Coverdale" },
|
||||
new Artist { Name = "David Guetta" },
|
||||
new Artist { Name = "deadmau5" },
|
||||
new Artist { Name = "Deep Purple" },
|
||||
new Artist { Name = "Def Leppard" },
|
||||
new Artist { Name = "Deftones" },
|
||||
new Artist { Name = "Dennis Chambers" },
|
||||
new Artist { Name = "Deva Premal" },
|
||||
new Artist { Name = "Dio" },
|
||||
new Artist { Name = "Djavan" },
|
||||
new Artist { Name = "Dolly Parton" },
|
||||
new Artist { Name = "Donna Summer" },
|
||||
new Artist { Name = "Dr. Dre" },
|
||||
new Artist { Name = "Dread Zeppelin" },
|
||||
new Artist { Name = "Dream Theater" },
|
||||
new Artist { Name = "Duck Sauce" },
|
||||
new Artist { Name = "Earl Scruggs" },
|
||||
new Artist { Name = "Ed Motta" },
|
||||
new Artist { Name = "Edo de Waart & San Francisco Symphony" },
|
||||
new Artist { Name = "Elis Regina" },
|
||||
new Artist { Name = "Eminem" },
|
||||
new Artist { Name = "English Concert & Trevor Pinnock" },
|
||||
new Artist { Name = "Enya" },
|
||||
new Artist { Name = "Epica" },
|
||||
new Artist { Name = "Eric B. and Rakim" },
|
||||
new Artist { Name = "Eric Clapton" },
|
||||
new Artist { Name = "Eugene Ormandy" },
|
||||
new Artist { Name = "Faith No More" },
|
||||
new Artist { Name = "Falamansa" },
|
||||
new Artist { Name = "Filter" },
|
||||
new Artist { Name = "Foo Fighters" },
|
||||
new Artist { Name = "Four Tet" },
|
||||
new Artist { Name = "Frank Zappa & Captain Beefheart" },
|
||||
new Artist { Name = "Fretwork" },
|
||||
new Artist { Name = "Funk Como Le Gusta" },
|
||||
new Artist { Name = "Garbage" },
|
||||
new Artist { Name = "Gerald Moore" },
|
||||
new Artist { Name = "Gilberto Gil" },
|
||||
new Artist { Name = "Godsmack" },
|
||||
new Artist { Name = "Gonzaguinha" },
|
||||
new Artist { Name = "Göteborgs Symfoniker & Neeme Järvi" },
|
||||
new Artist { Name = "Guns N' Roses" },
|
||||
new Artist { Name = "Gustav Mahler" },
|
||||
new Artist { Name = "In This Moment" },
|
||||
new Artist { Name = "Incognito" },
|
||||
new Artist { Name = "INXS" },
|
||||
new Artist { Name = "Iron Maiden" },
|
||||
new Artist { Name = "Jagjit Singh" },
|
||||
new Artist { Name = "James Levine" },
|
||||
new Artist { Name = "Jamiroquai" },
|
||||
new Artist { Name = "Jimi Hendrix" },
|
||||
new Artist { Name = "Jimmy Buffett" },
|
||||
new Artist { Name = "Jimmy Smith" },
|
||||
new Artist { Name = "Joe Satriani" },
|
||||
new Artist { Name = "John Digweed" },
|
||||
new Artist { Name = "John Mayer" },
|
||||
new Artist { Name = "Jorge Ben" },
|
||||
new Artist { Name = "Jota Quest" },
|
||||
new Artist { Name = "Journey" },
|
||||
new Artist { Name = "Judas Priest" },
|
||||
new Artist { Name = "Julian Bream" },
|
||||
new Artist { Name = "Justice" },
|
||||
new Artist { Name = "Orchestre de l'Opéra de Lyon" },
|
||||
new Artist { Name = "King Crimson" },
|
||||
new Artist { Name = "Kiss" },
|
||||
new Artist { Name = "LCD Soundsystem" },
|
||||
new Artist { Name = "Le Tigre" },
|
||||
new Artist { Name = "Led Zeppelin" },
|
||||
new Artist { Name = "Legião Urbana" },
|
||||
new Artist { Name = "Lenny Kravitz" },
|
||||
new Artist { Name = "Les Arts Florissants & William Christie" },
|
||||
new Artist { Name = "Limp Bizkit" },
|
||||
new Artist { Name = "Linkin Park" },
|
||||
new Artist { Name = "Live" },
|
||||
new Artist { Name = "Lokua Kanza" },
|
||||
new Artist { Name = "London Symphony Orchestra" },
|
||||
new Artist { Name = "Los Tigres del Norte" },
|
||||
new Artist { Name = "Luciana Souza/Romero Lubambo" },
|
||||
new Artist { Name = "Lulu Santos" },
|
||||
new Artist { Name = "Lura" },
|
||||
new Artist { Name = "Marcos Valle" },
|
||||
new Artist { Name = "Marillion" },
|
||||
new Artist { Name = "Marisa Monte" },
|
||||
new Artist { Name = "Mark Knopfler" },
|
||||
new Artist { Name = "Martin Roscoe" },
|
||||
new Artist { Name = "Massive Attack" },
|
||||
new Artist { Name = "Maurizio Pollini" },
|
||||
new Artist { Name = "Megadeth" },
|
||||
new Artist { Name = "Mela Tenenbaum, Pro Musica Prague & Richard Kapp" },
|
||||
new Artist { Name = "Melanie Fiona" },
|
||||
new Artist { Name = "Men At Work" },
|
||||
new Artist { Name = "Metallica" },
|
||||
new Artist { Name = "M-Flo" },
|
||||
new Artist { Name = "Michael Bolton" },
|
||||
new Artist { Name = "Michael Tilson Thomas" },
|
||||
new Artist { Name = "Miles Davis" },
|
||||
new Artist { Name = "Milton Nascimento" },
|
||||
new Artist { Name = "Mobile" },
|
||||
new Artist { Name = "Modest Mouse" },
|
||||
new Artist { Name = "Mötley Crüe" },
|
||||
new Artist { Name = "Motörhead" },
|
||||
new Artist { Name = "Mumford & Sons" },
|
||||
new Artist { Name = "Munkle" },
|
||||
new Artist { Name = "Nash Ensemble" },
|
||||
new Artist { Name = "Neil Young" },
|
||||
new Artist { Name = "New York Dolls" },
|
||||
new Artist { Name = "Nick Cave and the Bad Seeds" },
|
||||
new Artist { Name = "Nicolaus Esterhazy Sinfonia" },
|
||||
new Artist { Name = "Nine Inch Nails" },
|
||||
new Artist { Name = "Nirvana" },
|
||||
new Artist { Name = "Norah Jones" },
|
||||
new Artist { Name = "Nujabes" },
|
||||
new Artist { Name = "O Terço" },
|
||||
new Artist { Name = "Oasis" },
|
||||
new Artist { Name = "Olodum" },
|
||||
new Artist { Name = "Opeth" },
|
||||
new Artist { Name = "Orchestra of The Age of Enlightenment" },
|
||||
new Artist { Name = "Os Paralamas Do Sucesso" },
|
||||
new Artist { Name = "Ozzy Osbourne" },
|
||||
new Artist { Name = "Paddy Casey" },
|
||||
new Artist { Name = "Page & Plant" },
|
||||
new Artist { Name = "Papa Wemba" },
|
||||
new Artist { Name = "Paul D'Ianno" },
|
||||
new Artist { Name = "Paul Oakenfold" },
|
||||
new Artist { Name = "Paul Van Dyk" },
|
||||
new Artist { Name = "Pearl Jam" },
|
||||
new Artist { Name = "Pet Shop Boys" },
|
||||
new Artist { Name = "Pink Floyd" },
|
||||
new Artist { Name = "Plug" },
|
||||
new Artist { Name = "Porcupine Tree" },
|
||||
new Artist { Name = "Portishead" },
|
||||
new Artist { Name = "Prince" },
|
||||
new Artist { Name = "Projected" },
|
||||
new Artist { Name = "PSY" },
|
||||
new Artist { Name = "Public Enemy" },
|
||||
new Artist { Name = "Queen" },
|
||||
new Artist { Name = "Queensrÿche" },
|
||||
new Artist { Name = "R.E.M." },
|
||||
new Artist { Name = "Radiohead" },
|
||||
new Artist { Name = "Rancid" },
|
||||
new Artist { Name = "Raul Seixas" },
|
||||
new Artist { Name = "Raunchy" },
|
||||
new Artist { Name = "Red Hot Chili Peppers" },
|
||||
new Artist { Name = "Rick Ross" },
|
||||
new Artist { Name = "Robert James" },
|
||||
new Artist { Name = "London Classical Players" },
|
||||
new Artist { Name = "Royal Philharmonic Orchestra" },
|
||||
new Artist { Name = "Run DMC" },
|
||||
new Artist { Name = "Rush" },
|
||||
new Artist { Name = "Santana" },
|
||||
new Artist { Name = "Sara Tavares" },
|
||||
new Artist { Name = "Sarah Brightman" },
|
||||
new Artist { Name = "Sasha" },
|
||||
new Artist { Name = "Scholars Baroque Ensemble" },
|
||||
new Artist { Name = "Scorpions" },
|
||||
new Artist { Name = "Sergei Prokofiev & Yuri Temirkanov" },
|
||||
new Artist { Name = "Sheryl Crow" },
|
||||
new Artist { Name = "Sir Georg Solti & Wiener Philharmoniker" },
|
||||
new Artist { Name = "Skank" },
|
||||
new Artist { Name = "Skrillex" },
|
||||
new Artist { Name = "Slash" },
|
||||
new Artist { Name = "Slayer" },
|
||||
new Artist { Name = "Soul-Junk" },
|
||||
new Artist { Name = "Soundgarden" },
|
||||
new Artist { Name = "Spyro Gyra" },
|
||||
new Artist { Name = "Stevie Ray Vaughan & Double Trouble" },
|
||||
new Artist { Name = "Stevie Ray Vaughan" },
|
||||
new Artist { Name = "Sting" },
|
||||
new Artist { Name = "Stone Temple Pilots" },
|
||||
new Artist { Name = "Styx" },
|
||||
new Artist { Name = "Sufjan Stevens" },
|
||||
new Artist { Name = "Supreme Beings of Leisure" },
|
||||
new Artist { Name = "System Of A Down" },
|
||||
new Artist { Name = "T&N" },
|
||||
new Artist { Name = "Talking Heads" },
|
||||
new Artist { Name = "Tears For Fears" },
|
||||
new Artist { Name = "Ted Nugent" },
|
||||
new Artist { Name = "Temple of the Dog" },
|
||||
new Artist { Name = "Terry Bozzio, Tony Levin & Steve Stevens" },
|
||||
new Artist { Name = "The 12 Cellists of The Berlin Philharmonic" },
|
||||
new Artist { Name = "The Axis of Awesome" },
|
||||
new Artist { Name = "The Beatles" },
|
||||
new Artist { Name = "The Black Crowes" },
|
||||
new Artist { Name = "The Black Keys" },
|
||||
new Artist { Name = "The Carpenters" },
|
||||
new Artist { Name = "The Cat Empire" },
|
||||
new Artist { Name = "The Cult" },
|
||||
new Artist { Name = "The Cure" },
|
||||
new Artist { Name = "The Decemberists" },
|
||||
new Artist { Name = "The Doors" },
|
||||
new Artist { Name = "The Eagles of Death Metal" },
|
||||
new Artist { Name = "The Go! Team" },
|
||||
new Artist { Name = "The Head and the Heart" },
|
||||
new Artist { Name = "The Jezabels" },
|
||||
new Artist { Name = "The King's Singers" },
|
||||
new Artist { Name = "The Lumineers" },
|
||||
new Artist { Name = "The Offspring" },
|
||||
new Artist { Name = "The Police" },
|
||||
new Artist { Name = "The Posies" },
|
||||
new Artist { Name = "The Prodigy" },
|
||||
new Artist { Name = "The Rolling Stones" },
|
||||
new Artist { Name = "The Rubberbandits" },
|
||||
new Artist { Name = "The Smashing Pumpkins" },
|
||||
new Artist { Name = "The Stone Roses" },
|
||||
new Artist { Name = "The Who" },
|
||||
new Artist { Name = "Them Crooked Vultures" },
|
||||
new Artist { Name = "TheStart" },
|
||||
new Artist { Name = "Thievery Corporation" },
|
||||
new Artist { Name = "Tiësto" },
|
||||
new Artist { Name = "Tim Maia" },
|
||||
new Artist { Name = "Ton Koopman" },
|
||||
new Artist { Name = "Tool" },
|
||||
new Artist { Name = "Tori Amos" },
|
||||
new Artist { Name = "Trampled By Turtles" },
|
||||
new Artist { Name = "Trans-Siberian Orchestra" },
|
||||
new Artist { Name = "Tygers of Pan Tang" },
|
||||
new Artist { Name = "U2" },
|
||||
new Artist { Name = "UB40" },
|
||||
new Artist { Name = "Uh Huh Her " },
|
||||
new Artist { Name = "Van Halen" },
|
||||
new Artist { Name = "Various Artists" },
|
||||
new Artist { Name = "Velvet Revolver" },
|
||||
new Artist { Name = "Venus Hum" },
|
||||
new Artist { Name = "Vicente Fernandez" },
|
||||
new Artist { Name = "Vinícius De Moraes" },
|
||||
new Artist { Name = "Weezer" },
|
||||
new Artist { Name = "Weird Al" },
|
||||
new Artist { Name = "Wendy Carlos" },
|
||||
new Artist { Name = "Wilhelm Kempff" },
|
||||
new Artist { Name = "Yano" },
|
||||
new Artist { Name = "Yehudi Menuhin" },
|
||||
new Artist { Name = "Yes" },
|
||||
new Artist { Name = "Yo-Yo Ma" },
|
||||
new Artist { Name = "Zeca Pagodinho" },
|
||||
new Artist { Name = "אריק אינשטיין"}
|
||||
};
|
||||
|
||||
// TODO [EF] Swap to store generated keys when available
|
||||
int artistId = 1;
|
||||
artists = new Dictionary<string, Artist>();
|
||||
foreach (Artist artist in artistsList)
|
||||
{
|
||||
artist.ArtistId = artistId++;
|
||||
artists.Add(artist.Name, artist);
|
||||
}
|
||||
}
|
||||
|
||||
return artists;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, Genre> genres;
|
||||
public static Dictionary<string, Genre> Genres
|
||||
{
|
||||
get
|
||||
{
|
||||
if (genres == null)
|
||||
{
|
||||
var genresList = new Genre[]
|
||||
{
|
||||
new Genre { Name = "Pop" },
|
||||
new Genre { Name = "Rock" },
|
||||
new Genre { Name = "Jazz" },
|
||||
new Genre { Name = "Metal" },
|
||||
new Genre { Name = "Electronic" },
|
||||
new Genre { Name = "Blues" },
|
||||
new Genre { Name = "Latin" },
|
||||
new Genre { Name = "Rap" },
|
||||
new Genre { Name = "Classical" },
|
||||
new Genre { Name = "Alternative" },
|
||||
new Genre { Name = "Country" },
|
||||
new Genre { Name = "R&B" },
|
||||
new Genre { Name = "Indie" },
|
||||
new Genre { Name = "Punk" },
|
||||
new Genre { Name = "World" }
|
||||
};
|
||||
|
||||
genres = new Dictionary<string, Genre>();
|
||||
// TODO [EF] Swap to store generated keys when available
|
||||
int genreId = 1;
|
||||
foreach (Genre genre in genresList)
|
||||
{
|
||||
genre.GenreId = genreId++;
|
||||
|
||||
// TODO [EF] Remove when null values are supported by update pipeline
|
||||
genre.Description = genre.Name + " is great music (if you like it).";
|
||||
|
||||
genres.Add(genre.Name, genre);
|
||||
}
|
||||
}
|
||||
|
||||
return genres;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Data.Entity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public partial class ShoppingCart
|
||||
{
|
||||
MusicStoreContext _db;
|
||||
string ShoppingCartId { get; set; }
|
||||
|
||||
public ShoppingCart(MusicStoreContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context)
|
||||
{
|
||||
var cart = new ShoppingCart(db);
|
||||
cart.ShoppingCartId = cart.GetCartId(context);
|
||||
return cart;
|
||||
}
|
||||
|
||||
public void AddToCart(Album album)
|
||||
{
|
||||
// Get the matching cart and album instances
|
||||
var cartItem = _db.CartItems.SingleOrDefault(
|
||||
c => c.CartId == ShoppingCartId
|
||||
&& c.AlbumId == album.AlbumId);
|
||||
|
||||
if (cartItem == null)
|
||||
{
|
||||
// TODO [EF] Swap to store generated key once we support identity pattern
|
||||
var nextCartItemId = _db.CartItems.Any()
|
||||
? _db.CartItems.Max(c => c.CartItemId) + 1
|
||||
: 1;
|
||||
|
||||
// Create a new cart item if no cart item exists
|
||||
cartItem = new CartItem
|
||||
{
|
||||
CartItemId = nextCartItemId,
|
||||
AlbumId = album.AlbumId,
|
||||
CartId = ShoppingCartId,
|
||||
Count = 1,
|
||||
DateCreated = DateTime.Now
|
||||
};
|
||||
|
||||
_db.CartItems.Add(cartItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the item does exist in the cart, then add one to the quantity
|
||||
cartItem.Count++;
|
||||
|
||||
// TODO [EF] Remove this line once change detection is available
|
||||
_db.Update(cartItem);
|
||||
}
|
||||
}
|
||||
|
||||
public int RemoveFromCart(int id)
|
||||
{
|
||||
// Get the cart
|
||||
var cartItem = _db.CartItems.Single(
|
||||
cart => cart.CartId == ShoppingCartId
|
||||
&& cart.CartItemId == id);
|
||||
|
||||
int itemCount = 0;
|
||||
|
||||
if (cartItem != null)
|
||||
{
|
||||
if (cartItem.Count > 1)
|
||||
{
|
||||
cartItem.Count--;
|
||||
|
||||
// TODO [EF] Remove this line once change detection is available
|
||||
_db.Update(cartItem);
|
||||
|
||||
itemCount = cartItem.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_db.CartItems.Remove(cartItem);
|
||||
}
|
||||
}
|
||||
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
public void EmptyCart()
|
||||
{
|
||||
var cartItems = _db.CartItems.Where(cart => cart.CartId == ShoppingCartId);
|
||||
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
_db.Remove(cartItem);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CartItem> GetCartItems()
|
||||
{
|
||||
var cartItems = _db.CartItems.Where(cart => cart.CartId == ShoppingCartId).ToList();
|
||||
//TODO: Auto population of the related album data not available until EF feature is lighted up.
|
||||
foreach (var cartItem in cartItems)
|
||||
{
|
||||
cartItem.Album = _db.Albums.Single(a => a.AlbumId == cartItem.AlbumId);
|
||||
}
|
||||
|
||||
return cartItems;
|
||||
}
|
||||
|
||||
public int GetCount()
|
||||
{
|
||||
// Get the count of each item in the cart and sum them up
|
||||
int? count = (from cartItems in _db.CartItems
|
||||
where cartItems.CartId == ShoppingCartId
|
||||
select (int?)cartItems.Count).Sum();
|
||||
|
||||
// Return 0 if all entries are null
|
||||
return count ?? 0;
|
||||
}
|
||||
|
||||
public decimal GetTotal()
|
||||
{
|
||||
// Multiply album price by count of that album to get
|
||||
// the current price for each of those albums in the cart
|
||||
// sum all album price totals to get the cart total
|
||||
|
||||
// TODO Collapse to a single query once EF supports querying related data
|
||||
decimal total = 0;
|
||||
foreach (var item in _db.CartItems.Where(c => c.CartId == ShoppingCartId))
|
||||
{
|
||||
var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId);
|
||||
total += item.Count * album.Price;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public int CreateOrder(Order order)
|
||||
{
|
||||
decimal orderTotal = 0;
|
||||
|
||||
var cartItems = GetCartItems();
|
||||
|
||||
// TODO [EF] Swap to store generated identity key when supported
|
||||
var nextId = _db.OrderDetails.Any()
|
||||
? _db.OrderDetails.Max(o => o.OrderDetailId) + 1
|
||||
: 1;
|
||||
|
||||
// Iterate over the items in the cart, adding the order details for each
|
||||
foreach (var item in cartItems)
|
||||
{
|
||||
//var album = _db.Albums.Find(item.AlbumId);
|
||||
var album = _db.Albums.Single(a => a.AlbumId == item.AlbumId);
|
||||
|
||||
var orderDetail = new OrderDetail
|
||||
{
|
||||
OrderDetailId = nextId,
|
||||
AlbumId = item.AlbumId,
|
||||
OrderId = order.OrderId,
|
||||
UnitPrice = album.Price,
|
||||
Quantity = item.Count,
|
||||
};
|
||||
|
||||
// Set the order total of the shopping cart
|
||||
orderTotal += (item.Count * album.Price);
|
||||
|
||||
_db.OrderDetails.Add(orderDetail);
|
||||
|
||||
nextId++;
|
||||
}
|
||||
|
||||
// Set the order's total to the orderTotal count
|
||||
order.Total = orderTotal;
|
||||
|
||||
// Empty the shopping cart
|
||||
EmptyCart();
|
||||
|
||||
// Return the OrderId as the confirmation number
|
||||
return order.OrderId;
|
||||
}
|
||||
|
||||
// We're using HttpContextBase to allow access to cookies.
|
||||
public string GetCartId(HttpContext context)
|
||||
{
|
||||
var sessionCookie = context.Request.Cookies["Session"];
|
||||
string cartId = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(sessionCookie))
|
||||
{
|
||||
//A GUID to hold the cartId.
|
||||
cartId = Guid.NewGuid().ToString();
|
||||
|
||||
// Send cart Id as a cookie to the client.
|
||||
context.Response.Cookies.Append("Session", cartId);
|
||||
}
|
||||
else
|
||||
{
|
||||
cartId = sessionCookie;
|
||||
}
|
||||
|
||||
return cartId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>93891170-a8d5-46fd-a291-40f90cf258c2</ProjectGuid>
|
||||
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
|
||||
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<DevelopmentServerPort>1575</DevelopmentServerPort>
|
||||
<GruntGulpQueried>True</GruntGulpQueried>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
@model IEnumerable<MusicStore.Models.Album>
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Store Manager";
|
||||
ViewBag.ngApp = "MusicStore.Admin";
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
<h1>Store Manager</h1>
|
||||
|
||||
<div class="ng-view"></div>
|
||||
|
||||
@*@Html.InlineData("Lookup", "ArtistsApi")*@
|
||||
@*@Html.InlineData("Lookup", "GenresApi")*@
|
||||
|
||||
@section Scripts {
|
||||
|
||||
<script src="~/lib/angular/angular.js"></script>
|
||||
<script src="~/lib/angular-route/angular-route.js"></script>
|
||||
<script src="~/lib/angular-bootstrap/ui-bootstrap-tpls.js"></script>
|
||||
@* TODO: This is currently all the compiled TypeScript, non-minified. Need to explore options
|
||||
for alternate loading schemes, e.g. AMD loader of individual modules, min vs. non-min, etc. *@
|
||||
<script src="~/js/@(ViewBag.ngApp).js"></script>
|
||||
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Home Page";
|
||||
ViewBag.ngApp = "MusicStore.Store";
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
||||
@section NavBarItems {
|
||||
|
||||
<li app-genre-menu></li>
|
||||
@*@Html.InlineData("GenreMenuList", "GenresApi")*@
|
||||
|
||||
}
|
||||
|
||||
<div ng-view></div>
|
||||
|
||||
@*@Html.InlineData("MostPopular", "AlbumsApi")*@
|
||||
|
||||
@section Scripts {
|
||||
|
||||
<script src="~/lib/angular/angular.js"></script>
|
||||
<script src="~/lib/angular-route/angular-route.js"></script>
|
||||
@* TODO: This is currently all the compiled TypeScript, non-minified. Need to explore options
|
||||
for alternate loading schemes, e.g. AMD loader of individual modules, min vs. non-min, etc. *@
|
||||
<script src="~/js/@(ViewBag.ngApp).js"></script>
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Spa
|
||||
{
|
||||
public class SiteSettings
|
||||
{
|
||||
public string DefaultAdminUsername { get; set; }
|
||||
public string DefaultAdminPassword { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
using AutoMapper;
|
||||
using Microsoft.AspNet.Authorization;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Identity.EntityFramework;
|
||||
using Microsoft.Data.Entity;
|
||||
using Microsoft.Framework.Configuration;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Dnx.Runtime;
|
||||
using MusicStore.Apis;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Spa
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IApplicationEnvironment env)
|
||||
{
|
||||
var builder = new ConfigurationBuilder(env.ApplicationBasePath)
|
||||
.AddJsonFile("Config.json")
|
||||
.AddEnvironmentVariables();
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public Microsoft.Framework.Configuration.IConfiguration Configuration { get; set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<SiteSettings>(settings =>
|
||||
{
|
||||
settings.DefaultAdminUsername = Configuration["DefaultAdminUsername"];
|
||||
settings.DefaultAdminPassword = Configuration["DefaultAdminPassword"];
|
||||
});
|
||||
|
||||
// Add MVC services to the service container
|
||||
services.AddMvc();
|
||||
|
||||
// Add EF services to the service container
|
||||
services.AddEntityFramework()
|
||||
.AddSqlServer()
|
||||
.AddDbContext<MusicStoreContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]);
|
||||
});
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<MusicStoreContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
// Add application services to the service container
|
||||
//services.AddTransient<IModelMetadataProvider, BuddyModelMetadataProvider>();
|
||||
|
||||
// Configure Auth
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
options.AddPolicy("app-ManageStore", new AuthorizationPolicyBuilder().RequireClaim("app-ManageStore", "Allowed").Build());
|
||||
});
|
||||
|
||||
Mapper.CreateMap<AlbumChangeDto, Album>();
|
||||
Mapper.CreateMap<Album, AlbumChangeDto>();
|
||||
Mapper.CreateMap<Album, AlbumResultDto>();
|
||||
Mapper.CreateMap<AlbumResultDto, Album>();
|
||||
Mapper.CreateMap<Artist, ArtistResultDto>();
|
||||
Mapper.CreateMap<ArtistResultDto, Artist>();
|
||||
Mapper.CreateMap<Genre, GenreResultDto>();
|
||||
Mapper.CreateMap<GenreResultDto, Genre>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// Initialize the sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
|
||||
|
||||
// Configure the HTTP request pipeline
|
||||
|
||||
// Add cookie auth
|
||||
app.UseIdentity();
|
||||
|
||||
// Add static files
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add MVC
|
||||
app.UseMvc();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
@model MusicStore.Models.LoginViewModel
|
||||
|
||||
@{
|
||||
//TODO: Until we have a way to specify the layout page at application level.
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
ViewBag.Title = "Log in";
|
||||
ViewBag.ngApp = "MusicStore.Store";
|
||||
}
|
||||
|
||||
@section NavBarItems {
|
||||
|
||||
<li app-genre-menu></li>
|
||||
@*@Html.InlineData("GenreMenuList", "GenresApi")*@
|
||||
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<section id="loginForm">
|
||||
@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post,
|
||||
new {
|
||||
@class = "form-horizontal",
|
||||
role = "form",
|
||||
novalidate = "",
|
||||
name = "login",
|
||||
app_prevent_submit = "login.$invalid",
|
||||
ng_submit = "login.submitAttempted=true"
|
||||
}))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<h4>Use a local account to log in.</h4>
|
||||
<hr />
|
||||
@Html.ValidationSummary(true)
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.UserName, formName: "login", className: "has-error")">
|
||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@Html.ngTextBoxFor(m => m.UserName, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.UserName, "login", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@* What this might look like using Tag Helpers:
|
||||
<@div class="form-group" validation-for="UserName" validation-form-name="login" validation-class="has-error">
|
||||
<@label for="UserName" class="col-md-2 control-label"></@label>
|
||||
<div class="col-md-10">
|
||||
<@input for="UserName" class="form-control" />
|
||||
<@span validation-for="UserName" validation-form-name="login" class="field-validation-error"></@span>
|
||||
</div>
|
||||
</@div>
|
||||
*@
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.Password, formName: "login", className: "has-error")">
|
||||
@Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@Html.ngPasswordFor(m => m.Password, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.Password, "login", new { @class = "help-block field-validation-error" })
|
||||
</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">
|
||||
<button type="submit" class="btn btn-default">Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
@Html.ActionLink("Register", "Register") if you don't have a local account.
|
||||
</p>
|
||||
}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@*TODO : Until script helpers are available, adding script references manually*@
|
||||
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||
@*<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>*@
|
||||
<script src="~/lib/angular/angular.js"></script>
|
||||
<script src="~/lib/angular-route/angular-route.js"></script>
|
||||
@* TODO: This is currently all the compiled TypeScript, non-minified. Need to explore options
|
||||
for alternate loading schemes, e.g. AMD loader of individual modules, min vs. non-min, etc. *@
|
||||
<script src="~/js/MusicStore.Store.js"></script>
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
@{
|
||||
//TODO: Until we have a way to specify the layout page at application level.
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
ViewBag.Title = "Manage Account";
|
||||
ViewBag.ngApp = "MusicStore.Store";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<p class="text-success">@ViewBag.StatusMessage</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
@await Html.PartialAsync("_ChangePasswordPartial")
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script src="~/js/angular.js"></script>
|
||||
<script src="~/js/angular-route.js"></script>
|
||||
@* TODO: This is currently all the compiled TypeScript, non-minified. Need to explore options
|
||||
for alternate loading schemes, e.g. AMD loader of individual modules, min vs. non-min, etc. *@
|
||||
<script src="~/js/MusicStore.Store.js"></script>
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
@model MusicStore.Models.RegisterViewModel
|
||||
@{
|
||||
//TODO: Until we have a way to specify the layout page at application level.
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
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()
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
@Html.TextBoxFor(m => m.UserName, 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 {
|
||||
@*TODO : Until script helpers are available, adding script references manually*@
|
||||
@*@Scripts.Render("~/bundles/jqueryval")*@
|
||||
<script src="@Url.Content("~/Scripts/jquery.validate.js")"></script>
|
||||
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")"></script>
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
@using System.Security.Claims
|
||||
|
||||
@model MusicStore.Models.ManageUserViewModel
|
||||
|
||||
<p>You're logged in as <strong>@User.GetUserName()</strong>.</p>
|
||||
|
||||
@using (Html.BeginForm("Manage", "Account", FormMethod.Post,
|
||||
new { @class = "form-horizontal",
|
||||
role = "form", novalidate = "", name = "changePassword",
|
||||
app_prevent_submit = "changePassword.$invalid",
|
||||
ng_submit = "changePassword.submitAttempted=true" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
<h4>Change Password</h4>
|
||||
<hr />
|
||||
@Html.ValidationSummary()
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.OldPassword, formName: "changePassword", className: "has-error")">
|
||||
@Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@Html.ngPasswordFor(m => m.OldPassword, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.OldPassword, "changePassword", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.NewPassword, formName: "changePassword", className: "has-error")">
|
||||
@Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@Html.ngPasswordFor(m => m.NewPassword, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.NewPassword, "changePassword", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.ConfirmPassword, formName: "changePassword", className: "has-error")">
|
||||
@Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
@Html.ngPasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.ConfirmPassword, "changePassword", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-primary">Change password</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html ng-app="@ViewBag.ngApp" data-url-base="@Url.Content("~/")">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>@ViewBag.Title – MVC Music Store</title>
|
||||
<link rel="stylesheet" href="~/css/site.css" />
|
||||
<script src="~/lib/respond/respond.src.js"></script>
|
||||
<script src="~/lib/modernizr/modernizr.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-inverse navbar-fixed-top">
|
||||
<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>
|
||||
@Html.ActionLink("ASP.NET MVC Music Store", "Home", "Page", null, new { @class = "navbar-brand" })
|
||||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>@Html.ActionLink("Home", "Home", "Page")</li>
|
||||
|
||||
@RenderSection("NavBarItems", required: false)
|
||||
|
||||
@*@Html.Action("CartSummary", "ShoppingCart")*@
|
||||
</ul>
|
||||
@(await Html.PartialAsync("_LoginPartial"))
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container body-content">
|
||||
@RenderBody()
|
||||
<hr />
|
||||
<footer class="navbar navbar-fixed-bottom navbar-default text-center">
|
||||
<p><a href="https://github.com/aspnet/musicstore">github.com/aspnet/musicstore</a></p>
|
||||
<small>@Html.ActionLink("admin", "Admin", "Page")</small>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
@* TODO: Need to figure out best way to switch these to min links for release, e.g. new helper,
|
||||
Grunt task to replace, CDN support, etc. *@
|
||||
<script src="~/lib/jquery/jquery.js"></script>
|
||||
@*<script src="~/lib/jquery.validation/jquery.validate.js"></script>*@
|
||||
<script src="~/lib/bootstrap/js/bootstrap.js"></script>
|
||||
|
||||
@RenderSection("scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
@using System.Security.Principal
|
||||
@using System.Security.Claims
|
||||
@using Microsoft.AspNet.Identity
|
||||
|
||||
@{
|
||||
//Func<string, HtmlString> js = input => Html.Raw(HttpUtility.JavaScriptStringEncode(input, false));
|
||||
}
|
||||
|
||||
@if (User.IsSignedIn())
|
||||
{
|
||||
using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
@Html.ActionLink("Hello " + User.GetUserName() + "!", "Manage", "Account", routeValues: null, htmlAttributes: new { title = "Manage" })
|
||||
</li>
|
||||
<li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
|
||||
</ul>
|
||||
|
||||
@Html.Json(new {
|
||||
isAuthenticated = true,
|
||||
userName = User.GetUserName(),
|
||||
userId = User.GetUserId(),
|
||||
roles = ((ClaimsPrincipal)User).Claims
|
||||
.Where(c => c.Type == ClaimTypes.Role)
|
||||
.Select(role => role.Value),
|
||||
claims = ((ClaimsPrincipal)User).Claims
|
||||
.Where(c => c.Type.StartsWith("app-", StringComparison.OrdinalIgnoreCase))
|
||||
.Select(role => new { role.Type, role.Value })
|
||||
},
|
||||
new { id = "userDetails" })
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
|
||||
<li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink", app_login_link = "" })</li>
|
||||
</ul>
|
||||
|
||||
@Html.Json(new {
|
||||
isAuthenticated = false,
|
||||
userName = (string)null,
|
||||
userId = (string)null,
|
||||
roles = Enumerable.Empty<string>()
|
||||
},
|
||||
new { id = "userDetails" })
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"name": "MvcMusicStore",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"bootstrap": "~3.2.0",
|
||||
"jquery-validation": "~1.13.1",
|
||||
"jquery": "~2.1.1",
|
||||
"modernizr": "~2.7.1",
|
||||
"respond": "~1.4.2",
|
||||
"angular": "~1.3.3",
|
||||
"angular-route": "~1.3.3",
|
||||
"angular-bootstrap": "~0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dt-angular": "~1.2.16",
|
||||
"dt-angular-ui-bootstrap": "*",
|
||||
"dt-jquery": "~2.0.0"
|
||||
},
|
||||
"exportsOverride": {
|
||||
"bootstrap": {
|
||||
"js": "dist/js/*.*",
|
||||
"css": "dist/css/*.*",
|
||||
"fonts": "dist/fonts/*.*"
|
||||
},
|
||||
"/jquery/": {
|
||||
"": "dist/*.*"
|
||||
},
|
||||
"/angular/": {
|
||||
"": "*.{js,map}"
|
||||
},
|
||||
"modernizr": {
|
||||
"": "modernizr.js"
|
||||
},
|
||||
"respond": {
|
||||
"": "dest/*.*"
|
||||
},
|
||||
"dt-*": {}
|
||||
},
|
||||
"resolutions": {
|
||||
"angular": "~1.3.3"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
using System;
|
||||
using Microsoft.AspNet.Mvc.Razor.Precompilation;
|
||||
using Microsoft.Dnx.Runtime;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
public class RazorPreCompilation : RazorPreCompileModule
|
||||
{
|
||||
public RazorPreCompilation(IApplicationEnvironment applicationEnvironment)
|
||||
{
|
||||
GenerateSymbols = string.Equals(applicationEnvironment.Configuration,
|
||||
"debug",
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"options": { "force": true },
|
||||
"assets": [ "wwwroot/ng-apps" ],
|
||||
"tsng": [ "ng-apps/**/*.ng.ts" ]
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"assets": {
|
||||
"files": [
|
||||
{
|
||||
"expand": true,
|
||||
"cwd": "ng-apps/",
|
||||
"src": [
|
||||
"<%= staticFilePattern %>"
|
||||
],
|
||||
"dest": "wwwroot/ng-apps",
|
||||
"options": { "force": true }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"dev": {
|
||||
"options": {
|
||||
"cleancss": false
|
||||
},
|
||||
"files": {
|
||||
"wwwroot/css/site.css": "wwwroot/**/*.less"
|
||||
}
|
||||
},
|
||||
"release": {
|
||||
"options": {
|
||||
"cleancss": true
|
||||
},
|
||||
"files": {
|
||||
"wwwroot/css/site.css": "wwwroot/**/*.less"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
{
|
||||
"options": {
|
||||
"configuration": {
|
||||
"rules": {
|
||||
"class-name": true,
|
||||
"curly": true,
|
||||
"eofline": false,
|
||||
"forin": true,
|
||||
"indent": [ true, 4 ],
|
||||
"label-position": true,
|
||||
"label-undefined": true,
|
||||
"max-line-length": [ true, 140 ],
|
||||
"no-arg": true,
|
||||
"no-bitwise": true,
|
||||
"no-console": [
|
||||
true,
|
||||
"debug",
|
||||
"info",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"trace"
|
||||
],
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-key": true,
|
||||
"no-duplicate-variable": true,
|
||||
"no-empty": true,
|
||||
"no-eval": true,
|
||||
"no-string-literal": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unreachable": true,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-whitespace"
|
||||
],
|
||||
"quotemark": [ true, "double" ],
|
||||
"radix": true,
|
||||
"semicolon": true,
|
||||
"triple-equals": [ true, "allow-null-check" ],
|
||||
"variable-name": false,
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"src": [ "ng-apps/**/*.ts", "!**/*.ng.ts" ]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"options": {
|
||||
"extension": ".ng.ts"
|
||||
},
|
||||
"dev": {
|
||||
"files": [
|
||||
{
|
||||
"src": [ "ng-apps/components/**/*.ts", "ng-apps/MusicStore.Store/**/*.ts", "!**/*.ng.ts" ],
|
||||
"dest": "ng-apps"
|
||||
},
|
||||
{
|
||||
"src": ["ng-apps/components/**/*.ts", "ng-apps/MusicStore.Admin/**/*.ts", "!**/*.ng.ts"],
|
||||
"dest": "ng-apps"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"options": {
|
||||
"module": "amd",
|
||||
"target": "es5",
|
||||
"sourcemap": false
|
||||
},
|
||||
"dev": {
|
||||
"files": [
|
||||
{
|
||||
"src": ["ng-apps/components/**/*.ng.ts", "ng-apps/MusicStore.Store/**/*.ng.ts"],
|
||||
"dest": "wwwroot/js/MusicStore.Store.js"
|
||||
},
|
||||
{
|
||||
"src": ["ng-apps/components/**/*.ng.ts", "ng-apps/MusicStore.Admin/**/*.ng.ts"],
|
||||
"dest": "wwwroot/js/MusicStore.Admin.js"
|
||||
}
|
||||
]
|
||||
},
|
||||
"release": {
|
||||
"options": {
|
||||
"sourcemap": true
|
||||
},
|
||||
"files": "<%= typescript.dev.files %>"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"options": {
|
||||
"banner": "/*! <%= pkg.name %> <%= grunt.template.today('dd-mm-yyyy') %> */\n"
|
||||
},
|
||||
"release": {
|
||||
"files": {
|
||||
"wwwroot/app.min.js": ["<%= typescript.dev.dest %>"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"typescript": {
|
||||
"files": ["ng-apps/**/*.ts", "!**/*.ng.ts"],
|
||||
"tasks": ["ts"]
|
||||
},
|
||||
"bower": {
|
||||
"files": ["bower_components/**/*.*"],
|
||||
"tasks": ["bower:install"]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
<div class="modal-header">
|
||||
<h3>Really delete this album?</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="text-center">
|
||||
<p>
|
||||
<img ng-src="{{ viewModel.album.AlbumArtUrl }}" ng-show="viewModel.album.AlbumArtUrl" alt="Album art" />
|
||||
</p>
|
||||
<p>
|
||||
<strong>{{ viewModel.album.Title }}</strong><br />
|
||||
<em>by {{ viewModel.album.Artist.Name }}</em>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-danger" ng-click="viewModel.ok()">
|
||||
Delete Album
|
||||
</button>
|
||||
<button type="button" class="btn btn-default" ng-click="viewModel.cancel()">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
module MusicStore.Admin.Catalog {
|
||||
export interface IAlbumDeleteModalViewModel {
|
||||
album: Models.IAlbum;
|
||||
ok();
|
||||
cancel();
|
||||
}
|
||||
|
||||
// We don't register this controller with Angular's DI system because the $modal service
|
||||
// will create and resolve its dependencies directly
|
||||
|
||||
//@NgController(skip=true)
|
||||
export class AlbumDeleteModalController implements IAlbumDeleteModalViewModel {
|
||||
private _modalInstance: ng.ui.bootstrap.IModalServiceInstance;
|
||||
|
||||
constructor($modalInstance: ng.ui.bootstrap.IModalServiceInstance, album: Models.IAlbum) {
|
||||
this._modalInstance = $modalInstance;
|
||||
this.album = album;
|
||||
}
|
||||
|
||||
public album: Models.IAlbum;
|
||||
|
||||
public ok() {
|
||||
this._modalInstance.close(true);
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._modalInstance.dismiss("cancel");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
<div ng-controller="MusicStore.Admin.Catalog.AlbumDetailsController as viewModel">
|
||||
<h2>Album <small>Details</small></h2>
|
||||
<hr />
|
||||
|
||||
<form class="form-horizontal" role="form">
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.Artist, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ viewModel.album.Artist.Name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.Genre, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ viewModel.album.Genre.Name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.Title, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ viewModel.album.Title }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.Price, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ viewModel.album.Price | currency }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(m => m.AlbumArtUrl, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static">{{ viewModel.album.AlbumArtUrl }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="viewModel.album.AlbumArtUrl">
|
||||
<label class="col-md-2 control-label">Album Art</label>
|
||||
<div class="col-md-10">
|
||||
<p class="form-control-static"><img ng-src="{{ viewModel.album.AlbumArtUrl }}" alt="Album Art" /></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<a class="btn btn-primary" ng-href="#/albums/{{ viewModel.album.AlbumId }}/edit">Edit</a>
|
||||
<button type="button" class="btn btn-danger" ng-click="viewModel.deleteAlbum()">Delete</button>
|
||||
<a class="btn btn-default" ng-href="#/albums">Back to List</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
module MusicStore.Admin.Catalog {
|
||||
interface IAlbumDetailsRouteParams extends ng.route.IRouteParamsService {
|
||||
albumId: number;
|
||||
}
|
||||
|
||||
interface IAlbumDetailsViewModel {
|
||||
album: Models.IAlbum;
|
||||
deleteAlbum();
|
||||
}
|
||||
|
||||
class AlbumDetailsController implements IAlbumDetailsViewModel {
|
||||
private _modal: ng.ui.bootstrap.IModalService;
|
||||
private _location: ng.ILocationService;
|
||||
private _albumApi: AlbumApi.IAlbumApiService;
|
||||
private _viewAlert: ViewAlert.IViewAlertService;
|
||||
|
||||
constructor($routeParams: IAlbumDetailsRouteParams,
|
||||
$modal: ng.ui.bootstrap.IModalService,
|
||||
$location: ng.ILocationService,
|
||||
albumApi: AlbumApi.IAlbumApiService,
|
||||
viewAlert: ViewAlert.IViewAlertService) {
|
||||
|
||||
this._modal = $modal;
|
||||
this._location = $location;
|
||||
this._albumApi = albumApi;
|
||||
this._viewAlert = viewAlert;
|
||||
|
||||
albumApi.getAlbumDetails($routeParams.albumId).then(album => this.album = album);
|
||||
}
|
||||
|
||||
public album: Models.IAlbum;
|
||||
|
||||
public deleteAlbum() {
|
||||
var deleteModal = this._modal.open({
|
||||
templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumDeleteModal.cshtml",
|
||||
controller: "MusicStore.Admin.Catalog.AlbumDeleteModalController as viewModel",
|
||||
resolve: {
|
||||
album: () => this.album
|
||||
}
|
||||
});
|
||||
|
||||
deleteModal.result.then(shouldDelete => {
|
||||
if (!shouldDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._albumApi.deleteAlbum(this.album.AlbumId).then(result => {
|
||||
// Navigate back to the list
|
||||
this._viewAlert.alert = {
|
||||
type: Models.AlertType.success,
|
||||
message: result.data.Message
|
||||
};
|
||||
this._location.path("/albums").replace();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
<div ng-controller="MusicStore.Admin.Catalog.AlbumEditController as viewModel">
|
||||
<h2>Album <small>{{ viewModel.mode | titlecase }}</small></h2>
|
||||
<hr />
|
||||
|
||||
<alert ng-show="viewModel.alert" type="{{ viewModel.alert.type.value }}" close="viewModel.clearAlert()">
|
||||
{{ viewModel.alert.message }}
|
||||
<ul ng-show="viewModel.alert.modelErrors">
|
||||
<li ng-repeat="modelError in viewModel.alert.modelErrors">{{ modelError.ErrorMessage }}</li>
|
||||
</ul>
|
||||
</alert>
|
||||
|
||||
<form name="editAlbum" class="form-horizontal" ng-submit="viewModel.save()" novalidate>
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.ArtistId, formName: "editAlbum", className: "has-error")">
|
||||
@Html.LabelFor(m => m.Artist, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@Html.ngDropDownListFor(m => m.ArtistId, m => m.Artist.Name, source: "viewModel.artists", nullOption: "-- choose Artist --",
|
||||
htmlAttributes: new { @class = "form-control", ng_model = "viewModel.album.ArtistId", ng_disabled = "viewModel.disabled || viewModel.artists.length < 2" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.ArtistId, "editAlbum", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.GenreId, formName: "editAlbum", className: "has-error")">
|
||||
@Html.LabelFor(m => m.Genre, new { @class = "col-md-2 control-label" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@Html.ngDropDownListFor(m => m.GenreId, m => m.Genre.Name, source: "viewModel.genres", nullOption: "-- choose Genre --",
|
||||
htmlAttributes: new { @class = "form-control", ng_model = "viewModel.album.GenreId", ng_disabled = "viewModel.disabled || viewModel.genres.length < 2" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(m => m.GenreId, "editAlbum", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.Title, formName: "editAlbum", className: "has-error")">
|
||||
@Html.LabelFor(m => m.Title, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
@Html.ngTextBoxFor(m => m.Title, new { @class = "form-control", ng_model = "viewModel.album.Title", ng_disabled = "viewModel.disabled" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(model => model.Title, "editAlbum", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.Price, formName: "editAlbum", className: "has-error")">
|
||||
@Html.LabelFor(m => m.Price, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">$</span>
|
||||
@Html.ngTextBoxFor(m => m.Price, new { @class = "form-control", ng_model = "viewModel.album.Price", ng_disabled = "viewModel.disabled" })
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(model => model.Price, "editAlbum", new { @class = "help-block field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="@Html.ngValidationClassFor(m => m.AlbumArtUrl, formName: "editAlbum", className: "has-error")">
|
||||
@Html.LabelFor(m => m.AlbumArtUrl, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
@Html.ngTextBoxFor(m => m.AlbumArtUrl, new { @class = "form-control", ng_model = "viewModel.album.AlbumArtUrl", ng_disabled = "viewModel.disabled" })
|
||||
</div>
|
||||
</div>
|
||||
@Html.ngValidationMessageFor(model => model.AlbumArtUrl, "editAlbum", new { @class = "field-validation-error" })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="viewModel.album.AlbumArtUrl && !editAlbum.AlbumArtUrl.$invalid">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<img ng-src="{{ viewModel.album.AlbumArtUrl }}" alt="Album Art" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<button type="submit" class="btn btn-primary"
|
||||
ng-disabled="viewModel.disabled || !viewModel.album || editAlbum.$invalid">
|
||||
Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger"
|
||||
ng-click="viewModel.deleteAlbum()"
|
||||
ng-show="viewModel.mode === 'edit'">Delete</button>
|
||||
<a class="btn btn-default" ng-href="#/albums">Back to List</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
@ -1,190 +0,0 @@
|
|||
module MusicStore.Admin.Catalog {
|
||||
interface IAlbumDetailsRouteParams extends ng.route.IRouteParamsService {
|
||||
mode: string;
|
||||
albumId: number;
|
||||
}
|
||||
|
||||
interface IAlbumDetailsViewModel {
|
||||
mode: string; // edit or new
|
||||
disabled: boolean;
|
||||
album: Models.IAlbum;
|
||||
alert: Models.IAlert;
|
||||
artists: Array<Models.IArtist>;
|
||||
genres: Array<Models.IGenreLookup>;
|
||||
save();
|
||||
clearAlert();
|
||||
}
|
||||
|
||||
class AlbumEditController implements IAlbumDetailsViewModel {
|
||||
private _albumApi: AlbumApi.IAlbumApiService;
|
||||
private _artistApi: ArtistApi.IArtistApiService;
|
||||
private _genreApi: GenreApi.IGenreApiService;
|
||||
private _viewAlert: ViewAlert.IViewAlertService;
|
||||
private _modal: ng.ui.bootstrap.IModalService;
|
||||
private _location: ng.ILocationService;
|
||||
private _timeout: ng.ITimeoutService;
|
||||
private _log: ng.ILogService;
|
||||
|
||||
constructor($routeParams: IAlbumDetailsRouteParams,
|
||||
albumApi: AlbumApi.IAlbumApiService,
|
||||
artistApi: ArtistApi.IArtistApiService,
|
||||
genreApi: GenreApi.IGenreApiService,
|
||||
viewAlert: ViewAlert.IViewAlertService,
|
||||
$modal: ng.ui.bootstrap.IModalService,
|
||||
$location: ng.ILocationService,
|
||||
$timeout: ng.ITimeoutService,
|
||||
$q: ng.IQService,
|
||||
$log: ng.ILogService) {
|
||||
|
||||
this._albumApi = albumApi;
|
||||
this._artistApi = artistApi;
|
||||
this._genreApi = genreApi;
|
||||
this._viewAlert = viewAlert;
|
||||
this._modal = $modal;
|
||||
this._location = $location;
|
||||
this._timeout = $timeout;
|
||||
this._log = $log;
|
||||
|
||||
this.mode = $routeParams.mode;
|
||||
|
||||
this.alert = viewAlert.alert;
|
||||
|
||||
artistApi.getArtistsLookup().then(artists => this.artists = artists);
|
||||
genreApi.getGenresLookup().then(genres => this.genres = genres);
|
||||
|
||||
if (this.mode.toLowerCase() === "edit") {
|
||||
// TODO: Handle album load failure
|
||||
albumApi.getAlbumDetails($routeParams.albumId).then(album => {
|
||||
this.album = album;
|
||||
|
||||
// Pre-load the lookup arrays with the current values if not set yet
|
||||
this.genres = this.genres || [album.Genre];
|
||||
this.artists = this.artists || [album.Artist];
|
||||
|
||||
this.disabled = false;
|
||||
});
|
||||
} else {
|
||||
this.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public mode: string;
|
||||
|
||||
public disabled = true;
|
||||
|
||||
public album: Models.IAlbum;
|
||||
|
||||
public alert: Models.IAlert;
|
||||
|
||||
public artists: Array<Models.IArtist>;
|
||||
|
||||
public genres: Array<Models.IGenreLookup>;
|
||||
|
||||
public save() {
|
||||
this.disabled = true;
|
||||
|
||||
var apiMethod = this.mode.toLowerCase() === "edit" ? this._albumApi.updateAlbum : this._albumApi.createAlbum;
|
||||
apiMethod = apiMethod.bind(this._albumApi);
|
||||
|
||||
apiMethod(this.album).then(
|
||||
// Success
|
||||
response => {
|
||||
var alert = {
|
||||
type: Models.AlertType.success,
|
||||
message: response.data.Message
|
||||
};
|
||||
|
||||
// TODO: Do we need to destroy this timeout on controller unload?
|
||||
this._timeout(() => this.alert !== alert || this.clearAlert(), 3000);
|
||||
|
||||
if (this.mode.toLowerCase() === "new") {
|
||||
this._log.info("Created album successfully!");
|
||||
|
||||
var albumId: number = response.data.Data;
|
||||
|
||||
this._viewAlert.alert = alert;
|
||||
|
||||
// Reload the view with the new album ID
|
||||
this._location.path("/albums/" + albumId + "/edit").replace();
|
||||
|
||||
// TODO: Should we reload the data from the server?
|
||||
} else {
|
||||
this.alert = alert;
|
||||
this.disabled = false;
|
||||
this._log.info("Updated album " + this.album.AlbumId + " successfully!");
|
||||
}
|
||||
},
|
||||
// Error
|
||||
response => {
|
||||
// TODO: Make this common logic, e.g. base controller class, injected helper service, etc.
|
||||
if (response.status === 400) {
|
||||
// We made a bad request
|
||||
if (response.data && response.data.ModelErrors) {
|
||||
// The server says the update failed validation
|
||||
// TODO: Map errors back to client validators and/or summary
|
||||
this.alert = {
|
||||
type: Models.AlertType.danger,
|
||||
message: response.data.Message,
|
||||
modelErrors: response.data.ModelErrors
|
||||
};
|
||||
this.disabled = false;
|
||||
} else {
|
||||
// Some other bad request, just show the message
|
||||
this.alert = {
|
||||
type: Models.AlertType.danger,
|
||||
message: response.data.Message
|
||||
};
|
||||
}
|
||||
} else if (response.status === 404) {
|
||||
// The album wasn't found, probably deleted. Leave the form disabled and show error message.
|
||||
this.alert = {
|
||||
type: Models.AlertType.danger,
|
||||
message: response.data.Message
|
||||
};
|
||||
} else if (response.status === 401) {
|
||||
// We need to authenticate again
|
||||
// TODO: Should we just redirect to login page, show a message with a link, or something else
|
||||
this.alert = {
|
||||
type: Models.AlertType.danger,
|
||||
message: "Your session has timed out. Please log in and try again."
|
||||
};
|
||||
} else if (!response.status) {
|
||||
// Request timed out or no response from server or worse
|
||||
this._log.error("Error updating album " + this.album.AlbumId);
|
||||
this._log.error(response);
|
||||
this.alert = { type: Models.AlertType.danger, message: "An unexpected error occurred. Please try again." };
|
||||
this.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public deleteAlbum() {
|
||||
var deleteModal = this._modal.open({
|
||||
templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumDeleteModal.cshtml",
|
||||
controller: "MusicStore.Admin.Catalog.AlbumDeleteModalController as viewModel",
|
||||
resolve: {
|
||||
album: () => this.album
|
||||
}
|
||||
});
|
||||
|
||||
deleteModal.result.then(shouldDelete => {
|
||||
if (!shouldDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._albumApi.deleteAlbum(this.album.AlbumId).then(result => {
|
||||
// Navigate back to the list
|
||||
this._viewAlert.alert = {
|
||||
type: Models.AlertType.success,
|
||||
message: result.data.Message
|
||||
};
|
||||
this._location.path("/albums").replace();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public clearAlert() {
|
||||
this.alert = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
<div ng-controller="MusicStore.Admin.Catalog.AlbumListController as viewModel">
|
||||
<h2>Albums</h2>
|
||||
<p>
|
||||
<a class="btn btn-default" href="#/albums/new">Create new</a>
|
||||
</p>
|
||||
|
||||
<alert ng-show="viewModel.alert" type="{{ viewModel.alert.type.value }}" close="viewModel.clearAlert()">
|
||||
{{ viewModel.alert.message }}
|
||||
<ul ng-show="viewModel.alert.modelErrors">
|
||||
<li ng-repeat="modelError in viewModel.alert.modelErrors">{{ modelError.ErrorMessage }}</li>
|
||||
</ul>
|
||||
</alert>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<a title="Sort by this column" href="#/albums" ng-click="viewModel.sortBy('@Html.NameFor(m => m.Genre.Name)')">@Html.DisplayNameFor(m => m.Genre)</a>
|
||||
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet" title="Sorted ascending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Genre.Name)' && !viewModel.sortDescending"></span>
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet-alt" title="Sorted descending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Genre.Name)' && viewModel.sortDescending"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a title="Sort by this column" href="#/albums" ng-click="viewModel.sortBy('@Html.NameFor(m => m.Artist.Name)')">@Html.DisplayNameFor(m => m.Artist)</a>
|
||||
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet" title="Sorted ascending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Artist.Name)' && !viewModel.sortDescending"></span>
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet-alt" title="Sorted descending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Artist.Name)' && viewModel.sortDescending"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a title="Sort by this column" href="#/albums" ng-click="viewModel.sortBy('@Html.NameFor(m => m.Title)')">@Html.DisplayNameFor(m => m.Title)</a>
|
||||
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet" title="Sorted ascending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Title)' && !viewModel.sortDescending"></span>
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet-alt" title="Sorted descending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Title)' && viewModel.sortDescending"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a title="Sort by this column" href="#/albums" ng-click="viewModel.sortBy('@Html.NameFor(m => m.Price)')">@Html.DisplayNameFor(m => m.Price)</a>
|
||||
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet" title="Sorted ascending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Price)' && !viewModel.sortDescending"></span>
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet-alt" title="Sorted descending" ng-show="viewModel.sortColumn == '@Html.NameFor(m => m.Price)' && viewModel.sortDescending"></span>
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="album in viewModel.albums">
|
||||
<td>
|
||||
{{ album.Genre.Name }}
|
||||
</td>
|
||||
<td>
|
||||
{{ album.Artist.Name | truncate:25 }}
|
||||
</td>
|
||||
<td>
|
||||
{{ album.Title | truncate:25 }}
|
||||
</td>
|
||||
<td>
|
||||
{{ album.Price | currency }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a ng-href="#/albums/{{ album.AlbumId }}/details" class="btn btn-default">Details</a>
|
||||
<a ng-href="#/albums/{{ album.AlbumId }}/edit" class="btn btn-default">Edit</a>
|
||||
<a ng-click="viewModel.deleteAlbum(album)" class="btn btn-default">Delete</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<pagination class="pagination-sm" ng-model="viewModel.currentPage" ng-change="viewModel.loadPage()"
|
||||
items-per-page="viewModel.pageSize"
|
||||
total-items="viewModel.totalCount" max-size="6"
|
||||
rotate="false" boundary-links="true"></pagination>
|
||||
<p>
|
||||
{{ viewModel.totalCount }} total albums
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
module MusicStore.Admin.Catalog {
|
||||
interface IAlbumListViewModel {
|
||||
albums: Array<Models.IAlbum>;
|
||||
totalCount: number;
|
||||
currentPage: number;
|
||||
pageSize: number;
|
||||
loadPage(page?: number);
|
||||
deleteAlbum(album: Models.IAlbum);
|
||||
clearAlert();
|
||||
}
|
||||
|
||||
class AlbumListController implements IAlbumListViewModel {
|
||||
private _albumApi: AlbumApi.IAlbumApiService;
|
||||
private _modal: ng.ui.bootstrap.IModalService;
|
||||
private _timeout: ng.ITimeoutService;
|
||||
private _log: ng.ILogService;
|
||||
|
||||
constructor(albumApi: AlbumApi.IAlbumApiService,
|
||||
viewAlert: ViewAlert.IViewAlertService,
|
||||
$modal: ng.ui.bootstrap.IModalService,
|
||||
$timeout: ng.ITimeoutService,
|
||||
$log: ng.ILogService) {
|
||||
|
||||
this._albumApi = albumApi;
|
||||
this._modal = $modal;
|
||||
this._timeout = $timeout;
|
||||
this._log = $log;
|
||||
|
||||
this.currentPage = 1;
|
||||
this.pageSize = 50;
|
||||
this.sortColumn = "Title";
|
||||
|
||||
this.loadPage(1);
|
||||
|
||||
this.showAlert(viewAlert.alert, 3000);
|
||||
viewAlert.alert = null;
|
||||
}
|
||||
|
||||
public alert: Models.IAlert;
|
||||
|
||||
public albums: Array<Models.IAlbum>;
|
||||
|
||||
public totalCount: number;
|
||||
|
||||
public currentPage: number;
|
||||
|
||||
public pageSize: number;
|
||||
|
||||
public sortColumn: string;
|
||||
|
||||
public sortDescending: boolean;
|
||||
|
||||
public loadPage(page?: number) {
|
||||
page = page || this.currentPage;
|
||||
var sortByExpression = this.getSortByExpression();
|
||||
this._albumApi.getAlbums(page, this.pageSize, sortByExpression).then(result => {
|
||||
this.albums = result.Data;
|
||||
this.currentPage = result.Page;
|
||||
this.totalCount = result.TotalCount;
|
||||
});
|
||||
}
|
||||
|
||||
public sortBy(column: string) {
|
||||
if (this.sortColumn === column) {
|
||||
// Just flip the direction
|
||||
this.sortDescending = !this.sortDescending;
|
||||
} else {
|
||||
this.sortColumn = column;
|
||||
this.sortDescending = false;
|
||||
}
|
||||
|
||||
this.loadPage();
|
||||
}
|
||||
|
||||
public deleteAlbum(album: Models.IAlbum) {
|
||||
var deleteModal = this._modal.open({
|
||||
templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumDeleteModal.cshtml",
|
||||
controller: "MusicStore.Admin.Catalog.AlbumDeleteModalController as viewModel",
|
||||
resolve: {
|
||||
album: () => album
|
||||
}
|
||||
});
|
||||
|
||||
deleteModal.result.then(shouldDelete => {
|
||||
if (!shouldDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._albumApi.deleteAlbum(album.AlbumId).then(result => {
|
||||
this.loadPage();
|
||||
|
||||
this.showAlert({
|
||||
type: Models.AlertType.success,
|
||||
message: result.data.Message
|
||||
}, 3000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public clearAlert() {
|
||||
this.alert = null;
|
||||
}
|
||||
|
||||
private showAlert(alert: Models.IAlert, closeAfter?: number) {
|
||||
if (!alert) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.alert = alert;
|
||||
|
||||
// TODO: Do we need to destroy this timeout on controller unload?
|
||||
if (closeAfter) {
|
||||
this._timeout(() => this.alert !== alert || this.clearAlert(), closeAfter);
|
||||
}
|
||||
}
|
||||
|
||||
private getSortByExpression() {
|
||||
if (this.sortDescending) {
|
||||
return this.sortColumn + " DESC";
|
||||
}
|
||||
return this.sortColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
/// <reference path="../references.ts" />
|
||||
|
||||
module MusicStore.Admin {
|
||||
|
||||
var dependencies = [
|
||||
"ngRoute",
|
||||
"ui.bootstrap",
|
||||
MusicStore.InlineData,
|
||||
MusicStore.GenreMenu,
|
||||
MusicStore.UrlResolver,
|
||||
MusicStore.UserDetails,
|
||||
MusicStore.LoginLink,
|
||||
MusicStore.Visited,
|
||||
MusicStore.TitleCase,
|
||||
MusicStore.Truncate,
|
||||
MusicStore.GenreApi,
|
||||
MusicStore.AlbumApi,
|
||||
MusicStore.ArtistApi,
|
||||
MusicStore.ViewAlert,
|
||||
MusicStore.Admin.Catalog
|
||||
];
|
||||
|
||||
// Use this method to register work which needs to be performed on module loading.
|
||||
// Note only providers can be injected as dependencies here.
|
||||
function configuration($routeProvider: ng.route.IRouteProvider, $logProvider: ng.ILogProvider) {
|
||||
// TODO: Enable debug logging based on server config
|
||||
// TODO: Capture all logged errors and send back to server
|
||||
$logProvider.debugEnabled(true);
|
||||
|
||||
// Configure routes
|
||||
$routeProvider
|
||||
.when("/albums/:albumId/details", { templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumDetails.cshtml" })
|
||||
.when("/albums/:albumId/:mode", { templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumEdit.cshtml" })
|
||||
.when("/albums/:mode", { templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumEdit.cshtml" })
|
||||
.when("/albums", { templateUrl: "ng-apps/MusicStore.Admin/Catalog/AlbumList.cshtml" })
|
||||
.otherwise({ redirectTo: "/albums" });
|
||||
}
|
||||
|
||||
// Use this method to register work which should be performed when the injector is done loading all modules.
|
||||
function run($log: ng.ILogService, userDetails: UserDetails.IUserDetailsService) {
|
||||
$log.log(userDetails.getUserDetails());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<div ng-controller="MusicStore.Store.Catalog.AlbumDetailsController as viewModel">
|
||||
<h2>{{ viewModel.album.Title }}</h2>
|
||||
|
||||
<p>
|
||||
<img ng-alt="{{ viewModel.album.Title }}" ng-src="{{ viewModel.album.AlbumArtUrl}}" />
|
||||
</p>
|
||||
|
||||
<div id="album-details">
|
||||
<p>
|
||||
<em>Genre:</em>
|
||||
{{ viewModel.album.Genre.Name }}
|
||||
</p>
|
||||
<p>
|
||||
<em>Artist:</em>
|
||||
{{ viewModel.album.Artist.Name }}
|
||||
</p>
|
||||
<p>
|
||||
<em>Price:</em>
|
||||
{{ viewModel.album.Price | currency }}
|
||||
</p>
|
||||
<p class="button">
|
||||
<!-- TODO: Shopping cart functionality -->
|
||||
Add to cart
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
module MusicStore.Store.Catalog {
|
||||
interface IAlbumDetailsViewModel {
|
||||
album: Models.IAlbum;
|
||||
}
|
||||
|
||||
interface IAlbumDetailsRouteParams extends ng.route.IRouteParamsService {
|
||||
albumId: number;
|
||||
}
|
||||
|
||||
class AlbumDetailsController implements IAlbumDetailsViewModel {
|
||||
public album: Models.IAlbum;
|
||||
|
||||
constructor($routeParams: IAlbumDetailsRouteParams, albumApi: AlbumApi.IAlbumApiService) {
|
||||
var viewModel = this,
|
||||
albumId = $routeParams.albumId;
|
||||
|
||||
albumApi.getAlbumDetails(albumId).then(album => {
|
||||
viewModel.album = album;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<div ng-controller="MusicStore.Store.Catalog.GenreDetailsController as viewModel" class="genre">
|
||||
<h3><em>{{ viewModel.genre.Name }}</em> Albums</h3>
|
||||
|
||||
<ul id="album-list" class="list-unstyled">
|
||||
<li ng-repeat="album in viewModel.albums" class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
|
||||
<a ng-href="#/albums/{{ album.AlbumId }}">
|
||||
<img ng-show="album.AlbumArtUrl" ng-alt="{{ album.Title }}" ng-src="{{ album.AlbumArtUrl }}" />
|
||||
<h5 class="control-label">{{ album.Title }}</h5>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
module MusicStore.Store.Catalog {
|
||||
interface IGenreDetailsViewModel {
|
||||
albums: Array<Models.IAlbum>;
|
||||
}
|
||||
|
||||
interface IGenreDetailsRouteParams extends ng.route.IRouteParamsService {
|
||||
genreId: number;
|
||||
}
|
||||
|
||||
class GenreDetailsController implements IGenreDetailsViewModel {
|
||||
public albums: Array<Models.IAlbum>;
|
||||
|
||||
constructor($routeParams: IGenreDetailsRouteParams, genreApi: GenreApi.IGenreApiService) {
|
||||
var viewModel = this;
|
||||
|
||||
genreApi.getGenreAlbums($routeParams.genreId).success(result => {
|
||||
viewModel.albums = result;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<div ng-controller="MusicStore.Store.Catalog.GenreListController as viewModel">
|
||||
<h3>Browse Genres</h3>
|
||||
|
||||
<p>
|
||||
Select from {{ viewModel.genres.length }} genres:
|
||||
</p>
|
||||
<ul class="list-group">
|
||||
<li ng-repeat="genre in viewModel.genres" class="list-group-item">
|
||||
<a ng-href="#/albums/genres/{{ genre.GenreId }}">{{ genre.Name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
module MusicStore.Store.Catalog {
|
||||
interface IGenreListViewModel {
|
||||
genres: Array<Models.IGenre>;
|
||||
}
|
||||
|
||||
class GenreListController implements IGenreListViewModel {
|
||||
public genres: Array<Models.IGenre>;
|
||||
|
||||
constructor(genreApi: GenreApi.IGenreApiService) {
|
||||
var viewModel = this;
|
||||
|
||||
genreApi.getGenresList().success(function (genres) {
|
||||
viewModel.genres = genres;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
<div class="jumbotron">
|
||||
<h1>MVC Music Store</h1>
|
||||
<img src="images/home-showcase.png" />
|
||||
</div>
|
||||
|
||||
<div ng-controller="MusicStore.Store.Home.HomeController as viewModel">
|
||||
<ul class="row list-unstyled" id="album-list">
|
||||
<li ng-repeat="album in viewModel.albums" class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
|
||||
<a ng-href="#/albums/{{ album.AlbumId }}">
|
||||
<img ng-alt="{{ album.Title }}" ng-src="{{ album.AlbumArtUrl }}" />
|
||||
<h4>{{ album.Title }}</h4>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
module MusicStore.Store.Home {
|
||||
interface IHomeViewModel {
|
||||
albums: Array<Models.IAlbum>
|
||||
}
|
||||
|
||||
class HomeController implements IHomeViewModel {
|
||||
public albums: Array<Models.IAlbum>;
|
||||
|
||||
constructor(albumApi: AlbumApi.IAlbumApiService) {
|
||||
var viewModel = this;
|
||||
|
||||
albumApi.getMostPopularAlbums().then(albums => {
|
||||
viewModel.albums = albums;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/// <reference path="../references.ts" />
|
||||
|
||||
module MusicStore.Store {
|
||||
|
||||
var dependencies = [
|
||||
"ngRoute",
|
||||
MusicStore.InlineData,
|
||||
MusicStore.PreventSubmit,
|
||||
MusicStore.GenreMenu,
|
||||
MusicStore.UrlResolver,
|
||||
MusicStore.UserDetails,
|
||||
MusicStore.LoginLink,
|
||||
MusicStore.GenreApi,
|
||||
MusicStore.AlbumApi,
|
||||
MusicStore.Visited,
|
||||
MusicStore.Store.Home,
|
||||
MusicStore.Store.Catalog
|
||||
];
|
||||
|
||||
// Use this method to register work which needs to be performed on module loading.
|
||||
// Note only providers can be injected as dependencies here.
|
||||
function configuration($routeProvider: ng.route.IRouteProvider, $logProvider: ng.ILogProvider) {
|
||||
// TODO: Enable debug logging based on server config
|
||||
// TODO: Capture all logged errors and send back to server
|
||||
$logProvider.debugEnabled(true);
|
||||
|
||||
$routeProvider
|
||||
.when("/", { templateUrl: "ng-apps/MusicStore.Store/Home/Home.html" })
|
||||
.when("/albums/genres", { templateUrl: "ng-apps/MusicStore.Store/Catalog/GenreList.html" })
|
||||
.when("/albums/genres/:genreId", { templateUrl: "ng-apps/MusicStore.Store/Catalog/GenreDetails.html" })
|
||||
.when("/albums/:albumId", { templateUrl: "ng-apps/MusicStore.Store/Catalog/AlbumDetails.html" })
|
||||
.otherwise({ redirectTo: "/" });
|
||||
}
|
||||
|
||||
// Use this method to register work which should be performed when the injector is done loading all modules.
|
||||
function run($log: ng.ILogService, userDetails: UserDetails.IUserDetailsService) {
|
||||
$log.log(userDetails.getUserDetails());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
module MusicStore.AlbumApi {
|
||||
export interface IAlbumApiService {
|
||||
getAlbums(page?: number, pageSize?: number, sortBy?: string): ng.IPromise<Models.IPagedList<Models.IAlbum>>;
|
||||
getAlbumDetails(albumId: number): ng.IPromise<Models.IAlbum>;
|
||||
getMostPopularAlbums(count?: number): ng.IPromise<Array<Models.IAlbum>>;
|
||||
createAlbum(album: Models.IAlbum, config?: ng.IRequestConfig): ng.IHttpPromise<Models.IApiResult>;
|
||||
updateAlbum(album: Models.IAlbum, config?: ng.IRequestConfig): ng.IHttpPromise<Models.IApiResult>;
|
||||
deleteAlbum(albumId: number, config?: ng.IRequestConfig): ng.IHttpPromise<Models.IApiResult>;
|
||||
}
|
||||
|
||||
class AlbumApiService implements IAlbumApiService {
|
||||
private _inlineData: ng.ICacheObject;
|
||||
private _q: ng.IQService;
|
||||
private _http: ng.IHttpService;
|
||||
private _urlResolver: UrlResolver.IUrlResolverService;
|
||||
|
||||
constructor($cacheFactory: ng.ICacheFactoryService,
|
||||
$q: ng.IQService,
|
||||
$http: ng.IHttpService,
|
||||
urlResolver: UrlResolver.IUrlResolverService) {
|
||||
this._inlineData = $cacheFactory.get("inlineData");
|
||||
this._q = $q;
|
||||
this._http = $http;
|
||||
this._urlResolver = urlResolver;
|
||||
}
|
||||
|
||||
public getAlbums(page?: number, pageSize?: number, sortBy?: string) {
|
||||
var url = this._urlResolver.resolveUrl("~/api/albums"),
|
||||
query: any = {},
|
||||
querySeparator = "?",
|
||||
inlineData;
|
||||
|
||||
if (page) {
|
||||
query.page = page;
|
||||
}
|
||||
|
||||
if (pageSize) {
|
||||
query.pageSize = pageSize;
|
||||
}
|
||||
|
||||
if (sortBy) {
|
||||
query.sortBy = sortBy;
|
||||
}
|
||||
|
||||
for (var key in query) {
|
||||
if (query.hasOwnProperty(key)) {
|
||||
url += querySeparator + key + "=" + encodeURIComponent(query[key]);
|
||||
if (querySeparator === "?") {
|
||||
querySeparator = "&";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inlineData = this._inlineData ? this._inlineData.get(url) : null;
|
||||
|
||||
if (inlineData) {
|
||||
return this._q.when(inlineData);
|
||||
} else {
|
||||
return this._http.get(url).then(result => result.data);
|
||||
}
|
||||
}
|
||||
|
||||
public getAlbumDetails(albumId: number) {
|
||||
var url = this._urlResolver.resolveUrl("~/api/albums/" + albumId);
|
||||
return this._http.get(url).then(result => result.data);
|
||||
}
|
||||
|
||||
public getMostPopularAlbums(count?: number) {
|
||||
var url = this._urlResolver.resolveUrl("~/api/albums/mostPopular"),
|
||||
inlineData = this._inlineData ? this._inlineData.get(url) : null;
|
||||
|
||||
if (inlineData) {
|
||||
return this._q.when(inlineData);
|
||||
} else {
|
||||
if (count && count > 0) {
|
||||
url += "?count=" + count;
|
||||
}
|
||||
|
||||
return this._http.get(url).then(result => result.data);
|
||||
}
|
||||
}
|
||||
|
||||
public createAlbum(album: Models.IAlbum, config?: ng.IRequestConfig) {
|
||||
var url = this._urlResolver.resolveUrl("api/albums");
|
||||
return this._http.post(url, album, config || { timeout: 10000 });
|
||||
}
|
||||
|
||||
public updateAlbum(album: Models.IAlbum, config?: ng.IRequestConfig) {
|
||||
var url = this._urlResolver.resolveUrl("api/albums/" + album.AlbumId + "/update");
|
||||
return this._http.put(url, album, config || { timeout: 10000 });
|
||||
}
|
||||
|
||||
public deleteAlbum(albumId: number, config?: ng.IRequestConfig) {
|
||||
var url = this._urlResolver.resolveUrl("api/albums/" + albumId);
|
||||
return this._http.delete(url, config || { timeout: 10000 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
module MusicStore.ArtistApi {
|
||||
export interface IArtistApiService {
|
||||
getArtistsLookup(): ng.IPromise<Array<Models.IArtist>>;
|
||||
}
|
||||
|
||||
class ArtistsApiService implements IArtistApiService {
|
||||
private _inlineData: ng.ICacheObject;
|
||||
private _q: ng.IQService;
|
||||
private _http: ng.IHttpService;
|
||||
private _urlResolver: UrlResolver.IUrlResolverService;
|
||||
|
||||
constructor($cacheFactory: ng.ICacheFactoryService,
|
||||
$q: ng.IQService,
|
||||
$http: ng.IHttpService,
|
||||
urlResolver: UrlResolver.IUrlResolverService) {
|
||||
this._inlineData = $cacheFactory.get("inlineData");
|
||||
this._q = $q;
|
||||
this._http = $http;
|
||||
this._urlResolver = urlResolver;
|
||||
}
|
||||
|
||||
public getArtistsLookup() {
|
||||
var url = this._urlResolver.resolveUrl("~/api/artists/lookup"),
|
||||
inlineData = this._inlineData ? this._inlineData.get(url) : null;
|
||||
|
||||
if (inlineData) {
|
||||
return this._q.when(inlineData);
|
||||
} else {
|
||||
return this._http.get(url).then(result => result.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
module MusicStore.GenreApi {
|
||||
export interface IGenreApiService {
|
||||
getGenresLookup(): ng.IPromise<Array<Models.IGenreLookup>>;
|
||||
getGenresMenu(): ng.IPromise<Array<Models.IGenre>>;
|
||||
getGenresList(): ng.IHttpPromise<Array<Models.IGenre>>;
|
||||
getGenreAlbums(genreId: number): ng.IHttpPromise<Array<Models.IAlbum>>;
|
||||
}
|
||||
|
||||
class GenreApiService implements IGenreApiService {
|
||||
private _inlineData: ng.ICacheObject;
|
||||
private _q: ng.IQService;
|
||||
private _http: ng.IHttpService;
|
||||
private _urlResolver: UrlResolver.IUrlResolverService;
|
||||
|
||||
constructor($cacheFactory: ng.ICacheFactoryService,
|
||||
$q: ng.IQService,
|
||||
$http: ng.IHttpService,
|
||||
urlResolver: UrlResolver.IUrlResolverService) {
|
||||
this._inlineData = $cacheFactory.get("inlineData");
|
||||
this._q = $q;
|
||||
this._http = $http;
|
||||
this._urlResolver = urlResolver;
|
||||
}
|
||||
|
||||
public getGenresLookup() {
|
||||
var url = this._urlResolver.resolveUrl("~/api/genres/lookup"),
|
||||
inlineData = this._inlineData ? this._inlineData.get(url) : null;
|
||||
|
||||
if (inlineData) {
|
||||
return this._q.when(inlineData);
|
||||
} else {
|
||||
return this._http.get(url).then(result => result.data);
|
||||
}
|
||||
}
|
||||
|
||||
public getGenresMenu() {
|
||||
var url = this._urlResolver.resolveUrl("~/api/genres/menu"),
|
||||
inlineData = this._inlineData ? this._inlineData.get(url) : null;
|
||||
|
||||
if (inlineData) {
|
||||
return this._q.when(inlineData);
|
||||
} else {
|
||||
return this._http.get(url).then(result => result.data);
|
||||
}
|
||||
}
|
||||
|
||||
public getGenresList() {
|
||||
var url = this._urlResolver.resolveUrl("~/api/genres");
|
||||
return this._http.get(url);
|
||||
}
|
||||
|
||||
public getGenreAlbums(genreId: number) {
|
||||
var url = this._urlResolver.resolveUrl("~/api/genres/" + genreId + "/albums");
|
||||
return this._http.get(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
<li class="dropdown" ng-controller="MusicStore.GenreMenu.GenreMenuController as viewModel">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown">Store <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li ng-repeat="genre in viewModel.genres">
|
||||
<a ng-href="{{ viewModel.urlBase }}#/albums/genres/{{ genre.GenreId }}" title="{{ genre.Description }}">{{ genre.Name }}</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="{{ viewModel.urlBase }}#/albums/genres">More…</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
module MusicStore.GenreMenu {
|
||||
interface IGenreMenuViewModel {
|
||||
genres: Array<Models.IGenre>;
|
||||
urlBase: string;
|
||||
}
|
||||
|
||||
class GenreMenuController implements IGenreMenuViewModel {
|
||||
constructor(genreApi: GenreApi.IGenreApiService, urlResolver: UrlResolver.IUrlResolverService) {
|
||||
var viewModel = this;
|
||||
|
||||
genreApi.getGenresMenu().then(genres => {
|
||||
viewModel.genres = genres;
|
||||
});
|
||||
|
||||
viewModel.urlBase = urlResolver.base;
|
||||
}
|
||||
|
||||
public genres: Array<Models.IGenre>;
|
||||
|
||||
public urlBase: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
module MusicStore.GenreMenu {
|
||||
|
||||
//@NgDirective('appGenreMenu')
|
||||
class GenreMenuDirective implements ng.IDirective {
|
||||
public replace = true;
|
||||
public restrict = "A";
|
||||
public templateUrl;
|
||||
|
||||
constructor(urlResolver: UrlResolver.IUrlResolverService) {
|
||||
this.templateUrl = urlResolver.resolveUrl("~/ng-apps/components/GenreMenu/GenreMenu.html");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
module MusicStore.InlineData {
|
||||
interface InlineDataAttributes extends ng.IAttributes {
|
||||
type: string;
|
||||
for: string;
|
||||
}
|
||||
|
||||
//@NgDirective('appInlineData')
|
||||
class InlineDataDirective implements ng.IDirective {
|
||||
private _cache: ng.ICacheObject;
|
||||
private _log: ng.ILogService;
|
||||
|
||||
constructor($cacheFactory: ng.ICacheFactoryService, $log: ng.ILogService) {
|
||||
this._cache = $cacheFactory.get("inlineData") || $cacheFactory("inlineData");
|
||||
this._log = $log;
|
||||
}
|
||||
|
||||
public restrict = "A";
|
||||
|
||||
public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: InlineDataAttributes) {
|
||||
var data = attrs.type === "application/json"
|
||||
? angular.fromJson(element.text())
|
||||
: element.text();
|
||||
|
||||
this._log.info("appInlineData: Inline data element found for " + attrs.for);
|
||||
|
||||
this._cache.put(attrs.for, data);
|
||||
|
||||
//element.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
module MusicStore.LoginLink {
|
||||
interface LoginLinkAttributes extends ng.IAttributes {
|
||||
href: string;
|
||||
}
|
||||
|
||||
//@NgDirective('appLoginLink')
|
||||
class LoginLinkDirective implements ng.IDirective {
|
||||
private _window: ng.IWindowService;
|
||||
|
||||
constructor(urlResolver: UrlResolver.IUrlResolverService, $window: ng.IWindowService) {
|
||||
this._window = $window;
|
||||
}
|
||||
|
||||
public restrict = "A";
|
||||
|
||||
public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: LoginLinkAttributes) {
|
||||
if (!element.is("a[href]")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Grab the original login URL
|
||||
var loginUrl = attrs.href;
|
||||
|
||||
element.click(event => {
|
||||
// Update the returnUrl querystring value to current path
|
||||
var currentUrl = this._window.location.pathname + this._window.location.search + this._window.location.hash,
|
||||
newUrl = loginUrl + "?returnUrl=" + encodeURIComponent(currentUrl);
|
||||
|
||||
element.prop("href", newUrl);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IAlbum {
|
||||
AlbumId: number;
|
||||
GenreId: number;
|
||||
ArtistId: number;
|
||||
|
||||
Title: string;
|
||||
AlbumArtUrl: string;
|
||||
Price: number;
|
||||
|
||||
Artist: IArtist;
|
||||
Genre: IGenre;
|
||||
|
||||
DetailsUrl: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IAlert {
|
||||
type: AlertType;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface IModelErrorAlert extends IAlert {
|
||||
modelErrors: Array<IModelError>;
|
||||
}
|
||||
|
||||
export class AlertType {
|
||||
constructor(public value: string) {
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
// Values
|
||||
static success = new AlertType("success");
|
||||
static info = new AlertType("info");
|
||||
static warning = new AlertType("warning");
|
||||
static danger = new AlertType("danger");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IApiResult {
|
||||
Message?: string;
|
||||
Data?: any;
|
||||
ModelErrors?: Array<IModelError>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IArtist {
|
||||
ArtistId: number;
|
||||
Name: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IGenre {
|
||||
GenreId: number;
|
||||
Name: string;
|
||||
Description: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IGenreLookup {
|
||||
GenreId: number;
|
||||
Name: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IModelError {
|
||||
FieldName: string;
|
||||
ErrorMessage: string;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IPagedList<T> {
|
||||
Data: Array<T>;
|
||||
Page: number;
|
||||
PageSize: number;
|
||||
TotalCount: number;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
module MusicStore.Models {
|
||||
export interface IUserDetails {
|
||||
isAuthenticated: boolean;
|
||||
userName: string;
|
||||
userId: string;
|
||||
roles: Array<string>;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
module MusicStore.PreventSubmit {
|
||||
interface IPreventSubmitAttributes extends ng.IAttributes {
|
||||
name: string;
|
||||
appPreventSubmit: string;
|
||||
}
|
||||
|
||||
//@NgDirective('appPreventSubmit')
|
||||
class PreventSubmitDirective implements ng.IDirective {
|
||||
private _preventSubmit: any;
|
||||
|
||||
public restrict = "A";
|
||||
|
||||
public link(scope: any, element: ng.IAugmentedJQuery, attrs: IPreventSubmitAttributes) {
|
||||
// TODO: Just make this directive apply to all <form> tags and no-op if no action attr
|
||||
|
||||
element.submit(e => {
|
||||
if (scope.$eval(attrs.appPreventSubmit)) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
module MusicStore.TitleCase {
|
||||
|
||||
//@NgFilter('titlecase')
|
||||
function titleCase(input: string) {
|
||||
var out = "",
|
||||
lastChar = "";
|
||||
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
out = out + (lastChar === " " || lastChar === ""
|
||||
? input.charAt(i).toUpperCase()
|
||||
: input.charAt(i));
|
||||
|
||||
lastChar = input.charAt(i);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
module MusicStore.Truncate {
|
||||
|
||||
//@NgFilter
|
||||
function truncate(input: string, length: number) {
|
||||
if (!input) {
|
||||
return input;
|
||||
}
|
||||
|
||||
if (input.length <= length) {
|
||||
return input;
|
||||
} else {
|
||||
return input.substr(0, length).trim() + "…";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
module MusicStore.UrlResolver {
|
||||
export interface IUrlResolverService {
|
||||
base: string;
|
||||
resolveUrl(relativeUrl: string);
|
||||
}
|
||||
|
||||
class UrlResolverService implements IUrlResolverService {
|
||||
private _base: string;
|
||||
|
||||
constructor($rootElement: ng.IAugmentedJQuery) {
|
||||
this._base = $rootElement.attr("data-url-base");
|
||||
|
||||
// Add trailing slash if not present
|
||||
if (this._base === "" || this._base.substr(this._base.length - 1) !== "/") {
|
||||
this._base = this._base + "/";
|
||||
}
|
||||
}
|
||||
|
||||
public get base() {
|
||||
return this._base;
|
||||
}
|
||||
|
||||
public resolveUrl(relativeUrl: string) {
|
||||
var firstChar = relativeUrl.substr(0, 1);
|
||||
|
||||
if (firstChar === "~") {
|
||||
relativeUrl = relativeUrl.substr(1);
|
||||
}
|
||||
|
||||
firstChar = relativeUrl.substr(0, 1);
|
||||
|
||||
if (firstChar === "/") {
|
||||
relativeUrl = relativeUrl.substr(1);
|
||||
}
|
||||
|
||||
return this._base + relativeUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
module MusicStore.UserDetails {
|
||||
export interface IUserDetailsService {
|
||||
getUserDetails(): Models.IUserDetails;
|
||||
getUserDetails(elementId: string): Models.IUserDetails;
|
||||
}
|
||||
|
||||
class UserDetailsService implements IUserDetailsService {
|
||||
private _document: ng.IDocumentService;
|
||||
private _userDetails: Models.IUserDetails;
|
||||
|
||||
constructor($document: ng.IDocumentService) {
|
||||
this._document = $document;
|
||||
}
|
||||
|
||||
public getUserDetails(elementId = "userDetails") {
|
||||
if (!this._userDetails) {
|
||||
//var el = this._document.querySelector("[data-json-id='" + elementId + "']");
|
||||
var el = this._document.find("#" + elementId + "[type='application/json']");
|
||||
|
||||
if (el.length) {
|
||||
this._userDetails = angular.fromJson(el.text());
|
||||
} else {
|
||||
this._userDetails = {
|
||||
isAuthenticated: false,
|
||||
userId: null,
|
||||
userName: null,
|
||||
roles: []
|
||||
};
|
||||
}
|
||||
}
|
||||
return this._userDetails;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
module MusicStore.ViewAlert {
|
||||
export interface IViewAlertService {
|
||||
alert: Models.IAlert;
|
||||
}
|
||||
|
||||
class ViewAlertService implements IViewAlertService {
|
||||
public alert: Models.IAlert;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
module MusicStore.Visited {
|
||||
interface IVisitedFormController extends ng.IFormController {
|
||||
focus?: boolean;
|
||||
visited?: boolean;
|
||||
}
|
||||
|
||||
//@NgDirective('input')
|
||||
//@NgDirective('select')
|
||||
class VisitedDirective implements ng.IDirective {
|
||||
private _window: ng.IWindowService;
|
||||
|
||||
constructor($window: ng.IWindowService) {
|
||||
this._window = $window;
|
||||
}
|
||||
|
||||
public restrict = "E";
|
||||
|
||||
public require = "?ngModel";
|
||||
|
||||
public link(scope: ng.IScope, element: ng.IAugmentedJQuery, attrs: ng.IAttributes, ctrl: IVisitedFormController) {
|
||||
if (!ctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
element.on("focus", event => {
|
||||
element.addClass("has-focus");
|
||||
scope.$apply(() => ctrl.focus = true);
|
||||
});
|
||||
|
||||
element.on("blur", event => {
|
||||
element.removeClass("has-focus");
|
||||
element.addClass("has-visited");
|
||||
scope.$apply(() => {
|
||||
ctrl.focus = false;
|
||||
ctrl.visited = true;
|
||||
});
|
||||
});
|
||||
|
||||
element.closest("form").on("submit", function () {
|
||||
element.addClass("has-visited");
|
||||
|
||||
scope.$apply(() => {
|
||||
ctrl.focus = false;
|
||||
ctrl.visited = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
/// <reference path="../bower_components/dt-angular/angular.d.ts" />
|
||||
/// <reference path="../bower_components/dt-angular/angular-route.d.ts" />
|
||||
/// <reference path="../bower_components/dt-angular-ui-bootstrap/angular-ui-bootstrap.d.ts" />
|
||||
|
||||
declare module ng {
|
||||
export interface ILogProvider {
|
||||
debugEnabled(enabled: boolean);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"name": "MusicStore",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bower-task": "^0.4.0",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-copy": "~0.5.0",
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-contrib-less": "~0.11.0",
|
||||
"grunt-contrib-uglify": "~0.4.0",
|
||||
"grunt-contrib-watch": "~0.6.1",
|
||||
"grunt-ide-support": "~0.1.7",
|
||||
"grunt-tslint": "~0.4.1",
|
||||
"grunt-tsng": "~0.1.3",
|
||||
"grunt-typescript": "~0.6.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
{
|
||||
"webroot": "wwwroot",
|
||||
"exclude": [
|
||||
"wwwroot",
|
||||
"bower_components",
|
||||
"node_modules",
|
||||
"grunt"
|
||||
],
|
||||
"publishExclude": [
|
||||
"bower.json",
|
||||
"package.json",
|
||||
"gruntfile.js",
|
||||
"bower_components",
|
||||
"node_modules",
|
||||
"grunt"
|
||||
],
|
||||
"authors": [
|
||||
"Microsoft"
|
||||
],
|
||||
"description": "Music store application on K as a SPA",
|
||||
"compilationOptions": {
|
||||
"define": [
|
||||
"DEBUG"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Server.Kestrel": "1.0.0-*",
|
||||
"Microsoft.AspNet.Server.IIS": "1.0.0-*",
|
||||
"Microsoft.AspNet.Mvc": "6.0.0-*",
|
||||
"Microsoft.AspNet.Server.WebListener": "1.0.0-*",
|
||||
"Microsoft.AspNet.StaticFiles": "1.0.0-*",
|
||||
"EntityFramework.InMemory": "7.0.0-*",
|
||||
"EntityFramework.SqlServer": "7.0.0-*",
|
||||
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-*",
|
||||
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*",
|
||||
"Microsoft.Framework.Configuration": "1.0.0-*",
|
||||
"Microsoft.Framework.Configuration.EnvironmentVariables": "1.0.0-*",
|
||||
"Microsoft.Framework.Configuration.Json": "1.0.0-*",
|
||||
"AutoMapper": "4.0.0-alpha1"
|
||||
},
|
||||
"commands": {
|
||||
"WebListener": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5102",
|
||||
"Kestrel": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls http://localhost:5104",
|
||||
"run": "run server.urls=http://localhost:5103"
|
||||
},
|
||||
"frameworks": {
|
||||
"dnx451": {},
|
||||
"dnxcore50": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
@import '../../bower_components/bootstrap/less/bootstrap.less';
|
||||
|
||||
@font-face {
|
||||
font-family: 'Glyphicons Halflings';
|
||||
src: url('../lib/bootstrap/fonts/glyphicons-halflings-regular.eot');
|
||||
src: url('../lib/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../lib/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'), url('../lib/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../lib/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
|
||||
}
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.nav, .pagination, .carousel, .panel-title a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
/* Set padding to keep content from hitting the edges */
|
||||
.body-content {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
/* Set width on the form input elements since they're 100% wide by default */
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
/*max-width: 280px;*/
|
||||
}
|
||||
|
||||
/* styles for validation helpers */
|
||||
.field-validation-error {
|
||||
color: #b94a48;
|
||||
}
|
||||
|
||||
.field-validation-valid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input.input-validation-error {
|
||||
border: 1px solid #b94a48;
|
||||
}
|
||||
|
||||
input[type="checkbox"].input-validation-error {
|
||||
border: 0 none;
|
||||
}
|
||||
|
||||
.validation-summary-errors {
|
||||
color: #b94a48;
|
||||
}
|
||||
|
||||
.validation-summary-valid {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Music Store additions */
|
||||
|
||||
ul#album-list li {
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
ul#album-list li img:hover {
|
||||
box-shadow: 1px 1px 7px #777;
|
||||
}
|
||||
|
||||
ul#album-list li img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
box-shadow: 1px 1px 5px #999;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul#album-list li a, ul#album-details li a {
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
ul#album-list li a:hover {
|
||||
background: none;
|
||||
-webkit-text-shadow: 1px 1px 2px #bbb;
|
||||
text-shadow: 1px 1px 2px #bbb;
|
||||
color: #363430;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue