├── .gitignore ├── .nuget ├── NuGet.Config ├── NuGet.exe └── NuGet.targets ├── EBookClient ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── Logo.scale-100.png │ ├── SmallLogo.scale-100.png │ ├── SplashScreen.scale-100.png │ └── StoreLogo.scale-100.png ├── Common │ ├── NavigationHelper.cs │ ├── ObservableDictionary.cs │ ├── ReadMe.txt │ ├── RelayCommand.cs │ └── SuspensionManager.cs ├── EBookClient.csproj ├── MainPage.xaml ├── MainPage.xaml.cs ├── MyEbooksPage.xaml ├── MyEbooksPage.xaml.cs ├── OAuthSettings.cs ├── OAuthTokenInfo.cs ├── Package.appxmanifest ├── Properties │ └── AssemblyInfo.cs ├── Queries │ ├── GetAllEbookPartsQuery.cs │ ├── GetMyEbooksQuery.cs │ ├── LoginInteractivelyAsync.cs │ ├── LoginPassivelyQuery.cs │ └── WhoAmiQuery.cs ├── UserName.cs └── packages.config ├── EbookManager.BackOffice ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ ├── IdentityConfig.cs │ └── RouteConfig.cs ├── Content │ ├── Site.css │ ├── bootstrap.css │ └── bootstrap.min.css ├── Controllers │ ├── AccountController.cs │ ├── AdminCatalogController.cs │ └── HomeController.cs ├── EbookManager.BackOffice.csproj ├── EbookPartFileHandler.ashx ├── EbookPartFileHandler.ashx.cs ├── Global.asax ├── Global.asax.cs ├── Models │ ├── AddEbookModel.cs │ ├── AdminCatalogModel.cs │ ├── EditEbookModel.cs │ ├── TenantDbContext.cs │ └── TenantRegistrationModels.cs ├── Project_Readme.html ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── _references.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-1.10.2.intellisense.js │ ├── jquery-1.10.2.js │ ├── jquery-1.10.2.min.js │ ├── jquery-1.10.2.min.map │ ├── jquery.validate-vsdoc.js │ ├── jquery.validate.js │ ├── jquery.validate.min.js │ ├── jquery.validate.unobtrusive.js │ ├── jquery.validate.unobtrusive.min.js │ ├── modernizr-2.6.2.js │ ├── respond.js │ └── respond.min.js ├── Utils │ └── DatabaseIssuerNameRegistry.cs ├── Views │ ├── Account │ │ └── SignOutCallback.cshtml │ ├── AdminCatalog │ │ ├── Add.cshtml │ │ ├── DeleteBook.cshtml │ │ ├── EditEBook.cshtml │ │ └── Index.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _Layout.cshtml │ │ └── _LoginPartial.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── packages.config ├── EbookManager.CommonViewModels ├── EbookManager.CommonViewModels.csproj ├── EbookPartViewModel.cs ├── EbookViewModel.cs └── Properties │ └── AssemblyInfo.cs ├── EbookManager.Database ├── DatabaseToProject.scmp ├── EbookManager.Database.sqlproj ├── ProjectToDatabase.scmp └── dbo │ └── Tables │ ├── T_EbookParts.sql │ ├── T_Ebooks.sql │ ├── T_Roles.sql │ ├── T_UserClaims.sql │ ├── T_UserEbooks.sql │ ├── T_UserLogins.sql │ ├── T_UserRoles.sql │ └── T_Users.sql ├── EbookManager.Domain ├── Catalog │ ├── Ebook.cs │ ├── EbookPart.cs │ └── UserEbook.cs ├── EbookManager.Domain.csproj ├── Properties │ └── AssemblyInfo.cs ├── Users │ ├── Role.cs │ ├── User.cs │ ├── UserClaim.cs │ ├── UserLogin.cs │ └── UserRole.cs └── packages.config ├── EbookManager.Repositories ├── App.config ├── CatalogRepository.cs ├── EbookManager.Repositories.csproj ├── EbookManagerDbContext.cs ├── Properties │ └── AssemblyInfo.cs ├── UsersRepository.cs └── packages.config ├── EbookManager.Web ├── Api │ ├── EbooksController.cs │ └── UserController.cs ├── App_Start │ ├── BundleConfig.cs │ ├── FilterConfig.cs │ ├── RouteConfig.cs │ ├── Startup.Log.cs │ ├── Startup.OAuth2.cs │ ├── Startup.WebAuth.cs │ └── WebApiConfig.cs ├── AuthenticationTokens.cs ├── Content │ ├── Site.css │ ├── bootstrap.css │ └── bootstrap.min.css ├── Controllers │ ├── AccountController.cs │ ├── CatalogController.cs │ ├── DemoAuthenticatedController.cs │ ├── HomeController.cs │ └── MySpaceController.cs ├── EbookManager.Web.csproj ├── Global.asax ├── Global.asax.cs ├── Models │ ├── AccountViewModels.cs │ └── CatalogViewModel.cs ├── OAuthServerProvider.cs ├── Owin │ └── LogMiddleware.cs ├── Project_Readme.html ├── Properties │ └── AssemblyInfo.cs ├── Scripts │ ├── _references.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-1.10.2.intellisense.js │ ├── jquery-1.10.2.js │ ├── jquery-1.10.2.min.js │ ├── jquery-1.10.2.min.map │ ├── jquery.validate-vsdoc.js │ ├── jquery.validate.js │ ├── jquery.validate.min.js │ ├── jquery.validate.unobtrusive.js │ ├── jquery.validate.unobtrusive.min.js │ ├── modernizr-2.6.2.js │ ├── respond.js │ └── respond.min.js ├── Startup.cs ├── Views │ ├── Account │ │ ├── ExternalLoginConfirmation.cshtml │ │ ├── ExternalLoginFailure.cshtml │ │ ├── Login.cshtml │ │ ├── Manage.cshtml │ │ ├── Register.cshtml │ │ ├── _ChangePasswordPartial.cshtml │ │ ├── _ExternalLoginsListPartial.cshtml │ │ ├── _RemoveAccountPartial.cshtml │ │ └── _SetPasswordPartial.cshtml │ ├── Catalog │ │ ├── Details.cshtml │ │ └── Index.cshtml │ ├── DemoAuthenticated │ │ └── Index.cshtml │ ├── Home │ │ └── Index.cshtml │ ├── MySpace │ │ └── Index.cshtml │ ├── Shared │ │ ├── DisplayTemplates │ │ │ └── EbookViewModel.cshtml │ │ ├── Error.cshtml │ │ ├── _Layout.cshtml │ │ └── _LoginPartial.cshtml │ ├── Web.config │ └── _ViewStart.cshtml ├── Web.Debug.config ├── Web.Release.config ├── Web.config ├── favicon.ico ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── packages.config ├── EbookManager.sln ├── README.md └── UnitTests ├── AssemblyResolver.cs ├── Properties └── AssemblyInfo.cs ├── TestAuthenticationMiddleware.cs ├── UnitTests.csproj ├── WebApiTests.cs ├── app.config └── packages.config /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | *.pubxml 98 | 99 | # NuGet Packages Directory 100 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 101 | packages/ 102 | 103 | # Windows Azure Build Output 104 | csx 105 | *.build.csdef 106 | 107 | # Windows Store app package directory 108 | AppPackages/ 109 | 110 | # Others 111 | sql/ 112 | *.Cache 113 | ClientBin/ 114 | [Ss]tyle[Cc]op.* 115 | ~$* 116 | *~ 117 | *.dbmdl 118 | *.[Pp]ublish.xml 119 | *.pfx 120 | *.publishsettings 121 | 122 | # RIA/Silverlight projects 123 | Generated_Code/ 124 | 125 | # Backup & report files from converting an old project file to a newer 126 | # Visual Studio version. Backup files are not needed, because we have git ;-) 127 | _UpgradeReport_Files/ 128 | Backup*/ 129 | UpgradeLog*.XML 130 | UpgradeLog*.htm 131 | 132 | # SQL Server files 133 | App_Data/*.mdf 134 | App_Data/*.ldf 135 | 136 | # ========================= 137 | # Windows detritus 138 | # ========================= 139 | 140 | # Windows image file caches 141 | Thumbs.db 142 | ehthumbs.db 143 | 144 | # Folder config file 145 | Desktop.ini 146 | 147 | # Recycle Bin used on file shares 148 | $RECYCLE.BIN/ 149 | 150 | # Mac crap 151 | .DS_Store 152 | -------------------------------------------------------------------------------- /.nuget/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/.nuget/NuGet.exe -------------------------------------------------------------------------------- /EBookClient/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | -------------------------------------------------------------------------------- /EBookClient/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.ApplicationModel; 7 | using Windows.ApplicationModel.Activation; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI.Xaml; 11 | using Windows.UI.Xaml.Controls; 12 | using Windows.UI.Xaml.Controls.Primitives; 13 | using Windows.UI.Xaml.Data; 14 | using Windows.UI.Xaml.Input; 15 | using Windows.UI.Xaml.Media; 16 | using Windows.UI.Xaml.Navigation; 17 | 18 | // The Blank Application template is documented at http://go.microsoft.com/fwlink/?LinkId=234227 19 | 20 | namespace EBookClient 21 | { 22 | /// 23 | /// Provides application-specific behavior to supplement the default Application class. 24 | /// 25 | sealed partial class App : Application 26 | { 27 | /// 28 | /// Initializes the singleton application object. This is the first line of authored code 29 | /// executed, and as such is the logical equivalent of main() or WinMain(). 30 | /// 31 | public App() 32 | { 33 | this.InitializeComponent(); 34 | this.Suspending += OnSuspending; 35 | } 36 | 37 | /// 38 | /// Invoked when the application is launched normally by the end user. Other entry points 39 | /// will be used such as when the application is launched to open a specific file. 40 | /// 41 | /// Details about the launch request and process. 42 | protected override void OnLaunched(LaunchActivatedEventArgs e) 43 | { 44 | 45 | #if DEBUG 46 | if (System.Diagnostics.Debugger.IsAttached) 47 | { 48 | this.DebugSettings.EnableFrameRateCounter = true; 49 | } 50 | #endif 51 | 52 | Frame rootFrame = Window.Current.Content as Frame; 53 | 54 | // Do not repeat app initialization when the Window already has content, 55 | // just ensure that the window is active 56 | if (rootFrame == null) 57 | { 58 | // Create a Frame to act as the navigation context and navigate to the first page 59 | rootFrame = new Frame(); 60 | // Set the default language 61 | rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; 62 | 63 | rootFrame.NavigationFailed += OnNavigationFailed; 64 | 65 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 66 | { 67 | //TODO: Load state from previously suspended application 68 | } 69 | 70 | // Place the frame in the current Window 71 | Window.Current.Content = rootFrame; 72 | } 73 | 74 | if (rootFrame.Content == null) 75 | { 76 | // When the navigation stack isn't restored navigate to the first page, 77 | // configuring the new page by passing required information as a navigation 78 | // parameter 79 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 80 | } 81 | // Ensure the current window is active 82 | Window.Current.Activate(); 83 | } 84 | 85 | /// 86 | /// Invoked when Navigation to a certain page fails 87 | /// 88 | /// The Frame which failed navigation 89 | /// Details about the navigation failure 90 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 91 | { 92 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 93 | } 94 | 95 | /// 96 | /// Invoked when application execution is being suspended. Application state is saved 97 | /// without knowing whether the application will be terminated or resumed with the contents 98 | /// of memory still intact. 99 | /// 100 | /// The source of the suspend request. 101 | /// Details about the suspend request. 102 | private void OnSuspending(object sender, SuspendingEventArgs e) 103 | { 104 | var deferral = e.SuspendingOperation.GetDeferral(); 105 | //TODO: Save application state and stop any background activity 106 | deferral.Complete(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /EBookClient/Assets/Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EBookClient/Assets/Logo.scale-100.png -------------------------------------------------------------------------------- /EBookClient/Assets/SmallLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EBookClient/Assets/SmallLogo.scale-100.png -------------------------------------------------------------------------------- /EBookClient/Assets/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EBookClient/Assets/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /EBookClient/Assets/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EBookClient/Assets/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /EBookClient/Common/ObservableDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Windows.Foundation.Collections; 5 | 6 | namespace EBookClient.Common 7 | { 8 | /// 9 | /// Implementation of IObservableMap that supports reentrancy for use as a default view 10 | /// model. 11 | /// 12 | public class ObservableDictionary : IObservableMap 13 | { 14 | private class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs 15 | { 16 | public ObservableDictionaryChangedEventArgs(CollectionChange change, string key) 17 | { 18 | this.CollectionChange = change; 19 | this.Key = key; 20 | } 21 | 22 | public CollectionChange CollectionChange { get; private set; } 23 | public string Key { get; private set; } 24 | } 25 | 26 | private Dictionary _dictionary = new Dictionary(); 27 | public event MapChangedEventHandler MapChanged; 28 | 29 | private void InvokeMapChanged(CollectionChange change, string key) 30 | { 31 | var eventHandler = MapChanged; 32 | if (eventHandler != null) 33 | { 34 | eventHandler(this, new ObservableDictionaryChangedEventArgs(change, key)); 35 | } 36 | } 37 | 38 | public void Add(string key, object value) 39 | { 40 | this._dictionary.Add(key, value); 41 | this.InvokeMapChanged(CollectionChange.ItemInserted, key); 42 | } 43 | 44 | public void Add(KeyValuePair item) 45 | { 46 | this.Add(item.Key, item.Value); 47 | } 48 | 49 | public bool Remove(string key) 50 | { 51 | if (this._dictionary.Remove(key)) 52 | { 53 | this.InvokeMapChanged(CollectionChange.ItemRemoved, key); 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | public bool Remove(KeyValuePair item) 60 | { 61 | object currentValue; 62 | if (this._dictionary.TryGetValue(item.Key, out currentValue) && 63 | Object.Equals(item.Value, currentValue) && this._dictionary.Remove(item.Key)) 64 | { 65 | this.InvokeMapChanged(CollectionChange.ItemRemoved, item.Key); 66 | return true; 67 | } 68 | return false; 69 | } 70 | 71 | public object this[string key] 72 | { 73 | get 74 | { 75 | return this._dictionary[key]; 76 | } 77 | set 78 | { 79 | this._dictionary[key] = value; 80 | this.InvokeMapChanged(CollectionChange.ItemChanged, key); 81 | } 82 | } 83 | 84 | public void Clear() 85 | { 86 | var priorKeys = this._dictionary.Keys.ToArray(); 87 | this._dictionary.Clear(); 88 | foreach (var key in priorKeys) 89 | { 90 | this.InvokeMapChanged(CollectionChange.ItemRemoved, key); 91 | } 92 | } 93 | 94 | public ICollection Keys 95 | { 96 | get { return this._dictionary.Keys; } 97 | } 98 | 99 | public bool ContainsKey(string key) 100 | { 101 | return this._dictionary.ContainsKey(key); 102 | } 103 | 104 | public bool TryGetValue(string key, out object value) 105 | { 106 | return this._dictionary.TryGetValue(key, out value); 107 | } 108 | 109 | public ICollection Values 110 | { 111 | get { return this._dictionary.Values; } 112 | } 113 | 114 | public bool Contains(KeyValuePair item) 115 | { 116 | return this._dictionary.Contains(item); 117 | } 118 | 119 | public int Count 120 | { 121 | get { return this._dictionary.Count; } 122 | } 123 | 124 | public bool IsReadOnly 125 | { 126 | get { return false; } 127 | } 128 | 129 | public IEnumerator> GetEnumerator() 130 | { 131 | return this._dictionary.GetEnumerator(); 132 | } 133 | 134 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 135 | { 136 | return this._dictionary.GetEnumerator(); 137 | } 138 | 139 | public void CopyTo(KeyValuePair[] array, int arrayIndex) 140 | { 141 | int arraySize = array.Length; 142 | foreach (var pair in this._dictionary) 143 | { 144 | if (arrayIndex >= arraySize) break; 145 | array[arrayIndex++] = pair; 146 | } 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /EBookClient/Common/ReadMe.txt: -------------------------------------------------------------------------------- 1 | The Common directory contains classes that simplify application development. 2 | 3 | Classes in the Common directory form part of your project and may be further enhanced to meet your 4 | needs. Care should be taken when altering existing methods and properties as incompatible changes 5 | will require corresponding changes to code included in a variety of Visual Studio templates. For 6 | example, additional pages added to your project are written assuming that the original methods and 7 | properties in Common classes are still present and that the names of the types have not changed. -------------------------------------------------------------------------------- /EBookClient/Common/RelayCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Input; 7 | 8 | namespace EBookClient.Common 9 | { 10 | /// 11 | /// A command whose sole purpose is to relay its functionality 12 | /// to other objects by invoking delegates. 13 | /// The default return value for the CanExecute method is 'true'. 14 | /// needs to be called whenever 15 | /// is expected to return a different value. 16 | /// 17 | public class RelayCommand : ICommand 18 | { 19 | private readonly Action _execute; 20 | private readonly Func _canExecute; 21 | 22 | /// 23 | /// Raised when RaiseCanExecuteChanged is called. 24 | /// 25 | public event EventHandler CanExecuteChanged; 26 | 27 | /// 28 | /// Creates a new command that can always execute. 29 | /// 30 | /// The execution logic. 31 | public RelayCommand(Action execute) 32 | : this(execute, null) 33 | { 34 | } 35 | 36 | /// 37 | /// Creates a new command. 38 | /// 39 | /// The execution logic. 40 | /// The execution status logic. 41 | public RelayCommand(Action execute, Func canExecute) 42 | { 43 | if (execute == null) 44 | throw new ArgumentNullException("execute"); 45 | _execute = execute; 46 | _canExecute = canExecute; 47 | } 48 | 49 | /// 50 | /// Determines whether this can execute in its current state. 51 | /// 52 | /// 53 | /// Data used by the command. If the command does not require data to be passed, this object can be set to null. 54 | /// 55 | /// true if this command can be executed; otherwise, false. 56 | public bool CanExecute(object parameter) 57 | { 58 | return _canExecute == null ? true : _canExecute(); 59 | } 60 | 61 | /// 62 | /// Executes the on the current command target. 63 | /// 64 | /// 65 | /// Data used by the command. If the command does not require data to be passed, this object can be set to null. 66 | /// 67 | public void Execute(object parameter) 68 | { 69 | _execute(); 70 | } 71 | 72 | /// 73 | /// Method used to raise the event 74 | /// to indicate that the return value of the 75 | /// method has changed. 76 | /// 77 | public void RaiseCanExecuteChanged() 78 | { 79 | var handler = CanExecuteChanged; 80 | if (handler != null) 81 | { 82 | handler(this, EventArgs.Empty); 83 | } 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /EBookClient/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | 29 |

50 |
51 | 52 |
53 | 54 | 55 |
56 | 57 |
58 | } 59 | 60 | @section scripts{ 61 | 69 | } 70 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/AdminCatalog/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.BackOffice.Models.AdminCatalogModel 2 | 3 | @{ 4 | ViewBag.Title = "Administration du catalogue"; 5 | } 6 | 7 |

@ViewBag.Title

8 |
9 | 10 |
11 | @Html.ActionLink("Ajouter un Ebook", "Add", "AdminCatalog", null, new { @class = "btn btn-primary" }) 12 |
13 | 14 |
15 | @if (!Model.Ebooks.Any()) 16 | { 17 |
18 | Ooops 19 |

Le catalogue est vide

20 |
21 | } 22 | else 23 | { 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | @foreach (var book in Model.Ebooks) 34 | { 35 | 36 | 37 | 38 | 39 | 40 | } 41 | 42 |
TitreRésumé
@book.Title@book.Summary@Html.RouteLink("Editer", "editEbook", new { ebookId = book.Id }) - @Html.RouteLink("Supprimer", "deleteEbook", new { ebookId = book.Id })
43 | } 44 |
45 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Accueil"; 3 | } 4 | 5 |
6 |

Ebook Manager Back Office

7 |

Application de démonstration utilisée pour la session "ASP.NET MVC 5 et Web API 2, Techdays 2014".

8 |
9 | 10 |
11 |
12 |

Gestion du catalogue

13 |

14 | Administration du catalogue d'ebooks 15 |

16 |

@Html.ActionLink("Accéder", "Index", "AdminCatalog", null, new { @class = "btn btn-primary" })

17 |
18 |
-------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | 3 | @{ 4 | ViewBag.Title = "Error"; 5 | } 6 | 7 |

Error.

8 |

An error occurred while processing your request.

9 | 10 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title - Ebook Manager Back Office 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 12 | 30 |
31 | 32 | @if (TempData.ContainsKey("Success")) 33 | { 34 |
35 |

@TempData["Success"]

36 |
37 | } 38 | 39 | @RenderBody() 40 |
41 |
42 |

© @DateTime.Now.Year - Techdays 2014 Ebook Manager - Julien Corioland / Simon Ferquel

43 |
44 |
45 | 46 | @Scripts.Render("~/bundles/jquery") 47 | @Scripts.Render("~/bundles/bootstrap") 48 | @RenderSection("scripts", required: false) 49 | 50 | 51 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @if (Request.IsAuthenticated) 2 | { 3 | 4 | 12 | 13 | } -------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /EbookManager.BackOffice/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.BackOffice/favicon.ico -------------------------------------------------------------------------------- /EbookManager.BackOffice/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.BackOffice/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /EbookManager.BackOffice/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.BackOffice/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /EbookManager.BackOffice/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.BackOffice/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /EbookManager.BackOffice/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /EbookManager.CommonViewModels/EbookManager.CommonViewModels.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5CDD3448-1AD3-4C86-97B9-6C713B1251D3} 8 | Library 9 | Properties 10 | EbookManager.CommonViewModels 11 | EbookManager.CommonViewModels 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | bin\Release\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | {ff4d5ad0-5608-406a-9c71-cafbd9b7aa31} 50 | EbookManager.Domain 51 | 52 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /EbookManager.CommonViewModels/EbookPartViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using EbookManager.Domain.Catalog; 7 | 8 | namespace EbookManager.CommonViewModels 9 | { 10 | public class EbookPartViewModel 11 | { 12 | public Guid EbookId { get; set; } 13 | public int Position { get; set; } 14 | public string ContentType { get; set; } 15 | public string FileName { get; set; } 16 | 17 | public static EbookPartViewModel FromEbookPart(EbookPart part) 18 | { 19 | return new EbookPartViewModel() 20 | { 21 | ContentType = part.ContentType, 22 | EbookId = part.EbookId, 23 | Position = part.Position, 24 | FileName = part.FileName 25 | }; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /EbookManager.CommonViewModels/EbookViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using EbookManager.Domain.Catalog; 8 | 9 | namespace EbookManager.CommonViewModels 10 | { 11 | public class EbookViewModel 12 | { 13 | public Guid Id { get; set; } 14 | 15 | [Display(Name = "Titre")] 16 | public string Title { get; set; } 17 | 18 | [Display(Name = "Résumé")] 19 | public string Summary { get; set; } 20 | 21 | public string Thumbnail { get; set; } 22 | 23 | [Display(Name = "Nombre de partie(s)")] 24 | public int PartsCount { get; set; } 25 | 26 | public static EbookViewModel FromEbook(Ebook ebook) 27 | { 28 | return new EbookViewModel() 29 | { 30 | Id = ebook.Id, 31 | PartsCount = ebook.Parts != null ? ebook.Parts.Count : 0, 32 | Summary = ebook.Summary, 33 | Thumbnail = Convert.ToBase64String(ebook.Thumbnail), 34 | Title = ebook.Title 35 | }; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /EbookManager.CommonViewModels/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EbookManager.CommonViewModels")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EbookManager.CommonViewModels")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b4d6c33a-121e-4830-8949-c46c28805f72")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EbookManager.Database/EbookManager.Database.sqlproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | EbookManager.Database 8 | 2.0 9 | 4.1 10 | {3356fe8e-cafb-4f91-9886-74b8cb2b08a2} 11 | Microsoft.Data.Tools.Schema.Sql.Sql110DatabaseSchemaProvider 12 | Database 13 | 14 | 15 | EbookManager.Database 16 | EbookManager.Database 17 | 1033, CI 18 | BySchemaAndSchemaType 19 | True 20 | v4.5 21 | CS 22 | Properties 23 | False 24 | True 25 | True 26 | 27 | 28 | 29 | bin\Release\ 30 | $(MSBuildProjectName).sql 31 | False 32 | pdbonly 33 | true 34 | false 35 | true 36 | prompt 37 | 4 38 | 39 | 40 | bin\Debug\ 41 | $(MSBuildProjectName).sql 42 | false 43 | true 44 | full 45 | false 46 | true 47 | true 48 | prompt 49 | 4 50 | 51 | 52 | 53 | 10.0 54 | 55 | True 56 | 10.0 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_EbookParts.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_EbookParts] ( 2 | [EbookId] UNIQUEIDENTIFIER NOT NULL, 3 | [Position] INT NOT NULL, 4 | [PartContent] VARBINARY (MAX) NOT NULL, 5 | [ContentType] NVARCHAR (50) NOT NULL, 6 | [FileName] NVARCHAR (120) NOT NULL, 7 | CONSTRAINT [PK_T_EbookParts] PRIMARY KEY CLUSTERED ([EbookId] ASC, [Position] ASC), 8 | CONSTRAINT [FK_T_EbookParts_T_Ebooks] FOREIGN KEY ([EbookId]) REFERENCES [dbo].[T_Ebooks] ([Id]) 9 | ); 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_Ebooks.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_Ebooks] ( 2 | [Id] UNIQUEIDENTIFIER NOT NULL, 3 | [Title] NVARCHAR (120) NOT NULL, 4 | [Summary] NVARCHAR (500) NOT NULL, 5 | [Thumbnail] VARBINARY (MAX) NOT NULL, 6 | CONSTRAINT [PK_T_Ebooks] PRIMARY KEY CLUSTERED ([Id] ASC) 7 | ); 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_Roles.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_Roles] ( 2 | [Id] NVARCHAR (80) NOT NULL, 3 | [Name] NVARCHAR (120) NOT NULL, 4 | CONSTRAINT [PK_T_Roles] PRIMARY KEY CLUSTERED ([Id] ASC) 5 | ); 6 | 7 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_UserClaims.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_UserClaims] ( 2 | [Id] INT IDENTITY (1, 1) NOT NULL, 3 | [ClaimType] NVARCHAR (120) NOT NULL, 4 | [ClaimValue] NVARCHAR (120) NOT NULL, 5 | [UserId] NVARCHAR (120) NOT NULL, 6 | CONSTRAINT [PK_T_UserClaims] PRIMARY KEY CLUSTERED ([Id] ASC), 7 | CONSTRAINT [FK_T_UserClaims_T_Users] FOREIGN KEY ([UserId]) REFERENCES [dbo].[T_Users] ([Id]) 8 | ); 9 | 10 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_UserEbooks.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_UserEbooks] ( 2 | [UserId] NVARCHAR (120) NOT NULL, 3 | [EbookId] UNIQUEIDENTIFIER NOT NULL, 4 | CONSTRAINT [PK_T_UserEbooks] PRIMARY KEY CLUSTERED ([UserId] ASC, [EbookId] ASC), 5 | CONSTRAINT [FK_T_UserEbooks_T_Ebooks] FOREIGN KEY ([EbookId]) REFERENCES [dbo].[T_Ebooks] ([Id]), 6 | CONSTRAINT [FK_T_UserEbooks_T_Users] FOREIGN KEY ([UserId]) REFERENCES [dbo].[T_Users] ([Id]) 7 | ); 8 | 9 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_UserLogins.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_UserLogins] ( 2 | [LoginProvider] NVARCHAR (80) NOT NULL, 3 | [ProviderKey] NVARCHAR (120) NOT NULL, 4 | [UserId] NVARCHAR (120) NOT NULL, 5 | CONSTRAINT [PK_T_UserLogins] PRIMARY KEY CLUSTERED ([LoginProvider] ASC, [ProviderKey] ASC, [UserId] ASC), 6 | CONSTRAINT [FK_T_UserLogins_T_Users] FOREIGN KEY ([UserId]) REFERENCES [dbo].[T_Users] ([Id]) 7 | ); 8 | 9 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_UserRoles.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_UserRoles] ( 2 | [UserId] NVARCHAR (120) NOT NULL, 3 | [RoleId] NVARCHAR (80) NOT NULL, 4 | CONSTRAINT [PK_T_UserRoles] PRIMARY KEY CLUSTERED ([UserId] ASC, [RoleId] ASC), 5 | CONSTRAINT [FK_T_UserRoles_T_Roles] FOREIGN KEY ([RoleId]) REFERENCES [dbo].[T_Roles] ([Id]), 6 | CONSTRAINT [FK_T_UserRoles_T_Users] FOREIGN KEY ([UserId]) REFERENCES [dbo].[T_Users] ([Id]) 7 | ); 8 | 9 | -------------------------------------------------------------------------------- /EbookManager.Database/dbo/Tables/T_Users.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[T_Users] ( 2 | [Id] NVARCHAR (120) NOT NULL, 3 | [UserName] NVARCHAR (120) NOT NULL, 4 | [PasswordHash] NVARCHAR (255) NULL, 5 | [SecurityStamp] NVARCHAR (255) NULL, 6 | CONSTRAINT [PK_T_Users] PRIMARY KEY CLUSTERED ([Id] ASC) 7 | ); 8 | 9 | 10 | GO 11 | CREATE UNIQUE NONCLUSTERED INDEX [IX_T_Users] 12 | ON [dbo].[T_Users]([UserName] ASC); 13 | 14 | -------------------------------------------------------------------------------- /EbookManager.Domain/Catalog/Ebook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace EbookManager.Domain.Catalog 10 | { 11 | [Table("T_Ebooks")] 12 | public class Ebook 13 | { 14 | [Key] 15 | public Guid Id { get; set; } 16 | 17 | [Required] 18 | [MaxLength(120)] 19 | public string Title { get; set; } 20 | 21 | [Required] 22 | [MaxLength(500)] 23 | public string Summary { get; set; } 24 | 25 | public byte[] Thumbnail { get; set; } 26 | 27 | public ICollection Parts { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EbookManager.Domain/Catalog/EbookPart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using EbookManager.Domain.Users; 9 | 10 | namespace EbookManager.Domain.Catalog 11 | { 12 | [Table("T_EbookParts")] 13 | public class EbookPart 14 | { 15 | [Key] 16 | [Column(Order = 0)] 17 | public Guid EbookId { get; set; } 18 | 19 | [ForeignKey("EbookId")] 20 | public Ebook Ebook { get; set; } 21 | 22 | [Key] 23 | [Column(Order = 1)] 24 | public int Position { get; set; } 25 | 26 | public string ContentType { get; set; } 27 | 28 | public string FileName { get; set; } 29 | 30 | public byte[] PartContent { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EbookManager.Domain/Catalog/UserEbook.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using EbookManager.Domain.Users; 9 | 10 | namespace EbookManager.Domain.Catalog 11 | { 12 | [Table("T_UserEbooks")] 13 | public class UserEbook 14 | { 15 | [ForeignKey("UserId")] 16 | public User User { get; set; } 17 | 18 | [Key] 19 | [Column(Order = 0)] 20 | public string UserId { get; set; } 21 | 22 | [ForeignKey("EbookId")] 23 | public Ebook Ebook { get; set; } 24 | 25 | [Key] 26 | [Column(Order = 1)] 27 | public Guid EbookId { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EbookManager.Domain/EbookManager.Domain.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FF4D5AD0-5608-406A-9C71-CAFBD9B7AA31} 8 | Library 9 | Properties 10 | EbookManager.Domain 11 | EbookManager.Domain 12 | v4.5 13 | 512 14 | 15 | ..\ 16 | true 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\Microsoft.AspNet.Identity.Core.1.0.0\lib\net45\Microsoft.AspNet.Identity.Core.dll 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 71 | -------------------------------------------------------------------------------- /EbookManager.Domain/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EbookManager.Domain")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EbookManager.Domain")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("40301bbf-2356-4b03-9206-9a991a7936fd")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EbookManager.Domain/Users/Role.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace EbookManager.Domain.Users 5 | { 6 | [Table("T_Roles")] 7 | public class Role 8 | { 9 | [Key] 10 | [MaxLength(80)] 11 | public string Id { get; set; } 12 | 13 | [Required] 14 | [MaxLength(120)] 15 | public string Name { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /EbookManager.Domain/Users/User.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.ComponentModel.DataAnnotations; 3 | using System.ComponentModel.DataAnnotations.Schema; 4 | using EbookManager.Domain.Catalog; 5 | using Microsoft.AspNet.Identity; 6 | 7 | namespace EbookManager.Domain.Users 8 | { 9 | [Table("T_Users")] 10 | public class User : IUser 11 | { 12 | public User() { } 13 | 14 | public User(string userName) 15 | { 16 | this.UserName = userName; 17 | } 18 | 19 | [Key] 20 | [MaxLength(120)] 21 | public string Id { get; set; } 22 | 23 | [Required] 24 | [MaxLength(120)] 25 | public string UserName { get; set; } 26 | 27 | [MaxLength(255)] 28 | public string PasswordHash { get; set; } 29 | 30 | [MaxLength(255)] 31 | public string SecurityStamp { get; set; } 32 | 33 | public ICollection Claims { get; set; } 34 | public ICollection Logins { get; set; } 35 | public ICollection Roles { get; set; } 36 | public ICollection Ebooks { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /EbookManager.Domain/Users/UserClaim.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace EbookManager.Domain.Users 5 | { 6 | [Table("T_UserClaims")] 7 | public class UserClaim 8 | { 9 | [Key] 10 | public int Id { get; set; } 11 | 12 | [Required] 13 | [MaxLength(120)] 14 | public string ClaimType { get; set; } 15 | 16 | 17 | [Required] 18 | [MaxLength(120)] 19 | public string ClaimValue { get; set; } 20 | 21 | 22 | [Required] 23 | [MaxLength(120)] 24 | public string UserId { get; set; } 25 | 26 | [ForeignKey("UserId")] 27 | public User User { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /EbookManager.Domain/Users/UserLogin.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace EbookManager.Domain.Users 5 | { 6 | [Table("T_UserLogins")] 7 | public class UserLogin 8 | { 9 | [Key] 10 | [Column(Order = 0)] 11 | [MaxLength(80)] 12 | public string LoginProvider { get; set; } 13 | 14 | [Key] 15 | [Column(Order = 1)] 16 | [MaxLength(120)] 17 | public string ProviderKey { get; set; } 18 | 19 | [Key] 20 | [Column(Order = 2)] 21 | [MaxLength(120)] 22 | public string UserId { get; set; } 23 | 24 | [ForeignKey("UserId")] 25 | public User User { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /EbookManager.Domain/Users/UserRole.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | using System.ComponentModel.DataAnnotations.Schema; 3 | 4 | namespace EbookManager.Domain.Users 5 | { 6 | [Table("T_UserRoles")] 7 | public class UserRole 8 | { 9 | [Key] 10 | [Column(Order = 0)] 11 | [MaxLength(120)] 12 | public string UserId { get; set; } 13 | 14 | [ForeignKey("UserId")] 15 | public User User { get; set; } 16 | 17 | [Key] 18 | [Column(Order = 1)] 19 | [MaxLength(80)] 20 | public string RoleId { get; set; } 21 | 22 | [ForeignKey("RoleId")] 23 | public Role Role { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /EbookManager.Domain/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /EbookManager.Repositories/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /EbookManager.Repositories/CatalogRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using EbookManager.Domain.Catalog; 9 | using EbookManager.Domain.Users; 10 | 11 | namespace EbookManager.Repositories 12 | { 13 | public class CatalogRepository 14 | { 15 | private readonly EbookManagerDbContext db; 16 | public CatalogRepository(EbookManagerDbContext db) 17 | { 18 | this.db = db; 19 | } 20 | 21 | public async Task> LoadCatalogAsync() 22 | { 23 | return await this.db.Ebooks.Include("Parts").ToListAsync(); 24 | } 25 | 26 | public async Task> LoadCatalogWithoutPartsAsync() 27 | { 28 | return await this.db.Ebooks.ToListAsync(); 29 | } 30 | 31 | public async Task> LoadUserCatalog(string userName) 32 | { 33 | var user = await this.db.Users.Include("Ebooks").FirstOrDefaultAsync(u => u.UserName == userName); 34 | if(user == null) 35 | throw new InvalidOperationException("The user does not exists"); 36 | 37 | var ebooksIds = user.Ebooks.Select(e => e.EbookId).ToArray(); 38 | var ebooks = await this.db.Ebooks.Where(e => ebooksIds.Contains(e.Id)).ToListAsync(); 39 | 40 | return ebooks; 41 | } 42 | 43 | public async Task>> LoadUserCatalogWithPartCount(string userName) 44 | { 45 | var user = await this.db.Users.Include("Ebooks").FirstOrDefaultAsync(u => u.UserName == userName); 46 | if (user == null) 47 | throw new InvalidOperationException("The user does not exists"); 48 | 49 | var ebooksIds = user.Ebooks.Select(e => e.EbookId).ToArray(); 50 | var ebooks = await this.db.Ebooks.Where(e => ebooksIds.Contains(e.Id)) 51 | .Select(b=>new {Ebook = b, PartCount = b.Parts.Count()}).ToListAsync(); 52 | 53 | return ebooks.Select(b=>Tuple.Create(b.Ebook, b.PartCount)).ToList(); 54 | } 55 | 56 | public async Task BuyEbookAsync(string userId, Guid ebookId) 57 | { 58 | var user = await this.db.Users.Include("Ebooks").FirstOrDefaultAsync(u => u.Id == userId); 59 | if (user != null) 60 | { 61 | user.Ebooks.Add(new UserEbook(){ EbookId = ebookId, UserId = user.Id }); 62 | await this.db.SaveChangesAsync(); 63 | } 64 | } 65 | 66 | public async Task AddEbookAsync(Ebook ebook) 67 | { 68 | this.db.Ebooks.Add(ebook); 69 | await this.db.SaveChangesAsync(); 70 | } 71 | 72 | public async Task GetEbookAsync(Guid ebookId) 73 | { 74 | return await this.db.Ebooks.Include("Parts").FirstOrDefaultAsync(e => e.Id == ebookId); 75 | } 76 | 77 | public async Task UpdateBookAsync(Ebook ebook) 78 | { 79 | this.db.Entry(ebook).State = EntityState.Modified; 80 | await this.db.SaveChangesAsync(); 81 | } 82 | 83 | public EbookPart GetEbookPart(Guid ebookId, int position) 84 | { 85 | return this.db.EbookParts.FirstOrDefault(e => e.EbookId == ebookId && e.Position == position); 86 | } 87 | 88 | public async Task DeleteEbookAsync(Guid ebookId) 89 | { 90 | var ebook = await GetEbookAsync(ebookId); 91 | if (ebook != null) 92 | { 93 | foreach (var part in ebook.Parts.ToList()) 94 | { 95 | this.db.EbookParts.Remove(part); 96 | } 97 | 98 | this.db.Ebooks.Remove(ebook); 99 | 100 | await this.db.SaveChangesAsync(); 101 | } 102 | } 103 | 104 | public async Task UserOwnsBookAsync(string userId, Guid ebookId) 105 | { 106 | var user = await this.db.Users.Include("Ebooks").FirstOrDefaultAsync(u => u.Id == userId); 107 | if (user == null) 108 | return false; 109 | 110 | return user.Ebooks.Any(e => e.EbookId == ebookId); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /EbookManager.Repositories/EbookManager.Repositories.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2305FB3A-503F-466E-8A67-726D47E49A61} 8 | Library 9 | Properties 10 | EbookManager.Repositories 11 | EbookManager.Repositories 12 | v4.5 13 | 512 14 | 15 | ..\ 16 | true 17 | 18 | 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | ..\packages\EntityFramework.6.0.2\lib\net45\EntityFramework.dll 38 | 39 | 40 | ..\packages\EntityFramework.6.0.2\lib\net45\EntityFramework.SqlServer.dll 41 | 42 | 43 | ..\packages\Microsoft.AspNet.Identity.Core.1.0.0\lib\net45\Microsoft.AspNet.Identity.Core.dll 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {ff4d5ad0-5608-406a-9c71-cafbd9b7aa31} 63 | EbookManager.Domain 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 79 | -------------------------------------------------------------------------------- /EbookManager.Repositories/EbookManagerDbContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Entity; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using EbookManager.Domain.Catalog; 8 | using EbookManager.Domain.Users; 9 | 10 | namespace EbookManager.Repositories 11 | { 12 | public class EbookManagerDbContext : DbContext 13 | { 14 | static EbookManagerDbContext() 15 | { 16 | Database.SetInitializer(new NullDatabaseInitializer()); 17 | } 18 | 19 | public EbookManagerDbContext() 20 | : base("EbookManagerDatabase") 21 | { 22 | 23 | } 24 | 25 | public IDbSet Users { get; set; } 26 | public IDbSet UserLogins { get; set; } 27 | public IDbSet UserClaims { get; set; } 28 | public IDbSet UserRoles { get; set; } 29 | public IDbSet Ebooks { get; set; } 30 | public IDbSet EbookParts { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /EbookManager.Repositories/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EbookManager.Repositories")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EbookManager.Repositories")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7fcfbc98-bc2d-4eee-9cc4-3b4c9fcd324d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /EbookManager.Repositories/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /EbookManager.Web/Api/EbooksController.cs: -------------------------------------------------------------------------------- 1 | using EbookManager.Repositories; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | using System.Web.Http; 9 | 10 | namespace EbookManager.Web.Api 11 | { 12 | [Authorize] 13 | [RoutePrefix("api/ebooks")] 14 | public class EbooksController : ApiController 15 | { 16 | [HttpGet] 17 | [Route("my")] 18 | public async Task GetMyEbooks() 19 | { 20 | using (var db = new EbookManagerDbContext()) 21 | { 22 | var userName = User.Identity.Name; 23 | 24 | var catalogRepository = new CatalogRepository(db); 25 | var ebooks = await catalogRepository.LoadUserCatalogWithPartCount(userName); 26 | return Request.CreateResponse(ebooks.Select(e => new { Id = e.Item1.Id, Summary = e.Item1.Summary, Title = e.Item1.Title, Thumbnail = e.Item1.Thumbnail, PartCount = e.Item2 })); 27 | } 28 | } 29 | 30 | [HttpGet] 31 | [Route("ebook/{ebookId}/part/{index}")] 32 | public async Task GetEbookPart(Guid ebookId, int index) 33 | { 34 | using (var db = new EbookManagerDbContext()) 35 | { 36 | var catalogRepository = new CatalogRepository(db); 37 | var part = catalogRepository.GetEbookPart(ebookId, index); 38 | var response = new HttpResponseMessage(HttpStatusCode.OK); 39 | response.Content = new ByteArrayContent(part.PartContent); 40 | response.Content.Headers.ContentLength = part.PartContent.Length; 41 | response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(part.ContentType); 42 | return response; 43 | } 44 | } 45 | 46 | [HttpGet] 47 | [Route("")] 48 | [Queryable] 49 | [AllowAnonymous] 50 | public async Task GetEbooks() 51 | { 52 | using (var db = new EbookManagerDbContext()) 53 | { 54 | var userName = "julien"; 55 | var catalogRepository = new CatalogRepository(db); 56 | var userEbooks = await catalogRepository.LoadUserCatalog(userName); 57 | 58 | return Request.CreateResponse(HttpStatusCode.OK, userEbooks); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /EbookManager.Web/Api/UserController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Security.Claims; 7 | using System.Web.Http; 8 | using Owin; 9 | 10 | namespace EbookManager.Web.Api 11 | { 12 | [Authorize] 13 | [RoutePrefix("api/user")] 14 | public class UserController : ApiController 15 | { 16 | [HttpGet] 17 | [Route("whoami")] 18 | public HttpResponseMessage WhoAmi() 19 | { 20 | return Request.CreateResponse(new { UserName = User.Identity.Name, NameIdentifier = ((ClaimsIdentity)User.Identity).FindFirst(ClaimTypes.NameIdentifier).Value }); 21 | } 22 | 23 | 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/BundleConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Optimization; 3 | 4 | namespace EbookManager.Web 5 | { 6 | public class BundleConfig 7 | { 8 | // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 9 | public static void RegisterBundles(BundleCollection bundles) 10 | { 11 | bundles.Add(new ScriptBundle("~/bundles/jquery").Include( 12 | "~/Scripts/jquery-{version}.js")); 13 | 14 | bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( 15 | "~/Scripts/jquery.validate*")); 16 | 17 | // Use the development version of Modernizr to develop with and learn from. Then, when you're 18 | // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. 19 | bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( 20 | "~/Scripts/modernizr-*")); 21 | 22 | bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( 23 | "~/Scripts/bootstrap.js", 24 | "~/Scripts/respond.js")); 25 | 26 | bundles.Add(new StyleBundle("~/Content/css").Include( 27 | "~/Content/bootstrap.css", 28 | "~/Content/site.css")); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/FilterConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web; 2 | using System.Web.Mvc; 3 | 4 | namespace EbookManager.Web 5 | { 6 | public class FilterConfig 7 | { 8 | public static void RegisterGlobalFilters(GlobalFilterCollection filters) 9 | { 10 | filters.Add(new HandleErrorAttribute()); 11 | filters.Add(new AuthorizeAttribute()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | using System.Web.Routing; 7 | 8 | namespace EbookManager.Web 9 | { 10 | public class RouteConfig 11 | { 12 | public static void RegisterRoutes(RouteCollection routes) 13 | { 14 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 15 | routes.MapMvcAttributeRoutes(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/Startup.Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using EbookManager.Web.Owin; 6 | using Owin; 7 | 8 | namespace EbookManager.Web 9 | { 10 | public partial class Startup 11 | { 12 | public void ConfigureLogMiddleware(IAppBuilder app) 13 | { 14 | app.Use(typeof (LogMiddleware)); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/Startup.OAuth2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using Microsoft.Owin; 6 | using Microsoft.Owin.Security.OAuth; 7 | using Owin; 8 | 9 | namespace EbookManager.Web 10 | { 11 | public partial class Startup 12 | { 13 | public static OAuthAuthorizationServerOptions OAuthServerOptions { get; set; } 14 | 15 | static Startup() 16 | { 17 | OAuthServerOptions = new OAuthAuthorizationServerOptions 18 | { 19 | AllowInsecureHttp = true, 20 | Provider = new OAuthServerProvider(), 21 | AuthorizeEndpointPath = new PathString("/oauth/authorize"), 22 | TokenEndpointPath = new PathString("/oauth/token"), 23 | AuthorizationCodeProvider = new AuthorizationCodeProvider(), 24 | AccessTokenProvider = new AccessTokenProvider(), 25 | RefreshTokenProvider = new RefreshTokenProvider() 26 | }; 27 | } 28 | 29 | public void ConfigureOAuth2(IAppBuilder app) 30 | { 31 | // Activation de l'authentification via OAuth2 32 | app.UseOAuthAuthorizationServer(OAuthServerOptions); 33 | app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions 34 | { 35 | AccessTokenFormat = OAuthServerOptions.AccessTokenFormat, 36 | AccessTokenProvider = OAuthServerOptions.AccessTokenProvider, 37 | }); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/Startup.WebAuth.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNet.Identity; 2 | using Microsoft.Owin; 3 | using Microsoft.Owin.Security.ActiveDirectory; 4 | using Microsoft.Owin.Security.Cookies; 5 | using Microsoft.Owin.Security.OAuth; 6 | using Owin; 7 | 8 | namespace EbookManager.Web 9 | { 10 | public partial class Startup 11 | { 12 | public void ConfigureWebAuth(IAppBuilder app) 13 | { 14 | //Utilisation de cookie pour stocker les informations sur les utilisateurs authentifiés 15 | app.UseCookieAuthentication(new CookieAuthenticationOptions 16 | { 17 | LoginPath = new PathString("/Account/Login"), 18 | AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie 19 | }); 20 | 21 | // Utilisation d'un cookie pour le stockage des info temporaires des utilisateurs authentifiés 22 | // via un fournisseur externe 23 | app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); 24 | 25 | // Activation du fournisseur d'authentification Microsoft 26 | app.UseMicrosoftAccountAuthentication( 27 | clientId: "00000000481106FF", 28 | clientSecret: "MqwDmwpeGTezV6o4sJdgVKIfZm76TZt6"); 29 | 30 | // Activation du fournisseur d'authentification Facebook 31 | app.UseFacebookAuthentication( 32 | appId: "636293909765104", 33 | appSecret: "807a0a17eeaa20dabc1bd72f1be33775"); 34 | 35 | // Activation du fournisseur d'authentification Google 36 | app.UseGoogleAuthentication(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /EbookManager.Web/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin.Security.OAuth; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Web.Http; 6 | using System.Web.Http.Batch; 7 | 8 | namespace EbookManager.Web 9 | { 10 | public static class WebApiConfig 11 | { 12 | public static void Register(HttpConfiguration config) 13 | { 14 | #region Web API Batching 15 | // Web API configuration and services 16 | var batchHandler = new DefaultHttpBatchHandler(GlobalConfiguration.DefaultServer) 17 | { 18 | ExecutionOrder = BatchExecutionOrder.NonSequential 19 | }; 20 | config.Routes.MapHttpBatchRoute( 21 | routeName: "WebApiBatch", 22 | routeTemplate: "api/batch", 23 | batchHandler: batchHandler); 24 | #endregion 25 | 26 | #region Authentification 27 | 28 | config.SuppressDefaultHostAuthentication(); 29 | config.Filters.Add(new HostAuthenticationFilter(Startup.OAuthServerOptions.AuthenticationType)); 30 | 31 | #endregion 32 | 33 | #region Attribute Routing 34 | // Web API routes 35 | config.MapHttpAttributeRoutes(); 36 | #endregion 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /EbookManager.Web/AuthenticationTokens.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin.Security.Infrastructure; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Web; 6 | 7 | namespace EbookManager.Web 8 | { 9 | public class AuthorizationCodeProvider : AuthenticationTokenProvider 10 | { 11 | public override async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context) 12 | { 13 | context.SetToken(context.SerializeTicket()); 14 | } 15 | 16 | public override async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context) 17 | { 18 | context.DeserializeTicket(context.Token); 19 | } 20 | } 21 | 22 | public class AccessTokenProvider : AuthenticationTokenProvider 23 | { 24 | public override async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context) 25 | { 26 | context.SetToken(context.SerializeTicket()); 27 | } 28 | 29 | public override async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context) 30 | { 31 | context.DeserializeTicket(context.Token); 32 | } 33 | } 34 | public class RefreshTokenProvider : AuthenticationTokenProvider 35 | { 36 | public override async System.Threading.Tasks.Task CreateAsync(AuthenticationTokenCreateContext context) 37 | { 38 | // refresh tokens don't expire 39 | context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddYears(100); 40 | context.SetToken(context.SerializeTicket()); 41 | } 42 | 43 | public override async System.Threading.Tasks.Task ReceiveAsync(AuthenticationTokenReceiveContext context) 44 | { 45 | context.DeserializeTicket(context.Token); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /EbookManager.Web/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 50px; 3 | padding-bottom: 20px; 4 | } 5 | 6 | /* Set padding to keep content from hitting the edges */ 7 | .body-content { 8 | padding-left: 15px; 9 | padding-right: 15px; 10 | } 11 | 12 | /* Set width on the form input elements since they're 100% wide by default */ 13 | input, 14 | select, 15 | textarea { 16 | max-width: 280px; 17 | } 18 | 19 | /* styles for validation helpers */ 20 | .field-validation-error { 21 | color: #b94a48; 22 | } 23 | 24 | .field-validation-valid { 25 | display: none; 26 | } 27 | 28 | input.input-validation-error { 29 | border: 1px solid #b94a48; 30 | } 31 | 32 | input[type="checkbox"].input-validation-error { 33 | border: 0 none; 34 | } 35 | 36 | .validation-summary-errors { 37 | color: #b94a48; 38 | } 39 | 40 | .validation-summary-valid { 41 | display: none; 42 | } 43 | 44 | .ebook-thumbnail { 45 | max-width: 250px; 46 | } 47 | 48 | .ebook-details { 49 | margin-bottom: 15px; 50 | } 51 | 52 | .ebook-details .img-thumbnail { 53 | max-width: 150px; 54 | } -------------------------------------------------------------------------------- /EbookManager.Web/Controllers/CatalogController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Claims; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using System.Web.Mvc; 8 | using EbookManager.CommonViewModels; 9 | using EbookManager.Repositories; 10 | using EbookManager.Web.Models; 11 | 12 | namespace EbookManager.Web.Controllers 13 | { 14 | [RoutePrefix("catalog")] 15 | public class CatalogController : Controller 16 | { 17 | [Route("")] 18 | public async Task Index() 19 | { 20 | using (var db = new EbookManagerDbContext()) 21 | { 22 | var catalogRepository = new CatalogRepository(db); 23 | var ebooks = await catalogRepository.LoadCatalogWithoutPartsAsync(); 24 | 25 | var model = new CatalogViewModel(); 26 | model.Ebooks.AddRange(ebooks.Select(e => EbookViewModel.FromEbook(e))); 27 | 28 | return View(model); 29 | } 30 | } 31 | 32 | [Route("ebook/{ebookId}", Name = "ebookDetails")] 33 | public async Task Details(Guid ebookId) 34 | { 35 | using (var db = new EbookManagerDbContext()) 36 | { 37 | var catalogRepository = new CatalogRepository(db); 38 | var ebook = await catalogRepository.GetEbookAsync(ebookId); 39 | 40 | var model = EbookViewModel.FromEbook(ebook); 41 | 42 | var userId = ClaimsPrincipal.Current.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value; 43 | 44 | ViewBag.UserOwnsBook = await catalogRepository.UserOwnsBookAsync(userId, ebookId); 45 | 46 | return View(model); 47 | } 48 | } 49 | 50 | [HttpPost] 51 | [Route("buy/{ebookId}")] 52 | public async Task Buy(Guid ebookId) 53 | { 54 | using (var db = new EbookManagerDbContext()) 55 | { 56 | var catalogRepository = new CatalogRepository(db); 57 | 58 | var userId = ClaimsPrincipal.Current.Claims.First(c => c.Type == ClaimTypes.NameIdentifier).Value; 59 | await catalogRepository.BuyEbookAsync(userId, ebookId); 60 | 61 | TempData["Success"] = "Votre achat a été pris en compte !"; 62 | 63 | return RedirectToAction("Index", "MySpace"); 64 | } 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /EbookManager.Web/Controllers/DemoAuthenticatedController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace EbookManager.Web.Controllers 8 | { 9 | [Authorize] 10 | [RoutePrefix("demowebviewauth")] 11 | public class DemoAuthenticatedController : Controller 12 | { 13 | [Route("")] 14 | public ActionResult Index() 15 | { 16 | return View(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /EbookManager.Web/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Mvc; 6 | 7 | namespace EbookManager.Web.Controllers 8 | { 9 | [RoutePrefix("")] 10 | public class HomeController : Controller 11 | { 12 | [Route("")] 13 | public ActionResult Index() 14 | { 15 | return View(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /EbookManager.Web/Controllers/MySpaceController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Web; 6 | using System.Web.Mvc; 7 | using EbookManager.CommonViewModels; 8 | using EbookManager.Repositories; 9 | using EbookManager.Web.Models; 10 | 11 | namespace EbookManager.Web.Controllers 12 | { 13 | [RoutePrefix("my")] 14 | public class MySpaceController : Controller 15 | { 16 | [Route("")] 17 | public async Task Index() 18 | { 19 | using (var db = new EbookManagerDbContext()) 20 | { 21 | var userName = User.Identity.Name; 22 | 23 | var catalogRepository = new CatalogRepository(db); 24 | var ebooks = await catalogRepository.LoadUserCatalog(userName); 25 | 26 | var model = new CatalogViewModel(); 27 | model.Ebooks.AddRange(ebooks.Select(e => EbookViewModel.FromEbook(e))); 28 | 29 | return View(model); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /EbookManager.Web/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="EbookManager.Web.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /EbookManager.Web/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Http; 6 | using System.Web.Mvc; 7 | using System.Web.Optimization; 8 | using System.Web.Routing; 9 | 10 | namespace EbookManager.Web 11 | { 12 | public class MvcApplication : System.Web.HttpApplication 13 | { 14 | protected void Application_Start() 15 | { 16 | AreaRegistration.RegisterAllAreas(); 17 | GlobalConfiguration.Configure(WebApiConfig.Register); 18 | FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 19 | RouteConfig.RegisterRoutes(RouteTable.Routes); 20 | BundleConfig.RegisterBundles(BundleTable.Bundles); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /EbookManager.Web/Models/AccountViewModels.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace EbookManager.Web.Models 4 | { 5 | public class ExternalLoginConfirmationViewModel 6 | { 7 | [Required] 8 | [Display(Name = "Nom d'utilisateur")] 9 | public string UserName { get; set; } 10 | } 11 | 12 | public class ManageUserViewModel 13 | { 14 | [Required] 15 | [DataType(DataType.Password)] 16 | [Display(Name = "Mot de passe actuel")] 17 | public string OldPassword { get; set; } 18 | 19 | [Required] 20 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 21 | [DataType(DataType.Password)] 22 | [Display(Name = "Nouveau mot de passe")] 23 | public string NewPassword { get; set; } 24 | 25 | [DataType(DataType.Password)] 26 | [Display(Name = "Confirmation du mot de passe")] 27 | [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] 28 | public string ConfirmPassword { get; set; } 29 | } 30 | 31 | public class LoginViewModel 32 | { 33 | [Required] 34 | [Display(Name = "Nom d'utilisateur")] 35 | public string UserName { get; set; } 36 | 37 | [Required] 38 | [DataType(DataType.Password)] 39 | [Display(Name = "Mot de passe")] 40 | public string Password { get; set; } 41 | 42 | [Display(Name = "Se souvenir de moi ?")] 43 | public bool RememberMe { get; set; } 44 | } 45 | 46 | public class RegisterViewModel 47 | { 48 | [Required] 49 | [Display(Name = "Nom d'utilisateur")] 50 | public string UserName { get; set; } 51 | 52 | [Required] 53 | [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] 54 | [DataType(DataType.Password)] 55 | [Display(Name = "Mot de passe")] 56 | public string Password { get; set; } 57 | 58 | [DataType(DataType.Password)] 59 | [Display(Name = "Confirmation du mot de passe")] 60 | [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] 61 | public string ConfirmPassword { get; set; } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /EbookManager.Web/Models/CatalogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using EbookManager.CommonViewModels; 6 | 7 | namespace EbookManager.Web.Models 8 | { 9 | public class CatalogViewModel 10 | { 11 | public CatalogViewModel() 12 | { 13 | this.Ebooks = new List(); 14 | } 15 | 16 | public List Ebooks { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /EbookManager.Web/OAuthServerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin.Security.OAuth; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Security.Claims; 6 | using System.Threading.Tasks; 7 | using System.Web; 8 | 9 | namespace EbookManager.Web 10 | { 11 | public class OAuthServerProvider : OAuthAuthorizationServerProvider 12 | { 13 | // /oauth/authorize 14 | public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context) 15 | { 16 | if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated) 17 | { 18 | // si l'utilisateur est loggé, 19 | // on crée un ticket d'authent, on crée un authorization code et on fait le redirect 20 | var redirectUri = context.Request.Query["redirect_uri"]; 21 | var clientId = context.Request.Query["client_id"]; 22 | var authorizeCodeContext = new Microsoft.Owin.Security.Infrastructure.AuthenticationTokenCreateContext(context.OwinContext, context.Options.AuthorizationCodeFormat, 23 | new Microsoft.Owin.Security.AuthenticationTicket((ClaimsIdentity)context.Request.User.Identity, new Microsoft.Owin.Security.AuthenticationProperties(new Dictionary 24 | { 25 | {"client_id", clientId}, 26 | {"redirect_uri", redirectUri} 27 | }) 28 | { 29 | IssuedUtc = DateTimeOffset.UtcNow, 30 | ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan) 31 | })); 32 | await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext); 33 | 34 | // clear cookies 35 | var cookies = context.Request.Cookies.ToList(); 36 | foreach (var c in cookies) 37 | { 38 | context.Response.Cookies.Delete(c.Key, new Microsoft.Owin.CookieOptions()); 39 | } 40 | context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token)); 41 | } 42 | else 43 | { 44 | // si on n'est pas loggé, on redirige vers la page de login 45 | context.Response.Redirect("/account/login?returnUrl=" + Uri.EscapeDataString(context.Request.Uri.ToString())); 46 | } 47 | context.RequestCompleted(); 48 | } 49 | public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context) 50 | { 51 | // validation d'une authorize request 52 | if (context.AuthorizeRequest.ClientId == "win8client" && context.AuthorizeRequest.IsAuthorizationCodeGrantType) 53 | { 54 | context.Validated(); 55 | } 56 | else 57 | { 58 | context.Rejected(); 59 | } 60 | } 61 | 62 | public override async Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) 63 | { 64 | // appelé pour valider la redirect_uri. 65 | // dans la vraie vie, on valide vraiment :) 66 | context.Validated(context.RedirectUri); 67 | } 68 | 69 | public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 70 | { 71 | // appelé pour valider que le client id et client secret sont valides 72 | string clientId; 73 | string clientSecret; 74 | if (context.TryGetFormCredentials(out clientId, out clientSecret)) 75 | { 76 | if (clientId == "win8client" && clientSecret == "oauthcadeboite") 77 | { 78 | context.Validated(clientId); 79 | return; 80 | } 81 | } 82 | 83 | context.Rejected(); 84 | 85 | } 86 | public override async Task ValidateTokenRequest(OAuthValidateTokenRequestContext context) 87 | { 88 | // valide la requète de token 89 | // dans note cas on accepte les requètes de type "authorize code" et "refresh_token" 90 | if (context.TokenRequest.IsAuthorizationCodeGrantType || context.TokenRequest.IsRefreshTokenGrantType) 91 | { 92 | context.Validated(); 93 | } 94 | else 95 | { 96 | context.Rejected(); 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /EbookManager.Web/Owin/LogMiddleware.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.Web; 7 | using Microsoft.Owin; 8 | 9 | namespace EbookManager.Web.Owin 10 | { 11 | public class LogMiddleware : OwinMiddleware 12 | { 13 | public LogMiddleware(OwinMiddleware next) : base(next) 14 | { 15 | 16 | } 17 | 18 | public async override Task Invoke(IOwinContext context) 19 | { 20 | Debug.WriteLine("Début d'exécution de la requête : {0} {1}", context.Request.Method, context.Request.Uri); 21 | await Next.Invoke(context); 22 | Debug.WriteLine("Fin d'exécution de la requête : {0} {1}", context.Request.Method, context.Request.Uri); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /EbookManager.Web/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EbookManager.Web")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("EbookManager.Web")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("84851e8b-7dbf-496c-9285-6cfaf9d2b5b9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /EbookManager.Web/Scripts/_references.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.Web/Scripts/_references.js -------------------------------------------------------------------------------- /EbookManager.Web/Scripts/jquery.validate.unobtrusive.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /* 16 | ** Unobtrusive validation support library for jQuery and jQuery Validate 17 | ** Copyright (C) Microsoft Corporation. All rights reserved. 18 | */ 19 | (function(a){var d=a.validator,b,e="unobtrusiveValidation";function c(a,b,c){a.rules[b]=c;if(a.message)a.messages[b]=a.message}function j(a){return a.replace(/^\s+|\s+$/g,"").split(/\s*,\s*/g)}function f(a){return a.replace(/([!"#$%&'()*+,./:;<=>?@\[\\\]^`{|}~])/g,"\\$1")}function h(a){return a.substr(0,a.lastIndexOf(".")+1)}function g(a,b){if(a.indexOf("*.")===0)a=a.replace("*.",b);return a}function m(c,e){var b=a(this).find("[data-valmsg-for='"+f(e[0].name)+"']"),d=b.attr("data-valmsg-replace"),g=d?a.parseJSON(d)!==false:null;b.removeClass("field-validation-valid").addClass("field-validation-error");c.data("unobtrusiveContainer",b);if(g){b.empty();c.removeClass("input-validation-error").appendTo(b)}else c.hide()}function l(e,d){var c=a(this).find("[data-valmsg-summary=true]"),b=c.find("ul");if(b&&b.length&&d.errorList.length){b.empty();c.addClass("validation-summary-errors").removeClass("validation-summary-valid");a.each(d.errorList,function(){a("
  • ").html(this.message).appendTo(b)})}}function k(d){var b=d.data("unobtrusiveContainer"),c=b.attr("data-valmsg-replace"),e=c?a.parseJSON(c):null;if(b){b.addClass("field-validation-valid").removeClass("field-validation-error");d.removeData("unobtrusiveContainer");e&&b.empty()}}function n(){var b=a(this);b.data("validator").resetForm();b.find(".validation-summary-errors").addClass("validation-summary-valid").removeClass("validation-summary-errors");b.find(".field-validation-error").addClass("field-validation-valid").removeClass("field-validation-error").removeData("unobtrusiveContainer").find(">*").removeData("unobtrusiveContainer")}function i(c){var b=a(c),d=b.data(e),f=a.proxy(n,c);if(!d){d={options:{errorClass:"input-validation-error",errorElement:"span",errorPlacement:a.proxy(m,c),invalidHandler:a.proxy(l,c),messages:{},rules:{},success:a.proxy(k,c)},attachValidation:function(){b.unbind("reset."+e,f).bind("reset."+e,f).validate(this.options)},validate:function(){b.validate();return b.valid()}};b.data(e,d)}return d}d.unobtrusive={adapters:[],parseElement:function(b,h){var d=a(b),f=d.parents("form")[0],c,e,g;if(!f)return;c=i(f);c.options.rules[b.name]=e={};c.options.messages[b.name]=g={};a.each(this.adapters,function(){var c="data-val-"+this.name,i=d.attr(c),h={};if(i!==undefined){c+="-";a.each(this.params,function(){h[this]=d.attr(c+this)});this.adapt({element:b,form:f,message:i,params:h,rules:e,messages:g})}});a.extend(e,{__dummy__:true});!h&&c.attachValidation()},parse:function(b){var c=a(b).parents("form").andSelf().add(a(b).find("form")).filter("form");a(b).find(":input").filter("[data-val=true]").each(function(){d.unobtrusive.parseElement(this,true)});c.each(function(){var a=i(this);a&&a.attachValidation()})}};b=d.unobtrusive.adapters;b.add=function(c,a,b){if(!b){b=a;a=[]}this.push({name:c,params:a,adapt:b});return this};b.addBool=function(a,b){return this.add(a,function(d){c(d,b||a,true)})};b.addMinMax=function(e,g,f,a,d,b){return this.add(e,[d||"min",b||"max"],function(b){var e=b.params.min,d=b.params.max;if(e&&d)c(b,a,[e,d]);else if(e)c(b,g,e);else d&&c(b,f,d)})};b.addSingleVal=function(a,b,d){return this.add(a,[b||"val"],function(e){c(e,d||a,e.params[b])})};d.addMethod("__dummy__",function(){return true});d.addMethod("regex",function(b,c,d){var a;if(this.optional(c))return true;a=(new RegExp(d)).exec(b);return a&&a.index===0&&a[0].length===b.length});d.addMethod("nonalphamin",function(c,d,b){var a;if(b){a=c.match(/\W/g);a=a&&a.length>=b}return a});if(d.methods.extension){b.addSingleVal("accept","mimtype");b.addSingleVal("extension","extension")}else b.addSingleVal("extension","extension","accept");b.addSingleVal("regex","pattern");b.addBool("creditcard").addBool("date").addBool("digits").addBool("email").addBool("number").addBool("url");b.addMinMax("length","minlength","maxlength","rangelength").addMinMax("range","min","max","range");b.addMinMax("minlength","minlength").addMinMax("maxlength","minlength","maxlength");b.add("equalto",["other"],function(b){var i=h(b.element.name),j=b.params.other,d=g(j,i),e=a(b.form).find(":input").filter("[name='"+f(d)+"']")[0];c(b,"equalTo",e)});b.add("required",function(a){(a.element.tagName.toUpperCase()!=="INPUT"||a.element.type.toUpperCase()!=="CHECKBOX")&&c(a,"required",true)});b.add("remote",["url","type","additionalfields"],function(b){var d={url:b.params.url,type:b.params.type||"GET",data:{}},e=h(b.element.name);a.each(j(b.params.additionalfields||b.element.name),function(i,h){var c=g(h,e);d.data[c]=function(){return a(b.form).find(":input").filter("[name='"+f(c)+"']").val()}});c(b,"remote",d)});b.add("password",["min","nonalphamin","regex"],function(a){a.params.min&&c(a,"minlength",a.params.min);a.params.nonalphamin&&c(a,"nonalphamin",a.params.nonalphamin);a.params.regex&&c(a,"regex",a.params.regex)});a(function(){d.unobtrusive.parse(document)})})(jQuery); -------------------------------------------------------------------------------- /EbookManager.Web/Scripts/respond.min.js: -------------------------------------------------------------------------------- 1 | /* NUGET: BEGIN LICENSE TEXT 2 | * 3 | * Microsoft grants you the right to use these script files for the sole 4 | * purpose of either: (i) interacting through your browser with the Microsoft 5 | * website or online service, subject to the applicable licensing or use 6 | * terms; or (ii) using the files as included with a Microsoft product subject 7 | * to that product's license terms. Microsoft reserves all other rights to the 8 | * files not expressly granted by Microsoft, whether by implication, estoppel 9 | * or otherwise. Insofar as a script file is dual licensed under GPL, 10 | * Microsoft neither took the code under GPL nor distributes it thereunder but 11 | * under the terms set out in this paragraph. All notices and licenses 12 | * below are for informational purposes only. 13 | * 14 | * NUGET: END LICENSE TEXT */ 15 | /*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ 16 | /*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ 17 | window.matchMedia=window.matchMedia||(function(e,f){var c,a=e.documentElement,b=a.firstElementChild||a.firstChild,d=e.createElement("body"),g=e.createElement("div");g.id="mq-test-1";g.style.cssText="position:absolute;top:-100em";d.style.background="none";d.appendChild(g);return function(h){g.innerHTML='­';a.insertBefore(d,b);c=g.offsetWidth==42;a.removeChild(d);return{matches:c,media:h}}})(document); 18 | 19 | /*! Respond.js v1.2.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 20 | (function(e){e.respond={};respond.update=function(){};respond.mediaQueriesSupported=e.matchMedia&&e.matchMedia("only all").matches;if(respond.mediaQueriesSupported){return}var w=e.document,s=w.documentElement,i=[],k=[],q=[],o={},h=30,f=w.getElementsByTagName("head")[0]||s,g=w.getElementsByTagName("base")[0],b=f.getElementsByTagName("link"),d=[],a=function(){var D=b,y=D.length,B=0,A,z,C,x;for(;B-1,minw:F.match(/\(min\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:F.match(/\(max\-width:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}}j()},l,r,v=function(){var z,A=w.createElement("div"),x=w.body,y=false;A.style.cssText="position:absolute;font-size:1em;width:1em";if(!x){x=y=w.createElement("body");x.style.background="none"}x.appendChild(A);s.insertBefore(x,s.firstChild);z=A.offsetWidth;if(y){s.removeChild(x)}else{x.removeChild(A)}z=p=parseFloat(z);return z},p,j=function(I){var x="clientWidth",B=s[x],H=w.compatMode==="CSS1Compat"&&B||w.body[x]||B,D={},G=b[b.length-1],z=(new Date()).getTime();if(I&&l&&z-l-1?(p||v()):1)}if(!!J){J=parseFloat(J)*(J.indexOf(y)>-1?(p||v()):1)}if(!K.hasquery||(!A||!L)&&(A||H>=C)&&(L||H<=J)){if(!D[K.media]){D[K.media]=[]}D[K.media].push(k[K.rules])}}for(var E in q){if(q[E]&&q[E].parentNode===f){f.removeChild(q[E])}}for(var E in D){var M=w.createElement("style"),F=D[E].join("\n");M.type="text/css";M.media=E;f.insertBefore(M,G.nextSibling);if(M.styleSheet){M.styleSheet.cssText=F}else{M.appendChild(w.createTextNode(F))}q.push(M)}},n=function(x,z){var y=c();if(!y){return}y.open("GET",x,true);y.onreadystatechange=function(){if(y.readyState!=4||y.status!=200&&y.status!=304){return}z(y.responseText)};if(y.readyState==4){return}y.send(null)},c=(function(){var x=false;try{x=new XMLHttpRequest()}catch(y){x=new ActiveXObject("Microsoft.XMLHTTP")}return function(){return x}})();a();respond.update=a;function t(){j(true)}if(e.addEventListener){e.addEventListener("resize",t,false)}else{if(e.attachEvent){e.attachEvent("onresize",t)}}})(this); -------------------------------------------------------------------------------- /EbookManager.Web/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using Owin; 3 | 4 | [assembly: OwinStartupAttribute(typeof(EbookManager.Web.Startup))] 5 | namespace EbookManager.Web 6 | { 7 | public partial class Startup 8 | { 9 | public void Configuration(IAppBuilder app) 10 | { 11 | //Configuration du middleware de logs OWIN 12 | ConfigureLogMiddleware(app); 13 | 14 | //Configuration de l'authentification Web via OWIN 15 | ConfigureWebAuth(app); 16 | 17 | //Configuration de l'authentification OAuth2 via OWIN 18 | ConfigureOAuth2(app); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/ExternalLoginConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.Web.Models.ExternalLoginConfirmationViewModel 2 | @{ 3 | ViewBag.Title = "S'enregistrer"; 4 | } 5 |

    @ViewBag.Title.

    6 |

    Associez votre compte @ViewBag.LoginProvider.

    7 | 8 | @using (Html.BeginForm("ExternalLoginConfirmation", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 | 12 |
    13 | @Html.ValidationSummary(true) 14 |

    15 | Vous vous êtes authentifié à l'aide de @ViewBag.LoginProvider. 16 | Pour terminer votre inscription, merci d'entrer votre nom d'utilisateur ci-dessous. 17 |

    18 |
    19 | @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" }) 20 |
    21 | @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) 22 | @Html.ValidationMessageFor(m => m.UserName) 23 |
    24 |
    25 |
    26 |
    27 | 28 |
    29 |
    30 | } 31 | 32 | @section Scripts { 33 | @Scripts.Render("~/bundles/jqueryval") 34 | } 35 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/ExternalLoginFailure.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Echec d'authentification"; 3 | } 4 | 5 |

    @ViewBag.Title.

    6 |

    Le service ne vous a pas correctement authentifié.

    7 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/Login.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.Web.Models.LoginViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Connexion"; 5 | } 6 | 7 |

    @ViewBag.Title.

    8 |
    9 |
    10 |
    11 | @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 12 | { 13 | @Html.AntiForgeryToken() 14 |

    Se connecter à l'aide d'un compte local.

    15 |
    16 | @Html.ValidationSummary(true) 17 |
    18 | @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" }) 19 |
    20 | @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) 21 | @Html.ValidationMessageFor(m => m.UserName) 22 |
    23 |
    24 |
    25 | @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) 26 |
    27 | @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) 28 | @Html.ValidationMessageFor(m => m.Password) 29 |
    30 |
    31 |
    32 |
    33 |
    34 | @Html.CheckBoxFor(m => m.RememberMe) 35 | @Html.LabelFor(m => m.RememberMe) 36 |
    37 |
    38 |
    39 |
    40 |
    41 | 42 |
    43 |
    44 |

    45 | @Html.ActionLink("Créez un compte", "Register") si vous n'en avez pas déjà un. 46 |

    47 | } 48 |
    49 |
    50 |
    51 |
    52 | @Html.Partial("_ExternalLoginsListPartial", new { Action = "ExternalLogin", ReturnUrl = ViewBag.ReturnUrl }) 53 |
    54 |
    55 |
    56 | @section Scripts { 57 | @Scripts.Render("~/bundles/jqueryval") 58 | } -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/Manage.cshtml: -------------------------------------------------------------------------------- 1 | @using EbookManager.Web.Models; 2 | @using Microsoft.AspNet.Identity; 3 | @{ 4 | ViewBag.Title = "Gérer mon compte"; 5 | } 6 | 7 |

    @ViewBag.Title.

    8 | 9 |

    @ViewBag.StatusMessage

    10 |
    11 |
    12 | @if (ViewBag.HasLocalPassword) 13 | { 14 | @Html.Partial("_ChangePasswordPartial") 15 | } 16 | else 17 | { 18 | @Html.Partial("_SetPasswordPartial") 19 | } 20 | 21 |
    22 | @Html.Action("RemoveAccountList") 23 | @Html.Partial("_ExternalLoginsListPartial", new { Action = "LinkLogin", ReturnUrl = ViewBag.ReturnUrl }) 24 |
    25 |
    26 |
    27 | @section Scripts { 28 | @Scripts.Render("~/bundles/jqueryval") 29 | } 30 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/Register.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.Web.Models.RegisterViewModel 2 | @{ 3 | ViewBag.Title = "S'enregistrer"; 4 | } 5 | 6 |

    @ViewBag.Title.

    7 | 8 | @using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 9 | { 10 | @Html.AntiForgeryToken() 11 |

    Créer une nouveau compte.

    12 |
    13 | @Html.ValidationSummary() 14 |
    15 | @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" }) 16 |
    17 | @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" }) 18 |
    19 |
    20 |
    21 | @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" }) 22 |
    23 | @Html.PasswordFor(m => m.Password, new { @class = "form-control" }) 24 |
    25 |
    26 |
    27 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 28 |
    29 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 30 |
    31 |
    32 |
    33 |
    34 | 35 |
    36 |
    37 | } 38 | 39 | @section Scripts { 40 | @Scripts.Render("~/bundles/jqueryval") 41 | } 42 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/_ChangePasswordPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNet.Identity 2 | @model EbookManager.Web.Models.ManageUserViewModel 3 | 4 |

    Vous êtes connecté en tant que @User.Identity.GetUserName().

    5 | 6 | @using (Html.BeginForm("Manage", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 7 | { 8 | @Html.AntiForgeryToken() 9 |

    Changement de mot de passe

    10 |
    11 | @Html.ValidationSummary() 12 |
    13 | @Html.LabelFor(m => m.OldPassword, new { @class = "col-md-2 control-label" }) 14 |
    15 | @Html.PasswordFor(m => m.OldPassword, new { @class = "form-control" }) 16 |
    17 |
    18 |
    19 | @Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" }) 20 |
    21 | @Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" }) 22 |
    23 |
    24 |
    25 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 26 |
    27 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 28 |
    29 |
    30 | 31 |
    32 |
    33 | 34 |
    35 |
    36 | } 37 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/_ExternalLoginsListPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.Owin.Security 2 | 3 |

    Utilisez un service externe pour vous authentifier.

    4 |
    5 | @{ 6 | var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes(); 7 | if (loginProviders.Count() == 0) 8 | { 9 |
    10 |

    Aucun service configuré.

    11 |
    12 | } 13 | else 14 | { 15 | string action = Model.Action; 16 | string returnUrl = Model.ReturnUrl; 17 | using (Html.BeginForm(action, "Account", new { ReturnUrl = returnUrl })) 18 | { 19 | @Html.AntiForgeryToken() 20 |
    21 |

    22 | @foreach (AuthenticationDescription p in loginProviders) 23 | { 24 | 25 | } 26 |

    27 |
    28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/_RemoveAccountPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model ICollection 2 | 3 | @if (Model.Count > 0) 4 | { 5 |

    Comptes associés

    6 | 7 | 8 | @foreach (var account in Model) 9 | { 10 | 11 | 12 | 30 | 31 | } 32 | 33 |
    @account.LoginProvider 13 | @if (ViewBag.ShowRemoveButton) 14 | { 15 | using (Html.BeginForm("Disassociate", "Account")) 16 | { 17 | @Html.AntiForgeryToken() 18 |
    19 | @Html.Hidden("loginProvider", account.LoginProvider) 20 | @Html.Hidden("providerKey", account.ProviderKey) 21 | 22 |
    23 | } 24 | } 25 | else 26 | { 27 | @:   28 | } 29 |
    34 | } 35 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Account/_SetPasswordPartial.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.Web.Models.ManageUserViewModel 2 | 3 | 4 | @using (Html.BeginForm("Manage", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" })) 5 | { 6 | @Html.AntiForgeryToken() 7 | 8 |
    9 | @Html.ValidationSummary() 10 |
    11 | @Html.LabelFor(m => m.NewPassword, new { @class = "col-md-2 control-label" }) 12 |
    13 | @Html.PasswordFor(m => m.NewPassword, new { @class = "form-control" }) 14 |
    15 |
    16 |
    17 | @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" }) 18 |
    19 | @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" }) 20 |
    21 |
    22 |
    23 |
    24 | 25 |
    26 |
    27 | } 28 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Catalog/Details.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.CommonViewModels.EbookViewModel 2 | @{ 3 | ViewBag.Title = Model.Title; 4 | } 5 | 6 |

    @Model.Title

    7 |
    8 | 9 |
    10 |
    11 | @Model.Title 12 |
    13 |
    14 | 15 |

    Résumé

    16 |
    17 |
    18 | @Html.DisplayFor(m => m.Summary) 19 |
    20 |
    21 | 22 |
    23 |
    24 | Cet ebook est composé de @Model.PartsCount partie(s). 25 |
    26 |
    27 | 28 |
    29 | 30 | @if (ViewBag.UserOwnsBook) 31 | { 32 |
    33 |
    34 | Vous possédez déjà cet eBook dans @Html.ActionLink("votre catalogue", "Index", "MySpace"). 35 |
    36 |
    37 | } 38 | else 39 | { 40 |
    41 |
    42 | @using (Html.BeginForm("Buy", "Catalog", FormMethod.Post)) 43 | { 44 | @Html.HiddenFor(m => m.Id) 45 | 46 | } 47 |
    48 |
    49 | } 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Catalog/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.Web.Models.CatalogViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Parcourir le catalogue"; 5 | } 6 | 7 |

    @ViewBag.Title

    8 |
    9 | 10 | @if (!Model.Ebooks.Any()) 11 | { 12 |
    13 | Ooops 14 |

    Notre catalogue semble vide...

    15 |
    16 | } 17 | else 18 | { 19 |
    20 | @foreach (var ebook in Model.Ebooks) 21 | { 22 | @Html.DisplayFor(m => ebook) 23 | } 24 |
    25 | } -------------------------------------------------------------------------------- /EbookManager.Web/Views/DemoAuthenticated/Index.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewBag.Title = "Index"; 4 | } 5 | 6 |

    Index

    7 | 8 |
    9 |

    Demo Webview

    10 |

    You are correcly authenticated as @User.Identity.Name

    11 |
    -------------------------------------------------------------------------------- /EbookManager.Web/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewBag.Title = "Accueil"; 3 | } 4 | 5 |
    6 |

    Ebook Manager

    7 |

    Application de démonstration utilisée pour la session "ASP.NET MVC 5 et Web API 2, Techdays 2014".

    8 |
    9 | 10 |
    11 |
    12 |

    Catalogue

    13 |

    14 | Parcourez notre catalogue de livre, achetez et commencez à lire en quelques instants... 15 |

    16 |

    @Html.ActionLink("Accéder", "Index", "Catalog", null, new{ @class = "btn btn-primary"})

    17 |
    18 |
    19 |

    Mes eBooks

    20 |

    Retrouvez votre collection d'eBooks déjà acheté sur Ebook Manager.

    21 |

    @Html.ActionLink("Accéder", "Index", "MySpace", null, new { @class = "btn btn-primary" })

    22 |
    23 |
    -------------------------------------------------------------------------------- /EbookManager.Web/Views/MySpace/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.Web.Models.CatalogViewModel 2 | 3 | @{ 4 | ViewBag.Title = "Votre catalogue d'eBooks"; 5 | } 6 | 7 |

    @ViewBag.Title

    8 |
    9 | 10 | @if (!Model.Ebooks.Any()) 11 | { 12 |
    13 | Ooops 14 |

    Votre catalogue est vide... Rendez-vous sur @Html.ActionLink("cette page", "Index", "Catalog") pour découvrir notre collection !

    15 |
    16 | } 17 | else 18 | { 19 |
    20 | @foreach (var ebook in Model.Ebooks) 21 | { 22 | @Html.DisplayFor(m => ebook) 23 | } 24 |
    25 | } -------------------------------------------------------------------------------- /EbookManager.Web/Views/Shared/DisplayTemplates/EbookViewModel.cshtml: -------------------------------------------------------------------------------- 1 | @model EbookManager.CommonViewModels.EbookViewModel 2 | 3 |
    4 |
    5 |
    6 | 7 | @Model.Title 8 | 9 |
    10 |
    @Model.Title
    11 |
    @Model.Summary
    12 |
    13 |
    14 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model System.Web.Mvc.HandleErrorInfo 2 | 3 | @{ 4 | ViewBag.Title = "Error"; 5 | } 6 | 7 |

    Error.

    8 |

    An error occurred while processing your request.

    9 | 10 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | @ViewBag.Title - Techdays 2014 7 | @Styles.Render("~/Content/css") 8 | @Scripts.Render("~/bundles/modernizr") 9 | 10 | 11 | 12 | 35 |
    36 | @RenderBody() 37 |
    38 |
    39 |

    © @DateTime.Now.Year - Techdays 2014 Ebook Manager - Julien Corioland / Simon Ferquel

    40 |
    41 |
    42 | 43 | @Scripts.Render("~/bundles/jquery") 44 | @Scripts.Render("~/bundles/jqueryval") 45 | @Scripts.Render("~/bundles/bootstrap") 46 | @RenderSection("scripts", required: false) 47 | 48 | 49 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Shared/_LoginPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNet.Identity 2 | @if (Request.IsAuthenticated) 3 | { 4 | using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" })) 5 | { 6 | @Html.AntiForgeryToken() 7 | 8 | 14 | } 15 | } 16 | else 17 | { 18 | 22 | } 23 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 |
    7 |
    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /EbookManager.Web/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } 4 | -------------------------------------------------------------------------------- /EbookManager.Web/Web.Debug.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /EbookManager.Web/Web.Release.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /EbookManager.Web/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /EbookManager.Web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.Web/favicon.ico -------------------------------------------------------------------------------- /EbookManager.Web/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.Web/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /EbookManager.Web/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.Web/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /EbookManager.Web/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonferquel/techdays-paris-2014-mvc-webapi/2676a01fa3fc0ebfa52e98fa93ae4b03aac3b50d/EbookManager.Web/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /EbookManager.Web/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | techdays-paris-2014-mvc-webapi 2 | ============================== 3 | 4 | Code source associé à la session http://www.microsoft.com/france/mstechdays/programmes/2014/fiche-session.aspx?ID=5f7102fa-7fc1-4aed-b916-00a9303415c3 5 | -------------------------------------------------------------------------------- /UnitTests/AssemblyResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Web.Http.Dispatcher; 8 | 9 | namespace UnitTests 10 | { 11 | class TestWebApiResolver : DefaultAssembliesResolver 12 | { 13 | public override ICollection GetAssemblies() 14 | { 15 | return new List { typeof(EbookManager.Web.Api.UserController).Assembly }; 16 | 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /UnitTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("UnitTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("UnitTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4ca04fe7-22cd-4c03-b65c-de1271730a30")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /UnitTests/TestAuthenticationMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Owin; 2 | using Microsoft.Owin.Security; 3 | using Microsoft.Owin.Security.Infrastructure; 4 | using Owin; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Security.Claims; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace UnitTests 13 | { 14 | public class TestAuthenticationOptions : AuthenticationOptions 15 | { 16 | ClaimsIdentity _identity; 17 | public ClaimsIdentity Identity { get { return _identity; } } 18 | 19 | public TestAuthenticationOptions(ClaimsIdentity identity) 20 | : base(identity.AuthenticationType) 21 | { 22 | _identity = identity; 23 | base.AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active; 24 | } 25 | 26 | } 27 | 28 | public static class TestAuthenticationMiddlewareExtensions 29 | { 30 | public static IAppBuilder UseTestAuthentication(this IAppBuilder app, ClaimsIdentity identity) 31 | { 32 | return app.Use(new TestAuthenticationOptions(identity)); 33 | } 34 | } 35 | public class TestAuthenticationMiddleware : AuthenticationMiddleware 36 | { 37 | public TestAuthenticationMiddleware(OwinMiddleware next, TestAuthenticationOptions options) 38 | : base(next, options) 39 | { 40 | 41 | } 42 | protected override AuthenticationHandler CreateHandler() 43 | { 44 | return new TestAuthenticationHandler(); 45 | } 46 | } 47 | 48 | public class TestAuthenticationHandler : AuthenticationHandler 49 | { 50 | protected override async Task AuthenticateCoreAsync() 51 | { 52 | return new AuthenticationTicket(Options.Identity, new AuthenticationProperties()); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /UnitTests/WebApiTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EbookManager.Web.Owin; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using System.Threading.Tasks; 5 | using Microsoft.Owin.Testing; 6 | using Owin; 7 | using System.Security.Claims; 8 | using System.Web.Http; 9 | using System.Net.Http; 10 | using System.Web.Http.Dispatcher; 11 | using System.Collections.Generic; 12 | using System.Reflection; 13 | 14 | namespace UnitTests 15 | { 16 | [TestClass] 17 | public class WebApiTests 18 | { 19 | static ClaimsIdentity _testIdentity; 20 | class OwinTestConf 21 | { 22 | public void Configuration(IAppBuilder app) 23 | { 24 | app.Use(typeof (LogMiddleware)); 25 | app.UseTestAuthentication(_testIdentity); 26 | HttpConfiguration config = new HttpConfiguration(); 27 | config.Services.Replace(typeof(IAssembliesResolver), new TestWebApiResolver()); 28 | config.MapHttpAttributeRoutes(); 29 | app.UseWebApi(config); 30 | } 31 | } 32 | 33 | 34 | 35 | class WhoAmiResult 36 | { 37 | public string UserName { get; set; } 38 | public string NameIdentifier { get; set; } 39 | } 40 | 41 | [TestMethod] 42 | public async Task Test_WhoAmi() 43 | { 44 | _testIdentity = new ClaimsIdentity("testidentity"); 45 | _testIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "test name identifier")); 46 | _testIdentity.AddClaim(new Claim(ClaimTypes.Name, "test user")); 47 | using (var server = TestServer.Create()) 48 | { 49 | using (var client = new HttpClient(server.Handler)) 50 | { 51 | var response = await client.GetAsync("http://testserver/api/user/whoami"); 52 | var result = await response.Content.ReadAsAsync(); 53 | Assert.AreEqual("test user", result.UserName); 54 | Assert.AreEqual("test name identifier", result.NameIdentifier); 55 | } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /UnitTests/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /UnitTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | --------------------------------------------------------------------------------