Merge in 'release/5.0' changes
This commit is contained in:
commit
5d8e736b22
|
|
@ -861,18 +861,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.WebSoc
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.ResponseCaching.Microbenchmarks", "src\Middleware\perf\ResponseCaching.Microbenchmarks\Microsoft.AspNetCore.ResponseCaching.Microbenchmarks.csproj", "{8A745E35-8098-4EB4-AC55-587B9F0DC4BE}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MusicStore", "MusicStore", "{884AED21-7931-42A3-B08A-E58F7B0D6E7F}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{8DA88110-5A13-41A9-9F9D-674D921EB442}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicStore", "src\MusicStore\samples\MusicStore\MusicStore.csproj", "{4BCF2228-159D-4547-8D54-CDD2252D076A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2D347127-3B13-47B1-84EC-770A9C1E11B0}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicStore.Test", "src\MusicStore\test\MusicStore.Test\MusicStore.Test.csproj", "{A7802482-55BA-4D95-BE79-8A601131F527}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MusicStore.E2ETests", "src\MusicStore\test\MusicStore.E2ETests\MusicStore.E2ETests.csproj", "{81947277-035F-41F6-A1D4-8EA362CB46D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MicrosoftAccount", "MicrosoftAccount", "{8C4006DF-FF48-46B6-A124-10B1EEAA266E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.MicrosoftAccount", "src\Security\Authentication\MicrosoftAccount\src\Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj", "{F5C54062-B19C-4291-A816-F1B5A167369A}"
|
||||
|
|
@ -1009,8 +997,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BlazorTemplates.Tests", "sr
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Framework", "Framework", "{A4C26078-B6D8-4FD8-87A6-7C15A3482038}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App.Runtime", "App.Runtime", "{F1A6BA04-853E-43B5-B27E-54558603837B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.App.Runtime", "src\Framework\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj", "{3163C56E-99BC-4BD1-8880-4F94A2407741}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Razor.Test", "src\Razor\Razor\test\Microsoft.AspNetCore.Razor.Test.csproj", "{776BFCDC-8591-4E3D-BD0D-858A188A033D}"
|
||||
|
|
@ -1531,6 +1517,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HostingStartup", "src\SiteE
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomBasePathApp", "src\Components\WebAssembly\testassets\CustomBasePathApp\CustomBasePathApp.csproj", "{E2461809-D2EA-436D-B5C3-8A9EE0A283B8}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "App.Ref", "App.Ref", "{9BAB79EC-E469-4EA4-9FDF-91A5944C28FD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.App.Ref.Internal", "src\Framework\App.Ref.Internal\src\Microsoft.AspNetCore.App.Ref.Internal.csproj", "{E3993626-E5A9-440E-BA77-2A22823FAF52}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -4421,42 +4411,6 @@ Global
|
|||
{8A745E35-8098-4EB4-AC55-587B9F0DC4BE}.Release|x64.Build.0 = Release|Any CPU
|
||||
{8A745E35-8098-4EB4-AC55-587B9F0DC4BE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{8A745E35-8098-4EB4-AC55-587B9F0DC4BE}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Debug|Any CPU.ActiveCfg = Debug|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Debug|Any CPU.Build.0 = Debug|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Debug|x64.Build.0 = Debug|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Debug|x86.Build.0 = Debug|x86
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Release|Any CPU.ActiveCfg = Release|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Release|Any CPU.Build.0 = Release|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Release|x64.ActiveCfg = Release|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Release|x64.Build.0 = Release|x64
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Release|x86.ActiveCfg = Release|x86
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A}.Release|x86.Build.0 = Release|x86
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527}.Release|x86.Build.0 = Release|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F5C54062-B19C-4291-A816-F1B5A167369A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F5C54062-B19C-4291-A816-F1B5A167369A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F5C54062-B19C-4291-A816-F1B5A167369A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
|
|
@ -7323,6 +7277,18 @@ Global
|
|||
{E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E2461809-D2EA-436D-B5C3-8A9EE0A283B8}.Release|x86.Build.0 = Release|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Release|x64.Build.0 = Release|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -7755,12 +7721,6 @@ Global
|
|||
{EE65018D-FA12-461D-B2C5-44CA6E385530} = {E5963C9F-20A6-4385-B364-814D2581FADF}
|
||||
{A8E1962B-688E-44B3-81F3-BBB9891534CE} = {EE65018D-FA12-461D-B2C5-44CA6E385530}
|
||||
{8A745E35-8098-4EB4-AC55-587B9F0DC4BE} = {EE65018D-FA12-461D-B2C5-44CA6E385530}
|
||||
{884AED21-7931-42A3-B08A-E58F7B0D6E7F} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
|
||||
{8DA88110-5A13-41A9-9F9D-674D921EB442} = {884AED21-7931-42A3-B08A-E58F7B0D6E7F}
|
||||
{4BCF2228-159D-4547-8D54-CDD2252D076A} = {8DA88110-5A13-41A9-9F9D-674D921EB442}
|
||||
{2D347127-3B13-47B1-84EC-770A9C1E11B0} = {884AED21-7931-42A3-B08A-E58F7B0D6E7F}
|
||||
{A7802482-55BA-4D95-BE79-8A601131F527} = {2D347127-3B13-47B1-84EC-770A9C1E11B0}
|
||||
{81947277-035F-41F6-A1D4-8EA362CB46D2} = {2D347127-3B13-47B1-84EC-770A9C1E11B0}
|
||||
{8C4006DF-FF48-46B6-A124-10B1EEAA266E} = {822D1519-77F0-484A-B9AB-F694C2CC25F1}
|
||||
{F5C54062-B19C-4291-A816-F1B5A167369A} = {8C4006DF-FF48-46B6-A124-10B1EEAA266E}
|
||||
{056535BC-3E30-49E2-BE3B-630C62E641A5} = {1A0EFF9F-E699-4303-AE50-BFAF9804EEB6}
|
||||
|
|
@ -7829,8 +7789,7 @@ Global
|
|||
{6DE03095-7EAC-41DF-8AE4-3018ED29BC61} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
|
||||
{18CE63FC-3BFE-47DF-A8D7-9D716FEB04C9} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
|
||||
{A4C26078-B6D8-4FD8-87A6-7C15A3482038} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
|
||||
{F1A6BA04-853E-43B5-B27E-54558603837B} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038}
|
||||
{3163C56E-99BC-4BD1-8880-4F94A2407741} = {F1A6BA04-853E-43B5-B27E-54558603837B}
|
||||
{3163C56E-99BC-4BD1-8880-4F94A2407741} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038}
|
||||
{776BFCDC-8591-4E3D-BD0D-858A188A033D} = {66A0F71A-FFB5-4763-913E-FB4A4F17EAA2}
|
||||
{ED34C082-619E-4CCB-9D26-4E2375633FA2} = {AB316CF4-D9C3-427E-8460-7C5235BED45E}
|
||||
{8BA59986-C9AA-4A78-BE18-0A344FB56D09} = {B27FBAC2-ADA3-4A05-B232-64011B6B2DA3}
|
||||
|
|
@ -8068,7 +8027,7 @@ Global
|
|||
{37329855-01B8-4B03-9765-1A941B06E43C} = {8C15FD04-7F90-43FC-B488-023432FE3CE1}
|
||||
{D3246226-BC1A-47F1-8E3E-C3380A8F13FB} = {8C15FD04-7F90-43FC-B488-023432FE3CE1}
|
||||
{B06ADD57-E855-4D8C-85DC-B323509AE540} = {898F7E0B-1671-42CB-9DFB-689AFF212ED3}
|
||||
{BAD47859-95DF-4C8F-9AF7-C48B68F478A1} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038}
|
||||
{BAD47859-95DF-4C8F-9AF7-C48B68F478A1} = {9BAB79EC-E469-4EA4-9FDF-91A5944C28FD}
|
||||
{010A9638-F20E-4FE6-A186-85732BFC9CB0} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038}
|
||||
{3D34C81F-2CB5-459E-87E9-0CC04757A2A0} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
|
||||
{04CFE286-6D32-41EF-8887-4B5F8086A365} = {6126DCE4-9692-4EE2-B240-C65743572995}
|
||||
|
|
@ -8090,6 +8049,8 @@ Global
|
|||
{E83B0BCC-A8E0-4FBD-BE51-9A533C9CB972} = {DFC4F588-B4B4-484B-AB93-B36721374AD3}
|
||||
{5D6F99C5-D292-4459-B8BD-8E4AD42E1B21} = {E83B0BCC-A8E0-4FBD-BE51-9A533C9CB972}
|
||||
{E2461809-D2EA-436D-B5C3-8A9EE0A283B8} = {7D2B0799-A634-42AC-AE77-5D167BA51389}
|
||||
{9BAB79EC-E469-4EA4-9FDF-91A5944C28FD} = {A4C26078-B6D8-4FD8-87A6-7C15A3482038}
|
||||
{E3993626-E5A9-440E-BA77-2A22823FAF52} = {9BAB79EC-E469-4EA4-9FDF-91A5944C28FD}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
|
||||
|
|
|
|||
|
|
@ -158,7 +158,6 @@
|
|||
$(RepoRoot)src\Razor\**\*.*proj;
|
||||
$(RepoRoot)src\Mvc\**\*.*proj;
|
||||
$(RepoRoot)src\Azure\**\*.*proj;
|
||||
$(RepoRoot)src\MusicStore\**\*.*proj;
|
||||
$(RepoRoot)src\SignalR\**\*.csproj;
|
||||
$(RepoRoot)src\Components\**\*.csproj;
|
||||
$(RepoRoot)src\Analyzers\**\*.csproj;
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Microsoft.Net.Component.4.6.1.TargetingPack",
|
||||
"Microsoft.Net.Component.4.7.2.SDK",
|
||||
"Microsoft.Net.Component.4.7.2.TargetingPack",
|
||||
"Microsoft.VisualStudio.Workload.ManagedDesktop",
|
||||
"Microsoft.VisualStudio.Workload.NetCoreTools",
|
||||
"Microsoft.VisualStudio.Workload.NetWeb",
|
||||
"Microsoft.VisualStudio.Workload.VisualStudioExtension"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<Project>
|
||||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Some of the tests make assertions which require symbols to map exactly back to the source root. -->
|
||||
<DeterministicSourcePaths>false</DeterministicSourcePaths>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
{
|
||||
"solution": {
|
||||
"path": "..\\..\\AspNetCore.sln",
|
||||
"projects" : [
|
||||
"src\\MusicStore\\samples\\MusicStore\\MusicStore.csproj",
|
||||
"src\\MusicStore\\test\\MusicStore.Test\\MusicStore.Test.csproj",
|
||||
"src\\MusicStore\\test\\MusicStore.E2ETests\\MusicStore.E2ETests.csproj",
|
||||
"src\\Antiforgery\\src\\Microsoft.AspNetCore.Antiforgery.csproj",
|
||||
"src\\DataProtection\\Abstractions\\src\\Microsoft.AspNetCore.DataProtection.Abstractions.csproj",
|
||||
"src\\DataProtection\\Cryptography.Internal\\src\\Microsoft.AspNetCore.Cryptography.Internal.csproj",
|
||||
"src\\DataProtection\\Cryptography.KeyDerivation\\src\\Microsoft.AspNetCore.Cryptography.KeyDerivation.csproj",
|
||||
"src\\DataProtection\\DataProtection\\src\\Microsoft.AspNetCore.DataProtection.csproj",
|
||||
"src\\DefaultBuilder\\src\\Microsoft.AspNetCore.csproj",
|
||||
"src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj",
|
||||
"src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj",
|
||||
"src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj",
|
||||
"src\\Hosting\\Server.IntegrationTesting\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.csproj",
|
||||
"src\\Html\\Abstractions\\src\\Microsoft.AspNetCore.Html.Abstractions.csproj",
|
||||
"src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj",
|
||||
"src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj",
|
||||
"src\\Http\\Headers\\src\\Microsoft.Net.Http.Headers.csproj",
|
||||
"src\\Http\\Http.Abstractions\\src\\Microsoft.AspNetCore.Http.Abstractions.csproj",
|
||||
"src\\Http\\Http.Extensions\\src\\Microsoft.AspNetCore.Http.Extensions.csproj",
|
||||
"src\\Http\\Http.Features\\src\\Microsoft.AspNetCore.Http.Features.csproj",
|
||||
"src\\Http\\Http\\src\\Microsoft.AspNetCore.Http.csproj",
|
||||
"src\\Http\\Routing.Abstractions\\src\\Microsoft.AspNetCore.Routing.Abstractions.csproj",
|
||||
"src\\Http\\Routing\\src\\Microsoft.AspNetCore.Routing.csproj",
|
||||
"src\\Http\\WebUtilities\\src\\Microsoft.AspNetCore.WebUtilities.csproj",
|
||||
"src\\Identity\\Core\\src\\Microsoft.AspNetCore.Identity.csproj",
|
||||
"src\\Identity\\EntityFrameworkCore\\src\\Microsoft.AspNetCore.Identity.EntityFrameworkCore.csproj",
|
||||
"src\\Identity\\Extensions.Core\\src\\Microsoft.Extensions.Identity.Core.csproj",
|
||||
"src\\Identity\\Extensions.Stores\\src\\Microsoft.Extensions.Identity.Stores.csproj",
|
||||
"src\\Middleware\\CORS\\src\\Microsoft.AspNetCore.Cors.csproj",
|
||||
"src\\Middleware\\Diagnostics.Abstractions\\src\\Microsoft.AspNetCore.Diagnostics.Abstractions.csproj",
|
||||
"src\\Middleware\\Diagnostics.EntityFrameworkCore\\src\\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj",
|
||||
"src\\Middleware\\Diagnostics\\src\\Microsoft.AspNetCore.Diagnostics.csproj",
|
||||
"src\\Middleware\\HostFiltering\\src\\Microsoft.AspNetCore.HostFiltering.csproj",
|
||||
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",
|
||||
"src\\Middleware\\Localization\\src\\Microsoft.AspNetCore.Localization.csproj",
|
||||
"src\\Middleware\\ResponseCaching.Abstractions\\src\\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj",
|
||||
"src\\Middleware\\Session\\src\\Microsoft.AspNetCore.Session.csproj",
|
||||
"src\\Middleware\\StaticFiles\\src\\Microsoft.AspNetCore.StaticFiles.csproj",
|
||||
"src\\Mvc\\Mvc.Abstractions\\src\\Microsoft.AspNetCore.Mvc.Abstractions.csproj",
|
||||
"src\\Mvc\\Mvc.Analyzers\\src\\Microsoft.AspNetCore.Mvc.Analyzers.csproj",
|
||||
"src\\Mvc\\Mvc.ApiExplorer\\src\\Microsoft.AspNetCore.Mvc.ApiExplorer.csproj",
|
||||
"src\\Mvc\\Mvc.Core\\src\\Microsoft.AspNetCore.Mvc.Core.csproj",
|
||||
"src\\Mvc\\Mvc.Cors\\src\\Microsoft.AspNetCore.Mvc.Cors.csproj",
|
||||
"src\\Mvc\\Mvc.DataAnnotations\\src\\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj",
|
||||
"src\\Mvc\\Mvc.Localization\\src\\Microsoft.AspNetCore.Mvc.Localization.csproj",
|
||||
"src\\Mvc\\Mvc.Razor\\src\\Microsoft.AspNetCore.Mvc.Razor.csproj",
|
||||
"src\\Mvc\\Mvc.RazorPages\\src\\Microsoft.AspNetCore.Mvc.RazorPages.csproj",
|
||||
"src\\Mvc\\Mvc.TagHelpers\\src\\Microsoft.AspNetCore.Mvc.TagHelpers.csproj",
|
||||
"src\\Mvc\\Mvc.ViewFeatures\\src\\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj",
|
||||
"src\\Mvc\\Mvc\\src\\Microsoft.AspNetCore.Mvc.csproj",
|
||||
"src\\Razor\\Razor.Runtime\\src\\Microsoft.AspNetCore.Razor.Runtime.csproj",
|
||||
"src\\Razor\\Razor\\src\\Microsoft.AspNetCore.Razor.csproj",
|
||||
"src\\Security\\Authentication\\Cookies\\src\\Microsoft.AspNetCore.Authentication.Cookies.csproj",
|
||||
"src\\Security\\Authentication\\Core\\src\\Microsoft.AspNetCore.Authentication.csproj",
|
||||
"src\\Security\\Authentication\\Facebook\\src\\Microsoft.AspNetCore.Authentication.Facebook.csproj",
|
||||
"src\\Security\\Authentication\\Google\\src\\Microsoft.AspNetCore.Authentication.Google.csproj",
|
||||
"src\\Security\\Authentication\\MicrosoftAccount\\src\\Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj",
|
||||
"src\\Security\\Authentication\\OAuth\\src\\Microsoft.AspNetCore.Authentication.OAuth.csproj",
|
||||
"src\\Security\\Authentication\\OpenIdConnect\\src\\Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj",
|
||||
"src\\Security\\Authentication\\Twitter\\src\\Microsoft.AspNetCore.Authentication.Twitter.csproj",
|
||||
"src\\Security\\Authorization\\Core\\src\\Microsoft.AspNetCore.Authorization.csproj",
|
||||
"src\\Security\\Authorization\\Policy\\src\\Microsoft.AspNetCore.Authorization.Policy.csproj",
|
||||
"src\\Servers\\Connections.Abstractions\\src\\Microsoft.AspNetCore.Connections.Abstractions.csproj",
|
||||
"src\\Servers\\HttpSys\\src\\Microsoft.AspNetCore.Server.HttpSys.csproj",
|
||||
"src\\Servers\\IIS\\IIS\\src\\Microsoft.AspNetCore.Server.IIS.csproj",
|
||||
"src\\Servers\\IIS\\IISIntegration\\src\\Microsoft.AspNetCore.Server.IISIntegration.csproj",
|
||||
"src\\Servers\\IIS\\IntegrationTesting.IIS\\src\\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj",
|
||||
"src\\Servers\\Kestrel\\Core\\src\\Microsoft.AspNetCore.Server.Kestrel.Core.csproj",
|
||||
"src\\Servers\\Kestrel\\Kestrel\\src\\Microsoft.AspNetCore.Server.Kestrel.csproj",
|
||||
"src\\Servers\\Kestrel\\Transport.Sockets\\src\\Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.csproj"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"Default": {
|
||||
"rules": [
|
||||
"DefaultCompositeRule"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
MusicStore (test application)
|
||||
=============================
|
||||
|
||||
This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/dotnet/aspnetcore) repo.
|
||||
|
||||
This is a test application used for ASP.NET Core internal test processes.
|
||||
It is not intended to be a representative sample of how to use ASP.NET Core.
|
||||
|
||||
Samples and docs for ASP.NET Core can be found here: <https://docs.asp.net>.
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@ECHO OFF
|
||||
SET RepoRoot=%~dp0..\..
|
||||
%RepoRoot%\build.cmd -projects %~dp0**\*.*proj %*
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MusicStore.Models;
|
||||
using MusicStore.ViewModels;
|
||||
|
||||
namespace MusicStore.Areas.Admin.Controllers
|
||||
{
|
||||
[Area("Admin")]
|
||||
[Authorize("ManageStore")]
|
||||
public class StoreManagerController : Controller
|
||||
{
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public StoreManagerController(MusicStoreContext dbContext, IOptions<AppSettings> options)
|
||||
{
|
||||
DbContext = dbContext;
|
||||
_appSettings = options.Value;
|
||||
}
|
||||
|
||||
public MusicStoreContext DbContext { get; }
|
||||
|
||||
//
|
||||
// GET: /StoreManager/
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var albums = await DbContext.Albums
|
||||
.Include(a => a.Genre)
|
||||
.Include(a => a.Artist)
|
||||
.ToListAsync();
|
||||
|
||||
return View(albums);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /StoreManager/Details/5
|
||||
public async Task<IActionResult> Details(
|
||||
[FromServices] IMemoryCache cache,
|
||||
int id)
|
||||
{
|
||||
var cacheKey = GetCacheKey(id);
|
||||
|
||||
Album album;
|
||||
if (!cache.TryGetValue(cacheKey, out album))
|
||||
{
|
||||
album = await DbContext.Albums
|
||||
.Where(a => a.AlbumId == id)
|
||||
.Include(a => a.Artist)
|
||||
.Include(a => a.Genre)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (album != null)
|
||||
{
|
||||
if (_appSettings.CacheDbResults)
|
||||
{
|
||||
//Remove it from cache if not retrieved in last 10 minutes.
|
||||
cache.Set(
|
||||
cacheKey,
|
||||
album,
|
||||
new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(10)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (album == null)
|
||||
{
|
||||
cache.Remove(cacheKey);
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(album);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /StoreManager/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name");
|
||||
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name");
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: /StoreManager/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(
|
||||
Album album,
|
||||
[FromServices] IMemoryCache cache,
|
||||
CancellationToken requestAborted)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
DbContext.Albums.Add(album);
|
||||
await DbContext.SaveChangesAsync(requestAborted);
|
||||
|
||||
var albumData = new AlbumData
|
||||
{
|
||||
Title = album.Title,
|
||||
Url = Url.Action("Details", "Store", new { id = album.AlbumId })
|
||||
};
|
||||
|
||||
cache.Remove("latestAlbum");
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name", album.GenreId);
|
||||
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name", album.ArtistId);
|
||||
return View(album);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /StoreManager/Edit/5
|
||||
public async Task<IActionResult> Edit(int id)
|
||||
{
|
||||
var album = await DbContext.Albums.
|
||||
Where(a => a.AlbumId == id).
|
||||
FirstOrDefaultAsync();
|
||||
|
||||
if (album == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name", album.GenreId);
|
||||
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name", album.ArtistId);
|
||||
return View(album);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /StoreManager/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(
|
||||
[FromServices] IMemoryCache cache,
|
||||
Album album,
|
||||
CancellationToken requestAborted)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
DbContext.Update(album);
|
||||
await DbContext.SaveChangesAsync(requestAborted);
|
||||
//Invalidate the cache entry as it is modified
|
||||
cache.Remove(GetCacheKey(album.AlbumId));
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name", album.GenreId);
|
||||
ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name", album.ArtistId);
|
||||
return View(album);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /StoreManager/RemoveAlbum/5
|
||||
public async Task<IActionResult> RemoveAlbum(int id)
|
||||
{
|
||||
var album = await DbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefaultAsync();
|
||||
if (album == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(album);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /StoreManager/RemoveAlbum/5
|
||||
[HttpPost, ActionName("RemoveAlbum")]
|
||||
public async Task<IActionResult> RemoveAlbumConfirmed(
|
||||
[FromServices] IMemoryCache cache,
|
||||
int id,
|
||||
CancellationToken requestAborted)
|
||||
{
|
||||
var album = await DbContext.Albums.Where(a => a.AlbumId == id).FirstOrDefaultAsync();
|
||||
if (album == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
DbContext.Albums.Remove(album);
|
||||
await DbContext.SaveChangesAsync(requestAborted);
|
||||
//Remove the cache entry as it is removed
|
||||
cache.Remove(GetCacheKey(id));
|
||||
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
private static string GetCacheKey(int id)
|
||||
{
|
||||
return string.Format("album_{0}", id);
|
||||
}
|
||||
|
||||
// NOTE: this is used for end to end testing only
|
||||
//
|
||||
// GET: /StoreManager/GetAlbumIdFromName
|
||||
// Note: Added for automated testing purpose. Application does not use this.
|
||||
[HttpGet]
|
||||
[SkipStatusCodePages]
|
||||
[EnableCors("CorsPolicy")]
|
||||
public async Task<IActionResult> GetAlbumIdFromName(string albumName)
|
||||
{
|
||||
var album = await DbContext.Albums.Where(a => a.Title == albumName).FirstOrDefaultAsync();
|
||||
|
||||
if (album == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return Content(album.AlbumId.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Create";
|
||||
}
|
||||
|
||||
<h2>Create</h2>
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<div class="form-horizontal">
|
||||
<h4>Album</h4>
|
||||
<hr />
|
||||
@Html.ValidationSummary(true)
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.GenreId, "GenreId", new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.DropDownList("GenreId", String.Empty)
|
||||
@Html.ValidationMessageFor(model => model.GenreId)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.ArtistId, "ArtistId", new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.DropDownList("ArtistId", String.Empty)
|
||||
@Html.ValidationMessageFor(model => model.ArtistId)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.EditorFor(model => model.Title)
|
||||
@Html.ValidationMessageFor(model => model.Title)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.Price, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.EditorFor(model => model.Price)
|
||||
@Html.ValidationMessageFor(model => model.Price)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.AlbumArtUrl, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.EditorFor(model => model.AlbumArtUrl)
|
||||
@Html.ValidationMessageFor(model => model.AlbumArtUrl)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Create" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div>
|
||||
@Html.ActionLink("Back to List", "Index")
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@*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,58 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Details";
|
||||
}
|
||||
|
||||
<h2>Details</h2>
|
||||
|
||||
<div>
|
||||
<h4>Album</h4>
|
||||
<hr />
|
||||
<dl class="dl-horizontal">
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.Artist.Name)
|
||||
</dt>
|
||||
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.Artist.Name)
|
||||
</dd>
|
||||
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.Genre.Name)
|
||||
</dt>
|
||||
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.Genre.Name)
|
||||
</dd>
|
||||
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.Title)
|
||||
</dt>
|
||||
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.Title)
|
||||
</dd>
|
||||
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.Price)
|
||||
</dt>
|
||||
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.Price)
|
||||
</dd>
|
||||
|
||||
<dt>
|
||||
@Html.DisplayNameFor(model => model.AlbumArtUrl)
|
||||
</dt>
|
||||
|
||||
<dd>
|
||||
@Html.DisplayFor(model => model.AlbumArtUrl)
|
||||
</dd>
|
||||
|
||||
</dl>
|
||||
</div>
|
||||
<p>
|
||||
@Html.ActionLink("Edit", "Edit", new { id = Model.AlbumId }) |
|
||||
@Html.ActionLink("Back to List", "Index")
|
||||
</p>
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Edit";
|
||||
}
|
||||
|
||||
<h2>Edit</h2>
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
@Html.AntiForgeryToken()
|
||||
|
||||
<div class="form-horizontal">
|
||||
<h4>Album</h4>
|
||||
<hr />
|
||||
@Html.ValidationSummary(true)
|
||||
@Html.HiddenFor(model => model.AlbumId)
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.GenreId, "GenreId", new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.DropDownList("GenreId", String.Empty)
|
||||
@Html.ValidationMessageFor(model => model.GenreId)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.ArtistId, "ArtistId", new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.DropDownList("ArtistId", String.Empty)
|
||||
@Html.ValidationMessageFor(model => model.ArtistId)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.Title, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.EditorFor(model => model.Title)
|
||||
@Html.ValidationMessageFor(model => model.Title)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.Price, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.EditorFor(model => model.Price)
|
||||
@Html.ValidationMessageFor(model => model.Price)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@Html.LabelFor(model => model.AlbumArtUrl, new { @class = "control-label col-md-2" })
|
||||
<div class="col-md-10">
|
||||
@Html.EditorFor(model => model.AlbumArtUrl)
|
||||
@Html.ValidationMessageFor(model => model.AlbumArtUrl)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Save" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div>
|
||||
@Html.ActionLink("Back to List", "Index")
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@*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,66 +0,0 @@
|
|||
@model IEnumerable<MusicStore.Models.Album>
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Index";
|
||||
}
|
||||
|
||||
<h2>Index</h2>
|
||||
|
||||
<p>
|
||||
@Html.ActionLink("Create New", "Create")
|
||||
</p>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.Genre.Name)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.FirstOrDefault().Artist.Name)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.FirstOrDefault().Title)
|
||||
</th>
|
||||
<th>
|
||||
@Html.DisplayNameFor(model => model.FirstOrDefault().Price)
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
||||
@foreach (var item in Model)
|
||||
{
|
||||
<tr>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => item.Genre.Name)
|
||||
</td>
|
||||
<td>
|
||||
@if (item.Artist.Name.Length <= 25)
|
||||
{
|
||||
@item.Artist.Name
|
||||
}
|
||||
else
|
||||
{
|
||||
@item.Artist.Name.Substring(0, 25)<text>...</text>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@if (item.Title.Length <= 25)
|
||||
{
|
||||
@item.Title
|
||||
}
|
||||
else
|
||||
{
|
||||
@item.Title.Substring(0, 25)<text>...</text>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
@Html.DisplayFor(modelItem => item.Price)
|
||||
</td>
|
||||
<td>
|
||||
@Html.ActionLink("Edit", "Edit", new { id = item.AlbumId }) |
|
||||
@Html.ActionLink("Details", "Details", new { id = item.AlbumId }) |
|
||||
@Html.ActionLink("Delete", "RemoveAlbum", new { id = item.AlbumId })
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
</table>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
@model MusicStore.Models.Album
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Delete";
|
||||
}
|
||||
|
||||
@if (Model != null)
|
||||
{
|
||||
<h2>Delete Confirmation</h2>
|
||||
|
||||
<p>
|
||||
Are you sure you want to delete the album titled
|
||||
<strong>@Model.Title</strong>?
|
||||
</p>
|
||||
|
||||
@using (Html.BeginForm())
|
||||
{
|
||||
<p>
|
||||
<input type="submit" value="Delete" />
|
||||
</p>
|
||||
<p>
|
||||
@Html.ActionLink("Back to List", "Index")
|
||||
</p>
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@Html.Label(null, "Unable to locate the album")
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
@{
|
||||
Layout = "/Views/Shared/_Layout.cshtml";
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Components
|
||||
{
|
||||
[ViewComponent(Name = "CartSummary")]
|
||||
public class CartSummaryComponent : ViewComponent
|
||||
{
|
||||
public CartSummaryComponent(MusicStoreContext dbContext)
|
||||
{
|
||||
DbContext = dbContext;
|
||||
}
|
||||
|
||||
private MusicStoreContext DbContext { get; }
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync()
|
||||
{
|
||||
var cart = ShoppingCart.GetCart(DbContext, HttpContext);
|
||||
|
||||
var cartItems = await cart.GetCartItems();
|
||||
|
||||
ViewBag.CartCount = cartItems.Sum(c => c.Count);
|
||||
ViewBag.CartSummary = string.Join("\n", cartItems.Select(c => c.Album.Title).Distinct());
|
||||
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Components
|
||||
{
|
||||
[ViewComponent(Name = "GenreMenu")]
|
||||
public class GenreMenuComponent : ViewComponent
|
||||
{
|
||||
public GenreMenuComponent(MusicStoreContext dbContext)
|
||||
{
|
||||
DbContext = dbContext;
|
||||
}
|
||||
|
||||
private MusicStoreContext DbContext { get; }
|
||||
|
||||
public async Task<IViewComponentResult> InvokeAsync()
|
||||
{
|
||||
// TODO use nested sum https://github.com/aspnet/EntityFramework/issues/3792
|
||||
//.OrderByDescending(
|
||||
// g => g.Albums.Sum(a => a.OrderDetails.Sum(od => od.Quantity)))
|
||||
|
||||
var genres = await DbContext.Genres.Select(g => g.Name).Take(9).ToListAsync();
|
||||
|
||||
return View(genres);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstracts the system clock to facilitate testing.
|
||||
/// </summary>
|
||||
public interface ISystemClock
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a DateTime object that is set to the current date and time on this computer,
|
||||
/// expressed as the Coordinated Universal Time(UTC)
|
||||
/// </summary>
|
||||
DateTime UtcNow { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to the normal system clock.
|
||||
/// </summary>
|
||||
public class SystemClock : ISystemClock
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DateTime UtcNow
|
||||
{
|
||||
get
|
||||
{
|
||||
// The clock measures whole seconds only, and truncates the milliseconds,
|
||||
// because millisecond resolution is inconsistent among various underlying systems.
|
||||
DateTime utcNow = DateTime.UtcNow;
|
||||
return utcNow.AddMilliseconds(-utcNow.Millisecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,512 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class AccountController : Controller
|
||||
{
|
||||
private readonly ILogger<AccountController> _logger;
|
||||
|
||||
public AccountController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
ILogger<AccountController> logger)
|
||||
{
|
||||
UserManager = userManager;
|
||||
SignInManager = signInManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public UserManager<ApplicationUser> UserManager { get; }
|
||||
|
||||
public SignInManager<ApplicationUser> SignInManager { get; }
|
||||
|
||||
//
|
||||
// GET: /Account/Login
|
||||
[AllowAnonymous]
|
||||
public IActionResult Login(string returnUrl = null)
|
||||
{
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Login
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// This doesn't count login failures towards account lockout
|
||||
// To enable password failures to trigger account lockout, change to lockoutOnFailure: true
|
||||
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("Logged in {userName}.", model.Email);
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
{
|
||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
return View("Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("Failed to log in {userName}.", model.Email);
|
||||
ModelState.AddModelError("", "Invalid login attempt.");
|
||||
return View(model);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/VerifyCode
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> VerifyCode(string provider, bool rememberMe, string returnUrl = null)
|
||||
{
|
||||
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
// Remove before production
|
||||
#if DEMO
|
||||
if (user != null)
|
||||
{
|
||||
ViewBag.Code = await UserManager.GenerateTwoFactorTokenAsync(user, provider);
|
||||
}
|
||||
#endif
|
||||
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/VerifyCode
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// The following code protects for brute force attacks against the two factor codes.
|
||||
// If a user enters incorrect codes for a specified amount of time then the user account
|
||||
// will be locked out for a specified amount of time.
|
||||
// You can configure the account lockout settings in IdentityConfig
|
||||
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToLocal(model.ReturnUrl);
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
return View("Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError("", "Invalid code.");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Register
|
||||
[AllowAnonymous]
|
||||
public IActionResult Register()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Register
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Register(RegisterViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
|
||||
var result = await UserManager.CreateAsync(user, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User {userName} was created.", model.Email);
|
||||
//Bug: Remember browser option missing?
|
||||
//Uncomment this and comment the later part if account verification is not needed.
|
||||
//await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
|
||||
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
|
||||
// Send an email with this link
|
||||
string code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
|
||||
await MessageServices.SendEmailAsync(model.Email, "Confirm your account",
|
||||
"Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
|
||||
#if !DEMO
|
||||
return RedirectToAction("Index", "Home");
|
||||
#else
|
||||
//To display the email link in a friendly page instead of sending email
|
||||
ViewBag.Link = callbackUrl;
|
||||
return View("DemoLinkDisplay");
|
||||
#endif
|
||||
|
||||
}
|
||||
AddErrors(result);
|
||||
}
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ConfirmEmail
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> ConfirmEmail(string userId, string code)
|
||||
{
|
||||
if (userId == null || code == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
var user = await UserManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var result = await UserManager.ConfirmEmailAsync(user, code);
|
||||
return View(result.Succeeded ? "ConfirmEmail" : "Error");
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ForgotPassword
|
||||
[AllowAnonymous]
|
||||
public ActionResult ForgotPassword()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ForgotPassword
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var user = await UserManager.FindByNameAsync(model.Email);
|
||||
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user)))
|
||||
{
|
||||
// Don't reveal that the user does not exist or is not confirmed
|
||||
return View("ForgotPasswordConfirmation");
|
||||
}
|
||||
|
||||
// For more information on how to enable account confirmation and password reset please visit http://go.microsoft.com/fwlink/?LinkID=320771
|
||||
// Send an email with this link
|
||||
string code = await UserManager.GeneratePasswordResetTokenAsync(user);
|
||||
var callbackUrl = Url.Action("ResetPassword", "Account", new { code = code }, protocol: HttpContext.Request.Scheme);
|
||||
await MessageServices.SendEmailAsync(model.Email, "Reset Password",
|
||||
"Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
|
||||
#if !DEMO
|
||||
return RedirectToAction("ForgotPasswordConfirmation");
|
||||
#else
|
||||
//To display the email link in a friendly page instead of sending email
|
||||
ViewBag.Link = callbackUrl;
|
||||
return View("DemoLinkDisplay");
|
||||
#endif
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", string.Format("We could not locate an account with email : {0}", model.Email));
|
||||
|
||||
// If we got this far, something failed, redisplay form
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ForgotPasswordConfirmation
|
||||
[AllowAnonymous]
|
||||
public ActionResult ForgotPasswordConfirmation()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ResetPassword
|
||||
[AllowAnonymous]
|
||||
public ActionResult ResetPassword(string code)
|
||||
{
|
||||
//TODO: Fix this?
|
||||
var resetPasswordViewModel = new ResetPasswordViewModel() { Code = code };
|
||||
return code == null ? View("Error") : View(resetPasswordViewModel);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ResetPassword
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
var user = await UserManager.FindByNameAsync(model.Email);
|
||||
if (user == null)
|
||||
{
|
||||
// Don't reveal that the user does not exist
|
||||
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||
}
|
||||
var result = await UserManager.ResetPasswordAsync(user, model.Code, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToAction("ResetPasswordConfirmation", "Account");
|
||||
}
|
||||
AddErrors(result);
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ResetPasswordConfirmation
|
||||
[AllowAnonymous]
|
||||
public ActionResult ResetPasswordConfirmation()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ExternalLogin
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult ExternalLogin(string provider, string returnUrl = null)
|
||||
{
|
||||
// Request a redirect to the external login provider
|
||||
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
|
||||
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
|
||||
return new ChallengeResult(provider, properties);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/SendCode
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> SendCode(bool rememberMe, string returnUrl = null)
|
||||
{
|
||||
//TODO : Default rememberMe as well?
|
||||
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(user);
|
||||
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
|
||||
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/SendCode
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> SendCode(SendCodeViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
// Generate the token and send it
|
||||
var code = await UserManager.GenerateTwoFactorTokenAsync(user, model.SelectedProvider);
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
var message = "Your security code is: " + code;
|
||||
if (model.SelectedProvider == "Email")
|
||||
{
|
||||
await MessageServices.SendEmailAsync(await UserManager.GetEmailAsync(user), "Security Code", message);
|
||||
}
|
||||
else if (model.SelectedProvider == "Phone")
|
||||
{
|
||||
await MessageServices.SendSmsAsync(await UserManager.GetPhoneNumberAsync(user), message);
|
||||
}
|
||||
|
||||
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ExternalLoginCallback
|
||||
[AllowAnonymous]
|
||||
public async Task<ActionResult> ExternalLoginCallback(string returnUrl = null)
|
||||
{
|
||||
var loginInfo = await SignInManager.GetExternalLoginInfoAsync();
|
||||
if (loginInfo == null)
|
||||
{
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
// Sign in the user with this external login provider if the user already has a login
|
||||
var result = await SignInManager.ExternalLoginSignInAsync(loginInfo.LoginProvider, loginInfo.ProviderKey, isPersistent: false);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
if (result.RequiresTwoFactor)
|
||||
{
|
||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
|
||||
}
|
||||
if (result.IsLockedOut)
|
||||
{
|
||||
return View("Lockout");
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the user does not have an account, then prompt the user to create an account
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
ViewBag.LoginProvider = loginInfo.LoginProvider;
|
||||
// REVIEW: handle case where email not in claims?
|
||||
var email = loginInfo.Principal.FindFirstValue(ClaimTypes.Email);
|
||||
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/ExternalLoginConfirmation
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl = null)
|
||||
{
|
||||
if (SignInManager.IsSignedIn(User))
|
||||
{
|
||||
return RedirectToAction("Index", "Manage");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// Get the information about the user from the external login provider
|
||||
var info = await SignInManager.GetExternalLoginInfoAsync();
|
||||
if (info == null)
|
||||
{
|
||||
return View("ExternalLoginFailure");
|
||||
}
|
||||
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
|
||||
var result = await UserManager.CreateAsync(user);
|
||||
|
||||
// NOTE: Used for end to end testing only
|
||||
//Just for automated testing adding a claim named 'ManageStore' - Not required for production
|
||||
var manageClaim = info.Principal.Claims.Where(c => c.Type == "ManageStore").FirstOrDefault();
|
||||
if (manageClaim != null)
|
||||
{
|
||||
await UserManager.AddClaimAsync(user, manageClaim);
|
||||
}
|
||||
|
||||
if (result.Succeeded)
|
||||
{
|
||||
result = await UserManager.AddLoginAsync(user, info);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
}
|
||||
AddErrors(result);
|
||||
}
|
||||
|
||||
ViewBag.ReturnUrl = returnUrl;
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/LogOff
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<ActionResult> LogOff()
|
||||
{
|
||||
var userName = HttpContext.User.Identity.Name;
|
||||
// clear all items from the cart
|
||||
HttpContext.Session.Clear();
|
||||
|
||||
await SignInManager.SignOutAsync();
|
||||
|
||||
// TODO: Currently SignInManager.SignOut does not sign out OpenIdc and does not have a way to pass in a specific
|
||||
// AuthType to sign out.
|
||||
var appEnv = HttpContext.RequestServices.GetService<IWebHostEnvironment>();
|
||||
if (appEnv.EnvironmentName.StartsWith("OpenIdConnect"))
|
||||
{
|
||||
return new SignOutResult("OpenIdConnect", new AuthenticationProperties
|
||||
{
|
||||
RedirectUri = Url.Action("Index", "Home")
|
||||
});
|
||||
}
|
||||
|
||||
_logger.LogInformation("{userName} logged out.", userName);
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/ExternalLoginFailure
|
||||
[AllowAnonymous]
|
||||
public ActionResult ExternalLoginFailure()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private void AddErrors(IdentityResult result)
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError("", error.Description);
|
||||
_logger.LogWarning("Error in creating user: {error}", error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
private Task<ApplicationUser> GetCurrentUserAsync()
|
||||
{
|
||||
return UserManager.GetUserAsync(HttpContext.User);
|
||||
}
|
||||
|
||||
private ActionResult RedirectToLocal(string returnUrl)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class CheckoutController : Controller
|
||||
{
|
||||
private const string PromoCode = "FREE";
|
||||
|
||||
private readonly ILogger<CheckoutController> _logger;
|
||||
|
||||
public CheckoutController(ILogger<CheckoutController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Checkout/
|
||||
public IActionResult AddressAndPayment()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Checkout/AddressAndPayment
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AddressAndPayment(
|
||||
[FromServices] MusicStoreContext dbContext,
|
||||
[FromForm] Order order,
|
||||
CancellationToken requestAborted)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(order);
|
||||
}
|
||||
|
||||
var formCollection = await HttpContext.Request.ReadFormAsync();
|
||||
|
||||
try
|
||||
{
|
||||
if (string.Equals(formCollection["PromoCode"].FirstOrDefault(), PromoCode,
|
||||
StringComparison.OrdinalIgnoreCase) == false)
|
||||
{
|
||||
return View(order);
|
||||
}
|
||||
else
|
||||
{
|
||||
order.Username = HttpContext.User.Identity.Name;
|
||||
order.OrderDate = DateTime.Now;
|
||||
|
||||
//Add the Order
|
||||
dbContext.Orders.Add(order);
|
||||
|
||||
//Process the order
|
||||
var cart = ShoppingCart.GetCart(dbContext, HttpContext);
|
||||
await cart.CreateOrder(order);
|
||||
|
||||
// Save all changes
|
||||
await dbContext.SaveChangesAsync(requestAborted);
|
||||
|
||||
_logger.LogInformation("User {userName} started checkout of {orderId}.", order.Username, order.OrderId);
|
||||
|
||||
return RedirectToAction("Complete", new { id = order.OrderId });
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Checkout failed");
|
||||
//Invalid - redisplay with errors
|
||||
return View(order);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Checkout/Complete
|
||||
|
||||
public async Task<IActionResult> Complete(
|
||||
[FromServices] MusicStoreContext dbContext,
|
||||
int id)
|
||||
{
|
||||
var userName = HttpContext.User.Identity.Name;
|
||||
|
||||
// Validate customer owns this order
|
||||
bool isValid = await dbContext.Orders.AnyAsync(
|
||||
o => o.OrderId == id &&
|
||||
o.Username == userName);
|
||||
|
||||
if (isValid)
|
||||
{
|
||||
_logger.LogInformation("User {userName} completed checkout on order {orderId}.", userName, id);
|
||||
return View(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError(
|
||||
"User {userName} tried to checkout with an order ({orderId}) that doesn't belong to them.",
|
||||
userName,
|
||||
id);
|
||||
return View("Error");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public HomeController(IOptions<AppSettings> options)
|
||||
{
|
||||
_appSettings = options.Value;
|
||||
}
|
||||
//
|
||||
// GET: /Home/
|
||||
public async Task<IActionResult> Index(
|
||||
[FromServices] MusicStoreContext dbContext,
|
||||
[FromServices] IMemoryCache cache)
|
||||
{
|
||||
// Get most popular albums
|
||||
var cacheKey = "topselling";
|
||||
List<Album> albums;
|
||||
if (!cache.TryGetValue(cacheKey, out albums))
|
||||
{
|
||||
albums = await GetTopSellingAlbumsAsync(dbContext, 6);
|
||||
|
||||
if (albums != null && albums.Count > 0)
|
||||
{
|
||||
if (_appSettings.CacheDbResults)
|
||||
{
|
||||
// Refresh it every 10 minutes.
|
||||
// Let this be the last item to be removed by cache if cache GC kicks in.
|
||||
cache.Set(
|
||||
cacheKey,
|
||||
albums,
|
||||
new MemoryCacheEntryOptions()
|
||||
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10))
|
||||
.SetPriority(CacheItemPriority.High));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return View(albums);
|
||||
}
|
||||
|
||||
public IActionResult Error()
|
||||
{
|
||||
return View("~/Views/Shared/Error.cshtml");
|
||||
}
|
||||
|
||||
public IActionResult StatusCodePage()
|
||||
{
|
||||
return View("~/Views/Shared/StatusCodePage.cshtml");
|
||||
}
|
||||
|
||||
public IActionResult AccessDenied()
|
||||
{
|
||||
return View("~/Views/Shared/AccessDenied.cshtml");
|
||||
}
|
||||
|
||||
private Task<List<Album>> GetTopSellingAlbumsAsync(MusicStoreContext dbContext, int count)
|
||||
{
|
||||
// Group the order details by album and return
|
||||
// the albums with the highest count
|
||||
|
||||
return dbContext.Albums
|
||||
.OrderByDescending(a => a.OrderDetails.Count)
|
||||
.Take(count)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,365 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class ManageController : Controller
|
||||
{
|
||||
public ManageController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
IAuthenticationSchemeProvider schemes)
|
||||
{
|
||||
UserManager = userManager;
|
||||
SignInManager = signInManager;
|
||||
SchemeProvider = schemes;
|
||||
}
|
||||
|
||||
public UserManager<ApplicationUser> UserManager { get; }
|
||||
|
||||
public SignInManager<ApplicationUser> SignInManager { get; }
|
||||
|
||||
public IAuthenticationSchemeProvider SchemeProvider { get; }
|
||||
|
||||
//
|
||||
// GET: /Manage/Index
|
||||
public async Task<ActionResult> Index(ManageMessageId? message = null)
|
||||
{
|
||||
ViewBag.StatusMessage =
|
||||
message == ManageMessageId.ChangePasswordSuccess ? "Your password has been changed."
|
||||
: message == ManageMessageId.SetPasswordSuccess ? "Your password has been set."
|
||||
: message == ManageMessageId.SetTwoFactorSuccess ? "Your two-factor authentication provider has been set."
|
||||
: message == ManageMessageId.Error ? "An error has occurred."
|
||||
: message == ManageMessageId.AddPhoneSuccess ? "Your phone number was added."
|
||||
: message == ManageMessageId.RemovePhoneSuccess ? "Your phone number was removed."
|
||||
: "";
|
||||
|
||||
var user = await GetCurrentUserAsync();
|
||||
var model = new IndexViewModel
|
||||
{
|
||||
HasPassword = await UserManager.HasPasswordAsync(user),
|
||||
PhoneNumber = await UserManager.GetPhoneNumberAsync(user),
|
||||
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(user),
|
||||
Logins = await UserManager.GetLoginsAsync(user),
|
||||
BrowserRemembered = await SignInManager.IsTwoFactorClientRememberedAsync(user)
|
||||
};
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/RemoveLogin
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RemoveLogin(string loginProvider, string providerKey)
|
||||
{
|
||||
ManageMessageId? message = ManageMessageId.Error;
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
var result = await UserManager.RemoveLoginAsync(user, loginProvider, providerKey);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
message = ManageMessageId.RemoveLoginSuccess;
|
||||
}
|
||||
}
|
||||
return RedirectToAction("ManageLogins", new { Message = message });
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/AddPhoneNumber
|
||||
public IActionResult AddPhoneNumber()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/AddPhoneNumber
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
var user = await GetCurrentUserAsync();
|
||||
// Generate the token and send it
|
||||
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(user, model.Number);
|
||||
await MessageServices.SendSmsAsync(model.Number, "Your security code is: " + code);
|
||||
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/EnableTwoFactorAuthentication
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EnableTwoFactorAuthentication()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
await UserManager.SetTwoFactorEnabledAsync(user, true);
|
||||
// TODO: flow remember me somehow?
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
}
|
||||
return RedirectToAction("Index", "Manage");
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/DisableTwoFactorAuthentication
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DisableTwoFactorAuthentication()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
await UserManager.SetTwoFactorEnabledAsync(user, false);
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
}
|
||||
return RedirectToAction("Index", "Manage");
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/VerifyPhoneNumber
|
||||
public async Task<IActionResult> VerifyPhoneNumber(string phoneNumber)
|
||||
{
|
||||
// This code allows you exercise the flow without actually sending codes
|
||||
// For production use please register a SMS provider in IdentityConfig and generate a code here.
|
||||
#if DEMO
|
||||
ViewBag.Code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), phoneNumber);
|
||||
#endif
|
||||
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/VerifyPhoneNumber
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
var result = await UserManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
|
||||
}
|
||||
}
|
||||
// If we got this far, something failed, redisplay form
|
||||
ModelState.AddModelError("", "Failed to verify phone");
|
||||
return View(model);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/RemovePhoneNumber
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RemovePhoneNumber()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
var result = await UserManager.SetPhoneNumberAsync(user, null);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.RemovePhoneSuccess });
|
||||
}
|
||||
}
|
||||
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.Error });
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Manage/ChangePassword
|
||||
public IActionResult ChangePassword()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Account/Manage
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
|
||||
}
|
||||
AddErrors(result);
|
||||
return View(model);
|
||||
}
|
||||
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Manage/SetPassword
|
||||
public IActionResult SetPassword()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/SetPassword
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> SetPassword(SetPasswordViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
var result = await UserManager.AddPasswordAsync(user, model.NewPassword);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
|
||||
}
|
||||
AddErrors(result);
|
||||
return View(model);
|
||||
}
|
||||
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/RememberBrowser
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RememberBrowser()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user != null)
|
||||
{
|
||||
await SignInManager.RememberTwoFactorClientAsync(user);
|
||||
await SignInManager.SignInAsync(user, isPersistent: false);
|
||||
}
|
||||
return RedirectToAction("Index", "Manage");
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/ForgetBrowser
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> ForgetBrowser()
|
||||
{
|
||||
await SignInManager.ForgetTwoFactorClientAsync();
|
||||
return RedirectToAction("Index", "Manage");
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Account/Manage
|
||||
public async Task<IActionResult> ManageLogins(ManageMessageId? message = null)
|
||||
{
|
||||
ViewBag.StatusMessage =
|
||||
message == ManageMessageId.RemoveLoginSuccess ? "The external login was removed."
|
||||
: message == ManageMessageId.AddLoginSuccess ? "The external login was added."
|
||||
: message == ManageMessageId.Error ? "An error has occurred."
|
||||
: "";
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
var userLogins = await UserManager.GetLoginsAsync(user);
|
||||
var schemes = await SchemeProvider.GetAllSchemesAsync();
|
||||
var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
|
||||
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
|
||||
return View(new ManageLoginsViewModel
|
||||
{
|
||||
CurrentLogins = userLogins,
|
||||
OtherLogins = otherLogins
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// POST: /Manage/LinkLogin
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult LinkLogin(string provider)
|
||||
{
|
||||
// Request a redirect to the external login provider to link a login for the current user
|
||||
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
|
||||
var properties = SignInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, UserManager.GetUserId(User));
|
||||
return new ChallengeResult(provider, properties);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Manage/LinkLoginCallback
|
||||
public async Task<ActionResult> LinkLoginCallback()
|
||||
{
|
||||
var user = await GetCurrentUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
return View("Error");
|
||||
}
|
||||
|
||||
var loginInfo = await SignInManager.GetExternalLoginInfoAsync(await UserManager.GetUserIdAsync(user));
|
||||
if (loginInfo == null)
|
||||
{
|
||||
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
|
||||
}
|
||||
|
||||
var result = await UserManager.AddLoginAsync(user, loginInfo);
|
||||
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
|
||||
return RedirectToAction("ManageLogins", new { Message = message });
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
private void AddErrors(IdentityResult result)
|
||||
{
|
||||
foreach (var error in result.Errors)
|
||||
{
|
||||
ModelState.AddModelError("", error.Description);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ManageMessageId
|
||||
{
|
||||
AddPhoneSuccess,
|
||||
AddLoginSuccess,
|
||||
ChangePasswordSuccess,
|
||||
SetTwoFactorSuccess,
|
||||
SetPasswordSuccess,
|
||||
RemoveLoginSuccess,
|
||||
RemovePhoneSuccess,
|
||||
Error
|
||||
}
|
||||
|
||||
private Task<ApplicationUser> GetCurrentUserAsync()
|
||||
{
|
||||
return UserManager.GetUserAsync(HttpContext.User);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MusicStore.Models;
|
||||
using MusicStore.ViewModels;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
public class ShoppingCartController : Controller
|
||||
{
|
||||
private readonly ILogger<ShoppingCartController> _logger;
|
||||
|
||||
public ShoppingCartController(MusicStoreContext dbContext, ILogger<ShoppingCartController> logger)
|
||||
{
|
||||
DbContext = dbContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public MusicStoreContext DbContext { get; }
|
||||
|
||||
//
|
||||
// GET: /ShoppingCart/
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var cart = ShoppingCart.GetCart(DbContext, HttpContext);
|
||||
|
||||
// Set up our ViewModel
|
||||
var viewModel = new ShoppingCartViewModel
|
||||
{
|
||||
CartItems = await cart.GetCartItems(),
|
||||
CartTotal = await cart.GetTotal()
|
||||
};
|
||||
|
||||
// Return the view
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /ShoppingCart/AddToCart/5
|
||||
|
||||
public async Task<IActionResult> AddToCart(int id, CancellationToken requestAborted)
|
||||
{
|
||||
// Retrieve the album from the database
|
||||
var addedAlbum = await DbContext.Albums
|
||||
.SingleAsync(album => album.AlbumId == id);
|
||||
|
||||
// Add it to the shopping cart
|
||||
var cart = ShoppingCart.GetCart(DbContext, HttpContext);
|
||||
|
||||
await cart.AddToCart(addedAlbum);
|
||||
|
||||
await DbContext.SaveChangesAsync(requestAborted);
|
||||
_logger.LogInformation("Album {albumId} was added to the cart.", addedAlbum.AlbumId);
|
||||
|
||||
// Go back to the main store page for more shopping
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX: /ShoppingCart/RemoveFromCart/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RemoveFromCart(
|
||||
int id,
|
||||
CancellationToken requestAborted)
|
||||
{
|
||||
// Retrieve the current user's shopping cart
|
||||
var cart = ShoppingCart.GetCart(DbContext, HttpContext);
|
||||
|
||||
// Get the name of the album to display confirmation
|
||||
var cartItem = await DbContext.CartItems
|
||||
.Where(item => item.CartItemId == id)
|
||||
.Include(c => c.Album)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
string message;
|
||||
int itemCount;
|
||||
if (cartItem != null)
|
||||
{
|
||||
// Remove from cart
|
||||
itemCount = cart.RemoveFromCart(id);
|
||||
|
||||
await DbContext.SaveChangesAsync(requestAborted);
|
||||
|
||||
string removed = (itemCount > 0) ? " 1 copy of " : string.Empty;
|
||||
message = removed + cartItem.Album.Title + " has been removed from your shopping cart.";
|
||||
}
|
||||
else
|
||||
{
|
||||
itemCount = 0;
|
||||
message = "Could not find this item, nothing has been removed from your shopping cart.";
|
||||
}
|
||||
|
||||
// Display the confirmation message
|
||||
|
||||
var results = new ShoppingCartRemoveViewModel
|
||||
{
|
||||
Message = message,
|
||||
CartTotal = await cart.GetTotal(),
|
||||
CartCount = await cart.GetCount(),
|
||||
ItemCount = itemCount,
|
||||
DeleteId = id
|
||||
};
|
||||
|
||||
_logger.LogInformation("Album {id} was removed from a cart.", id);
|
||||
|
||||
return Json(results);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.Controllers
|
||||
{
|
||||
public class StoreController : Controller
|
||||
{
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public StoreController(MusicStoreContext dbContext, IOptions<AppSettings> options)
|
||||
{
|
||||
DbContext = dbContext;
|
||||
_appSettings = options.Value;
|
||||
}
|
||||
|
||||
public MusicStoreContext DbContext { get; }
|
||||
|
||||
//
|
||||
// GET: /Store/
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var genres = await DbContext.Genres.ToListAsync();
|
||||
|
||||
return View(genres);
|
||||
}
|
||||
|
||||
//
|
||||
// GET: /Store/Browse?genre=Disco
|
||||
public async Task<IActionResult> Browse(string genre)
|
||||
{
|
||||
// Retrieve Genre genre and its Associated associated Albums albums from database
|
||||
var genreModel = await DbContext.Genres
|
||||
.Include(g => g.Albums)
|
||||
.Where(g => g.Name == genre)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (genreModel == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(genreModel);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Details(
|
||||
[FromServices] IMemoryCache cache,
|
||||
int id)
|
||||
{
|
||||
var cacheKey = string.Format("album_{0}", id);
|
||||
Album album;
|
||||
if (!cache.TryGetValue(cacheKey, out album))
|
||||
{
|
||||
album = await DbContext.Albums
|
||||
.Where(a => a.AlbumId == id)
|
||||
.Include(a => a.Artist)
|
||||
.Include(a => a.Genre)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (album != null)
|
||||
{
|
||||
if (_appSettings.CacheDbResults)
|
||||
{
|
||||
//Remove it from cache if not retrieved in last 10 minutes
|
||||
cache.Set(
|
||||
cacheKey,
|
||||
album,
|
||||
new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(10)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (album == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
return View(album);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MusicStore.Mocks.Common
|
||||
{
|
||||
public class CustomStateDataFormat : ISecureDataFormat<AuthenticationProperties>
|
||||
{
|
||||
private static string _lastSavedAuthenticationProperties;
|
||||
|
||||
public string Protect(AuthenticationProperties data, string purose)
|
||||
{
|
||||
return Protect(data);
|
||||
}
|
||||
|
||||
public string Protect(AuthenticationProperties data)
|
||||
{
|
||||
_lastSavedAuthenticationProperties = Serialize(data);
|
||||
return "ValidStateData";
|
||||
}
|
||||
|
||||
public AuthenticationProperties Unprotect(string state, string purpose)
|
||||
{
|
||||
return Unprotect(state);
|
||||
}
|
||||
|
||||
public AuthenticationProperties Unprotect(string state)
|
||||
{
|
||||
return state == "ValidStateData" ? DeSerialize(_lastSavedAuthenticationProperties) : null;
|
||||
}
|
||||
|
||||
private string Serialize(AuthenticationProperties data)
|
||||
{
|
||||
return JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
}
|
||||
|
||||
private AuthenticationProperties DeSerialize(string state)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<AuthenticationProperties>(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace MusicStore.Mocks.Common
|
||||
{
|
||||
internal class Helpers
|
||||
{
|
||||
internal static void ThrowIfConditionFailed(Func<bool> condition, string errorMessage)
|
||||
{
|
||||
if (!condition())
|
||||
{
|
||||
throw new Exception(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.Facebook
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for FacebookMockBackChannelHttpHandler
|
||||
/// </summary>
|
||||
public class FacebookMockBackChannelHttpHandler : HttpMessageHandler
|
||||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://graph.facebook.com/v2.6/oauth/access_token"))
|
||||
{
|
||||
var formData = new FormCollection(await new FormReader(await request.Content.ReadAsStreamAsync()).ReadFormAsync());
|
||||
if (formData["grant_type"] == "authorization_code")
|
||||
{
|
||||
if (formData["code"] == "ValidCode")
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => ((string)formData["redirect_uri"]).EndsWith("signin-facebook"), "Redirect URI is not ending with /signin-facebook");
|
||||
Helpers.ThrowIfConditionFailed(() => formData["client_id"] == "[AppId]", "Invalid client Id received");
|
||||
Helpers.ThrowIfConditionFailed(() => formData["client_secret"] == "[AppSecret]", "Invalid client secret received");
|
||||
response.Content = new StringContent("{ \"access_token\": \"ValidAccessToken\", \"expires_in\": \"100\" }");
|
||||
return response;
|
||||
}
|
||||
response.StatusCode = (HttpStatusCode)400;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://graph.facebook.com/v2.6/me"))
|
||||
{
|
||||
var queryParameters = new QueryCollection(QueryHelpers.ParseQuery(request.RequestUri.Query));
|
||||
Helpers.ThrowIfConditionFailed(() => queryParameters["appsecret_proof"].Count > 0, "appsecret_proof is empty");
|
||||
if (queryParameters["access_token"] == "ValidAccessToken")
|
||||
{
|
||||
response.Content = new StringContent("{\"id\":\"Id\",\"name\":\"AspnetvnextTest AspnetvnextTest\",\"first_name\":\"AspnetvnextTest\",\"last_name\":\"AspnetvnextTest\",\"link\":\"https:\\/\\/www.facebook.com\\/myLink\",\"username\":\"AspnetvnextTest.AspnetvnextTest.7\",\"gender\":\"male\",\"email\":\"AspnetvnextTest\\u0040test.com\",\"timezone\":-7,\"locale\":\"en_US\",\"verified\":true,\"updated_time\":\"2013-08-06T20:38:48+0000\",\"CertValidatorInvoked\":\"ValidAccessToken\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Content = new StringContent("{\"error\":{\"message\":\"Invalid OAuth access token.\",\"type\":\"OAuthException\",\"code\":190}}");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Facebook;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.Facebook
|
||||
{
|
||||
internal class TestFacebookEvents
|
||||
{
|
||||
internal static Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
||||
{
|
||||
if (context.Principal != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Email)?.Value == "AspnetvnextTest@test.com", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "Id", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst("urn:facebook:link")?.Value == "https://www.facebook.com/myLink", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.User.GetString("id") == context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value, "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(100), "");
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "");
|
||||
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task OnTicketReceived(TicketReceivedContext context)
|
||||
{
|
||||
if (context.Principal != null && context.Options.SignInScheme == IdentityConstants.ExternalScheme)
|
||||
{
|
||||
//This way we will know all events were fired.
|
||||
var identity = context.Principal.Identities.First();
|
||||
var manageStoreClaim = identity?.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
identity.RemoveClaim(manageStoreClaim);
|
||||
identity.AddClaim(new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task RedirectToAuthorizationEndpoint(RedirectContext<OAuthOptions> context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace MusicStore.Mocks.Google
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for GoogleMockBackChannelHttpHandler
|
||||
/// </summary>
|
||||
public class GoogleMockBackChannelHttpHandler : HttpMessageHandler
|
||||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://www.googleapis.com/oauth2/v4/token"))
|
||||
{
|
||||
var formData = new FormCollection(await new FormReader(await request.Content.ReadAsStreamAsync()).ReadFormAsync());
|
||||
if (formData["grant_type"] == "authorization_code")
|
||||
{
|
||||
if (formData["code"] == "ValidCode")
|
||||
{
|
||||
if (formData["redirect_uri"].Count > 0 && ((string)formData["redirect_uri"]).EndsWith("signin-google") &&
|
||||
formData["client_id"] == "[ClientId]" && formData["client_secret"] == "[ClientSecret]")
|
||||
{
|
||||
response.Content = new StringContent("{\"access_token\":\"ValidAccessToken\",\"refresh_token\":\"ValidRefreshToken\",\"token_type\":\"Bearer\",\"expires_in\":\"1200\",\"id_token\":\"Token\"}", Encoding.UTF8, "application/json");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
response.StatusCode = (HttpStatusCode)400;
|
||||
return response;
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://www.googleapis.com/plus/v1/people/me"))
|
||||
{
|
||||
if (request.Headers.Authorization.Parameter == "ValidAccessToken")
|
||||
{
|
||||
response.Content = new StringContent("{ \"kind\": \"plus#person\",\n \"etag\": \"\\\"YFr-hUROXQN7IOa3dUHg9dQ8eq0/2hY18HdHEP8NLykSTVEiAhkKsBE\\\"\",\n \"gender\": \"male\",\n \"emails\": [\n {\n \"value\": \"AspnetvnextTest@gmail.com\",\n \"type\": \"account\"\n }\n ],\n \"objectType\": \"person\",\n \"id\": \"106790274378320830963\",\n \"displayName\": \"AspnetvnextTest AspnetvnextTest\",\n \"name\": {\n \"familyName\": \"AspnetvnextTest\",\n \"givenName\": \"FirstName\"\n },\n \"url\": \"https://plus.google.com/106790274378320830963\",\n \"image\": {\n \"url\": \"https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg?sz=50\"\n },\n \"isPlusUser\": true,\n \"language\": \"en\",\n \"circledByCount\": 0,\n \"verified\": false\n}\n", Encoding.UTF8, "application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Content = new StringContent("{\"error\":{\"message\":\"Invalid OAuth access token.\",\"type\":\"OAuthException\",\"code\":190}}");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Google;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.Google
|
||||
{
|
||||
internal class TestGoogleEvents
|
||||
{
|
||||
internal static Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
||||
{
|
||||
if (context.Principal != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "Access token is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.RefreshToken == "ValidRefreshToken", "Refresh token is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Email)?.Value == "AspnetvnextTest@gmail.com", "Email is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "106790274378320830963", "Id is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Surname)?.Value == "AspnetvnextTest", "FamilyName is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "Name is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(1200), "ExpiresIn is not valid");
|
||||
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task OnTicketReceived(TicketReceivedContext context)
|
||||
{
|
||||
if (context.Principal != null && context.Options.SignInScheme == IdentityConstants.ExternalScheme)
|
||||
{
|
||||
//This way we will know all events were fired.
|
||||
var identity = context.Principal.Identities.First();
|
||||
var manageStoreClaim = identity?.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
identity.RemoveClaim(manageStoreClaim);
|
||||
identity.AddClaim(new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task RedirectToAuthorizationEndpoint(RedirectContext<OAuthOptions> context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace MusicStore.Mocks.MicrosoftAccount
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for MicrosoftAccountMockBackChannelHandler
|
||||
/// </summary>
|
||||
public class MicrosoftAccountMockBackChannelHandler : HttpMessageHandler
|
||||
{
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://login.microsoftonline.com/common/oauth2/v2.0/token"))
|
||||
{
|
||||
var formData = new FormCollection(await new FormReader(await request.Content.ReadAsStreamAsync()).ReadFormAsync());
|
||||
if (formData["grant_type"] == "authorization_code")
|
||||
{
|
||||
if (formData["code"] == "ValidCode")
|
||||
{
|
||||
if (formData["redirect_uri"].Count > 0 && ((string)formData["redirect_uri"]).EndsWith("signin-microsoft") &&
|
||||
formData["client_id"] == "[ClientId]" && formData["client_secret"] == "[ClientSecret]")
|
||||
{
|
||||
response.Content = new StringContent("{\"token_type\":\"bearer\",\"expires_in\":3600,\"scope\":\"https://graph.microsoft.com/user.read\",\"access_token\":\"ValidAccessToken\",\"refresh_token\":\"ValidRefreshToken\",\"authentication_token\":\"ValidAuthenticationToken\"}");
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.StatusCode = (HttpStatusCode)400;
|
||||
return response;
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://graph.microsoft.com/v1.0/me"))
|
||||
{
|
||||
if (request.Headers.Authorization.Parameter == "ValidAccessToken")
|
||||
{
|
||||
response.Content = new StringContent("{\r \"id\": \"fccf9a24999f4f4f\", \r \"displayName\": \"AspnetvnextTest AspnetvnextTest\", \r \"givenName\": \"AspnetvnextTest\", \r \"surname\": \"AspnetvnextTest\", \r \"link\": \"https://profile.live.com/\", \r \"gender\": null, \r \"locale\": \"en_US\", \r \"updated_time\": \"2013-08-27T22:18:14+0000\"\r}");
|
||||
}
|
||||
else
|
||||
{
|
||||
response.Content = new StringContent("{\r \"error\": {\r \"code\": \"request_token_invalid\", \r \"message\": \"The access token isn't valid.\"\r }\r}", Encoding.UTF8, "text/javascript");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.MicrosoftAccount;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.MicrosoftAccount
|
||||
{
|
||||
internal class TestMicrosoftAccountEvents
|
||||
{
|
||||
internal static Task OnCreatingTicket(OAuthCreatingTicketContext context)
|
||||
{
|
||||
if (context.Principal != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "ValidAccessToken", "Access token is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.RefreshToken == "ValidRefreshToken", "Refresh token is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.GivenName)?.Value == "AspnetvnextTest", "Given name is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Surname)?.Value == "AspnetvnextTest", "Surname is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == "fccf9a24999f4f4f", "Id is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.Name)?.Value == "AspnetvnextTest AspnetvnextTest", "Name is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ExpiresIn.Value == TimeSpan.FromSeconds(3600), "ExpiresIn is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Identity.FindFirst(ClaimTypes.NameIdentifier)?.Value == context.User.GetString("id"), "User id is not valid");
|
||||
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task OnTicketReceived(TicketReceivedContext context)
|
||||
{
|
||||
if (context.Principal != null && context.Options.SignInScheme == IdentityConstants.ExternalScheme)
|
||||
{
|
||||
//This way we will know all events were fired.
|
||||
var identity = context.Principal.Identities.First();
|
||||
var manageStoreClaim = identity?.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
identity.RemoveClaim(manageStoreClaim);
|
||||
identity.AddClaim(new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task RedirectToAuthorizationEndpoint(RedirectContext<OAuthOptions> context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace MusicStore.Mocks.OpenIdConnect
|
||||
{
|
||||
internal class CustomStringDataFormat : ISecureDataFormat<string>
|
||||
{
|
||||
private const string _capturedNonce = "635579928639517715.OTRjOTVkM2EtMDRmYS00ZDE3LThhZGUtZWZmZGM4ODkzZGZkMDRlNDhkN2MtOWIwMC00ZmVkLWI5MTItMTUwYmQ4MzdmOWI0";
|
||||
|
||||
public string Protect(string data)
|
||||
{
|
||||
return "protectedString";
|
||||
}
|
||||
|
||||
public string Protect(string data, string purpose)
|
||||
{
|
||||
return purpose + "protectedString";
|
||||
}
|
||||
|
||||
public string Unprotect(string protectedText)
|
||||
{
|
||||
return protectedText == "protectedString" ? _capturedNonce : null;
|
||||
}
|
||||
|
||||
public string Unprotect(string protectedText, string purpose)
|
||||
{
|
||||
return protectedText == (purpose + "protectedString") ? _capturedNonce : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
|
||||
namespace MusicStore.Mocks.OpenIdConnect
|
||||
{
|
||||
internal class OpenIdConnectBackChannelHttpHandler : HttpMessageHandler
|
||||
{
|
||||
private IWebHostEnvironment _env;
|
||||
|
||||
public OpenIdConnectBackChannelHttpHandler(IWebHostEnvironment env)
|
||||
{
|
||||
_env = env;
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
|
||||
var basePath = Path.GetFullPath(Path.Combine(_env.ContentRootPath, "ForTesting", "Mocks", "OpenIdConnect"));
|
||||
|
||||
if (request.RequestUri.AbsoluteUri == "https://login.windows.net/[tenantName].onmicrosoft.com/.well-known/openid-configuration")
|
||||
{
|
||||
response.Content = new StringContent(File.ReadAllText(Path.Combine(basePath, "openid-configuration.json")));
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri == "https://login.windows.net/common/discovery/keys")
|
||||
{
|
||||
response.Content = new StringContent(File.ReadAllText(Path.Combine(basePath, "keys.json")));
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri == "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/oauth2/token")
|
||||
{
|
||||
response.Content = new StringContent("{\"id_token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtyaU1QZG1Cdng2OHNrVDgtbVBBQjNCc2VlQSJ9.eyJhdWQiOiJjOTk0OTdhYS0zZWUyLTQ3MDctYjhhOC1jMzNmNTEzMjNmZWYiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC80YWZiYzY4OS04MDViLTQ4Y2YtYTI0Yy1kNGFhMzI0OGEyNDgvIiwiaWF0IjoxNDIyMzk1NzYzLCJuYmYiOjE0MjIzOTU3NjMsImV4cCI6MTQyMjM5OTY2MywidmVyIjoiMS4wIiwidGlkIjoiNGFmYmM2ODktODA1Yi00OGNmLWEyNGMtZDRhYTMyNDhhMjQ4IiwiYW1yIjpbInB3ZCJdLCJvaWQiOiJmODc2YWJlYi1kNmI1LTQ0ZTQtOTcxNi02MjY2YWMwMTgxYTgiLCJ1cG4iOiJ1c2VyM0BwcmFidXJhamdtYWlsLm9ubWljcm9zb2Z0LmNvbSIsInN1YiI6IlBVZGhjbFA1UGdJalNVOVAxUy1IZWxEYVNGU2YtbVhWMVk2MC1LMnZXcXciLCJnaXZlbl9uYW1lIjoiVXNlcjMiLCJmYW1pbHlfbmFtZSI6IlVzZXIzIiwibmFtZSI6IlVzZXIzIiwidW5pcXVlX25hbWUiOiJ1c2VyM0BwcmFidXJhamdtYWlsLm9ubWljcm9zb2Z0LmNvbSIsIm5vbmNlIjoiNjM1NTc5OTI4NjM5NTE3NzE1Lk9UUmpPVFZrTTJFdE1EUm1ZUzAwWkRFM0xUaGhaR1V0WldabVpHTTRPRGt6Wkdaa01EUmxORGhrTjJNdE9XSXdNQzAwWm1Wa0xXSTVNVEl0TVRVd1ltUTRNemRtT1dJMCIsImNfaGFzaCI6IkZHdDN3Y1FBRGUwUFkxUXg3TzFyNmciLCJwd2RfZXhwIjoiNjY5MzI4MCIsInB3ZF91cmwiOiJodHRwczovL3BvcnRhbC5taWNyb3NvZnRvbmxpbmUuY29tL0NoYW5nZVBhc3N3b3JkLmFzcHgifQ.coAdCkdMgnslMHagdU8IBgH7Z0dilRdMfKytyqPJuTr6sbmbhrAoAj-KeGwbKgzrd-BeDk_rW47dntWuuAqGrAOGzxXvS2dcSWgoEKoXuDccIL5b4rIomRpfJpaeE-YwiU3usyRvoQCpHmtOa0g7xVilIj3_1-9ylMgRDY5qcrtQ_hEZlGuYyiCPR0dw8WmNU7r6PKObG-o3Yk_RbEBHjnaWxKoJwrVUEZUQOJDAvlr6ZYEmGTlD_BM0Rc_0fJZPU7A3uN9PHLw1atm-chN06IDXf23R33JI_xFuEZnj9HZQ_eIzNCl7GFmUryK3FFgYJpIbsI0BIFuksSikXz33IA\", \"access_token\": \"access\"}");
|
||||
}
|
||||
|
||||
return Task.FromResult(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.OpenIdConnect
|
||||
{
|
||||
internal class TestOpenIdConnectEvents
|
||||
{
|
||||
private static List<string> eventsFired = new List<string>();
|
||||
|
||||
internal static Task MessageReceived(MessageReceivedContext context)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.ProtocolMessage != null, "ProtocolMessage is null.");
|
||||
eventsFired.Add(nameof(MessageReceived));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task TokenValidated(TokenValidatedContext context)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.Principal != null, "context.Principal is null.");
|
||||
Helpers.ThrowIfConditionFailed(() => context.Principal.Identity != null, "context.Principal.Identity is null.");
|
||||
Helpers.ThrowIfConditionFailed(() => !string.IsNullOrWhiteSpace(context.Principal.Identity.Name), "context.Principal.Identity.Name is null.");
|
||||
eventsFired.Add(nameof(TokenValidated));
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.TokenEndpointRequest.Code == "AAABAAAAvPM1KaPlrEqdFSBzjqfTGGBtrTYVn589oKw4lLgJ6Svz0AhPVOJr0J2-Uu_KffGlqIbYlRAyxmt-vZ7VlSVdrWvOkNhK9OaAMaSD7LDoPbBTVMEkB0MdAgBTV34l2el-s8ZI02_9PvgQaORZs7n8eGaGbcoKAoxiDn2OcKuJVplXYgrGUwU4VpRaqe6RaNzuseM7qBFbLIv4Wps8CndE6W8ccmuu6EvGC6-H4uF9EZL7gU4nEcTcvkE4Qyt8do6VhTVfM1ygRNQgmV1BCig5t_5xfhL6-xWQdy15Uzn_Df8VSsyDXe8s9cxyKlqc_AIyLFy_NEiMQFUqjZWKd_rR3A8ugug15SEEGuo1kF3jMc7dVMdE6OF9UBd-Ax5ILWT7V4clnRQb6-CXB538DlolREfE-PowXYruFBA-ARD6rwAVtuVfCSbS0Zr4ZqfNjt6x8yQdK-OkdQRZ1thiZcZlm1lyb2EquGZ8Deh2iWBoY1uNcyjzhG-L43EivxtHAp6Y8cErhbo41iacgqOycgyJWxiB5J0HHkxD0nQ2RVVuY8Ybc9sdgyfKkkK2wZ3idGaRCdZN8Q9VBhWRXPDMqHWG8t3aZRtvJ_Xd3WhjNPJC0GpepUGNNQtXiEoIECC363o1z6PZC5-E7U3l9xK06BZkcfTOnggUiSWNCrxUKS44dNqaozdYlO5E028UgAEhJ4eDtcP3PZty-0j4j5Mw0F2FmyAA",
|
||||
"context.TokenEndpointRequest.Code is invalid.");
|
||||
eventsFired.Add(nameof(AuthorizationCodeReceived));
|
||||
|
||||
// Verify all events are fired.
|
||||
if (eventsFired.Contains(nameof(RedirectToIdentityProvider)) &&
|
||||
eventsFired.Contains(nameof(MessageReceived)) &&
|
||||
eventsFired.Contains(nameof(TokenValidated)) &&
|
||||
eventsFired.Contains(nameof(AuthorizationCodeReceived)))
|
||||
{
|
||||
((ClaimsIdentity)context.Principal.Identity).AddClaim(new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task RedirectToIdentityProvider(RedirectContext context)
|
||||
{
|
||||
eventsFired.Add(nameof(RedirectToIdentityProvider));
|
||||
|
||||
if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
|
||||
{
|
||||
context.ProtocolMessage.PostLogoutRedirectUri =
|
||||
context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + new PathString("/Account/Login");
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"keys": [
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "sig",
|
||||
"kid": "kriMPdmBvx68skT8-mPAB3BseeA",
|
||||
"x5t": "kriMPdmBvx68skT8-mPAB3BseeA",
|
||||
"n": "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuw==",
|
||||
"e": "AQAB",
|
||||
"x5c": [
|
||||
"MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kty": "RSA",
|
||||
"use": "sig",
|
||||
"kid": "MnC_VZcATfM5pOYiJHMba9goEKY",
|
||||
"x5t": "MnC_VZcATfM5pOYiJHMba9goEKY",
|
||||
"n": "vIqz+4+ER/vNWLON9yv8hIYV737JQ6rCl6XfzOC628seYUPf0TaGk91CFxefhzh23V9Tkq+RtwN1Vs/z57hO82kkzL+cQHZX3bMJD+GEGOKXCEXURN7VMyZWMAuzQoW9vFb1k3cR1RW/EW/P+C8bb2dCGXhBYqPfHyimvz2WarXhntPSbM5XyS5v5yCw5T/Vuwqqsio3V8wooWGMpp61y12NhN8bNVDQAkDPNu2DT9DXB1g0CeFINp/KAS/qQ2Kq6TSvRHJqxRR68RezYtje9KAqwqx4jxlmVAQy0T3+T+IAbsk1wRtWDndhO6s1Os+dck5TzyZ/dNOhfXgelixLUQ==",
|
||||
"e": "AQAB",
|
||||
"x5c": [
|
||||
"MIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdYNAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7JNcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaYxNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmRqWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBDYRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ=="
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
{
|
||||
"issuer": "https://sts.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/",
|
||||
"authorization_endpoint": "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/oauth2/authorize",
|
||||
"token_endpoint": "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/oauth2/token",
|
||||
"token_endpoint_auth_methods_supported": [
|
||||
"client_secret_post",
|
||||
"private_key_jwt"
|
||||
],
|
||||
"jwks_uri": "https://login.windows.net/common/discovery/keys",
|
||||
"response_types_supported": [
|
||||
"code",
|
||||
"id_token",
|
||||
"code id_token",
|
||||
"token"
|
||||
],
|
||||
"response_modes_supported": [
|
||||
"query",
|
||||
"fragment",
|
||||
"form_post"
|
||||
],
|
||||
"subject_types_supported": [
|
||||
"pairwise"
|
||||
],
|
||||
"scopes_supported": [
|
||||
"openid"
|
||||
],
|
||||
"id_token_signing_alg_values_supported": [
|
||||
"RS256"
|
||||
],
|
||||
"microsoft_multi_refresh_token": true,
|
||||
"check_session_iframe": "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/oauth2/checksession",
|
||||
"end_session_endpoint": "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/oauth2/logout",
|
||||
"userinfo_endpoint": "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/openid/userinfo"
|
||||
}
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using MusicStore.Components;
|
||||
using MusicStore.Mocks.Common;
|
||||
using MusicStore.Mocks.OpenIdConnect;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
public class StartupOpenIdConnectTesting
|
||||
{
|
||||
private readonly Platform _platform;
|
||||
|
||||
public StartupOpenIdConnectTesting(IWebHostEnvironment env)
|
||||
{
|
||||
//Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources,
|
||||
//then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("config.json")
|
||||
.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
|
||||
|
||||
Configuration = builder.Build();
|
||||
_platform = new Platform();
|
||||
Env = env;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
public IWebHostEnvironment Env { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
|
||||
|
||||
// Add EF services to the services container
|
||||
// Add EF services to the services container
|
||||
services.AddDbContext<MusicStoreContext>(options =>
|
||||
options.UseSqlite("Data Source=MusicStore.db;Cache=Shared"));
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<MusicStoreContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
// Create an Azure Active directory application and copy paste the following
|
||||
services.AddAuthentication().AddOpenIdConnect(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/[tenantName].onmicrosoft.com";
|
||||
options.ClientId = "c99497aa-3ee2-4707-b8a8-c33f51323fef";
|
||||
options.BackchannelHttpHandler = new OpenIdConnectBackChannelHttpHandler(Env);
|
||||
options.StringDataFormat = new CustomStringDataFormat();
|
||||
options.StateDataFormat = new CustomStateDataFormat();
|
||||
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
|
||||
options.UseTokenLifetime = false;
|
||||
options.TokenValidationParameters.ValidateLifetime = false;
|
||||
options.ProtocolValidator.RequireNonce = true;
|
||||
options.ProtocolValidator.NonceLifetime = TimeSpan.FromDays(36500);
|
||||
|
||||
options.Events = new OpenIdConnectEvents
|
||||
{
|
||||
OnMessageReceived = TestOpenIdConnectEvents.MessageReceived,
|
||||
OnAuthorizationCodeReceived = TestOpenIdConnectEvents.AuthorizationCodeReceived,
|
||||
OnRedirectToIdentityProvider = TestOpenIdConnectEvents.RedirectToIdentityProvider,
|
||||
OnTokenValidated = TestOpenIdConnectEvents.TokenValidated,
|
||||
};
|
||||
});
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("CorsPolicy", builder =>
|
||||
{
|
||||
builder.WithOrigins("http://example.com");
|
||||
});
|
||||
});
|
||||
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc();
|
||||
|
||||
//Add InMemoryCache
|
||||
services.AddSingleton<IMemoryCache, MemoryCache>();
|
||||
|
||||
// Add session related services.
|
||||
services.AddMemoryCache();
|
||||
services.AddDistributedMemoryCache();
|
||||
services.AddSession();
|
||||
|
||||
// Add the system clock service
|
||||
services.AddSingleton<ISystemClock, SystemClock>();
|
||||
|
||||
// Configure Auth
|
||||
services.Configure<AuthorizationOptions>(options =>
|
||||
{
|
||||
options.AddPolicy("ManageStore", new AuthorizationPolicyBuilder().RequireClaim("ManageStore", "Allowed").Build());
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// force the en-US culture, so that the app behaves the same even on machines with different default culture
|
||||
var supportedCultures = new[] { new CultureInfo("en-US") };
|
||||
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-US"),
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures
|
||||
});
|
||||
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
// Display custom error page in production when error occurs
|
||||
// During development use the ErrorPage middleware to display error information in the browser
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseMigrationsEndPoint();
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add the endpoint routing matcher middleware to the request pipeline
|
||||
app.UseRouting();
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseAuthentication();
|
||||
|
||||
// Add the authorization middleware to the request pipeline
|
||||
app.UseAuthorization();
|
||||
|
||||
// Add endpoints to the request pipeline
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "areaRoute",
|
||||
pattern: "{area:exists}/{controller}/{action}",
|
||||
defaults: new { action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "api",
|
||||
pattern: "{controller}/{id?}");
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Authentication.OAuth;
|
||||
using Microsoft.AspNetCore.Authentication.Twitter;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MusicStore.Components;
|
||||
using MusicStore.Mocks.Common;
|
||||
using MusicStore.Mocks.Facebook;
|
||||
using MusicStore.Mocks.Google;
|
||||
using MusicStore.Mocks.MicrosoftAccount;
|
||||
using MusicStore.Mocks.Twitter;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
public class StartupSocialTesting
|
||||
{
|
||||
private readonly Platform _platform;
|
||||
|
||||
public StartupSocialTesting(IWebHostEnvironment hostingEnvironment)
|
||||
{
|
||||
//Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources,
|
||||
//then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(hostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("config.json")
|
||||
.AddEnvironmentVariables() //All environment variables in the process's context flow in as configuration values.
|
||||
.AddJsonFile("configoverride.json", optional: true); // Used to override some configuration parameters that cannot be overridden by environment.
|
||||
|
||||
Configuration = builder.Build();
|
||||
_platform = new Platform();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
|
||||
|
||||
// Add EF services to the services container
|
||||
services.AddDbContext<MusicStoreContext>(options =>
|
||||
options.UseSqlite("Data Source=MusicStore.db;Cache=Shared"));
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<MusicStoreContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.ConfigureApplicationCookie(options => options.AccessDeniedPath = "/Home/AccessDenied");
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("CorsPolicy", builder =>
|
||||
{
|
||||
builder.WithOrigins("http://example.com");
|
||||
});
|
||||
});
|
||||
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc();
|
||||
|
||||
//Add InMemoryCache
|
||||
services.AddSingleton<IMemoryCache, MemoryCache>();
|
||||
|
||||
// Add session related services.
|
||||
services.AddMemoryCache();
|
||||
services.AddDistributedMemoryCache();
|
||||
services.AddSession();
|
||||
|
||||
// Add the system clock service
|
||||
services.AddSingleton<ISystemClock, SystemClock>();
|
||||
|
||||
// Configure Auth
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("ManageStore", new AuthorizationPolicyBuilder().RequireClaim("ManageStore", "Allowed").Build());
|
||||
});
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddFacebook(options =>
|
||||
{
|
||||
options.AppId = "[AppId]";
|
||||
options.AppSecret = "[AppSecret]";
|
||||
options.Events = new OAuthEvents()
|
||||
{
|
||||
OnCreatingTicket = TestFacebookEvents.OnCreatingTicket,
|
||||
OnTicketReceived = TestFacebookEvents.OnTicketReceived,
|
||||
OnRedirectToAuthorizationEndpoint = TestFacebookEvents.RedirectToAuthorizationEndpoint
|
||||
};
|
||||
options.BackchannelHttpHandler = new FacebookMockBackChannelHttpHandler();
|
||||
options.StateDataFormat = new CustomStateDataFormat();
|
||||
options.Scope.Add("email");
|
||||
options.Scope.Add("read_friendlists");
|
||||
options.Scope.Add("user_checkins");
|
||||
}).AddGoogle(options =>
|
||||
{
|
||||
options.ClientId = "[ClientId]";
|
||||
options.ClientSecret = "[ClientSecret]";
|
||||
options.AccessType = "offline";
|
||||
options.Events = new OAuthEvents()
|
||||
{
|
||||
OnCreatingTicket = TestGoogleEvents.OnCreatingTicket,
|
||||
OnTicketReceived = TestGoogleEvents.OnTicketReceived,
|
||||
OnRedirectToAuthorizationEndpoint = TestGoogleEvents.RedirectToAuthorizationEndpoint
|
||||
};
|
||||
options.StateDataFormat = new CustomStateDataFormat();
|
||||
options.BackchannelHttpHandler = new GoogleMockBackChannelHttpHandler();
|
||||
}).AddTwitter(options =>
|
||||
{
|
||||
options.ConsumerKey = "[ConsumerKey]";
|
||||
options.ConsumerSecret = "[ConsumerSecret]";
|
||||
options.Events = new TwitterEvents()
|
||||
{
|
||||
OnCreatingTicket = TestTwitterEvents.OnCreatingTicket,
|
||||
OnTicketReceived = TestTwitterEvents.OnTicketReceived,
|
||||
OnRedirectToAuthorizationEndpoint = TestTwitterEvents.RedirectToAuthorizationEndpoint
|
||||
};
|
||||
options.StateDataFormat = new CustomTwitterStateDataFormat();
|
||||
options.BackchannelHttpHandler = new TwitterMockBackChannelHttpHandler();
|
||||
}).AddMicrosoftAccount(options =>
|
||||
{
|
||||
options.ClientId = "[ClientId]";
|
||||
options.ClientSecret = "[ClientSecret]";
|
||||
options.Events = new OAuthEvents()
|
||||
{
|
||||
OnCreatingTicket = TestMicrosoftAccountEvents.OnCreatingTicket,
|
||||
OnTicketReceived = TestMicrosoftAccountEvents.OnTicketReceived,
|
||||
OnRedirectToAuthorizationEndpoint = TestMicrosoftAccountEvents.RedirectToAuthorizationEndpoint
|
||||
};
|
||||
options.BackchannelHttpHandler = new MicrosoftAccountMockBackChannelHandler();
|
||||
options.StateDataFormat = new CustomStateDataFormat();
|
||||
options.Scope.Add("wl.basic");
|
||||
options.Scope.Add("wl.signin");
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// force the en-US culture, so that the app behaves the same even on machines with different default culture
|
||||
var supportedCultures = new[] { new CultureInfo("en-US") };
|
||||
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-US"),
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures
|
||||
});
|
||||
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
// Error page middleware displays a nice formatted HTML page for any unhandled exceptions in the request pipeline.
|
||||
// Note: Not recommended for production.
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseMigrationsEndPoint();
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add the endpoint routing matcher middleware to the request pipeline
|
||||
app.UseRouting();
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseAuthentication();
|
||||
|
||||
// Add the authorization middleware to the request pipeline
|
||||
app.UseAuthorization();
|
||||
|
||||
// Add endpoints to the request pipeline
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "areaRoute",
|
||||
pattern: "{area:exists}/{controller}/{action}",
|
||||
defaults: new { action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "api",
|
||||
pattern: "{controller}/{id?}");
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Twitter;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace MusicStore.Mocks.Twitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for CustomTwitterStateDataFormat
|
||||
/// </summary>
|
||||
public class CustomTwitterStateDataFormat : ISecureDataFormat<RequestToken>
|
||||
{
|
||||
private static string _lastSavedRequestToken;
|
||||
|
||||
public string Protect(RequestToken data)
|
||||
{
|
||||
data.Token = "valid_oauth_token";
|
||||
_lastSavedRequestToken = Serialize(data);
|
||||
return "valid_oauth_token";
|
||||
}
|
||||
|
||||
public string Protect(RequestToken data, string purpose)
|
||||
{
|
||||
return Protect(data);
|
||||
}
|
||||
|
||||
public RequestToken Unprotect(string state)
|
||||
{
|
||||
return state == "valid_oauth_token" ? DeSerialize(_lastSavedRequestToken) : null;
|
||||
}
|
||||
|
||||
public RequestToken Unprotect(string state, string purpose)
|
||||
{
|
||||
return Unprotect(state);
|
||||
}
|
||||
|
||||
private string Serialize(RequestToken data)
|
||||
{
|
||||
return JsonConvert.SerializeObject(data, Formatting.Indented);
|
||||
}
|
||||
|
||||
private RequestToken DeSerialize(string state)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<RequestToken>(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Twitter;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using MusicStore.Mocks.Common;
|
||||
|
||||
namespace MusicStore.Mocks.Twitter
|
||||
{
|
||||
internal class TestTwitterEvents
|
||||
{
|
||||
internal static Task OnCreatingTicket(TwitterCreatingTicketContext context)
|
||||
{
|
||||
if (context.Principal != null)
|
||||
{
|
||||
Helpers.ThrowIfConditionFailed(() => context.UserId == "valid_user_id", "UserId is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.ScreenName == "valid_screen_name", "ScreenName is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessToken == "valid_oauth_token", "AccessToken is not valid");
|
||||
Helpers.ThrowIfConditionFailed(() => context.AccessTokenSecret == "valid_oauth_token_secret", "AccessTokenSecret is not valid");
|
||||
context.Principal.Identities.First().AddClaim(new Claim("ManageStore", "false"));
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task OnTicketReceived(TicketReceivedContext context)
|
||||
{
|
||||
if (context.Principal != null && context.Options.SignInScheme == IdentityConstants.ExternalScheme)
|
||||
{
|
||||
//This way we will know all Events were fired.
|
||||
var identity = context.Principal.Identities.First();
|
||||
var manageStoreClaim = identity?.Claims.Where(c => c.Type == "ManageStore" && c.Value == "false").FirstOrDefault();
|
||||
if (manageStoreClaim != null)
|
||||
{
|
||||
identity.RemoveClaim(manageStoreClaim);
|
||||
identity.AddClaim(new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
internal static Task RedirectToAuthorizationEndpoint(RedirectContext<TwitterOptions> context)
|
||||
{
|
||||
context.Response.Redirect(context.RedirectUri + "&custom_redirect_uri=custom");
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
|
||||
namespace MusicStore.Mocks.Twitter
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for TwitterMockBackChannelHttpHandler
|
||||
/// </summary>
|
||||
public class TwitterMockBackChannelHttpHandler : HttpMessageHandler
|
||||
{
|
||||
private static bool _requestTokenEndpointInvoked = false;
|
||||
|
||||
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
var response = new HttpResponseMessage();
|
||||
|
||||
if (request.RequestUri.AbsoluteUri.StartsWith("https://api.twitter.com/oauth/access_token"))
|
||||
{
|
||||
var formData = new FormCollection(await new FormReader(await request.Content.ReadAsStreamAsync()).ReadFormAsync());
|
||||
if (formData["oauth_verifier"] == "valid_oauth_verifier")
|
||||
{
|
||||
if (_requestTokenEndpointInvoked)
|
||||
{
|
||||
var response_Form_data = new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
new KeyValuePair<string, string>("oauth_token", "valid_oauth_token"),
|
||||
new KeyValuePair<string, string>("oauth_token_secret", "valid_oauth_token_secret"),
|
||||
new KeyValuePair<string, string>("user_id", "valid_user_id"),
|
||||
new KeyValuePair<string, string>("screen_name", "valid_screen_name"),
|
||||
};
|
||||
|
||||
response.Content = new FormUrlEncodedContent(response_Form_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.StatusCode = HttpStatusCode.InternalServerError;
|
||||
response.Content = new StringContent("RequestTokenEndpoint is not invoked");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
response.StatusCode = (HttpStatusCode)400;
|
||||
return response;
|
||||
}
|
||||
else if (request.RequestUri.AbsoluteUri.StartsWith("https://api.twitter.com/oauth/request_token"))
|
||||
{
|
||||
var response_Form_data = new List<KeyValuePair<string, string>>()
|
||||
{
|
||||
new KeyValuePair<string, string>("oauth_callback_confirmed", "true"),
|
||||
new KeyValuePair<string, string>("oauth_token", "valid_oauth_token"),
|
||||
new KeyValuePair<string, string>("oauth_token_secret", "valid_oauth_token_secret")
|
||||
};
|
||||
|
||||
_requestTokenEndpointInvoked = true;
|
||||
response.Content = new FormUrlEncodedContent(response_Form_data);
|
||||
return response;
|
||||
}
|
||||
|
||||
throw new NotImplementedException(request.RequestUri.AbsoluteUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
namespace MusicStore
|
||||
{
|
||||
public class StoreConfig
|
||||
{
|
||||
public const string ConnectionStringKey = "Data__DefaultConnection__ConnectionString";
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
The contents of this folder are used for end to end testing.
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
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,113 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class ExternalLoginConfirmationViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public class ExternalLoginListViewModel
|
||||
{
|
||||
public string ReturnUrl { get; set; }
|
||||
}
|
||||
|
||||
public class SendCodeViewModel
|
||||
{
|
||||
public string SelectedProvider { get; set; }
|
||||
public ICollection<SelectListItem> Providers { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
|
||||
public class VerifyCodeViewModel
|
||||
{
|
||||
[Required]
|
||||
public string Provider { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Code")]
|
||||
public string Code { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
[Display(Name = "Remember this browser?")]
|
||||
public bool RememberBrowser { get; set; }
|
||||
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
|
||||
public class ForgotViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
public class LoginViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Email")]
|
||||
[EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Display(Name = "Remember me?")]
|
||||
public bool RememberMe { get; set; }
|
||||
}
|
||||
|
||||
public class RegisterViewModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm password")]
|
||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
public class ResetPasswordViewModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Password")]
|
||||
public string Password { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm password")]
|
||||
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
}
|
||||
|
||||
public class ForgotPasswordViewModel
|
||||
{
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
[Display(Name = "Email")]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class Album
|
||||
{
|
||||
[ScaffoldColumn(false)]
|
||||
public int AlbumId { get; set; }
|
||||
|
||||
public int GenreId { get; set; }
|
||||
|
||||
public int ArtistId { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(160, MinimumLength = 2)]
|
||||
public string Title { get; set; }
|
||||
|
||||
[Required]
|
||||
[Range(0.01, 100.00)]
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
[Column(TypeName = "decimal(18,2)")]
|
||||
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 List<OrderDetail> OrderDetails { get; set; }
|
||||
|
||||
[ScaffoldColumn(false)]
|
||||
[BindNever]
|
||||
[Required]
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
public Album()
|
||||
{
|
||||
Created = DateTime.UtcNow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,17 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class Genre
|
||||
{
|
||||
public int GenreId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public List<Album> Albums { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class IndexViewModel
|
||||
{
|
||||
public bool HasPassword { get; set; }
|
||||
public IList<UserLoginInfo> Logins { get; set; }
|
||||
public string PhoneNumber { get; set; }
|
||||
public bool TwoFactor { get; set; }
|
||||
public bool BrowserRemembered { get; set; }
|
||||
}
|
||||
|
||||
public class ManageLoginsViewModel
|
||||
{
|
||||
public IList<UserLoginInfo> CurrentLogins { get; set; }
|
||||
public IList<AuthenticationScheme> OtherLogins { get; set; }
|
||||
}
|
||||
|
||||
public class FactorViewModel
|
||||
{
|
||||
public string Purpose { get; set; }
|
||||
}
|
||||
|
||||
public class SetPasswordViewModel
|
||||
{
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "New password")]
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm new password")]
|
||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
public class ChangePasswordViewModel
|
||||
{
|
||||
[Required]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Current password")]
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
[Required]
|
||||
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "New password")]
|
||||
public string NewPassword { get; set; }
|
||||
|
||||
[DataType(DataType.Password)]
|
||||
[Display(Name = "Confirm new password")]
|
||||
[Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
|
||||
public string ConfirmPassword { get; set; }
|
||||
}
|
||||
|
||||
public class AddPhoneNumberViewModel
|
||||
{
|
||||
[Required]
|
||||
[Phone]
|
||||
[Display(Name = "Phone Number")]
|
||||
public string Number { get; set; }
|
||||
}
|
||||
|
||||
public class VerifyPhoneNumberViewModel
|
||||
{
|
||||
[Required]
|
||||
[Display(Name = "Code")]
|
||||
public string Code { get; set; }
|
||||
|
||||
[Required]
|
||||
[Phone]
|
||||
[Display(Name = "Phone Number")]
|
||||
public string PhoneNumber { get; set; }
|
||||
}
|
||||
|
||||
public class ConfigureTwoFactorViewModel
|
||||
{
|
||||
public string SelectedProvider { get; set; }
|
||||
public ICollection<SelectListItem> Providers { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class ApplicationUser : IdentityUser { }
|
||||
|
||||
public class MusicStoreContext : IdentityDbContext<ApplicationUser>
|
||||
{
|
||||
public MusicStoreContext(DbContextOptions<MusicStoreContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class Order
|
||||
{
|
||||
[BindNever]
|
||||
[ScaffoldColumn(false)]
|
||||
public int OrderId { get; set; }
|
||||
|
||||
[BindNever]
|
||||
[ScaffoldColumn(false)]
|
||||
public System.DateTime OrderDate { get; set; }
|
||||
|
||||
[BindNever]
|
||||
[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; }
|
||||
|
||||
[BindNever]
|
||||
[ScaffoldColumn(false)]
|
||||
[Column(TypeName = "decimal(18,2)")]
|
||||
public decimal Total { get; set; }
|
||||
|
||||
[BindNever]
|
||||
public List<OrderDetail> OrderDetails { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
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; }
|
||||
|
||||
[Column(TypeName = "decimal(18,2)")]
|
||||
public decimal UnitPrice { get; set; }
|
||||
|
||||
public virtual Album Album { get; set; }
|
||||
|
||||
public virtual Order Order { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,934 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public static class SampleData
|
||||
{
|
||||
const string imgUrl = "~/Images/placeholder.png";
|
||||
const string defaultAdminUserName = "DefaultAdminUserName";
|
||||
const string defaultAdminPassword = "DefaultAdminPassword";
|
||||
|
||||
public static async Task InitializeMusicStoreDatabaseAsync(IServiceProvider serviceProvider, bool createUsers = true)
|
||||
{
|
||||
using (var serviceScope = serviceProvider.CreateScope())
|
||||
{
|
||||
var scopeServiceProvider = serviceScope.ServiceProvider;
|
||||
var db = scopeServiceProvider.GetService<MusicStoreContext>();
|
||||
|
||||
if (await db.Database.EnsureCreatedAsync())
|
||||
{
|
||||
await InsertTestData(scopeServiceProvider);
|
||||
if (createUsers)
|
||||
{
|
||||
await CreateAdminUser(scopeServiceProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
var db = serviceScope.ServiceProvider.GetService<MusicStoreContext>();
|
||||
existingData = db.Set<TEntity>().ToList();
|
||||
}
|
||||
|
||||
using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
|
||||
{
|
||||
var db = serviceScope.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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a store manager user who can manage the inventory.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task CreateAdminUser(IServiceProvider serviceProvider)
|
||||
{
|
||||
var env = serviceProvider.GetService<IWebHostEnvironment>();
|
||||
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(env.ContentRootPath)
|
||||
.AddJsonFile("config.json")
|
||||
.AddEnvironmentVariables();
|
||||
var configuration = builder.Build();
|
||||
|
||||
//const string adminRole = "Administrator";
|
||||
|
||||
var userManager = serviceProvider.GetService<UserManager<ApplicationUser>>();
|
||||
// TODO: Identity SQL does not support roles yet
|
||||
//var roleManager = serviceProvider.GetService<ApplicationRoleManager>();
|
||||
//if (!await roleManager.RoleExistsAsync(adminRole))
|
||||
//{
|
||||
// await roleManager.CreateAsync(new IdentityRole(adminRole));
|
||||
//}
|
||||
|
||||
var user = await userManager.FindByNameAsync(configuration[defaultAdminUserName]);
|
||||
if (user == null)
|
||||
{
|
||||
user = new ApplicationUser { UserName = configuration[defaultAdminUserName] };
|
||||
await userManager.CreateAsync(user, configuration[defaultAdminPassword]);
|
||||
//await userManager.AddToRoleAsync(user, adminRole);
|
||||
await userManager.AddClaimAsync(user, new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
|
||||
// NOTE: For end to end testing only
|
||||
var envPerfLab = configuration["PERF_LAB"];
|
||||
if (envPerfLab == "true")
|
||||
{
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
var email = string.Format("User{0:D3}@example.com", i);
|
||||
var normalUser = await userManager.FindByEmailAsync(email);
|
||||
if (normalUser == null)
|
||||
{
|
||||
await userManager.CreateAsync(new ApplicationUser { UserName = email, Email = email }, "Password~!1");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 = "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 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 = "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 = "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 = "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(+>", 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 = "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 = "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 = "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 = "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 = "Somewhere in Time", Genre = genres["Metal"], Price = 8.99M, Artist = artists["Iron Maiden"], 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 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 = "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 = "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 = "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 = "Page & Plant" },
|
||||
new Artist { Name = "Papa Wemba" },
|
||||
new Artist { Name = "Paul D'Ianno" },
|
||||
new Artist { Name = "Paul Oakenfold" },
|
||||
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 = "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 = "אריק אינשטיין"}
|
||||
};
|
||||
|
||||
artists = new Dictionary<string, Artist>();
|
||||
foreach (Artist artist in artistsList)
|
||||
{
|
||||
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 = "R&B" },
|
||||
new Genre { Name = "Indie" },
|
||||
new Genre { Name = "Punk" },
|
||||
new Genre { Name = "World" }
|
||||
};
|
||||
|
||||
genres = new Dictionary<string, Genre>();
|
||||
|
||||
foreach (Genre genre in genresList)
|
||||
{
|
||||
genres.Add(genre.Name, genre);
|
||||
}
|
||||
}
|
||||
|
||||
return genres;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,183 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace MusicStore.Models
|
||||
{
|
||||
public class ShoppingCart
|
||||
{
|
||||
private readonly MusicStoreContext _dbContext;
|
||||
private readonly string _shoppingCartId;
|
||||
|
||||
private ShoppingCart(MusicStoreContext dbContext, string id)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_shoppingCartId = id;
|
||||
}
|
||||
|
||||
public static ShoppingCart GetCart(MusicStoreContext db, HttpContext context)
|
||||
=> GetCart(db, GetCartId(context));
|
||||
|
||||
public static ShoppingCart GetCart(MusicStoreContext db, string cartId)
|
||||
=> new ShoppingCart(db, cartId);
|
||||
|
||||
public async Task AddToCart(Album album)
|
||||
{
|
||||
// Get the matching cart and album instances
|
||||
var cartItem = await _dbContext.CartItems.SingleOrDefaultAsync(
|
||||
c => c.CartId == _shoppingCartId
|
||||
&& c.AlbumId == album.AlbumId);
|
||||
|
||||
if (cartItem == null)
|
||||
{
|
||||
// Create a new cart item if no cart item exists
|
||||
cartItem = new CartItem
|
||||
{
|
||||
AlbumId = album.AlbumId,
|
||||
CartId = _shoppingCartId,
|
||||
Count = 1,
|
||||
DateCreated = DateTime.Now
|
||||
};
|
||||
|
||||
_dbContext.CartItems.Add(cartItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the item does exist in the cart, then add one to the quantity
|
||||
cartItem.Count++;
|
||||
}
|
||||
}
|
||||
|
||||
public int RemoveFromCart(int id)
|
||||
{
|
||||
// Get the cart
|
||||
var cartItem = _dbContext.CartItems.SingleOrDefault(
|
||||
cart => cart.CartId == _shoppingCartId
|
||||
&& cart.CartItemId == id);
|
||||
|
||||
int itemCount = 0;
|
||||
|
||||
if (cartItem != null)
|
||||
{
|
||||
if (cartItem.Count > 1)
|
||||
{
|
||||
cartItem.Count--;
|
||||
itemCount = cartItem.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbContext.CartItems.Remove(cartItem);
|
||||
}
|
||||
}
|
||||
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
public async Task EmptyCart()
|
||||
{
|
||||
var cartItems = await _dbContext
|
||||
.CartItems
|
||||
.Where(cart => cart.CartId == _shoppingCartId)
|
||||
.ToArrayAsync();
|
||||
|
||||
_dbContext.CartItems.RemoveRange(cartItems);
|
||||
}
|
||||
|
||||
public Task<List<CartItem>> GetCartItems()
|
||||
{
|
||||
return _dbContext
|
||||
.CartItems
|
||||
.Where(cart => cart.CartId == _shoppingCartId)
|
||||
.Include(c => c.Album)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public Task<List<string>> GetCartAlbumTitles()
|
||||
{
|
||||
return _dbContext
|
||||
.CartItems
|
||||
.Where(cart => cart.CartId == _shoppingCartId)
|
||||
.Select(c => c.Album.Title)
|
||||
.OrderBy(n => n)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public Task<int> GetCount()
|
||||
{
|
||||
// Get the count of each item in the cart and sum them up
|
||||
return _dbContext
|
||||
.CartItems
|
||||
.Where(c => c.CartId == _shoppingCartId)
|
||||
.Select(c => c.Count)
|
||||
.SumAsync();
|
||||
}
|
||||
|
||||
public async Task<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
|
||||
|
||||
// No way to do decimal sum on server with SQLite, but client eval is fine here
|
||||
return (await _dbContext
|
||||
.CartItems
|
||||
.Where(c => c.CartId == _shoppingCartId)
|
||||
.Select(c => c.Album.Price * c.Count)
|
||||
.ToListAsync())
|
||||
.Sum();
|
||||
}
|
||||
|
||||
public async Task CreateOrder(Order order)
|
||||
{
|
||||
decimal orderTotal = 0;
|
||||
|
||||
var cartItems = await GetCartItems();
|
||||
|
||||
// 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 = await _dbContext.Albums.SingleAsync(a => a.AlbumId == item.AlbumId);
|
||||
|
||||
var orderDetail = new OrderDetail
|
||||
{
|
||||
AlbumId = item.AlbumId,
|
||||
Order = order,
|
||||
UnitPrice = album.Price,
|
||||
Quantity = item.Count,
|
||||
};
|
||||
|
||||
// Set the order total of the shopping cart
|
||||
orderTotal += (item.Count * album.Price);
|
||||
|
||||
_dbContext.OrderDetails.Add(orderDetail);
|
||||
}
|
||||
|
||||
// Set the order's total to the orderTotal count
|
||||
order.Total = orderTotal;
|
||||
|
||||
// Empty the shopping cart
|
||||
await EmptyCart();
|
||||
}
|
||||
|
||||
// We're using HttpContextBase to allow access to sessions.
|
||||
private static string GetCartId(HttpContext context)
|
||||
{
|
||||
var cartId = context.Session.GetString("Session");
|
||||
|
||||
if (cartId == null)
|
||||
{
|
||||
//A GUID to hold the cartId.
|
||||
cartId = Guid.NewGuid().ToString();
|
||||
|
||||
// Send cart Id as a cookie to the client.
|
||||
context.Session.SetString("Session", cartId);
|
||||
}
|
||||
|
||||
return cartId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<Import Project="$(RepoRoot)src\Servers\IIS\build\testsite.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>Music store application on ASP.NET Core</Description>
|
||||
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
|
||||
<DefineConstants>$(DefineConstants);DEMO</DefineConstants>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<InProcessTestSite>true</InProcessTestSite>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="ForTesting\**\*" CopyToPublishDirectory="Never" Condition=" '$(PublishForTesting)' != 'true' " />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.AspNetCore" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication.Cookies" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication.Facebook" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication.Google" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
|
||||
<Reference Include="Microsoft.AspNetCore.Authentication.Twitter" />
|
||||
<Reference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" />
|
||||
<Reference Include="Microsoft.AspNetCore.Identity" />
|
||||
<Reference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
|
||||
<Reference Include="Microsoft.AspNetCore.Mvc" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.HttpSys" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.IIS" />
|
||||
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
|
||||
<Reference Include="Microsoft.AspNetCore.Session" />
|
||||
<Reference Include="Microsoft.AspNetCore.StaticFiles" />
|
||||
<Reference Include="Microsoft.EntityFrameworkCore.Sqlite" />
|
||||
<Reference Include="Microsoft.Extensions.Configuration.CommandLine" />
|
||||
<!-- Avoid CS1705 errors due to mix of assemblies brought in transitively. -->
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
|
||||
<Reference Include="Newtonsoft.Json" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="VerifyPrecompiledViews" AfterTargets="Publish">
|
||||
<Error Text="Did not use Razor Sdk to compile views. Actual $(ResolvedRazorCompileToolset)" Condition="'$(ResolvedRazorCompileToolset)' != 'RazorSdk'" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ExpectedViewOutput>$(PublishDir)$(RazorTargetName).dll</ExpectedViewOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<Error Text="Did not precompiled view binary '$(ExpectedViewOutput)'" Condition="!Exists('$(ExpectedViewOutput)')" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
@page
|
||||
@{ throw new InvalidOperationException(); }
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
internal class Platform
|
||||
{
|
||||
// Defined in winnt.h
|
||||
private const int PRODUCT_NANO_SERVER = 0x0000006D;
|
||||
private const int PRODUCT_DATACENTER_NANO_SERVER = 0x0000008F;
|
||||
private const int PRODUCT_STANDARD_NANO_SERVER = 0x00000090;
|
||||
|
||||
[DllImport("api-ms-win-core-sysinfo-l1-2-1.dll", SetLastError = false)]
|
||||
private static extern bool GetProductInfo(
|
||||
int dwOSMajorVersion,
|
||||
int dwOSMinorVersion,
|
||||
int dwSpMajorVersion,
|
||||
int dwSpMinorVersion,
|
||||
out int pdwReturnedProductType);
|
||||
|
||||
private bool? _isNano;
|
||||
private bool? _isWindows;
|
||||
|
||||
public bool IsRunningOnWindows
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isWindows == null)
|
||||
{
|
||||
_isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
}
|
||||
|
||||
return _isWindows.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRunningOnNanoServer
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isNano == null)
|
||||
{
|
||||
var osVersion = new Version(RtlGetVersion() ?? string.Empty);
|
||||
|
||||
try
|
||||
{
|
||||
int productType;
|
||||
if (GetProductInfo(osVersion.Major, osVersion.Minor, 0, 0, out productType))
|
||||
{
|
||||
_isNano = productType == PRODUCT_NANO_SERVER ||
|
||||
productType == PRODUCT_DATACENTER_NANO_SERVER ||
|
||||
productType == PRODUCT_STANDARD_NANO_SERVER;
|
||||
}
|
||||
else
|
||||
{
|
||||
_isNano = false;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// If the API call fails, the API set is not there which means
|
||||
// that we are definetely not running on Nano
|
||||
_isNano = false;
|
||||
}
|
||||
}
|
||||
|
||||
return _isNano.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Sql client not available on mono, non-windows, or nano
|
||||
public bool UseInMemoryStore
|
||||
{
|
||||
get
|
||||
{
|
||||
return !IsRunningOnWindows || IsRunningOnNanoServer;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct RTL_OSVERSIONINFOEX
|
||||
{
|
||||
internal uint dwOSVersionInfoSize;
|
||||
internal uint dwMajorVersion;
|
||||
internal uint dwMinorVersion;
|
||||
internal uint dwBuildNumber;
|
||||
internal uint dwPlatformId;
|
||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||||
internal string szCSDVersion;
|
||||
}
|
||||
|
||||
// This call avoids the shimming Windows does to report old versions
|
||||
[DllImport("ntdll")]
|
||||
private static extern int RtlGetVersion(out RTL_OSVERSIONINFOEX lpVersionInformation);
|
||||
|
||||
internal static string RtlGetVersion()
|
||||
{
|
||||
RTL_OSVERSIONINFOEX osvi = new RTL_OSVERSIONINFOEX();
|
||||
osvi.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osvi);
|
||||
if (RtlGetVersion(out osvi) == 0)
|
||||
{
|
||||
return $"{osvi.dwMajorVersion}.{osvi.dwMinorVersion}.{osvi.dwBuildNumber}";
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.HttpSys;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddCommandLine(args)
|
||||
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
|
||||
.Build();
|
||||
|
||||
var builder = new HostBuilder()
|
||||
.ConfigureWebHost(webHostBuilder =>
|
||||
{
|
||||
webHostBuilder
|
||||
.UseConfiguration(config)
|
||||
.UseIISIntegration()
|
||||
.UseStartup("MusicStore");
|
||||
|
||||
var environment = webHostBuilder.GetSetting("environment") ??
|
||||
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
|
||||
|
||||
if (string.Equals(webHostBuilder.GetSetting("server"), "Microsoft.AspNetCore.Server.HttpSys", System.StringComparison.Ordinal))
|
||||
{
|
||||
if (string.Equals(environment, "NtlmAuthentication", System.StringComparison.Ordinal))
|
||||
{
|
||||
// Set up NTLM authentication for WebListener like below.
|
||||
// For IIS and IISExpress: Use inetmgr to setup NTLM authentication on the application vDir or
|
||||
// modify the applicationHost.config to enable NTLM.
|
||||
webHostBuilder.UseHttpSys(options =>
|
||||
{
|
||||
options.Authentication.Schemes = AuthenticationSchemes.NTLM;
|
||||
options.Authentication.AllowAnonymous = false;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
webHostBuilder.UseHttpSys();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
webHostBuilder.UseKestrel();
|
||||
}
|
||||
|
||||
// In Proc
|
||||
webHostBuilder.UseIIS();
|
||||
|
||||
webHostBuilder.ConfigureLogging(factory =>
|
||||
{
|
||||
factory.AddConsole();
|
||||
|
||||
var logLevel = string.Equals(environment, "Development", StringComparison.Ordinal) ? LogLevel.Information : LogLevel.Warning;
|
||||
factory.SetMinimumLevel(logLevel);
|
||||
|
||||
// Turn off Info logging for EF commands
|
||||
factory.AddFilter("Microsoft.EntityFrameworkCore.Database.Command", LogLevel.Warning);
|
||||
});
|
||||
})
|
||||
.UseDefaultServiceProvider((context, options) => {
|
||||
options.ValidateScopes = true;
|
||||
});
|
||||
|
||||
var host = builder.Build();
|
||||
|
||||
return host.RunAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
namespace MusicStore
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
||||
public string SiteTitle { get; set; }
|
||||
|
||||
public bool CacheDbResults { get; set; } = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": true,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:5762/",
|
||||
"sslPort": 0
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"ANCM IIS Express": {
|
||||
"commandName": "Executable",
|
||||
"executablePath": "$(IISExpressPath)",
|
||||
"commandLineArgs": "$(IISExpressArguments)",
|
||||
"environmentVariables": {
|
||||
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
|
||||
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
|
||||
"ASPNETCORE_MODULE_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
|
||||
"LAUNCHER_ARGS": "$(TargetPath)",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"LAUNCHER_PATH": "$(DotNetPath)",
|
||||
"ASPNETCORE_MODULE_DEBUG": "console"
|
||||
}
|
||||
},
|
||||
"ANCM IIS": {
|
||||
"commandName": "Executable",
|
||||
"executablePath": "$(IISPath)",
|
||||
"commandLineArgs": "$(IISArguments)",
|
||||
"environmentVariables": {
|
||||
"IIS_SITE_PATH": "$(MSBuildThisFileDirectory)",
|
||||
"ANCMV2_PATH": "$(AspNetCoreModuleV2ShimDll)",
|
||||
"ASPNETCORE_MODULE_OUTOFPROCESS_HANDLER": "$(AspNetCoreModuleV2OutOfProcessHandlerDll)",
|
||||
"LAUNCHER_ARGS": "$(TargetPath)",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"LAUNCHER_PATH": "$(DotNetPath)",
|
||||
"ASPNETCORE_MODULE_DEBUG": "console"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
/// <autosync enabled="true" />
|
||||
/// <reference path="../wwwroot/Scripts/bootstrap.js" />
|
||||
/// <reference path="../wwwroot/Scripts/jquery.signalR-2.0.1.js" />
|
||||
/// <reference path="../wwwroot/Scripts/jquery.validate.js" />
|
||||
/// <reference path="../wwwroot/Scripts/jquery.validate.unobtrusive.js" />
|
||||
/// <reference path="../wwwroot/Scripts/jquery-3.5.1.js" />
|
||||
/// <reference path="../wwwroot/Scripts/modernizr-2.6.2.js" />
|
||||
/// <reference path="../wwwroot/Scripts/respond.js" />
|
||||
|
|
@ -1,221 +0,0 @@
|
|||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MusicStore.Components;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private readonly Platform _platform;
|
||||
|
||||
public Startup(IWebHostEnvironment hostingEnvironment)
|
||||
{
|
||||
// Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1'
|
||||
// is found in both the registered sources, then the later source will win. By this way a Local config
|
||||
// can be overridden by a different setting while deployed remotely.
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(hostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("config.json")
|
||||
//All environment variables in the process's context flow in as configuration values.
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
_platform = new Platform();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
|
||||
|
||||
// Add EF services to the services container
|
||||
services.AddDbContext<MusicStoreContext>(options =>
|
||||
options.UseSqlite("Data Source=MusicStore.db;Cache=Shared"));
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<MusicStoreContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.ConfigureApplicationCookie(options => options.AccessDeniedPath = "/Home/AccessDenied");
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("CorsPolicy", builder =>
|
||||
{
|
||||
builder.WithOrigins("http://example.com");
|
||||
});
|
||||
});
|
||||
|
||||
services.AddLogging();
|
||||
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc();
|
||||
|
||||
// Add memory cache services
|
||||
services.AddMemoryCache();
|
||||
services.AddDistributedMemoryCache();
|
||||
|
||||
// Add session related services.
|
||||
services.AddSession();
|
||||
|
||||
// Add the system clock service
|
||||
services.AddSingleton<ISystemClock, SystemClock>();
|
||||
|
||||
// Configure Auth
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(
|
||||
"ManageStore",
|
||||
authBuilder =>
|
||||
{
|
||||
authBuilder.RequireClaim("ManageStore", "Allowed");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
services.AddAuthentication()
|
||||
.AddFacebook(options =>
|
||||
{
|
||||
options.AppId = "550624398330273";
|
||||
options.AppSecret = "10e56a291d6b618da61b1e0dae3a8954";
|
||||
})
|
||||
.AddGoogle(options =>
|
||||
{
|
||||
options.ClientId = "995291875932-0rt7417v5baevqrno24kv332b7d6d30a.apps.googleusercontent.com";
|
||||
options.ClientSecret = "J_AT57H5KH_ItmMdu0r6PfXm";
|
||||
})
|
||||
.AddTwitter(options =>
|
||||
{
|
||||
options.ConsumerKey = "lDSPIu480ocnXYZ9DumGCDw37";
|
||||
options.ConsumerSecret = "fpo0oWRNc3vsZKlZSq1PyOSoeXlJd7NnG4Rfc94xbFXsdcc3nH";
|
||||
})
|
||||
// The MicrosoftAccount service has restrictions that prevent the use of
|
||||
// http://localhost:5001/ for test applications.
|
||||
// As such, here is how to change this sample to uses http://ktesting.com:5001/ instead.
|
||||
|
||||
// From an admin command console first enter:
|
||||
// notepad C:\Windows\System32\drivers\etc\hosts
|
||||
// and add this to the file, save, and exit (and reboot?):
|
||||
// 127.0.0.1 ktesting.com
|
||||
|
||||
// Then you can choose to run the app as admin (see below) or add the following ACL as admin:
|
||||
// netsh http add urlacl url=http://ktesting:5001/ user=[domain\user]
|
||||
|
||||
// The sample app can then be run via:
|
||||
// dnx . web
|
||||
.AddMicrosoftAccount(options =>
|
||||
{
|
||||
// MicrosoftAccount requires project changes
|
||||
options.ClientId = "000000004012C08A";
|
||||
options.ClientSecret = "GaMQ2hCnqAC6EcDLnXsAeBVIJOLmeutL";
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
//This method is invoked when ASPNETCORE_ENVIRONMENT is 'Development' or is not defined
|
||||
//The allowed values are Development,Staging and Production
|
||||
public void ConfigureDevelopment(IApplicationBuilder app)
|
||||
{
|
||||
// StatusCode pages to gracefully handle status codes 400-599.
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
// Display custom error page in production when error occurs
|
||||
// During development use the ErrorPage middleware to display error information in the browser
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseMigrationsEndPoint();
|
||||
|
||||
Configure(app);
|
||||
}
|
||||
|
||||
//This method is invoked when ASPNETCORE_ENVIRONMENT is 'Staging'
|
||||
//The allowed values are Development,Staging and Production
|
||||
public void ConfigureStaging(IApplicationBuilder app)
|
||||
{
|
||||
// StatusCode pages to gracefully handle status codes 400-599.
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
|
||||
Configure(app);
|
||||
}
|
||||
|
||||
//This method is invoked when ASPNETCORE_ENVIRONMENT is 'Production'
|
||||
//The allowed values are Development,Staging and Production
|
||||
public void ConfigureProduction(IApplicationBuilder app)
|
||||
{
|
||||
// StatusCode pages to gracefully handle status codes 400-599.
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
|
||||
Configure(app);
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// force the en-US culture, so that the app behaves the same even on machines with different default culture
|
||||
var supportedCultures = new[] { new CultureInfo("en-US") };
|
||||
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-US"),
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures
|
||||
});
|
||||
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Response.Headers["Arch"] = RuntimeInformation.ProcessArchitecture.ToString();
|
||||
return next();
|
||||
});
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add the endpoint routing matcher middleware to the request pipeline
|
||||
app.UseRouting();
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseAuthentication();
|
||||
|
||||
// Add the authorization middleware to the request pipeline
|
||||
app.UseAuthorization();
|
||||
|
||||
// Add endpoints to the request pipeline
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "areaRoute",
|
||||
pattern: "{area:exists}/{controller}/{action}",
|
||||
defaults: new { action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "api",
|
||||
pattern: "{controller}/{id?}");
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MusicStore.Components;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
/// <summary>
|
||||
/// To make runtime to load an environment based startup class, specify the environment by the following ways:
|
||||
/// 1. Drop a Microsoft.AspNetCore.Hosting.ini file in the wwwroot folder
|
||||
/// 2. Add a setting in the ini file named 'ASPNETCORE_ENVIRONMENT' with value of the format 'Startup[EnvironmentName]'.
|
||||
/// For example: To load a Startup class named 'StartupNtlmAuthentication' the value of the env should be
|
||||
/// 'NtlmAuthentication' (eg. ASPNETCORE_ENVIRONMENT=NtlmAuthentication). Runtime adds a 'Startup' prefix to this and
|
||||
/// loads 'StartupNtlmAuthentication'.
|
||||
/// If no environment name is specified the default startup class loaded is 'Startup'.
|
||||
///
|
||||
/// Alternative ways to specify environment are:
|
||||
/// 1. Set the environment variable named SET ASPNETCORE_ENVIRONMENT=NtlmAuthentication
|
||||
/// 2. For selfhost based servers pass in a command line variable named --env with this value. Eg:
|
||||
/// "commands": {
|
||||
/// "web": "Microsoft.AspNetCore.Hosting --server Microsoft.AspNetCore.Server.WebListener
|
||||
/// --server.urls http://localhost:5002 --ASPNETCORE_ENVIRONMENT NtlmAuthentication",
|
||||
/// },
|
||||
/// </summary>
|
||||
public class StartupNtlmAuthentication
|
||||
{
|
||||
public StartupNtlmAuthentication(IWebHostEnvironment hostingEnvironment)
|
||||
{
|
||||
// Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1'
|
||||
// is found in both the registered sources, then the later source will win. By this way a Local config
|
||||
// can be overridden by a different setting while deployed remotely.
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(hostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("config.json")
|
||||
//All environment variables in the process's context flow in as configuration values.
|
||||
.AddEnvironmentVariables();
|
||||
Configuration = builder.Build();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
|
||||
|
||||
// Add EF services to the services container
|
||||
services.AddDbContext<MusicStoreContext>(options =>
|
||||
options.UseSqlite("Data Source=MusicStore.db;Cache=Shared"));
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<MusicStoreContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("CorsPolicy", builder =>
|
||||
{
|
||||
builder.WithOrigins("http://example.com");
|
||||
});
|
||||
});
|
||||
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc();
|
||||
|
||||
// Add memory cache services
|
||||
services.AddMemoryCache();
|
||||
services.AddDistributedMemoryCache();
|
||||
|
||||
// Add session related services.
|
||||
services.AddSession();
|
||||
|
||||
// Add the system clock service
|
||||
services.AddSingleton<ISystemClock, SystemClock>();
|
||||
|
||||
// Configure Auth
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(
|
||||
"ManageStore",
|
||||
authBuilder => {
|
||||
authBuilder.RequireClaim("ManageStore", "Allowed");
|
||||
});
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// force the en-US culture, so that the app behaves the same even on machines with different default culture
|
||||
var supportedCultures = new[] { new CultureInfo("en-US") };
|
||||
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-US"),
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures
|
||||
});
|
||||
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
// Error page middleware displays a nice formatted HTML page for any unhandled exceptions in the
|
||||
// request pipeline.
|
||||
// Note: Not recommended for production.
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseMigrationsEndPoint();
|
||||
|
||||
app.Use((context, next) =>
|
||||
{
|
||||
context.Response.Headers["Arch"] = RuntimeInformation.ProcessArchitecture.ToString();
|
||||
return next();
|
||||
});
|
||||
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
// Who will get admin access? For demo sake I'm listing the currently logged on user as the application
|
||||
// administrator. But this can be changed to suit the needs.
|
||||
var identity = (ClaimsIdentity)context.User.Identity;
|
||||
|
||||
if (context.User.Identity.Name == WindowsIdentity.GetCurrent().Name)
|
||||
{
|
||||
identity.AddClaim(new Claim("ManageStore", "Allowed"));
|
||||
}
|
||||
|
||||
await next.Invoke();
|
||||
});
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add the endpoint routing matcher middleware to the request pipeline
|
||||
app.UseRouting();
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseAuthentication();
|
||||
|
||||
// Add the authorization middleware to the request pipeline
|
||||
app.UseAuthorization();
|
||||
|
||||
// Add endpoints to the request pipeline
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "areaRoute",
|
||||
pattern: "{area:exists}/{controller}/{action}",
|
||||
defaults: new { action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "api",
|
||||
pattern: "{controller}/{id?}");
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices, false).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Localization;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using MusicStore.Components;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore
|
||||
{
|
||||
/// <summary>
|
||||
/// To make runtime to load an environment based startup class, specify the environment by the following ways:
|
||||
/// 1. Drop a Microsoft.AspNetCore.Hosting.ini file in the wwwroot folder
|
||||
/// 2. Add a setting in the ini file named 'ASPNETCORE_ENVIRONMENT' with value of the format 'Startup[EnvironmentName]'.
|
||||
/// For example: To load a Startup class named 'StartupOpenIdConnect' the value of the env should be
|
||||
/// 'OpenIdConnect' (eg. ASPNETCORE_ENVIRONMENT=OpenIdConnect). Runtime adds a 'Startup' prefix to this
|
||||
/// and loads 'StartupOpenIdConnect'.
|
||||
///
|
||||
/// If no environment name is specified the default startup class loaded is 'Startup'.
|
||||
/// Alternative ways to specify environment are:
|
||||
/// 1. Set the environment variable named SET ASPNETCORE_ENVIRONMENT=OpenIdConnect
|
||||
/// 2. For selfhost based servers pass in a command line variable named --env with this value. Eg:
|
||||
/// "commands": {
|
||||
/// "web": "Microsoft.AspNetCore.Hosting --server Microsoft.AspNetCore.Server.WebListener
|
||||
/// --server.urls http://localhost:5002 --ASPNET_ENV OpenIdConnect",
|
||||
/// },
|
||||
/// </summary>
|
||||
public class StartupOpenIdConnect
|
||||
{
|
||||
private readonly Platform _platform;
|
||||
|
||||
public StartupOpenIdConnect(IWebHostEnvironment hostingEnvironment)
|
||||
{
|
||||
// Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1'
|
||||
// is found in both the registered sources, then the later source will win. By this way a Local config can
|
||||
// be overridden by a different setting while deployed remotely.
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(hostingEnvironment.ContentRootPath)
|
||||
.AddJsonFile("config.json")
|
||||
//All environment variables in the process's context flow in as configuration values.
|
||||
.AddEnvironmentVariables();
|
||||
|
||||
Configuration = builder.Build();
|
||||
_platform = new Platform();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
|
||||
|
||||
// Add EF services to the services container
|
||||
services.AddDbContext<MusicStoreContext>(options =>
|
||||
options.UseSqlite("Data Source=MusicStore.db;Cache=Shared"));
|
||||
|
||||
// Add Identity services to the services container
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
.AddEntityFrameworkStores<MusicStoreContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("CorsPolicy", builder =>
|
||||
{
|
||||
builder.WithOrigins("http://example.com");
|
||||
});
|
||||
});
|
||||
|
||||
// Add MVC services to the services container
|
||||
services.AddMvc();
|
||||
|
||||
// Add memory cache services
|
||||
services.AddMemoryCache();
|
||||
services.AddDistributedMemoryCache();
|
||||
|
||||
// Add session related services.
|
||||
services.AddSession();
|
||||
|
||||
// Add the system clock service
|
||||
services.AddSingleton<ISystemClock, SystemClock>();
|
||||
|
||||
// Configure Auth
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(
|
||||
"ManageStore",
|
||||
authBuilder =>
|
||||
{
|
||||
authBuilder.RequireClaim("ManageStore", "Allowed");
|
||||
});
|
||||
});
|
||||
|
||||
// Create an Azure Active directory application and copy paste the following
|
||||
services.AddAuthentication().AddOpenIdConnect(options =>
|
||||
{
|
||||
options.Authority = "https://login.windows.net/[tenantName].onmicrosoft.com";
|
||||
options.ClientId = "[ClientId]";
|
||||
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
|
||||
});
|
||||
|
||||
services.AddDatabaseDeveloperPageExceptionFilter();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// force the en-US culture, so that the app behaves the same even on machines with different default culture
|
||||
var supportedCultures = new[] { new CultureInfo("en-US") };
|
||||
|
||||
app.UseRequestLocalization(new RequestLocalizationOptions
|
||||
{
|
||||
DefaultRequestCulture = new RequestCulture("en-US"),
|
||||
SupportedCultures = supportedCultures,
|
||||
SupportedUICultures = supportedCultures
|
||||
});
|
||||
|
||||
app.UseStatusCodePagesWithRedirects("~/Home/StatusCodePage");
|
||||
|
||||
// Display custom error page in production when error occurs
|
||||
// During development use the ErrorPage middleware to display error information in the browser
|
||||
app.UseDeveloperExceptionPage();
|
||||
|
||||
app.UseMigrationsEndPoint();
|
||||
|
||||
// Configure Session.
|
||||
app.UseSession();
|
||||
|
||||
// Add static files to the request pipeline
|
||||
app.UseStaticFiles();
|
||||
|
||||
// Add the endpoint routing matcher middleware to the request pipeline
|
||||
app.UseRouting();
|
||||
|
||||
// Add cookie-based authentication to the request pipeline
|
||||
app.UseAuthentication();
|
||||
|
||||
// Add the authorization middleware to the request pipeline
|
||||
app.UseAuthorization();
|
||||
|
||||
// Add endpoints to the request pipeline
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
name: "areaRoute",
|
||||
pattern: "{area:exists}/{controller}/{action}",
|
||||
defaults: new { action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "default",
|
||||
pattern: "{controller}/{action}/{id?}",
|
||||
defaults: new { controller = "Home", action = "Index" });
|
||||
|
||||
endpoints.MapControllerRoute(
|
||||
name: "api",
|
||||
pattern: "{controller}/{id?}");
|
||||
});
|
||||
|
||||
//Populates the MusicStore sample data
|
||||
SampleData.InitializeMusicStoreDatabaseAsync(app.ApplicationServices).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
namespace MusicStore.ViewModels
|
||||
{
|
||||
public class AlbumData
|
||||
{
|
||||
public string Title { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
namespace MusicStore.ViewModels
|
||||
{
|
||||
public class ShoppingCartRemoveViewModel
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public decimal CartTotal { get; set; }
|
||||
public int CartCount { get; set; }
|
||||
public int ItemCount { get; set; }
|
||||
public int DeleteId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using MusicStore.Models;
|
||||
|
||||
namespace MusicStore.ViewModels
|
||||
{
|
||||
public class ShoppingCartViewModel
|
||||
{
|
||||
public List<CartItem> CartItems { get; set; }
|
||||
public decimal CartTotal { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Confirm Email";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
<div>
|
||||
<p>
|
||||
Thank you for confirming your email. Please <a asp-controller="Account" asp-action="Login">Click here to Log in</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
@model ExternalLoginConfirmationViewModel
|
||||
@{
|
||||
ViewBag.Title = "Register";
|
||||
}
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
<h3>Associate your @ViewBag.LoginProvider account.</h3>
|
||||
|
||||
<form asp-controller="Account" asp-action="ExternalLoginConfirmation" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" class="form-horizontal" role="form">
|
||||
<h4>Association Form</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<p class="text-info">
|
||||
You've successfully authenticated with <strong>@ViewBag.LoginProvider</strong>.
|
||||
Please enter a user name for this site below and click the Register button to finish
|
||||
logging in.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</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>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Login Failure";
|
||||
}
|
||||
|
||||
<hgroup>
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
<h3 class="text-danger">Unsuccessful login with service.</h3>
|
||||
</hgroup>
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
@model ForgotPasswordViewModel
|
||||
@{
|
||||
ViewBag.Title = "Forgot your password?";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="ForgotPassword" method="post" class="form-horizontal" role="form">
|
||||
<h4>Enter your email.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" class="btn btn-default" value="Email Link" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Forgot Password Confirmation";
|
||||
}
|
||||
|
||||
<hgroup class="title">
|
||||
<h1>@ViewBag.Title.</h1>
|
||||
</hgroup>
|
||||
<div>
|
||||
<p>
|
||||
Please check your email to reset your password.
|
||||
</p>
|
||||
<p>
|
||||
For demo purpose only: <a asp-action="ResetPassword" asp-route-code="@ViewBag.Code">Click here to reset the password</a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
@model LoginViewModel
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Log in";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<section id="loginForm">
|
||||
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" class="form-horizontal" role="form">
|
||||
<h4>Use a local account to log in.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<div class="checkbox">
|
||||
<input asp-for="RememberMe" />
|
||||
<label asp-for="RememberMe"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Log in" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<a asp-action="Register">Register as a new user?</a>
|
||||
</p>
|
||||
<p>
|
||||
<a asp-action="ForgotPassword">Forgot your password?</a>
|
||||
</p>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<section id="socialLoginForm">
|
||||
@await Html.PartialAsync("_ExternalLoginsListPartial", new ExternalLoginListViewModel { ReturnUrl = ViewBag.ReturnUrl })
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
@model RegisterViewModel
|
||||
@{
|
||||
ViewBag.Title = "Register";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="Register" method="post" class="form-horizontal" role="form">
|
||||
<h4>Create a new account.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="ConfirmPassword" class="form-control" />
|
||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||
</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>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Register Confirmation";
|
||||
}
|
||||
|
||||
<hgroup class="title">
|
||||
<h1>@ViewBag.Title.</h1>
|
||||
</hgroup>
|
||||
<div>
|
||||
<p>
|
||||
Please check your email to activate your account.
|
||||
</p>
|
||||
<p>
|
||||
Demo/testing purposes only: The sample displays the code and user id in the page: <a asp-action="ConfirmEmail" asp-route-code="@ViewBag.Code" asp-route-userId="@ViewBag.UserId">Click here to confirm your email: </a>
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
@model ResetPasswordViewModel
|
||||
@{
|
||||
ViewBag.Title = "Reset password";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="ResetPassword" method="post" class="form-horizontal" role="form">
|
||||
<h4>Reset your password.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<input asp-for="Code" type="hidden" />
|
||||
<div class="form-group">
|
||||
<label asp-for="Email" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Email" class="form-control" />
|
||||
<span asp-validation-for="Email" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Password" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Password" class="form-control" />
|
||||
<span asp-validation-for="Password" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="ConfirmPassword" class="form-control" />
|
||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" class="btn btn-default" value="Reset" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Reset password confirmation";
|
||||
}
|
||||
|
||||
<hgroup class="title">
|
||||
<h1>@ViewBag.Title.</h1>
|
||||
</hgroup>
|
||||
<div>
|
||||
<p>
|
||||
Your password has been reset. Please <a asp-controller="Account" asp-action="Login">Click here to log in</a>.
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
@model SendCodeViewModel
|
||||
@{
|
||||
ViewBag.Title = "Send Verification Code";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="SendCode" asp-route-returnurl="@Model.ReturnUrl" method="post" class="form-horizontal" role="form">
|
||||
<input asp-for="RememberMe" type="hidden" />
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
Select Two-Factor Authentication Provider:
|
||||
<select asp-for="SelectedProvider" asp-items="Model.Providers"></select>
|
||||
<input type="submit" value="Submit" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
@model VerifyCodeViewModel
|
||||
@{
|
||||
ViewBag.Title = "Verify";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Account" asp-action="VerifyCode" asp-route-returnurl="@Model.ReturnUrl" method="post" class="form-horizontal" role="form">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<input asp-for="Provider" type="hidden" />
|
||||
<input asp-for="RememberMe" type="hidden" />
|
||||
<h4>Enter verification code</h4>
|
||||
<p class="text-danger">
|
||||
For DEMO only: You can type in this code in the below text box to proceed: [ @ViewBag.Code ]
|
||||
<br />
|
||||
Please change this code to register an SMS/Email service in IdentityConfig to send a message.
|
||||
</p>
|
||||
<hr />
|
||||
<div class="form-group">
|
||||
<label asp-for="Code" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Code" class="form-control" />
|
||||
<span asp-validation-for="Code" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<div class="checkbox">
|
||||
<input asp-for="RememberBrowser" />
|
||||
<label asp-for="RememberBrowser"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" class="btn btn-default" value="Submit" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
@using Microsoft.AspNetCore.Authentication
|
||||
@model ExternalLoginListViewModel
|
||||
@inject IAuthenticationSchemeProvider SchemeProvider
|
||||
<h4>Use another service to log in.</h4>
|
||||
<hr />
|
||||
@{
|
||||
var schemes = await SchemeProvider.GetAllSchemesAsync();
|
||||
var loginProviders = schemes.ToList();
|
||||
if (!loginProviders.Any())
|
||||
{
|
||||
<div>
|
||||
<p>
|
||||
There are no external authentication services configured. See <a href="http://go.microsoft.com/fwlink/?LinkId=313242">this article</a>
|
||||
for details on setting up this ASP.NET application to support logging in via external services.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form asp-controller="Account" asp-action="ExternalLogin" asp-route-returnurl="@ViewBag.ReturnUrl" method="post" class="form-horizontal" role="form">
|
||||
<div id="socialLoginList">
|
||||
<p>
|
||||
@foreach (var p in loginProviders)
|
||||
{
|
||||
<button type="submit" class="btn btn-default" id="@p.Name" name="provider" value="@p.Name" title="Log in using your @p.Name account">@p.Name</button>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
@model Order
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Address And Payment";
|
||||
}
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
||||
<form asp-antiforgery="true">
|
||||
<h2>Address And Payment</h2>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<fieldset>
|
||||
<legend>Shipping Information</legend>
|
||||
|
||||
@Html.EditorForModel()
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Payment</legend>
|
||||
<p>We're running a promotion: all music is free with the promo code: "FREE"</p>
|
||||
|
||||
<div class="editor-label">
|
||||
<label title="Promo Code">Promo Code</label>
|
||||
</div>
|
||||
<div class="editor-field">
|
||||
@Html.TextBox("PromoCode")
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Submit Order" />
|
||||
</form>
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
@model int
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Checkout Complete";
|
||||
}
|
||||
|
||||
<h2>Checkout Complete</h2>
|
||||
|
||||
<p>Thanks for your order! Your order number is: @Model</p>
|
||||
|
||||
<p>
|
||||
How about shopping for some more music in our
|
||||
<a asp-controller="Home" asp-action="Index">Store</a>
|
||||
</p>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
@inject IOptions<AppSettings> AppSettings
|
||||
@{
|
||||
ViewBag.Title = "Home Page";
|
||||
}
|
||||
|
||||
<div class="jumbotron">
|
||||
<h1>@AppSettings.Value.SiteTitle</h1>
|
||||
<img src="~/Images/home-showcase.png" />
|
||||
</div>
|
||||
|
||||
<ul class="row list-unstyled" id="album-list">
|
||||
@foreach (var album in Model)
|
||||
{
|
||||
<li class="col-lg-2 col-md-2 col-sm-2 col-xs-4 container">
|
||||
<a asp-controller="Store" asp-action="Details" asp-route-id="@album.AlbumId">
|
||||
<img alt="@album.Title" src="@Url.Content(@album.AlbumArtUrl)" />
|
||||
<h4>@album.Title</h4>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
@model AddPhoneNumberViewModel
|
||||
@{
|
||||
ViewBag.Title = "Add Phone Number";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
<form asp-controller="Manage" asp-action="AddPhoneNumber" method="post" class="form-horizontal" role="form">
|
||||
<h4>Add a phone number.</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Number" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Number" class="form-control" />
|
||||
<span asp-validation-for="Number" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" class="btn btn-default" value="Send verification code" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
@model ChangePasswordViewModel
|
||||
@{
|
||||
ViewBag.Title = "Change Password";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Manage" asp-action="ChangePassword" method="post" class="form-horizontal" role="form">
|
||||
<h4>Change Password Form</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="OldPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="OldPassword" class="form-control" />
|
||||
<span asp-validation-for="OldPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="NewPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="NewPassword" class="form-control" />
|
||||
<span asp-validation-for="NewPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="ConfirmPassword" class="form-control" />
|
||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Change password" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
@model IndexViewModel
|
||||
@{
|
||||
ViewData["Title"] = "Manage your account";
|
||||
}
|
||||
|
||||
<h2>@ViewData["Title"].</h2>
|
||||
<p class="text-success">@ViewData["StatusMessage"]</p>
|
||||
|
||||
<div>
|
||||
<h4>Change your account settings</h4>
|
||||
<hr />
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Password:</dt>
|
||||
<dd>
|
||||
@if (Model.HasPassword)
|
||||
{
|
||||
<text>[ <a asp-controller="Manage" asp-action="ChangePassword">Change</a> ]</text>
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>[ <a asp-controller="Manage" asp-action="SetPassword">Create</a> ]</text>
|
||||
}
|
||||
</dd>
|
||||
<dt>External Logins:</dt>
|
||||
<dd>
|
||||
@Model.Logins.Count [ <a asp-controller="Manage" asp-action="ManageLogins">Manage</a> ]
|
||||
</dd>
|
||||
<dt>Phone Number:</dt>
|
||||
<dd>
|
||||
<p>
|
||||
Phone Numbers can used as a second factor of verification in two-factor authentication.
|
||||
See <a href="http://go.microsoft.com/fwlink/?LinkID=532713">this article</a>
|
||||
for details on setting up this ASP.NET application to support two-factor authentication using SMS.
|
||||
</p>
|
||||
@*@(Model.PhoneNumber ?? "None")
|
||||
@if (Model.PhoneNumber != null)
|
||||
{
|
||||
<br />
|
||||
<text>[ <a asp-controller="Manage" asp-action="AddPhoneNumber">Change</a> ]</text>
|
||||
<form asp-controller="Manage" asp-action="RemovePhoneNumber" method="post" role="form">
|
||||
[<button type="submit" class="btn-link">Remove</button>]
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<text>[ <a asp-controller="Manage" asp-action="AddPhoneNumber">Add</a> ]</text>
|
||||
}*@
|
||||
</dd>
|
||||
|
||||
<dt>Two-Factor Authentication:</dt>
|
||||
<dd>
|
||||
<p>
|
||||
There are no two-factor authentication providers configured. See <a href="http://go.microsoft.com/fwlink/?LinkID=532713">this article</a>
|
||||
for setting up this application to support two-factor authentication.
|
||||
</p>
|
||||
@*@if (Model.TwoFactor)
|
||||
{
|
||||
<form asp-controller="Manage" asp-action="DisableTwoFactorAuthentication" method="post" class="form-horizontal" role="form">
|
||||
Enabled [<button type="submit" class="btn-link">Disable</button>]
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form asp-controller="Manage" asp-action="EnableTwoFactorAuthentication" method="post" class="form-horizontal" role="form">
|
||||
[<button type="submit" class="btn-link">Enable</button>] Disabled
|
||||
</form>
|
||||
}*@
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
@model ManageLoginsViewModel
|
||||
@{
|
||||
ViewBag.Title = "Manage your external logins";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<p class="text-success">@ViewBag.StatusMessage</p>
|
||||
@if (Model.CurrentLogins.Count > 0)
|
||||
{
|
||||
<h4>Registered Logins</h4>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
@foreach (var account in Model.CurrentLogins)
|
||||
{
|
||||
<tr>
|
||||
<td>@account.LoginProvider</td>
|
||||
<td>
|
||||
@if (ViewBag.ShowRemoveButton)
|
||||
{
|
||||
<form asp-controller="Manage" asp-action="RemoveLogin" method="post" class="form-horizontal" role="form">
|
||||
<div>
|
||||
<input asp-for="@account.LoginProvider" type="hidden" />
|
||||
<input asp-for="@account.ProviderKey" type="hidden" />
|
||||
<input type="submit" class="btn btn-default" value="Remove" title="Remove this @account.LoginProvider login from your account" />
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
@:
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
@if (Model.OtherLogins.Any())
|
||||
{
|
||||
<h4>Add another service to log in.</h4>
|
||||
<hr />
|
||||
<form asp-controller="Manage" asp-action="LinkLogin" method="post" class="form-horizontal" role="form">
|
||||
<div id="socialLoginList">
|
||||
<p>
|
||||
@foreach (var p in Model.OtherLogins)
|
||||
{
|
||||
<button type="submit" class="btn btn-default" id="@p.Name" name="provider" value="@p.Name" title="Log in using your @p.Name account">@p.Name</button>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
@model SetPasswordViewModel
|
||||
@{
|
||||
ViewBag.Title = "Set Password";
|
||||
}
|
||||
|
||||
<p class="text-info">
|
||||
You do not have a local username/password for this site. Add a local
|
||||
account so you can log in without an external login.
|
||||
</p>
|
||||
|
||||
<form asp-controller="Manage" asp-action="SetPassword" method="post" class="form-horizontal" role="form">
|
||||
<h4>Set your password</h4>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="NewPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="NewPassword" class="form-control" />
|
||||
<span asp-validation-for="NewPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="ConfirmPassword" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="ConfirmPassword" class="form-control" />
|
||||
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" value="Set password" class="btn btn-default" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
@model VerifyPhoneNumberViewModel
|
||||
@{
|
||||
ViewBag.Title = "Verify Phone Number";
|
||||
}
|
||||
|
||||
<h2>@ViewBag.Title.</h2>
|
||||
|
||||
<form asp-controller="Manage" asp-action="VerifyPhoneNumber" method="post" class="form-horizontal" role="form">
|
||||
<input asp-for="PhoneNumber" type="hidden" />
|
||||
<h4>Enter verification code</h4>
|
||||
<p class="text-danger">
|
||||
For DEMO only: You can type in this code in the below text box to proceed: @ViewBag.Code
|
||||
<br />
|
||||
Please change this code to register an SMS service in IdentityConfig to send a text message.
|
||||
</p>
|
||||
<hr />
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Code" class="col-md-2 control-label"></label>
|
||||
<div class="col-md-10">
|
||||
<input asp-for="Code" class="form-control" />
|
||||
<span asp-validation-for="Code" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-offset-2 col-md-10">
|
||||
<input type="submit" class="btn btn-default" value="Submit" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@section Scripts {
|
||||
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Access denied due to insufficient permissions";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Access denied due to insufficient permissions.</h1>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
@model Album
|
||||
|
||||
@if (Model != null)
|
||||
{
|
||||
<li>
|
||||
<br />
|
||||
<small><i><label title="New Arrivals!" style="color:red">New Arrivals!</label></i></small>
|
||||
<i><a id="NewArrivalsPanel" asp-area="" asp-controller="Store" asp-action="Details" asp-route-id="@Model.AlbumId">@Model.Title</a></i>
|
||||
</li>
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
@if (ViewBag.CartCount > 0)
|
||||
{
|
||||
<li>
|
||||
<a asp-area="" asp-controller="ShoppingCart" asp-action="Index" title="@ViewBag.CartSummary">
|
||||
<span class="glyphicon glyphicon-shopping-cart"></span>
|
||||
<span id="cart-status">
|
||||
@ViewBag.CartCount
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
@model IEnumerable<string>
|
||||
|
||||
<li class="dropdown">
|
||||
<a asp-area="" asp-controller="Store" asp-action="Index" class="dropdown-toggle" data-toggle="dropdown">Store <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
@foreach (var genreName in Model)
|
||||
{
|
||||
<li>
|
||||
<a asp-area="" asp-controller="Store" asp-action="Browse" asp-route-Genre="@genreName">@genreName</a>
|
||||
</li>
|
||||
}
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a asp-area="" asp-controller="Store" asp-action="Index">More...</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Demo link display page - Not for production use";
|
||||
}
|
||||
|
||||
<hgroup class="title">
|
||||
<h1>@ViewBag.Title.</h1>
|
||||
</hgroup>
|
||||
<div>
|
||||
<p>
|
||||
Demo link display page - Not for production use.
|
||||
</p>
|
||||
|
||||
@if (ViewBag.Link != null)
|
||||
{
|
||||
<p class="text-danger">
|
||||
For DEMO only: You can click this link to confirm the email: [[<a href="@ViewBag.Link">link</a>]]
|
||||
<br />
|
||||
Please change this code to register an email service in IdentityConfig to send an email.
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Error";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Locked Out";
|
||||
}
|
||||
|
||||
<hgroup>
|
||||
<h1 class="text-danger">Locked out.</h1>
|
||||
<h2 class="text-danger">This account has been locked out, please try again later.</h2>
|
||||
</hgroup>
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
@{
|
||||
ViewBag.Title = "Item not found";
|
||||
}
|
||||
|
||||
<h1 class="text-danger">Item not found.</h1>
|
||||
<h2 class="text-danger">Unable to find the item you are searching for. Please try again.</h2>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue