├── .gitattributes ├── .gitignore ├── Chapter04 └── TicTacToe │ ├── Controllers │ ├── HomeController.cs │ └── UserRegistrationController.cs │ ├── Extensions │ └── CommunicationMiddlewareExtension.cs │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── ErrorViewModel.cs │ └── UserModel.cs │ ├── Program.cs │ ├── Services │ ├── IUserService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── Views │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── package.json │ └── wwwroot │ ├── css │ └── site.css │ ├── favicon.ico │ ├── js │ └── site.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter05 └── TicTacToe │ ├── Controllers │ ├── GameInvitationController.cs │ ├── HomeController.cs │ └── UserRegistrationController.cs │ ├── Extensions │ └── CommunicationMiddlewareExtension.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ └── UserModel.cs │ ├── Options │ └── EmailServiceOptions.cs │ ├── Program.cs │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── GameInvitationService.cs │ ├── IEmailService.cs │ ├── IGameInvitationService.cs │ ├── IUserService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── Views │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ └── wwwroot │ ├── app │ └── js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter06 ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj └── TicTacToe │ ├── Controllers │ ├── GameInvitationController.cs │ ├── HomeController.cs │ └── UserRegistrationController.cs │ ├── Extensions │ ├── CommunicationMiddlewareExtension.cs │ └── ConfigureLoggingExtension.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ └── UserModel.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ └── LoggingProviderOption.cs │ ├── Program.cs │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── GameInvitationService.cs │ ├── IEmailService.cs │ ├── IGameInvitationService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── Views │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── Shared │ │ ├── Error.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ └── wwwroot │ ├── app │ └── js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter07 ├── TicTacToe.IntegrationTests │ ├── IntegrationTests.cs │ └── TicTacToe.IntegrationTests.csproj ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj ├── TicTacToe.UnitTests │ ├── FileLoggerTests.cs │ ├── TicTacToe.UnitTests.csproj │ └── UserServiceTests.cs └── TicTacToe │ ├── Areas │ └── Account │ │ ├── Controllers │ │ └── HomeController.cs │ │ └── Views │ │ └── Home │ │ └── Index.cshtml │ ├── Components │ └── GameSessionViewComponent.cs │ ├── Controllers │ ├── GameInvitationController.cs │ ├── GameSessionController.cs │ ├── HomeController.cs │ ├── LeaderboardController.cs │ └── UserRegistrationController.cs │ ├── Extensions │ ├── CollectionsExtensionMethods.cs │ ├── CommunicationMiddlewareExtension.cs │ └── ConfigureLoggingExtension.cs │ ├── Filters │ └── DetectMobileFilter.cs │ ├── Helpers │ └── EmailViewRenderHelper.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── GameInvitation.Index.resx │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── LeaderBoard.Index.fr.resx │ │ ├── LeaderBoard.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── AccountModel.cs │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ ├── GameSessionModel.cs │ ├── InvitationEmailModel.cs │ ├── TurnModel.cs │ ├── UserModel.cs │ └── UserRegistrationEmailModel.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ └── LoggingProviderOption.cs │ ├── Program.cs │ ├── ScaffoldingReadMe.txt │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── EmailTemplateRenderService.cs │ ├── GameInvitationService.cs │ ├── GameSessionService.cs │ ├── IEmailService.cs │ ├── IEmailTemplateRenderService.cs │ ├── IGameInvitationService.cs │ ├── IGameSessionService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TagHelpers │ └── GravatarTagHelper.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── ViewEngines │ ├── EmailViewEngine.cs │ └── IEmailViewEngine.cs │ ├── Views │ ├── EmailTemplates │ │ ├── InvitationEmail.cshtml │ │ └── UserRegistrationEmail.cshtml │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── GameSession │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── LeaderBoard │ │ └── Index.cshtml │ ├── Shared │ │ ├── Components │ │ │ └── GameSession │ │ │ │ └── default.cshtml │ │ ├── Error.cshtml │ │ ├── _Account.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LayoutEmail.cshtml │ │ ├── _LayoutMobile.cshtml │ │ ├── _Menu.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ └── wwwroot │ ├── Emails │ ├── EmailTemplates_UserRegistrationEmail.29090919094651.html │ ├── EmailTemplates_UserRegistrationEmail.29091019100325.html │ ├── EmailTemplates_UserRegistrationEmail.29091319133402.html │ └── EmailTemplates_UserRegistrationEmail.29091719171612.html │ ├── app │ └── js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter08 ├── TicTacToe.IntegrationTests │ ├── IntegrationTests.cs │ └── TicTacToe.IntegrationTests.csproj ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj ├── TicTacToe.UnitTests │ ├── FileLoggerTests.cs │ ├── TicTacToe.UnitTests.csproj │ └── UserServiceTests.cs └── TicTacToe │ ├── Areas │ └── Account │ │ ├── Controllers │ │ └── HomeController.cs │ │ └── Views │ │ └── Home │ │ └── Index.cshtml │ ├── Components │ └── GameSessionViewComponent.cs │ ├── Controllers │ ├── GameInvitationApiController.cs │ ├── GameInvitationController.cs │ ├── GameSessionController.cs │ ├── HomeController.cs │ ├── LeaderboardController.cs │ └── UserRegistrationController.cs │ ├── Extensions │ ├── CommunicationMiddlewareExtension.cs │ └── ConfigureLoggingExtension.cs │ ├── Filters │ └── DetectMobileFilter.cs │ ├── Helpers │ └── EmailViewRenderHelper.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── GameInvitation.Index.resx │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── LeaderBoard.Index.fr.resx │ │ ├── LeaderBoard.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── AccountModel.cs │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ ├── GameSessionModel.cs │ ├── InvitationEmailModel.cs │ ├── TurnModel.cs │ ├── UserModel.cs │ └── UserRegistrationEmailModel.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ └── LoggingProviderOption.cs │ ├── Program.cs │ ├── ScaffoldingReadMe.txt │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── EmailTemplateRenderService.cs │ ├── GameInvitationService.cs │ ├── GameSessionService.cs │ ├── IEmailService.cs │ ├── IEmailTemplateRenderService.cs │ ├── IGameInvitationService.cs │ ├── IGameSessionService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TagHelpers │ └── GravatarTagHelper.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── ViewEngines │ ├── EmailViewEngine.cs │ └── IEmailViewEngine.cs │ ├── Views │ ├── EmailTemplates │ │ ├── InvitationEmail.cshtml │ │ └── UserRegistrationEmail.cshtml │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── GameSession │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── LeaderBoard │ │ └── Index.cshtml │ ├── Shared │ │ ├── Components │ │ │ └── GameSession │ │ │ │ └── default.cshtml │ │ ├── Error.cshtml │ │ ├── _Account.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LayoutEmail.cshtml │ │ ├── _LayoutMobile.cshtml │ │ ├── _Menu.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ └── wwwroot │ ├── Emails │ ├── EmailTemplates_UserRegistrationEmail.29090919094651.html │ ├── EmailTemplates_UserRegistrationEmail.29091019100325.html │ ├── EmailTemplates_UserRegistrationEmail.29091319133402.html │ ├── EmailTemplates_UserRegistrationEmail.29091719171612.html │ ├── EmailTemplates_UserRegistrationEmail.29091719172330.html │ ├── EmailTemplates_UserRegistrationEmail.29091919191453.html │ ├── EmailTemplates_UserRegistrationEmail.30091019102908.html │ └── EmailTemplates_UserRegistrationEmail.30091219123320.html │ ├── app │ └── js │ │ ├── CheckGameSessionIsFinished.js │ │ ├── CheckTurnIsFinished.js │ │ ├── GameSession.js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter09 ├── TicTacToe.IntegrationTests │ ├── IntegrationTests.cs │ └── TicTacToe.IntegrationTests.csproj ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj ├── TicTacToe.UnitTests │ ├── FileLoggerTests.cs │ ├── TicTacToe.UnitTests.csproj │ └── UserServiceTests.cs └── TicTacToe │ ├── Areas │ └── Account │ │ ├── Controllers │ │ └── HomeController.cs │ │ └── Views │ │ └── Home │ │ └── Index.cshtml │ ├── Components │ └── GameSessionViewComponent.cs │ ├── Controllers │ ├── GameInvitationApiController.cs │ ├── GameInvitationController.cs │ ├── GameSessionController.cs │ ├── HomeController.cs │ ├── LeaderboardController.cs │ └── UserRegistrationController.cs │ ├── Data │ ├── GameDbContext.cs │ └── GameDbContextFactory.cs │ ├── Extensions │ ├── CommunicationMiddlewareExtension.cs │ ├── ConfigureLoggingExtension.cs │ └── ModelBuilderExtensions.cs │ ├── Filters │ └── DetectMobileFilter.cs │ ├── Helpers │ └── EmailViewRenderHelper.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── GameInvitation.Index.resx │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── LeaderBoard.Index.fr.resx │ │ ├── LeaderBoard.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Migrations │ ├── 20191203114005_InitialDbSchema.Designer.cs │ ├── 20191203114005_InitialDbSchema.cs │ └── GameDbContextModelSnapshot.cs │ ├── Models │ ├── AccountModel.cs │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ ├── GameSessionModel.cs │ ├── InvitationEmailModel.cs │ ├── TurnModel.cs │ ├── UserModel.cs │ └── UserRegistrationEmailModel.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ └── LoggingProviderOption.cs │ ├── Program.cs │ ├── ScaffoldingReadMe.txt │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── EmailTemplateRenderService.cs │ ├── GameInvitationService.cs │ ├── GameSessionService.cs │ ├── IEmailService.cs │ ├── IEmailTemplateRenderService.cs │ ├── IGameInvitationService.cs │ ├── IGameSessionService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TagHelpers │ └── GravatarTagHelper.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── ViewEngines │ ├── EmailViewEngine.cs │ └── IEmailViewEngine.cs │ ├── Views │ ├── EmailTemplates │ │ ├── InvitationEmail.cshtml │ │ └── UserRegistrationEmail.cshtml │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── GameSession │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── LeaderBoard │ │ └── Index.cshtml │ ├── Shared │ │ ├── Components │ │ │ └── GameSession │ │ │ │ └── default.cshtml │ │ ├── Error.cshtml │ │ ├── _Account.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LayoutEmail.cshtml │ │ ├── _LayoutMobile.cshtml │ │ ├── _Menu.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ └── wwwroot │ ├── Emails │ ├── EmailTemplates_UserRegistrationEmail.29090919094651.html │ ├── EmailTemplates_UserRegistrationEmail.29091019100325.html │ ├── EmailTemplates_UserRegistrationEmail.29091319133402.html │ ├── EmailTemplates_UserRegistrationEmail.29091719171612.html │ ├── EmailTemplates_UserRegistrationEmail.29091719172330.html │ ├── EmailTemplates_UserRegistrationEmail.29091919191453.html │ ├── EmailTemplates_UserRegistrationEmail.30091019102908.html │ └── EmailTemplates_UserRegistrationEmail.30091219123320.html │ ├── app │ └── js │ │ ├── CheckGameSessionIsFinished.js │ │ ├── CheckTurnIsFinished.js │ │ ├── GameSession.js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter10 ├── TicTacToe.IntegrationTests │ ├── IntegrationTests.cs │ └── TicTacToe.IntegrationTests.csproj ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj ├── TicTacToe.UnitTests │ ├── FileLoggerTests.cs │ ├── TicTacToe.UnitTests.csproj │ └── UserServiceTests.cs └── TicTacToe │ ├── Areas │ └── Account │ │ ├── Controllers │ │ └── HomeController.cs │ │ └── Views │ │ └── Home │ │ └── Index.cshtml │ ├── Components │ └── GameSessionViewComponent.cs │ ├── Controllers │ ├── AccountController.cs │ ├── GameInvitationApiController.cs │ ├── GameInvitationController.cs │ ├── GameSessionController.cs │ ├── HomeController.cs │ ├── LeaderboardController.cs │ └── UserRegistrationController.cs │ ├── Data │ ├── GameDbContext.cs │ └── GameDbContextFactory.cs │ ├── Extensions │ ├── CommunicationMiddlewareExtension.cs │ ├── ConfigureLoggingExtension.cs │ └── ModelBuilderExtensions.cs │ ├── Filters │ └── DetectMobileFilter.cs │ ├── Helpers │ └── EmailViewRenderHelper.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── GameInvitation.Index.resx │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── LeaderBoard.Index.fr.resx │ │ ├── LeaderBoard.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Managers │ └── ApplicationUserManager.cs │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── AccountModel.cs │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ ├── GameSessionModel.cs │ ├── InvitationEmailModel.cs │ ├── LoginModel.cs │ ├── RoleModel.cs │ ├── TurnModel.cs │ ├── UserModel.cs │ └── UserRegistrationEmailModel.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ └── LoggingProviderOption.cs │ ├── Program.cs │ ├── ScaffoldingReadMe.txt │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── EmailTemplateRenderService.cs │ ├── GameInvitationService.cs │ ├── GameSessionService.cs │ ├── IEmailService.cs │ ├── IEmailTemplateRenderService.cs │ ├── IGameInvitationService.cs │ ├── IGameSessionService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TagHelpers │ └── GravatarTagHelper.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── ViewEngines │ ├── EmailViewEngine.cs │ └── IEmailViewEngine.cs │ ├── Views │ ├── Account │ │ └── Login.cshtml │ ├── EmailTemplates │ │ ├── InvitationEmail.cshtml │ │ └── UserRegistrationEmail.cshtml │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── GameSession │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── LeaderBoard │ │ └── Index.cshtml │ ├── Shared │ │ ├── Components │ │ │ └── GameSession │ │ │ │ └── default.cshtml │ │ ├── Error.cshtml │ │ ├── _Account.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LayoutEmail.cshtml │ │ ├── _LayoutMobile.cshtml │ │ ├── _Menu.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ └── wwwroot │ ├── Emails │ ├── EmailTemplates_UserRegistrationEmail.29090919094651.html │ ├── EmailTemplates_UserRegistrationEmail.29091019100325.html │ ├── EmailTemplates_UserRegistrationEmail.29091319133402.html │ ├── EmailTemplates_UserRegistrationEmail.29091719171612.html │ ├── EmailTemplates_UserRegistrationEmail.29091719172330.html │ ├── EmailTemplates_UserRegistrationEmail.29091919191453.html │ ├── EmailTemplates_UserRegistrationEmail.30091019102908.html │ └── EmailTemplates_UserRegistrationEmail.30091219123320.html │ ├── app │ └── js │ │ ├── CheckGameSessionIsFinished.js │ │ ├── CheckTurnIsFinished.js │ │ ├── GameSession.js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter12 ├── TicTacToe.IntegrationTests │ ├── IntegrationTests.cs │ └── TicTacToe.IntegrationTests.csproj ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj ├── TicTacToe.UnitTests │ ├── FileLoggerTests.cs │ ├── TicTacToe.UnitTests.csproj │ └── UserServiceTests.cs └── TicTacToe │ ├── Areas │ └── Account │ │ ├── Controllers │ │ └── HomeController.cs │ │ └── Views │ │ └── Home │ │ └── Index.cshtml │ ├── Components │ └── GameSessionViewComponent.cs │ ├── Controllers │ ├── AccountController.cs │ ├── GameInvitationApiController.cs │ ├── GameInvitationController.cs │ ├── GameSessionController.cs │ ├── HomeController.cs │ ├── LeaderboardController.cs │ └── UserRegistrationController.cs │ ├── Data │ ├── GameDbContext.cs │ └── GameDbContextFactory.cs │ ├── Extensions │ ├── CommunicationMiddlewareExtension.cs │ ├── ConfigureLoggingExtension.cs │ └── ModelBuilderExtensions.cs │ ├── Filters │ └── DetectMobileFilter.cs │ ├── Helpers │ └── EmailViewRenderHelper.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── GameInvitation.Index.resx │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── LeaderBoard.Index.fr.resx │ │ ├── LeaderBoard.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Managers │ └── ApplicationUserManager.cs │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── AccountModel.cs │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ ├── GameSessionModel.cs │ ├── InvitationEmailModel.cs │ ├── LoginModel.cs │ ├── RoleModel.cs │ ├── TurnModel.cs │ ├── UserModel.cs │ └── UserRegistrationEmailModel.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ └── LoggingProviderOption.cs │ ├── Program.cs │ ├── ScaffoldingReadMe.txt │ ├── Services │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── EmailTemplateRenderService.cs │ ├── GameInvitationService.cs │ ├── GameSessionService.cs │ ├── IEmailService.cs │ ├── IEmailTemplateRenderService.cs │ ├── IGameInvitationService.cs │ ├── IGameSessionService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TagHelpers │ └── GravatarTagHelper.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── ViewEngines │ ├── EmailViewEngine.cs │ └── IEmailViewEngine.cs │ ├── Views │ ├── Account │ │ └── Login.cshtml │ ├── EmailTemplates │ │ ├── InvitationEmail.cshtml │ │ └── UserRegistrationEmail.cshtml │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── GameSession │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── LeaderBoard │ │ └── Index.cshtml │ ├── Shared │ │ ├── Components │ │ │ └── GameSession │ │ │ │ └── default.cshtml │ │ ├── Error.cshtml │ │ ├── _Account.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LayoutEmail.cshtml │ │ ├── _LayoutMobile.cshtml │ │ ├── _Menu.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ ├── web.config │ └── wwwroot │ ├── Emails │ ├── EmailTemplates_UserRegistrationEmail.29090919094651.html │ ├── EmailTemplates_UserRegistrationEmail.29091019100325.html │ ├── EmailTemplates_UserRegistrationEmail.29091319133402.html │ ├── EmailTemplates_UserRegistrationEmail.29091719171612.html │ ├── EmailTemplates_UserRegistrationEmail.29091719172330.html │ ├── EmailTemplates_UserRegistrationEmail.29091919191453.html │ ├── EmailTemplates_UserRegistrationEmail.30091019102908.html │ └── EmailTemplates_UserRegistrationEmail.30091219123320.html │ ├── app │ └── js │ │ ├── CheckGameSessionIsFinished.js │ │ ├── CheckTurnIsFinished.js │ │ ├── GameSession.js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── Chapter13 ├── TicTacToe.IntegrationTests │ ├── IntegrationTests.cs │ └── TicTacToe.IntegrationTests.csproj ├── TicTacToe.Logging │ ├── FileLogger.cs │ ├── FileLoggerExtensions.cs │ ├── FileLoggerHelper.cs │ ├── FileLoggerProvider.cs │ ├── LogEntry.cs │ └── TicTacToe.Logging.csproj ├── TicTacToe.UnitTests │ ├── FileLoggerTests.cs │ ├── TicTacToe.UnitTests.csproj │ └── UserServiceTests.cs └── TicTacToe │ ├── Areas │ └── Account │ │ ├── Controllers │ │ └── HomeController.cs │ │ └── Views │ │ ├── Home │ │ └── Index.cshtml │ │ └── _ViewImports.cshtml │ ├── Components │ └── GameSessionViewComponent.cs │ ├── Controllers │ ├── AccountController.cs │ ├── GameInvitationApiController.cs │ ├── GameInvitationController.cs │ ├── GameSessionController.cs │ ├── HomeController.cs │ ├── LeaderboardController.cs │ └── UserRegistrationController.cs │ ├── Data │ ├── GameDbContext.cs │ └── GameDbContextFactory.cs │ ├── Extensions │ ├── CommunicationMiddlewareExtension.cs │ ├── ConfigureLoggingExtension.cs │ ├── EmailServiceExtension.cs │ └── ModelBuilderExtensions.cs │ ├── Filters │ └── DetectMobileFilter.cs │ ├── Helpers │ └── EmailViewRenderHelper.cs │ ├── Localization │ ├── Controllers │ │ ├── GameInvitationController.fr.resx │ │ └── GameInvitationController.resx │ ├── Models │ │ ├── UserModel.fr.resx │ │ └── UserModel.resx │ └── Views │ │ ├── GameInvitation.Index.resx │ │ ├── Home.Index.fr.resx │ │ ├── Home.Index.resx │ │ ├── LeaderBoard.Index.fr.resx │ │ ├── LeaderBoard.Index.resx │ │ ├── UserRegistration.Index.fr.resx │ │ └── UserRegistration.Index.resx │ ├── Managers │ └── ApplicationUserManager.cs │ ├── Middlewares │ └── CommunicationMiddleware.cs │ ├── Models │ ├── AccountModel.cs │ ├── ErrorViewModel.cs │ ├── GameInvitationModel.cs │ ├── GameSessionModel.cs │ ├── InvitationEmailModel.cs │ ├── LoginModel.cs │ ├── ResetPasswordEmailModel.cs │ ├── ResetPasswordModel.cs │ ├── RoleModel.cs │ ├── TurnModel.cs │ ├── TwoFactorCodeModel.cs │ ├── TwoFactorEmailModel.cs │ ├── UserModel.cs │ ├── UserRegistrationEmailModel.cs │ ├── UserRoleModel.cs │ └── ValidateTwoFactorModel.cs │ ├── Monitoring │ └── ApplicationDiagnosticListener.cs │ ├── Options │ ├── EmailServiceOptions.cs │ ├── LoggingOptions.cs │ ├── LoggingProviderOption.cs │ └── MonitoringOptions.cs │ ├── Program.cs │ ├── ScaffoldingReadMe.txt │ ├── Services │ ├── AmazonWebServicesMonitoringService.cs │ ├── AzureApplicationInsightsMonitoringService.cs │ ├── CultureProviderResolverService.cs │ ├── EmailService.cs │ ├── EmailTemplateRenderService.cs │ ├── GameInvitationService.cs │ ├── GameSessionService.cs │ ├── IEmailService.cs │ ├── IEmailTemplateRenderService.cs │ ├── IGameInvitationService.cs │ ├── IGameSessionService.cs │ ├── IMonitoringService.cs │ ├── IUserService.cs │ ├── SendGridEmailService.cs │ └── UserService.cs │ ├── Startup.cs │ ├── TagHelpers │ └── GravatarTagHelper.cs │ ├── TicTacToe.csproj │ ├── TicTacToe.sln │ ├── ViewEngines │ ├── EmailViewEngine.cs │ └── IEmailViewEngine.cs │ ├── Views │ ├── Account │ │ └── Login.cshtml │ ├── EmailTemplates │ │ ├── InvitationEmail.cshtml │ │ └── UserRegistrationEmail.cshtml │ ├── GameInvitation │ │ ├── GameInvitationConfirmation.cshtml │ │ └── Index.cshtml │ ├── GameSession │ │ └── Index.cshtml │ ├── Home │ │ ├── Index.cshtml │ │ └── Privacy.cshtml │ ├── LeaderBoard │ │ └── Index.cshtml │ ├── Shared │ │ ├── Components │ │ │ └── GameSession │ │ │ │ └── default.cshtml │ │ ├── Error.cshtml │ │ ├── _Account.cshtml │ │ ├── _CookieConsentPartial.cshtml │ │ ├── _Layout.cshtml │ │ ├── _LayoutEmail.cshtml │ │ ├── _LayoutMobile.cshtml │ │ ├── _Menu.cshtml │ │ └── _ValidationScriptsPartial.cshtml │ ├── UserRegistration │ │ ├── EmailConfirmation.cshtml │ │ └── Index.cshtml │ ├── _ViewImports.cshtml │ └── _ViewStart.cshtml │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── bundleconfig.json │ ├── bundleconfig.json.bindings │ ├── package.json │ ├── web.config │ └── wwwroot │ ├── Emails │ ├── EmailTemplates_UserRegistrationEmail.29090919094651.html │ ├── EmailTemplates_UserRegistrationEmail.29091019100325.html │ ├── EmailTemplates_UserRegistrationEmail.29091319133402.html │ ├── EmailTemplates_UserRegistrationEmail.29091719171612.html │ ├── EmailTemplates_UserRegistrationEmail.29091719172330.html │ ├── EmailTemplates_UserRegistrationEmail.29091919191453.html │ ├── EmailTemplates_UserRegistrationEmail.30091019102908.html │ └── EmailTemplates_UserRegistrationEmail.30091219123320.html │ ├── app │ └── js │ │ ├── CheckGameSessionIsFinished.js │ │ ├── CheckTurnIsFinished.js │ │ ├── GameSession.js │ │ ├── scripts1.js │ │ └── scripts2.js │ ├── css │ ├── site.css │ └── site.min.css │ ├── favicon.ico │ ├── js │ ├── site.js │ └── site.min.js │ └── lib │ ├── bootstrap │ ├── LICENSE │ └── dist │ │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ └── bootstrap.min.css.map │ │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── jquery-validation-unobtrusive │ ├── LICENSE.txt │ ├── jquery.validate.unobtrusive.js │ └── jquery.validate.unobtrusive.min.js │ ├── jquery-validation │ ├── LICENSE.md │ └── dist │ │ ├── additional-methods.js │ │ ├── additional-methods.min.js │ │ ├── jquery.validate.js │ │ └── jquery.validate.min.js │ └── jquery │ ├── LICENSE.txt │ └── dist │ ├── jquery.js │ ├── jquery.min.js │ └── jquery.min.map ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Mvc; 7 | using TicTacToe.Models; 8 | 9 | namespace TicTacToe.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | public IActionResult Index() 14 | { 15 | return View(); 16 | } 17 | 18 | public IActionResult Privacy() 19 | { 20 | return View(); 21 | } 22 | 23 | [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] 24 | public IActionResult Error() 25 | { 26 | return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Middlewares/CommunicationMiddleware.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Middlewares 9 | { 10 | public class CommunicationMiddleware 11 | { 12 | private readonly RequestDelegate _next; 13 | private readonly IUserService _userService; 14 | 15 | public CommunicationMiddleware(RequestDelegate next, 16 | IUserService userService) 17 | { 18 | _next = next; 19 | _userService = userService; 20 | } 21 | 22 | public async Task Invoke(HttpContext context) 23 | { 24 | await _next.Invoke(context); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Models/UserModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class UserModel 10 | { 11 | public Guid Id { get; set; } 12 | [Required()] 13 | public string FirstName { get; set; } 14 | [Required()] 15 | public string LastName { get; set; } 16 | 17 | [Required(), DataType(DataType.EmailAddress)] 18 | public string Email { get; set; } 19 | 20 | [Required(), DataType(DataType.Password)] 21 | public string Password { get; set; } 22 | public bool IsEmailConfirmed { get; set; } 23 | public System.DateTime? EmailConfirmationDate { get; set; } 24 | public int Score { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using TicTacToe.Models; 3 | 4 | namespace TicTacToe.Services 5 | { 6 | public interface IUserService 7 | { 8 | Task IsOnline(string name); 9 | Task RegisterUser(UserModel userModel); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Services/UserService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using TicTacToe.Models; 6 | 7 | namespace TicTacToe.Services 8 | { 9 | public class UserService : IUserService 10 | { 11 | public Task RegisterUser(UserModel userModel) 12 | { 13 | return Task.FromResult(true); 14 | } 15 | public Task IsOnline(string name) 16 | { 17 | return Task.FromResult(true); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 | 5 |
6 |
7 |

Tic-Tac-Toe

8 |
9 |

Tic-Tac-Toe is a two-player turn-based game.

10 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

11 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

12 |
13 |

Register by clicking here

14 |
15 |
16 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.4.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter04/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter04/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter04/TicTacToe/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /Chapter04/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Models/GameInvitationModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class GameInvitationModel 9 | { 10 | public Guid Id { get; set; } 11 | public string EmailTo { get; set; } 12 | public string InvitedBy { get; set; } 13 | public bool IsConfirmed { get; set; } 14 | public DateTime ConfirmationDate { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameInvitationService 8 | { 9 | Task Add(GameInvitationModel gameInvitationModel); 10 | Task Get(Guid id); 11 | Task Update(GameInvitationModel gameInvitationModel); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using TicTacToe.Models; 3 | 4 | namespace TicTacToe.Services 5 | { 6 | public interface IUserService 7 | { 8 | Task IsOnline(string name); 9 | Task RegisterUser(UserModel userModel); 10 | Task GetUserByEmail(string email); 11 | Task UpdateUser(UserModel user); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

@Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 7 | Model.EmailTo]

8 | @section Scripts{ 9 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/GameInvitation/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | 3 | @{ 4 | ViewData["Title"] = "Index"; 5 | } 6 | 7 |

Index

8 | 9 |

GameInvitationModel

10 |
11 |
12 |
13 |
14 |
15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 |
28 | 29 |
30 | Back to List 31 |
32 | 33 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |

@Localizer["Title"]

5 |
6 |
7 |

Tic-Tac-Toe

8 |
9 |

Tic-Tac-Toe is a two-player turn-based game.

10 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

11 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

12 |
13 |

Register by clicking here

14 |
15 |
16 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 8 | @section Scripts 9 | { 10 | 15 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "Email": { 10 | "MailType": "SMTP", 11 | "MailServer": "localhost", 12 | "MailPort": 25, 13 | "UseSSL": false, 14 | "UserId": "", 15 | "Password": "", 16 | "RemoteServerAPI": "", 17 | "RemoteServerKey": "" 18 | }, 19 | "AllowedHosts": "*" 20 | } 21 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js" 13 | ], 14 | "sourceMap": true, 15 | "includeInProject": true 16 | }, 17 | { 18 | "outputFileName": "wwwroot/js/site.min.js", 19 | "inputFiles": [ 20 | "wwwroot/js/site.js" 21 | ], 22 | "minify": { 23 | "enabled": true, 24 | "renameLocals": true 25 | }, 26 | "sourceMap": false 27 | } 28 | ] 29 | 30 | -------------------------------------------------------------------------------- /Chapter05/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter05/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.3.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter05/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter05/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter05/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Models/GameInvitationModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class GameInvitationModel 9 | { 10 | public Guid Id { get; set; } 11 | public string EmailTo { get; set; } 12 | public string InvitedBy { get; set; } 13 | public bool IsConfirmed { get; set; } 14 | public DateTime ConfirmationDate { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameInvitationService 8 | { 9 | Task Add(GameInvitationModel gameInvitationModel); 10 | Task Get(Guid id); 11 | Task Update(GameInvitationModel gameInvitationModel); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using TicTacToe.Models; 3 | 4 | namespace TicTacToe.Services 5 | { 6 | public interface IUserService 7 | { 8 | Task IsOnline(string name); 9 | Task RegisterUser(UserModel userModel); 10 | Task GetUserByEmail(string email); 11 | Task UpdateUser(UserModel user); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

@Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 7 | Model.EmailTo]

8 | @section Scripts{ 9 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/GameInvitation/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | 3 | @{ 4 | ViewData["Title"] = "Index"; 5 | } 6 | 7 |

Index

8 | 9 |

GameInvitationModel

10 |
11 |
12 |
13 |
14 |
15 | 16 |
17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 |
25 |
26 |
27 |
28 | 29 |
30 | Back to List 31 |
32 | 33 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |

@Localizer["Title"]

5 |
6 |
7 |

Tic-Tac-Toe

8 |
9 |

Tic-Tac-Toe is a two-player turn-based game.

10 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

11 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

12 |
13 |

Register by clicking here

14 |
15 |
16 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

8 | 9 | @if (Model.ShowRequestId) 10 | { 11 |

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

18 | Swapping to Development environment will display more detailed information about the error that occurred. 19 |

20 |

21 | The Development environment shouldn't be enabled for deployed applications. 22 | It can result in displaying sensitive information from exceptions to end users. 23 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development 24 | and restarting the app. 25 |

26 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 8 | @section Scripts 9 | { 10 | 15 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "Providers": [ 4 | { 5 | "Name": "Console", 6 | "LogLevel": "1" 7 | }, 8 | { 9 | "Name": "File", 10 | "LogLevel": "2" 11 | } 12 | ], 13 | "MinimumLevel": 1 14 | }, 15 | "Email": { 16 | "MailType": "SMTP", 17 | "MailServer": "localhost", 18 | "MailPort": 25, 19 | "UseSSL": false, 20 | "UserId": "", 21 | "Password": "", 22 | "RemoteServerAPI": "", 23 | "RemoteServerKey": "" 24 | }, 25 | "AllowedHosts": "*" 26 | } 27 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js" 13 | ], 14 | "sourceMap": true, 15 | "includeInProject": true 16 | }, 17 | { 18 | "outputFileName": "wwwroot/js/site.min.js", 19 | "inputFiles": [ 20 | "wwwroot/js/site.js" 21 | ], 22 | "minify": { 23 | "enabled": true, 24 | "renameLocals": true 25 | }, 26 | "sourceMap": false 27 | } 28 | ] 29 | 30 | -------------------------------------------------------------------------------- /Chapter06/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter06/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.4.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter06/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter06/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter06/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.IntegrationTests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.IntegrationTests 6 | { 7 | public class IntegrationTests 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.IntegrationTests/TicTacToe.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.UnitTests/FileLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using TicTacToe.Logging; 7 | using Xunit; 8 | 9 | namespace TicTacToe.UnitTests 10 | { 11 | public class FileLoggerTests 12 | { 13 | [Fact] 14 | public void ShouldCreateALogFileAndAddEntry() 15 | { 16 | var fileLogger = new FileLogger( 17 | "Test", (category, level) => true, 18 | Path.Combine(Directory.GetCurrentDirectory(), "testlog.log")); 19 | var isEnabled = fileLogger.IsEnabled(LogLevel.Information); 20 | Assert.True(isEnabled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe.UnitTests/TicTacToe.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Areas/Account/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using TicTacToe.Services; 8 | 9 | namespace TicTacToe.Areas.Account.Controllers 10 | { 11 | [Area("Account")] 12 | public class HomeController : Controller 13 | { 14 | private IUserService _userService; 15 | public HomeController(IUserService userService) 16 | { 17 | _userService = userService; 18 | } 19 | public async Task Index() 20 | { 21 | var email = HttpContext.Session.GetString("email"); 22 | var user = await _userService.GetUserByEmail(email); 23 | return View(user); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Components/GameSessionViewComponent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Components 9 | { 10 | [ViewComponent(Name = "GameSession")] 11 | public class GameSessionViewComponent : ViewComponent 12 | { 13 | IGameSessionService _gameSessionService; 14 | public GameSessionViewComponent(IGameSessionService gameSessionService) 15 | { 16 | _gameSessionService = gameSessionService; 17 | } 18 | public async Task InvokeAsync(Guid gameSessionId) 19 | { 20 | var session = await _gameSessionService.GetGameSession(gameSessionId); 21 | return View(session); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Controllers/LeaderboardController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Controllers 9 | { 10 | public class LeaderboardController : Controller 11 | { 12 | private IUserService _userService; 13 | public LeaderboardController(IUserService userService) 14 | { 15 | _userService = userService; 16 | } 17 | public async Task Index() 18 | { 19 | var users = await _userService.GetTopUsers(10); 20 | return View(users); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Extensions/CollectionsExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Extensions 7 | { 8 | public static class CollectionsExtensionMethods 9 | { 10 | public static bool IsNullOrEmpty(this IEnumerable genericEnumerable) 11 | { 12 | return (genericEnumerable == null) || (!genericEnumerable.Any()); 13 | } 14 | 15 | public static bool IsNullOrEmpty(this ICollection genericCollection) 16 | { 17 | if (genericCollection == null) 18 | { 19 | return true; 20 | } 21 | return genericCollection.Count < 1; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/AccountModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class AccountModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/GameInvitationModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class GameInvitationModel 9 | { 10 | public Guid Id { get; set; } 11 | public string EmailTo { get; set; } 12 | public string InvitedBy { get; set; } 13 | public bool IsConfirmed { get; set; } 14 | public DateTime ConfirmationDate { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/GameSessionModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class GameSessionModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId1 { get; set; } 12 | public Guid UserId2 { get; set; } 13 | public UserModel User1 { get; set; } 14 | public UserModel User2 { get; set; } 15 | public IEnumerable Turns { get; set; } 16 | public UserModel Winner { get; set; } 17 | public UserModel ActiveUser { get; set; } 18 | public Guid WinnerId { get; set; } 19 | public Guid ActiveUserId { get; set; } 20 | public bool TurnFinished { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/InvitationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class InvitationEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public UserModel InvitedBy { get; set; } 12 | public DateTime InvitedDate { get; set; } 13 | public string ConfirmationUrl { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/TurnModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TurnModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId { get; set; } 12 | public UserModel User { get; set; } 13 | public int X { get; set; } 14 | public int Y { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Models/UserRegistrationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class UserRegistrationEmailModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | Scaffolding has generated all the files and added the required dependencies. 2 | 3 | However the Application's Startup code may required additional changes for things to work end to end. 4 | Add the following code to the Configure method in your Application's Startup class if not already done: 5 | 6 | app.UseMvc(routes => 7 | { 8 | routes.MapRoute( 9 | name : "areas", 10 | template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Services/IEmailTemplateRenderService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailTemplateRenderService 6 | { 7 | Task RenderTemplate(string templateName, T model, string host) where T : class; 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameInvitationService 8 | { 9 | Task Add(GameInvitationModel gameInvitationModel); 10 | Task Get(Guid id); 11 | Task Update(GameInvitationModel gameInvitationModel); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Services/IGameSessionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameSessionService 8 | { 9 | Task GetGameSession(Guid gameSessionId); 10 | Task CreateGameSession(Guid invitationId, string invitedByEmail, string invitedPlayerEmail); 11 | Task AddTurn(Guid id, string email, int x, int y); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IUserService 8 | { 9 | Task IsOnline(string name); 10 | Task RegisterUser(UserModel userModel); 11 | Task GetUserByEmail(string email); 12 | Task UpdateUser(UserModel user); 13 | Task> GetTopUsers(int numberOfUsers); 14 | } 15 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/ViewEngines/IEmailViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.ViewEngines 4 | { 5 | public interface IEmailViewEngine 6 | { 7 | Task RenderEmailToString(string viewName, TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/EmailTemplates/InvitationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.InvitationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | You have been invited by @($"{Model.InvitedBy.FirstName} { Model.InvitedBy.LastName} ") to play the Tic-Tac-Toe game. 8 | Please click here to join the game. -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/EmailTemplates/UserRegistrationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.UserRegistrationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | Thank you for registering on our website. Please click here to confirm your email. -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

7 | @Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 8 | Model.EmailTo] 9 |

10 | @section Desktop{

@Localizer["DesktopTitle"]

} 11 | @section Mobile {

@Localizer["MobileTitle"]

} 12 | @section Scripts{ 13 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |
5 |
6 | @*

Tic-Tac-Toe

*@ 7 | @section Desktop {

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 |
10 |

Tic-Tac-Toe is a two-player turn-based game.

11 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

12 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

13 |
14 |

Register by clicking here

15 |
16 |
17 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | @section Desktop{

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 10 | @section Scripts 11 | { 12 | 17 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | @addTagHelper *, TicTacToe 7 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | //Layout = "_Layout"; 3 | Layout = Convert.ToString(ViewData["Layout"]); 4 | } 5 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "Providers": [ 4 | { 5 | "Name": "Console", 6 | "LogLevel": "1" 7 | }, 8 | { 9 | "Name": "File", 10 | "LogLevel": "2" 11 | } 12 | ], 13 | "MinimumLevel": 1 14 | }, 15 | "Email": { 16 | "MailType": "SMTP", 17 | "MailServer": "localhost", 18 | "MailPort": 25, 19 | "UseSSL": false, 20 | "UserId": "", 21 | "Password": "", 22 | "RemoteServerAPI": "", 23 | "RemoteServerKey": "" 24 | }, 25 | "AllowedHosts": "*" 26 | } 27 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js" 13 | ], 14 | "sourceMap": true, 15 | "includeInProject": true 16 | }, 17 | { 18 | "outputFileName": "wwwroot/js/site.min.js", 19 | "inputFiles": [ 20 | "wwwroot/js/site.js" 21 | ], 22 | "minify": { 23 | "enabled": true, 24 | "renameLocals": true 25 | }, 26 | "sourceMap": false 27 | } 28 | ] 29 | 30 | -------------------------------------------------------------------------------- /Chapter07/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter07/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.4.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter07/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter07/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter07/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.IntegrationTests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.IntegrationTests 6 | { 7 | public class IntegrationTests 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.IntegrationTests/TicTacToe.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.UnitTests/FileLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using TicTacToe.Logging; 7 | using Xunit; 8 | 9 | namespace TicTacToe.UnitTests 10 | { 11 | public class FileLoggerTests 12 | { 13 | [Fact] 14 | public void ShouldCreateALogFileAndAddEntry() 15 | { 16 | var fileLogger = new FileLogger( 17 | "Test", (category, level) => true, 18 | Path.Combine(Directory.GetCurrentDirectory(), "testlog.log")); 19 | var isEnabled = fileLogger.IsEnabled(LogLevel.Information); 20 | Assert.True(isEnabled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe.UnitTests/TicTacToe.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Areas/Account/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using TicTacToe.Services; 8 | 9 | namespace TicTacToe.Areas.Account.Controllers 10 | { 11 | [Area("Account")] 12 | public class HomeController : Controller 13 | { 14 | private IUserService _userService; 15 | public HomeController(IUserService userService) 16 | { 17 | _userService = userService; 18 | } 19 | public async Task Index() 20 | { 21 | var email = HttpContext.Session.GetString("email"); 22 | var user = await _userService.GetUserByEmail(email); 23 | return View(user); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Components/GameSessionViewComponent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Components 9 | { 10 | [ViewComponent(Name = "GameSession")] 11 | public class GameSessionViewComponent : ViewComponent 12 | { 13 | IGameSessionService _gameSessionService; 14 | public GameSessionViewComponent(IGameSessionService gameSessionService) 15 | { 16 | _gameSessionService = gameSessionService; 17 | } 18 | public async Task InvokeAsync(Guid gameSessionId) 19 | { 20 | var session = await _gameSessionService.GetGameSession(gameSessionId); 21 | return View(session); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Controllers/LeaderboardController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Controllers 9 | { 10 | public class LeaderboardController : Controller 11 | { 12 | private IUserService _userService; 13 | public LeaderboardController(IUserService userService) 14 | { 15 | _userService = userService; 16 | } 17 | public async Task Index() 18 | { 19 | var users = await _userService.GetTopUsers(10); 20 | return View(users); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/AccountModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class AccountModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/GameInvitationModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class GameInvitationModel 9 | { 10 | public Guid Id { get; set; } 11 | public string EmailTo { get; set; } 12 | public string InvitedBy { get; set; } 13 | public bool IsConfirmed { get; set; } 14 | public DateTime ConfirmationDate { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/GameSessionModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class GameSessionModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId1 { get; set; } 12 | public Guid UserId2 { get; set; } 13 | public UserModel User1 { get; set; } 14 | public UserModel User2 { get; set; } 15 | public IEnumerable Turns { get; set; } 16 | public UserModel Winner { get; set; } 17 | public UserModel ActiveUser { get; set; } 18 | public Guid WinnerId { get; set; } 19 | public Guid ActiveUserId { get; set; } 20 | public bool TurnFinished { get; set; } 21 | public int TurnNumber { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/InvitationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class InvitationEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public UserModel InvitedBy { get; set; } 12 | public DateTime InvitedDate { get; set; } 13 | public string ConfirmationUrl { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/TurnModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TurnModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId { get; set; } 12 | public UserModel User { get; set; } 13 | public int X { get; set; } 14 | public int Y { get; set; } 15 | public string Email { get; set; } 16 | public string IconNumber { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Models/UserRegistrationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class UserRegistrationEmailModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | Scaffolding has generated all the files and added the required dependencies. 2 | 3 | However the Application's Startup code may required additional changes for things to work end to end. 4 | Add the following code to the Configure method in your Application's Startup class if not already done: 5 | 6 | app.UseMvc(routes => 7 | { 8 | routes.MapRoute( 9 | name : "areas", 10 | template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Services/IEmailTemplateRenderService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailTemplateRenderService 6 | { 7 | Task RenderTemplate(string templateName, T model, string host) where T : class; 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using TicTacToe.Models; 5 | 6 | namespace TicTacToe.Services 7 | { 8 | public interface IGameInvitationService 9 | { 10 | Task Add(GameInvitationModel gameInvitationModel); 11 | Task Get(Guid id); 12 | Task Update(GameInvitationModel gameInvitationModel); 13 | Task> All(); 14 | Task Delete(Guid id); 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Services/IGameSessionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameSessionService 8 | { 9 | Task GetGameSession(Guid gameSessionId); 10 | Task CreateGameSession(Guid invitationId, string invitedByEmail, string invitedPlayerEmail); 11 | Task AddTurn(Guid id, string email, int x, int y); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IUserService 8 | { 9 | Task IsOnline(string name); 10 | Task RegisterUser(UserModel userModel); 11 | Task GetUserByEmail(string email); 12 | Task UpdateUser(UserModel user); 13 | Task> GetTopUsers(int numberOfUsers); 14 | } 15 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/ViewEngines/IEmailViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.ViewEngines 4 | { 5 | public interface IEmailViewEngine 6 | { 7 | Task RenderEmailToString(string viewName, TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/EmailTemplates/InvitationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.InvitationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | You have been invited by @($"{Model.InvitedBy.FirstName} { Model.InvitedBy.LastName} ") to play the Tic-Tac-Toe game. 8 | Please click here to join the game. -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/EmailTemplates/UserRegistrationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.UserRegistrationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | Thank you for registering on our website. Please click here to confirm your email. -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

7 | @Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 8 | Model.EmailTo] 9 |

10 | @section Desktop{

@Localizer["DesktopTitle"]

} 11 | @section Mobile {

@Localizer["MobileTitle"]

} 12 | @section Scripts{ 13 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |
5 |
6 | @*

Tic-Tac-Toe

*@ 7 | @section Desktop {

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 |
10 |

Tic-Tac-Toe is a two-player turn-based game.

11 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

12 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

13 |
14 |

Register by clicking here

15 |
16 |
17 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | @section Desktop{

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 10 | @section Scripts 11 | { 12 | 17 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | @addTagHelper *, TicTacToe 7 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | //Layout = "_Layout"; 3 | Layout = Convert.ToString(ViewData["Layout"]); 4 | } 5 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "Providers": [ 4 | { 5 | "Name": "Console", 6 | "LogLevel": "1" 7 | }, 8 | { 9 | "Name": "File", 10 | "LogLevel": "2" 11 | } 12 | ], 13 | "MinimumLevel": 1 14 | }, 15 | "Email": { 16 | "MailType": "SMTP", 17 | "MailServer": "localhost", 18 | "MailPort": 25, 19 | "UseSSL": false, 20 | "UserId": "", 21 | "Password": "", 22 | "RemoteServerAPI": "", 23 | "RemoteServerKey": "" 24 | }, 25 | "AllowedHosts": "*" 26 | } 27 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js", 13 | "wwwroot/app/js/GameSession.js", 14 | "wwwroot/app/js/CheckTurnIsFinished.js", 15 | "wwwroot/app/js/CheckGameSessionIsFinished.js" 16 | ], 17 | "sourceMap": true, 18 | "includeInProject": true 19 | }, 20 | { 21 | "outputFileName": "wwwroot/js/site.min.js", 22 | "inputFiles": [ 23 | "wwwroot/js/site.js" 24 | ], 25 | "minify": { 26 | "enabled": true, 27 | "renameLocals": true 28 | }, 29 | "sourceMap": false 30 | } 31 | ] 32 | 33 | -------------------------------------------------------------------------------- /Chapter08/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter08/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.3.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/wwwroot/app/js/CheckGameSessionIsFinished.js: -------------------------------------------------------------------------------- 1 | function CheckGameSessionIsFinished() { 2 | var port = document.location.port ? (":" + document.location.port) : ""; 3 | var url = document.location.protocol + "//" + 4 | document.location.hostname + port + 5 | "/restapi/v1/CheckGameSessionIsFinished/" + window.GameSessionId; 6 | 7 | $.get(url, function (data) { 8 | debugger; 9 | if (data.indexOf("won") > 0 || data == "The game was a draw.") { 10 | alert(data); 11 | window.location.href = document.location.protocol + 12 | "//" + document.location.hostname + port; 13 | } 14 | }); 15 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | //alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | //alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | //alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | //alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter08/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter08/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter08/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.IntegrationTests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.IntegrationTests 6 | { 7 | public class IntegrationTests 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.IntegrationTests/TicTacToe.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.UnitTests/FileLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using TicTacToe.Logging; 7 | using Xunit; 8 | 9 | namespace TicTacToe.UnitTests 10 | { 11 | public class FileLoggerTests 12 | { 13 | [Fact] 14 | public void ShouldCreateALogFileAndAddEntry() 15 | { 16 | var fileLogger = new FileLogger( 17 | "Test", (category, level) => true, 18 | Path.Combine(Directory.GetCurrentDirectory(), "testlog.log")); 19 | var isEnabled = fileLogger.IsEnabled(LogLevel.Information); 20 | Assert.True(isEnabled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe.UnitTests/TicTacToe.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Areas/Account/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using TicTacToe.Services; 8 | 9 | namespace TicTacToe.Areas.Account.Controllers 10 | { 11 | [Area("Account")] 12 | public class HomeController : Controller 13 | { 14 | private IUserService _userService; 15 | public HomeController(IUserService userService) 16 | { 17 | _userService = userService; 18 | } 19 | public async Task Index() 20 | { 21 | var email = HttpContext.Session.GetString("email"); 22 | var user = await _userService.GetUserByEmail(email); 23 | return View(user); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Components/GameSessionViewComponent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Components 9 | { 10 | [ViewComponent(Name = "GameSession")] 11 | public class GameSessionViewComponent : ViewComponent 12 | { 13 | IGameSessionService _gameSessionService; 14 | public GameSessionViewComponent(IGameSessionService gameSessionService) 15 | { 16 | _gameSessionService = gameSessionService; 17 | } 18 | public async Task InvokeAsync(Guid gameSessionId) 19 | { 20 | var session = await _gameSessionService.GetGameSession(gameSessionId); 21 | return View(session); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Controllers/LeaderboardController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Controllers 9 | { 10 | public class LeaderboardController : Controller 11 | { 12 | private IUserService _userService; 13 | public LeaderboardController(IUserService userService) 14 | { 15 | _userService = userService; 16 | } 17 | public async Task Index() 18 | { 19 | var users = await _userService.GetTopUsers(10); 20 | return View(users); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Data/GameDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | 4 | namespace TicTacToe.Data 5 | { 6 | public class GameDbContextFactory : IDesignTimeDbContextFactory 7 | { 8 | public GameDbContext CreateDbContext(string[] args) 9 | { 10 | var optionsBuilder = 11 | new DbContextOptionsBuilder(); 12 | optionsBuilder.UseSqlServer(@"Server= (localdb)\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true"); 13 | return new GameDbContext(optionsBuilder.Options); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Extensions/ModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace TicTacToe.Extensions 10 | { 11 | public static class ModelBuilderExtensions 12 | { 13 | public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder) 14 | { 15 | foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes()) 16 | { 17 | entity.SetTableName(entity.DisplayName()); 18 | 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Models/AccountModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class AccountModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Models/GameInvitationModel.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.Threading.Tasks; 7 | 8 | namespace TicTacToe.Models 9 | { 10 | public class GameInvitationModel 11 | { 12 | [Key] 13 | public Guid Id { get; set; } 14 | public string EmailTo { get; set; } 15 | 16 | public string InvitedBy { get; set; } 17 | 18 | [ForeignKey(nameof(InvitedByUserId))] 19 | public UserModel InvitedByUser { get; set; } 20 | public Guid InvitedByUserId { get; set; } 21 | 22 | public bool IsConfirmed { get; set; } 23 | public DateTime ConfirmationDate { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Models/InvitationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class InvitationEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public UserModel InvitedBy { get; set; } 12 | public DateTime InvitedDate { get; set; } 13 | public string ConfirmationUrl { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Models/TurnModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TurnModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId { get; set; } 12 | public UserModel User { get; set; } 13 | public int X { get; set; } 14 | public int Y { get; set; } 15 | public string Email { get; set; } 16 | public string IconNumber { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Models/UserRegistrationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class UserRegistrationEmailModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | Scaffolding has generated all the files and added the required dependencies. 2 | 3 | However the Application's Startup code may required additional changes for things to work end to end. 4 | Add the following code to the Configure method in your Application's Startup class if not already done: 5 | 6 | app.UseMvc(routes => 7 | { 8 | routes.MapRoute( 9 | name : "areas", 10 | template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Services/IEmailTemplateRenderService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailTemplateRenderService 6 | { 7 | Task RenderTemplate(string templateName, T model, string host) where T : class; 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using TicTacToe.Models; 5 | 6 | namespace TicTacToe.Services 7 | { 8 | public interface IGameInvitationService 9 | { 10 | Task Add(GameInvitationModel gameInvitationModel); 11 | Task Get(Guid id); 12 | Task Update(GameInvitationModel gameInvitationModel); 13 | Task> All(); 14 | Task Delete(Guid id); 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Services/IGameSessionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameSessionService 8 | { 9 | Task GetGameSession(Guid gameSessionId); 10 | Task CreateGameSession(Guid invitationId, string invitedByEmail, string invitedPlayerEmail); 11 | Task AddTurn(Guid id, string email, int x, int y); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IUserService 8 | { 9 | Task IsOnline(string name); 10 | Task RegisterUser(UserModel userModel); 11 | Task GetUserByEmail(string email); 12 | Task UpdateUser(UserModel user); 13 | Task> GetTopUsers(int numberOfUsers); 14 | } 15 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/ViewEngines/IEmailViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.ViewEngines 4 | { 5 | public interface IEmailViewEngine 6 | { 7 | Task RenderEmailToString(string viewName, TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/EmailTemplates/InvitationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.InvitationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | You have been invited by @($"{Model.InvitedBy.FirstName} { Model.InvitedBy.LastName} ") to play the Tic-Tac-Toe game. 8 | Please click here to join the game. -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/EmailTemplates/UserRegistrationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.UserRegistrationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | Thank you for registering on our website. Please click here to confirm your email. -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

7 | @Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 8 | Model.EmailTo] 9 |

10 | @section Desktop{

@Localizer["DesktopTitle"]

} 11 | @section Mobile {

@Localizer["MobileTitle"]

} 12 | @section Scripts{ 13 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |
5 |
6 | @*

Tic-Tac-Toe

*@ 7 | @section Desktop {

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 |
10 |

Tic-Tac-Toe is a two-player turn-based game.

11 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

12 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

13 |
14 |

Register by clicking here

15 |
16 |
17 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | @section Desktop{

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 10 | @section Scripts 11 | { 12 | 17 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | @addTagHelper *, TicTacToe 7 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | //Layout = "_Layout"; 3 | Layout = Convert.ToString(ViewData["Layout"]); 4 | } 5 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "Providers": [ 4 | { 5 | "Name": "Console", 6 | "LogLevel": "1" 7 | }, 8 | { 9 | "Name": "File", 10 | "LogLevel": "2" 11 | } 12 | ], 13 | "MinimumLevel": 1 14 | }, 15 | "ConnectionStrings": { 16 | "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true" 17 | }, 18 | "Email": { 19 | "MailType": "SMTP", 20 | "MailServer": "localhost", 21 | "MailPort": 25, 22 | "UseSSL": false, 23 | "UserId": "", 24 | "Password": "", 25 | "RemoteServerAPI": "", 26 | "RemoteServerKey": "" 27 | }, 28 | "AllowedHosts": "*" 29 | } 30 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js", 13 | "wwwroot/app/js/GameSession.js", 14 | "wwwroot/app/js/CheckTurnIsFinished.js", 15 | "wwwroot/app/js/CheckGameSessionIsFinished.js" 16 | ], 17 | "sourceMap": true, 18 | "includeInProject": true 19 | }, 20 | { 21 | "outputFileName": "wwwroot/js/site.min.js", 22 | "inputFiles": [ 23 | "wwwroot/js/site.js" 24 | ], 25 | "minify": { 26 | "enabled": true, 27 | "renameLocals": true 28 | }, 29 | "sourceMap": false 30 | } 31 | ] 32 | 33 | -------------------------------------------------------------------------------- /Chapter09/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter09/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.4.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/wwwroot/app/js/CheckGameSessionIsFinished.js: -------------------------------------------------------------------------------- 1 | function CheckGameSessionIsFinished() { 2 | var port = document.location.port ? (":" + document.location.port) : ""; 3 | var url = document.location.protocol + "//" + 4 | document.location.hostname + port + 5 | "/restapi/v1/CheckGameSessionIsFinished/" + window.GameSessionId; 6 | 7 | $.get(url, function (data) { 8 | debugger; 9 | if (data.indexOf("won") > 0 || data == "The game was a draw.") { 10 | alert(data); 11 | window.location.href = document.location.protocol + 12 | "//" + document.location.hostname + port; 13 | } 14 | }); 15 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | //alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | //alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | //alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | //alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter09/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter09/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter09/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.IntegrationTests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.IntegrationTests 6 | { 7 | public class IntegrationTests 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.IntegrationTests/TicTacToe.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.UnitTests/FileLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using TicTacToe.Logging; 7 | using Xunit; 8 | 9 | namespace TicTacToe.UnitTests 10 | { 11 | public class FileLoggerTests 12 | { 13 | [Fact] 14 | public void ShouldCreateALogFileAndAddEntry() 15 | { 16 | var fileLogger = new FileLogger( 17 | "Test", (category, level) => true, 18 | Path.Combine(Directory.GetCurrentDirectory(), "testlog.log")); 19 | var isEnabled = fileLogger.IsEnabled(LogLevel.Information); 20 | Assert.True(isEnabled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe.UnitTests/TicTacToe.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Areas/Account/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using TicTacToe.Services; 8 | 9 | namespace TicTacToe.Areas.Account.Controllers 10 | { 11 | [Area("Account")] 12 | public class HomeController : Controller 13 | { 14 | private IUserService _userService; 15 | public HomeController(IUserService userService) 16 | { 17 | _userService = userService; 18 | } 19 | public async Task Index() 20 | { 21 | var email = HttpContext.Session.GetString("email"); 22 | var user = await _userService.GetUserByEmail(email); 23 | return View(user); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Components/GameSessionViewComponent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Components 9 | { 10 | [ViewComponent(Name = "GameSession")] 11 | public class GameSessionViewComponent : ViewComponent 12 | { 13 | IGameSessionService _gameSessionService; 14 | public GameSessionViewComponent(IGameSessionService gameSessionService) 15 | { 16 | _gameSessionService = gameSessionService; 17 | } 18 | public async Task InvokeAsync(Guid gameSessionId) 19 | { 20 | var session = await _gameSessionService.GetGameSession(gameSessionId); 21 | return View(session); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace TicTacToe.Controllers 8 | { 9 | public class AccountController : Controller 10 | { 11 | public IActionResult Index() 12 | { 13 | return View(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Controllers/LeaderboardController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Controllers 9 | { 10 | public class LeaderboardController : Controller 11 | { 12 | private IUserService _userService; 13 | public LeaderboardController(IUserService userService) 14 | { 15 | _userService = userService; 16 | } 17 | public async Task Index() 18 | { 19 | var users = await _userService.GetTopUsers(10); 20 | return View(users); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Data/GameDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace TicTacToe.Data 9 | { 10 | public class GameDbContextFactory : IDesignTimeDbContextFactory 11 | { 12 | public GameDbContext CreateDbContext(string[] args) 13 | { 14 | var optionsBuilder = 15 | new DbContextOptionsBuilder(); 16 | optionsBuilder.UseSqlServer(@"Server= (localdb)\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true"); 17 | return new GameDbContext(optionsBuilder.Options); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Extensions/ModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace TicTacToe.Extensions 10 | { 11 | public static class ModelBuilderExtensions 12 | { 13 | public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder) 14 | { 15 | foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes()) 16 | { 17 | 18 | entity.SetTableName(entity.DisplayName()); 19 | } 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/AccountModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class AccountModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/GameInvitationModel.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.Threading.Tasks; 7 | 8 | namespace TicTacToe.Models 9 | { 10 | public class GameInvitationModel 11 | { 12 | [Key] 13 | public Guid Id { get; set; } 14 | public string EmailTo { get; set; } 15 | 16 | public string InvitedBy { get; set; } 17 | 18 | [ForeignKey(nameof(InvitedByUserId))] 19 | public UserModel InvitedByUser { get; set; } 20 | public Guid InvitedByUserId { get; set; } 21 | 22 | public bool IsConfirmed { get; set; } 23 | public DateTime ConfirmationDate { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/InvitationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class InvitationEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public UserModel InvitedBy { get; set; } 12 | public DateTime InvitedDate { get; set; } 13 | public string ConfirmationUrl { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/LoginModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class LoginModel 10 | { 11 | [Required] 12 | public string UserName { get; set; } 13 | [Required] 14 | public string Password { get; set; } 15 | public string ReturnUrl { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/RoleModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class RoleModel : IdentityRole 10 | { 11 | public RoleModel() 12 | { 13 | } 14 | 15 | public RoleModel(string roleName) : base(roleName) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/TurnModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TurnModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId { get; set; } 12 | public UserModel User { get; set; } 13 | public int X { get; set; } 14 | public int Y { get; set; } 15 | public string Email { get; set; } 16 | public string IconNumber { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Models/UserRegistrationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class UserRegistrationEmailModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | Scaffolding has generated all the files and added the required dependencies. 2 | 3 | However the Application's Startup code may required additional changes for things to work end to end. 4 | Add the following code to the Configure method in your Application's Startup class if not already done: 5 | 6 | app.UseMvc(routes => 7 | { 8 | routes.MapRoute( 9 | name : "areas", 10 | template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Services/IEmailTemplateRenderService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailTemplateRenderService 6 | { 7 | Task RenderTemplate(string templateName, T model, string host) where T : class; 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using TicTacToe.Models; 5 | 6 | namespace TicTacToe.Services 7 | { 8 | public interface IGameInvitationService 9 | { 10 | Task Add(GameInvitationModel gameInvitationModel); 11 | Task Get(Guid id); 12 | Task Update(GameInvitationModel gameInvitationModel); 13 | Task> All(); 14 | Task Delete(Guid id); 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Services/IGameSessionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameSessionService 8 | { 9 | Task GetGameSession(Guid gameSessionId); 10 | Task CreateGameSession(Guid invitationId, UserModel invitedByEmail, UserModel invitedPlayerEmail); 11 | Task AddTurn(Guid id, UserModel user, int x, int y); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IUserService 8 | { 9 | Task IsOnline(string name); 10 | Task RegisterUser(UserModel userModel); 11 | Task GetUserByEmail(string email); 12 | Task UpdateUser(UserModel user); 13 | Task> GetTopUsers(int numberOfUsers); 14 | Task ConfirmEmail(string email, string code); 15 | Task GetEmailConfirmationCode(UserModel user); 16 | } 17 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/ViewEngines/IEmailViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.ViewEngines 4 | { 5 | public interface IEmailViewEngine 6 | { 7 | Task RenderEmailToString(string viewName, TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/EmailTemplates/InvitationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.InvitationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | You have been invited by @($"{Model.InvitedBy.FirstName} { Model.InvitedBy.LastName} ") to play the Tic-Tac-Toe game. 8 | Please click here to join the game. -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/EmailTemplates/UserRegistrationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.UserRegistrationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | Thank you for registering on our website. Please click here to confirm your email. -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

7 | @Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 8 | Model.EmailTo] 9 |

10 | @section Desktop{

@Localizer["DesktopTitle"]

} 11 | @section Mobile {

@Localizer["MobileTitle"]

} 12 | @section Scripts{ 13 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |
5 |
6 | @*

Tic-Tac-Toe

*@ 7 | @section Desktop {

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 |
10 |

Tic-Tac-Toe is a two-player turn-based game.

11 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

12 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

13 |
14 |

Register by clicking here

15 |
16 |
17 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | @section Desktop{

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 10 | @section Scripts 11 | { 12 | 17 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | @addTagHelper *, TicTacToe 7 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | //Layout = "_Layout"; 3 | Layout = Convert.ToString(ViewData["Layout"]); 4 | } 5 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "Providers": [ 4 | { 5 | "Name": "Console", 6 | "LogLevel": "1" 7 | }, 8 | { 9 | "Name": "File", 10 | "LogLevel": "2" 11 | } 12 | ], 13 | "MinimumLevel": 1 14 | }, 15 | "ConnectionStrings": { 16 | "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true" 17 | }, 18 | "Email": { 19 | "MailType": "SMTP", 20 | "MailServer": "localhost", 21 | "MailPort": 25, 22 | "UseSSL": false, 23 | "UserId": "", 24 | "Password": "", 25 | "RemoteServerAPI": "", 26 | "RemoteServerKey": "" 27 | }, 28 | "AllowedHosts": "*" 29 | } 30 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js", 13 | "wwwroot/app/js/GameSession.js", 14 | "wwwroot/app/js/CheckTurnIsFinished.js", 15 | "wwwroot/app/js/CheckGameSessionIsFinished.js" 16 | ], 17 | "sourceMap": true, 18 | "includeInProject": true 19 | }, 20 | { 21 | "outputFileName": "wwwroot/js/site.min.js", 22 | "inputFiles": [ 23 | "wwwroot/js/site.js" 24 | ], 25 | "minify": { 26 | "enabled": true, 27 | "renameLocals": true 28 | }, 29 | "sourceMap": false 30 | } 31 | ] 32 | 33 | -------------------------------------------------------------------------------- /Chapter10/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter10/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.3.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/wwwroot/app/js/CheckGameSessionIsFinished.js: -------------------------------------------------------------------------------- 1 | function CheckGameSessionIsFinished() { 2 | var port = document.location.port ? (":" + document.location.port) : ""; 3 | var url = document.location.protocol + "//" + 4 | document.location.hostname + port + 5 | "/restapi/v1/CheckGameSessionIsFinished/" + window.GameSessionId; 6 | 7 | $.get(url, function (data) { 8 | debugger; 9 | if (data.indexOf("won") > 0 || data == "The game was a draw.") { 10 | alert(data); 11 | window.location.href = document.location.protocol + 12 | "//" + document.location.hostname + port; 13 | } 14 | }); 15 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | //alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | //alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | //alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | //alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter10/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter10/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter10/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.IntegrationTests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.IntegrationTests 6 | { 7 | public class IntegrationTests 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.IntegrationTests/TicTacToe.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.UnitTests/FileLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using TicTacToe.Logging; 7 | using Xunit; 8 | 9 | namespace TicTacToe.UnitTests 10 | { 11 | public class FileLoggerTests 12 | { 13 | [Fact] 14 | public void ShouldCreateALogFileAndAddEntry() 15 | { 16 | var fileLogger = new FileLogger( 17 | "Test", (category, level) => true, 18 | Path.Combine(Directory.GetCurrentDirectory(), "testlog.log")); 19 | var isEnabled = fileLogger.IsEnabled(LogLevel.Information); 20 | Assert.True(isEnabled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe.UnitTests/TicTacToe.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Areas/Account/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.Mvc; 7 | using TicTacToe.Services; 8 | 9 | namespace TicTacToe.Areas.Account.Controllers 10 | { 11 | [Area("Account")] 12 | public class HomeController : Controller 13 | { 14 | private IUserService _userService; 15 | public HomeController(IUserService userService) 16 | { 17 | _userService = userService; 18 | } 19 | public async Task Index() 20 | { 21 | var email = HttpContext.Session.GetString("email"); 22 | var user = await _userService.GetUserByEmail(email); 23 | return View(user); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Components/GameSessionViewComponent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Components 9 | { 10 | [ViewComponent(Name = "GameSession")] 11 | public class GameSessionViewComponent : ViewComponent 12 | { 13 | IGameSessionService _gameSessionService; 14 | public GameSessionViewComponent(IGameSessionService gameSessionService) 15 | { 16 | _gameSessionService = gameSessionService; 17 | } 18 | public async Task InvokeAsync(Guid gameSessionId) 19 | { 20 | var session = await _gameSessionService.GetGameSession(gameSessionId); 21 | return View(session); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Controllers/AccountController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace TicTacToe.Controllers 8 | { 9 | public class AccountController : Controller 10 | { 11 | public IActionResult Index() 12 | { 13 | return View(); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Controllers/LeaderboardController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Controllers 9 | { 10 | public class LeaderboardController : Controller 11 | { 12 | private IUserService _userService; 13 | public LeaderboardController(IUserService userService) 14 | { 15 | _userService = userService; 16 | } 17 | public async Task Index() 18 | { 19 | var users = await _userService.GetTopUsers(10); 20 | return View(users); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Data/GameDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace TicTacToe.Data 9 | { 10 | public class GameDbContextFactory : IDesignTimeDbContextFactory 11 | { 12 | public GameDbContext CreateDbContext(string[] args) 13 | { 14 | var optionsBuilder = 15 | new DbContextOptionsBuilder(); 16 | optionsBuilder.UseSqlServer(@"Server= (localdb)\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true"); 17 | return new GameDbContext(optionsBuilder.Options); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Extensions/ModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace TicTacToe.Extensions 10 | { 11 | public static class ModelBuilderExtensions 12 | { 13 | //public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder) 14 | //{ 15 | // //foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes()) 16 | // //{ 17 | // // //entity.Relational().TableName = entity.DisplayName(); 18 | 19 | // //} 20 | //} 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/AccountModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class AccountModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/GameInvitationModel.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.Threading.Tasks; 7 | 8 | namespace TicTacToe.Models 9 | { 10 | public class GameInvitationModel 11 | { 12 | [Key] 13 | public Guid Id { get; set; } 14 | public string EmailTo { get; set; } 15 | 16 | public string InvitedBy { get; set; } 17 | 18 | [ForeignKey(nameof(InvitedByUserId))] 19 | public UserModel InvitedByUser { get; set; } 20 | public Guid InvitedByUserId { get; set; } 21 | 22 | public bool IsConfirmed { get; set; } 23 | public DateTime ConfirmationDate { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/InvitationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class InvitationEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public UserModel InvitedBy { get; set; } 12 | public DateTime InvitedDate { get; set; } 13 | public string ConfirmationUrl { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/LoginModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class LoginModel 10 | { 11 | [Required] 12 | public string UserName { get; set; } 13 | [Required] 14 | public string Password { get; set; } 15 | public string ReturnUrl { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/RoleModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class RoleModel : IdentityRole 10 | { 11 | public RoleModel() 12 | { 13 | } 14 | 15 | public RoleModel(string roleName) : base(roleName) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/TurnModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TurnModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId { get; set; } 12 | public UserModel User { get; set; } 13 | public int X { get; set; } 14 | public int Y { get; set; } 15 | public string Email { get; set; } 16 | public string IconNumber { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Models/UserRegistrationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class UserRegistrationEmailModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | Scaffolding has generated all the files and added the required dependencies. 2 | 3 | However the Application's Startup code may required additional changes for things to work end to end. 4 | Add the following code to the Configure method in your Application's Startup class if not already done: 5 | 6 | app.UseMvc(routes => 7 | { 8 | routes.MapRoute( 9 | name : "areas", 10 | template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Services/IEmailTemplateRenderService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailTemplateRenderService 6 | { 7 | Task RenderTemplate(string templateName, T model, string host) where T : class; 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using TicTacToe.Models; 5 | 6 | namespace TicTacToe.Services 7 | { 8 | public interface IGameInvitationService 9 | { 10 | Task Add(GameInvitationModel gameInvitationModel); 11 | Task Get(Guid id); 12 | Task Update(GameInvitationModel gameInvitationModel); 13 | Task> All(); 14 | Task Delete(Guid id); 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Services/IGameSessionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameSessionService 8 | { 9 | Task GetGameSession(Guid gameSessionId); 10 | Task CreateGameSession(Guid invitationId, UserModel invitedByEmail, UserModel invitedPlayerEmail); 11 | Task AddTurn(Guid id, UserModel user, int x, int y); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Services/IUserService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IUserService 8 | { 9 | Task IsOnline(string name); 10 | Task RegisterUser(UserModel userModel); 11 | Task GetUserByEmail(string email); 12 | Task UpdateUser(UserModel user); 13 | Task> GetTopUsers(int numberOfUsers); 14 | Task ConfirmEmail(string email, string code); 15 | Task GetEmailConfirmationCode(UserModel user); 16 | } 17 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/ViewEngines/IEmailViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.ViewEngines 4 | { 5 | public interface IEmailViewEngine 6 | { 7 | Task RenderEmailToString(string viewName, TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/EmailTemplates/InvitationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.InvitationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | You have been invited by @($"{Model.InvitedBy.FirstName} { Model.InvitedBy.LastName} ") to play the Tic-Tac-Toe game. 8 | Please click here to join the game. -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/EmailTemplates/UserRegistrationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.UserRegistrationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | Thank you for registering on our website. Please click here to confirm your email. -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

7 | @Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 8 | Model.EmailTo] 9 |

10 | @section Desktop{

@Localizer["DesktopTitle"]

} 11 | @section Mobile {

@Localizer["MobileTitle"]

} 12 | @section Scripts{ 13 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |
5 |
6 | @*

Tic-Tac-Toe

*@ 7 | @section Desktop {

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 |
10 |

Tic-Tac-Toe is a two-player turn-based game.

11 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

12 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

13 |
14 |

Register by clicking here

15 |
16 |
17 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | @section Desktop{

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 10 | @section Scripts 11 | { 12 | 17 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | @addTagHelper *, TicTacToe 7 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | //Layout = "_Layout"; 3 | Layout = Convert.ToString(ViewData["Layout"]); 4 | } 5 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "Providers": [ 4 | { 5 | "Name": "Console", 6 | "LogLevel": "1" 7 | }, 8 | { 9 | "Name": "File", 10 | "LogLevel": "2" 11 | } 12 | ], 13 | "MinimumLevel": 1 14 | }, 15 | "ConnectionStrings": { 16 | "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true" 17 | }, 18 | "Email": { 19 | "MailType": "SMTP", 20 | "MailServer": "localhost", 21 | "MailPort": 25, 22 | "UseSSL": false, 23 | "UserId": "", 24 | "Password": "", 25 | "RemoteServerAPI": "", 26 | "RemoteServerKey": "" 27 | }, 28 | "AllowedHosts": "*" 29 | } 30 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js", 13 | "wwwroot/app/js/GameSession.js", 14 | "wwwroot/app/js/CheckTurnIsFinished.js", 15 | "wwwroot/app/js/CheckGameSessionIsFinished.js" 16 | ], 17 | "sourceMap": true, 18 | "includeInProject": true 19 | }, 20 | { 21 | "outputFileName": "wwwroot/js/site.min.js", 22 | "inputFiles": [ 23 | "wwwroot/js/site.js" 24 | ], 25 | "minify": { 26 | "enabled": true, 27 | "renameLocals": true 28 | }, 29 | "sourceMap": false 30 | } 31 | ] 32 | 33 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter12/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.3.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 8 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter12/TicTacToe/wwwroot/app/js/CheckGameSessionIsFinished.js: -------------------------------------------------------------------------------- 1 | function CheckGameSessionIsFinished() { 2 | var port = document.location.port ? (":" + document.location.port) : ""; 3 | var url = document.location.protocol + "//" + 4 | document.location.hostname + port + 5 | "/restapi/v1/CheckGameSessionIsFinished/" + window.GameSessionId; 6 | 7 | $.get(url, function (data) { 8 | debugger; 9 | if (data.indexOf("won") > 0 || data == "The game was a draw.") { 10 | alert(data); 11 | window.location.href = document.location.protocol + 12 | "//" + document.location.hostname + port; 13 | } 14 | }); 15 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | //alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | //alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | //alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | //alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter12/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter12/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter12/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.IntegrationTests/IntegrationTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.IntegrationTests 6 | { 7 | public class IntegrationTests 8 | { 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.IntegrationTests/TicTacToe.IntegrationTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.Logging/FileLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace TicTacToe.Logging 7 | { 8 | public class FileLoggerProvider : ILoggerProvider 9 | { 10 | private readonly Func _filter; 11 | private string _fileName; 12 | 13 | public FileLoggerProvider(Func filter, string fileName) 14 | { 15 | _filter = filter; 16 | _fileName = fileName; 17 | } 18 | 19 | public ILogger CreateLogger(string categoryName) 20 | { 21 | return new FileLogger(categoryName, _filter, _fileName); 22 | } 23 | 24 | public void Dispose() { } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.Logging/LogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TicTacToe.Logging 6 | { 7 | public class LogEntry 8 | { 9 | public int EventId { get; internal set; } 10 | public string Message { get; internal set; } 11 | public string LogLevel { get; internal set; } 12 | public DateTime CreatedTime { get; internal set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.Logging/TicTacToe.Logging.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.UnitTests/FileLoggerTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using TicTacToe.Logging; 7 | using Xunit; 8 | 9 | namespace TicTacToe.UnitTests 10 | { 11 | public class FileLoggerTests 12 | { 13 | [Fact] 14 | public void ShouldCreateALogFileAndAddEntry() 15 | { 16 | var fileLogger = new FileLogger( 17 | "Test", (category, level) => true, 18 | Path.Combine(Directory.GetCurrentDirectory(), "testlog.log")); 19 | var isEnabled = fileLogger.IsEnabled(LogLevel.Information); 20 | Assert.True(isEnabled); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe.UnitTests/TicTacToe.UnitTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Areas/Account/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using Microsoft.AspNetCore.Mvc.Localization 3 | @inject IViewLocalizer Localizer 4 | @addTagHelper *, TicTacToe 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Components/GameSessionViewComponent.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Components 9 | { 10 | [ViewComponent(Name = "GameSession")] 11 | public class GameSessionViewComponent : ViewComponent 12 | { 13 | IGameSessionService _gameSessionService; 14 | public GameSessionViewComponent(IGameSessionService gameSessionService) 15 | { 16 | _gameSessionService = gameSessionService; 17 | } 18 | public async Task InvokeAsync(Guid gameSessionId) 19 | { 20 | var session = await _gameSessionService.GetGameSession(gameSessionId); 21 | return View(session); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Controllers/LeaderboardController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using TicTacToe.Services; 7 | 8 | namespace TicTacToe.Controllers 9 | { 10 | public class LeaderboardController : Controller 11 | { 12 | private IUserService _userService; 13 | public LeaderboardController(IUserService userService) 14 | { 15 | _userService = userService; 16 | } 17 | public async Task Index() 18 | { 19 | var users = await _userService.GetTopUsers(10); 20 | return View(users); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Data/GameDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Design; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace TicTacToe.Data 9 | { 10 | public class GameDbContextFactory : IDesignTimeDbContextFactory 11 | { 12 | public GameDbContext CreateDbContext(string[] args) 13 | { 14 | var optionsBuilder = 15 | new DbContextOptionsBuilder(); 16 | optionsBuilder.UseSqlServer(@"Server= (localdb)\MSSQLLocalDB;Database=TicTacToe; Trusted_Connection=True;MultipleActiveResultSets=true"); 17 | return new GameDbContext(optionsBuilder.Options); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Extensions/CommunicationMiddlewareExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using TicTacToe.Middlewares; 7 | 8 | namespace TicTacToe.Extensions 9 | { 10 | public static class CommunicationMiddlewareExtension 11 | { 12 | public static IApplicationBuilder 13 | UseCommunicationMiddleware(this IApplicationBuilder app) 14 | { 15 | return app.UseMiddleware(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Extensions/ModelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore.Metadata; 3 | using Microsoft.EntityFrameworkCore.Metadata.Internal; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | namespace TicTacToe.Extensions 10 | { 11 | public static class ModelBuilderExtensions 12 | { 13 | public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder) 14 | { 15 | foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes()) 16 | { 17 | //entity.Relational().TableName = entity.DisplayName(); 18 | entity.SetTableName(entity.DisplayName()); 19 | } 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/AccountModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class AccountModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TicTacToe.Models 4 | { 5 | public class ErrorViewModel 6 | { 7 | public string RequestId { get; set; } 8 | 9 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 10 | } 11 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/GameInvitationModel.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.Threading.Tasks; 7 | 8 | namespace TicTacToe.Models 9 | { 10 | public class GameInvitationModel 11 | { 12 | [Key] 13 | public Guid Id { get; set; } 14 | public string EmailTo { get; set; } 15 | 16 | public string InvitedBy { get; set; } 17 | 18 | [ForeignKey(nameof(InvitedByUserId))] 19 | public UserModel InvitedByUser { get; set; } 20 | public Guid InvitedByUserId { get; set; } 21 | 22 | public bool IsConfirmed { get; set; } 23 | public DateTime ConfirmationDate { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/InvitationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class InvitationEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public UserModel InvitedBy { get; set; } 12 | public DateTime InvitedDate { get; set; } 13 | public string ConfirmationUrl { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/LoginModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class LoginModel 10 | { 11 | [Required] 12 | public string UserName { get; set; } 13 | [Required] 14 | public string Password { get; set; } 15 | public string ReturnUrl { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/ResetPasswordEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class ResetPasswordEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public string Email { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/ResetPasswordModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class ResetPasswordModel 9 | { 10 | public string Token { get; set; } 11 | public string UserName { get; set; } 12 | public string Password { get; set; } 13 | public string ConfirmPassword { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/RoleModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace TicTacToe.Models 8 | { 9 | public class RoleModel : IdentityRole 10 | { 11 | public RoleModel() 12 | { 13 | } 14 | 15 | public RoleModel(string roleName) : base(roleName) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/TurnModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TurnModel 9 | { 10 | public Guid Id { get; set; } 11 | public Guid UserId { get; set; } 12 | public UserModel User { get; set; } 13 | public int X { get; set; } 14 | public int Y { get; set; } 15 | public string Email { get; set; } 16 | public string IconNumber { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/TwoFactorCodeModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.ComponentModel.DataAnnotations; 6 | using System.ComponentModel.DataAnnotations.Schema; 7 | 8 | namespace TicTacToe.Models 9 | { 10 | public class TwoFactorCodeModel 11 | { 12 | 13 | [Key] 14 | public long Id { get; set; } 15 | public Guid UserId { get; set; } 16 | [ForeignKey("UserId")] 17 | public UserModel User { get; set; } 18 | public string TokenProvider { get; set; } 19 | public string TokenCode { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/TwoFactorEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class TwoFactorEmailModel 9 | { 10 | public string DisplayName { get; set; } 11 | public string Email { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/UserRegistrationEmailModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class UserRegistrationEmailModel 9 | { 10 | public string Email { get; set; } 11 | public string DisplayName { get; set; } 12 | public string ActionUrl { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/UserRoleModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace TicTacToe.Models 9 | { 10 | public class UserRoleModel : IdentityUserRole 11 | { 12 | [Key] 13 | public long Id { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Models/ValidateTwoFactorModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Models 7 | { 8 | public class ValidateTwoFactorModel 9 | { 10 | public string UserName { get; set; } 11 | public string Code { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Monitoring/ApplicationDiagnosticListener.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using Microsoft.Extensions.DiagnosticAdapter; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | 9 | namespace TicTacToe.Monitoring 10 | { 11 | public class ApplicationDiagnosticListener 12 | { 13 | [DiagnosticName("TicTacToe.MiddlewareStarting")] 14 | public virtual void OnMiddlewareStarting(HttpContext httpContext) 15 | { 16 | Console.WriteLine($"TicTacToe Middleware Starting, path: {httpContext.Request.Path}"); 17 | } 18 | 19 | [DiagnosticName("TicTacToe.NewUserRegistration")] 20 | public virtual void NewUserRegistration(string name) 21 | { 22 | Console.WriteLine($"New User Registration {name}"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Options/LoggingOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingOptions 9 | { 10 | public LoggingProviderOption[] Providers { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Options/LoggingProviderOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class LoggingProviderOption 9 | { 10 | public string Name { get; set; } 11 | public string Parameters { get; set; } 12 | public int LogLevel { get; set; } 13 | } 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Options/MonitoringOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Options 7 | { 8 | public class MonitoringOptions 9 | { 10 | public string MonitoringType { get; set; } 11 | public string MonitoringSettings { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/ScaffoldingReadMe.txt: -------------------------------------------------------------------------------- 1 | Scaffolding has generated all the files and added the required dependencies. 2 | 3 | However the Application's Startup code may required additional changes for things to work end to end. 4 | Add the following code to the Configure method in your Application's Startup class if not already done: 5 | 6 | app.UseMvc(routes => 7 | { 8 | routes.MapRoute( 9 | name : "areas", 10 | template : "{area:exists}/{controller=Home}/{action=Index}/{id?}" 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Services/IEmailService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailService 6 | { 7 | Task SendEmail(string emailTo, string subject, string message); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Services/IEmailTemplateRenderService.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.Services 4 | { 5 | public interface IEmailTemplateRenderService 6 | { 7 | Task RenderTemplate(string templateName, T model, string host) where T : class; 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Services/IGameInvitationService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using TicTacToe.Models; 5 | 6 | namespace TicTacToe.Services 7 | { 8 | public interface IGameInvitationService 9 | { 10 | Task Add(GameInvitationModel gameInvitationModel); 11 | Task Get(Guid id); 12 | Task Update(GameInvitationModel gameInvitationModel); 13 | Task> All(); 14 | Task Delete(Guid id); 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Services/IGameSessionService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using TicTacToe.Models; 4 | 5 | namespace TicTacToe.Services 6 | { 7 | public interface IGameSessionService 8 | { 9 | Task GetGameSession(Guid gameSessionId); 10 | Task CreateGameSession(Guid invitationId, UserModel invitedByEmail, UserModel invitedPlayerEmail); 11 | Task AddTurn(Guid id, UserModel user, int x, int y); 12 | } 13 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Services/IMonitoringService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace TicTacToe.Services 7 | { 8 | public interface IMonitoringService 9 | { 10 | void TrackEvent(string eventName, TimeSpan elapsed, IDictionary properties = null); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/ViewEngines/IEmailViewEngine.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace TicTacToe.ViewEngines 4 | { 5 | public interface IEmailViewEngine 6 | { 7 | Task RenderEmailToString(string viewName, TModel model); 8 | } 9 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/EmailTemplates/InvitationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.InvitationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | You have been invited by @($"{Model.InvitedBy.FirstName} { Model.InvitedBy.LastName} ") to play the Tic-Tac-Toe game. 8 | Please click here to join the game. -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/EmailTemplates/UserRegistrationEmail.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.UserRegistrationEmailModel 2 | @{ 3 | ViewData["Title"] = "View"; 4 | Layout = "_LayoutEmail"; 5 | } 6 |

Welcome @Model.DisplayName

7 | Thank you for registering on our website. Please click here to confirm your email. -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/GameInvitation/GameInvitationConfirmation.cshtml: -------------------------------------------------------------------------------- 1 | @model TicTacToe.Models.GameInvitationModel 2 | @{ 3 | ViewData["Title"] = "GameInvitationConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

7 | @Localizer["You have invited {0} to play a Tic-Tac-Toe game with you, please wait until the user is connected", 8 | Model.EmailTo] 9 |

10 | @section Desktop{

@Localizer["DesktopTitle"]

} 11 | @section Mobile {

@Localizer["MobileTitle"]

} 12 | @section Scripts{ 13 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Home Page"; 3 | } 4 |
5 |
6 | @*

Tic-Tac-Toe

*@ 7 | @section Desktop {

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 |
10 |

Tic-Tac-Toe is a two-player turn-based game.

11 |

Two players will choose who takes the Xs and who takes the Os. They will then be taking turns and mark spaces in a 3×3 grid by putting their marks, one mark per turn.

12 |

A player who succeeds in placing three of his arks in a horizontal, vertical, or diagonal row wins the game.

13 |
14 |

Register by clicking here

15 |
16 |
17 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/Home/Privacy.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Privacy Policy"; 3 | } 4 |

@ViewData["Title"]

5 | 6 |

Use this page to detail your site's privacy policy.

7 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/UserRegistration/EmailConfirmation.cshtml: -------------------------------------------------------------------------------- 1 |  2 | @{ 3 | ViewData["Title"] = "EmailConfirmation"; 4 | Layout = "~/Views/Shared/_Layout.cshtml"; 5 | } 6 |

EmailConfirmation

7 | @section Desktop{

@Localizer["DesktopTitle"]

} 8 | @section Mobile {

@Localizer["MobileTitle"]

} 9 | An email has been sent to @ViewBag.Email, please confirm your email address by clicking on the provided link. 10 | @section Scripts 11 | { 12 | 17 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using TicTacToe 2 | @using TicTacToe.Models 3 | @using Microsoft.AspNetCore.Mvc.Localization 4 | @inject IViewLocalizer Localizer 5 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 6 | @addTagHelper *, TicTacToe 7 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | //Layout = "_Layout"; 3 | Layout = Convert.ToString(ViewData["Layout"]); 4 | } 5 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/bundleconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFileName": "wwwroot/css/site.min.css", 4 | "inputFiles": [ 5 | "wwwroot/css/site.css" 6 | ] 7 | }, 8 | { 9 | "outputFileName": "wwwroot/js/site.js", 10 | "inputFiles": [ 11 | "wwwroot/app/js/scripts1.js", 12 | "wwwroot/app/js/scripts2.js", 13 | "wwwroot/app/js/GameSession.js", 14 | "wwwroot/app/js/CheckTurnIsFinished.js", 15 | "wwwroot/app/js/CheckGameSessionIsFinished.js" 16 | ], 17 | "sourceMap": true, 18 | "includeInProject": true 19 | }, 20 | { 21 | "outputFileName": "wwwroot/js/site.min.js", 22 | "inputFiles": [ 23 | "wwwroot/js/site.js" 24 | ], 25 | "minify": { 26 | "enabled": true, 27 | "renameLocals": true 28 | }, 29 | "sourceMap": false 30 | } 31 | ] 32 | 33 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/bundleconfig.json.bindings: -------------------------------------------------------------------------------- 1 | /// -------------------------------------------------------------------------------- /Chapter13/TicTacToe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "asp.net", 4 | "private": true, 5 | "devDependencies": { 6 | "bootstrap": "4.3.1", 7 | "jquery": "3.4.1", 8 | "jquery-validation": "1.17.0", 9 | "jquery-validation-unobtrusive": "3.2.11", 10 | "popper.js": "1.14.7" 11 | } 12 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Chapter13/TicTacToe/wwwroot/app/js/CheckGameSessionIsFinished.js: -------------------------------------------------------------------------------- 1 | function CheckGameSessionIsFinished() { 2 | var port = document.location.port ? (":" + document.location.port) : ""; 3 | var url = document.location.protocol + "//" + 4 | document.location.hostname + port + 5 | "/restapi/v1/CheckGameSessionIsFinished/" + window.GameSessionId; 6 | 7 | $.get(url, function (data) { 8 | debugger; 9 | if (data.indexOf("won") > 0 || data == "The game was a draw.") { 10 | alert(data); 11 | window.location.href = document.location.protocol + 12 | "//" + document.location.hostname + port; 13 | } 14 | }); 15 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/wwwroot/app/js/scripts1.js: -------------------------------------------------------------------------------- 1 |  2 | var interval; 3 | function EmailConfirmation(email) { 4 | if (window.WebSocket) { 5 | //alert("Websockets are enabled"); 6 | openSocket(email, "Email"); 7 | } 8 | else { 9 | //alert("Websockets are not enabled"); 10 | interval = setInterval(() => { 11 | CheckEmailConfirmationStatus(email); 12 | }, 5000); 13 | } 14 | } 15 | function GameInvitationConfirmation(id) { 16 | if (window.WebSocket) { 17 | //alert("Websockets are enabled"); 18 | openSocket(id, "GameInvitation"); 19 | } 20 | else { 21 | //alert("Websockets are not enabled"); 22 | interval = setInterval(() => { 23 | CheckGameInvitationConfirmationStatus(id); 24 | }, 5000); 25 | } 26 | } -------------------------------------------------------------------------------- /Chapter13/TicTacToe/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Learn-ASP.NET-Core-3-Second-Edition/982ad26305a7493d5eb97f6824c66f9fe6f878e7/Chapter13/TicTacToe/wwwroot/favicon.ico -------------------------------------------------------------------------------- /Chapter13/TicTacToe/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | --------------------------------------------------------------------------------