From e008e1dba2bcef68ddcb0a9cb61b71cff7eed407 Mon Sep 17 00:00:00 2001 From: Davidson Sousa Date: Sun, 6 Mar 2022 17:58:56 +0100 Subject: [PATCH 01/33] Features/upgrate to dotnet 6 (#108) * Moved files to src; Adapted C# code to a more modern approach; Minor refactoring everywhere * Removed old project folders/files * Removed temporary old solution * Refactored code; Set some members to nullable * Changed usage on TryUpdateModelAsync --- .editorconfig | 299 ++++++++++++------ CmsEngine.Application/Attributes/Orderable.cs | 13 - .../Attributes/Searchable.cs | 13 - .../Attributes/ShowOnDataTable.cs | 18 -- .../CmsEngine.Application.csproj | 19 -- .../Extensions/EnumerableExtensions.cs | 55 ---- .../Mapper/ApplicationUserExtension.cs | 126 -------- .../Extensions/Mapper/CategoryExtensions.cs | 187 ----------- .../Extensions/Mapper/ContactFormExtension.cs | 47 --- .../Extensions/Mapper/PageExtensions.cs | 178 ----------- .../Extensions/Mapper/PostExtensions.cs | 293 ----------------- .../Extensions/Mapper/TagExtensions.cs | 127 -------- .../Extensions/Mapper/WebsiteExtensions.cs | 146 --------- .../Helpers/DataTableHelper.cs | 87 ----- .../Helpers/Email/ContactForm.cs | 43 --- .../Helpers/Email/EmailSender.cs | 83 ----- .../Helpers/Email/EmailSettings.cs | 35 -- .../Helpers/Email/IEmailSender.cs | 9 - .../Helpers/ExpressionBuilder.cs | 133 -------- CmsEngine.Application/Helpers/FileHelper.cs | 88 ------ .../Models/EditModels/BaseEditModel.cs | 30 -- .../Models/EditModels/CategoryEditModel.cs | 28 -- .../Models/EditModels/CheckboxEditModel.cs | 10 - .../Models/EditModels/IEditModel.cs | 12 - .../Models/EditModels/PageEditModel.cs | 51 --- .../Models/EditModels/PostEditModel.cs | 62 ---- .../Models/EditModels/TagEditModel.cs | 13 - .../Models/EditModels/UserEditModel.cs | 29 -- .../Models/EditModels/WebsiteEditModel.cs | 73 ----- .../ExternalLoginViewModel.cs | 11 - .../ForgotPasswordViewModel.cs | 11 - .../AccountViewModels/LoginViewModel.cs | 18 -- .../LoginWith2faViewModel.cs | 18 -- .../LoginWithRecoveryCodeViewModel.cs | 12 - .../AccountViewModels/RegisterViewModel.cs | 23 -- .../ResetPasswordViewModel.cs | 23 -- .../Models/ViewModels/ApiDetailsViewModel.cs | 27 -- .../Models/ViewModels/BaseViewModel.cs | 10 - .../Models/ViewModels/CategoryViewModel.cs | 13 - .../ViewModels/ContactDetailsViewModel.cs | 17 - .../CategoryTableViewModel.cs | 16 - .../DataTableViewModels/DataColumn.cs | 22 -- .../DataTableViewModels/DataOrder.cs | 13 - .../DataTableViewModels/DataParameters.cs | 26 -- .../DataTableViewModels/DataTableViewModel.cs | 20 -- .../DataTableViewModels/PageTableViewModel.cs | 28 -- .../DataTableViewModels/PostTableViewModel.cs | 28 -- .../ViewModels/DataTableViewModels/Search.cs | 13 - .../DataTableViewModels/TagTableViewModel.cs | 13 - .../WebsiteTableViewModel.cs | 37 --- .../Models/ViewModels/DocumentViewModel.cs | 15 - .../Models/ViewModels/ErrorViewModel.cs | 20 -- .../Models/ViewModels/GoogleViewModel.cs | 33 -- .../Models/ViewModels/IViewModel.cs | 11 - .../Models/ViewModels/InstanceViewModel.cs | 36 --- .../ChangePasswordViewModel.cs | 25 -- .../EnableAuthenticatorViewModel.cs | 19 -- .../ExternalLoginsViewModel.cs | 17 - .../GenerateRecoveryCodesViewModel.cs | 7 - .../ManageViewModels/IndexViewModel.cs | 25 -- .../ManageViewModels/RemoveLoginViewModel.cs | 8 - .../ManageViewModels/SetPasswordViewModel.cs | 20 -- .../TwoFactorAuthenticationViewModel.cs | 11 - .../Models/ViewModels/PageViewModel.cs | 7 - .../Models/ViewModels/PostViewModel.cs | 13 - .../Models/ViewModels/SitemapViewModel.cs | 8 - .../Models/ViewModels/SocialMediaViewModel.cs | 50 --- .../Models/ViewModels/TagViewModel.cs | 9 - .../Models/ViewModels/UserViewModel.cs | 26 -- .../Models/ViewModels/WebsiteViewModel.cs | 16 - .../Services/CategoryService.cs | 192 ----------- .../Services/EmailService.cs | 59 ---- .../Services/Interfaces/ICategoryService.cs | 23 -- .../Services/Interfaces/IDataTableService.cs | 11 - .../Services/Interfaces/IEmailService.cs | 13 - .../Services/Interfaces/IPageService.cs | 23 -- .../Services/Interfaces/IPostService.cs | 28 -- .../Services/Interfaces/IService.cs | 10 - .../Services/Interfaces/ITagService.cs | 22 -- .../Services/Interfaces/IWebsiteService.cs | 20 -- .../Services/Interfaces/IXmlService.cs | 11 - CmsEngine.Application/Services/PageService.cs | 209 ------------ CmsEngine.Application/Services/PostService.cs | 280 ---------------- CmsEngine.Application/Services/Service.cs | 161 ---------- CmsEngine.Application/Services/TagService.cs | 176 ----------- .../Services/WebsiteService.cs | 172 ---------- CmsEngine.Application/Services/XmlService.cs | 98 ------ CmsEngine.Core/CmsEngine.Core.csproj | 13 - .../Constants/CmsEngineConstants.cs | 27 -- .../Constants/ContentTypeOptionsConstants.cs | 19 -- .../Constants/FrameOptionsConstants.cs | 28 -- CmsEngine.Core/Constants/MessageConstants.cs | 10 - CmsEngine.Core/Constants/ServerConstants.cs | 13 - .../StrictTransportSecurityConstants.cs | 28 -- .../Constants/XssProtectionConstants.cs | 34 -- CmsEngine.Core/Exceptions/EmailException.cs | 15 - .../Exceptions/NotFoundException.cs | 15 - .../Extensions/BooleanExtensions.cs | 15 - CmsEngine.Core/Extensions/EnumExtensions.cs | 58 ---- .../Extensions/IEnumerableExtensions.cs | 23 -- CmsEngine.Core/Extensions/ObjectExtensions.cs | 28 -- CmsEngine.Core/Extensions/StringExtensions.cs | 12 - CmsEngine.Core/Utils/CustomResolver.cs | 23 -- CmsEngine.Core/Utils/Enums.cs | 134 -------- CmsEngine.Core/Utils/GravatarUtilities.cs | 33 -- CmsEngine.Core/Utils/Guard.cs | 15 - CmsEngine.Core/Utils/PaginatedList.cs | 42 --- CmsEngine.Core/Utils/ReturnValue.cs | 32 -- CmsEngine.Core/Utils/TinyMceUploadResult.cs | 7 - CmsEngine.Core/Utils/UploadFilesResult.cs | 13 - CmsEngine.Data/CmsEngineContext.cs | 73 ----- CmsEngine.Data/Entities/ApplicationUser.cs | 15 - CmsEngine.Data/Entities/BaseEntity.cs | 35 -- CmsEngine.Data/Entities/Category.cs | 41 --- CmsEngine.Data/Entities/Document.cs | 40 --- CmsEngine.Data/Entities/Email.cs | 23 -- CmsEngine.Data/Entities/IModel.cs | 19 -- CmsEngine.Data/Entities/ModelConfiguration.cs | 259 --------------- CmsEngine.Data/Entities/Page.cs | 23 -- .../Entities/PageApplicationUser.cs | 14 - CmsEngine.Data/Entities/Post.cs | 33 -- .../Entities/PostApplicationUser.cs | 14 - CmsEngine.Data/Entities/PostCategory.cs | 11 - CmsEngine.Data/Entities/PostTag.cs | 11 - CmsEngine.Data/Entities/Tag.cs | 14 - CmsEngine.Data/Entities/Website.cs | 53 ---- .../Extensions/DbContextExtensions.cs | 16 - .../Extensions/ModelBuilderExtensions.cs | 110 ------- CmsEngine.Data/IUnitOfWork.cs | 24 -- .../Repositories/CategoryRepository.cs | 62 ---- .../Repositories/EmailRepository.cs | 21 -- .../Repositories/ICategoryRepository.cs | 14 - .../IDataModificationRangeRepository.cs | 26 -- .../IDataModificationRepository.cs | 31 -- .../Repositories/IEmailRepository.cs | 11 - .../Repositories/IPageRepository.cs | 19 -- .../Repositories/IPostRepository.cs | 25 -- .../Repositories/IReadRepository.cs | 68 ---- CmsEngine.Data/Repositories/ITagRepository.cs | 13 - .../Repositories/IWebsiteRepository.cs | 12 - CmsEngine.Data/Repositories/PageRepository.cs | 83 ----- CmsEngine.Data/Repositories/PostRepository.cs | 283 ----------------- CmsEngine.Data/Repositories/Repository.cs | 165 ---------- CmsEngine.Data/Repositories/TagRepository.cs | 30 -- .../Repositories/WebsiteRepository.cs | 36 --- CmsEngine.Data/UnitOfWork.cs | 96 ------ CmsEngine.Ui/.config/dotnet-tools.json | 12 - .../Areas/Cms/Controllers/BaseController.cs | 182 ----------- .../Cms/Controllers/CategoryController.cs | 122 ------- .../Areas/Cms/Controllers/HomeController.cs | 26 -- .../Areas/Cms/Controllers/PageController.cs | 137 -------- .../Areas/Cms/Controllers/PostController.cs | 137 -------- .../Areas/Cms/Controllers/TagController.cs | 121 ------- .../Cms/Controllers/WebsiteController.cs | 135 -------- .../Areas/Cms/Views/_ViewImports.cshtml | 10 - .../Pages/Account/ForgotPassword.cshtml.cs | 64 ---- .../Identity/Pages/Account/Login.cshtml.cs | 102 ------ .../Identity/Pages/Account/Logout.cshtml.cs | 31 -- .../Account/Manage/ChangePassword.cshtml.cs | 106 ------- .../Manage/DeletePersonalData.cshtml.cs | 84 ----- .../Pages/Account/Manage/Disable2fa.cshtml.cs | 62 ---- .../Pages/Account/Manage/Index.cshtml.cs | 178 ----------- .../Pages/Account/Manage/ManageNavPages.cs | 63 ---- .../Identity/Pages/Account/Register.cshtml.cs | 96 ------ .../Pages/Account/ResetPassword.cshtml.cs | 86 ----- CmsEngine.Ui/CmsEngine.Ui.csproj | 33 -- CmsEngine.Ui/Controllers/BaseController.cs | 75 ----- CmsEngine.Ui/Controllers/BlogController.cs | 85 ----- CmsEngine.Ui/Controllers/ErrorController.cs | 37 --- CmsEngine.Ui/Controllers/HomeController.cs | 97 ------ .../Extensions/EmailSenderExtensions.cs | 27 -- .../Extensions/HtmlHelperExtensions.cs | 20 -- CmsEngine.Ui/GlobalSuppressions.cs | 10 - .../ConfigureFileUploadMiddleware.cs | 35 -- .../Middleware/MiddlewareExtensions.cs | 39 --- .../SecurityHeaders/SecurityHeadersBuilder.cs | 212 ------------- .../SecurityHeaders/SecurityHeadersPolicy.cs | 17 - .../Middleware/SecurityHeadersMiddleware.cs | 66 ---- CmsEngine.Ui/Program.cs | 79 ----- .../RewriteRules/RedirectLowerCaseRule.cs | 34 -- .../RewriteRules/RedirectToNonWwwRule.cs | 42 --- CmsEngine.Ui/Startup.cs | 215 ------------- .../TagHelpers/CheckboxListTagHelper.cs | 73 ----- CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs | 77 ----- CmsEngine.Ui/certificate.json | 8 - CmsEngine.sln | 48 +-- .../Attributes/Orderable.cs | 10 + .../Attributes/Searchable.cs | 10 + .../Attributes/ShowOnDataTable.cs | 15 + .../CmsEngine.Application.csproj | 20 ++ .../Extensions/EnumerableExtensions.cs | 43 +++ .../Mapper/ApplicationUserExtension.cs | 119 +++++++ .../Extensions/Mapper/CategoryExtensions.cs | 179 +++++++++++ .../Extensions/Mapper/ContactFormExtension.cs | 42 +++ .../Extensions/Mapper/PageExtensions.cs | 170 ++++++++++ .../Extensions/Mapper/PostExtensions.cs | 285 +++++++++++++++++ .../Extensions/Mapper/TagExtensions.cs | 120 +++++++ .../Extensions/Mapper/WebsiteExtensions.cs | 140 ++++++++ src/CmsEngine.Application/GlobalUsings.cs | 36 +++ .../Helpers/DataTableHelper.cs | 74 +++++ .../Helpers/Email/CmsEngineEmailSender.cs | 73 +++++ .../Helpers/Email/ContactForm.cs | 39 +++ .../Helpers/Email/EmailSettings.cs | 32 ++ .../Helpers/Email/ICmsEngineEmailSender.cs | 6 + .../Helpers/ExpressionBuilder.cs | 120 +++++++ .../Helpers/ExpressionFilter.cs | 8 + .../Helpers/FileHelper.cs | 80 +++++ .../Models/EditModels/BaseEditModel.cs | 24 ++ .../Models/EditModels/CategoryEditModel.cs | 24 ++ .../Models/EditModels/CheckboxEditModel.cs | 9 + .../Models/EditModels/IEditModel.cs | 9 + .../Models/EditModels/PageEditModel.cs | 45 +++ .../Models/EditModels/PostEditModel.cs | 48 +++ .../Models/EditModels/TagEditModel.cs | 10 + .../Models/EditModels/UserEditModel.cs | 24 ++ .../Models/EditModels/WebsiteEditModel.cs | 69 ++++ .../ExternalLoginViewModel.cs | 8 + .../ForgotPasswordViewModel.cs | 8 + .../AccountViewModels/LoginViewModel.cs | 15 + .../LoginWith2faViewModel.cs | 15 + .../LoginWithRecoveryCodeViewModel.cs | 9 + .../AccountViewModels/RegisterViewModel.cs | 20 ++ .../ResetPasswordViewModel.cs | 20 ++ .../Models/ViewModels/ApiDetailsViewModel.cs | 22 ++ .../Models/ViewModels/BaseViewModel.cs | 7 + .../Models/ViewModels/CategoryViewModel.cs | 10 + .../ViewModels/ContactDetailsViewModel.cs | 14 + .../CategoryTableViewModel.cs | 13 + .../DataTableViewModels/DataColumn.cs | 19 ++ .../DataTableViewModels/DataOrder.cs | 10 + .../DataTableViewModels/DataParameters.cs | 22 ++ .../DataTableViewModels/DataTableViewModel.cs | 16 + .../DataTableViewModels/PageTableViewModel.cs | 24 ++ .../DataTableViewModels/PostTableViewModel.cs | 24 ++ .../ViewModels/DataTableViewModels/Search.cs | 10 + .../DataTableViewModels/TagTableViewModel.cs | 10 + .../WebsiteTableViewModel.cs | 31 ++ .../Models/ViewModels/DocumentViewModel.cs | 12 + .../Models/ViewModels/ErrorViewModel.cs | 19 ++ .../Models/ViewModels/GoogleViewModel.cs | 26 ++ .../Models/ViewModels/IViewModel.cs | 8 + .../Models/ViewModels/InstanceViewModel.cs | 31 ++ .../ChangePasswordViewModel.cs | 22 ++ .../EnableAuthenticatorViewModel.cs | 15 + .../ExternalLoginsViewModel.cs | 12 + .../GenerateRecoveryCodesViewModel.cs | 6 + .../ManageViewModels/IndexViewModel.cs | 22 ++ .../ManageViewModels/RemoveLoginViewModel.cs | 7 + .../ManageViewModels/SetPasswordViewModel.cs | 17 + .../TwoFactorAuthenticationViewModel.cs | 10 + .../Models/ViewModels/PageViewModel.cs | 6 + .../Models/ViewModels/PostViewModel.cs | 8 + .../Models/ViewModels/SitemapViewModel.cs | 7 + .../Models/ViewModels/SocialMediaViewModel.cs | 39 +++ .../Models/ViewModels/TagViewModel.cs | 8 + .../Models/ViewModels/UserViewModel.cs | 21 ++ .../Models/ViewModels/WebsiteViewModel.cs | 13 + .../Services/CategoryService.cs | 174 ++++++++++ .../Services/EmailService.cs | 46 +++ .../Services/Interfaces/ICategoryService.cs | 13 + .../Services/Interfaces/IDataTableService.cs | 7 + .../Services/Interfaces/IEmailService.cs | 7 + .../Services/Interfaces/IPageService.cs | 13 + .../Services/Interfaces/IPostService.cs | 18 ++ .../Services/Interfaces/IService.cs | 7 + .../Services/Interfaces/ITagService.cs | 12 + .../Services/Interfaces/IWebsiteService.cs | 11 + .../Services/Interfaces/IXmlService.cs | 7 + .../Services/PageService.cs | 191 +++++++++++ .../Services/PostService.cs | 262 +++++++++++++++ src/CmsEngine.Application/Services/Service.cs | 145 +++++++++ .../Services/TagService.cs | 158 +++++++++ .../Services/WebsiteService.cs | 155 +++++++++ .../Services/XmlService.cs | 87 +++++ src/CmsEngine.Core/CmsEngine.Core.csproj | 13 + .../Constants/ContentTypeOptions.cs | 17 + src/CmsEngine.Core/Constants/FrameOptions.cs | 27 ++ src/CmsEngine.Core/Constants/Main.cs | 26 ++ src/CmsEngine.Core/Constants/Message.cs | 9 + src/CmsEngine.Core/Constants/Server.cs | 12 + .../Constants/StrictTransportSecurity.cs | 27 ++ src/CmsEngine.Core/Constants/XssProtection.cs | 33 ++ .../Exceptions/EmailException.cs | 12 + .../Exceptions/NotFoundException.cs | 12 + .../Extensions/BooleanExtensions.cs | 14 + .../Extensions/EnumExtensions.cs | 52 +++ .../Extensions/IEnumerableExtension.cs | 18 ++ .../Extensions/StringExtensions.cs | 9 + src/CmsEngine.Core/GlobalUsings.cs | 7 + src/CmsEngine.Core/Utils/CustomResolver.cs | 18 ++ src/CmsEngine.Core/Utils/Enums.cs | 130 ++++++++ src/CmsEngine.Core/Utils/GravatarUtilities.cs | 29 ++ src/CmsEngine.Core/Utils/PaginatedList.cs | 27 ++ src/CmsEngine.Core/Utils/ReturnValue.cs | 29 ++ .../Utils/TinyMceUploadResult.cs | 6 + src/CmsEngine.Core/Utils/UploadFilesResult.cs | 12 + .../CmsEngine.Data}/CmsEngine.Data.csproj | 17 +- src/CmsEngine.Data/CmsEngineContext.cs | 63 ++++ .../Entities/ApplicationUser.cs | 11 + src/CmsEngine.Data/Entities/BaseEntity.cs | 27 ++ src/CmsEngine.Data/Entities/Category.cs | 32 ++ src/CmsEngine.Data/Entities/Document.cs | 29 ++ src/CmsEngine.Data/Entities/Email.cs | 19 ++ src/CmsEngine.Data/Entities/IModel.cs | 16 + .../Entities/ModelConfiguration.cs | 255 +++++++++++++++ src/CmsEngine.Data/Entities/Page.cs | 19 ++ .../Entities/PageApplicationUser.cs | 11 + src/CmsEngine.Data/Entities/Post.cs | 18 ++ .../Entities/PostApplicationUser.cs | 11 + src/CmsEngine.Data/Entities/PostCategory.cs | 10 + src/CmsEngine.Data/Entities/PostTag.cs | 10 + src/CmsEngine.Data/Entities/Tag.cs | 11 + src/CmsEngine.Data/Entities/Website.cs | 48 +++ .../Extensions/DbContextExtensions.cs | 10 + .../Extensions/ModelBuilderExtensions.cs | 105 ++++++ src/CmsEngine.Data/GlobalUsings.cs | 15 + src/CmsEngine.Data/IUnitOfWork.cs | 17 + .../20181231150109_InitialCreate.Designer.cs | 0 .../20181231150109_InitialCreate.cs | 0 ...20190211194130_DisqusShortName.Designer.cs | 0 .../20190211194130_DisqusShortName.cs | 0 ...22093305_Added_GoogleAnalytics.Designer.cs | 0 .../20190422093305_Added_GoogleAnalytics.cs | 0 ...20174931_GoogleRecaptchaFields.Designer.cs | 0 .../20191020174931_GoogleRecaptchaFields.cs | 0 ...92050_IncreaseCategoryNameSize.Designer.cs | 0 ...20191020192050_IncreaseCategoryNameSize.cs | 0 ...0191020221230_AddingEmailTable.Designer.cs | 0 .../20191020221230_AddingEmailTable.cs | 0 ...04_ShadowPropertiesForAuditing.Designer.cs | 0 ...00410203904_ShadowPropertiesForAuditing.cs | 0 ...creasedSizeForAuditingUserName.Designer.cs | 0 ...123624_IncreasedSizeForAuditingUserName.cs | 0 .../CmsEngineContextModelSnapshot.cs | 0 .../Repositories/CategoryRepository.cs | 54 ++++ .../Repositories/EmailRepository.cs | 14 + .../Interfaces/ICategoryRepository.cs | 9 + .../IDataModificationRangeRepository.cs | 22 ++ .../Interfaces/IDataModificationRepository.cs | 28 ++ .../Interfaces/IEmailRepository.cs | 6 + .../Interfaces/IPageRepository.cs | 11 + .../Interfaces/IPostRepository.cs | 17 + .../Interfaces/IReadRepository.cs | 61 ++++ .../Repositories/Interfaces/ITagRepository.cs | 8 + .../Interfaces/IWebsiteRepository.cs | 7 + .../Repositories/PageRepository.cs | 73 +++++ .../Repositories/PostRepository.cs | 273 ++++++++++++++++ src/CmsEngine.Data/Repositories/Repository.cs | 155 +++++++++ .../Repositories/TagRepository.cs | 24 ++ .../Repositories/WebsiteRepository.cs | 29 ++ src/CmsEngine.Data/UnitOfWork.cs | 88 ++++++ .../Areas/Cms/Controllers/BaseController.cs | 160 ++++++++++ .../Cms/Controllers/CategoryController.cs | 109 +++++++ .../Areas/Cms/Controllers/HomeController.cs | 19 ++ .../Areas/Cms/Controllers/PageController.cs | 123 +++++++ .../Areas/Cms/Controllers/PostController.cs | 124 ++++++++ .../Areas/Cms/Controllers/TagController.cs | 108 +++++++ .../Cms/Controllers/WebsiteController.cs | 121 +++++++ .../Cms/Views/Category/CreateEdit.cshtml | 0 .../Areas/Cms/Views/Category/List.cshtml | 0 .../Areas/Cms/Views/Home/Index.cshtml | 0 .../Areas/Cms/Views/Page/CreateEdit.cshtml | 2 +- .../Areas/Cms/Views/Page/List.cshtml | 0 .../Areas/Cms/Views/Post/CreateEdit.cshtml | 2 +- .../Areas/Cms/Views/Post/List.cshtml | 0 .../Areas/Cms/Views/Shared/Error.cshtml | 0 .../Views/Shared/_DataTablesScripts.cshtml | 0 .../Cms/Views/Shared/_DataTablesStyles.cshtml | 0 .../Cms/Views/Shared/_LoginPartial.cshtml | 0 .../Shared/_ValidationScriptsPartial.cshtml | 0 .../Areas/Cms/Views/Tag/CreateEdit.cshtml | 0 .../Areas/Cms/Views/Tag/List.cshtml | 0 .../Areas/Cms/Views/Website/CreateEdit.cshtml | 2 +- .../Areas/Cms/Views/Website/List.cshtml | 0 .../Areas/Cms/Views/_ViewImports.cshtml | 10 + .../Areas/Cms/Views/_ViewStart.cshtml | 0 .../Pages/Account/AccessDenied.cshtml | 0 .../Pages/Account/AccessDenied.cshtml.cs | 0 .../Pages/Account/ConfirmEmail.cshtml | 0 .../Pages/Account/ConfirmEmail.cshtml.cs | 0 .../Pages/Account/ExternalLogin.cshtml | 0 .../Pages/Account/ExternalLogin.cshtml.cs | 0 .../Pages/Account/ForgotPassword.cshtml | 0 .../Pages/Account/ForgotPassword.cshtml.cs | 52 +++ .../Account/ForgotPasswordConfirmation.cshtml | 0 .../ForgotPasswordConfirmation.cshtml.cs | 0 .../Identity/Pages/Account/Lockout.cshtml | 0 .../Identity/Pages/Account/Lockout.cshtml.cs | 0 .../Areas/Identity/Pages/Account/Login.cshtml | 0 .../Identity/Pages/Account/Login.cshtml.cs | 89 ++++++ .../Pages/Account/LoginWith2fa.cshtml | 0 .../Pages/Account/LoginWith2fa.cshtml.cs | 0 .../Account/LoginWithRecoveryCode.cshtml | 0 .../Account/LoginWithRecoveryCode.cshtml.cs | 0 .../Identity/Pages/Account/Logout.cshtml | 0 .../Identity/Pages/Account/Logout.cshtml.cs | 21 ++ .../Account/Manage/ChangePassword.cshtml | 0 .../Account/Manage/ChangePassword.cshtml.cs | 97 ++++++ .../Account/Manage/DeletePersonalData.cshtml | 0 .../Manage/DeletePersonalData.cshtml.cs | 74 +++++ .../Pages/Account/Manage/Disable2fa.cshtml | 0 .../Pages/Account/Manage/Disable2fa.cshtml.cs | 53 ++++ .../Manage/DownloadPersonalData.cshtml | 0 .../Manage/DownloadPersonalData.cshtml.cs | 0 .../Account/Manage/EnableAuthenticator.cshtml | 0 .../Manage/EnableAuthenticator.cshtml.cs | 0 .../Account/Manage/ExternalLogins.cshtml | 0 .../Account/Manage/ExternalLogins.cshtml.cs | 0 .../Manage/GenerateRecoveryCodes.cshtml | 0 .../Manage/GenerateRecoveryCodes.cshtml.cs | 0 .../Pages/Account/Manage/Index.cshtml | 0 .../Pages/Account/Manage/Index.cshtml.cs | 164 ++++++++++ .../Pages/Account/Manage/ManageNavPages.cs | 62 ++++ .../Pages/Account/Manage/PersonalData.cshtml | 0 .../Account/Manage/PersonalData.cshtml.cs | 0 .../Account/Manage/ResetAuthenticator.cshtml | 0 .../Manage/ResetAuthenticator.cshtml.cs | 0 .../Pages/Account/Manage/SetPassword.cshtml | 0 .../Account/Manage/SetPassword.cshtml.cs | 0 .../Manage/TwoFactorAuthentication.cshtml | 0 .../Manage/TwoFactorAuthentication.cshtml.cs | 0 .../Pages/Account/Manage/_Layout.cshtml | 0 .../Pages/Account/Manage/_ManageNav.cshtml | 0 .../Account/Manage/_StatusMessage.cshtml | 0 .../Pages/Account/Manage/_ViewImports.cshtml | 0 .../Identity/Pages/Account/Register.cshtml | 0 .../Identity/Pages/Account/Register.cshtml.cs | 84 +++++ .../Pages/Account/ResetPassword.cshtml | 0 .../Pages/Account/ResetPassword.cshtml.cs | 77 +++++ .../Account/ResetPasswordConfirmation.cshtml | 0 .../ResetPasswordConfirmation.cshtml.cs | 0 .../Pages/Account/_ViewImports.cshtml | 0 .../Areas/Identity/Pages/Error.cshtml | 0 .../Areas/Identity/Pages/Error.cshtml.cs | 0 .../Pages/_ValidationScriptsPartial.cshtml | 0 .../Areas/Identity/Pages/_ViewImports.cshtml | 0 .../Areas/Identity/Pages/_ViewStart.cshtml | 0 src/CmsEngine.Ui/CmsEngine.Ui.csproj | 30 ++ .../Controllers/BaseController.cs | 65 ++++ .../Controllers/BlogController.cs | 78 +++++ .../Controllers/ErrorController.cs | 32 ++ .../Controllers/HomeController.cs | 87 +++++ .../Extensions/EmailSenderExtensions.cs | 22 ++ .../Extensions/HtmlHelperExtensions.cs | 15 + .../Extensions/MiddlewareExtensions.cs | 34 ++ src/CmsEngine.Ui/GlobalUsings.cs | 46 +++ .../ConfigureFileUploadMiddleware.cs | 24 ++ .../Middleware/FileUploadOptions.cs | 7 + .../SecurityHeaders/SecurityHeadersBuilder.cs | 208 ++++++++++++ .../SecurityHeaders/SecurityHeadersPolicy.cs | 14 + .../Middleware/SecurityHeadersMiddleware.cs | 55 ++++ src/CmsEngine.Ui/Program.cs | 200 ++++++++++++ .../Properties/launchSettings.json | 27 +- .../Properties/serviceDependencies.json | 8 + .../Properties/serviceDependencies.local.json | 8 + .../RewriteRules/RedirectLowerCaseRule.cs | 29 ++ .../RewriteRules/RedirectToNonWwwRule.cs | 34 ++ .../TagHelpers/CheckboxListTagHelper.cs | 67 ++++ .../TagHelpers/GravatarTagHelper.cs | 69 ++++ .../CmsEngine.Ui}/Views/Blog/Index.cshtml | 2 +- .../CmsEngine.Ui}/Views/Blog/Post.cshtml | 4 +- .../Views/Blog/_PagedPost.cshtml | 2 +- .../CmsEngine.Ui}/Views/Error/Index.cshtml | 0 .../CmsEngine.Ui}/Views/Home/Archive.cshtml | 2 +- .../CmsEngine.Ui}/Views/Home/Contact.cshtml | 0 .../CmsEngine.Ui}/Views/Home/Index.cshtml | 4 +- .../CmsEngine.Ui}/Views/Home/Page.cshtml | 2 +- .../CmsEngine.Ui}/Views/Home/Privacy.cshtml | 0 .../Views/Home/_PostIndex.cshtml | 2 +- .../Views/Shared/Cms/_Dialog.cshtml | 0 .../Views/Shared/Cms/_ExternalLayout.cshtml | 0 .../Views/Shared/Cms/_Layout.cshtml | 0 .../Shared/EditorTemplates/DateTime.cshtml | 0 .../Views/Shared/_CookieConsentPartial.cshtml | 0 .../CmsEngine.Ui}/Views/Shared/_Footer.cshtml | 2 +- .../CmsEngine.Ui}/Views/Shared/_Layout.cshtml | 1 - .../Views/Shared/_LoginPartial.cshtml | 0 .../Views/Shared/_Messages.cshtml | 0 .../Views/Shared/_SearchForm.cshtml | 0 .../Views/Shared/_Sidebar.cshtml | 2 +- .../Shared/_ValidationScriptsPartial.cshtml | 0 .../CmsEngine.Ui}/Views/_ViewImports.cshtml | 4 +- .../CmsEngine.Ui}/Views/_ViewStart.cshtml | 0 .../appsettings.Development.json | 0 .../CmsEngine.Ui}/appsettings.json | 9 +- .../CmsEngine.Ui}/assets/fonts/blog.eot | Bin .../CmsEngine.Ui}/assets/fonts/blog.svg | 0 .../CmsEngine.Ui}/assets/fonts/blog.ttf | Bin .../CmsEngine.Ui}/assets/fonts/blog.woff | Bin .../CmsEngine.Ui}/assets/img/favicon.ico | Bin .../CmsEngine.Ui}/assets/img/favicon.png | Bin .../CmsEngine.Ui}/assets/img/flags/ASEAN.png | Bin .../assets/img/flags/Afghanistan.png | Bin .../assets/img/flags/African Union.png | Bin .../assets/img/flags/Albania.png | Bin .../assets/img/flags/Algeria.png | Bin .../assets/img/flags/American Samoa.png | Bin .../assets/img/flags/Andorra.png | Bin .../CmsEngine.Ui}/assets/img/flags/Angola.png | Bin .../assets/img/flags/Anguilla.png | Bin .../assets/img/flags/Antarctica.png | Bin .../assets/img/flags/Antigua & Barbuda.png | Bin .../assets/img/flags/Arab League.png | Bin .../assets/img/flags/Argentina.png | Bin .../assets/img/flags/Armenia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Aruba.png | Bin .../assets/img/flags/Australia.png | Bin .../assets/img/flags/Austria.png | Bin .../assets/img/flags/Azerbaijan.png | Bin .../assets/img/flags/Bahamas.png | Bin .../assets/img/flags/Bahrain.png | Bin .../assets/img/flags/Bangladesh.png | Bin .../assets/img/flags/Barbados.png | Bin .../assets/img/flags/Belarus.png | Bin .../assets/img/flags/Belgium.png | Bin .../CmsEngine.Ui}/assets/img/flags/Belize.png | Bin .../CmsEngine.Ui}/assets/img/flags/Benin.png | Bin .../assets/img/flags/Bermuda.png | Bin .../CmsEngine.Ui}/assets/img/flags/Bhutan.png | Bin .../assets/img/flags/Bolivia.png | Bin .../assets/img/flags/Bosnia & Herzegovina.png | Bin .../assets/img/flags/Botswana.png | Bin .../CmsEngine.Ui}/assets/img/flags/Brazil.png | Bin .../CmsEngine.Ui}/assets/img/flags/Brunei.png | Bin .../assets/img/flags/Bulgaria.png | Bin .../assets/img/flags/Burkina Faso.png | Bin .../assets/img/flags/Burundi.png | Bin .../assets/img/flags/CARICOM.png | Bin .../CmsEngine.Ui}/assets/img/flags/CIS.png | Bin .../assets/img/flags/Cambodja.png | Bin .../assets/img/flags/Cameroon.png | Bin .../CmsEngine.Ui}/assets/img/flags/Canada.png | Bin .../assets/img/flags/Cape Verde.png | Bin .../assets/img/flags/Cayman Islands.png | Bin .../img/flags/Central African Republic.png | Bin .../CmsEngine.Ui}/assets/img/flags/Chad.png | Bin .../CmsEngine.Ui}/assets/img/flags/Chile.png | Bin .../CmsEngine.Ui}/assets/img/flags/China.png | Bin .../assets/img/flags/Colombia.png | Bin .../assets/img/flags/Commonwealth.png | Bin .../assets/img/flags/Comoros.png | Bin .../assets/img/flags/Congo-Brazzaville.png | Bin .../img/flags/Congo-Kinshasa(Zaire).png | Bin .../assets/img/flags/Cook Islands.png | Bin .../assets/img/flags/Costa Rica.png | Bin .../assets/img/flags/Cote d'Ivoire.png | Bin .../assets/img/flags/Croatia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Cuba.png | Bin .../CmsEngine.Ui}/assets/img/flags/Cyprus.png | Bin .../assets/img/flags/Czech Republic.png | Bin .../assets/img/flags/Denmark.png | Bin .../assets/img/flags/Djibouti.png | Bin .../assets/img/flags/Dominica.png | Bin .../assets/img/flags/Dominican Republic.png | Bin .../assets/img/flags/Ecuador.png | Bin .../CmsEngine.Ui}/assets/img/flags/Egypt.png | Bin .../assets/img/flags/El Salvador.png | Bin .../assets/img/flags/England.png | Bin .../assets/img/flags/Equatorial Guinea.png | Bin .../assets/img/flags/Eritrea.png | Bin .../assets/img/flags/Estonia.png | Bin .../assets/img/flags/Ethiopia.png | Bin .../assets/img/flags/European Union.png | Bin .../CmsEngine.Ui}/assets/img/flags/Faroes.png | Bin .../CmsEngine.Ui}/assets/img/flags/Fiji.png | Bin .../assets/img/flags/Finland.png | Bin .../CmsEngine.Ui}/assets/img/flags/France.png | Bin .../CmsEngine.Ui}/assets/img/flags/Gabon.png | Bin .../CmsEngine.Ui}/assets/img/flags/Gambia.png | Bin .../assets/img/flags/Georgia.png | Bin .../assets/img/flags/Germany.png | Bin .../CmsEngine.Ui}/assets/img/flags/Ghana.png | Bin .../assets/img/flags/Gibraltar.png | Bin .../CmsEngine.Ui}/assets/img/flags/Greece.png | Bin .../assets/img/flags/Greenland.png | Bin .../assets/img/flags/Grenada.png | Bin .../assets/img/flags/Guadeloupe.png | Bin .../CmsEngine.Ui}/assets/img/flags/Guam.png | Bin .../assets/img/flags/Guatemala.png | Bin .../assets/img/flags/Guernsey.png | Bin .../assets/img/flags/Guinea-Bissau.png | Bin .../CmsEngine.Ui}/assets/img/flags/Guinea.png | Bin .../CmsEngine.Ui}/assets/img/flags/Guyana.png | Bin .../CmsEngine.Ui}/assets/img/flags/Haiti.png | Bin .../assets/img/flags/Honduras.png | Bin .../assets/img/flags/Hong Kong.png | Bin .../assets/img/flags/Hungary.png | Bin .../assets/img/flags/Iceland.png | Bin .../CmsEngine.Ui}/assets/img/flags/India.png | Bin .../assets/img/flags/Indonezia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Iran.png | Bin .../CmsEngine.Ui}/assets/img/flags/Iraq.png | Bin .../assets/img/flags/Ireland.png | Bin .../assets/img/flags/Islamic Conference.png | Bin .../assets/img/flags/Isle of Man.png | Bin .../CmsEngine.Ui}/assets/img/flags/Israel.png | Bin .../CmsEngine.Ui}/assets/img/flags/Italy.png | Bin .../assets/img/flags/Jamaica.png | Bin .../CmsEngine.Ui}/assets/img/flags/Japan.png | Bin .../CmsEngine.Ui}/assets/img/flags/Jersey.png | Bin .../CmsEngine.Ui}/assets/img/flags/Jordan.png | Bin .../assets/img/flags/Kazakhstan.png | Bin .../CmsEngine.Ui}/assets/img/flags/Kenya.png | Bin .../assets/img/flags/Kiribati.png | Bin .../CmsEngine.Ui}/assets/img/flags/Kosovo.png | Bin .../CmsEngine.Ui}/assets/img/flags/Kuwait.png | Bin .../assets/img/flags/Kyrgyzstan.png | Bin .../CmsEngine.Ui}/assets/img/flags/Laos.png | Bin .../CmsEngine.Ui}/assets/img/flags/Latvia.png | Bin .../assets/img/flags/Lebanon.png | Bin .../assets/img/flags/Lesotho.png | Bin .../assets/img/flags/Liberia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Libya.png | Bin .../assets/img/flags/Liechtenshein.png | Bin .../assets/img/flags/Lithuania.png | Bin .../assets/img/flags/Luxembourg.png | Bin .../CmsEngine.Ui}/assets/img/flags/Macao.png | Bin .../assets/img/flags/Macedonia.png | Bin .../assets/img/flags/Madagascar.png | Bin .../CmsEngine.Ui}/assets/img/flags/Malawi.png | Bin .../assets/img/flags/Malaysia.png | Bin .../assets/img/flags/Maldives.png | Bin .../CmsEngine.Ui}/assets/img/flags/Mali.png | Bin .../CmsEngine.Ui}/assets/img/flags/Malta.png | Bin .../assets/img/flags/Marshall Islands.png | Bin .../assets/img/flags/Martinique.png | Bin .../assets/img/flags/Mauritania.png | Bin .../assets/img/flags/Mauritius.png | Bin .../CmsEngine.Ui}/assets/img/flags/Mexico.png | Bin .../assets/img/flags/Micronesia.png | Bin .../assets/img/flags/Moldova.png | Bin .../CmsEngine.Ui}/assets/img/flags/Monaco.png | Bin .../assets/img/flags/Mongolia.png | Bin .../assets/img/flags/Montenegro.png | Bin .../assets/img/flags/Montserrat.png | Bin .../assets/img/flags/Morocco.png | Bin .../assets/img/flags/Mozambique.png | Bin .../assets/img/flags/Myanmar(Burma).png | Bin .../CmsEngine.Ui}/assets/img/flags/NATO.png | Bin .../assets/img/flags/Namibia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Nauru.png | Bin .../CmsEngine.Ui}/assets/img/flags/Nepal.png | Bin .../assets/img/flags/Netherlands Antilles.png | Bin .../assets/img/flags/Netherlands.png | Bin .../assets/img/flags/New Caledonia.png | Bin .../assets/img/flags/New Zealand.png | Bin .../assets/img/flags/Nicaragua.png | Bin .../CmsEngine.Ui}/assets/img/flags/Niger.png | Bin .../assets/img/flags/Nigeria.png | Bin .../assets/img/flags/North Korea.png | Bin .../assets/img/flags/Northern Cyprus.png | Bin .../assets/img/flags/Northern Ireland.png | Bin .../CmsEngine.Ui}/assets/img/flags/Norway.png | Bin .../CmsEngine.Ui}/assets/img/flags/OPEC.png | Bin .../assets/img/flags/Olimpic Movement.png | Bin .../CmsEngine.Ui}/assets/img/flags/Oman.png | Bin .../assets/img/flags/Pakistan.png | Bin .../CmsEngine.Ui}/assets/img/flags/Palau.png | Bin .../assets/img/flags/Palestine.png | Bin .../CmsEngine.Ui}/assets/img/flags/Panama.png | Bin .../assets/img/flags/Papua New Guinea.png | Bin .../assets/img/flags/Paraguay.png | Bin .../CmsEngine.Ui}/assets/img/flags/Peru.png | Bin .../assets/img/flags/Philippines.png | Bin .../CmsEngine.Ui}/assets/img/flags/Poland.png | Bin .../assets/img/flags/Portugal.png | Bin .../assets/img/flags/Puerto Rico.png | Bin .../CmsEngine.Ui}/assets/img/flags/Qatar.png | Bin .../assets/img/flags/Red Cross.png | Bin .../assets/img/flags/Reunion.png | Bin .../assets/img/flags/Romania.png | Bin .../CmsEngine.Ui}/assets/img/flags/Russia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Rwanda.png | Bin .../assets/img/flags/Saint Lucia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Samoa.png | Bin .../assets/img/flags/San Marino.png | Bin .../assets/img/flags/Sao Tome & Principe.png | Bin .../assets/img/flags/Saudi Arabia.png | Bin .../assets/img/flags/Scotland.png | Bin .../assets/img/flags/Senegal.png | Bin .../assets/img/flags/Serbia(Yugoslavia).png | Bin .../assets/img/flags/Seychelles.png | Bin .../assets/img/flags/Sierra Leone.png | Bin .../assets/img/flags/Singapore.png | Bin .../assets/img/flags/Slovakia.png | Bin .../assets/img/flags/Slovenia.png | Bin .../assets/img/flags/Solomon Islands.png | Bin .../assets/img/flags/Somalia.png | Bin .../assets/img/flags/Somaliland.png | Bin .../assets/img/flags/South Africa.png | Bin .../assets/img/flags/South Korea.png | Bin .../CmsEngine.Ui}/assets/img/flags/Spain.png | Bin .../assets/img/flags/Sri Lanka.png | Bin .../assets/img/flags/St Kitts & Nevis.png | Bin .../img/flags/St Vincent & the Grenadines.png | Bin .../CmsEngine.Ui}/assets/img/flags/Sudan.png | Bin .../assets/img/flags/Suriname.png | Bin .../assets/img/flags/Swaziland.png | Bin .../CmsEngine.Ui}/assets/img/flags/Sweden.png | Bin .../assets/img/flags/Switzerland.png | Bin .../CmsEngine.Ui}/assets/img/flags/Syria.png | Bin .../img/flags/Tahiti(French Polinesia).png | Bin .../CmsEngine.Ui}/assets/img/flags/Taiwan.png | Bin .../assets/img/flags/Tajikistan.png | Bin .../assets/img/flags/Tanzania.png | Bin .../assets/img/flags/Thailand.png | Bin .../assets/img/flags/Timor-Leste.png | Bin .../CmsEngine.Ui}/assets/img/flags/Togo.png | Bin .../CmsEngine.Ui}/assets/img/flags/Tonga.png | Bin .../assets/img/flags/Trinidad & Tobago.png | Bin .../assets/img/flags/Tunisia.png | Bin .../CmsEngine.Ui}/assets/img/flags/Turkey.png | Bin .../assets/img/flags/Turkmenistan.png | Bin .../img/flags/Turks and Caicos Islands.png | Bin .../CmsEngine.Ui}/assets/img/flags/Tuvalu.png | Bin .../CmsEngine.Ui}/assets/img/flags/USA.png | Bin .../CmsEngine.Ui}/assets/img/flags/Uganda.png | Bin .../assets/img/flags/Ukraine.png | Bin .../assets/img/flags/United Arab Emirates.png | Bin .../assets/img/flags/United Nations.png | Bin .../assets/img/flags/United-Kingdom.png | Bin .../assets/img/flags/Uruguay.png | Bin .../assets/img/flags/Uzbekistan.png | Bin .../assets/img/flags/Vanutau.png | Bin .../assets/img/flags/Vatican City.png | Bin .../assets/img/flags/Venezuela.png | Bin .../assets/img/flags/Viet Nam.png | Bin .../img/flags/Virgin Islands British.png | Bin .../assets/img/flags/Virgin Islands US.png | Bin .../CmsEngine.Ui}/assets/img/flags/Wales.png | Bin .../assets/img/flags/Western Sahara.png | Bin .../CmsEngine.Ui}/assets/img/flags/Yemen.png | Bin .../CmsEngine.Ui}/assets/img/flags/Zambia.png | Bin .../assets/img/flags/Zimbabwe.png | Bin .../CmsEngine.Ui}/assets/img/logo-symbol.png | Bin .../CmsEngine.Ui}/assets/img/logo.png | Bin .../CmsEngine.Ui}/assets/img/no-image.png | Bin .../assets/js/admin/0-configure.js | 0 .../CmsEngine.Ui}/assets/js/admin/1-dialog.js | 0 .../assets/js/admin/2-dialog-events.js | 0 .../CmsEngine.Ui}/assets/js/admin/3-utils.js | 0 .../assets/js/admin/4-navigation.js | 0 .../assets/js/admin/5-file-upload.js | 0 .../CmsEngine.Ui}/assets/js/admin/99-init.js | 0 .../CmsEngine.Ui}/assets/js/site/front.js | 0 .../CmsEngine.Ui}/assets/js/site/prism.js | 0 .../scss/admin/_bootstrap-variables.scss | 0 .../assets/scss/admin/_core-variables.scss | 0 .../assets/scss/admin/_custom.scss | 0 .../assets/scss/admin/admin.scss | 0 .../assets/scss/admin/core/_animate.scss | 0 .../assets/scss/admin/core/_aside.scss | 0 .../assets/scss/admin/core/_avatars.scss | 0 .../assets/scss/admin/core/_badge.scss | 0 .../scss/admin/core/_breadcrumb-menu.scss | 0 .../assets/scss/admin/core/_breadcrumb.scss | 0 .../assets/scss/admin/core/_buttons.scss | 0 .../assets/scss/admin/core/_callout.scss | 0 .../assets/scss/admin/core/_card.scss | 0 .../assets/scss/admin/core/_charts.scss | 0 .../scss/admin/core/_dropdown-menu-right.scss | 0 .../assets/scss/admin/core/_dropdown.scss | 0 .../assets/scss/admin/core/_footer.scss | 0 .../assets/scss/admin/core/_grid.scss | 0 .../assets/scss/admin/core/_input-group.scss | 0 .../assets/scss/admin/core/_layout.scss | 0 .../assets/scss/admin/core/_loading.scss | 0 .../assets/scss/admin/core/_mixins.scss | 0 .../assets/scss/admin/core/_mobile.scss | 0 .../assets/scss/admin/core/_modal.scss | 0 .../assets/scss/admin/core/_nav.scss | 0 .../assets/scss/admin/core/_navbar.scss | 0 .../assets/scss/admin/core/_others.scss | 0 .../assets/scss/admin/core/_progress.scss | 0 .../assets/scss/admin/core/_rtl.scss | 0 .../assets/scss/admin/core/_sidebar.scss | 0 .../assets/scss/admin/core/_switches.scss | 0 .../assets/scss/admin/core/_tables.scss | 0 .../assets/scss/admin/core/_temp.scss | 0 .../assets/scss/admin/core/_typography.scss | 0 .../assets/scss/admin/core/_utilities.scss | 0 .../assets/scss/admin/core/_variables.scss | 0 .../assets/scss/admin/core/_widgets.scss | 0 .../assets/scss/admin/core/core.scss | 0 .../admin/core/utilities/_background.scss | 0 .../scss/admin/core/utilities/_borders.scss | 0 .../scss/admin/core/utilities/_display.scss | 0 .../assets/scss/admin/vendors/_variables.scss | 0 .../scss/admin/vendors/chart.js/chart.scss | 0 .../assets/scss/site/fontastic.scss | 0 .../CmsEngine.Ui}/assets/scss/site/prism.scss | 0 .../CmsEngine.Ui}/assets/scss/site/site.scss | 0 .../assets/scss/site/style.blue.scss | 0 .../assets/scss/site/style.green.scss | 0 .../assets/scss/site/style.red.scss | 0 .../emailsettings.development.json | 23 ++ .../CmsEngine.Ui}/emailsettings.json | 0 .../CmsEngine.Ui}/gulp-tasks/build-vendors.js | 0 .../CmsEngine.Ui}/gulpfile.js | 0 .../CmsEngine.Ui}/package.json | 0 800 files changed, 8953 insertions(+), 9955 deletions(-) delete mode 100644 CmsEngine.Application/Attributes/Orderable.cs delete mode 100644 CmsEngine.Application/Attributes/Searchable.cs delete mode 100644 CmsEngine.Application/Attributes/ShowOnDataTable.cs delete mode 100644 CmsEngine.Application/CmsEngine.Application.csproj delete mode 100644 CmsEngine.Application/Extensions/EnumerableExtensions.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/PageExtensions.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/PostExtensions.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/TagExtensions.cs delete mode 100644 CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs delete mode 100644 CmsEngine.Application/Helpers/DataTableHelper.cs delete mode 100644 CmsEngine.Application/Helpers/Email/ContactForm.cs delete mode 100644 CmsEngine.Application/Helpers/Email/EmailSender.cs delete mode 100644 CmsEngine.Application/Helpers/Email/EmailSettings.cs delete mode 100644 CmsEngine.Application/Helpers/Email/IEmailSender.cs delete mode 100644 CmsEngine.Application/Helpers/ExpressionBuilder.cs delete mode 100644 CmsEngine.Application/Helpers/FileHelper.cs delete mode 100644 CmsEngine.Application/Models/EditModels/BaseEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/CategoryEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/IEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/PageEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/PostEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/TagEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/UserEditModel.cs delete mode 100644 CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/BaseViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/IViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/PageViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/PostViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/TagViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/UserViewModel.cs delete mode 100644 CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs delete mode 100644 CmsEngine.Application/Services/CategoryService.cs delete mode 100644 CmsEngine.Application/Services/EmailService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/ICategoryService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IDataTableService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IEmailService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IPageService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IPostService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/ITagService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IWebsiteService.cs delete mode 100644 CmsEngine.Application/Services/Interfaces/IXmlService.cs delete mode 100644 CmsEngine.Application/Services/PageService.cs delete mode 100644 CmsEngine.Application/Services/PostService.cs delete mode 100644 CmsEngine.Application/Services/Service.cs delete mode 100644 CmsEngine.Application/Services/TagService.cs delete mode 100644 CmsEngine.Application/Services/WebsiteService.cs delete mode 100644 CmsEngine.Application/Services/XmlService.cs delete mode 100644 CmsEngine.Core/CmsEngine.Core.csproj delete mode 100644 CmsEngine.Core/Constants/CmsEngineConstants.cs delete mode 100644 CmsEngine.Core/Constants/ContentTypeOptionsConstants.cs delete mode 100644 CmsEngine.Core/Constants/FrameOptionsConstants.cs delete mode 100644 CmsEngine.Core/Constants/MessageConstants.cs delete mode 100644 CmsEngine.Core/Constants/ServerConstants.cs delete mode 100644 CmsEngine.Core/Constants/StrictTransportSecurityConstants.cs delete mode 100644 CmsEngine.Core/Constants/XssProtectionConstants.cs delete mode 100644 CmsEngine.Core/Exceptions/EmailException.cs delete mode 100644 CmsEngine.Core/Exceptions/NotFoundException.cs delete mode 100644 CmsEngine.Core/Extensions/BooleanExtensions.cs delete mode 100644 CmsEngine.Core/Extensions/EnumExtensions.cs delete mode 100644 CmsEngine.Core/Extensions/IEnumerableExtensions.cs delete mode 100644 CmsEngine.Core/Extensions/ObjectExtensions.cs delete mode 100644 CmsEngine.Core/Extensions/StringExtensions.cs delete mode 100644 CmsEngine.Core/Utils/CustomResolver.cs delete mode 100644 CmsEngine.Core/Utils/Enums.cs delete mode 100644 CmsEngine.Core/Utils/GravatarUtilities.cs delete mode 100644 CmsEngine.Core/Utils/Guard.cs delete mode 100644 CmsEngine.Core/Utils/PaginatedList.cs delete mode 100644 CmsEngine.Core/Utils/ReturnValue.cs delete mode 100644 CmsEngine.Core/Utils/TinyMceUploadResult.cs delete mode 100644 CmsEngine.Core/Utils/UploadFilesResult.cs delete mode 100644 CmsEngine.Data/CmsEngineContext.cs delete mode 100644 CmsEngine.Data/Entities/ApplicationUser.cs delete mode 100644 CmsEngine.Data/Entities/BaseEntity.cs delete mode 100644 CmsEngine.Data/Entities/Category.cs delete mode 100644 CmsEngine.Data/Entities/Document.cs delete mode 100644 CmsEngine.Data/Entities/Email.cs delete mode 100644 CmsEngine.Data/Entities/IModel.cs delete mode 100644 CmsEngine.Data/Entities/ModelConfiguration.cs delete mode 100644 CmsEngine.Data/Entities/Page.cs delete mode 100644 CmsEngine.Data/Entities/PageApplicationUser.cs delete mode 100644 CmsEngine.Data/Entities/Post.cs delete mode 100644 CmsEngine.Data/Entities/PostApplicationUser.cs delete mode 100644 CmsEngine.Data/Entities/PostCategory.cs delete mode 100644 CmsEngine.Data/Entities/PostTag.cs delete mode 100644 CmsEngine.Data/Entities/Tag.cs delete mode 100644 CmsEngine.Data/Entities/Website.cs delete mode 100644 CmsEngine.Data/Extensions/DbContextExtensions.cs delete mode 100644 CmsEngine.Data/Extensions/ModelBuilderExtensions.cs delete mode 100644 CmsEngine.Data/IUnitOfWork.cs delete mode 100644 CmsEngine.Data/Repositories/CategoryRepository.cs delete mode 100644 CmsEngine.Data/Repositories/EmailRepository.cs delete mode 100644 CmsEngine.Data/Repositories/ICategoryRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IDataModificationRangeRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IDataModificationRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IEmailRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IPageRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IPostRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IReadRepository.cs delete mode 100644 CmsEngine.Data/Repositories/ITagRepository.cs delete mode 100644 CmsEngine.Data/Repositories/IWebsiteRepository.cs delete mode 100644 CmsEngine.Data/Repositories/PageRepository.cs delete mode 100644 CmsEngine.Data/Repositories/PostRepository.cs delete mode 100644 CmsEngine.Data/Repositories/Repository.cs delete mode 100644 CmsEngine.Data/Repositories/TagRepository.cs delete mode 100644 CmsEngine.Data/Repositories/WebsiteRepository.cs delete mode 100644 CmsEngine.Data/UnitOfWork.cs delete mode 100644 CmsEngine.Ui/.config/dotnet-tools.json delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs delete mode 100644 CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs delete mode 100644 CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs delete mode 100644 CmsEngine.Ui/CmsEngine.Ui.csproj delete mode 100644 CmsEngine.Ui/Controllers/BaseController.cs delete mode 100644 CmsEngine.Ui/Controllers/BlogController.cs delete mode 100644 CmsEngine.Ui/Controllers/ErrorController.cs delete mode 100644 CmsEngine.Ui/Controllers/HomeController.cs delete mode 100644 CmsEngine.Ui/Extensions/EmailSenderExtensions.cs delete mode 100644 CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs delete mode 100644 CmsEngine.Ui/GlobalSuppressions.cs delete mode 100644 CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs delete mode 100644 CmsEngine.Ui/Middleware/MiddlewareExtensions.cs delete mode 100644 CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs delete mode 100644 CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs delete mode 100644 CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs delete mode 100644 CmsEngine.Ui/Program.cs delete mode 100644 CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs delete mode 100644 CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs delete mode 100644 CmsEngine.Ui/Startup.cs delete mode 100644 CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs delete mode 100644 CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs delete mode 100644 CmsEngine.Ui/certificate.json create mode 100644 src/CmsEngine.Application/Attributes/Orderable.cs create mode 100644 src/CmsEngine.Application/Attributes/Searchable.cs create mode 100644 src/CmsEngine.Application/Attributes/ShowOnDataTable.cs create mode 100644 src/CmsEngine.Application/CmsEngine.Application.csproj create mode 100644 src/CmsEngine.Application/Extensions/EnumerableExtensions.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs create mode 100644 src/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs create mode 100644 src/CmsEngine.Application/GlobalUsings.cs create mode 100644 src/CmsEngine.Application/Helpers/DataTableHelper.cs create mode 100644 src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs create mode 100644 src/CmsEngine.Application/Helpers/Email/ContactForm.cs create mode 100644 src/CmsEngine.Application/Helpers/Email/EmailSettings.cs create mode 100644 src/CmsEngine.Application/Helpers/Email/ICmsEngineEmailSender.cs create mode 100644 src/CmsEngine.Application/Helpers/ExpressionBuilder.cs create mode 100644 src/CmsEngine.Application/Helpers/ExpressionFilter.cs create mode 100644 src/CmsEngine.Application/Helpers/FileHelper.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/BaseEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/IEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/PageEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/PostEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/TagEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/UserEditModel.cs create mode 100644 src/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/IViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/PageViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/PostViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/TagViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/UserViewModel.cs create mode 100644 src/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs create mode 100644 src/CmsEngine.Application/Services/CategoryService.cs create mode 100644 src/CmsEngine.Application/Services/EmailService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/ICategoryService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IDataTableService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IEmailService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IPageService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IPostService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/ITagService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs create mode 100644 src/CmsEngine.Application/Services/Interfaces/IXmlService.cs create mode 100644 src/CmsEngine.Application/Services/PageService.cs create mode 100644 src/CmsEngine.Application/Services/PostService.cs create mode 100644 src/CmsEngine.Application/Services/Service.cs create mode 100644 src/CmsEngine.Application/Services/TagService.cs create mode 100644 src/CmsEngine.Application/Services/WebsiteService.cs create mode 100644 src/CmsEngine.Application/Services/XmlService.cs create mode 100644 src/CmsEngine.Core/CmsEngine.Core.csproj create mode 100644 src/CmsEngine.Core/Constants/ContentTypeOptions.cs create mode 100644 src/CmsEngine.Core/Constants/FrameOptions.cs create mode 100644 src/CmsEngine.Core/Constants/Main.cs create mode 100644 src/CmsEngine.Core/Constants/Message.cs create mode 100644 src/CmsEngine.Core/Constants/Server.cs create mode 100644 src/CmsEngine.Core/Constants/StrictTransportSecurity.cs create mode 100644 src/CmsEngine.Core/Constants/XssProtection.cs create mode 100644 src/CmsEngine.Core/Exceptions/EmailException.cs create mode 100644 src/CmsEngine.Core/Exceptions/NotFoundException.cs create mode 100644 src/CmsEngine.Core/Extensions/BooleanExtensions.cs create mode 100644 src/CmsEngine.Core/Extensions/EnumExtensions.cs create mode 100644 src/CmsEngine.Core/Extensions/IEnumerableExtension.cs create mode 100644 src/CmsEngine.Core/Extensions/StringExtensions.cs create mode 100644 src/CmsEngine.Core/GlobalUsings.cs create mode 100644 src/CmsEngine.Core/Utils/CustomResolver.cs create mode 100644 src/CmsEngine.Core/Utils/Enums.cs create mode 100644 src/CmsEngine.Core/Utils/GravatarUtilities.cs create mode 100644 src/CmsEngine.Core/Utils/PaginatedList.cs create mode 100644 src/CmsEngine.Core/Utils/ReturnValue.cs create mode 100644 src/CmsEngine.Core/Utils/TinyMceUploadResult.cs create mode 100644 src/CmsEngine.Core/Utils/UploadFilesResult.cs rename {CmsEngine.Data => src/CmsEngine.Data}/CmsEngine.Data.csproj (53%) create mode 100644 src/CmsEngine.Data/CmsEngineContext.cs create mode 100644 src/CmsEngine.Data/Entities/ApplicationUser.cs create mode 100644 src/CmsEngine.Data/Entities/BaseEntity.cs create mode 100644 src/CmsEngine.Data/Entities/Category.cs create mode 100644 src/CmsEngine.Data/Entities/Document.cs create mode 100644 src/CmsEngine.Data/Entities/Email.cs create mode 100644 src/CmsEngine.Data/Entities/IModel.cs create mode 100644 src/CmsEngine.Data/Entities/ModelConfiguration.cs create mode 100644 src/CmsEngine.Data/Entities/Page.cs create mode 100644 src/CmsEngine.Data/Entities/PageApplicationUser.cs create mode 100644 src/CmsEngine.Data/Entities/Post.cs create mode 100644 src/CmsEngine.Data/Entities/PostApplicationUser.cs create mode 100644 src/CmsEngine.Data/Entities/PostCategory.cs create mode 100644 src/CmsEngine.Data/Entities/PostTag.cs create mode 100644 src/CmsEngine.Data/Entities/Tag.cs create mode 100644 src/CmsEngine.Data/Entities/Website.cs create mode 100644 src/CmsEngine.Data/Extensions/DbContextExtensions.cs create mode 100644 src/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs create mode 100644 src/CmsEngine.Data/GlobalUsings.cs create mode 100644 src/CmsEngine.Data/IUnitOfWork.cs rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20181231150109_InitialCreate.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20181231150109_InitialCreate.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20190211194130_DisqusShortName.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20190211194130_DisqusShortName.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20190422093305_Added_GoogleAnalytics.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20190422093305_Added_GoogleAnalytics.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20191020174931_GoogleRecaptchaFields.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20191020174931_GoogleRecaptchaFields.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20191020192050_IncreaseCategoryNameSize.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20191020192050_IncreaseCategoryNameSize.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20191020221230_AddingEmailTable.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20191020221230_AddingEmailTable.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20200410203904_ShadowPropertiesForAuditing.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20200410203904_ShadowPropertiesForAuditing.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20201212123624_IncreasedSizeForAuditingUserName.Designer.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/20201212123624_IncreasedSizeForAuditingUserName.cs (100%) rename {CmsEngine.Data => src/CmsEngine.Data}/Migrations/CmsEngineContextModelSnapshot.cs (100%) create mode 100644 src/CmsEngine.Data/Repositories/CategoryRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/EmailRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/ICategoryRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRangeRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IEmailRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IPageRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IPostRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IReadRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/ITagRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Interfaces/IWebsiteRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/PageRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/PostRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/Repository.cs create mode 100644 src/CmsEngine.Data/Repositories/TagRepository.cs create mode 100644 src/CmsEngine.Data/Repositories/WebsiteRepository.cs create mode 100644 src/CmsEngine.Data/UnitOfWork.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs create mode 100644 src/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Category/CreateEdit.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Category/List.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Home/Index.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Page/CreateEdit.cshtml (98%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Page/List.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Post/CreateEdit.cshtml (98%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Post/List.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Shared/Error.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Shared/_DataTablesScripts.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Shared/_DataTablesStyles.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Shared/_LoginPartial.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Shared/_ValidationScriptsPartial.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Tag/CreateEdit.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Tag/List.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Website/CreateEdit.cshtml (99%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/Website/List.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Cms/Views/_ViewStart.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/AccessDenied.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ConfirmEmail.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ExternalLogin.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ForgotPassword.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Lockout.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Lockout.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Login.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/LoginWith2fa.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Logout.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/Index.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/_Layout.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/Register.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ResetPassword.cshtml (100%) create mode 100644 src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Account/_ViewImports.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Error.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/Error.cshtml.cs (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/_ViewImports.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Areas/Identity/Pages/_ViewStart.cshtml (100%) create mode 100644 src/CmsEngine.Ui/CmsEngine.Ui.csproj create mode 100644 src/CmsEngine.Ui/Controllers/BaseController.cs create mode 100644 src/CmsEngine.Ui/Controllers/BlogController.cs create mode 100644 src/CmsEngine.Ui/Controllers/ErrorController.cs create mode 100644 src/CmsEngine.Ui/Controllers/HomeController.cs create mode 100644 src/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs create mode 100644 src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs create mode 100644 src/CmsEngine.Ui/Extensions/MiddlewareExtensions.cs create mode 100644 src/CmsEngine.Ui/GlobalUsings.cs create mode 100644 src/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs create mode 100644 src/CmsEngine.Ui/Middleware/FileUploadOptions.cs create mode 100644 src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs create mode 100644 src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs create mode 100644 src/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs create mode 100644 src/CmsEngine.Ui/Program.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Properties/launchSettings.json (67%) create mode 100644 src/CmsEngine.Ui/Properties/serviceDependencies.json create mode 100644 src/CmsEngine.Ui/Properties/serviceDependencies.local.json create mode 100644 src/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs create mode 100644 src/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs create mode 100644 src/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs create mode 100644 src/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Blog/Index.cshtml (96%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Blog/Post.cshtml (95%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Blog/_PagedPost.cshtml (93%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Error/Index.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Home/Archive.cshtml (96%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Home/Contact.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Home/Index.cshtml (85%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Home/Page.cshtml (95%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Home/Privacy.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Home/_PostIndex.cshtml (94%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/Cms/_Dialog.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/Cms/_ExternalLayout.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/Cms/_Layout.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/EditorTemplates/DateTime.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_CookieConsentPartial.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_Footer.cshtml (98%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_Layout.cshtml (99%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_LoginPartial.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_Messages.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_SearchForm.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_Sidebar.cshtml (96%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/Shared/_ValidationScriptsPartial.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/_ViewImports.cshtml (68%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/Views/_ViewStart.cshtml (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/appsettings.Development.json (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/appsettings.json (71%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/fonts/blog.eot (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/fonts/blog.svg (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/fonts/blog.ttf (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/fonts/blog.woff (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/favicon.ico (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/favicon.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/ASEAN.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Afghanistan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/African Union.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Albania.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Algeria.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/American Samoa.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Andorra.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Angola.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Anguilla.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Antarctica.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Antigua & Barbuda.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Arab League.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Argentina.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Armenia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Aruba.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Australia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Austria.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Azerbaijan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bahamas.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bahrain.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bangladesh.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Barbados.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Belarus.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Belgium.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Belize.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Benin.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bermuda.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bhutan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bolivia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bosnia & Herzegovina.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Botswana.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Brazil.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Brunei.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Bulgaria.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Burkina Faso.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Burundi.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/CARICOM.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/CIS.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cambodja.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cameroon.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Canada.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cape Verde.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cayman Islands.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Central African Republic.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Chad.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Chile.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/China.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Colombia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Commonwealth.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Comoros.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Congo-Brazzaville.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Congo-Kinshasa(Zaire).png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cook Islands.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Costa Rica.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cote d'Ivoire.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Croatia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cuba.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Cyprus.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Czech Republic.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Denmark.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Djibouti.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Dominica.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Dominican Republic.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Ecuador.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Egypt.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/El Salvador.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/England.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Equatorial Guinea.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Eritrea.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Estonia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Ethiopia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/European Union.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Faroes.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Fiji.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Finland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/France.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Gabon.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Gambia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Georgia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Germany.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Ghana.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Gibraltar.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Greece.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Greenland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Grenada.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guadeloupe.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guam.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guatemala.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guernsey.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guinea-Bissau.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guinea.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Guyana.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Haiti.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Honduras.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Hong Kong.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Hungary.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Iceland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/India.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Indonezia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Iran.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Iraq.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Ireland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Islamic Conference.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Isle of Man.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Israel.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Italy.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Jamaica.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Japan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Jersey.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Jordan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Kazakhstan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Kenya.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Kiribati.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Kosovo.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Kuwait.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Kyrgyzstan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Laos.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Latvia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Lebanon.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Lesotho.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Liberia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Libya.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Liechtenshein.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Lithuania.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Luxembourg.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Macao.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Macedonia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Madagascar.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Malawi.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Malaysia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Maldives.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Mali.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Malta.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Marshall Islands.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Martinique.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Mauritania.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Mauritius.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Mexico.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Micronesia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Moldova.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Monaco.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Mongolia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Montenegro.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Montserrat.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Morocco.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Mozambique.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Myanmar(Burma).png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/NATO.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Namibia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Nauru.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Nepal.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Netherlands Antilles.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Netherlands.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/New Caledonia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/New Zealand.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Nicaragua.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Niger.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Nigeria.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/North Korea.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Northern Cyprus.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Northern Ireland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Norway.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/OPEC.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Olimpic Movement.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Oman.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Pakistan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Palau.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Palestine.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Panama.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Papua New Guinea.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Paraguay.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Peru.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Philippines.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Poland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Portugal.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Puerto Rico.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Qatar.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Red Cross.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Reunion.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Romania.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Russia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Rwanda.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Saint Lucia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Samoa.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/San Marino.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Sao Tome & Principe.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Saudi Arabia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Scotland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Senegal.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Serbia(Yugoslavia).png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Seychelles.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Sierra Leone.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Singapore.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Slovakia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Slovenia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Solomon Islands.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Somalia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Somaliland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/South Africa.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/South Korea.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Spain.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Sri Lanka.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/St Kitts & Nevis.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/St Vincent & the Grenadines.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Sudan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Suriname.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Swaziland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Sweden.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Switzerland.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Syria.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Tahiti(French Polinesia).png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Taiwan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Tajikistan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Tanzania.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Thailand.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Timor-Leste.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Togo.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Tonga.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Trinidad & Tobago.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Tunisia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Turkey.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Turkmenistan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Turks and Caicos Islands.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Tuvalu.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/USA.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Uganda.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Ukraine.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/United Arab Emirates.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/United Nations.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/United-Kingdom.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Uruguay.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Uzbekistan.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Vanutau.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Vatican City.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Venezuela.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Viet Nam.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Virgin Islands British.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Virgin Islands US.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Wales.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Western Sahara.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Yemen.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Zambia.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/flags/Zimbabwe.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/logo-symbol.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/logo.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/img/no-image.png (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/0-configure.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/1-dialog.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/2-dialog-events.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/3-utils.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/4-navigation.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/5-file-upload.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/admin/99-init.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/site/front.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/js/site/prism.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/_bootstrap-variables.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/_core-variables.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/_custom.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/admin.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_animate.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_aside.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_avatars.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_badge.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_breadcrumb-menu.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_breadcrumb.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_buttons.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_callout.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_card.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_charts.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_dropdown-menu-right.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_dropdown.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_footer.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_grid.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_input-group.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_layout.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_loading.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_mixins.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_mobile.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_modal.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_nav.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_navbar.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_others.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_progress.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_rtl.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_sidebar.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_switches.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_tables.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_temp.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_typography.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_utilities.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_variables.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/_widgets.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/core.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/utilities/_background.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/utilities/_borders.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/core/utilities/_display.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/vendors/_variables.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/admin/vendors/chart.js/chart.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/site/fontastic.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/site/prism.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/site/site.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/site/style.blue.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/site/style.green.scss (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/assets/scss/site/style.red.scss (100%) create mode 100644 src/CmsEngine.Ui/emailsettings.development.json rename {CmsEngine.Ui => src/CmsEngine.Ui}/emailsettings.json (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/gulp-tasks/build-vendors.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/gulpfile.js (100%) rename {CmsEngine.Ui => src/CmsEngine.Ui}/package.json (100%) diff --git a/.editorconfig b/.editorconfig index 8947b9f9..d5beeea7 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,123 +1,216 @@ -# top-most EditorConfig file -root = true - -# Don't use tabs for indentation. -[*] -end_of_line = crlf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -# (Please don't specify an indent_size here; that has too many unintended consequences.) - -# Code files -[*.{cs,csx,vb,vbx}] -indent_size = 4 +# Rules in this file were initially inferred by Visual Studio IntelliCode from the D:\Repos\Personal\CmsEngine codebase based on best match to current usage at 2/19/2022 +# There already existed an .editorconfig file in this directory. Copy rules from this .editorconfig.inferred file to the existing .editorconfig file as desired to have them take effect at this location. +# You can modify the rules from these initially generated values to suit your own policies +# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference +[*.cs] -# Xml project files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] -indent_size = 2 +csharp_style_namespace_declarations = file_scoped:warning -# Xml config files -[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] -indent_size = 2 +#Core editorconfig formatting - indentation -# Dotnet code style settings: -[*.{cs,vb}] -# Sort using and Import directives with System.* appearing first -dotnet_sort_system_directives_first = true +#use soft tabs (spaces) for indentation +indent_style = space -# Async methods to be sufixed with Async -dotnet_naming_rule.async_methods_must_end_with_async.severity = warning -dotnet_naming_rule.async_methods_must_end_with_async.symbols = method_symbols -dotnet_naming_rule.async_methods_must_end_with_async.style = end_in_async_style +#Formatting - indentation options -dotnet_naming_symbols.method_symbols.applicable_kinds = method -dotnet_naming_symbols.method_symbols.required_modifiers = async +#indent switch case contents. +csharp_indent_case_contents = true +#indent switch labels +csharp_indent_switch_labels = true -dotnet_naming_style.end_in_async_style.capitalization = pascal_case -dotnet_naming_style.end_in_async_style.required_suffix = Async +#Formatting - new line options -# Name all constant fields using PascalCase -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = warning -dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +#place catch statements on a new line +csharp_new_line_before_catch = true +#place else statements on a new line +csharp_new_line_before_else = true +#require members of anonymous types to be on the same line +csharp_new_line_before_members_in_anonymous_types = false +#require members of object intializers to be on separate lines +csharp_new_line_before_members_in_object_initializers = true +#require braces to be on a new line for object_collection_array_initializers, methods, control_blocks, types, and lambdas (also known as "Allman" style) +csharp_new_line_before_open_brace = object_collection_array_initializers, methods, control_blocks, types, lambdas + +#Formatting - organize using options + +#sort System.* using directives alphabetically, and place them before other usings +dotnet_sort_system_directives_first = true + +#Formatting - spacing options + +#require NO space between a cast and the value +csharp_space_after_cast = false +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_after_colon_in_inheritance_clause = true +#require a space after a keyword in a control flow statement such as a for loop +csharp_space_after_keywords_in_control_flow_statements = true +#require a space before the colon for bases or interfaces in a type declaration +csharp_space_before_colon_in_inheritance_clause = true +#remove space within empty argument list parentheses +csharp_space_between_method_call_empty_parameter_list_parentheses = false +#remove space between method call name and opening parenthesis +csharp_space_between_method_call_name_and_opening_parenthesis = false +#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call +csharp_space_between_method_call_parameter_list_parentheses = false +#remove space within empty parameter list parentheses for a method declaration +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list. +csharp_space_between_method_declaration_parameter_list_parentheses = false + +#Formatting - wrapping options + +#leave code block on single line +csharp_preserve_single_line_blocks = true + +#Style - Code block preferences + +#prefer curly braces even for one line of code +csharp_prefer_braces = true:suggestion + +#Style - expression bodied member options + +#prefer block bodies for constructors +csharp_style_expression_bodied_constructors = false:suggestion +#prefer block bodies for methods +csharp_style_expression_bodied_methods = false:suggestion + +#Style - expression level options + +#prefer out variables to be declared before the method call +csharp_style_inlined_variable_declaration = false:suggestion +#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_member_access = true:suggestion + +#Style - Expression-level preferences + +#prefer objects to be initialized using object initializers when possible +dotnet_style_object_initializer = true:suggestion +#prefer inferred anonymous type member names +dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion +#prefer inferred tuple element names +dotnet_style_prefer_inferred_tuple_names = true:suggestion + +#Style - implicit and explicit types + +#prefer var over explicit type in all cases, unless overridden by another code style rule +csharp_style_var_elsewhere = true:suggestion +#prefer var is used to declare variables with built-in system types such as int +csharp_style_var_for_built_in_types = true:suggestion +#prefer var when the type is already mentioned on the right-hand side of a declaration expression +csharp_style_var_when_type_is_apparent = true:suggestion + +#Style - language keyword and framework type options + +#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +#Style - modifier options + +#prefer accessibility modifiers to be declared except for public interface members. This will currently not differ from always and will act as future proofing for if C# adds default interface methods. +dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion + +#Style - Modifier preferences + +#when this rule is set to a list of modifiers, prefer the specified ordering. +csharp_preferred_modifier_order = public,private,protected,async,static,override,readonly:suggestion + +#Style - qualification options + +#prefer fields not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_field = false:suggestion +#prefer methods not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_method = false:suggestion +#prefer properties not to be prefaced with this. or Me. in Visual Basic +dotnet_style_qualification_for_property = false:suggestion +csharp_indent_labels = one_less_than_current +csharp_space_around_binary_operators = before_and_after +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = false:suggestion +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent -dotnet_naming_symbols.constant_fields.applicable_kinds = field -dotnet_naming_symbols.constant_fields.required_modifiers = const +[*.{cs,vb}] +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +indent_size = 4 +end_of_line = crlf +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:warning +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion +[*.{cs,vb}] +#### Naming styles #### -dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Naming rules -# Static fields should have s_ prefix -dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion -dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields -dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i -dotnet_naming_symbols.static_fields.applicable_kinds = field -dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case -#dotnet_naming_style.static_prefix_style.required_prefix = s_ -dotnet_naming_style.static_prefix_style.capitalization = camel_case +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case -# Internal and private fields should be _camelCase -dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion -dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields -dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +# Symbol specifications -dotnet_naming_symbols.private_internal_fields.applicable_kinds = field -dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = -#dotnet_naming_style.camel_case_underscore_style.required_prefix = _ -dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = -# Avoid "this." and "Me." if not necessary -dotnet_style_qualification_for_field = false : suggestion -dotnet_style_qualification_for_property = false : suggestion -dotnet_style_qualification_for_method = false : suggestion -dotnet_style_qualification_for_event = false : suggestion +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = -# Use language keywords instead of framework type names for type references -dotnet_style_predefined_type_for_locals_parameters_members = true : suggestion -dotnet_style_predefined_type_for_member_access = true : suggestion +# Naming styles -# Suggest more modern language features when available -dotnet_style_object_initializer = true : suggestion -dotnet_style_collection_initializer = true : suggestion -dotnet_style_coalesce_expression = true : suggestion -dotnet_style_null_propagation = true : suggestion -dotnet_style_explicit_tuple_names = true : suggestion +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case -# CSharp code style settings: -[*.cs] -# Only use var when it's obvious what the variable type is -csharp_style_var_for_built_in_types = false:warning -csharp_style_var_when_type_is_apparent = false:none -csharp_style_var_elsewhere = true:warning - -# Prefer method-like constructs to have a block body -csharp_style_expression_bodied_methods = false : suggestion -csharp_style_expression_bodied_constructors = false : suggestion -csharp_style_expression_bodied_operators = false : suggestion - -# Prefer property-like constructs to have an expression-body -csharp_style_expression_bodied_properties = false : suggestion -csharp_style_expression_bodied_indexers = false : suggestion -csharp_style_expression_bodied_accessors = false : suggestion - -# Suggest more modern language features when available -csharp_style_pattern_matching_over_is_with_cast_check = true : suggestion -csharp_style_pattern_matching_over_as_with_null_check = true : suggestion -csharp_style_inlined_variable_declaration = true : suggestion -csharp_style_throw_expression = true : suggestion -csharp_style_conditional_delegate_call = true : suggestion - -# Newline settings -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true -csharp_new_line_before_catch = true -csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true -csharp_new_line_before_members_in_anonymous_types = true +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case -# IDE0042: Deconstruct variable declaration -csharp_style_deconstructed_variable_declaration = false:suggestion +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case diff --git a/CmsEngine.Application/Attributes/Orderable.cs b/CmsEngine.Application/Attributes/Orderable.cs deleted file mode 100644 index d22a98d4..00000000 --- a/CmsEngine.Application/Attributes/Orderable.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace CmsEngine.Application.Attributes -{ - /// - /// Enables the property to be orderable in the Search functionality - /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class Orderable : Attribute - { - - } -} diff --git a/CmsEngine.Application/Attributes/Searchable.cs b/CmsEngine.Application/Attributes/Searchable.cs deleted file mode 100644 index d4351169..00000000 --- a/CmsEngine.Application/Attributes/Searchable.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace CmsEngine.Application.Attributes -{ - /// - /// Enables the property to be searchable in the Search functionality - /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class Searchable : Attribute - { - - } -} diff --git a/CmsEngine.Application/Attributes/ShowOnDataTable.cs b/CmsEngine.Application/Attributes/ShowOnDataTable.cs deleted file mode 100644 index 22a86933..00000000 --- a/CmsEngine.Application/Attributes/ShowOnDataTable.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace CmsEngine.Application.Attributes -{ - /// - /// Enables the properto to be visible in the DataTable - /// - [AttributeUsage(AttributeTargets.Property)] - public sealed class ShowOnDataTable : Attribute - { - public int Order { get; } - - public ShowOnDataTable(int order) - { - Order = order; - } - } -} diff --git a/CmsEngine.Application/CmsEngine.Application.csproj b/CmsEngine.Application/CmsEngine.Application.csproj deleted file mode 100644 index bcaf39db..00000000 --- a/CmsEngine.Application/CmsEngine.Application.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net5.0 - - - - - - - - - - - - - - - diff --git a/CmsEngine.Application/Extensions/EnumerableExtensions.cs b/CmsEngine.Application/Extensions/EnumerableExtensions.cs deleted file mode 100644 index ed5289d0..00000000 --- a/CmsEngine.Application/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.ViewModels; -using CmsEngine.Core; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace CmsEngine.Application.Extensions -{ - public static class EnumerableExtensions - { - public static Expression> GetSearchExpression(this IEnumerable element, string searchValue, IEnumerable properties) - { - var expressionFilter = new List(); - - foreach (var property in properties) - { - expressionFilter.Add(new ExpressionFilter - { - PropertyName = property.Name, - Operation = Operation.Contains, - Value = searchValue - }); - } - - return ExpressionBuilder.GetExpression(expressionFilter, LogicalOperator.Or); - } - - public static IEnumerable PopulateSelectList(this IEnumerable items, IEnumerable selectedItems = null) where T : BaseViewModel - { - return items.Select(x => new SelectListItem - { - Text = x.GetType().GetProperty("Name").GetValue(x).ToString(), - Value = x.VanityId.ToString(), - Disabled = false, - Selected = selectedItems?.Contains(x.VanityId.ToString()) ?? false - }).OrderBy(o => o.Text); - } - - public static IEnumerable PopulateCheckboxList(this IEnumerable items, IEnumerable selectedItems = null) where T : BaseViewModel - { - return items.Select(x => new CheckboxEditModel - { - Label = x.GetType().GetProperty("Name").GetValue(x).ToString(), - Value = x.VanityId.ToString(), - Enabled = true, - Selected = selectedItems?.Contains(x.VanityId.ToString()) ?? false - }).OrderBy(o => o.Label); - } - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs b/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs deleted file mode 100644 index c5744eda..00000000 --- a/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class ApplicationUserExtensions - { - /// - /// Maps ApplicationUser model into a UserEditModel - /// - /// - /// - public static UserEditModel MapToEditModel(this ApplicationUser item) - { - return new UserEditModel - { - VanityId = Guid.Parse(item.Id), - Name = item.Name, - Surname = item.Surname, - Email = item.Email, - UserName = item.UserName - }; - } - - /// - /// Maps ApplicationUser model into a UserViewModel - /// - /// - /// - public static UserViewModel MapToViewModel(this ApplicationUser item) - { - return new UserViewModel - { - VanityId = Guid.Parse(item.Id), - Name = item.Name, - Surname = item.Surname, - Email = item.Email, - UserName = item.UserName - }; - } - - /// - /// Maps Name, Surname, Email and UserName - /// - /// - /// - public static IEnumerable MapToViewModelSimple(this IEnumerable users) - { - var viewModels = new List(); - - foreach (var item in users) - { - viewModels.Add(new UserViewModel - { - Name = item.Name, - Surname = item.Surname, - Email = item.Email, - UserName = item.UserName - }); - } - - return viewModels; - } - - /// - /// Maps a UserEditModel into a ApplicationUser - /// - /// - /// - public static ApplicationUser MapToModel(this UserEditModel item) - { - return new ApplicationUser - { - Id = item.VanityId.ToString(), - Name = item.Name, - Surname = item.Surname, - Email = item.Email, - UserName = item.UserName - }; - } - - /// - /// Maps a UserEditModel into a specific ApplicationUser - /// - /// - /// - /// - public static ApplicationUser MapToModel(this UserEditModel item, ApplicationUser user) - { - user.Id = item.VanityId.ToString(); - user.Name = item.Name; - user.Surname = item.Surname; - user.Email = item.Email; - user.UserName = item.UserName; - - return user; - } - - ///// - ///// Maps an IEnumerable into an IEnumerable - ///// - ///// - ///// - //public static IEnumerable MapToTableViewModel(this IEnumerable tags) - //{ - // var tableViewModel = new List(); - - // foreach (var item in tags) - // { - // tableViewModel.Add(new ApplicationUserTableViewModel - // { - // VanityId = Guid.Parse(item.Id), - // Name = item.Name, - // Surname = item.Surname, - // Email = item.Email, - // UserName = item.UserName - // }); - // } - - // return tableViewModel; - //} - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs b/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs deleted file mode 100644 index 222497a8..00000000 --- a/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class CategoryExtensions - { - /// - /// Maps Category model into a CategoryEditModel - /// - /// - /// - public static CategoryEditModel MapToEditModel(this Category item) - { - return new CategoryEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Description = item.Description, - Slug = item.Slug - }; - } - - /// - /// Maps a CategoryEditModel into a Category - /// - /// - /// - public static Category MapToModel(this CategoryEditModel item) - { - return new Category - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Description = item.Description, - Slug = item.Slug, - }; - } - - /// - /// Maps a CategoryEditModel into a specific Category - /// - /// - /// - /// - public static Category MapToModel(this CategoryEditModel item, Category category) - { - category.Id = item.Id; - category.VanityId = item.VanityId; - category.Name = item.Name; - category.Description = item.Description; - category.Slug = item.Slug; - - return category; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToTableViewModel(this IEnumerable categories) - { - var tableViewModel = new List(); - - foreach (var item in categories) - { - tableViewModel.Add(new CategoryTableViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Description = item.Description, - Slug = item.Slug - }); - } - - return tableViewModel; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToViewModel(this IEnumerable categories) - { - var viewModel = new List(); - - foreach (var item in categories) - { - viewModel.Add(new CategoryViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Description = item.Description, - Slug = item.Slug, - Posts = item.PostCategories.Select(x => x.Post).MapToViewModel() - }); - } - - return viewModel; - } - - /// - /// Maps VanityId, Name and Slug - /// - /// - /// - public static IEnumerable MapToViewModelSimple(this IEnumerable categories) - { - var viewModel = new List(); - - foreach (var item in categories) - { - viewModel.Add(new CategoryViewModel - { - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug - }); - } - - return viewModel; - } - - /// - /// Maps VanityId, Name and Slug with post count - /// - /// - /// - public static IEnumerable MapToViewModelWithPostCount(this IEnumerable categories) - { - var viewModel = new List(); - - foreach (var item in categories) - { - viewModel.Add(new CategoryViewModel - { - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug, - PostCount = item.PostCount - }); - } - - return viewModel; - } - - /// - /// Maps VanityId, Name and Slug with Posts - /// - /// - /// - public static IEnumerable MapToViewModelWithPost(this IEnumerable categories) - { - var viewModel = new List(); - - foreach (var item in categories) - { - viewModel.Add(new CategoryViewModel - { - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug, - Posts = item.Posts.Select(p => new PostViewModel - { - VanityId = p.VanityId, - Title = p.Title, - Description = p.Description, - Slug = p.Slug, - PublishedOn = p.PublishedOn.ToShortDateString() - }) - }); - } - - return viewModel; - } - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs b/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs deleted file mode 100644 index 96dbf895..00000000 --- a/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Collections.Generic; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class ContactFormExtension - { - /// - /// Maps a ContactFormEditModel into a ContactForm - /// - /// - /// - public static Email MapToModel(this ContactForm item) - { - return new Email - { - From = item.From, - Subject = item.Subject, - Message = item.Message - }; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToViewModel(this IEnumerable emails) - { - var viewModel = new List(); - - foreach (var item in emails) - { - viewModel.Add(new ContactForm - { - From = item.From, - Subject = item.Subject, - Message = item.Message - }); - } - - return viewModel; - } - - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs b/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs deleted file mode 100644 index 74f9829d..00000000 --- a/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class PageExtensions - { - /// - /// Maps Page model into a PageEditModel - /// - /// - /// - public static PageEditModel MapToEditModel(this Page item) - { - return new PageEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn, - Status = item.Status - }; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToEditModel(this IEnumerable pages) - { - var editModels = new List(); - - foreach (var item in pages) - { - editModels.Add(new PageEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn, - Status = item.Status - }); - } - - return editModels; - } - - /// - /// Maps a PageEditModel into a Page - /// - /// - /// - public static Page MapToModel(this PageEditModel item) - { - return new Page - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn, - Status = item.Status - }; - } - - /// - /// Maps a PageEditModel into a specific Page - /// - /// - /// - /// - public static Page MapToModel(this PageEditModel item, Page page) - { - page.Id = item.Id; - page.VanityId = item.VanityId; - page.Title = item.Title; - page.Slug = item.Slug; - page.Description = item.Description; - page.DocumentContent = item.DocumentContent; - page.HeaderImage = item.HeaderImage; - page.PublishedOn = item.PublishedOn; - page.Status = item.Status; - - return page; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToTableViewModel(this IEnumerable pages) - { - var tableViewModel = new List(); - - foreach (var item in pages) - { - tableViewModel.Add(new PageTableViewModel - { - VanityId = item.VanityId, - Title = item.Title, - Description = item.Description, - Slug = item.Slug, - PublishedOn = item.PublishedOn.ToString(), - Status = item.Status, - Author = item.ApplicationUsers.MapToViewModelSimple().Single() - }); - } - - return tableViewModel; - } - - /// - /// Maps Page model into a PageViewModel - /// - /// - /// - public static PageViewModel MapToViewModel(this Page item) - { - return new PageViewModel - { - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Author = item.ApplicationUsers.MapToViewModelSimple().Single() - }; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToViewModel(this IEnumerable pages) - { - var editModels = new List(); - - foreach (var item in pages) - { - editModels.Add(new PageViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Status = item.Status - }); - } - - return editModels; - } - - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs b/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs deleted file mode 100644 index 6599f786..00000000 --- a/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs +++ /dev/null @@ -1,293 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class PostExtensions - { - /// - /// Maps Post model into a PostEditModel - /// - /// - /// - public static PostEditModel MapToEditModel(this Post item) - { - return new PostEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn, - Status = item.Status, - SelectedCategories = item.PostCategories.Select(x => x.Category.VanityId.ToString()), - SelectedTags = item.PostTags.Select(x => x.Tag.VanityId.ToString()) - }; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToEditModel(this IEnumerable posts) - { - var editModels = new List(); - - foreach (var item in posts) - { - editModels.Add(new PostEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn, - Status = item.Status - }); - } - - return editModels; - } - - /// - /// Maps a PostEditModel into a Post - /// - /// - /// - public static Post MapToModel(this PostEditModel item) - { - return new Post - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn, - Status = item.Status - }; - } - - /// - /// Maps a PostEditModel into a specific Post - /// - /// - /// - /// - public static Post MapToModel(this PostEditModel item, Post post) - { - post.Id = item.Id; - post.VanityId = item.VanityId; - post.Title = item.Title; - post.Slug = item.Slug; - post.Description = item.Description; - post.DocumentContent = item.DocumentContent; - post.HeaderImage = item.HeaderImage; - post.PublishedOn = item.PublishedOn; - post.Status = item.Status; - - return post; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToTableViewModel(this IEnumerable posts) - { - var tableViewModel = new List(); - - foreach (var item in posts) - { - tableViewModel.Add(new PostTableViewModel - { - VanityId = item.VanityId, - Title = item.Title, - Description = item.Description, - Slug = item.Slug, - PublishedOn = item.PublishedOn.ToString(), - Status = item.Status, - Author = item.ApplicationUsers.MapToViewModelSimple().Single() - }); - } - - return tableViewModel; - } - - /// - /// Maps Post model into a PostViewModel - /// - /// - /// - public static PostViewModel MapToViewModel(this Post item) - { - return new PostViewModel - { - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Categories = item.Categories.MapToViewModelSimple(), - Author = item.ApplicationUsers.MapToViewModelSimple().Single() - }; - } - - /// - /// Maps Post model into a PostViewModel - /// - /// - /// - public static IEnumerable MapToViewModel(this IEnumerable posts) - { - var viewModels = new List(); - - foreach (var item in posts) - { - viewModels.Add(new PostViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Status = item.Status - }); - } - - return viewModels; - } - - /// - /// Maps Post model into a PostViewModel with Categories - /// - /// - /// - public static IEnumerable MapToViewModelWithCategories(this IEnumerable posts) - { - var viewModels = new List(); - - foreach (var item in posts) - { - viewModels.Add(new PostViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Status = item.Status, - Categories = item.PostCategories.Select(x => x.Category).MapToViewModel() - }); - } - - return viewModels; - } - - /// - /// Maps Post model into a PostViewModel with Tags - /// - /// - /// - public static IEnumerable MapToViewModelWithTags(this IEnumerable posts) - { - var viewModels = new List(); - - foreach (var item in posts) - { - viewModels.Add(new PostViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - DocumentContent = item.DocumentContent, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Status = item.Status, - Tags = item.PostTags.Select(x => x.Tag).MapToViewModel() - }); - } - - return viewModels; - } - - /// - /// Maps limited information for Partial Views - /// - /// - /// - public static IEnumerable MapToViewModelForPartialView(this IEnumerable posts) - { - var viewModels = new List(); - - foreach (var item in posts) - { - viewModels.Add(new PostViewModel - { - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Categories = item.Categories.MapToViewModelSimple(), - Author = item.ApplicationUsers.MapToViewModelSimple().Single() - }); - } - - return viewModels; - } - - /// - /// Maps limited information for Partial Views for Tags - /// - /// - /// - public static IEnumerable MapToViewModelForPartialViewForTags(this IEnumerable posts) - { - var viewModels = new List(); - - foreach (var item in posts) - { - viewModels.Add(new PostViewModel - { - VanityId = item.VanityId, - Title = item.Title, - Slug = item.Slug, - Description = item.Description, - HeaderImage = item.HeaderImage, - PublishedOn = item.PublishedOn.ToShortDateString(), - Categories = item.Categories.MapToViewModelSimple(), - Tags = item.Tags.MapToViewModelSimple(), - Author = item.ApplicationUsers.MapToViewModelSimple().Single() - }); - } - - return viewModels; - } - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs b/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs deleted file mode 100644 index 3c3b7d2a..00000000 --- a/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Collections.Generic; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class TagExtensions - { - /// - /// Maps Tag model into a TagEditModel - /// - /// - /// - public static TagEditModel MapToEditModel(this Tag item) - { - return new TagEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug - }; - } - - /// - /// Maps a TagEditModel into a Tag - /// - /// - /// - public static Tag MapToModel(this TagEditModel item) - { - return new Tag - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug, - }; - } - - /// - /// Maps a TagEditModel into a specific Tag - /// - /// - /// - /// - public static Tag MapToModel(this TagEditModel item, Tag tag) - { - tag.Id = item.Id; - tag.VanityId = item.VanityId; - tag.Name = item.Name; - tag.Slug = item.Slug; - - return tag; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToTableViewModel(this IEnumerable tags) - { - var tableViewModel = new List(); - - foreach (var item in tags) - { - tableViewModel.Add(new TagTableViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug - }); - } - - return tableViewModel; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToViewModel(this IEnumerable tags) - { - var viewModel = new List(); - - foreach (var item in tags) - { - viewModel.Add(new TagViewModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug - }); - } - - return viewModel; - } - - /// - /// Maps VanityId, Name and Slug - /// - /// - /// - public static IEnumerable MapToViewModelSimple(this IEnumerable tags) - { - var viewModel = new List(); - - foreach (var item in tags) - { - viewModel.Add(new TagViewModel - { - VanityId = item.VanityId, - Name = item.Name, - Slug = item.Slug - }); - } - - return viewModel; - } - } -} diff --git a/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs b/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs deleted file mode 100644 index 4d795036..00000000 --- a/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Collections.Generic; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Extensions.Mapper -{ - public static class WebsiteExtensions - { - /// - /// Maps Website model into a WebsiteEditModel - /// - /// - /// - public static WebsiteEditModel MapToEditModel(this Website item) - { - return new WebsiteEditModel - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Description = item.Description, - Tagline = item.Tagline, - HeaderImage = item.HeaderImage, - Culture = item.Culture, - UrlFormat = item.UrlFormat, - DateFormat = item.DateFormat, - SiteUrl = item.SiteUrl, - ArticleLimit = item.ArticleLimit, - Address = item.Address, - Phone = item.Phone, - Email = item.Email, - FacebookAppId = item.FacebookAppId, - FacebookApiVersion = item.FacebookApiVersion, - DisqusShortName = item.DisqusShortName, - Facebook = item.Facebook, - Twitter = item.Twitter, - Instagram = item.Instagram, - LinkedIn = item.LinkedIn, - GoogleAnalytics = item.GoogleAnalytics, - GoogleRecaptchaSiteKey = item.GoogleRecaptchaSiteKey, - GoogleRecaptchaSecretKey = item.GoogleRecaptchaSecretKey - }; - } - - /// - /// Maps a WebsiteEditModel into a Website - /// - /// - /// - public static Website MapToModel(this WebsiteEditModel item) - { - return new Website - { - Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Description = item.Description, - Tagline = item.Tagline, - HeaderImage = item.HeaderImage, - Culture = item.Culture, - UrlFormat = item.UrlFormat, - DateFormat = item.DateFormat, - SiteUrl = item.SiteUrl, - ArticleLimit = item.ArticleLimit, - Address = item.Address, - Phone = item.Phone, - Email = item.Email, - FacebookAppId = item.FacebookAppId, - FacebookApiVersion = item.FacebookApiVersion, - DisqusShortName = item.DisqusShortName, - Facebook = item.Facebook, - Twitter = item.Twitter, - Instagram = item.Instagram, - LinkedIn = item.LinkedIn, - GoogleAnalytics = item.GoogleAnalytics, - GoogleRecaptchaSiteKey = item.GoogleRecaptchaSiteKey, - GoogleRecaptchaSecretKey = item.GoogleRecaptchaSecretKey - }; - } - - /// - /// Maps a WebsiteEditModel into a specific Website - /// - /// - /// - /// - public static Website MapToModel(this WebsiteEditModel item, Website website) - { - website.Id = item.Id; - website.VanityId = item.VanityId; - website.Name = item.Name; - website.Description = item.Description; - website.Tagline = item.Tagline; - website.HeaderImage = item.HeaderImage; - website.Culture = item.Culture; - website.UrlFormat = item.UrlFormat; - website.DateFormat = item.DateFormat; - website.SiteUrl = item.SiteUrl; - website.ArticleLimit = item.ArticleLimit; - website.Address = item.Address; - website.Phone = item.Phone; - website.Email = item.Email; - website.FacebookAppId = item.FacebookAppId; - website.FacebookApiVersion = item.FacebookApiVersion; - website.DisqusShortName = item.DisqusShortName; - website.Facebook = item.Facebook; - website.Twitter = item.Twitter; - website.Instagram = item.Instagram; - website.LinkedIn = item.LinkedIn; - website.GoogleAnalytics = item.GoogleAnalytics; - website.GoogleRecaptchaSiteKey = item.GoogleRecaptchaSiteKey; - website.GoogleRecaptchaSecretKey = item.GoogleRecaptchaSecretKey; - - return website; - } - - /// - /// Maps an IEnumerable into an IEnumerable - /// - /// - /// - public static IEnumerable MapToTableViewModel(this IEnumerable websites) - { - var tableViewModel = new List(); - - foreach (var item in websites) - { - tableViewModel.Add(new WebsiteTableViewModel - { - //Id = item.Id, - VanityId = item.VanityId, - Name = item.Name, - Tagline = item.Tagline, - Culture = item.Culture, - UrlFormat = item.UrlFormat, - DateFormat = item.DateFormat, - SiteUrl = item.SiteUrl, - GoogleAnalytics = item.GoogleAnalytics - }); - } - - return tableViewModel; - } - } -} diff --git a/CmsEngine.Application/Helpers/DataTableHelper.cs b/CmsEngine.Application/Helpers/DataTableHelper.cs deleted file mode 100644 index e13a19b4..00000000 --- a/CmsEngine.Application/Helpers/DataTableHelper.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.Encodings.Web; -using CmsEngine.Application.Attributes; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Core.Extensions; -using CmsEngine.Extensions; - -namespace CmsEngine.Application.Helpers -{ - public static class DataTableHelper - { - public static DataTableViewModel BuildDataTable(IEnumerable listItems, int recordsTotal, int recordsFiltered, int draw, int start, int length) - { - var listString = new List>(); - - foreach (var item in listItems.Skip(start).Take(length)) - { - // Get the properties which should appear in the DataTable - var itemProperties = item.GetType() - .GetProperties() - .Where(p => Attribute.IsDefined(p, typeof(ShowOnDataTable))) - .OrderBy(o => o.GetCustomAttributes(false).OfType().First().Order); - - // An empty value must *always* be the first property because of the checkboxes - var listPropertes = new List { string.Empty }; - - // Loop through and add the properties found - foreach (var property in itemProperties) - { - listPropertes.Add(PrepareProperty(item, property)); - } - - // VanityId must *always* be the last property - listPropertes.Add(item.VanityId.ToString()); - - listString.Add(listPropertes); - } - - return new DataTableViewModel - { - Data = listString, - RecordsTotal = recordsTotal, - RecordsFiltered = recordsFiltered, - Draw = draw - }; - } - - private static string PrepareProperty(IViewModel item, PropertyInfo property) - { - GeneralStatus generalStatus; - object value = item.GetType().GetProperty(property.Name).GetValue(item); - - switch (property.PropertyType.Name) - { - case "DocumentStatus": - string documentStatus = value?.ToString() ?? ""; - switch (documentStatus) - { - case "Published": - generalStatus = GeneralStatus.Success; - break; - case "PendingApproval": - generalStatus = GeneralStatus.Warning; - break; - default: - generalStatus = GeneralStatus.Info; - break; - } - - return $"{documentStatus.ToEnum().GetName()}"; - case "UserViewModel": - var author = (UserViewModel)value; - return HtmlEncoder.Default.Encode(author?.FullName ?? ""); - case "Boolean": - generalStatus = (bool)value ? GeneralStatus.Success : GeneralStatus.Danger; - return $"{((bool)value).ToYesNo().ToUpper()}"; - default: - return HtmlEncoder.Default.Encode(value?.ToString() ?? ""); - } - } - } -} diff --git a/CmsEngine.Application/Helpers/Email/ContactForm.cs b/CmsEngine.Application/Helpers/Email/ContactForm.cs deleted file mode 100644 index 5b179b45..00000000 --- a/CmsEngine.Application/Helpers/Email/ContactForm.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Application.Helpers.Email -{ - public class ContactForm - { - [Required] - [DataType(DataType.EmailAddress)] - public string From { get; set; } - [DataType(DataType.EmailAddress)] - public string To { get; set; } - [Required] - [MaxLength(150)] - public string Subject { get; set; } - [Required] - [MaxLength(500)] - public string Message { get; set; } - - public ContactForm() - { - - } - - public ContactForm(string to, string subject, string message) - { - To = to; - Subject = subject; - Message = message; - } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("From", From), - new JProperty("To", To), - new JProperty("Subject", Subject), - new JProperty("Message", Message) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Application/Helpers/Email/EmailSender.cs b/CmsEngine.Application/Helpers/Email/EmailSender.cs deleted file mode 100644 index 328fc476..00000000 --- a/CmsEngine.Application/Helpers/Email/EmailSender.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Net; -using System.Net.Mail; -using System.Text; -using System.Threading.Tasks; -using CmsEngine.Core.Exceptions; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; - -namespace CmsEngine.Application.Helpers.Email -{ - public class EmailSender : IEmailSender - { - private readonly EmailSettings _emailSettings; - private readonly ILogger _logger; - - public EmailSender(IOptions emailSettings, ILogger logger) - { - _emailSettings = emailSettings.Value; - _logger = logger; - } - - public async Task SendEmailAsync(ContactForm contactForm) - { - await ExecuteAsync(contactForm); - } - - private async Task ExecuteAsync(ContactForm contactForm) - { - _logger.LogDebug("SendEmailAsync(contactForm: {0})", contactForm.ToString()); - - string from = contactForm.From ?? _emailSettings.Username; - string body = $"From: {from}\r\nTo: {contactForm.To}\r\n-----\r\n\r\n{contactForm.Message}"; - - try - { - MailMessage message = new MailMessage - { - From = new MailAddress(from), - Subject = $"🌐 CmsEngine - {contactForm.Subject}", - SubjectEncoding = Encoding.UTF8, - IsBodyHtml = false, - Body = body, - BodyEncoding = Encoding.UTF8, - Priority = MailPriority.Normal - }; - - if (!string.IsNullOrWhiteSpace(contactForm.To)) - { - message.To.Add(contactForm.To); - } - - if (!string.IsNullOrWhiteSpace(_emailSettings.CcEmail)) - { - message.CC.Add(_emailSettings.CcEmail); - } - - if (!string.IsNullOrWhiteSpace(_emailSettings.BccEmail)) - { - message.Bcc.Add(_emailSettings.BccEmail); - } - - using (var smtp = new SmtpClient(_emailSettings.Domain, _emailSettings.Port)) - { - smtp.EnableSsl = true; - smtp.DeliveryMethod = SmtpDeliveryMethod.Network; - smtp.UseDefaultCredentials = false; - smtp.Credentials = new NetworkCredential(_emailSettings.Username, _emailSettings.Password); - - _logger.LogDebug("Message {0}", message.ToString()); - await smtp.SendMailAsync(message); - } - - _logger.LogDebug("Email sent from {0} to {1}", message.From, message.To[0]); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error when sending e-mail"); - throw new EmailException("Error when sending e-mail", ex); - } - } - } -} diff --git a/CmsEngine.Application/Helpers/Email/EmailSettings.cs b/CmsEngine.Application/Helpers/Email/EmailSettings.cs deleted file mode 100644 index a4a92d66..00000000 --- a/CmsEngine.Application/Helpers/Email/EmailSettings.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Application.Helpers.Email -{ - public class EmailSettings - { - public string Domain { get; set; } - - public int Port { get; set; } - - public string Username { get; set; } - - public string Password { get; set; } - - public string FromEmail { get; set; } - - public string CcEmail { get; set; } - - public string BccEmail { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Domain", Domain), - new JProperty("Port", Port), - new JProperty("Username", Username), - new JProperty("Password", Password), - new JProperty("FromEmail", FromEmail), - new JProperty("CcEmail", CcEmail), - new JProperty("BccEmail", BccEmail) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Application/Helpers/Email/IEmailSender.cs b/CmsEngine.Application/Helpers/Email/IEmailSender.cs deleted file mode 100644 index 1b926c45..00000000 --- a/CmsEngine.Application/Helpers/Email/IEmailSender.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Threading.Tasks; - -namespace CmsEngine.Application.Helpers.Email -{ - public interface IEmailSender - { - Task SendEmailAsync(ContactForm mailEditModel); - } -} diff --git a/CmsEngine.Application/Helpers/ExpressionBuilder.cs b/CmsEngine.Application/Helpers/ExpressionBuilder.cs deleted file mode 100644 index a3f9b32e..00000000 --- a/CmsEngine.Application/Helpers/ExpressionBuilder.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; -using CmsEngine.Core; - -namespace CmsEngine.Application.Helpers -{ - public sealed class ExpressionFilter - { - public string PropertyName { get; set; } - public Operation Operation { get; set; } - public object Value { get; set; } - } - public static class ExpressionBuilder - { - private static readonly MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); - private static readonly MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }); - private static readonly MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }); - - - public static Expression> GetExpression(IList filters, LogicalOperator logicalOperator) - { - if (filters.Count == 0) - { - return null; - } - - ParameterExpression param = Expression.Parameter(typeof(T), "t"); - Expression exp = null; - - while (filters.Count > 0) - { - var f1 = filters[0]; - var nullCheck = new ExpressionFilter - { - PropertyName = f1.PropertyName, - Operation = Operation.NotEqual, - Value = null - }; - - if (exp == null) - { - exp = GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso); - } - else - { - switch (logicalOperator) - { - case LogicalOperator.And: - exp = Expression.And(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); - break; - case LogicalOperator.Or: - exp = Expression.Or(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); - break; - case LogicalOperator.OrElse: - exp = Expression.OrElse(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); - break; - case LogicalOperator.AndAlso: - default: - exp = Expression.AndAlso(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); - break; - } - } - - filters.Remove(f1); - } - - return Expression.Lambda>(exp, param); - } - - private static Expression GetExpression(ParameterExpression param, ExpressionFilter filter) - { - MemberExpression member = Expression.Property(param, filter.PropertyName); - ConstantExpression constant = Expression.Constant(filter.Value); - - switch (filter.Operation) - { - case Operation.Equals: - return Expression.Equal(member, constant); - - case Operation.NotEqual: - return Expression.NotEqual(member, constant); - - case Operation.GreaterThan: - return Expression.GreaterThan(member, constant); - - case Operation.GreaterThanOrEqual: - return Expression.GreaterThanOrEqual(member, constant); - - case Operation.LessThan: - return Expression.LessThan(member, constant); - - case Operation.LessThanOrEqual: - return Expression.LessThanOrEqual(member, constant); - - case Operation.Contains: - return Expression.Call(member, containsMethod, constant); - - case Operation.NotContain: - return Expression.Not(Expression.Call(member, containsMethod, constant)); - - case Operation.StartsWith: - return Expression.Call(member, startsWithMethod, constant); - - case Operation.EndsWith: - return Expression.Call(member, endsWithMethod, constant); - } - - return null; - } - - private static BinaryExpression GetExpression - (ParameterExpression param, ExpressionFilter filter1, ExpressionFilter filter2, LogicalOperator logicalOperator) - { - Expression bin1 = GetExpression(param, filter1); - Expression bin2 = GetExpression(param, filter2); - - switch (logicalOperator) - { - case LogicalOperator.And: - return Expression.And(bin1, bin2); - case LogicalOperator.Or: - return Expression.Or(bin1, bin2); - case LogicalOperator.OrElse: - return Expression.OrElse(bin1, bin2); - case LogicalOperator.AndAlso: - default: - return Expression.AndAlso(bin1, bin2); - } - } - } -} diff --git a/CmsEngine.Application/Helpers/FileHelper.cs b/CmsEngine.Application/Helpers/FileHelper.cs deleted file mode 100644 index 17238249..00000000 --- a/CmsEngine.Application/Helpers/FileHelper.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Drawing; -using System.Drawing.Imaging; -using System.Globalization; -using System.IO; - -namespace CmsEngine.Application.Helpers -{ - public static class FileHelper - { - public static string FormatFileSize(string filename) - { - string[] sizes = { "B", "KB", "MB", "GB" }; - double len = new FileInfo(filename).Length; - int order = 0; - - while (len >= 1024 && order + 1 < sizes.Length) - { - order++; - len /= 1024; - } - - return string.Format("{0:0} {1}", len, sizes[order]); - } - - public static bool IsImage(string fileName) - { - return fileName.EndsWith(".jpeg", true, CultureInfo.InvariantCulture) - || fileName.EndsWith(".jpg", true, CultureInfo.InvariantCulture) - || fileName.EndsWith(".png", true, CultureInfo.InvariantCulture) - || fileName.EndsWith(".gif", true, CultureInfo.InvariantCulture) - || fileName.EndsWith(".bmp", true, CultureInfo.InvariantCulture); - } - - public static void ResizeImage(string originalFile, string newFile, int newWidth, int maxHeight, bool onlyResizeIfWider) - { - Image fullsizeImage = Image.FromFile(originalFile); - - // Prevent using images internal thumbnail - fullsizeImage.RotateFlip(RotateFlipType.Rotate180FlipNone); - fullsizeImage.RotateFlip(RotateFlipType.Rotate180FlipNone); - - if (onlyResizeIfWider && fullsizeImage.Width <= newWidth) - { - newWidth = fullsizeImage.Width; - } - - int newHeight = fullsizeImage.Height * newWidth / fullsizeImage.Width; - if (newHeight > maxHeight) - { - // Resize with height instead - newWidth = fullsizeImage.Width * maxHeight / fullsizeImage.Height; - newHeight = maxHeight; - } - - Image newImage = fullsizeImage.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero); - - // Clear handle to original file so that we can overwrite it if necessary - fullsizeImage.Dispose(); - - var encoderParameters = new EncoderParameters(1); - encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); - - // Save resized picture - newImage.Save(newFile, GetEncoderInfo("image/jpeg"), encoderParameters); - } - - private static ImageCodecInfo GetEncoderInfo(string mimeType) - { - int j; - ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders(); - for (j = 0; j < encoders.Length; ++j) - { - if (encoders[j].MimeType == mimeType) - { - return encoders[j]; - } - } - return null; - } - - public static Bitmap CropImage(Image img, Rectangle cropArea) - { - Bitmap bmpImage = new Bitmap(img); - return bmpImage.Clone(cropArea, bmpImage.PixelFormat); - } - } -} diff --git a/CmsEngine.Application/Models/EditModels/BaseEditModel.cs b/CmsEngine.Application/Models/EditModels/BaseEditModel.cs deleted file mode 100644 index 07874207..00000000 --- a/CmsEngine.Application/Models/EditModels/BaseEditModel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Application.EditModels -{ - public class BaseEditModel - { - public bool IsNew - { - get - { - return (Id == 0 && VanityId == Guid.Empty); - } - } - - public int Id { get; set; } - - public Guid VanityId { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId) - ); - return jsonResult.ToString(); - } - - } -} diff --git a/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs b/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs deleted file mode 100644 index 158c175b..00000000 --- a/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Application.EditModels -{ - public class CategoryEditModel : BaseEditModel, IEditModel - { - [Required] - [MaxLength(35, ErrorMessage = "The name must have less than 35 characters")] - public string Name { get; set; } - - public string Slug { get; set; } - - public string Description { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId), - new JProperty("Name", Name), - new JProperty("Slug", Slug), - new JProperty("Description", Description) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs b/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs deleted file mode 100644 index 89d4bd78..00000000 --- a/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace CmsEngine.Application.EditModels -{ - public class CheckboxEditModel - { - public string Label { get; set; } - public string Value { get; set; } - public bool Selected { get; set; } - public bool Enabled { get; set; } - } -} diff --git a/CmsEngine.Application/Models/EditModels/IEditModel.cs b/CmsEngine.Application/Models/EditModels/IEditModel.cs deleted file mode 100644 index 9891910e..00000000 --- a/CmsEngine.Application/Models/EditModels/IEditModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace CmsEngine.Application.EditModels -{ - public interface IEditModel - { - bool IsNew { get; } - - int Id { get; set; } - Guid VanityId { get; set; } - } -} diff --git a/CmsEngine.Application/Models/EditModels/PageEditModel.cs b/CmsEngine.Application/Models/EditModels/PageEditModel.cs deleted file mode 100644 index b5bc384d..00000000 --- a/CmsEngine.Application/Models/EditModels/PageEditModel.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using CmsEngine.Core; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Application.EditModels -{ - public class PageEditModel : BaseEditModel, IEditModel - { - public PageEditModel() - { - Status = DocumentStatus.Draft; - PublishedOn = DateTime.Now; - } - - [Required] - [MaxLength(100, ErrorMessage = "The title must have less than 100 characters")] - public string Title { get; set; } - - public string Slug { get; set; } - - public string HeaderImage { get; set; } - - [Required] - [MaxLength(150, ErrorMessage = "The description must have less than 150 characters")] - public string Description { get; set; } - - public string DocumentContent { get; set; } - - public DocumentStatus Status { get; set; } - - [Required] - //[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd hh:mm}", ApplyFormatInEditMode = true)] - public DateTime PublishedOn { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId), - new JProperty("Title", Title), - new JProperty("Slug", Slug), - new JProperty("HeaderImage", HeaderImage), - new JProperty("Description", Description), - new JProperty("Status", Status.ToString()), - new JProperty("PublishedOn", PublishedOn) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Application/Models/EditModels/PostEditModel.cs b/CmsEngine.Application/Models/EditModels/PostEditModel.cs deleted file mode 100644 index d89eab29..00000000 --- a/CmsEngine.Application/Models/EditModels/PostEditModel.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using CmsEngine.Core; -using Microsoft.AspNetCore.Mvc.Rendering; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Application.EditModels -{ - public class PostEditModel : BaseEditModel, IEditModel - { - public PostEditModel() - { - Status = DocumentStatus.Draft; - PublishedOn = DateTime.Now; - } - - [Required] - [MaxLength(100, ErrorMessage = "The title must have less than 100 characters")] - public string Title { get; set; } - - public string Slug { get; set; } - - public string HeaderImage { get; set; } - - [Required] - [MaxLength(150, ErrorMessage = "The description must have less than 150 characters")] - public string Description { get; set; } - - public string DocumentContent { get; set; } - - public IEnumerable Categories { get; set; } - - public IEnumerable SelectedCategories { get; set; } - - // TODO: Perhaps replace the SelectListItem by something else in order to make it less ASP.NET Core dependent - public IEnumerable Tags { get; set; } - - public IEnumerable SelectedTags { get; set; } - - public DocumentStatus Status { get; set; } - - [Required] - [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy HH:mm}", ApplyFormatInEditMode = true)] - public DateTime PublishedOn { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId), - new JProperty("Title", Title), - new JProperty("Slug", Slug), - new JProperty("HeaderImage", HeaderImage), - new JProperty("Description", Description), - new JProperty("Status", Status.ToString()), - new JProperty("PublishedOn", PublishedOn) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Application/Models/EditModels/TagEditModel.cs b/CmsEngine.Application/Models/EditModels/TagEditModel.cs deleted file mode 100644 index 5d573717..00000000 --- a/CmsEngine.Application/Models/EditModels/TagEditModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.EditModels -{ - public class TagEditModel : BaseEditModel, IEditModel - { - [Required] - [MaxLength(15, ErrorMessage = "The name must have less than 15 characters")] - public string Name { get; set; } - - public string Slug { get; set; } - } -} diff --git a/CmsEngine.Application/Models/EditModels/UserEditModel.cs b/CmsEngine.Application/Models/EditModels/UserEditModel.cs deleted file mode 100644 index 0c1f2a9e..00000000 --- a/CmsEngine.Application/Models/EditModels/UserEditModel.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.EditModels -{ - public class UserEditModel : BaseEditModel, IEditModel - { - public string FullName - { - get - { - return $"{Name} {Surname}"; - } - } - - [Required] - public string Name { get; set; } - - [Required] - public string Surname { get; set; } - - [Required] - public string Email { get; set; } - - [Required] - public string Password { get; set; } - - public string UserName { get; set; } - } -} diff --git a/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs b/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs deleted file mode 100644 index 633f7907..00000000 --- a/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using CmsEngine.Core.Constants; - -namespace CmsEngine.Application.EditModels -{ - public class WebsiteEditModel : BaseEditModel, IEditModel - { - [Required] - [MaxLength(25)] - public string Name { get; set; } - - [MaxLength(200)] - public string Tagline { get; set; } - - public string Description { get; set; } - - public string HeaderImage { get; set; } - - [Required] - [MaxLength(5)] - public string Culture { get; set; } - - [Required] - [MaxLength(100)] - public string UrlFormat { get; set; } = $"{CmsEngineConstants.SiteUrl}/{CmsEngineConstants.Type}/{CmsEngineConstants.Slug}"; - - [Required] - [MaxLength(10)] - public string DateFormat { get; set; } = "MM/dd/yyyy"; - - [Required] - [MaxLength(250)] - public string SiteUrl { get; set; } - - [Required] - public int ArticleLimit { get; set; } - - [MaxLength(250)] - public string Address { get; set; } - - [MaxLength(20)] - public string Phone { get; set; } - - [Required] - [MaxLength(250)] - public string Email { get; set; } - - [MaxLength(20)] - public string Facebook { get; set; } - - [MaxLength(20)] - public string Twitter { get; set; } - - [MaxLength(20)] - public string Instagram { get; set; } - - [MaxLength(20)] - public string LinkedIn { get; set; } - - [MaxLength(30)] - public string FacebookAppId { get; set; } - - [MaxLength(10)] - public string FacebookApiVersion { get; set; } - - [MaxLength(30)] - public string DisqusShortName { get; set; } - - public string GoogleAnalytics { get; set; } - public string GoogleRecaptchaSiteKey { get; set; } - public string GoogleRecaptchaSecretKey { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs deleted file mode 100644 index ac5991c4..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class ExternalLoginViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs deleted file mode 100644 index 37a8547b..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class ForgotPasswordViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs deleted file mode 100644 index 0b67b78b..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class LoginViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs deleted file mode 100644 index 1becfd34..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class LoginWith2faViewModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Authenticator code")] - public string TwoFactorCode { get; set; } - - [Display(Name = "Remember this machine")] - public bool RememberMachine { get; set; } - - public bool RememberMe { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs deleted file mode 100644 index 6f7a0e0b..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class LoginWithRecoveryCodeViewModel - { - [Required] - [DataType(DataType.Text)] - [Display(Name = "Recovery Code")] - public string RecoveryCode { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs deleted file mode 100644 index 82b63bb2..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class RegisterViewModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs b/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs deleted file mode 100644 index 040e0d8e..00000000 --- a/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.AccountViewModels -{ - public class ResetPasswordViewModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - - public string Code { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs b/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs deleted file mode 100644 index 0c315fd2..00000000 --- a/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class ApiDetailsViewModel - { - public string FacebookAppId { get; set; } - public string FacebookApiVersion { get; set; } - - public bool HasFacebookDetails - { - get - { - return !string.IsNullOrWhiteSpace(FacebookAppId) && !string.IsNullOrWhiteSpace(FacebookApiVersion); - } - } - - public string DisqusShortName { get; set; } - - public bool HasDisqusDetails - { - get - { - return !string.IsNullOrWhiteSpace(DisqusShortName); - } - } - - } -} diff --git a/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs b/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs deleted file mode 100644 index 9b7d2091..00000000 --- a/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace CmsEngine.Application.ViewModels -{ - public class BaseViewModel : IViewModel - { - public int Id { get; set; } - public Guid VanityId { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs b/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs deleted file mode 100644 index 11971d3d..00000000 --- a/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace CmsEngine.Application.ViewModels -{ - public class CategoryViewModel : BaseViewModel, IViewModel - { - public string Name { get; set; } - public string Slug { get; set; } - public string Description { get; set; } - public int PostCount { get; set; } - public IEnumerable Posts { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs b/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs deleted file mode 100644 index fd450207..00000000 --- a/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class ContactDetailsViewModel - { - public string Address { get; set; } - public string Phone { get; set; } - public string Email { get; set; } - - public bool HasEmail - { - get - { - return !string.IsNullOrWhiteSpace(Email); - } - } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs deleted file mode 100644 index da5802ba..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CmsEngine.Application.Attributes; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class CategoryTableViewModel : BaseViewModel, IViewModel - { - [Searchable, Orderable, ShowOnDataTable(0)] - public string Name { get; set; } - - [Searchable, Orderable, ShowOnDataTable(1)] - public string Slug { get; set; } - - [Searchable, Orderable, ShowOnDataTable(2)] - public string Description { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs deleted file mode 100644 index a8252672..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class DataColumn - { - [JsonProperty(PropertyName = "data")] - public int Data { get; set; } - - [JsonProperty(PropertyName = "name")] - public string Name { get; set; } - - [JsonProperty(PropertyName = "searchable")] - public bool Searchable { get; set; } - - [JsonProperty(PropertyName = "orderable")] - public bool Orderable { get; set; } - - [JsonProperty(PropertyName = "search")] - public Search Search { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs deleted file mode 100644 index 493fe3d6..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Newtonsoft.Json; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class DataOrder - { - [JsonProperty(PropertyName = "column")] - public int Column { get; set; } - - [JsonProperty(PropertyName = "dir")] - public string Dir { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs deleted file mode 100644 index cba97dc9..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class DataParameters - { - [JsonProperty(PropertyName = "draw")] - public int Draw { get; set; } - - [JsonProperty(PropertyName = "columns")] - public List Columns { get; set; } - - [JsonProperty(PropertyName = "order")] - public List Order { get; set; } - - [JsonProperty(PropertyName = "start")] - public int Start { get; set; } - - [JsonProperty(PropertyName = "length")] - public int Length { get; set; } - - [JsonProperty(PropertyName = "search")] - public Search Search { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs deleted file mode 100644 index 633ee17b..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class DataTableViewModel - { - [JsonProperty(PropertyName = "draw")] - public int Draw { get; set; } - - [JsonProperty(PropertyName = "recordsTotal")] - public int RecordsTotal { get; set; } - - [JsonProperty(PropertyName = "recordsFiltered")] - public int RecordsFiltered { get; set; } - - [JsonProperty(PropertyName = "data")] - public List> Data { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs deleted file mode 100644 index 113b2475..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using CmsEngine.Application.Attributes; -using CmsEngine.Core; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class PageTableViewModel : BaseViewModel, IViewModel - { - [Searchable, Orderable, ShowOnDataTable(0)] - public string Title { get; set; } - - [Searchable, Orderable, ShowOnDataTable(2)] - public string Slug { get; set; } - - [Searchable, Orderable, ShowOnDataTable(1)] - public string Description { get; set; } - - public string DocumentContent { get; set; } - - [Searchable, Orderable, ShowOnDataTable(3)] - public UserViewModel Author { get; set; } - - [Orderable, ShowOnDataTable(5)] - public DocumentStatus Status { get; set; } - - [Orderable, ShowOnDataTable(4)] - public string PublishedOn { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs deleted file mode 100644 index c75a1381..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs +++ /dev/null @@ -1,28 +0,0 @@ -using CmsEngine.Application.Attributes; -using CmsEngine.Core; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class PostTableViewModel : BaseViewModel - { - [Searchable, Orderable, ShowOnDataTable(0)] - public string Title { get; set; } - - [Searchable, Orderable, ShowOnDataTable(2)] - public string Slug { get; set; } - - [Searchable, Orderable, ShowOnDataTable(1)] - public string Description { get; set; } - - public string DocumentContent { get; set; } - - [Orderable, ShowOnDataTable(3)] - public UserViewModel Author { get; set; } - - [Orderable, ShowOnDataTable(5)] - public DocumentStatus Status { get; set; } - - [Orderable, ShowOnDataTable(4)] - public string PublishedOn { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs deleted file mode 100644 index ccff89ea..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Newtonsoft.Json; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class Search - { - [JsonProperty(PropertyName = "regex")] - public bool Regex { get; set; } - - [JsonProperty(PropertyName = "value")] - public string Value { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs deleted file mode 100644 index d06ba573..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using CmsEngine.Application.Attributes; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class TagTableViewModel : BaseViewModel, IViewModel - { - [Searchable, Orderable, ShowOnDataTable(0)] - public string Name { get; set; } - - [Searchable, Orderable, ShowOnDataTable(1)] - public string Slug { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs b/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs deleted file mode 100644 index 5924ecb9..00000000 --- a/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs +++ /dev/null @@ -1,37 +0,0 @@ -using CmsEngine.Application.Attributes; -using CmsEngine.Core.Constants; - -namespace CmsEngine.Application.ViewModels.DataTableViewModels -{ - public class WebsiteTableViewModel : BaseViewModel, IViewModel - { - [Searchable, Orderable, ShowOnDataTable(0)] - public string Name { get; set; } - - [Searchable, Orderable, ShowOnDataTable(1)] - public string Tagline { get; set; } - - [Searchable, Orderable, ShowOnDataTable(2)] - public string Culture { get; set; } - - [Searchable, Orderable, ShowOnDataTable(3)] - public string UrlFormat { get; set; } = $"{CmsEngineConstants.SiteUrl}/{CmsEngineConstants.Type}/{CmsEngineConstants.Slug}"; - - [Searchable, Orderable, ShowOnDataTable(4)] - public string DateFormat { get; set; } = "MM/dd/yyyy"; - - [Searchable, Orderable, ShowOnDataTable(5)] - public string SiteUrl { get; set; } - - public string GoogleAnalytics { get; set; } - - [Orderable, ShowOnDataTable(6)] - public bool HasGoogleAnalytics - { - get - { - return !string.IsNullOrWhiteSpace(GoogleAnalytics); - } - } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs b/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs deleted file mode 100644 index deb0cfa9..00000000 --- a/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs +++ /dev/null @@ -1,15 +0,0 @@ -using CmsEngine.Core; - -namespace CmsEngine.Application.ViewModels -{ - public class DocumentViewModel : BaseViewModel - { - public string Title { get; set; } - public string Slug { get; set; } - public string Description { get; set; } - public string HeaderImage { get; set; } - public string DocumentContent { get; set; } - public DocumentStatus Status { get; set; } - public string PublishedOn { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs b/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs deleted file mode 100644 index d9f9b959..00000000 --- a/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class ErrorViewModel - { - public string Title { get; } - public string Message { get; } - - public ErrorViewModel(string message) - { - Title = "Error"; - Message = message; - } - - public ErrorViewModel(string pageTitle, string message) - { - Title = pageTitle; - Message = message; - } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs b/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs deleted file mode 100644 index c1da0738..00000000 --- a/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class GoogleViewModel - { - public string GoogleAnalytics { get; set; } - public string GoogleRecaptchaSiteKey { get; set; } - public string GoogleRecaptchaSecretKey { get; set; } - - public bool HasAnalytics - { - get - { - return !string.IsNullOrWhiteSpace(GoogleAnalytics); - } - } - - public bool HasRecaptchaSiteKey - { - get - { - return !string.IsNullOrWhiteSpace(GoogleRecaptchaSiteKey); - } - } - - public bool HasRecaptchaSecretKey - { - get - { - return !string.IsNullOrWhiteSpace(GoogleRecaptchaSecretKey); - } - } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/IViewModel.cs b/CmsEngine.Application/Models/ViewModels/IViewModel.cs deleted file mode 100644 index 42a8b652..00000000 --- a/CmsEngine.Application/Models/ViewModels/IViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace CmsEngine.Application.ViewModels -{ - public interface IViewModel - { - int Id { get; set; } - - Guid VanityId { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs b/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs deleted file mode 100644 index c50f08ea..00000000 --- a/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Core; - -namespace CmsEngine.Application.ViewModels -{ - public class InstanceViewModel - { - public int Id { get; set; } - public string Name { get; set; } - public string Tagline { get; set; } - public string Description { get; set; } - public string HeaderImage { get; set; } - public string Culture { get; set; } - public string UrlFormat { get; set; } - public string DateFormat { get; set; } - public string SiteUrl { get; set; } - public int ArticleLimit { get; set; } - public string GoogleAnalytics { get; set; } - public ContactDetailsViewModel ContactDetails { get; set; } - public ApiDetailsViewModel ApiDetails { get; set; } - public DocumentViewModel SelectedDocument { get; set; } - public ContactForm ContactForm { get; set; } - public SocialMediaViewModel SocialMedia { get; set; } - public GoogleViewModel Google { get; set; } - - public string PageTitle { get; set; } - - public PaginatedList PagedPosts { get; set; } - public IEnumerable LatestPosts { get; set; } // TODO: Maybe I need 2 versions of this property - For Index and for Footer - public IEnumerable Pages { get; set; } - public IEnumerable Categories { get; set; } - public IEnumerable CategoriesWithPosts { get; set; } - public IEnumerable Tags { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs deleted file mode 100644 index b0c5f138..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class ChangePasswordViewModel - { - [Required] - [DataType(DataType.Password)] - [Display(Name = "Current password")] - public string OldPassword { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - - public string StatusMessage { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs deleted file mode 100644 index fb43801f..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class EnableAuthenticatorViewModel - { - [Required] - [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Text)] - [Display(Name = "Verification Code")] - public string Code { get; set; } - - [ReadOnly(true)] - public string SharedKey { get; set; } - - public string AuthenticatorUri { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs deleted file mode 100644 index 6983c35e..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Identity; - -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class ExternalLoginsViewModel - { - public IList CurrentLogins { get; set; } - - public IList OtherLogins { get; set; } - - public bool ShowRemoveButton { get; set; } - - public string StatusMessage { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs deleted file mode 100644 index 6f853d37..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class GenerateRecoveryCodesViewModel - { - public string[] RecoveryCodes { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs deleted file mode 100644 index a63cdbce..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class IndexViewModel - { - public string Username { get; set; } - - public string Name { get; set; } - - public string Surname { get; set; } - - public bool IsEmailConfirmed { get; set; } - - [Required] - [EmailAddress] - public string Email { get; set; } - - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } - - public string StatusMessage { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs deleted file mode 100644 index 91f065d5..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class RemoveLoginViewModel - { - public string LoginProvider { get; set; } - public string ProviderKey { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs deleted file mode 100644 index 339179ae..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class SetPasswordViewModel - { - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - - public string StatusMessage { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs b/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs deleted file mode 100644 index 95c6d01f..00000000 --- a/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace CmsEngine.Application.ViewModels.ManageViewModels -{ - public class TwoFactorAuthenticationViewModel - { - public bool HasAuthenticator { get; set; } - - public int RecoveryCodesLeft { get; set; } - - public bool Is2faEnabled { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/PageViewModel.cs b/CmsEngine.Application/Models/ViewModels/PageViewModel.cs deleted file mode 100644 index 8c993cd2..00000000 --- a/CmsEngine.Application/Models/ViewModels/PageViewModel.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class PageViewModel : DocumentViewModel - { - public UserViewModel Author { get; set; } = new UserViewModel(); - } -} diff --git a/CmsEngine.Application/Models/ViewModels/PostViewModel.cs b/CmsEngine.Application/Models/ViewModels/PostViewModel.cs deleted file mode 100644 index 00b1f6f9..00000000 --- a/CmsEngine.Application/Models/ViewModels/PostViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace CmsEngine.Application.ViewModels -{ - public class PostViewModel : DocumentViewModel - { - public IEnumerable Categories { get; set; } = new List(); - public IEnumerable Tags { get; set; } = new List(); - public UserViewModel Author { get; set; } = new UserViewModel(); - } -} - - diff --git a/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs b/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs deleted file mode 100644 index 17b2dba9..00000000 --- a/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class SitemapViewModel - { - public string Url { get; set; } - public string PublishedOn { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs b/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs deleted file mode 100644 index ee9dc8a7..00000000 --- a/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class SocialMediaViewModel - { - public string Facebook { get; set; } - public string Twitter { get; set; } - public string Instagram { get; set; } - public string LinkedIn { get; set; } - - public bool HasFacebook - { - get - { - return !string.IsNullOrWhiteSpace(Facebook); - } - } - - public bool HasTwitter - { - get - { - return !string.IsNullOrWhiteSpace(Twitter); - } - } - - public bool HasInstagram - { - get - { - return !string.IsNullOrWhiteSpace(Instagram); - } - } - - public bool HasLinkedIn - { - get - { - return !string.IsNullOrWhiteSpace(LinkedIn); - } - } - - public bool HasSocialMedia - { - get - { - return HasFacebook || HasTwitter || HasInstagram || HasLinkedIn; - } - } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/TagViewModel.cs b/CmsEngine.Application/Models/ViewModels/TagViewModel.cs deleted file mode 100644 index 44cfc835..00000000 --- a/CmsEngine.Application/Models/ViewModels/TagViewModel.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace CmsEngine.Application.ViewModels -{ - public class TagViewModel : BaseViewModel, IViewModel - { - public string Name { get; set; } - - public string Slug { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/UserViewModel.cs b/CmsEngine.Application/Models/ViewModels/UserViewModel.cs deleted file mode 100644 index edaed69f..00000000 --- a/CmsEngine.Application/Models/ViewModels/UserViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -using CmsEngine.Application.Attributes; - -namespace CmsEngine.Application.ViewModels -{ - public class UserViewModel : BaseViewModel, IViewModel - { - [Searchable, Orderable, ShowOnDataTable(0)] - public string Name { get; set; } - - [Searchable, Orderable, ShowOnDataTable(2)] - public string Surname { get; set; } - - [Searchable, Orderable, ShowOnDataTable(1)] - public string Email { get; set; } - - public string FullName - { - get - { - return $"{Name} {Surname}"; - } - } - - public string UserName { get; set; } - } -} diff --git a/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs b/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs deleted file mode 100644 index 8795cb7b..00000000 --- a/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -using CmsEngine.Core.Constants; - -namespace CmsEngine.Application.ViewModels -{ - public class WebsiteViewModel : BaseViewModel, IViewModel - { - public string Name { get; set; } - public string Tagline { get; set; } - public string Description { get; set; } - public string HeaderImagePath { get; set; } - public string Culture { get; set; } - public string UrlFormat { get; set; } = $"{CmsEngineConstants.SiteUrl}/{CmsEngineConstants.Type}/{CmsEngineConstants.Slug}"; - public string DateFormat { get; set; } = "MM/dd/yyyy"; - public string SiteUrl { get; set; } - } -} diff --git a/CmsEngine.Application/Services/CategoryService.cs b/CmsEngine.Application/Services/CategoryService.cs deleted file mode 100644 index e5b2c7f8..00000000 --- a/CmsEngine.Application/Services/CategoryService.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Attributes; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Extensions; -using CmsEngine.Application.Extensions.Mapper; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class CategoryService : Service, ICategoryService - { - private readonly IUnitOfWork _unitOfWork; - - public CategoryService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - } - - public async Task Delete(Guid id) - { - var item = await _unitOfWork.Categories.GetByIdAsync(id); - - var returnValue = new ReturnValue($"Category '{item.Name}' deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Categories.Delete(item); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the category"); - } - - return returnValue; - } - - public async Task DeleteRange(Guid[] ids) - { - var items = await _unitOfWork.Categories.GetByMultipleIdsAsync(ids); - - var returnValue = new ReturnValue($"Categories deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Categories.DeleteRange(items); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the categories"); - } - - return returnValue; - } - - public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) - { - if (!string.IsNullOrWhiteSpace(searchValue)) - { - var searchableProperties = typeof(CategoryTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); - items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); - } - - return items; - } - - public async Task> GetCategoriesWithPost() - { - logger.LogDebug("CategoryService > GetCategoriesWithPost()"); - var items = await _unitOfWork.Categories.GetCategoriesWithPostOrderedByName(); - logger.LogDebug("Categories loaded: {0}", items.Count()); - return items.MapToViewModelWithPost(); - } - - public async Task> GetCategoriesWithPostCount() - { - logger.LogDebug("CategoryService > GetCategoriesWithPostCount()"); - var items = await _unitOfWork.Categories.GetCategoriesWithPostCountOrderedByName(); - logger.LogDebug("Categories loaded: {0}", items.Count()); - return items.MapToViewModelWithPostCount(); - } - - public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) - { - var items = await _unitOfWork.Categories.GetAllAsync(); - int recordsTotal = items.Count(); - - if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) - { - items = FilterForDataTable(parameters.Search.Value, items); - } - - items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); - - return (items.MapToTableViewModel(), recordsTotal, items.Count()); - } - - public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) - { - try - { - switch (column) - { - case 1: - items = direction == "asc" ? items.OrderBy(o => o.Name) : items.OrderByDescending(o => o.Name); - break; - case 2: - items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); - break; - case 3: - items = direction == "asc" ? items.OrderBy(o => o.Description) : items.OrderByDescending(o => o.Description); - break; - default: - items = items.OrderBy(o => o.Name); - break; - } - } - catch - { - throw; - } - - return items; - } - - public async Task Save(CategoryEditModel categoryEditModel) - { - logger.LogDebug("CmsService > Save(CategoryEditModel: {0})", categoryEditModel.ToString()); - - var returnValue = new ReturnValue($"Category '{categoryEditModel.Name}' saved."); - - try - { - if (categoryEditModel.IsNew) - { - logger.LogDebug("New category"); - var category = categoryEditModel.MapToModel(); - category.WebsiteId = Instance.Id; - - await unitOfWork.Categories.Insert(category); - } - else - { - logger.LogDebug("Update category"); - var category = categoryEditModel.MapToModel(await unitOfWork.Categories.GetByIdAsync(categoryEditModel.VanityId)); - category.WebsiteId = Instance.Id; - - _unitOfWork.Categories.Update(category); - } - - await _unitOfWork.Save(); - logger.LogDebug("Category saved"); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while saving the category"); - } - - return returnValue; - } - - public CategoryEditModel SetupEditModel() - { - logger.LogDebug("CmsService > SetupEditModel()"); - return new CategoryEditModel(); - } - - public async Task SetupEditModel(Guid id) - { - logger.LogDebug("CmsService > SetupCategoryEditModel(id: {0})", id); - var item = await _unitOfWork.Categories.GetByIdAsync(id); - logger.LogDebug("Category: {0}", item.ToString()); - - return item?.MapToEditModel(); - } - } -} diff --git a/CmsEngine.Application/Services/EmailService.cs b/CmsEngine.Application/Services/EmailService.cs deleted file mode 100644 index e88f0aef..00000000 --- a/CmsEngine.Application/Services/EmailService.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Extensions.Mapper; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Core; -using CmsEngine.Data; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class EmailService : Service, IEmailService - { - private readonly IUnitOfWork _unitOfWork; - public EmailService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - } - - - public async Task> GetOrderedByDate() - { - logger.LogDebug("EmailService > GetOrderedByDate()"); - var items = await _unitOfWork.Emails.GetOrderedByDate(); - logger.LogDebug("E-mails loaded: {0}", items.Count()); - return items.MapToViewModel(); - } - - public async Task Save(ContactForm contactForm) - { - logger.LogDebug("CmsService > Save(contactForm: {0})", contactForm.ToString()); - - var returnValue = new ReturnValue($"E-mail saved."); - - try - { - logger.LogDebug("New e-mail"); - var message = contactForm.MapToModel(); - message.DateReceived = DateTime.Now; - - await _unitOfWork.Emails.Insert(message); - - await _unitOfWork.Save(); - logger.LogDebug("E-mail saved"); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while saving the e-mail"); - } - - return returnValue; - } - } -} diff --git a/CmsEngine.Application/Services/Interfaces/ICategoryService.cs b/CmsEngine.Application/Services/Interfaces/ICategoryService.cs deleted file mode 100644 index 17e77e78..00000000 --- a/CmsEngine.Application/Services/Interfaces/ICategoryService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Services -{ - public interface ICategoryService : IDataTableService - { - CategoryEditModel SetupEditModel(); - Task SetupEditModel(Guid id); - Task Delete(Guid id); - Task DeleteRange(Guid[] ids); - Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); - Task Save(CategoryEditModel categoryEditModel); - Task> GetCategoriesWithPostCount(); - Task> GetCategoriesWithPost(); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IDataTableService.cs b/CmsEngine.Application/Services/Interfaces/IDataTableService.cs deleted file mode 100644 index e57a14a4..00000000 --- a/CmsEngine.Application/Services/Interfaces/IDataTableService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Services -{ - public interface IDataTableService where T : BaseEntity - { - IEnumerable FilterForDataTable(string searchValue, IEnumerable items); - IEnumerable OrderForDataTable(int column, string direction, IEnumerable items); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IEmailService.cs b/CmsEngine.Application/Services/Interfaces/IEmailService.cs deleted file mode 100644 index 265fe8ea..00000000 --- a/CmsEngine.Application/Services/Interfaces/IEmailService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Core; - -namespace CmsEngine.Application.Services -{ - public interface IEmailService - { - Task Save(ContactForm contactForm); - Task> GetOrderedByDate(); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IPageService.cs b/CmsEngine.Application/Services/Interfaces/IPageService.cs deleted file mode 100644 index 394a0217..00000000 --- a/CmsEngine.Application/Services/Interfaces/IPageService.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Services -{ - public interface IPageService : IDataTableService - { - PageEditModel SetupEditModel(); - Task SetupEditModel(Guid id); - Task Delete(Guid id); - Task DeleteRange(Guid[] ids); - Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); - Task Save(PageEditModel pageEditModel); - Task GetBySlug(string slug); - Task> GetAllPublished(); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IPostService.cs b/CmsEngine.Application/Services/Interfaces/IPostService.cs deleted file mode 100644 index 056b7a00..00000000 --- a/CmsEngine.Application/Services/Interfaces/IPostService.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Services -{ - public interface IPostService : IDataTableService - { - Task SetupEditModel(); - Task SetupEditModel(Guid id); - Task Delete(Guid id); - Task DeleteRange(Guid[] ids); - Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); - Task Save(PostEditModel postEditModel); - Task> GetPublishedOrderedByDate(int count = 0); - Task GetBySlug(string slug); - Task> GetPublishedByCategoryForPagination(string categorySlug, int page = 1); - Task> GetPublishedByTagForPagination(string tagSlug, int page = 1); - Task> GetPublishedForPagination(int page = 1); - Task> GetPublishedLatestPosts(int count); - Task> FindPublishedForPaginationOrderByDateDescending(string searchTerm = "", int page = 1); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IService.cs b/CmsEngine.Application/Services/Interfaces/IService.cs deleted file mode 100644 index e481371e..00000000 --- a/CmsEngine.Application/Services/Interfaces/IService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using CmsEngine.Application.ViewModels; - -namespace CmsEngine.Application.Services -{ - public interface IService - { - InstanceViewModel Instance { get; } - UserViewModel CurrentUser { get; } - } -} diff --git a/CmsEngine.Application/Services/Interfaces/ITagService.cs b/CmsEngine.Application/Services/Interfaces/ITagService.cs deleted file mode 100644 index 6829c15d..00000000 --- a/CmsEngine.Application/Services/Interfaces/ITagService.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Services -{ - public interface ITagService : IDataTableService - { - TagEditModel SetupEditModel(); - Task SetupEditModel(Guid id); - Task Delete(Guid id); - Task DeleteRange(Guid[] ids); - Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); - Task Save(TagEditModel tagEditModel); - Task> GetAllTags(); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs b/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs deleted file mode 100644 index 26024689..00000000 --- a/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Application.Services -{ - public interface IWebsiteService : IDataTableService - { - WebsiteEditModel SetupEditModel(); - Task SetupEditModel(Guid id); - Task Delete(Guid id); - Task DeleteRange(Guid[] ids); - Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); - Task Save(WebsiteEditModel categoryEditModel); - } -} diff --git a/CmsEngine.Application/Services/Interfaces/IXmlService.cs b/CmsEngine.Application/Services/Interfaces/IXmlService.cs deleted file mode 100644 index 244728aa..00000000 --- a/CmsEngine.Application/Services/Interfaces/IXmlService.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Threading.Tasks; -using System.Xml.Linq; - -namespace CmsEngine.Application.Services -{ - public interface IXmlService - { - Task GenerateFeed(); - Task GenerateSitemap(); - } -} diff --git a/CmsEngine.Application/Services/PageService.cs b/CmsEngine.Application/Services/PageService.cs deleted file mode 100644 index e774a7b8..00000000 --- a/CmsEngine.Application/Services/PageService.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Attributes; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Extensions; -using CmsEngine.Application.Extensions.Mapper; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class PageService : Service, IPageService - { - private readonly IUnitOfWork _unitOfWork; - - public PageService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - } - - public async Task Delete(Guid id) - { - var item = await _unitOfWork.Pages.GetByIdAsync(id); - - var returnValue = new ReturnValue($"Page '{item.Title}' deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Pages.Delete(item); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the page"); - } - - return returnValue; - } - - public async Task DeleteRange(Guid[] ids) - { - var items = await _unitOfWork.Pages.GetByMultipleIdsAsync(ids); - - var returnValue = new ReturnValue($"Pages deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Pages.DeleteRange(items); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the pages"); - } - - return returnValue; - } - - public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) - { - if (!string.IsNullOrWhiteSpace(searchValue)) - { - var searchableProperties = typeof(PageTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); - items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); - } - return items; - } - - public async Task> GetAllPublished() - { - logger.LogDebug("PageService > GetPagesByStatusReadOnly()"); - var items = await _unitOfWork.Pages.GetByStatusOrderByDescending(DocumentStatus.Published); - logger.LogDebug("Pages loaded: {0}", items.Count()); - return items.MapToViewModel(); - } - - public async Task GetBySlug(string slug) - { - logger.LogDebug($"PageService > GetBySlug({slug})"); - var item = await _unitOfWork.Pages.GetBySlug(slug); - return item?.MapToViewModel(); - } - - public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) - { - var items = await _unitOfWork.Pages.GetForDataTable(); - int recordsTotal = items.Count(); - if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) - { - items = FilterForDataTable(parameters.Search.Value, items); - } - items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); - return (items.MapToTableViewModel(), recordsTotal, items.Count()); - } - - public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) - { - try - { - switch (column) - { - case 1: - items = direction == "asc" ? items.OrderBy(o => o.Title) : items.OrderByDescending(o => o.Title); - break; - case 2: - items = direction == "asc" ? items.OrderBy(o => o.Description) : items.OrderByDescending(o => o.Description); - break; - case 3: - items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); - break; - //case 4: - // items = direction == "asc" ? items.OrderBy(o => o.Author.FullName) : items.OrderByDescending(o => o.Author.FullName); - // break; - case 5: - items = direction == "asc" ? items.OrderBy(o => o.PublishedOn) : items.OrderByDescending(o => o.PublishedOn); - break; - case 6: - items = direction == "asc" ? items.OrderBy(o => o.Status) : items.OrderByDescending(o => o.Status); - break; - default: - items = items.OrderByDescending(o => o.PublishedOn); - break; - } - } - catch - { - throw; - } - - return items; - } - - public async Task Save(PageEditModel pageEditModel) - { - logger.LogDebug("PageService > Save(PageEditModel: {0})", pageEditModel.ToString()); - - var returnValue = new ReturnValue($"Page '{pageEditModel.Title}' saved."); - - try - { - if (pageEditModel.IsNew) - { - logger.LogDebug("New page"); - var page = pageEditModel.MapToModel(); - page.WebsiteId = Instance.Id; - - await PrepareRelatedPropertiesAsync(page); - await unitOfWork.Pages.Insert(page); - } - else - { - logger.LogDebug("Update page"); - var page = pageEditModel.MapToModel(await unitOfWork.Pages.GetForSavingById(pageEditModel.VanityId)); - page.WebsiteId = Instance.Id; - - _unitOfWork.Pages.RemoveRelatedItems(page); - await PrepareRelatedPropertiesAsync(page); - _unitOfWork.Pages.Update(page); - } - - await _unitOfWork.Save(); - logger.LogDebug("Page saved"); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while saving the page"); - } - - return returnValue; - } - - public PageEditModel SetupEditModel() - { - logger.LogDebug("PageService > SetupEditModel()"); - return new PageEditModel(); - } - - public async Task SetupEditModel(Guid id) - { - logger.LogDebug("PageService > SetupPageEditModel(id: {0})", id); - var item = await _unitOfWork.Pages.GetByIdAsync(id); - logger.LogDebug("Page: {0}", item.ToString()); - return item?.MapToEditModel(); - } - - private async Task PrepareRelatedPropertiesAsync(Page page) - { - var user = await GetCurrentUserAsync(); - page.PageApplicationUsers.Clear(); - page.PageApplicationUsers.Add(new PageApplicationUser - { - PageId = page.Id, - ApplicationUserId = user.Id - }); - } - } -} diff --git a/CmsEngine.Application/Services/PostService.cs b/CmsEngine.Application/Services/PostService.cs deleted file mode 100644 index 60d72b43..00000000 --- a/CmsEngine.Application/Services/PostService.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Attributes; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Extensions; -using CmsEngine.Application.Extensions.Mapper; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class PostService : Service, IPostService - { - private readonly IUnitOfWork _unitOfWork; - - public PostService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - } - - public async Task Delete(Guid id) - { - var item = await _unitOfWork.Posts.GetByIdAsync(id); - - var returnValue = new ReturnValue($"Post '{item.Title}' deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Posts.Delete(item); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the post"); - } - - return returnValue; - } - - public async Task DeleteRange(Guid[] ids) - { - var items = await _unitOfWork.Posts.GetByMultipleIdsAsync(ids); - - var returnValue = new ReturnValue($"Posts deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Posts.DeleteRange(items); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the posts"); - } - - return returnValue; - } - - public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) - { - if (!string.IsNullOrWhiteSpace(searchValue)) - { - var searchableProperties = typeof(PostTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); - items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); - } - return items; - } - - public async Task GetBySlug(string slug) - { - logger.LogDebug($"PostService > GetBySlug({slug})"); - var item = await _unitOfWork.Posts.GetBySlug(slug); - return item?.MapToViewModel(); - } - - public async Task> GetPublishedOrderedByDate(int count = 0) - { - logger.LogDebug("PostService > GetByStatus(count: {0})", count); - var items = await _unitOfWork.Posts.GetByStatusOrderByDescending(DocumentStatus.Published); - logger.LogDebug("Posts loaded: {0}", items.Count()); - - return items.MapToEditModel(); - } - - public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) - { - var items = await _unitOfWork.Posts.GetForDataTable(); - int recordsTotal = items.Count(); - if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) - { - items = FilterForDataTable(parameters.Search.Value, items); - } - items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); - return (items.MapToTableViewModel(), recordsTotal, items.Count()); - } - - public async Task> GetPublishedByCategoryForPagination(string categorySlug, int page = 1) - { - logger.LogDebug("CmsService > GetPublishedByCategoryForPagination(categorySlug: {0}, page: {1})", categorySlug, page); - var posts = await _unitOfWork.Posts.GetPublishedByCategoryForPagination(categorySlug, page, Instance.ArticleLimit); - return new PaginatedList(posts.Items.MapToViewModelForPartialView(), posts.Count, page, Instance.ArticleLimit); - } - - public async Task> GetPublishedByTagForPagination(string tagSlug, int page = 1) - { - logger.LogDebug("CmsService > GetPublishedByTagForPagination(tagSlug: {0}, page: {1})", tagSlug, page); - var posts = await _unitOfWork.Posts.GetPublishedByTagForPagination(tagSlug, page, Instance.ArticleLimit); - return new PaginatedList(posts.Items.MapToViewModelForPartialViewForTags(), posts.Count, page, Instance.ArticleLimit); - } - - public async Task> GetPublishedForPagination(int page = 1) - { - logger.LogDebug("CmsService > GetPublishedForPagination(page: {0})", page); - var posts = await _unitOfWork.Posts.GetPublishedForPagination(page, Instance.ArticleLimit); - return new PaginatedList(posts.Items.MapToViewModelForPartialView(), posts.Count, page, Instance.ArticleLimit); - } - - public async Task> GetPublishedLatestPosts(int count) - { - logger.LogDebug("CmsService > GetPublishedLatestPosts(count: {0})", count); - var posts = await _unitOfWork.Posts.GetPublishedLatestPosts(count); - return posts.MapToViewModelForPartialView(); - } - - public async Task> FindPublishedForPaginationOrderByDateDescending(string searchTerm = "", int page = 1) - { - logger.LogDebug("CmsService > FindPublishedForPaginationOrderByDateDescending(page: {0}, searchTerm: {1})", page, searchTerm); - var posts = await _unitOfWork.Posts.FindPublishedForPaginationOrderByDateDescending(page, searchTerm, Instance.ArticleLimit); - return new PaginatedList(posts.Items.MapToViewModelForPartialView(), posts.Count, page, Instance.ArticleLimit); - } - - public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) - { - try - { - switch (column) - { - case 1: - items = direction == "asc" ? items.OrderBy(o => o.Title) : items.OrderByDescending(o => o.Title); - break; - case 2: - items = direction == "asc" ? items.OrderBy(o => o.Description) : items.OrderByDescending(o => o.Description); - break; - case 3: - items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); - break; - //case 4: - // items = direction == "asc" ? items.OrderBy(o => o.Author.FullName) : items.OrderByDescending(o => o.Author.FullName); - // break; - case 5: - items = direction == "asc" ? items.OrderBy(o => o.PublishedOn) : items.OrderByDescending(o => o.PublishedOn); - break; - case 6: - items = direction == "asc" ? items.OrderBy(o => o.Status) : items.OrderByDescending(o => o.Status); - break; - default: - items = items.OrderByDescending(o => o.PublishedOn); - break; - } - } - catch - { - throw; - } - - return items; - } - - public async Task Save(PostEditModel postEditModel) - { - logger.LogDebug("PostService > Save(PostEditModel: {0})", postEditModel.ToString()); - var returnValue = new ReturnValue($"Post '{postEditModel.Title}' saved."); - - try - { - if (postEditModel.IsNew) - { - logger.LogDebug("New post"); - var post = postEditModel.MapToModel(); - post.WebsiteId = Instance.Id; - - await PrepareRelatedPropertiesAsync(postEditModel, post); - await unitOfWork.Posts.Insert(post); - } - else - { - logger.LogDebug("Update post"); - var post = postEditModel.MapToModel(await unitOfWork.Posts.GetForSavingById(postEditModel.VanityId)); - post.WebsiteId = Instance.Id; - - _unitOfWork.Posts.RemoveRelatedItems(post); - await PrepareRelatedPropertiesAsync(postEditModel, post); - _unitOfWork.Posts.Update(post); - } - - await _unitOfWork.Save(); - logger.LogDebug("Post saved"); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while saving the post"); - } - - return returnValue; - } - - public async Task SetupEditModel() - { - logger.LogDebug("PostService > SetupEditModel()"); - return new PostEditModel - { - Categories = (await unitOfWork.Categories.GetAllAsync()).MapToViewModelSimple().PopulateCheckboxList(), - Tags = (await unitOfWork.Tags.GetAllAsync()).MapToViewModelSimple().PopulateSelectList() - }; - } - - public async Task SetupEditModel(Guid id) - { - logger.LogDebug("PostService > SetupPostEditModel(id: {0})", id); - var item = await _unitOfWork.Posts.GetForEditingById(id); - logger.LogDebug("Post: {0}", item.ToString()); - var postEditModel = item.MapToEditModel(); - postEditModel.Categories = (await unitOfWork.Categories.GetAllAsync()).MapToViewModelSimple().PopulateCheckboxList(postEditModel.SelectedCategories); - postEditModel.Tags = (await unitOfWork.Tags.GetAllAsync()).MapToViewModelSimple().PopulateSelectList(postEditModel.SelectedTags); - - return postEditModel; - } - - private async Task PrepareRelatedPropertiesAsync(PostEditModel postEditModel, Post post) - { - post.PostCategories.Clear(); - if (postEditModel.SelectedCategories != null) - { - var categoryIds = await _unitOfWork.Categories.GetIdsByMultipleGuidsAsync(postEditModel.SelectedCategories.ToList().ConvertAll(Guid.Parse)); - foreach (int categoryId in categoryIds) - { - post.PostCategories.Add(new PostCategory - { - PostId = post.Id, - CategoryId = categoryId - }); - } - } - - post.PostTags.Clear(); - if (postEditModel.SelectedTags != null) - { - var tagIds = await _unitOfWork.Tags.GetIdsByMultipleGuidsAsync(postEditModel.SelectedTags.ToList().ConvertAll(Guid.Parse)); - foreach (int tagId in tagIds) - { - post.PostTags.Add(new PostTag - { - PostId = post.Id, - TagId = tagId - }); - } - } - - var user = await GetCurrentUserAsync(); - post.PostApplicationUsers.Clear(); - post.PostApplicationUsers.Add(new PostApplicationUser - { - PostId = post.Id, - ApplicationUserId = user.Id - }); - } - } -} diff --git a/CmsEngine.Application/Services/Service.cs b/CmsEngine.Application/Services/Service.cs deleted file mode 100644 index 539d8364..00000000 --- a/CmsEngine.Application/Services/Service.cs +++ /dev/null @@ -1,161 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.ViewModels; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Exceptions; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class Service : IService - { - private readonly IHttpContextAccessor httpContextAccessor; - private readonly IMemoryCache memoryCache; - private readonly string instanceHost; - private readonly string instanceKey; - - protected readonly IUnitOfWork unitOfWork; - protected readonly ILogger logger; - - public InstanceViewModel Instance - { - get - { - return GetInstance(); - } - } - public UserViewModel CurrentUser - { - get - { - return GetCurrentUserViewModelAsync().GetAwaiter().GetResult(); - } - } - - public Service(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - { - unitOfWork = uow ?? throw new ArgumentNullException(nameof(uow)); - httpContextAccessor = hca; - logger = loggerFactory.CreateLogger("Service"); - this.memoryCache = memoryCache; - - instanceHost = httpContextAccessor.HttpContext.Request.Host.Host; - instanceKey = $"{CmsEngineConstants.CacheKey.Instance}_{instanceHost}"; - } - - internal async Task GetCurrentUserAsync() - { - logger.LogDebug("GetCurrentUserAsync() for {0}", httpContextAccessor.HttpContext.User.Identity.Name); - - try - { - return await unitOfWork.Users.FindByNameAsync(httpContextAccessor.HttpContext.User.Identity.Name); - } - catch (Exception ex) - { - logger.LogError(ex, "Error when trying to load CurrentUser"); - throw; - } - } - - protected void SaveInstanceToCache(object instance) - { - var timeSpan = TimeSpan.FromDays(7); //TODO: Perhaps set this in the config file. Or DB - logger.LogDebug("Adding '{0}' to cache with expiration date to {1}", instanceKey, DateTime.Now.AddMilliseconds(timeSpan.TotalMilliseconds).ToString()); - var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(timeSpan); - memoryCache.Set(instanceKey, instance, cacheEntryOptions); - } - - private InstanceViewModel GetInstance() - { - logger.LogDebug("GetInstanceAsync()"); - - Website website; - InstanceViewModel instance; - - try - { - logger.LogDebug("Loading '{0}' from cache", instanceKey); - if (!memoryCache.TryGetValue(instanceKey, out instance)) - { - logger.LogDebug("Empty cache for '{0}'. Loading instance from DB", instanceKey); - website = unitOfWork.Websites.GetWebsiteInstanceByHost(instanceHost); - - if (website == null) - { - throw new NotFoundException($"Instance for '{instanceHost}' not found"); - } - - instance = new InstanceViewModel - { - Id = website.Id, - Name = website.Name, - Description = website.Description, - Tagline = website.Tagline, - HeaderImage = website.HeaderImage, - Culture = website.Culture, - UrlFormat = website.UrlFormat, - DateFormat = website.DateFormat, - SiteUrl = website.SiteUrl, - ArticleLimit = website.ArticleLimit, - PageTitle = website.Name, - ContactDetails = new ContactDetailsViewModel - { - Address = website.Address, - Phone = website.Phone, - Email = website.Email, - }, - ApiDetails = new ApiDetailsViewModel - { - FacebookAppId = website.FacebookAppId, - FacebookApiVersion = website.FacebookApiVersion, - DisqusShortName = website.DisqusShortName - }, - SocialMedia = new SocialMediaViewModel - { - Facebook = website.Facebook, - Twitter = website.Twitter, - Instagram = website.Instagram, - LinkedIn = website.LinkedIn - }, - Google = new GoogleViewModel - { - GoogleAnalytics = website.GoogleAnalytics, - GoogleRecaptchaSiteKey = website.GoogleRecaptchaSiteKey, - GoogleRecaptchaSecretKey = website.GoogleRecaptchaSecretKey - } - }; - - SaveInstanceToCache(instance); - } - } - catch (Exception ex) - { - logger.LogError(ex, "Error when trying to load Instance"); - throw; - } - - return instance; - } - - private async Task GetCurrentUserViewModelAsync() - { - var user = await GetCurrentUserAsync(); - - return user == null - ? null - : new UserViewModel - { - VanityId = Guid.Parse(user.Id), - Name = user.Name, - Surname = user.Surname, - Email = user.Email, - UserName = user.UserName - }; - } - } -} diff --git a/CmsEngine.Application/Services/TagService.cs b/CmsEngine.Application/Services/TagService.cs deleted file mode 100644 index ee86e351..00000000 --- a/CmsEngine.Application/Services/TagService.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Attributes; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Extensions; -using CmsEngine.Application.Extensions.Mapper; -using CmsEngine.Application.ViewModels; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class TagService : Service, ITagService - { - private readonly IUnitOfWork _unitOfWork; - - public TagService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - } - - public async Task Delete(Guid id) - { - var item = await _unitOfWork.Tags.GetByIdAsync(id); - - var returnValue = new ReturnValue($"Tag '{item.Name}' deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Tags.Delete(item); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the tag"); - } - - return returnValue; - } - - public async Task DeleteRange(Guid[] ids) - { - var items = await _unitOfWork.Tags.GetByMultipleIdsAsync(ids); - - var returnValue = new ReturnValue($"Tags deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Tags.DeleteRange(items); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the tags"); - } - - return returnValue; - } - - public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) - { - if (!string.IsNullOrWhiteSpace(searchValue)) - { - var searchableProperties = typeof(TagTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); - items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); - } - return items; - } - - public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) - { - var items = await _unitOfWork.Tags.GetAllAsync(); - int recordsTotal = items.Count(); - if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) - { - items = FilterForDataTable(parameters.Search.Value, items); - } - items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); - return (items.MapToTableViewModel(), recordsTotal, items.Count()); - } - - public async Task> GetAllTags() - { - logger.LogDebug("TagService > GetAllTags()"); - var items = await _unitOfWork.Tags.GetAllAsync(); - logger.LogDebug("Tags loaded: {0}", items.Count()); - return items.MapToViewModel(); - } - - public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) - { - try - { - switch (column) - { - case 1: - items = direction == "asc" ? items.OrderBy(o => o.Name) : items.OrderByDescending(o => o.Name); - break; - case 2: - items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); - break; - default: - items = items.OrderBy(o => o.Name); - break; - } - } - catch - { - throw; - } - - return items; - } - - public async Task Save(TagEditModel tagEditModel) - { - logger.LogDebug("CmsService > Save(TagEditModel: {0})", tagEditModel.ToString()); - - var returnValue = new ReturnValue($"Tag '{tagEditModel.Name}' saved."); - - try - { - if (tagEditModel.IsNew) - { - logger.LogDebug("New tag"); - var tag = tagEditModel.MapToModel(); - tag.WebsiteId = Instance.Id; - - await unitOfWork.Tags.Insert(tag); - } - else - { - logger.LogDebug("Update tag"); - var tag = tagEditModel.MapToModel(await unitOfWork.Tags.GetByIdAsync(tagEditModel.VanityId)); - tag.WebsiteId = Instance.Id; - - _unitOfWork.Tags.Update(tag); - } - - await _unitOfWork.Save(); - logger.LogDebug("Tag saved"); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while saving the tag"); - } - - return returnValue; - } - - public TagEditModel SetupEditModel() - { - logger.LogDebug("CmsService > SetupEditModel()"); - return new TagEditModel(); - } - - public async Task SetupEditModel(Guid id) - { - logger.LogDebug("CmsService > SetupTagEditModel(id: {0})", id); - var item = await _unitOfWork.Tags.GetByIdAsync(id); - logger.LogDebug("Tag: {0}", item.ToString()); - return item?.MapToEditModel(); - } - } -} diff --git a/CmsEngine.Application/Services/WebsiteService.cs b/CmsEngine.Application/Services/WebsiteService.cs deleted file mode 100644 index 6ca22cdd..00000000 --- a/CmsEngine.Application/Services/WebsiteService.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Attributes; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Extensions; -using CmsEngine.Application.Extensions.Mapper; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class WebsiteService : Service, IWebsiteService - { - private readonly IUnitOfWork _unitOfWork; - private readonly IMemoryCache _memoryCache; - - public WebsiteService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - _memoryCache = memoryCache; - } - - public async Task Delete(Guid id) - { - var item = await _unitOfWork.Websites.GetByIdAsync(id); - - var returnValue = new ReturnValue($"Website '{item.Name}' deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Websites.Delete(item); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the website"); - } - - return returnValue; - } - - public async Task DeleteRange(Guid[] ids) - { - var items = await _unitOfWork.Websites.GetByMultipleIdsAsync(ids); - - var returnValue = new ReturnValue($"Websites deleted at {DateTime.Now.ToString("T")}."); - - try - { - _unitOfWork.Websites.DeleteRange(items); - await _unitOfWork.Save(); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while deleting the websites"); - } - - return returnValue; - } - - public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) - { - if (!string.IsNullOrWhiteSpace(searchValue)) - { - var searchableProperties = typeof(WebsiteTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); - items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); - } - return items; - } - - public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) - { - var items = await _unitOfWork.Websites.GetForDataTable(); - int recordsTotal = items.Count(); - if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) - { - items = FilterForDataTable(parameters.Search.Value, items); - } - items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); - return (items.MapToTableViewModel(), recordsTotal, items.Count()); - } - - public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) - { - try - { - switch (column) - { - case 1: - items = direction == "asc" ? items.OrderBy(o => o.Name) : items.OrderByDescending(o => o.Name); - break; - case 2: - items = direction == "asc" ? items.OrderBy(o => o.Tagline) : items.OrderByDescending(o => o.Tagline); - break; - case 3: - items = direction == "asc" ? items.OrderBy(o => o.Culture) : items.OrderByDescending(o => o.Culture); - break; - default: - items = items.OrderBy(o => o.Name); - break; - } - } - catch - { - throw; - } - - return items; - } - - public async Task Save(WebsiteEditModel websiteEditModel) - { - logger.LogDebug("CmsService > Save(WebsiteEditModel: {0})", websiteEditModel.ToString()); - - var returnValue = new ReturnValue($"Website '{websiteEditModel.Name}' saved."); - - try - { - if (websiteEditModel.IsNew) - { - logger.LogDebug("New website"); - var website = websiteEditModel.MapToModel(); - - await unitOfWork.Websites.Insert(website); - } - else - { - logger.LogDebug("Update website"); - var website = websiteEditModel.MapToModel(await unitOfWork.Websites.GetByIdAsync(websiteEditModel.VanityId)); - - _unitOfWork.Websites.Update(website); - } - - await _unitOfWork.Save(); - - SaveInstanceToCache(websiteEditModel); - logger.LogDebug("Website saved"); - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - returnValue.SetErrorMessage("An error has occurred while saving the website"); - } - - return returnValue; - } - - public WebsiteEditModel SetupEditModel() - { - logger.LogDebug("CmsService > SetupEditModel()"); - return new WebsiteEditModel(); - } - - public async Task SetupEditModel(Guid id) - { - logger.LogDebug("CmsService > SetupEditModel(id: {0})", id); - var item = await _unitOfWork.Websites.GetByIdAsync(id); - logger.LogDebug("Website: {0}", item.ToString()); - return item?.MapToEditModel(); - } - } -} diff --git a/CmsEngine.Application/Services/XmlService.cs b/CmsEngine.Application/Services/XmlService.cs deleted file mode 100644 index b09ae7a7..00000000 --- a/CmsEngine.Application/Services/XmlService.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Xml.Linq; -using CmsEngine.Application.ViewModels; -using CmsEngine.Data; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Application.Services -{ - public class XmlService : Service, IXmlService - { - private readonly IUnitOfWork _unitOfWork; - - public XmlService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) - : base(uow, hca, loggerFactory, memoryCache) - { - _unitOfWork = uow; - } - - public async Task GenerateFeed() - { - var articleList = new List(); - - foreach (var item in await _unitOfWork.Posts.GetPublishedPostsOrderByDescending(o => o.PublishedOn)) - { - string url = FormatUrl("blog/post", item.Slug); - articleList.Add(new XElement("item", - new XElement("title", item.Title), - new XElement("link", url), - new XElement("description", item.DocumentContent), - new XElement("pubDate", item.PublishedOn.ToString("r")), - new XElement("guid", url))); - } - - return new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("rss", - new XElement("channel", - new XElement("title", Instance.Name), - new XElement("link", FormatUrl(string.Empty)), - new XElement("description", Instance.Tagline), - new XElement("language", Instance.Culture.ToLowerInvariant()), - new XElement("generator", "MultiCMS"), - articleList - ), - new XAttribute("version", "2.0"))); - } - - public async Task GenerateSitemap() - { - var items = new List(); - - var orderedPosts = await _unitOfWork.Posts.GetPublishedPostsOrderByDescending(o => o.PublishedOn); - items.AddRange(orderedPosts.Select(x => new SitemapViewModel - { - Url = FormatUrl("blog/post", x.Slug), - PublishedOn = x.PublishedOn.ToString("yyyy-MM-dd") - })); - - var orderedPages = await _unitOfWork.Pages.GetOrderByDescending(o => o.PublishedOn); - items.AddRange(orderedPages.Select(x => new SitemapViewModel - { - Url = FormatUrl("page", x.Slug), - PublishedOn = x.PublishedOn.ToString("yyyy-MM-dd") - })); - - XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9"; - return new XDocument(new XDeclaration("1.0", "utf-8", null), - new XElement(ns + "urlset", - from item in items - select new XElement(ns + "url", - new XElement(ns + "loc", item.Url), - new XElement(ns + "lastmod", item.PublishedOn), - new XElement(ns + "changefreq", "monthly") - ))); - - } - - private string FormatUrl(string type, string slug = "") - { - string url = ""; - - if (!string.IsNullOrWhiteSpace(Instance.UrlFormat)) - { - url = Instance.UrlFormat.Replace("[site_url]", Instance.SiteUrl) - .Replace("[culture]", Instance.Culture) - .Replace("[short_culture]", Instance.Culture.Substring(0, 2)) - .Replace("[type]", type) - .Replace("[slug]", slug); - } - - url = url.EndsWith("/") ? url.Substring(0, url.LastIndexOf('/')) : url; - - return url; - } - } -} diff --git a/CmsEngine.Core/CmsEngine.Core.csproj b/CmsEngine.Core/CmsEngine.Core.csproj deleted file mode 100644 index 9584ba00..00000000 --- a/CmsEngine.Core/CmsEngine.Core.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net5.0 - - - - - - - - - diff --git a/CmsEngine.Core/Constants/CmsEngineConstants.cs b/CmsEngine.Core/Constants/CmsEngineConstants.cs deleted file mode 100644 index f5c2fba0..00000000 --- a/CmsEngine.Core/Constants/CmsEngineConstants.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - public static class CmsEngineConstants - { - public const string SiteUrl = "[site_url]"; - public const string Culture = "[culture]"; - public const string ShortCulture = "[short_culture]"; - public const string Type = "[type]"; - public const string Slug = "[slug]"; - - public const string WwwDot = "www."; - public const string Localhost = "localhost"; - - public static class ImagePath - { - public const string Default = "/image/{0}/{1}"; - public const string Path640 = "/image/{0}/640x426_{1}"; - public const string Path320 = "/image/{0}/320x213_{1}"; - public const string Path120 = "/image/{0}/120x120_{1}"; - } - - public static class CacheKey - { - public const string Instance = "Instance"; - } - } -} diff --git a/CmsEngine.Core/Constants/ContentTypeOptionsConstants.cs b/CmsEngine.Core/Constants/ContentTypeOptionsConstants.cs deleted file mode 100644 index 6230b92b..00000000 --- a/CmsEngine.Core/Constants/ContentTypeOptionsConstants.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - /// - /// X-Content-Type-Options-related constants. - /// - public static class ContentTypeOptionsConstants - { - /// - /// Header value for X-Content-Type-Options - /// - public static readonly string Header = "X-Content-Type-Options"; - - /// - /// Disables content sniffing - /// - public static readonly string NoSniff = "nosniff"; - - } -} diff --git a/CmsEngine.Core/Constants/FrameOptionsConstants.cs b/CmsEngine.Core/Constants/FrameOptionsConstants.cs deleted file mode 100644 index 865e4464..00000000 --- a/CmsEngine.Core/Constants/FrameOptionsConstants.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - /// - /// X-Frame-Options-related constants. - /// - public static class FrameOptionsConstants - { - /// - /// The header value for X-Frame-Options - /// - public static readonly string Header = "X-Frame-Options"; - - /// - /// The page cannot be displayed in a frame, regardless of the site attempting to do so. - /// - public static readonly string Deny = "DENY"; - - /// - /// The page can only be displayed in a frame on the same origin as the page itself. - /// - public static readonly string SameOrigin = "SAMEORIGIN"; - - /// - /// The page can only be displayed in a frame on the specified origin. {0} specifies the format string - /// - public static readonly string AllowFromUri = "ALLOW-FROM {0}"; - } -} diff --git a/CmsEngine.Core/Constants/MessageConstants.cs b/CmsEngine.Core/Constants/MessageConstants.cs deleted file mode 100644 index 244aa30f..00000000 --- a/CmsEngine.Core/Constants/MessageConstants.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - public static class MessageConstants - { - public const string InfoMessage = "InfoMessage"; - public const string SuccessMessage = "SuccessMessage"; - public const string WarningMessage = "WarningMessage"; - public const string DangerMessage = "DangerMessage"; - } -} diff --git a/CmsEngine.Core/Constants/ServerConstants.cs b/CmsEngine.Core/Constants/ServerConstants.cs deleted file mode 100644 index b8175651..00000000 --- a/CmsEngine.Core/Constants/ServerConstants.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - /// - /// Server headery-related constants. - /// - public static class ServerConstants - { - /// - /// The header value for X-Powered-By - /// - public static readonly string Header = "Server"; - } -} diff --git a/CmsEngine.Core/Constants/StrictTransportSecurityConstants.cs b/CmsEngine.Core/Constants/StrictTransportSecurityConstants.cs deleted file mode 100644 index 3851c0e3..00000000 --- a/CmsEngine.Core/Constants/StrictTransportSecurityConstants.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - /// - /// Strict-Transport-Security-related constants. - /// - public static class StrictTransportSecurityConstants - { - /// - /// Header value for Strict-Transport-Security - /// - public static readonly string Header = "Strict-Transport-Security"; - - /// - /// Tells the user-agent to cache the domain in the STS list for the provided number of seconds {0} - /// - public static readonly string MaxAge = "max-age={0}"; - - /// - /// Tells the user-agent to cache the domain in the STS list for the provided number of seconds {0} and include any sub-domains. - /// - public static readonly string MaxAgeIncludeSubdomains = "max-age={0}; includeSubDomains"; - - /// - /// Tells the user-agent to remove, or not cache the host in the STS cache. - /// - public static readonly string NoCache = "max-age=0"; - } -} diff --git a/CmsEngine.Core/Constants/XssProtectionConstants.cs b/CmsEngine.Core/Constants/XssProtectionConstants.cs deleted file mode 100644 index 879a86f5..00000000 --- a/CmsEngine.Core/Constants/XssProtectionConstants.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace CmsEngine.Core.Constants -{ - /// - /// X-XSS-Protection-related constants. - /// - public static class XssProtectionConstants - { - /// - /// Header value for X-XSS-Protection - /// - public static readonly string Header = "X-XSS-Protection"; - - /// - /// Enables the XSS Protections - /// - public static readonly string Enabled = "1"; - - /// - /// Disables the XSS Protections offered by the user-agent. - /// - public static readonly string Disabled = "0"; - - /// - /// Enables XSS protections and instructs the user-agent to block the response in the event that script has been inserted from user input, instead of sanitizing. - /// - public static readonly string Block = "1; mode=block"; - - /// - /// A partially supported directive that tells the user-agent to report potential XSS attacks to a single URL. Data will be POST'd to the report URL in JSON format. - /// {0} specifies the report url, including protocol - /// - public static readonly string Report = "1; report={0}"; - } -} diff --git a/CmsEngine.Core/Exceptions/EmailException.cs b/CmsEngine.Core/Exceptions/EmailException.cs deleted file mode 100644 index b6aea9a9..00000000 --- a/CmsEngine.Core/Exceptions/EmailException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace CmsEngine.Core.Exceptions -{ - public class EmailException : Exception - { - public EmailException(string message) : base(message) - { - } - - public EmailException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/CmsEngine.Core/Exceptions/NotFoundException.cs b/CmsEngine.Core/Exceptions/NotFoundException.cs deleted file mode 100644 index 4368b264..00000000 --- a/CmsEngine.Core/Exceptions/NotFoundException.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace CmsEngine.Core.Exceptions -{ - public class NotFoundException : Exception - { - public NotFoundException(string message) : base(message) - { - } - - public NotFoundException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/CmsEngine.Core/Extensions/BooleanExtensions.cs b/CmsEngine.Core/Extensions/BooleanExtensions.cs deleted file mode 100644 index a1690afd..00000000 --- a/CmsEngine.Core/Extensions/BooleanExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace CmsEngine.Extensions -{ - public static class BooleanExtensions - { - /// - /// Convert boolean values to "Yes" or "No" - /// - /// - /// - public static string ToYesNo(this bool value) - { - return value ? "Yes" : "No"; - } - } -} diff --git a/CmsEngine.Core/Extensions/EnumExtensions.cs b/CmsEngine.Core/Extensions/EnumExtensions.cs deleted file mode 100644 index 8dab0ff2..00000000 --- a/CmsEngine.Core/Extensions/EnumExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq; -using System.Reflection; - -namespace CmsEngine.Core.Extensions -{ - public static class EnumExtensions - { - /// - /// Returns the Name property from DisplayAttribute - /// - /// - /// - public static string GetName(this Enum value) - { - // Get attributes - var field = value.GetType().GetField(value.ToString()); - var attributes = field.GetCustomAttributes(false); - - dynamic displayAttribute = null; - - if (attributes.Any()) - { - displayAttribute = attributes.ElementAt(0); - } - - // Return name - return displayAttribute?.Name ?? field.Name; - } - - /// - /// Returns the value of a DescriptionAttribute for a given Enum value - /// - /// Source: http://blogs.msdn.com/b/abhinaba/archive/2005/10/21/483337.aspx - /// - /// - public static string GetDescription(this Enum value) - { - - Type type = value.GetType(); - MemberInfo[] memberInfo = type.GetMember(value.ToString()); - - if (memberInfo != null && memberInfo.Length > 0) - { - object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false).ToArray(); - - if (attrs != null && attrs.Length > 0) - { - return ((DescriptionAttribute)attrs[0]).Description; - } - } - - return value.ToString(); - - } - } -} diff --git a/CmsEngine.Core/Extensions/IEnumerableExtensions.cs b/CmsEngine.Core/Extensions/IEnumerableExtensions.cs deleted file mode 100644 index da12d7b4..00000000 --- a/CmsEngine.Core/Extensions/IEnumerableExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace CmsEngine.Core.Extensions -{ - public static class IEnumerableExtensions - { - public static IEnumerable Except(this IEnumerable items, IEnumerable other, Func getKeyFunc) - { - if (other == null) - { - return items; - } - - return items - .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems }) - .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp }) - .Where(t => t.temp == null || t.temp.Equals(default(T))) - .Select(t => t.t.item); - } - } -} diff --git a/CmsEngine.Core/Extensions/ObjectExtensions.cs b/CmsEngine.Core/Extensions/ObjectExtensions.cs deleted file mode 100644 index fb91bbfc..00000000 --- a/CmsEngine.Core/Extensions/ObjectExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; - -namespace CmsEngine.Core.Extensions -{ - public static class ObjectExtensions - { - public static object MapTo(this object source, object target, bool ignoreId = false) - { - var sourceProperties = source.GetType().GetProperties(); - - foreach (var sourceProp in sourceProperties) - { - if (ignoreId == true && (sourceProp.Name == "Id" || sourceProp.Name == "VanityId")) - { - continue; - } - - var targetProp = target.GetType().GetProperties().Where(p => p.Name == sourceProp.Name).FirstOrDefault(); - if (targetProp != null && targetProp.CanWrite && targetProp.GetSetMethod() != null && targetProp.GetType().Name == sourceProp.GetType().Name) - { - targetProp.SetValue(target, sourceProp.GetValue(source)); - } - } - return target; - } - - } -} diff --git a/CmsEngine.Core/Extensions/StringExtensions.cs b/CmsEngine.Core/Extensions/StringExtensions.cs deleted file mode 100644 index 869518c3..00000000 --- a/CmsEngine.Core/Extensions/StringExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace CmsEngine.Core.Extensions -{ - public static class StringExtensions - { - public static T ToEnum(this string value) - { - return (T)Enum.Parse(typeof(T), value, true); - } - } -} diff --git a/CmsEngine.Core/Utils/CustomResolver.cs b/CmsEngine.Core/Utils/CustomResolver.cs deleted file mode 100644 index 53c5fa99..00000000 --- a/CmsEngine.Core/Utils/CustomResolver.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace CmsEngine.Core -{ - public sealed class CustomResolver : DefaultContractResolver - { - protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) - { - JsonProperty prop = base.CreateProperty(member, memberSerialization); - var propInfo = member as PropertyInfo; - if (propInfo != null) - { - if (propInfo.GetMethod.IsVirtual && !propInfo.GetMethod.IsFinal) - { - prop.ShouldSerialize = obj => false; - } - } - return prop; - } - } -} diff --git a/CmsEngine.Core/Utils/Enums.cs b/CmsEngine.Core/Utils/Enums.cs deleted file mode 100644 index a872c897..00000000 --- a/CmsEngine.Core/Utils/Enums.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace CmsEngine.Core -{ - public enum Gender - { - Male, - Female - } - - public enum Operation - { - Coalesce, - Equals, - NotEqual, - GreaterThan, - LessThan, - GreaterThanOrEqual, - LessThanOrEqual, - Contains, - NotContain, - StartsWith, - EndsWith - } - - public enum LogicalOperator - { - And, - AndAlso, - Or, - OrElse - } - - public enum SignInStatus - { - Success, - LockedOut, - RequiresVerification, - Failure - } - - /// - /// Represents the status of the documents created in the website - /// - public enum DocumentStatus - { - Published = 0, - - [Display(Name = "Pending approval")] - PendingApproval = 1, - - Draft = 2 - } - - /// - /// Represents the general status of an object - /// It's analogous to Bootstrap status colors - /// - public enum GeneralStatus - { - Primary, - Secondary, - Success, - Danger, - Warning, - Info, - Light, - Dark - } - - public enum PageType - { - Create, - Edit, - List - } - - /// - /// In addition to allowing you to use your own image, Gravatar has a number of built in options which you can also use as defaults. Most of these work by taking the requested email hash and using it to generate a themed image that is unique to that email address - /// - public enum DefaultImage - { - /// Default Gravatar logo - [Description("")] - Default, - - /// 404 - do not load any image if none is associated with the email hash, instead return an HTTP 404 (File Not Found) response - [Description("404")] - Http404, - - /// Mystery-Man - a simple, cartoon-style silhouetted outline of a person (does not vary by email hash) - [Description("mm")] - MysteryMan, - - /// Identicon - a geometric pattern based on an email hash - [Description("identicon")] - Identicon, - - /// MonsterId - a generated 'monster' with different colors, faces, etc - [Description("monsterid")] - MonsterId, - - /// Wavatar - generated faces with differing features and backgrounds - [Description("wavatar")] - Wavatar, - - /// Retro - awesome generated, 8-bit arcade-style pixelated faces - [Description("retro")] - Retro - } - - /// - /// Gravatar allows users to self-rate their images so that they can indicate if an image is appropriate for a certain audience. By default, only 'G' rated images are displayed unless you indicate that you would like to see higher ratings - /// - public enum Rating - { - /// Suitable for display on all websites with any audience type - [Description("g")] - G, - - /// May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence - [Description("pg")] - PG, - - /// May contain such things as harsh profanity, intense violence, nudity, or hard drug use - [Description("r")] - R, - - /// May contain hardcore sexual imagery or extremely disturbing violence - [Description("x")] - X - } -} diff --git a/CmsEngine.Core/Utils/GravatarUtilities.cs b/CmsEngine.Core/Utils/GravatarUtilities.cs deleted file mode 100644 index ef45d59c..00000000 --- a/CmsEngine.Core/Utils/GravatarUtilities.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Security.Cryptography; -using System.Text; - -namespace CmsEngine.Core -{ - public static class GravatarUtilities - { - /// - /// Generates an MD5 hash of the given string - /// - /// Source: http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx - public static string GetMd5Hash(string input) - { - - // Convert the input string to a byte array and compute the hash. - byte[] data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(input)); - - // Create a new Stringbuilder to collect the bytes - // and create a string. - StringBuilder sBuilder = new StringBuilder(); - - // Loop through each byte of the hashed data - // and format each one as a hexadecimal string. - for (int i = 0; i < data.Length; i++) - { - sBuilder.Append(data[i].ToString("x2")); - } - - // Return the hexadecimal string. - return sBuilder.ToString(); - } - } -} diff --git a/CmsEngine.Core/Utils/Guard.cs b/CmsEngine.Core/Utils/Guard.cs deleted file mode 100644 index d4dcaa7f..00000000 --- a/CmsEngine.Core/Utils/Guard.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace CmsEngine.Core.Utils -{ - public static class Guard - { - public static void ThrownExceptionIfNull(object value, string objectName) - { - if(value is null) - { - throw new ArgumentNullException(objectName); - } - } - } -} diff --git a/CmsEngine.Core/Utils/PaginatedList.cs b/CmsEngine.Core/Utils/PaginatedList.cs deleted file mode 100644 index 24363490..00000000 --- a/CmsEngine.Core/Utils/PaginatedList.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace CmsEngine.Core -{ - public class PaginatedList : List - { - public int PageIndex { get; private set; } - public int TotalPages { get; private set; } - - public PaginatedList(IEnumerable items, int count, int pageIndex, int pageSize) - { - PageIndex = pageIndex; - TotalPages = (int)Math.Ceiling(count / (double)pageSize); - - AddRange(items); - } - - public bool HasPreviousPage - { - get - { - return PageIndex > 1; - } - } - - public bool HasNextPage - { - get - { - return PageIndex < TotalPages; - } - } - - //public static async Task> CreateAsync(IQueryable source, int pageIndex, int pageSize) - //{ - // var count = await source.CountAsync(); - // var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync(); - // return new PaginatedList(items, count, pageIndex, pageSize); - //} - } -} diff --git a/CmsEngine.Core/Utils/ReturnValue.cs b/CmsEngine.Core/Utils/ReturnValue.cs deleted file mode 100644 index f5be5201..00000000 --- a/CmsEngine.Core/Utils/ReturnValue.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Newtonsoft.Json; - -namespace CmsEngine.Core -{ - public class ReturnValue - { - public ReturnValue(string message, bool isError = false) - { - Message = message; - IsError = IsError; - } - - [JsonProperty(PropertyName = "message")] - public string Message { get; private set; } - - [JsonProperty(PropertyName = "isError")] - public bool IsError { get; private set; } - - [JsonProperty(PropertyName = "exception")] - public string Exception { get; private set; } - - [JsonProperty(PropertyName = "value")] - public object Value { get; set; } - - public void SetErrorMessage(string message, string exception = "") - { - Message = message; - Exception = exception; - IsError = true; - } - } -} diff --git a/CmsEngine.Core/Utils/TinyMceUploadResult.cs b/CmsEngine.Core/Utils/TinyMceUploadResult.cs deleted file mode 100644 index 63196b7f..00000000 --- a/CmsEngine.Core/Utils/TinyMceUploadResult.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace CmsEngine.Core -{ - public class TinyMceUploadResult - { - public string Location { get; set; } - } -} diff --git a/CmsEngine.Core/Utils/UploadFilesResult.cs b/CmsEngine.Core/Utils/UploadFilesResult.cs deleted file mode 100644 index a6b80661..00000000 --- a/CmsEngine.Core/Utils/UploadFilesResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace CmsEngine.Core -{ - public struct UploadFilesResult - { - public string FileName { get; set; } - public string ThumbnailName { get; set; } - public string Path { get; set; } - public long Length { get; set; } - public string Size { get; set; } - public string ContentType { get; set; } - public bool IsImage { get; set; } - } -} diff --git a/CmsEngine.Data/CmsEngineContext.cs b/CmsEngine.Data/CmsEngineContext.cs deleted file mode 100644 index fef9bc9f..00000000 --- a/CmsEngine.Data/CmsEngineContext.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data -{ - public class CmsEngineContext : IdentityDbContext - { - private readonly IHttpContextAccessor httpContextAccessor; - - public DbSet Websites { get; set; } - public DbSet Pages { get; set; } - public DbSet Posts { get; set; } - public DbSet Tags { get; set; } - public DbSet Categories { get; set; } - public DbSet Emails { get; set; } - - public CmsEngineContext(DbContextOptions options, IHttpContextAccessor hca) : base(options) - { - //Database.SetInitializer(new CmsEngineInitializer()); - - ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; - httpContextAccessor = hca; - } - - protected override void OnModelCreating(ModelBuilder builder) - { - //modelBuilder.Conventions.Remove(); - - // Model configuration - builder.Entity(ModelConfiguration.ConfigureWebsite); - builder.Entity(ModelConfiguration.ConfigurePage); - builder.Entity(ModelConfiguration.ConfigurePost); - builder.Entity(ModelConfiguration.ConfigureTag); - builder.Entity(ModelConfiguration.ConfigureCategory); - builder.Entity(ModelConfiguration.ConfigureEmail); - builder.Entity(ModelConfiguration.ConfigurePostCategory); - builder.Entity(ModelConfiguration.ConfigurePostTag); - builder.Entity(ModelConfiguration.ConfigurePostApplicationUser); - builder.Entity(ModelConfiguration.ConfigurePageApplicationUser); - - base.OnModelCreating(builder); - } - - public override Task SaveChangesAsync(CancellationToken cancellationToken = default) - { - ChangeTracker.DetectChanges(); - - var timeStamp = DateTime.Now; - var entries = ChangeTracker.Entries().Where(e => e.Entity is BaseEntity && (e.State == EntityState.Added || e.State == EntityState.Modified)); - // TODO: Find a better way to get the user - string currentUsername = httpContextAccessor.HttpContext.User.Identity.Name; - foreach (var entry in entries) - { - if (entry.State == EntityState.Added) - { - entry.Property("DateCreated").CurrentValue = timeStamp; - entry.Property("UserCreated").CurrentValue = currentUsername; - } - - entry.Property("DateModified").CurrentValue = timeStamp; - entry.Property("UserModified").CurrentValue = currentUsername; - } - - return base.SaveChangesAsync(cancellationToken); - } - } -} diff --git a/CmsEngine.Data/Entities/ApplicationUser.cs b/CmsEngine.Data/Entities/ApplicationUser.cs deleted file mode 100644 index 91b2aca7..00000000 --- a/CmsEngine.Data/Entities/ApplicationUser.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using Microsoft.AspNetCore.Identity; - -namespace CmsEngine.Data.Entities -{ - // Add profile data for application users by adding properties to the ApplicationUser class - public class ApplicationUser : IdentityUser - { - public string Name { get; set; } - public string Surname { get; set; } - - public virtual ICollection PostApplicationUsers { get; set; } - public virtual ICollection PageApplicationUsers { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/BaseEntity.cs b/CmsEngine.Data/Entities/BaseEntity.cs deleted file mode 100644 index 57328b9a..00000000 --- a/CmsEngine.Data/Entities/BaseEntity.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Data.Entities -{ - public class BaseEntity - { - [NotMapped] - public bool IsNew - { - get - { - return Id == 0 && VanityId == Guid.Empty; - } - } - - public bool IsDeleted { get; set; } - - [Key] - public int Id { get; set; } - - public Guid VanityId { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Data/Entities/Category.cs b/CmsEngine.Data/Entities/Category.cs deleted file mode 100644 index d711b0da..00000000 --- a/CmsEngine.Data/Entities/Category.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Data.Entities -{ - public class Category : BaseEntity - { - public int WebsiteId { get; set; } - public virtual Website Website { get; set; } - - public virtual ICollection PostCategories { get; set; } - - public string Name { get; set; } - public string Slug { get; set; } - public string Description { get; set; } - - // Properties for data projection only - [NotMapped] - public IEnumerable Posts { get; set; } - [NotMapped] - public int PostCount { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId), - new JProperty("Name", Name), - new JProperty("Slug", Slug), - new JProperty("Description", Description) - ); - return jsonResult.ToString(); - } - - public Category() - { - Posts = new List(); - } - } -} diff --git a/CmsEngine.Data/Entities/Document.cs b/CmsEngine.Data/Entities/Document.cs deleted file mode 100644 index c00ec3e8..00000000 --- a/CmsEngine.Data/Entities/Document.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using CmsEngine.Core; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Data.Entities -{ - public abstract class Document : BaseEntity - { - public Document() - { - Status = DocumentStatus.Draft; - PublishedOn = DateTime.Now; - } - - public string Title { get; set; } - public string Slug { get; set; } - public string HeaderImage { get; set; } - - public string Description { get; set; } - public string DocumentContent { get; set; } - public DocumentStatus Status { get; set; } - - public DateTime PublishedOn { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId), - new JProperty("Title", Title), - new JProperty("Slug", Slug), - new JProperty("HeaderImage", HeaderImage), - new JProperty("Description", Description), - new JProperty("Status", Status.ToString()), - new JProperty("PublishedOn", PublishedOn) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Data/Entities/Email.cs b/CmsEngine.Data/Entities/Email.cs deleted file mode 100644 index a4d0f255..00000000 --- a/CmsEngine.Data/Entities/Email.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Data.Entities -{ - public class Email : BaseEntity - { - public string From { get; set; } - public string Subject { get; set; } - public string Message { get; set; } - public DateTime DateReceived { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("From", From), - new JProperty("Subject", Subject), - new JProperty("Message", Message) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Data/Entities/IModel.cs b/CmsEngine.Data/Entities/IModel.cs deleted file mode 100644 index a9dc40a6..00000000 --- a/CmsEngine.Data/Entities/IModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace CmsEngine.Data.Entities -{ - public interface IModel - { - bool IsNew { get; } - bool IsDeleted { get; set; } - - int Id { get; set; } - Guid VanityId { get; set; } - - DateTime DateCreated { get; set; } - DateTime DateModified { get; set; } - - string UserCreated { get; set; } - string UserModified { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/ModelConfiguration.cs b/CmsEngine.Data/Entities/ModelConfiguration.cs deleted file mode 100644 index 96e4a434..00000000 --- a/CmsEngine.Data/Entities/ModelConfiguration.cs +++ /dev/null @@ -1,259 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace CmsEngine.Data.Entities -{ - public static class ModelConfiguration - { - public static void ConfigureWebsite(EntityTypeBuilder b) - { - // Fields - b.HasKey(model => model.Id); - - b.Property(model => model.Name) - .HasMaxLength(200) - .IsRequired(); - - b.Property(model => model.Tagline) - .HasMaxLength(200); - - b.Property(model => model.Culture) - .HasMaxLength(5) - .IsRequired(); - - b.Property(model => model.UrlFormat) - .HasMaxLength(100) - .IsRequired(); - - b.Property(model => model.DateFormat) - .HasMaxLength(10) - .IsRequired(); - - b.Property(model => model.SiteUrl) - .HasMaxLength(250) - .IsRequired(); - - b.Property(model => model.ArticleLimit) - .IsRequired(); - - b.Property(model => model.Address) - .HasMaxLength(250); - - b.Property(model => model.Phone) - .HasMaxLength(20); - - b.Property(model => model.Email) - .HasMaxLength(250); - - b.Property(model => model.Facebook) - .HasMaxLength(20); - - b.Property(model => model.Twitter) - .HasMaxLength(20); - - b.Property(model => model.Instagram) - .HasMaxLength(20); - - b.Property(model => model.LinkedIn) - .HasMaxLength(20); - - b.Property(model => model.FacebookAppId) - .HasMaxLength(30); - - b.Property(model => model.FacebookApiVersion) - .HasMaxLength(10); - - b.Property(model => model.VanityId) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("newid()"); - - AddPropertiesForAuditing(b); - - // Relationships - b.HasMany(model => model.Posts); - b.HasMany(model => model.Pages); - b.HasMany(model => model.Tags); - b.HasMany(model => model.Categories); - } - - public static void ConfigurePage(EntityTypeBuilder b) - { - // Fields - b.HasKey(model => model.Id); - - b.Property(model => model.Title) - .HasMaxLength(100) - .IsRequired(); - - b.Property(model => model.Slug) - .HasMaxLength(100) - .IsRequired(); - - b.Property(model => model.Description) - .HasMaxLength(150) - .IsRequired(); - - //b.Property(model => model.DocumentContent) - // .IsRequired(); - - b.Property(model => model.PublishedOn) - .IsRequired(); - - b.Property(model => model.VanityId) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("newid()"); - - AddPropertiesForAuditing(b); - - // Relationships - b.HasOne(model => model.Website) - .WithMany(model => model.Pages); - } - - public static void ConfigurePost(EntityTypeBuilder b) - { - // Fields - b.HasKey(model => model.Id); - - b.Property(model => model.Title) - .HasMaxLength(100) - .IsRequired(); - - b.Property(model => model.Slug) - .HasMaxLength(100) - .IsRequired(); - - b.Property(model => model.Description) - .HasMaxLength(150) - .IsRequired(); - - //b.Property(model => model.DocumentContent) - // .IsRequired(); - - - b.Property(model => model.PublishedOn) - .IsRequired(); - - b.Property(model => model.VanityId) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("newid()"); - - AddPropertiesForAuditing(b); - - // Relationships - b.HasOne(model => model.Website) - .WithMany(model => model.Posts); - } - - public static void ConfigureCategory(EntityTypeBuilder b) - { - // Fields - b.HasKey(model => model.Id); - - b.Property(model => model.Name) - .HasMaxLength(35) - .IsRequired(); - - b.Property(model => model.Slug) - .HasMaxLength(35) - .IsRequired(); - - b.Property(model => model.Description) - .HasMaxLength(200); - - b.Property(model => model.VanityId) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("newid()"); - - AddPropertiesForAuditing(b); - } - - public static void ConfigureTag(EntityTypeBuilder b) - { - // Fields - b.HasKey(model => model.Id); - b.Property(model => model.Name) - .HasMaxLength(25) - .IsRequired(); - b.Property(model => model.Slug) - .HasMaxLength(25) - .IsRequired(); - b.Property(model => model.VanityId) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("newid()"); - - AddPropertiesForAuditing(b); - } - public static void ConfigureEmail(EntityTypeBuilder b) - { - // Fields - b.HasKey(model => model.Id); - b.Property(model => model.Subject) - .HasMaxLength(150) - .IsRequired(); - b.Property(model => model.Message) - .HasMaxLength(500) - .IsRequired(); - b.Property(model => model.VanityId) - .ValueGeneratedOnAdd() - .HasDefaultValueSql("newid()"); - - AddPropertiesForAuditing(b); - } - - // Many to many - - public static void ConfigurePostCategory(EntityTypeBuilder b) - { - b.HasKey(model => new { model.PostId, model.CategoryId }); - } - - public static void ConfigurePostTag(EntityTypeBuilder b) - { - b.HasKey(model => new { model.PostId, model.TagId }); - - b.HasOne(model => model.Post) - .WithMany(p => p.PostTags) - .HasForeignKey(model => model.PostId); - - b.HasOne(model => model.Tag) - .WithMany(c => c.PostTags) - .HasForeignKey(model => model.TagId); - } - - public static void ConfigurePostApplicationUser(EntityTypeBuilder b) - { - b.HasKey(model => new { model.PostId, model.ApplicationUserId }); - - b.HasOne(model => model.Post) - .WithMany(p => p.PostApplicationUsers) - .HasForeignKey(model => model.PostId); - - b.HasOne(model => model.ApplicationUser) - .WithMany(c => c.PostApplicationUsers) - .HasForeignKey(model => model.ApplicationUserId); - } - - public static void ConfigurePageApplicationUser(EntityTypeBuilder b) - { - b.HasKey(model => new { model.PageId, model.ApplicationUserId }); - - b.HasOne(model => model.Page) - .WithMany(p => p.PageApplicationUsers) - .HasForeignKey(model => model.PageId); - - b.HasOne(model => model.ApplicationUser) - .WithMany(c => c.PageApplicationUsers) - .HasForeignKey(model => model.ApplicationUserId); - } - - private static void AddPropertiesForAuditing(EntityTypeBuilder b) where T : BaseEntity - { - b.Property("DateCreated"); - b.Property("DateModified"); - b.Property("UserCreated").HasMaxLength(50); - b.Property("UserModified").HasMaxLength(50); - } - } -} diff --git a/CmsEngine.Data/Entities/Page.cs b/CmsEngine.Data/Entities/Page.cs deleted file mode 100644 index 86f38ccd..00000000 --- a/CmsEngine.Data/Entities/Page.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; - -namespace CmsEngine.Data.Entities -{ - public class Page : Document - { - public int WebsiteId { get; set; } - public virtual Website Website { get; set; } - public virtual ICollection PageApplicationUsers { get; set; } - - // Property used for data projection only - [NotMapped] - public IEnumerable ApplicationUsers { get; set; } - - public Page() - { - PageApplicationUsers = new List(); - - ApplicationUsers = new List(); - } - } -} diff --git a/CmsEngine.Data/Entities/PageApplicationUser.cs b/CmsEngine.Data/Entities/PageApplicationUser.cs deleted file mode 100644 index f8c7a686..00000000 --- a/CmsEngine.Data/Entities/PageApplicationUser.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace CmsEngine.Data.Entities -{ - [Table("PageAspNetUser")] - public class PageApplicationUser - { - public int PageId { get; set; } - public virtual Page Page { get; set; } - - public string ApplicationUserId { get; set; } - public virtual ApplicationUser ApplicationUser { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/Post.cs b/CmsEngine.Data/Entities/Post.cs deleted file mode 100644 index 7653d35f..00000000 --- a/CmsEngine.Data/Entities/Post.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; - -namespace CmsEngine.Data.Entities -{ - public class Post : Document - { - public int WebsiteId { get; set; } - public virtual Website Website { get; set; } - public virtual ICollection PostCategories { get; set; } - public virtual ICollection PostTags { get; set; } - public virtual ICollection PostApplicationUsers { get; set; } - - // Properties used for data projection only - [NotMapped] - public IEnumerable Categories { get; set; } - [NotMapped] - public IEnumerable Tags { get; set; } - [NotMapped] - public IEnumerable ApplicationUsers { get; set; } - - public Post() - { - PostCategories = new List(); - PostTags = new List(); - PostApplicationUsers = new List(); - - Categories = new List(); - Tags = new List(); - ApplicationUsers = new List(); - } - } -} diff --git a/CmsEngine.Data/Entities/PostApplicationUser.cs b/CmsEngine.Data/Entities/PostApplicationUser.cs deleted file mode 100644 index a87ef9a3..00000000 --- a/CmsEngine.Data/Entities/PostApplicationUser.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; - -namespace CmsEngine.Data.Entities -{ - [Table("PostAspNetUser")] - public class PostApplicationUser - { - public int PostId { get; set; } - public virtual Post Post { get; set; } - - public string ApplicationUserId { get; set; } - public virtual ApplicationUser ApplicationUser { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/PostCategory.cs b/CmsEngine.Data/Entities/PostCategory.cs deleted file mode 100644 index 43c145c9..00000000 --- a/CmsEngine.Data/Entities/PostCategory.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace CmsEngine.Data.Entities -{ - public class PostCategory - { - public int PostId { get; set; } - public virtual Post Post { get; set; } - - public int CategoryId { get; set; } - public virtual Category Category { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/PostTag.cs b/CmsEngine.Data/Entities/PostTag.cs deleted file mode 100644 index 9df8b4b2..00000000 --- a/CmsEngine.Data/Entities/PostTag.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace CmsEngine.Data.Entities -{ - public class PostTag - { - public int PostId { get; set; } - public virtual Post Post { get; set; } - - public int TagId { get; set; } - public virtual Tag Tag { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/Tag.cs b/CmsEngine.Data/Entities/Tag.cs deleted file mode 100644 index ef1822a2..00000000 --- a/CmsEngine.Data/Entities/Tag.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; - -namespace CmsEngine.Data.Entities -{ - public class Tag : BaseEntity - { - public int WebsiteId { get; set; } - public virtual Website Website { get; set; } - public virtual ICollection PostTags { get; set; } - - public string Name { get; set; } - public string Slug { get; set; } - } -} diff --git a/CmsEngine.Data/Entities/Website.cs b/CmsEngine.Data/Entities/Website.cs deleted file mode 100644 index 1dbb054d..00000000 --- a/CmsEngine.Data/Entities/Website.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using CmsEngine.Core.Constants; -using Newtonsoft.Json.Linq; - -namespace CmsEngine.Data.Entities -{ - public class Website : BaseEntity - { - public virtual ICollection Pages { get; set; } - public virtual ICollection Posts { get; set; } - public virtual ICollection Tags { get; set; } - public virtual ICollection Categories { get; set; } - - public string Name { get; set; } - public string Tagline { get; set; } - public string Description { get; set; } - public string HeaderImage { get; set; } - public string Culture { get; set; } - public string UrlFormat { get; set; } = $"{CmsEngineConstants.SiteUrl}/{CmsEngineConstants.Type}/{CmsEngineConstants.Slug}"; - public string DateFormat { get; set; } = "MM/dd/yyyy"; - public string SiteUrl { get; set; } - public int ArticleLimit { get; set; } - - // Contact details - public string Address { get; set; } - public string Phone { get; set; } - public string Email { get; set; } - public string Facebook { get; set; } - public string Twitter { get; set; } - public string Instagram { get; set; } - public string LinkedIn { get; set; } - - // Api configuration - public string FacebookAppId { get; set; } - public string FacebookApiVersion { get; set; } - public string DisqusShortName { get; set; } - public string GoogleAnalytics { get; set; } - public string GoogleRecaptchaSiteKey { get; set; } - public string GoogleRecaptchaSecretKey { get; set; } - - public override string ToString() - { - var jsonResult = new JObject( - new JProperty("Id", Id), - new JProperty("VanityId", VanityId), - new JProperty("Name", Name), - new JProperty("SiteUrl", SiteUrl), - new JProperty("Tagline", Tagline) - ); - return jsonResult.ToString(); - } - } -} diff --git a/CmsEngine.Data/Extensions/DbContextExtensions.cs b/CmsEngine.Data/Extensions/DbContextExtensions.cs deleted file mode 100644 index f75b1ddf..00000000 --- a/CmsEngine.Data/Extensions/DbContextExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; -using CmsEngine.Core.Extensions; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Extensions -{ - public static class DbContextExtensions - { - public static void TryUpdateManyToMany(this DbContext db, IEnumerable currentItems, IEnumerable newItems, Func getKey) where T : class - { - db.Set().RemoveRange(currentItems.Except(newItems, getKey)); - db.Set().AddRange(newItems.Except(currentItems, getKey)); - } - } -} diff --git a/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs b/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs deleted file mode 100644 index 2102aea2..00000000 --- a/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs +++ /dev/null @@ -1,110 +0,0 @@ -using System; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Core.Extensions -{ - public static class ModelBuilderExtensions - { - public static void Seed(this ModelBuilder builder) - { - var website = new Website - { - Id = 1, - VanityId = Guid.NewGuid(), - Name = "Sample Website", - Description = "This is a sample website", - Culture = "en-US", - UrlFormat = "http://[site_url]/[type]/[slug]", - DateFormat = "MM/dd/yyyy", - SiteUrl = "cmsengine.test", - ArticleLimit = 10 - }; - - var applicationUser = new ApplicationUser - { - Id = Guid.NewGuid().ToString(), - UserName = "john@doe.com", - Email = "john@doe.com", - ConcurrencyStamp = Guid.NewGuid().ToString(), - EmailConfirmed = true, - LockoutEnabled = false, - NormalizedEmail = "JOHN@DOE.COM", - NormalizedUserName = "JOHN@DOE.COM", - PasswordHash = "AQAAAAEAACcQAAAAEGIUaLe7RWZGw8Tr5/xoUMOooAzJsLFw550fDqZkrbk8CD+urHQzYjK1xY8vcDMekw==", // P@ssword1 - SecurityStamp = "NBTDBYKTNLGHKQ3HI7YFEHPQN5YRXWQC", - TwoFactorEnabled = false, - Name = "John", - Surname = "Doe" - }; - - string documentContent = @"

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus libero, egestas vel tempus id, venenatis nec tellus. Nullam hendrerit id magna quis venenatis. Pellentesque rhoncus leo vitae turpis tristique, nec placerat tellus scelerisque. Aenean vitae rhoncus urna, non posuere elit. Nullam quam libero, porttitor in lectus convallis, pellentesque finibus libero. Suspendisse potenti. Fusce quis purus egestas, malesuada massa sed, dignissim purus. Curabitur vitae rhoncus nulla, sit amet dignissim quam.

-

Mauris lorem urna, convallis in enim nec, tristique ullamcorper nisl. Fusce nec tellus et arcu imperdiet ullamcorper vestibulum vitae mi. Sed bibendum molestie dolor sit amet rhoncus.Duis consectetur convallis auctor. In hac habitasse platea dictumst.Duis lorem nibh, mattis ut purus interdum, scelerisque molestie est. Nullam molestie a est vel ornare. Maecenas rhoncus accumsan ligula, at pretium purus tempus ut. Aliquam erat nulla, pretium vel eros vitae, blandit aliquam nibh. Nulla tincidunt, justo et ullamcorper dictum, augue lectus dictum ligula, eget rutrum sem nibh non felis.Aenean elementum, sem sit amet pulvinar tempus, neque eros faucibus turpis, quis molestie nisi libero quis purus.

-

Donec quam massa, tincidunt eu lacus in, lacinia hendrerit urna. Pellentesque pretium orci a felis tincidunt, sit amet volutpat est dapibus. Donec laoreet, massa in imperdiet laoreet, enim ligula auctor est, non imperdiet nisi diam vitae quam. Integer nec porttitor ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Morbi non pretium risus, a lobortis eros. Etiam blandit diam tortor. Ut feugiat eros id erat auctor, ut vehicula odio vestibulum.

-

Nunc sed ex sed diam euismod eleifend. Proin blandit lorem sed placerat fermentum. Curabitur non gravida felis, ac sollicitudin nibh. Morbi ornare sapien vitae nisl condimentum cursus.Vivamus bibendum condimentum metus, ut gravida orci bibendum maximus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Duis varius, tortor ac placerat faucibus, lectus mauris bibendum elit, id eleifend leo diam ac nulla.Aenean egestas urna facilisis purus ullamcorper vestibulum.Etiam commodo suscipit turpis, quis lobortis metus posuere sed.

-

Praesent in augue sit amet tortor ultricies maximus eu ac dui.Pellentesque et congue elit. Suspendisse potenti. Donec facilisis eu magna nec bibendum. Nullam in dignissim elit. Integer laoreet odio massa, vel vestibulum mauris varius et. Ut non ex sit amet nisl mollis laoreet.

"; - - var page = new Page - { - Id = 1, - VanityId = Guid.NewGuid(), - Title = "Sample page", - Slug = "sample-page", - Description = "This is a sample page from a sample website", - Status = DocumentStatus.Published, - PublishedOn = DateTime.Now, - DocumentContent = documentContent, - WebsiteId = website.Id - }; - - var post = new Post - { - Id = 1, - VanityId = Guid.NewGuid(), - Title = "Lorem Ipsum", - Slug = "lorem-ipsum", - Description = "Lorem ipsum dolor sit amet", - Status = DocumentStatus.Published, - PublishedOn = DateTime.Now, - DocumentContent = documentContent, - WebsiteId = website.Id - }; - - var category = new Category - { - Id = 1, - VanityId = Guid.NewGuid(), - Name = "Category example", - Slug = "category-example", - WebsiteId = website.Id - }; - - var postCategory = new PostCategory - { - CategoryId = category.Id, - PostId = post.Id - }; - - var postApplicationUser = new PostApplicationUser - { - ApplicationUserId = applicationUser.Id, - PostId = post.Id - }; - - var pageApplicationUser = new PageApplicationUser - { - ApplicationUserId = applicationUser.Id, - PageId = post.Id - }; - - builder.Entity().HasData(website); - builder.Entity().HasData(page); - builder.Entity().HasData(post); - builder.Entity().HasData(category); - builder.Entity().HasData(postCategory); - builder.Entity().HasData(applicationUser); - builder.Entity().HasData(postApplicationUser); - builder.Entity().HasData(pageApplicationUser); - } - } -} diff --git a/CmsEngine.Data/IUnitOfWork.cs b/CmsEngine.Data/IUnitOfWork.cs deleted file mode 100644 index 3eaec6f7..00000000 --- a/CmsEngine.Data/IUnitOfWork.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using CmsEngine.Data.Repositories; -using Microsoft.AspNetCore.Identity; - -namespace CmsEngine.Data -{ - public interface IUnitOfWork : IDisposable - { - ICategoryRepository Categories { get; } - IPageRepository Pages { get; } - IPostRepository Posts { get; } - ITagRepository Tags { get; } - IWebsiteRepository Websites { get; } - UserManager Users { get; } - IEmailRepository Emails { get; } - - /// - /// Saves all pending changes into the database - /// - Task Save(); - } -} diff --git a/CmsEngine.Data/Repositories/CategoryRepository.cs b/CmsEngine.Data/Repositories/CategoryRepository.cs deleted file mode 100644 index 4fd77aaa..00000000 --- a/CmsEngine.Data/Repositories/CategoryRepository.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Core; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Repositories -{ - public class CategoryRepository : Repository, ICategoryRepository - { - public CategoryRepository(CmsEngineContext context) : base(context) - { - } - - public async Task GetCategoryBySlug(string slug) - { - return await Get(q => q.Slug == slug).SingleOrDefaultAsync(); - } - - public async Task> GetCategoriesWithPostCountOrderedByName() - { - return await Get().Include(c => c.PostCategories) - .Where(q => q.PostCategories.Any(pc => pc.Post.Status == DocumentStatus.Published && pc.Post.IsDeleted == false)) - .Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug, - PostCount = c.PostCategories.Count() - }) - .OrderBy(o => o.Name).ToListAsync(); - } - - public async Task GetCategoryBySlugWithPosts(string slug) - { - return await Get(q => q.Slug == slug).Include(c => c.PostCategories).SingleOrDefaultAsync(); - } - - public async Task> GetCategoriesWithPostOrderedByName() - { - return await Get().Include(c => c.PostCategories) - .ThenInclude(pc => pc.Post) - .Where(q => q.PostCategories.Any(pc => pc.Post.Status == DocumentStatus.Published && pc.Post.IsDeleted == false)) - .Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug, - Posts = c.PostCategories.Select(pc => pc.Post).Where(q => q.IsDeleted == false).Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Description = p.Description, - Slug = p.Slug, - PublishedOn = p.PublishedOn - }) - }) - .OrderBy(o => o.Name).ToListAsync(); - } - } -} diff --git a/CmsEngine.Data/Repositories/EmailRepository.cs b/CmsEngine.Data/Repositories/EmailRepository.cs deleted file mode 100644 index 017150f2..00000000 --- a/CmsEngine.Data/Repositories/EmailRepository.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Repositories -{ - public class EmailRepository : Repository, IEmailRepository - { - public EmailRepository(CmsEngineContext context) : base(context) - { - } - - public async Task> GetOrderedByDate() - { - return await Get().OrderByDescending(o => o.DateReceived) - .ToListAsync(); - } - } -} diff --git a/CmsEngine.Data/Repositories/ICategoryRepository.cs b/CmsEngine.Data/Repositories/ICategoryRepository.cs deleted file mode 100644 index af6fe6c0..00000000 --- a/CmsEngine.Data/Repositories/ICategoryRepository.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Data.Repositories -{ - public interface ICategoryRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository - { - Task GetCategoryBySlug(string slug); - Task> GetCategoriesWithPostOrderedByName(); - Task> GetCategoriesWithPostCountOrderedByName(); - Task GetCategoryBySlugWithPosts(string slug); - } -} diff --git a/CmsEngine.Data/Repositories/IDataModificationRangeRepository.cs b/CmsEngine.Data/Repositories/IDataModificationRangeRepository.cs deleted file mode 100644 index 8dca3adf..00000000 --- a/CmsEngine.Data/Repositories/IDataModificationRangeRepository.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace CmsEngine.Data.Repositories -{ - public interface IDataModificationRangeRepository where TEntity : class - { - /// - /// Inserts multiple records - /// - /// - Task InsertRange(IEnumerable entities); - - /// - /// Updates multiple records - /// - /// - void UpdateRange(IEnumerable entities); - - /// - /// Deletes multiple records - /// - /// - void DeleteRange(IEnumerable entities); - } -} diff --git a/CmsEngine.Data/Repositories/IDataModificationRepository.cs b/CmsEngine.Data/Repositories/IDataModificationRepository.cs deleted file mode 100644 index eb19a8cb..00000000 --- a/CmsEngine.Data/Repositories/IDataModificationRepository.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Threading.Tasks; - -namespace CmsEngine.Data.Repositories -{ - public interface IDataModificationRepository where TEntity : class - { - /// - /// Inserts a record - /// - /// - Task Insert(TEntity entity); - - /// - /// Updates record - /// - /// - void Update(TEntity entity); - - /// - /// Deletes record from database - /// - /// - void Delete(TEntity entity); - - /// - /// Attaches non tracked entity to the DbContext - /// - /// - void Attach(TEntity entity); - } -} diff --git a/CmsEngine.Data/Repositories/IEmailRepository.cs b/CmsEngine.Data/Repositories/IEmailRepository.cs deleted file mode 100644 index ce86f3e2..00000000 --- a/CmsEngine.Data/Repositories/IEmailRepository.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Data.Repositories -{ - public interface IEmailRepository : IReadRepository, IDataModificationRepository - { - Task> GetOrderedByDate(); - } -} diff --git a/CmsEngine.Data/Repositories/IPageRepository.cs b/CmsEngine.Data/Repositories/IPageRepository.cs deleted file mode 100644 index c7a11c5c..00000000 --- a/CmsEngine.Data/Repositories/IPageRepository.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Threading.Tasks; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Data.Repositories -{ - public interface IPageRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository - { - Task> GetOrderByDescending(Expression> orderBy); - Task> GetByStatusOrderByDescending(DocumentStatus documentStatus); - Task> GetForDataTable(); - Task GetBySlug(string slug); - Task GetForSavingById(Guid id); - void RemoveRelatedItems(Page page); - } -} diff --git a/CmsEngine.Data/Repositories/IPostRepository.cs b/CmsEngine.Data/Repositories/IPostRepository.cs deleted file mode 100644 index 0ee99ac1..00000000 --- a/CmsEngine.Data/Repositories/IPostRepository.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Threading.Tasks; -using CmsEngine.Core; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Data.Repositories -{ - public interface IPostRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository - { - Task> GetPublishedPostsOrderByDescending(Expression> orderBy); - Task> GetByStatusOrderByDescending(DocumentStatus documentStatus); - Task<(IEnumerable Items, int Count)> GetPublishedByCategoryForPagination(string categorySlug, int page, int articleLimit); - Task<(IEnumerable Items, int Count)> GetPublishedByTagForPagination(string tagSlug, int page, int articleLimit); - Task<(IEnumerable Items, int Count)> FindPublishedForPaginationOrderByDateDescending(int page, string searchTerm, int articleLimit); - Task<(IEnumerable Items, int Count)> GetPublishedForPagination(int page, int articleLimit); - Task> GetPublishedLatestPosts(int count); - Task> GetForDataTable(); - Task GetForSavingById(Guid id); - Task GetForEditingById(Guid id); - Task GetBySlug(string slug); - void RemoveRelatedItems(Post post); - } -} diff --git a/CmsEngine.Data/Repositories/IReadRepository.cs b/CmsEngine.Data/Repositories/IReadRepository.cs deleted file mode 100644 index b536c8ef..00000000 --- a/CmsEngine.Data/Repositories/IReadRepository.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace CmsEngine.Data.Repositories -{ - public interface IReadRepository where TEntity : class - { - /// - /// Get all records which were not marked as deleted (IsDeleted == false) - /// - /// - Task> GetAllAsync(); - - /// - /// Gets item based on condition and includes extra table - /// - /// - /// - /// - IQueryable Get(Expression> filter = null, int count = 0); - - /// - /// Gets items based on condition for read-only - /// - /// - /// - /// - Task> GetReadOnlyAsync(Expression> filter = null); - - /// - /// Get record by id - /// - /// - /// - Task GetByIdAsync(int id); - - /// - /// Gets record by vanity id - /// - /// - /// - Task GetByIdAsync(Guid id); - - /// - /// Get multiple records by an array of ids - /// - /// - /// - Task> GetByMultipleIdsAsync(int[] ids); - - /// - /// Get multiple records by an array of vanity ids - /// - /// - /// - Task> GetByMultipleIdsAsync(Guid[] ids); - - /// - /// Get multiple records by an IEnumerable of vanity ids - /// - /// - /// - Task> GetIdsByMultipleGuidsAsync(IEnumerable ids); - } -} diff --git a/CmsEngine.Data/Repositories/ITagRepository.cs b/CmsEngine.Data/Repositories/ITagRepository.cs deleted file mode 100644 index 43f1d11e..00000000 --- a/CmsEngine.Data/Repositories/ITagRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Data.Repositories -{ - public interface ITagRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository - { - Task GetTagBySlug(string slug); - Task> GetTagsWithPosts(); - Task GetTagBySlugWithPosts(string slug); - } -} diff --git a/CmsEngine.Data/Repositories/IWebsiteRepository.cs b/CmsEngine.Data/Repositories/IWebsiteRepository.cs deleted file mode 100644 index f53d3368..00000000 --- a/CmsEngine.Data/Repositories/IWebsiteRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; - -namespace CmsEngine.Data.Repositories -{ - public interface IWebsiteRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository - { - Website GetWebsiteInstanceByHost(string host); - Task> GetForDataTable(); - } -} diff --git a/CmsEngine.Data/Repositories/PageRepository.cs b/CmsEngine.Data/Repositories/PageRepository.cs deleted file mode 100644 index cefcd539..00000000 --- a/CmsEngine.Data/Repositories/PageRepository.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using CmsEngine.Core; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Repositories -{ - public class PageRepository : Repository, IPageRepository - { - public PageRepository(CmsEngineContext context) : base(context) - { - - } - - public async Task> GetOrderByDescending(Expression> orderBy) - { - return await Get().OrderByDescending(orderBy).ToListAsync(); - } - - public async Task> GetByStatusOrderByDescending(DocumentStatus documentStatus) - { - return await Get(q => q.Status == documentStatus).OrderByDescending(o => o.PublishedOn).ToListAsync(); - } - - public async Task GetBySlug(string slug) - { - return await Get(q => q.Slug == slug) - .Select(p => new Page - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - DocumentContent = p.DocumentContent, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - ApplicationUsers = p.PageApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }) - .SingleOrDefaultAsync(); - } - - public async Task> GetForDataTable() - { - return await Get().Select(p => new Page - { - VanityId = p.VanityId, - Title = p.Title, - Description = p.Description, - Slug = p.Slug, - PublishedOn = p.PublishedOn, - Status = p.Status, - ApplicationUsers = p.PageApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }).ToListAsync(); - } - - public async Task GetForSavingById(Guid id) - { - return await Get(q => q.VanityId == id).Include(p => p.PageApplicationUsers) - .SingleOrDefaultAsync(); - } - - public void RemoveRelatedItems(Page page) - { - dbContext.RemoveRange(page.PageApplicationUsers); - } - } -} diff --git a/CmsEngine.Data/Repositories/PostRepository.cs b/CmsEngine.Data/Repositories/PostRepository.cs deleted file mode 100644 index 93a910a0..00000000 --- a/CmsEngine.Data/Repositories/PostRepository.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using CmsEngine.Core; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Repositories -{ - public class PostRepository : Repository, IPostRepository - { - public PostRepository(CmsEngineContext context) : base(context) - { - - } - - public async Task> GetPublishedPostsOrderByDescending(Expression> orderBy) - { - return await Get(q => q.Status == DocumentStatus.Published).OrderByDescending(orderBy).ToListAsync(); - } - - public async Task> GetByStatusOrderByDescending(DocumentStatus documentStatus) - { - return await Get(q => q.Status == documentStatus).OrderByDescending(o => o.PublishedOn).ToListAsync(); - } - - public async Task GetBySlug(string slug) - { - return await Get(q => q.Slug == slug) - .Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - DocumentContent = p.DocumentContent, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }) - .SingleOrDefaultAsync(); - } - - public async Task<(IEnumerable Items, int Count)> GetPublishedByCategoryForPagination(string categorySlug, int page, int articleLimit) - { - var posts = Get(q => q.Status == DocumentStatus.Published) - .Include(p => p.PostCategories) - .ThenInclude(pc => pc.Category) - .Include(p => p.PostApplicationUsers) - .ThenInclude(pau => pau.ApplicationUser) - .OrderByDescending(o => o.PublishedOn) - .Where(q => q.PostCategories.Any(pc => pc.Category.Slug == categorySlug)); - - int count = posts.Count(); - var items = await posts.Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); - - return (items, count); - } - - public async Task<(IEnumerable Items, int Count)> GetPublishedByTagForPagination(string tagSlug, int page, int articleLimit) - { - var posts = Get(q => q.Status == DocumentStatus.Published).Include(p => p.PostTags) - .ThenInclude(pt => pt.Tag) - .Include(p => p.PostCategories) - .ThenInclude(pc => pc.Category) - .Include(p => p.PostApplicationUsers) - .ThenInclude(pau => pau.ApplicationUser) - .OrderByDescending(o => o.PublishedOn) - .Where(q => q.PostTags.Any(pc => pc.Tag.Slug == tagSlug)); - - int count = posts.Count(); - var items = await posts.Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - Tags = p.PostTags.Select(pt => pt.Tag).Select(c => new Tag - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); - - return (items, count); - } - - public async Task<(IEnumerable Items, int Count)> FindPublishedForPaginationOrderByDateDescending(int page, string searchTerm, int articleLimit) - { - var posts = string.IsNullOrWhiteSpace(searchTerm) - ? Get(q => q.Status == DocumentStatus.Published) - .Include(p => p.PostApplicationUsers) - .ThenInclude(pau => pau.ApplicationUser) - : Get(q => (q.Title.Contains(searchTerm) || q.DocumentContent.Contains(searchTerm)) && q.Status == DocumentStatus.Published) - .Include(p => p.PostApplicationUsers) - .ThenInclude(pau => pau.ApplicationUser); - - int count = await posts.CountAsync(); - var items = await posts.Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }).OrderByDescending(o => o.PublishedOn).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); - - return (items, count); - } - - public async Task<(IEnumerable Items, int Count)> GetPublishedForPagination(int page, int articleLimit) - { - var posts = Get(q => q.Status == DocumentStatus.Published).Include(p => p.PostApplicationUsers) - .ThenInclude(pau => pau.ApplicationUser); - int count = posts.Count(); - var items = await posts.Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }).OrderByDescending(o => o.PublishedOn).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); - - return (items, count); - } - - public async Task> GetPublishedLatestPosts(int count) - { - return await Get(q => q.Status == DocumentStatus.Published) - .Include(p => p.PostCategories) - .ThenInclude(pc => pc.Category) - .Include(p => p.PostApplicationUsers) - .ThenInclude(pau => pau.ApplicationUser) - .Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Slug = p.Slug, - Description = p.Description, - HeaderImage = p.HeaderImage, - PublishedOn = p.PublishedOn, - Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category - { - VanityId = c.VanityId, - Name = c.Name, - Slug = c.Slug - }), - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }) - .OrderByDescending(o => o.PublishedOn).Take(count).ToListAsync(); - } - - public async Task> GetForDataTable() - { - return await Get().Select(p => new Post - { - VanityId = p.VanityId, - Title = p.Title, - Description = p.Description, - Slug = p.Slug, - PublishedOn = p.PublishedOn, - Status = p.Status, - ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser - { - Id = au.Id, - Name = au.Name, - Surname = au.Surname, - Email = au.Email - }) - }).ToListAsync(); - } - - public async Task GetForSavingById(Guid id) - { - return await Get(q => q.VanityId == id).Include(p => p.PostCategories) - .Include(p => p.PostTags) - .Include(p => p.PostApplicationUsers) - .SingleOrDefaultAsync(); - } - - public async Task GetForEditingById(Guid id) - { - return await Get(q => q.VanityId == id).Include(p => p.PostCategories) - .ThenInclude(pc => pc.Category) - .Include(p => p.PostTags) - .ThenInclude(pt => pt.Tag) - .SingleOrDefaultAsync(); - } - - public void RemoveRelatedItems(Post post) - { - dbContext.RemoveRange(post.PostApplicationUsers); - dbContext.RemoveRange(post.PostTags); - dbContext.RemoveRange(post.PostCategories); - } - } -} diff --git a/CmsEngine.Data/Repositories/Repository.cs b/CmsEngine.Data/Repositories/Repository.cs deleted file mode 100644 index 0ded13e0..00000000 --- a/CmsEngine.Data/Repositories/Repository.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; - -namespace CmsEngine.Data.Repositories -{ - public class Repository : IReadRepository, - IDataModificationRepository, - IDataModificationRangeRepository - where TEntity : BaseEntity - { - protected readonly CmsEngineContext dbContext; - - public Repository(CmsEngineContext context) - { - dbContext = context ?? throw new ArgumentNullException("Repository - Context"); - } - - public async Task> GetAllAsync() - { - return await GetValidRecords().ToListAsync(); - } - - public IQueryable Get(Expression> filter = null, int count = 0) - { - var recods = GetValidRecords(); - - if (filter != null) - { - recods = recods.Where(filter); - } - - if (count > 0) - { - recods = recods.Take(count); - } - - return recods; - } - - public async Task> GetReadOnlyAsync(Expression> filter = null) - { - var records = GetValidRecords(); - - if (filter != null) - { - records = records.Where(filter); - } - - return await records.ToListAsync(); - } - - public async Task GetByIdAsync(int id) - { - return await Get(q => q.Id == id).SingleOrDefaultAsync(); - } - - public async Task GetByIdAsync(Guid id) - { - return await Get(q => q.VanityId == id).SingleOrDefaultAsync(); - } - - public async Task> GetByMultipleIdsAsync(int[] ids) - { - return await Get(q => ids.Contains(q.Id)).ToListAsync(); - } - - public async Task> GetByMultipleIdsAsync(Guid[] ids) - { - return await Get(q => ids.Contains(q.VanityId)).ToListAsync(); - } - - public async Task> GetIdsByMultipleGuidsAsync(IEnumerable ids) - { - return await Get(q => ids.Contains(q.VanityId)).Select(x => x.Id).ToListAsync(); - } - - public async Task Insert(TEntity entity) - { - if (entity is null) - { - throw new ArgumentNullException(nameof(entity)); - } - - await dbContext.AddAsync(entity); - } - - public async Task InsertRange(IEnumerable entities) - { - if (entities is null) - { - throw new ArgumentNullException(nameof(entities)); - } - - await dbContext.AddRangeAsync(entities); - } - - public void Update(TEntity entity) - { - if (entity is null) - { - throw new ArgumentNullException(nameof(entity)); - } - - dbContext.Update(entity); - } - - public void UpdateRange(IEnumerable entities) - { - if (entities is null) - { - throw new ArgumentNullException(nameof(entities)); - } - - dbContext.UpdateRange(entities); - } - - public void Delete(TEntity entity) - { - if (entity is null) - { - throw new ArgumentNullException(nameof(entity)); - } - - // We never delete anything, only update the IsDelete flag - entity.IsDeleted = true; - Update(entity); - } - - public void DeleteRange(IEnumerable entities) - { - if (entities is null) - { - throw new ArgumentNullException(nameof(entities)); - } - - for (int i = 0; i < entities.Count(); i++) - { - ((List)entities)[i].IsDeleted = true; - } - - // We never delete anything - UpdateRange(entities); - } - - public void Attach(TEntity entity) - { - EntityEntry dbEntityEntry = dbContext.Entry(entity); - if (dbEntityEntry.State == EntityState.Detached) - { - dbContext.Attach(entity); - } - } - - private IQueryable GetValidRecords() - { - return dbContext.Set().Where(q => q.IsDeleted == false); - } - } -} diff --git a/CmsEngine.Data/Repositories/TagRepository.cs b/CmsEngine.Data/Repositories/TagRepository.cs deleted file mode 100644 index dcdff435..00000000 --- a/CmsEngine.Data/Repositories/TagRepository.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Repositories -{ - public class TagRepository : Repository, ITagRepository - { - public TagRepository(CmsEngineContext context) : base(context) - { - - } - - public async Task GetTagBySlug(string slug) - { - return await Get(q => q.Slug == slug).SingleOrDefaultAsync(); - } - - public async Task GetTagBySlugWithPosts(string slug) - { - return await Get(q => q.Slug == slug).Include(t => t.PostTags).SingleOrDefaultAsync(); - } - - public async Task> GetTagsWithPosts() - { - return await Get().Include(t => t.PostTags).ToListAsync(); - } - } -} diff --git a/CmsEngine.Data/Repositories/WebsiteRepository.cs b/CmsEngine.Data/Repositories/WebsiteRepository.cs deleted file mode 100644 index a62f5361..00000000 --- a/CmsEngine.Data/Repositories/WebsiteRepository.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data.Repositories -{ - public class WebsiteRepository : Repository, IWebsiteRepository - { - public WebsiteRepository(CmsEngineContext context) : base(context) - { - - } - - public async Task> GetForDataTable() - { - return await Get().Select(w => new Website - { - VanityId = w.VanityId, - Name = w.Name, - Tagline = w.Tagline, - Culture = w.Culture, - UrlFormat = w.UrlFormat, - DateFormat = w.DateFormat, - SiteUrl = w.SiteUrl, - GoogleAnalytics = w.GoogleAnalytics - }).ToListAsync(); - } - - public Website GetWebsiteInstanceByHost(string host) - { - return Get(q => q.SiteUrl == host).SingleOrDefault(); - } - } -} diff --git a/CmsEngine.Data/UnitOfWork.cs b/CmsEngine.Data/UnitOfWork.cs deleted file mode 100644 index 562a0046..00000000 --- a/CmsEngine.Data/UnitOfWork.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using CmsEngine.Data.Repositories; -using Microsoft.AspNetCore.Identity; -using Microsoft.EntityFrameworkCore; - -namespace CmsEngine.Data -{ - public class UnitOfWork : IUnitOfWork - { - private readonly CmsEngineContext _ctx; - private bool _disposed; - - public ICategoryRepository Categories { get; private set; } - public IPageRepository Pages { get; private set; } - public IPostRepository Posts { get; private set; } - public ITagRepository Tags { get; private set; } - public IWebsiteRepository Websites { get; private set; } - public UserManager Users { get; private set; } - public IEmailRepository Emails { get; private set; } - - public UnitOfWork(CmsEngineContext context, ICategoryRepository categoryRepository, IPageRepository pageRepository, - IPostRepository postRepository, ITagRepository tagRepository, IWebsiteRepository websiteRepository, - UserManager userManager, IEmailRepository emailRepository) - { - _ctx = context; - - Categories = categoryRepository; - Pages = pageRepository; - Posts = postRepository; - Tags = tagRepository; - Websites = websiteRepository; - Users = userManager; - Emails = emailRepository; - - _disposed = false; - } - - public async Task Save() - { - try - { - await _ctx.SaveChangesAsync(); - } - // TODO: Implement EF Core error handling - //catch (DbEntityValidationException ex) - //{ - // // Retrieve the error messages as a list of strings. - // var errorMessages = ex.EntityValidationErrors - // .SelectMany(x => x.ValidationErrors) - // .Select(x => x.ErrorMessage); - - // // Join the list to a single string. - // var fullErrorMessage = string.Join("; ", errorMessages); - - // // Combine the original exception message with the new one. - // var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage); - - // // Throw a new DbEntityValidationException with the improved exception message. - // throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); - //} - catch (DbUpdateConcurrencyException ex) - { - ex.Entries[0].Reload(); - } - catch (DbUpdateException ex) - { - var innerException = ex.InnerException; - throw new DbUpdateException(innerException.Message, innerException); - } - catch - { - throw; - } - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposed) - { - if (disposing) - { - _ctx.Dispose(); - } - } - _disposed = true; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - } -} diff --git a/CmsEngine.Ui/.config/dotnet-tools.json b/CmsEngine.Ui/.config/dotnet-tools.json deleted file mode 100644 index c735fef1..00000000 --- a/CmsEngine.Ui/.config/dotnet-tools.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "dotnet-ef": { - "version": "5.0.0", - "commands": [ - "dotnet-ef" - ] - } - } -} \ No newline at end of file diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs deleted file mode 100644 index ac5aebe8..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Threading.Tasks; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.Services; -using CmsEngine.Core; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace CmsEngine.Ui.Areas.Cms.Controllers -{ - [Authorize] - public class BaseController : Controller - { - public IService Service { get; private set; } - public ILogger Logger { get; private set; } - - public BaseController(ILogger logger) - { - Logger = logger; - } - - public BaseController(ILoggerFactory loggerFactory, IService service) - { - Guard.ThrownExceptionIfNull(loggerFactory, nameof(loggerFactory)); - Guard.ThrownExceptionIfNull(service, nameof(service)); - - Logger = loggerFactory.CreateLogger("CmsBaseController"); - Service = service; - - var cultureInfo = new CultureInfo(service.Instance.Culture); - - CultureInfo.DefaultThreadCurrentCulture = cultureInfo; - CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; - } - - public override void OnActionExecuting(ActionExecutingContext context) - { - base.OnActionExecuting(context); - - ViewBag.CurrentUser = Service?.CurrentUser; - } - - protected void SetupMessages(string pageTitle) - { - ViewBag.PageTitle = pageTitle; - } - - protected void SetupMessages(string pageTitle, PageType pageType, string description = "", string panelTitle = "") - { - SetupMessages(pageTitle); - - ViewBag.PageType = pageType.ToString(); - ViewBag.PageDescription = description; - ViewBag.PanelTitle = panelTitle; - } - - protected void SetupMessages(string pageTitle, PageType pageType, string modelError, string generalError, string description = "", string panelTitle = "") - { - SetupMessages(pageTitle, pageType, description, panelTitle); - - if (!string.IsNullOrWhiteSpace(modelError)) - { - ModelState.AddModelError("", modelError); - } - - TempData[MessageConstants.DangerMessage] = generalError; - } - - protected async Task UploadImageAsync(string webrootPath, string folderName) - { - string folderPath = GetUploadFolderPath(webrootPath, folderName); - - var formFile = Request.Form.Files[0]; - - if (formFile.Length == 0) - { - return null; - } - - _ = await UploadFileAsync(folderPath, formFile); - - string pathUrl = $"/image/{folderName}/"; - - var returnImage = new TinyMceUploadResult - { - Location = $"{pathUrl}{formFile.FileName}" - }; - - return Content(JsonConvert.SerializeObject(returnImage).ToLowerInvariant(), "application/json"); - } - - protected async Task PrepareAndUploadFilesAsync(string webrootPath, string folderName) - { - string folderPath = GetUploadFolderPath(webrootPath, folderName); - - var fileList = new List(); - - foreach (var formFile in Request.Form.Files) - { - if (formFile.Length == 0) - { - continue; - } - - string originalFile = await UploadFileAsync(folderPath, formFile); - - string fileSize = FileHelper.FormatFileSize(originalFile); - bool isImage = FileHelper.IsImage(formFile.FileName); - string pathUrl; - - if (isImage) - { - var imageSizes = new List<(int Width, int Height)> - { - (120, 120), - (320, 213), - (640, 426) - }; - - foreach (var imageSize in imageSizes) - { - ResizeImages(folderPath, formFile, originalFile, imageSize.Width, imageSize.Height); - } - - pathUrl = $"/image/{folderName}/"; - } - else - { - pathUrl = $"/file/{folderName}/"; - } - - fileList.Add(new UploadFilesResult - { - FileName = formFile.FileName, - Path = pathUrl, - Length = formFile.Length, - ContentType = formFile.ContentType, - IsImage = isImage, - Size = fileSize - }); - } - return Content(JsonConvert.SerializeObject(fileList).ToLowerInvariant(), "application/json"); - } - - private static void ResizeImages(string folderPath, IFormFile formFile, string originalFile, int width, int height) - { - string thumbnailFileName = Path.Combine(folderPath, $"{width}x{height}_{formFile.FileName}"); - FileHelper.ResizeImage(originalFile, thumbnailFileName, width, height, true); - } - - private static async Task UploadFileAsync(string folderPath, IFormFile formFile) - { - string filePath = Path.Combine(folderPath, Path.GetFileName(formFile.FileName)); - using (var stream = new FileStream(filePath, FileMode.Create)) - { - await formFile.CopyToAsync(stream); - } - - return filePath; - } - - private static string GetUploadFolderPath(string webrootPath, string folderName) - { - string folder = Path.Combine(webrootPath, "UploadedFiles", folderName); - - if (!Directory.Exists(folder)) - { - Directory.CreateDirectory(folder); - } - - return folder; - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs deleted file mode 100644 index f2cc1553..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.Services; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Cms.Controllers -{ - [Area("Cms")] - public class CategoryController : BaseController - { - private readonly ICategoryService _categoryService; - - public CategoryController(ILoggerFactory loggerFactory, IService service, ICategoryService categoryService) - : base(loggerFactory, service) - { - _categoryService = categoryService; - } - - public IActionResult Index() - { - SetupMessages("Categories", PageType.List, panelTitle: "List of categories"); - return View("List"); - } - - public IActionResult Create() - { - SetupMessages("Category", PageType.Create, panelTitle: "Create a new category"); - var categoryEditModel = _categoryService.SetupEditModel(); - - return View("CreateEdit", categoryEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task CreateAsync(CategoryEditModel categoryEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Categories", PageType.Create, panelTitle: "Create a new category"); - return View("CreateEdit", categoryEditModel); - } - - return await SaveAsync(categoryEditModel, nameof(CategoryController.Create)); - } - - public async Task EditAsync(Guid vanityId) - { - SetupMessages("Categories", PageType.Edit, panelTitle: "Edit an existing category"); - var categoryEditModel = await _categoryService.SetupEditModel(vanityId); - - return View("CreateEdit", categoryEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EditAsync(CategoryEditModel categoryEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Categories", PageType.Edit, panelTitle: "Edit an existing category"); - TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; - return View("CreateEdit", categoryEditModel); - } - - var categoryToUpdate = await _categoryService.SetupEditModel(categoryEditModel.VanityId); - - if (await TryUpdateModelAsync(categoryToUpdate)) - { - return await SaveAsync(categoryEditModel, nameof(CategoryController.EditAsync)); - } - - TempData[MessageConstants.WarningMessage] = "The model could not be updated."; - return RedirectToAction(nameof(CategoryController.EditAsync), categoryEditModel); - } - - [HttpPost] - public async Task DeleteAsync(Guid vanityId) - { - return Ok(await _categoryService.Delete(vanityId)); - } - - [HttpPost("cms/category/bulk-delete")] - public async Task BulkDeleteAsync([FromForm]Guid[] vanityId) - { - return Ok(await _categoryService.DeleteRange(vanityId)); - } - - [HttpPost] - public async Task GetDataAsync([FromForm]DataParameters parameters) - { - Guard.ThrownExceptionIfNull(parameters, nameof(parameters)); - - var items = await _categoryService.GetForDataTable(parameters); - var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); - - return Ok(dataTable); - } - - private async Task SaveAsync(CategoryEditModel categoryEditModel, string sender) - { - var returnValue = await _categoryService.Save(categoryEditModel); - - if (!returnValue.IsError) - { - TempData[MessageConstants.SuccessMessage] = returnValue.Message; - return RedirectToAction(nameof(CategoryController.Index)); - } - else - { - TempData[MessageConstants.DangerMessage] = returnValue.Message; - return RedirectToAction(sender); - } - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs deleted file mode 100644 index db14c031..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Threading.Tasks; -using CmsEngine.Application.Services; -using CmsEngine.Ui.Areas.Cms.Controllers; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Admin.Controllers -{ - [Area("Cms")] - public class HomeController : BaseController - { - private readonly IEmailService _emailService; - - public HomeController(ILoggerFactory loggerFactory, IService service, IEmailService emailService) - : base(loggerFactory, service) - { - _emailService = emailService; - } - - public async Task IndexAsync() - { - SetupMessages("Dashboard"); - return View(await _emailService.GetOrderedByDate()); - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs deleted file mode 100644 index 5a269958..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.Services; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Cms.Controllers -{ - [Area("Cms")] - public class PageController : BaseController - { - private readonly IWebHostEnvironment _env; - private readonly IPageService _pageService; - - public PageController(ILoggerFactory loggerFactory, IService service, - IWebHostEnvironment env, IPageService pageService) : base(loggerFactory, service) - { - _env = env; - _pageService = pageService; - } - - public IActionResult Index() - { - SetupMessages("Pages", PageType.List, panelTitle: "List of pages"); - return View("List"); - } - - public IActionResult Create() - { - SetupMessages("Page", PageType.Create, panelTitle: "Create a new page"); - var pageEditModel = _pageService.SetupEditModel(); - - return View("CreateEdit", pageEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task CreateAsync(PageEditModel pageEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Pages", PageType.Create, panelTitle: "Create a new page"); - return View("CreateEdit", pageEditModel); - } - - return await SaveAsync(pageEditModel, nameof(PageController.Create)); - } - - public async Task EditAsync(Guid vanityId) - { - SetupMessages("Pages", PageType.Edit, panelTitle: "Edit an existing page"); - var pageEditModel = await _pageService.SetupEditModel(vanityId); - - return View("CreateEdit", pageEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EditAsync(PageEditModel pageEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Pages", PageType.Edit, panelTitle: "Edit an existing page"); - TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; - return View("CreateEdit", pageEditModel); - } - - var pageToUpdate = await _pageService.SetupEditModel(pageEditModel.VanityId); - - if (await TryUpdateModelAsync(pageToUpdate)) - { - return await SaveAsync(pageEditModel, nameof(PageController.EditAsync)); - } - - TempData[MessageConstants.WarningMessage] = "The model could not be updated."; - return RedirectToAction(nameof(PageController.EditAsync), pageEditModel); - } - - [HttpPost] - public async Task DeleteAsync(Guid vanityId) - { - return Ok(await _pageService.Delete(vanityId)); - } - - [HttpPost("cms/page/bulk-delete")] - public async Task BulkDeleteAsync([FromForm]Guid[] vanityId) - { - return Ok(await _pageService.DeleteRange(vanityId)); - } - - [HttpPost] - public async Task GetDataAsync([FromForm]DataParameters parameters) - { - Guard.ThrownExceptionIfNull(parameters, nameof(parameters)); - - var items = await _pageService.GetForDataTable(parameters); - var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); - - return Ok(dataTable); - } - - [HttpPost] - public async Task UploadImagesAsync() - { - return await UploadImageAsync(_env.WebRootPath, "Page"); - } - - [HttpPost] - public async Task UploadFilesAsync() - { - return await PrepareAndUploadFilesAsync(_env.WebRootPath, "Page"); - } - - private async Task SaveAsync(PageEditModel pageEditModel, string sender) - { - var returnValue = await _pageService.Save(pageEditModel); - - if (!returnValue.IsError) - { - TempData[MessageConstants.SuccessMessage] = returnValue.Message; - return RedirectToAction(nameof(PageController.Index)); - } - else - { - TempData[MessageConstants.DangerMessage] = returnValue.Message; - return RedirectToAction(sender); - } - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs deleted file mode 100644 index c00e0591..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.Services; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Cms.Controllers -{ - [Area("Cms")] - public class PostController : BaseController - { - private readonly IWebHostEnvironment _env; - private readonly IPostService _postService; - - public PostController(ILoggerFactory loggerFactory, IService service, - IWebHostEnvironment env, IPostService postService) : base(loggerFactory, service) - { - _env = env; - _postService = postService; - } - - public IActionResult Index() - { - SetupMessages("Posts", PageType.List, panelTitle: "List of posts"); - return View("List"); - } - - public async Task CreateAsync() - { - SetupMessages("Post", PageType.Create, panelTitle: "Create a new post"); - var postEditModel = await _postService.SetupEditModel(); - - return View("CreateEdit", postEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task CreateAsync(PostEditModel postEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Posts", PageType.Create, panelTitle: "Create a new post"); - return View("CreateEdit", postEditModel); - } - - return await SaveAsync(postEditModel, nameof(PostController.CreateAsync)); - } - - public async Task EditAsync(Guid vanityId) - { - SetupMessages("Posts", PageType.Edit, panelTitle: "Edit an existing post"); - var postEditModel = await _postService.SetupEditModel(vanityId); - - return View("CreateEdit", postEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EditAsync(PostEditModel postEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Posts", PageType.Edit, panelTitle: "Edit an existing post"); - TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; - return View("CreateEdit", postEditModel); - } - - var postToUpdate = await _postService.SetupEditModel(postEditModel.VanityId); - - if (await TryUpdateModelAsync(postToUpdate)) - { - return await SaveAsync(postEditModel, nameof(PostController.EditAsync)); - } - - TempData[MessageConstants.WarningMessage] = "The model could not be updated."; - return RedirectToAction(nameof(PostController.EditAsync), postEditModel); - } - - [HttpPost] - public async Task DeleteAsync(Guid vanityId) - { - return Ok(await _postService.Delete(vanityId)); - } - - [HttpPost("cms/post/bulk-delete")] - public async Task BulkDeleteAsync([FromForm]Guid[] vanityId) - { - return Ok(await _postService.DeleteRange(vanityId)); - } - - [HttpPost] - public async Task GetDataAsync([FromForm]DataParameters parameters) - { - Guard.ThrownExceptionIfNull(parameters, nameof(parameters)); - - var items = await _postService.GetForDataTable(parameters); - var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); - - return Ok(dataTable); - } - - [HttpPost] - public async Task UploadImagesAsync() - { - return await UploadImageAsync(_env.WebRootPath, "Post"); - } - - [HttpPost] - public async Task UploadFilesAsync() - { - return await PrepareAndUploadFilesAsync(_env.WebRootPath, "Post"); - } - - private async Task SaveAsync(PostEditModel postEditModel, string sender) - { - var returnValue = await _postService.Save(postEditModel); - - if (!returnValue.IsError) - { - TempData[MessageConstants.SuccessMessage] = returnValue.Message; - return RedirectToAction(nameof(PostController.Index)); - } - else - { - TempData[MessageConstants.DangerMessage] = returnValue.Message; - return RedirectToAction(sender); - } - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs deleted file mode 100644 index f5e23a0f..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.Services; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Cms.Controllers -{ - [Area("Cms")] - public class TagController : BaseController - { - private readonly ITagService _tagService; - - public TagController(ILoggerFactory loggerFactory, IService service, ITagService tagService) - : base(loggerFactory, service) - { - _tagService = tagService; - } - - public IActionResult Index() - { - SetupMessages("Tags", PageType.List, panelTitle: "List of tags"); - //var tagViewModel = service.SetupViewModel(); - return View("List"); - } - - public IActionResult Create() - { - SetupMessages("Tag", PageType.Create, panelTitle: "Create a new tag"); - var tagEditModel = _tagService.SetupEditModel(); - - return View("CreateEdit", tagEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task CreateAsync(TagEditModel tagEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Tags", PageType.Create, panelTitle: "Create a new tag"); - return View("CreateEdit", tagEditModel); - } - - return await SaveAsync(tagEditModel, nameof(TagController.Create)); - } - - public async Task EditAsync(Guid vanityId) - { - SetupMessages("Tags", PageType.Edit, panelTitle: "Edit an existing tag"); - var tagEditModel = await _tagService.SetupEditModel(vanityId); - - return View("CreateEdit", tagEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EditAsync(TagEditModel tagEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Tags", PageType.Edit, panelTitle: "Edit an existing tag"); - return View("CreateEdit", tagEditModel); - } - - var tagToUpdate = await _tagService.SetupEditModel(tagEditModel.VanityId); - - if (await TryUpdateModelAsync(tagToUpdate)) - { - return await SaveAsync(tagEditModel, nameof(TagController.EditAsync)); - } - TempData[MessageConstants.WarningMessage] = "The model could not be updated."; - return RedirectToAction(nameof(TagController.EditAsync), tagEditModel); - } - - [HttpPost] - public async Task DeleteAsync(Guid vanityId) - { - return Ok(await _tagService.Delete(vanityId)); - } - - [HttpPost("cms/tag/bulk-delete")] - public async Task BulkDeleteAsync([FromForm]Guid[] vanityId) - { - return Ok(await _tagService.DeleteRange(vanityId)); - } - - [HttpPost] - public async Task GetDataAsync([FromForm]DataParameters parameters) - { - Guard.ThrownExceptionIfNull(parameters, nameof(parameters)); - - var items = await _tagService.GetForDataTable(parameters); - var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); - - return Ok(dataTable); - } - - private async Task SaveAsync(TagEditModel tagEditModel, string sender) - { - var returnValue = await _tagService.Save(tagEditModel); - - if (!returnValue.IsError) - { - TempData[MessageConstants.SuccessMessage] = returnValue.Message; - return RedirectToAction(nameof(TagController.Index)); - } - else - { - TempData[MessageConstants.DangerMessage] = returnValue.Message; - return RedirectToAction(sender); - } - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs b/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs deleted file mode 100644 index 258be40f..00000000 --- a/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.EditModels; -using CmsEngine.Application.Helpers; -using CmsEngine.Application.Services; -using CmsEngine.Application.ViewModels.DataTableViewModels; -using CmsEngine.Core; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Cms.Controllers -{ - [Area("Cms")] - public class WebsiteController : BaseController - { - private readonly IWebHostEnvironment _env; - private readonly IWebsiteService _websiteService; - - public WebsiteController(ILoggerFactory loggerFactory, IService service, - IWebHostEnvironment env, IWebsiteService websiteService) : base(loggerFactory, service) - { - _env = env; - _websiteService = websiteService; - } - - public IActionResult Index() - { - SetupMessages("Websites", PageType.List, panelTitle: "List of websites"); - return View("List"); - } - - public IActionResult Create() - { - SetupMessages("Website", PageType.Create, panelTitle: "Create a new website"); - var websiteEditModel = _websiteService.SetupEditModel(); - - return View("CreateEdit", websiteEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task CreateAsync(WebsiteEditModel websiteEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Websites", PageType.Create, panelTitle: "Create a new website"); - return View("CreateEdit", websiteEditModel); - } - - return await SaveAsync(websiteEditModel, nameof(WebsiteController.Create)); - } - - public async Task EditAsync(Guid vanityId) - { - SetupMessages("Websites", PageType.Edit, panelTitle: "Edit an existing website"); - var websiteEditModel = await _websiteService.SetupEditModel(vanityId); - - return View("CreateEdit", websiteEditModel); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task EditAsync(WebsiteEditModel websiteEditModel) - { - if (!ModelState.IsValid) - { - SetupMessages("Websites", PageType.Edit, panelTitle: "Edit an existing website"); - return View("CreateEdit", websiteEditModel); - } - - var websiteToUpdate = await _websiteService.SetupEditModel(websiteEditModel.VanityId); - - if (await TryUpdateModelAsync(websiteToUpdate)) - { - return await SaveAsync(websiteEditModel, nameof(WebsiteController.EditAsync)); - } - TempData[MessageConstants.WarningMessage] = "The model could not be updated."; - return RedirectToAction(nameof(WebsiteController.EditAsync), websiteEditModel); - } - - [HttpPost] - public async Task DeleteAsync(Guid vanityId) - { - return Ok(await _websiteService.Delete(vanityId)); - } - - [HttpPost("cms/website/bulk-delete")] - public async Task BulkDeleteAsync([FromForm]Guid[] vanityId) - { - return Ok(await _websiteService.DeleteRange(vanityId)); - } - - [HttpPost] - public async Task GetDataAsync([FromForm]DataParameters parameters) - { - Guard.ThrownExceptionIfNull(parameters, nameof(parameters)); - - var items = await _websiteService.GetForDataTable(parameters); - var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); - - return Ok(dataTable); - } - - [HttpPost] - public async Task UploadImagesAsync() - { - return await UploadImageAsync(_env.WebRootPath, "Website"); - } - - [HttpPost] - public async Task UploadFilesAsync() - { - return await PrepareAndUploadFilesAsync(_env.WebRootPath, "Website"); - } - - private async Task SaveAsync(WebsiteEditModel websiteEditModel, string sender) - { - var returnValue = await _websiteService.Save(websiteEditModel); - - if (!returnValue.IsError) - { - TempData[MessageConstants.SuccessMessage] = returnValue.Message; - return RedirectToAction(nameof(WebsiteController.Index)); - } - else - { - TempData[MessageConstants.DangerMessage] = returnValue.Message; - return RedirectToAction(sender); - } - } - } -} diff --git a/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml b/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml deleted file mode 100644 index c9440dd4..00000000 --- a/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml +++ /dev/null @@ -1,10 +0,0 @@ -@using Microsoft.AspNetCore.Identity -@using CmsEngine.Core -@using CmsEngine.Core.Constants -@using CmsEngine.Data.Entities -@using CmsEngine.Application.EditModels -@using CmsEngine.Application.ViewModels -@using CmsEngine.Application.ViewModels.AccountViewModels -@using CmsEngine.Application.ViewModels.ManageViewModels -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -@addTagHelper *, CmsEngine.Ui diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs deleted file mode 100644 index 7a4d7aac..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Data.Entities; -using CmsEngine.Ui.Extensions; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ForgotPasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly IEmailSender _emailSender; - - public ForgotPasswordModel(UserManager userManager, IEmailSender emailSender) - { - _userManager = userManager; - _emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - } - - public async Task OnPostAsync() - { - if (ModelState.IsValid) - { - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) - { - // Don't reveal that the user does not exist or is not confirmed - return RedirectToPage("./ForgotPasswordConfirmation"); - } - - // For more information on how to enable account confirmation and password reset please - // visit https://go.microsoft.com/fwlink/?LinkID=532713 - var code = await _userManager.GeneratePasswordResetTokenAsync(user); - var callbackUrl = Url.Page( - "/Account/ResetPassword", - pageHandler: null, - values: new { code }, - protocol: Request.Scheme); - - await _emailSender.SendPasswordResetAsync(Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); - - return RedirectToPage("./ForgotPasswordConfirmation"); - } - - return Page(); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs deleted file mode 100644 index 1634774d..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LoginModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LoginModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public IList ExternalLogins { get; set; } - - public string ReturnUrl { get; set; } - - [TempData] - public string ErrorMessage { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - - [Display(Name = "Remember me?")] - public bool RememberMe { get; set; } - } - - public async Task OnGetAsync(string returnUrl = null) - { - if (!string.IsNullOrEmpty(ErrorMessage)) - { - ModelState.AddModelError(string.Empty, ErrorMessage); - } - - returnUrl = returnUrl ?? Url.Content("~/cms"); - - // Clear the existing external cookie to ensure a clean login process - await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); - - ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); - - ReturnUrl = returnUrl; - } - - public async Task OnPostAsync(string returnUrl = null) - { - returnUrl = returnUrl ?? Url.Content("~/cms"); - - if (ModelState.IsValid) - { - // This doesn't count login failures towards account lockout - // To enable password failures to trigger account lockout, set lockoutOnFailure: true - var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); - if (result.Succeeded) - { - _logger.LogDebug("User logged in."); - return LocalRedirect(returnUrl); - } - if (result.RequiresTwoFactor) - { - return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); - } - if (result.IsLockedOut) - { - _logger.LogWarning("User account locked out."); - return RedirectToPage("./Lockout"); - } - else - { - ModelState.AddModelError(string.Empty, "Invalid login attempt."); - return Page(); - } - } - - // If we got this far, something failed, redisplay form - return Page(); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs deleted file mode 100644 index 5ecf83a2..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using CmsEngine.Ui.Admin.Controllers; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class LogoutModel : PageModel - { - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public LogoutModel(SignInManager signInManager, ILogger logger) - { - _signInManager = signInManager; - _logger = logger; - } - - public async Task OnGet() - { - await _signInManager.SignOutAsync(); - _logger.LogDebug("User logged out."); - return RedirectToAction(nameof(HomeController.IndexAsync), "Home", new { area = "cms" }); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs deleted file mode 100644 index 22b6d9ff..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; -using CmsEngine.Application.Services; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; -namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage -{ - public class ChangePasswordModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - private readonly IService _service; - - public ChangePasswordModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger, - IService service) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - _service = service; - } - - [BindProperty] - public InputModel Input { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - public class InputModel - { - [Required] - [DataType(DataType.Password)] - [Display(Name = "Current password")] - public string OldPassword { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "New password")] - public string NewPassword { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm new password")] - [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public async Task OnGetAsync() - { - ViewData["CurrentUser"] = _service?.CurrentUser; - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var hasPassword = await _userManager.HasPasswordAsync(user); - if (!hasPassword) - { - return RedirectToPage("./SetPassword"); - } - - return Page(); - } - - public async Task OnPostAsync() - { - ViewData["CurrentUser"] = _service?.CurrentUser; - - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); - if (!changePasswordResult.Succeeded) - { - foreach (var error in changePasswordResult.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - - await _signInManager.RefreshSignInAsync(user); - _logger.LogDebug("User changed their password successfully."); - StatusMessage = "Your password has been changed."; - - return RedirectToPage(); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs deleted file mode 100644 index f96371e9..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage -{ - public class DeletePersonalDataModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly ILogger _logger; - - public DeletePersonalDataModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _logger = logger; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [DataType(DataType.Password)] - public string Password { get; set; } - } - - public bool RequirePassword { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - RequirePassword = await _userManager.HasPasswordAsync(user); - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - RequirePassword = await _userManager.HasPasswordAsync(user); - if (RequirePassword) - { - if (!await _userManager.CheckPasswordAsync(user, Input.Password)) - { - ModelState.AddModelError(string.Empty, "Password not correct."); - return Page(); - } - } - - var result = await _userManager.DeleteAsync(user); - var userId = await _userManager.GetUserIdAsync(user); - if (!result.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'."); - } - - await _signInManager.SignOutAsync(); - - _logger.LogDebug("User with ID '{UserId}' deleted themselves.", userId); - - return Redirect("~/"); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs deleted file mode 100644 index ed8b4064..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage -{ - public class Disable2faModel : PageModel - { - private readonly UserManager _userManager; - private readonly ILogger _logger; - - public Disable2faModel( - UserManager userManager, - ILogger logger) - { - _userManager = userManager; - _logger = logger; - } - - [TempData] - public string StatusMessage { get; set; } - - public async Task OnGet() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - if (!await _userManager.GetTwoFactorEnabledAsync(user)) - { - throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); - } - - return Page(); - } - - public async Task OnPostAsync() - { - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); - if (!disable2faResult.Succeeded) - { - throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); - } - - _logger.LogDebug("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); - StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; - return RedirectToPage("./TwoFactorAuthentication"); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs deleted file mode 100644 index ecdf4172..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Application.Services; -using CmsEngine.Data.Entities; -using CmsEngine.Ui.Extensions; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage -{ - public partial class IndexModel : PageModel - { - private readonly UserManager _userManager; - private readonly SignInManager _signInManager; - private readonly IEmailSender _emailSender; - private readonly IService _service; - private readonly ILogger _logger; - - public IndexModel( - UserManager userManager, - SignInManager signInManager, - IEmailSender emailSender, - IService service, ILogger logger) - { - _userManager = userManager; - _signInManager = signInManager; - _emailSender = emailSender; - _service = service; - _logger = logger; - } - - public string Username { get; set; } - - public bool IsEmailConfirmed { get; set; } - - [TempData] - public string StatusMessage { get; set; } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - public string Username { get; set; } - - public string Name { get; set; } - - public string Surname { get; set; } - - [Required] - [EmailAddress] - public string Email { get; set; } - - [Phone] - [Display(Name = "Phone number")] - public string PhoneNumber { get; set; } - } - - public async Task OnGetAsync() - { - ViewData["CurrentUser"] = _service?.CurrentUser; - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var userName = await _userManager.GetUserNameAsync(user); - var email = await _userManager.GetEmailAsync(user); - var phoneNumber = await _userManager.GetPhoneNumberAsync(user); - - Username = userName; - - Input = new InputModel - { - Username = user.UserName, - Name = user.Name, - Surname = user.Surname, - Email = email, - PhoneNumber = phoneNumber - }; - - IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); - - return Page(); - } - - public async Task OnPostAsync() - { - _logger.LogDebug("Account > Manage > OnPostAsync()"); - ViewData["CurrentUser"] = _service?.CurrentUser; - - if (!ModelState.IsValid) - { - _logger.LogDebug("Invalid ModelState"); - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - _logger.LogDebug($"User not found. Id: {_userManager.GetUserId(User)}"); - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - var email = await _userManager.GetEmailAsync(user); - if (Input.Email != email) - { - var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); - if (!setEmailResult.Succeeded) - { - var userId = await _userManager.GetUserIdAsync(user); - throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'."); - } - } - - var phoneNumber = await _userManager.GetPhoneNumberAsync(user); - if (Input.PhoneNumber != phoneNumber) - { - var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); - if (!setPhoneResult.Succeeded) - { - var userId = await _userManager.GetUserIdAsync(user); - throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'."); - } - } - - if (Input.Name != user.Name || Input.Surname != user.Surname) - { - user.Name = Input.Name; - user.Surname = Input.Surname; - await _userManager.UpdateAsync(user); - } - - await _signInManager.RefreshSignInAsync(user); - StatusMessage = "Your profile has been updated"; - - _logger.LogDebug($"User {_userManager.GetUserId(User)} updated"); - - return RedirectToPage(); - } - - public async Task OnPostSendVerificationEmailAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.GetUserAsync(User); - if (user == null) - { - return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); - } - - - var userId = await _userManager.GetUserIdAsync(user); - var email = await _userManager.GetEmailAsync(user); - var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); - var callbackUrl = Url.Page( - "/Account/ConfirmEmail", - pageHandler: null, - values: new { userId = userId, code = code }, - protocol: Request.Scheme); - - await _emailSender.SendEmailConfirmationAsync(email, HtmlEncoder.Default.Encode(callbackUrl)); - - StatusMessage = "Verification email sent. Please check your email."; - return RedirectToPage(); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs deleted file mode 100644 index f093bf99..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage -{ - public static class ManageNavPages - { - public static string Index => "Index"; - - public static string ChangePassword => "ChangePassword"; - - public static string DownloadPersonalData => "DownloadPersonalData"; - - public static string DeletePersonalData => "DeletePersonalData"; - - public static string ExternalLogins => "ExternalLogins"; - - public static string PersonalData => "PersonalData"; - - public static string TwoFactorAuthentication => "TwoFactorAuthentication"; - - public static string IndexNavClass(ViewContext viewContext) - { - return PageNavClass(viewContext, Index); - } - - public static string ChangePasswordNavClass(ViewContext viewContext) - { - return PageNavClass(viewContext, ChangePassword); - } - - public static string DownloadPersonalDataNavClass(ViewContext viewContext) - { - return PageNavClass(viewContext, DownloadPersonalData); - } - - public static string DeletePersonalDataNavClass(ViewContext viewContext) { - return PageNavClass(viewContext, DeletePersonalData); - } - - public static string ExternalLoginsNavClass(ViewContext viewContext) { - return PageNavClass(viewContext, ExternalLogins); - } - - public static string PersonalDataNavClass(ViewContext viewContext) { - return PageNavClass(viewContext, PersonalData); - } - - public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) { - return PageNavClass(viewContext, TwoFactorAuthentication); - } - - public static string PageNavClass(ViewContext viewContext, string page) - { - Guard.ThrownExceptionIfNull(viewContext, nameof(viewContext)); - - string activePage = viewContext.ViewData["ActivePage"] as string - ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); - return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs deleted file mode 100644 index 8a6cbe0d..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Identity.UI.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class RegisterModel : PageModel - { - private readonly SignInManager signInManager; - private readonly UserManager userManager; - private readonly ILogger logger; - private readonly IEmailSender emailSender; - - public RegisterModel( - UserManager userManager, - SignInManager signInManager, - ILogger logger, - IEmailSender emailSender) - { - this.userManager = userManager; - this.signInManager = signInManager; - this.logger = logger; - this.emailSender = emailSender; - } - - [BindProperty] - public InputModel Input { get; set; } - - public string ReturnUrl { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - [Display(Name = "Email")] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - [Display(Name = "Password")] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - } - - public void OnGet(string returnUrl = null) - { - ReturnUrl = returnUrl; - } - - public async Task OnPostAsync(string returnUrl = null) - { - returnUrl ??= Url.Content("~/"); - if (ModelState.IsValid) - { - var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; - var result = await userManager.CreateAsync(user, Input.Password); - if (result.Succeeded) - { - logger.LogDebug("User created a new account with password."); - - string code = await userManager.GenerateEmailConfirmationTokenAsync(user); - string callbackUrl = Url.Page("/Account/ConfirmEmail", - pageHandler: null, - values: new { userId = user.Id, code }, - protocol: Request.Scheme); - - await emailSender.SendEmailAsync(Input.Email, "Confirm your email", - $"Please confirm your account by clicking here."); - - await signInManager.SignInAsync(user, isPersistent: false); - return LocalRedirect(returnUrl); - } - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - } - - // If we got this far, something failed, redisplay form - return Page(); - } - } -} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs deleted file mode 100644 index de3de6af..00000000 --- a/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Threading.Tasks; -using CmsEngine.Data.Entities; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace CmsEngine.Ui.Areas.Identity.Pages.Account -{ - [AllowAnonymous] - public class ResetPasswordModel : PageModel - { - private readonly UserManager _userManager; - - public ResetPasswordModel(UserManager userManager) - { - _userManager = userManager; - } - - [BindProperty] - public InputModel Input { get; set; } - - public class InputModel - { - [Required] - [EmailAddress] - public string Email { get; set; } - - [Required] - [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] - [DataType(DataType.Password)] - public string Password { get; set; } - - [DataType(DataType.Password)] - [Display(Name = "Confirm password")] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } - - public string Code { get; set; } - } - - public IActionResult OnGet(string code = null) - { - if (code == null) - { - return BadRequest("A code must be supplied for password reset."); - } - else - { - Input = new InputModel - { - Code = code - }; - return Page(); - } - } - - public async Task OnPostAsync() - { - if (!ModelState.IsValid) - { - return Page(); - } - - var user = await _userManager.FindByEmailAsync(Input.Email); - if (user == null) - { - // Don't reveal that the user does not exist - return RedirectToPage("./ResetPasswordConfirmation"); - } - - var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); - if (result.Succeeded) - { - return RedirectToPage("./ResetPasswordConfirmation"); - } - - foreach (var error in result.Errors) - { - ModelState.AddModelError(string.Empty, error.Description); - } - return Page(); - } - } -} diff --git a/CmsEngine.Ui/CmsEngine.Ui.csproj b/CmsEngine.Ui/CmsEngine.Ui.csproj deleted file mode 100644 index 2dba02d4..00000000 --- a/CmsEngine.Ui/CmsEngine.Ui.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - net5.0 - aspnet-CmsEngine.Ui-F1BE4E66-8A27-4EC1-866F-449A69A82325 - true - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - diff --git a/CmsEngine.Ui/Controllers/BaseController.cs b/CmsEngine.Ui/Controllers/BaseController.cs deleted file mode 100644 index 32409a50..00000000 --- a/CmsEngine.Ui/Controllers/BaseController.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Globalization; -using System.Threading.Tasks; -using CmsEngine.Application.Services; -using CmsEngine.Application.ViewModels; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Controllers -{ - public class BaseController : Controller - { - //protected readonly IService service; - public InstanceViewModel Instance { get; private set; } - public ILogger Logger { get; private set; } - - private readonly ICategoryService _categoryService; - private readonly IPageService _pageService; - private readonly IPostService _postService; - private readonly ITagService _tagService; - - public BaseController(ILoggerFactory loggerFactory, IService service, ICategoryService categoryService, IPageService pageService, IPostService postService, ITagService tagService) - { - Guard.ThrownExceptionIfNull(loggerFactory, nameof(loggerFactory)); - Guard.ThrownExceptionIfNull(service, nameof(service)); - - Logger = loggerFactory.CreateLogger("BaseController"); - Instance = service.Instance; - - _categoryService = categoryService; - _pageService = pageService; - _postService = postService; - _tagService = tagService; - - var cultureInfo = new CultureInfo(Instance.Culture); - - CultureInfo.DefaultThreadCurrentCulture = cultureInfo; - CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; - } - - public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) - { - Guard.ThrownExceptionIfNull(context, nameof(context)); - - if (context.ActionArguments.TryGetValue("q", out object searchValue)) - { - // Showing searched posts - Instance.PagedPosts = await _postService.FindPublishedForPaginationOrderByDateDescending(searchValue.ToString()); - } - else - { - if (context.ActionArguments.TryGetValue("page", out object value) && int.TryParse(value.ToString(), out int page)) - { - // Showing posts after paging - Instance.PagedPosts = await _postService.GetPublishedForPagination(page); - } - else - { - // Showing regular posts - Instance.PagedPosts = await _postService.GetPublishedForPagination(); - } - } - - Instance.LatestPosts = await _postService.GetPublishedLatestPosts(3); - Instance.Pages = await _pageService.GetAllPublished(); - Instance.Categories = await _categoryService.GetCategoriesWithPostCount(); - Instance.CategoriesWithPosts = await _categoryService.GetCategoriesWithPost(); - Instance.Tags = await _tagService.GetAllTags(); - - - await base.OnActionExecutionAsync(context, next); - } - } -} diff --git a/CmsEngine.Ui/Controllers/BlogController.cs b/CmsEngine.Ui/Controllers/BlogController.cs deleted file mode 100644 index 8a0b3eb7..00000000 --- a/CmsEngine.Ui/Controllers/BlogController.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using CmsEngine.Application.Services; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Controllers -{ - public class BlogController : BaseController - { - private readonly IPostService _postService; - private readonly IXmlService _xmlService; - - public BlogController(ILoggerFactory loggerFactory, ICategoryService categoryService, IPageService pageService, IPostService postService, - ITagService tagService, IXmlService xmlService, IService service) - : base(loggerFactory, service, categoryService, pageService, postService, tagService) - { - _postService = postService; - _xmlService = xmlService; - } - - public IActionResult Index(int page = 1, string q = "") - { - if (string.IsNullOrWhiteSpace(q)) - { - Instance.PageTitle = page == 1 - ? $"Blog - {Instance.Name}" - : $"Blog - {Instance.Name} - Page {page}"; - } - else - { - Instance.PageTitle = $"Results for '{q}' - {Instance.Name}"; - } - - return View(Instance); - } - - public async Task PostAsync(string slug) - { - Instance.SelectedDocument = await _postService.GetBySlug(slug); - - if (Instance.SelectedDocument == null) - { - return NotFound(); - } - - Instance.PageTitle = $"{Instance.SelectedDocument.Title} - {Instance.Name}"; - return View(Instance); - } - - public async Task CategoryAsync(string slug, int page = 1) - { - Instance.PagedPosts = await _postService.GetPublishedByCategoryForPagination(slug, page); - string selectedCategory = Instance.PagedPosts.SelectMany(p => p.Categories.Where(c => c.Slug == slug).Select(x => x.Name)).FirstOrDefault(); - - if (selectedCategory == null) - { - return NotFound(); - } - - Instance.PageTitle = $"{selectedCategory} - {Instance.Name}"; - return View("Index", Instance); - } - - public async Task TagAsync(string slug, int page = 1) - { - Instance.PagedPosts = await _postService.GetPublishedByTagForPagination(slug, page); - string selectedTag = Instance.PagedPosts.SelectMany(p => p.Tags.Where(t => t.Slug == slug).Select(x => x.Name)).FirstOrDefault(); - - if (selectedTag == null) - { - return NotFound(); - } - - Instance.PageTitle = $"#{selectedTag} - {Instance.Name}"; - return View("Index", Instance); - } - - public async Task FeedAsync() - { - var feed = await _xmlService.GenerateFeed(); - return Content(feed.ToString(), "text/xml"); - } - } -} diff --git a/CmsEngine.Ui/Controllers/ErrorController.cs b/CmsEngine.Ui/Controllers/ErrorController.cs deleted file mode 100644 index 09964bce..00000000 --- a/CmsEngine.Ui/Controllers/ErrorController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using CmsEngine.Application.ViewModels; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Controllers -{ - public class ErrorController : Controller - { - private readonly ILogger _logger; - - public ErrorController(ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger("ErrorController"); - } - - [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] - public IActionResult Index(string code) - { - ErrorViewModel errorViewModel; - - switch (code) - { - case "404": - _logger.LogError("Page not found"); - errorViewModel = new ErrorViewModel("404 - Page not found", "Sorry but this page does not exist"); - break; - default: - _logger.LogError("Default error"); - errorViewModel = new ErrorViewModel("Something went wrong"); - break; - } - - ViewBag.ErrorCode = code; - return View(errorViewModel); - } - } -} diff --git a/CmsEngine.Ui/Controllers/HomeController.cs b/CmsEngine.Ui/Controllers/HomeController.cs deleted file mode 100644 index 8b7c7f98..00000000 --- a/CmsEngine.Ui/Controllers/HomeController.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Application.Services; -using CmsEngine.Core.Constants; -using CmsEngine.Core.Exceptions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace CmsEngine.Ui.Controllers -{ - public class HomeController : BaseController - { - private readonly IEmailSender _emailSender; - private readonly IPageService _pageService; - private readonly IXmlService _xmlService; - private readonly IEmailService _emailService; - - public HomeController(ILoggerFactory loggerFactory, IEmailSender emailSender, IPageService pageService, IXmlService xmlService, - ICategoryService categoryService, ITagService tagService, IService service, IPostService postService, - IEmailService emailService) - : base(loggerFactory, service, categoryService, pageService, postService, tagService) - { - _emailSender = emailSender; - _pageService = pageService; - _xmlService = xmlService; - _emailService = emailService; - } - - public IActionResult Index() - { - Instance.PageTitle = $"{Instance.Name}"; - return View(Instance); - } - - public async Task PageAsync(string slug) - { - Instance.SelectedDocument = await _pageService.GetBySlug(slug); - - if (Instance.SelectedDocument == null) - { - return NotFound(); - } - - Instance.PageTitle = $"{Instance.SelectedDocument.Title} - {Instance.Name}"; - return View(Instance); - } - - public IActionResult Archive() - { - Instance.PageTitle = $"Archive - {Instance.Name}"; - return View(Instance); - } - - public IActionResult Contact() - { - Instance.PageTitle = $"Contact - {Instance.Name}"; - return View(Instance); - } - - [HttpPost] - public async Task ContactAsync(ContactForm contactForm, string returnUrl = null) - { - if (!ModelState.IsValid) - { - TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; - return View(Instance); - } - - ViewData["ReturnUrl"] = returnUrl; - contactForm.To = Instance.ContactDetails.Email; - - try - { - if ((await _emailService.Save(contactForm)).IsError) - { - throw new Exception("Error when saving e-mail"); - } - - await _emailSender.SendEmailAsync(contactForm); - TempData[MessageConstants.SuccessMessage] = "Your message was sent. I will answer as soon as I can."; - } - catch (EmailException) - { - TempData[MessageConstants.DangerMessage] = "We could not send the messsage. Please try other communication channels."; - } - - return RedirectToAction(nameof(HomeController.Contact)); - } - - public async Task SitemapAsync() - { - var sitemap = await _xmlService.GenerateSitemap(); - return Content(sitemap.ToString(), "text/xml"); - } - } -} diff --git a/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs b/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs deleted file mode 100644 index dc99f8c5..00000000 --- a/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text.Encodings.Web; -using System.Threading.Tasks; -using CmsEngine.Application.Helpers.Email; - -namespace CmsEngine.Ui.Extensions -{ - public static class EmailSenderExtensions - { - public static Task SendEmailConfirmationAsync(this IEmailSender emailSender, string email, string link) - { - var contactForm = new ContactForm(email, - "Confirm your email", - $"Please confirm your account by clicking this link: link"); - - return emailSender.SendEmailAsync(contactForm); - } - - public static Task SendPasswordResetAsync(this IEmailSender emailSender, string email, string link) - { - var contactForm = new ContactForm(email, - "Reset Password", - $"Please reset your password by clicking here: link"); - - return emailSender.SendEmailAsync(contactForm); - } - } -} diff --git a/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs b/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs deleted file mode 100644 index cc04fc64..00000000 --- a/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Linq; -using CmsEngine.Core.Utils; -using Microsoft.AspNetCore.Mvc.Rendering; - -namespace CmsEngine.Ui.Extensions -{ - public static class HtmlHelperExtensions - { - public static string IsSelected(this IHtmlHelper htmlHelper, string controllers, string actions, string cssClass = "active") - { - Guard.ThrownExceptionIfNull(htmlHelper, nameof(htmlHelper)); - - string currentAction = (htmlHelper.ViewContext.RouteData.Values["action"] as string)?.ToLower(); - string currentController = (htmlHelper.ViewContext.RouteData.Values["controller"] as string)?.ToLower(); - var acceptedActions = (actions ?? currentAction).Split(',').Select(x => x.Trim().ToLower()); - var acceptedControllers = (controllers ?? currentController).Split(',').Select(x => x.Trim().ToLower()); - return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ? cssClass : string.Empty; - } - } -} diff --git a/CmsEngine.Ui/GlobalSuppressions.cs b/CmsEngine.Ui/GlobalSuppressions.cs deleted file mode 100644 index 8cc8fe95..00000000 --- a/CmsEngine.Ui/GlobalSuppressions.cs +++ /dev/null @@ -1,10 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Reliability", "CA2007:Consider calling ConfigureAwait on the awaited task", Justification = "", Scope = "namespaceanddescendants", Target = "~N:CmsEngine.Ui")] -[assembly: SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "", Scope = "namespaceanddescendants", Target = "~N:CmsEngine.Ui")] -[assembly: SuppressMessage("Globalization", "CA1304:Specify CultureInfo", Justification = "", Scope = "member", Target = "~M:CmsEngine.Ui.Extensions.HtmlHelperExtensions.IsSelected(Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper,System.String,System.String,System.String)~System.String")] diff --git a/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs b/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs deleted file mode 100644 index 2a81f40e..00000000 --- a/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; - -namespace CmsEngine.Ui.Middleware -{ - public class ConfigureFileUploadMiddleware - { - private readonly RequestDelegate _next; - - public ConfigureFileUploadMiddleware(RequestDelegate next) - { - _next = next; - } - - public async Task InvokeAsync(HttpContext context, FileUploadOptions options) - { - string uploadPath = Path.Combine(options.Root, options.Folder); - - if (!Directory.Exists(uploadPath)) - { - Directory.CreateDirectory(uploadPath); - } - - // Call the next delegate/middleware in the pipeline - await _next(context); - } - } - - public class FileUploadOptions - { - public string Root { get; set; } - public string Folder { get; set; } - } -} diff --git a/CmsEngine.Ui/Middleware/MiddlewareExtensions.cs b/CmsEngine.Ui/Middleware/MiddlewareExtensions.cs deleted file mode 100644 index cfc8b289..00000000 --- a/CmsEngine.Ui/Middleware/MiddlewareExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using CmsEngine.Ui.Middleware.SecurityHeaders; -using Microsoft.AspNetCore.Builder; - -namespace CmsEngine.Ui.Middleware -{ - public static class MiddlewareExtensions - { - public static IApplicationBuilder ConfigureFileUpload(this IApplicationBuilder builder, FileUploadOptions options) - { - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - - return builder.UseMiddleware(options); - } - - public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder builder, SecurityHeadersBuilder securityHeadersBuilder) - { - if (builder is null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (securityHeadersBuilder is null) - { - throw new ArgumentNullException(nameof(securityHeadersBuilder)); - } - - return builder.UseMiddleware(securityHeadersBuilder.Build()); - } - } -} diff --git a/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs b/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs deleted file mode 100644 index fc0f2a47..00000000 --- a/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using CmsEngine.Core.Constants; - -namespace CmsEngine.Ui.Middleware.SecurityHeaders -{ - /// - /// Exposes methods to build a policy. - /// - public class SecurityHeadersBuilder - { - private readonly SecurityHeadersPolicy _policy = new SecurityHeadersPolicy(); - - /// - /// The number of seconds in one year - /// - public const int OneYearInSeconds = 60 * 60 * 24 * 365; - - /// - /// Add default headers in accordance with most secure approach - /// - public SecurityHeadersBuilder AddDefaultSecurePolicy() - { - // TODO: Have these settings in a configuration file - - AddFrameOptionsDeny(); - AddXssProtectionBlock(); - AddContentTypeOptionsNoSniff(); - AddStrictTransportSecurityMaxAge(); - RemoveServerHeader(); - - AddCustomHeader("Referrer-Policy", "strict-origin-when-cross-origin"); - AddCustomHeader("Feature-Policy", "geolocation 'none';midi 'none';notifications 'none';push 'none';sync-xhr 'none';" + - "microphone 'none';camera 'none';magnetometer 'none';gyroscope 'none';speaker 'self';" + - "vibrate 'none';fullscreen 'self';payment 'none';"); - AddCustomHeader("Content-Security-Policy", "default-src https: 'unsafe-inline' 'unsafe-eval'; " + - "img-src * 'self' data: https: blob:;" + - "style-src 'self' 'unsafe-inline' github.githubassets.com www.google.com platform.twitter.com cdn.syndication.twimg.com fonts.googleapis.com;" + - "script-src 'self' 'unsafe-inline' 'unsafe-eval' www.gstatic.com gist.github.com *.disqus.com www.googletagmanager.com www.google.com cse.google.com cdn.syndication.twimg.com platform.twitter.com cdn1.developermedia.com cdn2.developermedia.com apis.google.com www.googletagservices.com adservice.google.com securepubads.g.doubleclick.net ajax.aspnetcdn.com *.google-analytics.com"); - - RemoveHeader("X-Powered-By"); - - return this; - } - - /// - /// Add X-Frame-Options DENY to all requests. - /// The page cannot be displayed in a frame, regardless of the site attempting to do so - /// - public SecurityHeadersBuilder AddFrameOptionsDeny() - { - _policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.Deny; - return this; - } - - /// - /// Add X-Frame-Options SAMEORIGIN to all requests. - /// The page can only be displayed in a frame on the same origin as the page itself. - /// - public SecurityHeadersBuilder AddFrameOptionsSameOrigin() - { - _policy.SetHeaders[FrameOptionsConstants.Header] = FrameOptionsConstants.SameOrigin; - return this; - } - - /// - /// Add X-Frame-Options ALLOW-FROM {uri} to all requests, where the uri is provided - /// The page can only be displayed in a frame on the specified origin. - /// - /// The uri of the origin in which the page may be displayed in a frame - public SecurityHeadersBuilder AddFrameOptionsSameOrigin(string uri) - { - _policy.SetHeaders[FrameOptionsConstants.Header] = string.Format(FrameOptionsConstants.AllowFromUri, uri); - return this; - } - - - /// - /// Add X-XSS-Protection 1 to all requests. - /// Enables the XSS Protections - /// - public SecurityHeadersBuilder AddXssProtectionEnabled() - { - _policy.SetHeaders[XssProtectionConstants.Header] = XssProtectionConstants.Enabled; - return this; - } - - /// - /// Add X-XSS-Protection 0 to all requests. - /// Disables the XSS Protections offered by the user-agent. - /// - public SecurityHeadersBuilder AddXssProtectionDisabled() - { - _policy.SetHeaders[XssProtectionConstants.Header] = XssProtectionConstants.Disabled; - return this; - } - - /// - /// Add X-XSS-Protection 1; mode=block to all requests. - /// Enables XSS protections and instructs the user-agent to block the response in the event that script has been inserted from user input, instead of sanitizing. - /// - public SecurityHeadersBuilder AddXssProtectionBlock() - { - _policy.SetHeaders[XssProtectionConstants.Header] = XssProtectionConstants.Block; - return this; - } - - /// - /// Add X-XSS-Protection 1; report=http://site.com/report to all requests. - /// A partially supported directive that tells the user-agent to report potential XSS attacks to a single URL. Data will be POST'd to the report URL in JSON format. - /// - public SecurityHeadersBuilder AddXssProtectionReport(string reportUrl) - { - _policy.SetHeaders[XssProtectionConstants.Header] = - string.Format(XssProtectionConstants.Report, reportUrl); - return this; - } - - /// - /// Add Strict-Transport-Security max-age= to all requests. - /// Tells the user-agent to cache the domain in the STS list for the number of seconds provided. - /// - public SecurityHeadersBuilder AddStrictTransportSecurityMaxAge(int maxAge = OneYearInSeconds) - { - _policy.SetHeaders[StrictTransportSecurityConstants.Header] = - string.Format(StrictTransportSecurityConstants.MaxAge, maxAge); - return this; - } - - /// - /// Add Strict-Transport-Security max-age=; includeSubDomains to all requests. - /// Tells the user-agent to cache the domain in the STS list for the number of seconds provided and include any sub-domains. - /// - public SecurityHeadersBuilder AddStrictTransportSecurityMaxAgeIncludeSubDomains(int maxAge = OneYearInSeconds) - { - _policy.SetHeaders[StrictTransportSecurityConstants.Header] = - string.Format(StrictTransportSecurityConstants.MaxAgeIncludeSubdomains, maxAge); - return this; - } - - /// - /// Add Strict-Transport-Security max-age=0 to all requests. - /// Tells the user-agent to remove, or not cache the host in the STS cache - /// - public SecurityHeadersBuilder AddStrictTransportSecurityNoCache() - { - _policy.SetHeaders[StrictTransportSecurityConstants.Header] = - StrictTransportSecurityConstants.NoCache; - return this; - } - - /// - /// Add X-Content-Type-Options nosniff to all requests. - /// Can be set to protect against MIME type confusion attacks. - /// - public SecurityHeadersBuilder AddContentTypeOptionsNoSniff() - { - _policy.SetHeaders[ContentTypeOptionsConstants.Header] = ContentTypeOptionsConstants.NoSniff; - return this; - } - - /// - /// Removes the Server header from all responses - /// - public SecurityHeadersBuilder RemoveServerHeader() - { - _policy.RemoveHeaders.Add(ServerConstants.Header); - return this; - } - - /// - /// Adds a custom header to all requests - /// - /// The header name - /// The value for the header - /// - public SecurityHeadersBuilder AddCustomHeader(string header, string value) - { - if (string.IsNullOrEmpty(header)) - { - throw new ArgumentNullException(nameof(header)); - } - - _policy.SetHeaders[header] = value; - return this; - } - - /// - /// Remove a header from all requests - /// - /// The to remove - /// - public SecurityHeadersBuilder RemoveHeader(string header) - { - if (string.IsNullOrEmpty(header)) - { - throw new ArgumentNullException(nameof(header)); - } - - _policy.RemoveHeaders.Add(header); - return this; - } - - /// - /// Builds a new using the entries added. - /// - /// The constructed . - public SecurityHeadersPolicy Build() - { - return _policy; - } - } -} diff --git a/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs b/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs deleted file mode 100644 index a774f036..00000000 --- a/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; - -namespace CmsEngine.Ui.Middleware.SecurityHeaders -{ - public class SecurityHeadersPolicy - { - /// - /// A dictionary of Header, Value pairs that should be added to all requests - /// - public IDictionary SetHeaders { get; } = new Dictionary(); - - /// - /// A hashset of Headers that should be removed from all requests - /// - public ISet RemoveHeaders { get; } = new HashSet(); - } -} diff --git a/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs b/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs deleted file mode 100644 index 56e44b13..00000000 --- a/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Threading.Tasks; -using CmsEngine.Ui.Middleware.SecurityHeaders; -using Microsoft.AspNetCore.Http; - -namespace CmsEngine.Ui.Middleware -{ - /// - /// An ASP.NET middleware for adding security headers. - /// - public class SecurityHeadersMiddleware - { - private readonly RequestDelegate next; - private readonly SecurityHeadersPolicy policy; - - /// - /// Instantiates a new . - /// - /// The next middleware in the pipeline. - /// An instance of the which can be applied. - public SecurityHeadersMiddleware(RequestDelegate next, SecurityHeadersPolicy policy) - { - if (next == null) - { - throw new ArgumentNullException(nameof(next)); - } - - if (next == null) - { - throw new ArgumentNullException(nameof(policy)); - } - - this.next = next; - this.policy = policy; - } - - public async Task Invoke(HttpContext context) - { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - var response = context.Response; - - if (response == null) - { - throw new ArgumentNullException(nameof(response)); - } - - var headers = response.Headers; - - foreach (var headerValuePair in policy.SetHeaders) - { - headers[headerValuePair.Key] = headerValuePair.Value; - } - - foreach (var header in policy.RemoveHeaders) - { - headers.Remove(header); - } - - await next(context); - } - } -} diff --git a/CmsEngine.Ui/Program.cs b/CmsEngine.Ui/Program.cs deleted file mode 100644 index fa791f24..00000000 --- a/CmsEngine.Ui/Program.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Serilog; - -namespace CmsEngine.Ui -{ - public class Program - { - public static IConfiguration Configuration - { - get - { - string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"); - - return new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json") - .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true) - .AddJsonFile("emailsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"emailsettings.{environment}.json", optional: true, reloadOnChange: true) - .AddJsonFile("certificate.json", optional: true, reloadOnChange: true) - .AddJsonFile($"certificate.{environment}.json", optional: true, reloadOnChange: true) - .AddEnvironmentVariables() - .Build(); - } - } - - public static void Main(string[] args) - { - Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(Configuration) - .Enrich.FromLogContext().CreateLogger(); - try - { - Log.Debug("Starting host"); - CreateHostBuilder(args).Build().Run(); - } - catch (Exception ex) - { - Log.Fatal(ex, "Host terminated unexpectedly"); - throw; - } - finally - { - Log.CloseAndFlush(); - } - } - - public static IHostBuilder CreateHostBuilder(string[] args) - { - string certificateName = Configuration.GetSection("certificateSettings:fileName").Value; - string certificatePassword = Configuration.GetSection("certificateSettings:password").Value; - - return Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") - { - Log.Debug("Dev environment: Using Kestrel with port 5001"); - webBuilder.ConfigureKestrel(options => - { - options.AddServerHeader = false; - options.Listen(IPAddress.Loopback, 5001, listenOptions => - { - listenOptions.UseHttps(new X509Certificate2(certificateName, certificatePassword)); - }); - }); - } - - webBuilder.UseStartup() - .UseSerilog() - .UseConfiguration(Configuration); // This may affect the secrets. To be studied. - }); - } - } -} diff --git a/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs b/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs deleted file mode 100644 index 811aaee9..00000000 --- a/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Linq; -using Microsoft.AspNetCore.Rewrite; -using Microsoft.Net.Http.Headers; - -namespace CmsEngine.Ui.RewriteRules -{ - public class RedirectLowerCaseRule : IRule - { - private readonly int _statusCode; - public RedirectLowerCaseRule(int statusCode) - { - _statusCode = statusCode; - } - - public void ApplyRule(RewriteContext context) - { - var request = context.HttpContext.Request; - var path = context.HttpContext.Request.Path; - var host = context.HttpContext.Request.Host; - - if ((request.Method == "GET") && ((path.HasValue && path.Value.Any(char.IsUpper)) || (host.HasValue && host.Value.Any(char.IsUpper)))) - { - var response = context.HttpContext.Response; - response.StatusCode = _statusCode; - response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString; - context.Result = RuleResult.EndResponse; - } - else - { - context.Result = RuleResult.ContinueRules; - } - } - } -} diff --git a/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs b/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs deleted file mode 100644 index 1b4903dc..00000000 --- a/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using CmsEngine.Core.Constants; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; -using Microsoft.AspNetCore.Rewrite; -using Microsoft.Net.Http.Headers; - -namespace CmsEngine.Ui.RewriteRules -{ - public class RedirectToNonWwwRule : IRule - { - private readonly int _statusCode; - - public RedirectToNonWwwRule(int statusCode) - { - _statusCode = statusCode; - } - - public virtual void ApplyRule(RewriteContext context) - { - var httpRequest = context.HttpContext.Request; - if (httpRequest.Host.Host.Equals(CmsEngineConstants.Localhost, StringComparison.OrdinalIgnoreCase)) - { - context.Result = RuleResult.ContinueRules; - return; - } - - if (!httpRequest.Host.Value.StartsWith(CmsEngineConstants.WwwDot, StringComparison.OrdinalIgnoreCase)) - { - context.Result = RuleResult.ContinueRules; - return; - } - - var wwwHost = new HostString(httpRequest.Host.Value.Replace(CmsEngineConstants.WwwDot, string.Empty)); - var newUrl = UriHelper.BuildAbsolute(httpRequest.Scheme, wwwHost, httpRequest.PathBase, httpRequest.Path, httpRequest.QueryString); - var httpResponse = context.HttpContext.Response; - httpResponse.StatusCode = _statusCode; - httpResponse.Headers[HeaderNames.Location] = newUrl; - context.Result = RuleResult.EndResponse; - } - } -} diff --git a/CmsEngine.Ui/Startup.cs b/CmsEngine.Ui/Startup.cs deleted file mode 100644 index 9c00bfb5..00000000 --- a/CmsEngine.Ui/Startup.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.IO; -using CmsEngine.Application.Helpers.Email; -using CmsEngine.Application.Services; -using CmsEngine.Data; -using CmsEngine.Data.Entities; -using CmsEngine.Data.Repositories; -using CmsEngine.Ui.Middleware; -using CmsEngine.Ui.Middleware.SecurityHeaders; -using CmsEngine.Ui.RewriteRules; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Microsoft.AspNetCore.Rewrite; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Hosting; - -namespace CmsEngine.Ui -{ - public class Startup - { - public Startup(IConfiguration configuration, IWebHostEnvironment environment) - { - Configuration = configuration; - Environment = environment; - } - - public IConfiguration Configuration { get; } - public IWebHostEnvironment Environment { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddDatabaseDeveloperPageExceptionFilter(); - - services.Configure(options => - { - // This lambda determines whether user consent for non-essential cookies is needed for a given request. - options.CheckConsentNeeded = context => true; - options.MinimumSameSitePolicy = SameSiteMode.None; - }); - - services.Configure(Configuration.GetSection("EmailSettings")); - - // Add CmsEngineContext - services.AddDbContext(options => options.EnableSensitiveDataLogging(Environment.IsDevelopment()) - .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), - o => o.MigrationsAssembly("CmsEngine.Data") - .UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))); - - services.AddIdentity() - .AddEntityFrameworkStores() - .AddDefaultTokenProviders(); - - // Add HttpContextAccessor as .NET Core doesn't have HttpContext.Current anymore - services.TryAddSingleton(); - - // Add Repositories - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Add services - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - services.AddScoped(); - - // Add Unit of Work - services.AddScoped(); - - services.AddSingleton(); - - services.AddControllersWithViews(); - services.AddRazorPages(); - - services.ConfigureApplicationCookie(options => - { - options.LoginPath = $"/Identity/Account/Login"; - options.LogoutPath = $"/Identity/Account/Logout"; - options.AccessDeniedPath = $"/Identity/Account/AccessDenied"; - }); - - if (!Environment.IsDevelopment()) - { - services.AddHttpsRedirection(options => - { - options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect; - options.HttpsPort = 443; - }); - } - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseMigrationsEndPoint(); - } - else - { - app.UseStatusCodePagesWithReExecute("/error", "?code={0}"); - // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. - app.UseHsts(); - } - - const int http301 = StatusCodes.Status301MovedPermanently; - - // Added compatibility with the old davidsonsousa.net - var rewriteOptions = new RewriteOptions().Add(new RedirectToNonWwwRule(http301)) - .Add(new RedirectLowerCaseRule(http301)) - .AddRedirect("^en/(.*)", "blog/$1", http301) - .AddRedirect("^pt/(.*)", "blog/$1", http301) - .AddRedirect("^image/articles/(.*)", "image/post/$1", http301) - .AddRedirect("^image/pages/(.*)", "image/page/$1", http301) - .AddRedirect("^file/articles/(.*)", "file/post/$1", http301) - .AddRedirect("^file/pages/(.*)", "file/page/$1", http301); - app.UseRewriter(rewriteOptions); - app.UseHttpsRedirection(); - - // wwwroot - app.UseStaticFiles(); - - - // TODO: Fix this - //// Uploaded files - //app.ConfigureFileUpload(new FileUploadOptions - //{ - // Root = env.WebRootPath, - // Folder = "UploadedFiles" - //}); - - app.UseStaticFiles(new StaticFileOptions - { - FileProvider = new PhysicalFileProvider(Path.Combine(env.WebRootPath, "UploadedFiles")), - RequestPath = "/image" - }); - - app.UseStaticFiles(new StaticFileOptions - { - FileProvider = new PhysicalFileProvider(Path.Combine(env.WebRootPath, "UploadedFiles")), - RequestPath = "/file" - }); - - app.UseSecurityHeaders(new SecurityHeadersBuilder().AddDefaultSecurePolicy()); - - app.UseCookiePolicy(); - - app.UseRouting(); - - app.UseAuthentication(); - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "areaRoute", - pattern: "{area:exists}/{controller=Home}/{action=Index}/{vanityId?}"); - - endpoints.MapControllerRoute( - name: "blog", - pattern: "blog/{action}/{slug?}", - defaults: new { controller = "Blog", action = "Index" }); - - endpoints.MapControllerRoute( - name: "main", - pattern: "", - defaults: new { controller = "Home", action = "Index" }); - - endpoints.MapControllerRoute( - name: "sitemap", - pattern: "sitemap", - defaults: new { controller = "Home", action = "Sitemap" }); - - endpoints.MapControllerRoute( - name: "archive", - pattern: "archive", - defaults: new { controller = "Home", action = "Archive" }); - - endpoints.MapControllerRoute( - name: "contact", - pattern: "contact", - defaults: new { controller = "Home", action = "Contact" }); - - endpoints.MapControllerRoute( - name: "error", - pattern: "error", - defaults: new { controller = "Error", action = "Index" }); - - endpoints.MapControllerRoute( - name: "page", - pattern: "{slug}", - defaults: new { controller = "Home", action = "Page" }); - - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - endpoints.MapRazorPages(); - }); - } - } -} diff --git a/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs b/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs deleted file mode 100644 index 3bb2b10c..00000000 --- a/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using CmsEngine.Application.EditModels; -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace CmsEngine.Ui.TagHelpers -{ - public class CheckboxListTagHelper : TagHelper - { - /// - /// Checkbox name, used to group all checkboxes in the list - /// - public string Name { get; set; } - - /// - /// Class to be assigned to the outer
container - ///
- public string OuterContainerClass { get; set; } - - /// - /// Class to be assigned to the inner
container - ///
- public string InnerContainerClass { get; set; } - - /// - /// Class to be assigned to the - public string LabelClass { get; set; } - - /// - /// Class to be assigned to the - /// - public string InputClass { get; set; } - - /// - /// Items to appear in the checkbox list - /// - public IEnumerable Items { get; set; } - - public override void Process(TagHelperContext context, TagHelperOutput output) - { - if (Items == null) - { - return; - } - - output.TagName = "div"; - output.Attributes.SetAttribute("class", OuterContainerClass); - - InnerContainerClass = !string.IsNullOrWhiteSpace(InnerContainerClass) ? $" class=\"{InnerContainerClass}\"" : ""; - LabelClass = !string.IsNullOrWhiteSpace(LabelClass) ? $" class=\"{LabelClass}\"" : ""; - InputClass = !string.IsNullOrWhiteSpace(InputClass) ? $" class=\"{InputClass}\"" : ""; - - string isChecked; - string isEnabled; - var sb = new StringBuilder(); - - foreach (var item in Items) - { - isChecked = item.Selected ? " checked" : ""; - isEnabled = item.Enabled ? "" : " disabled"; - sb.Append("'); - sb.Append("'); - sb.Append("').Append(item.Label); - sb.Append(""); - } - - output.Content.SetHtmlContent(sb.ToString()); - output.TagMode = TagMode.StartTagAndEndTag; - } - } -} diff --git a/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs b/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs deleted file mode 100644 index 19e5e549..00000000 --- a/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Linq; -using System.Text.Encodings.Web; -using CmsEngine.Core; -using CmsEngine.Core.Extensions; -using Microsoft.AspNetCore.Razor.TagHelpers; - -namespace CmsEngine.Ui.TagHelpers -{ - public class GravatarTagHelper : TagHelper - { - /// - /// E-mail address registered on gravatar.com - /// - public string EmailAddress { get; set; } - - /// - /// Image size (default is 80) - /// - public int ImageSize { get; set; } = 80; - - /// - /// Default image loaded from gravatar.com (default is 0) - /// - public DefaultImage DefaultImage { get; set; } = DefaultImage.Default; - - /// - /// Default image url loaded from gravatar.com (default is "") - /// - public string DefaultImageUrl { get; set; } = ""; - public bool ForceDefaultImage { get; set; } - - /// - /// Image rating (default is G) - /// - public Rating Rating { get; set; } = Rating.G; - public bool ForceSecureRequest { get; set; } - public string AdditionalCssClasses { get; set; } - - public override void Process(TagHelperContext context, TagHelperOutput output) - { - if (output is null) - { - throw new ArgumentNullException(nameof(output)); - } - - output.TagName = "img"; - string email = string.IsNullOrWhiteSpace(EmailAddress) ? string.Empty : EmailAddress.ToLower(); - - output.Attributes.Add("src", - string.Format("{0}://{1}.gravatar.com/avatar/{2}?s={3}{4}{5}{6}", - "https", - "s", - GravatarUtilities.GetMd5Hash(email), - ImageSize, - "&d=" + (!string.IsNullOrEmpty(DefaultImageUrl) ? HtmlEncoder.Default.Encode(DefaultImageUrl) : DefaultImage.GetDescription()), - ForceDefaultImage ? "&f=y" : "", - "&r=" + Rating.GetDescription() - ) - ); - - if (!string.IsNullOrWhiteSpace(AdditionalCssClasses)) - { - if (output.Attributes.Any(x => string.Equals(x.Name, "class", StringComparison.OrdinalIgnoreCase))) - { - AdditionalCssClasses = output.Attributes.First(x => string.Equals(x.Name, "class", StringComparison.OrdinalIgnoreCase)).Value + " " + AdditionalCssClasses; - output.Attributes.Remove(output.Attributes.First(x => string.Equals(x.Name, "class", StringComparison.OrdinalIgnoreCase))); - } - - // Add the additional CSS classes - output.Attributes.Add("class", AdditionalCssClasses); - } - - base.Process(context, output); - } - } -} diff --git a/CmsEngine.Ui/certificate.json b/CmsEngine.Ui/certificate.json deleted file mode 100644 index 21c3a29f..00000000 --- a/CmsEngine.Ui/certificate.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - // A self-signed certificate is used for development - // Check this link for guidance: https://www.humankode.com/asp-net-core/develop-locally-with-https-self-signed-certificates-and-asp-net-core - "certificateSettings": { - "fileName": "", - "password": "" - } -} diff --git a/CmsEngine.sln b/CmsEngine.sln index 3e775ae1..cc197e0e 100644 --- a/CmsEngine.sln +++ b/CmsEngine.sln @@ -1,9 +1,9 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28803.156 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32210.238 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution files", "{456BB7BF-28D5-432E-A21E-F51EF27F7B27}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution files", "{5C4438C1-7278-44BF-8473-0F1655DA512B}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore @@ -11,13 +11,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution README.md = README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmsEngine.Data", "CmsEngine.Data\CmsEngine.Data.csproj", "{A11472A7-CE73-4E52-8C03-B2FB4A2BFD87}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CmsEngine.Core", "src\CmsEngine.Core\CmsEngine.Core.csproj", "{0DCFFE7F-6BF0-40F5-B8ED-B0A213B415C4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmsEngine.Core", "CmsEngine.Core\CmsEngine.Core.csproj", "{37A9090A-6417-465D-B2E0-159569BD20B1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CmsEngine.Data", "src\CmsEngine.Data\CmsEngine.Data.csproj", "{9D472F52-7D5D-4956-9DE2-DB5546262DAB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmsEngine.Application", "CmsEngine.Application\CmsEngine.Application.csproj", "{8D7732E4-AD83-4934-A29E-69CE21AC88E3}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CmsEngine.Application", "src\CmsEngine.Application\CmsEngine.Application.csproj", "{081C9102-37CB-4873-97D2-0A5063FFA4A3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CmsEngine.Ui", "CmsEngine.Ui\CmsEngine.Ui.csproj", "{F84694E9-E3E5-4764-9493-52E263D8AC7F}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CmsEngine.Ui", "src\CmsEngine.Ui\CmsEngine.Ui.csproj", "{AEED8BDE-0C8A-43BC-91B8-77B967511921}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,27 +25,27 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {A11472A7-CE73-4E52-8C03-B2FB4A2BFD87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A11472A7-CE73-4E52-8C03-B2FB4A2BFD87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A11472A7-CE73-4E52-8C03-B2FB4A2BFD87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A11472A7-CE73-4E52-8C03-B2FB4A2BFD87}.Release|Any CPU.Build.0 = Release|Any CPU - {37A9090A-6417-465D-B2E0-159569BD20B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37A9090A-6417-465D-B2E0-159569BD20B1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37A9090A-6417-465D-B2E0-159569BD20B1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37A9090A-6417-465D-B2E0-159569BD20B1}.Release|Any CPU.Build.0 = Release|Any CPU - {8D7732E4-AD83-4934-A29E-69CE21AC88E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8D7732E4-AD83-4934-A29E-69CE21AC88E3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8D7732E4-AD83-4934-A29E-69CE21AC88E3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8D7732E4-AD83-4934-A29E-69CE21AC88E3}.Release|Any CPU.Build.0 = Release|Any CPU - {F84694E9-E3E5-4764-9493-52E263D8AC7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F84694E9-E3E5-4764-9493-52E263D8AC7F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F84694E9-E3E5-4764-9493-52E263D8AC7F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F84694E9-E3E5-4764-9493-52E263D8AC7F}.Release|Any CPU.Build.0 = Release|Any CPU + {0DCFFE7F-6BF0-40F5-B8ED-B0A213B415C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DCFFE7F-6BF0-40F5-B8ED-B0A213B415C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DCFFE7F-6BF0-40F5-B8ED-B0A213B415C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DCFFE7F-6BF0-40F5-B8ED-B0A213B415C4}.Release|Any CPU.Build.0 = Release|Any CPU + {9D472F52-7D5D-4956-9DE2-DB5546262DAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D472F52-7D5D-4956-9DE2-DB5546262DAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D472F52-7D5D-4956-9DE2-DB5546262DAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D472F52-7D5D-4956-9DE2-DB5546262DAB}.Release|Any CPU.Build.0 = Release|Any CPU + {081C9102-37CB-4873-97D2-0A5063FFA4A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {081C9102-37CB-4873-97D2-0A5063FFA4A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {081C9102-37CB-4873-97D2-0A5063FFA4A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {081C9102-37CB-4873-97D2-0A5063FFA4A3}.Release|Any CPU.Build.0 = Release|Any CPU + {AEED8BDE-0C8A-43BC-91B8-77B967511921}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEED8BDE-0C8A-43BC-91B8-77B967511921}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEED8BDE-0C8A-43BC-91B8-77B967511921}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEED8BDE-0C8A-43BC-91B8-77B967511921}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9E28B0A3-6610-45BE-BC91-49EDD09DE9DA} + SolutionGuid = {0EB67BE0-B210-4352-8C85-01C9D6CA3E15} EndGlobalSection EndGlobal diff --git a/src/CmsEngine.Application/Attributes/Orderable.cs b/src/CmsEngine.Application/Attributes/Orderable.cs new file mode 100644 index 00000000..aae05c9f --- /dev/null +++ b/src/CmsEngine.Application/Attributes/Orderable.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Attributes; + +/// +/// Enables the property to be orderable in the Search functionality +/// +[AttributeUsage(AttributeTargets.Property)] +public sealed class Orderable : Attribute +{ + +} diff --git a/src/CmsEngine.Application/Attributes/Searchable.cs b/src/CmsEngine.Application/Attributes/Searchable.cs new file mode 100644 index 00000000..4a19d91e --- /dev/null +++ b/src/CmsEngine.Application/Attributes/Searchable.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Attributes; + +/// +/// Enables the property to be searchable in the Search functionality +/// +[AttributeUsage(AttributeTargets.Property)] +public sealed class Searchable : Attribute +{ + +} diff --git a/src/CmsEngine.Application/Attributes/ShowOnDataTable.cs b/src/CmsEngine.Application/Attributes/ShowOnDataTable.cs new file mode 100644 index 00000000..aad938f3 --- /dev/null +++ b/src/CmsEngine.Application/Attributes/ShowOnDataTable.cs @@ -0,0 +1,15 @@ +namespace CmsEngine.Application.Attributes; + +/// +/// Enables the properto to be visible in the DataTable +/// +[AttributeUsage(AttributeTargets.Property)] +public sealed class ShowOnDataTable : Attribute +{ + public int Order { get; } + + public ShowOnDataTable(int order) + { + Order = order; + } +} diff --git a/src/CmsEngine.Application/CmsEngine.Application.csproj b/src/CmsEngine.Application/CmsEngine.Application.csproj new file mode 100644 index 00000000..14686bc9 --- /dev/null +++ b/src/CmsEngine.Application/CmsEngine.Application.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + + diff --git a/src/CmsEngine.Application/Extensions/EnumerableExtensions.cs b/src/CmsEngine.Application/Extensions/EnumerableExtensions.cs new file mode 100644 index 00000000..4f3d6afa --- /dev/null +++ b/src/CmsEngine.Application/Extensions/EnumerableExtensions.cs @@ -0,0 +1,43 @@ +namespace CmsEngine.Application.Extensions; + +public static class EnumerableExtensions +{ + public static Expression> GetSearchExpression(this IEnumerable element, string searchValue, IEnumerable properties) + { + var expressionFilter = new List(); + + foreach (var property in properties) + { + expressionFilter.Add(new ExpressionFilter + { + PropertyName = property.Name, + Operation = Operation.Contains, + Value = searchValue + }); + } + + return ExpressionBuilder.GetExpression(expressionFilter, LogicalOperator.Or); + } + + public static IEnumerable PopulateSelectList(this IEnumerable items, IEnumerable selectedItems = null) where T : BaseViewModel + { + return items.Select(x => new SelectListItem + { + Text = x.GetType().GetProperty("Name").GetValue(x).ToString(), + Value = x.VanityId.ToString(), + Disabled = false, + Selected = selectedItems?.Contains(x.VanityId.ToString()) ?? false + }).OrderBy(o => o.Text); + } + + public static IEnumerable PopulateCheckboxList(this IEnumerable items, IEnumerable selectedItems = null) where T : BaseViewModel + { + return items.Select(x => new CheckboxEditModel + { + Label = x.GetType().GetProperty("Name").GetValue(x).ToString(), + Value = x.VanityId.ToString(), + Enabled = true, + Selected = selectedItems?.Contains(x.VanityId.ToString()) ?? false + }).OrderBy(o => o.Label); + } +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs b/src/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs new file mode 100644 index 00000000..145bafb4 --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/ApplicationUserExtension.cs @@ -0,0 +1,119 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class ApplicationUserExtensions +{ + /// + /// Maps ApplicationUser model into a UserEditModel + /// + /// + /// + public static UserEditModel MapToEditModel(this ApplicationUser item) + { + return new UserEditModel + { + VanityId = Guid.Parse(item.Id), + Name = item.Name, + Surname = item.Surname, + Email = item.Email, + UserName = item.UserName + }; + } + + /// + /// Maps ApplicationUser model into a UserViewModel + /// + /// + /// + public static UserViewModel MapToViewModel(this ApplicationUser item) + { + return new UserViewModel + { + VanityId = Guid.Parse(item.Id), + Name = item.Name, + Surname = item.Surname, + Email = item.Email, + UserName = item.UserName + }; + } + + /// + /// Maps Name, Surname, Email and UserName + /// + /// + /// + public static IEnumerable MapToViewModelSimple(this IEnumerable users) + { + var viewModels = new List(); + + foreach (var item in users) + { + viewModels.Add(new UserViewModel + { + Name = item.Name, + Surname = item.Surname, + Email = item.Email, + UserName = item.UserName + }); + } + + return viewModels; + } + + /// + /// Maps a UserEditModel into a ApplicationUser + /// + /// + /// + public static ApplicationUser MapToModel(this UserEditModel item) + { + return new ApplicationUser + { + Id = item.VanityId.ToString(), + Name = item.Name, + Surname = item.Surname, + Email = item.Email, + UserName = item.UserName + }; + } + + /// + /// Maps a UserEditModel into a specific ApplicationUser + /// + /// + /// + /// + public static ApplicationUser MapToModel(this UserEditModel item, ApplicationUser user) + { + user.Id = item.VanityId.ToString(); + user.Name = item.Name; + user.Surname = item.Surname; + user.Email = item.Email; + user.UserName = item.UserName; + + return user; + } + + ///// + ///// Maps an IEnumerable into an IEnumerable + ///// + ///// + ///// + //public static IEnumerable MapToTableViewModel(this IEnumerable tags) + //{ + // var tableViewModel = new List(); + + // foreach (var item in tags) + // { + // tableViewModel.Add(new ApplicationUserTableViewModel + // { + // VanityId = Guid.Parse(item.Id), + // Name = item.Name, + // Surname = item.Surname, + // Email = item.Email, + // UserName = item.UserName + // }); + // } + + // return tableViewModel; + //} +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs b/src/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs new file mode 100644 index 00000000..a99e5386 --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/CategoryExtensions.cs @@ -0,0 +1,179 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class CategoryExtensions +{ + /// + /// Maps Category model into a CategoryEditModel + /// + /// + /// + public static CategoryEditModel MapToEditModel(this Category item) + { + return new CategoryEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Description = item.Description, + Slug = item.Slug + }; + } + + /// + /// Maps a CategoryEditModel into a Category + /// + /// + /// + public static Category MapToModel(this CategoryEditModel item) + { + return new Category + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Description = item.Description, + Slug = item.Slug, + }; + } + + /// + /// Maps a CategoryEditModel into a specific Category + /// + /// + /// + /// + public static Category MapToModel(this CategoryEditModel item, Category category) + { + category.Id = item.Id; + category.VanityId = item.VanityId; + category.Name = item.Name; + category.Description = item.Description; + category.Slug = item.Slug; + + return category; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToTableViewModel(this IEnumerable categories) + { + var tableViewModel = new List(); + + foreach (var item in categories) + { + tableViewModel.Add(new CategoryTableViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Description = item.Description, + Slug = item.Slug + }); + } + + return tableViewModel; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToViewModel(this IEnumerable categories) + { + var viewModel = new List(); + + foreach (var item in categories) + { + viewModel.Add(new CategoryViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Description = item.Description, + Slug = item.Slug, + Posts = item.PostCategories.Select(x => x.Post).MapToViewModel() + }); + } + + return viewModel; + } + + /// + /// Maps VanityId, Name and Slug + /// + /// + /// + public static IEnumerable MapToViewModelSimple(this IEnumerable categories) + { + var viewModel = new List(); + + foreach (var item in categories) + { + viewModel.Add(new CategoryViewModel + { + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug + }); + } + + return viewModel; + } + + /// + /// Maps VanityId, Name and Slug with post count + /// + /// + /// + public static IEnumerable MapToViewModelWithPostCount(this IEnumerable categories) + { + var viewModel = new List(); + + foreach (var item in categories) + { + viewModel.Add(new CategoryViewModel + { + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug, + PostCount = item.PostCount + }); + } + + return viewModel; + } + + /// + /// Maps VanityId, Name and Slug with Posts + /// + /// + /// + public static IEnumerable MapToViewModelWithPost(this IEnumerable categories) + { + var viewModel = new List(); + + foreach (var item in categories) + { + viewModel.Add(new CategoryViewModel + { + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug, + Posts = item.Posts.Select(p => new PostViewModel + { + VanityId = p.VanityId, + Title = p.Title, + Description = p.Description, + Slug = p.Slug, + PublishedOn = p.PublishedOn.ToShortDateString() + }) + }); + } + + return viewModel; + } +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs b/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs new file mode 100644 index 00000000..b6c61596 --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs @@ -0,0 +1,42 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class ContactFormExtension +{ + /// + /// Maps a ContactFormEditModel into a ContactForm + /// + /// + /// + public static Email MapToModel(this ContactForm item) + { + return new Email + { + From = item.From, + Subject = item.Subject, + Message = item.Message + }; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToViewModel(this IEnumerable emails) + { + var viewModel = new List(); + + foreach (var item in emails) + { + viewModel.Add(new ContactForm + { + From = item.From, + Subject = item.Subject, + Message = item.Message + }); + } + + return viewModel; + } + +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs b/src/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs new file mode 100644 index 00000000..12f06a77 --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/PageExtensions.cs @@ -0,0 +1,170 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class PageExtensions +{ + /// + /// Maps Page model into a PageEditModel + /// + /// + /// + public static PageEditModel MapToEditModel(this Page item) + { + return new PageEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn, + Status = item.Status + }; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToEditModel(this IEnumerable pages) + { + var editModels = new List(); + + foreach (var item in pages) + { + editModels.Add(new PageEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn, + Status = item.Status + }); + } + + return editModels; + } + + /// + /// Maps a PageEditModel into a Page + /// + /// + /// + public static Page MapToModel(this PageEditModel item) + { + return new Page + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn, + Status = item.Status + }; + } + + /// + /// Maps a PageEditModel into a specific Page + /// + /// + /// + /// + public static Page MapToModel(this PageEditModel item, Page page) + { + page.Id = item.Id; + page.VanityId = item.VanityId; + page.Title = item.Title; + page.Slug = item.Slug; + page.Description = item.Description; + page.DocumentContent = item.DocumentContent; + page.HeaderImage = item.HeaderImage; + page.PublishedOn = item.PublishedOn; + page.Status = item.Status; + + return page; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToTableViewModel(this IEnumerable pages) + { + var tableViewModel = new List(); + + foreach (var item in pages) + { + tableViewModel.Add(new PageTableViewModel + { + VanityId = item.VanityId, + Title = item.Title, + Description = item.Description, + Slug = item.Slug, + PublishedOn = item.PublishedOn.ToString(), + Status = item.Status, + Author = item.ApplicationUsers.MapToViewModelSimple().Single() + }); + } + + return tableViewModel; + } + + /// + /// Maps Page model into a PageViewModel + /// + /// + /// + public static PageViewModel MapToViewModel(this Page item) + { + return new PageViewModel + { + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Author = item.ApplicationUsers.MapToViewModelSimple().Single() + }; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToViewModel(this IEnumerable pages) + { + var editModels = new List(); + + foreach (var item in pages) + { + editModels.Add(new PageViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Status = item.Status + }); + } + + return editModels; + } + +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs b/src/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs new file mode 100644 index 00000000..d79fadd9 --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/PostExtensions.cs @@ -0,0 +1,285 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class PostExtensions +{ + /// + /// Maps Post model into a PostEditModel + /// + /// + /// + public static PostEditModel MapToEditModel(this Post item) + { + return new PostEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn, + Status = item.Status, + SelectedCategories = item.PostCategories.Select(x => x.Category.VanityId.ToString()), + SelectedTags = item.PostTags.Select(x => x.Tag.VanityId.ToString()) + }; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToEditModel(this IEnumerable posts) + { + var editModels = new List(); + + foreach (var item in posts) + { + editModels.Add(new PostEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn, + Status = item.Status + }); + } + + return editModels; + } + + /// + /// Maps a PostEditModel into a Post + /// + /// + /// + public static Post MapToModel(this PostEditModel item) + { + return new Post + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn, + Status = item.Status + }; + } + + /// + /// Maps a PostEditModel into a specific Post + /// + /// + /// + /// + public static Post MapToModel(this PostEditModel item, Post post) + { + post.Id = item.Id; + post.VanityId = item.VanityId; + post.Title = item.Title; + post.Slug = item.Slug; + post.Description = item.Description; + post.DocumentContent = item.DocumentContent; + post.HeaderImage = item.HeaderImage; + post.PublishedOn = item.PublishedOn; + post.Status = item.Status; + + return post; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToTableViewModel(this IEnumerable posts) + { + var tableViewModel = new List(); + + foreach (var item in posts) + { + tableViewModel.Add(new PostTableViewModel + { + VanityId = item.VanityId, + Title = item.Title, + Description = item.Description, + Slug = item.Slug, + PublishedOn = item.PublishedOn.ToString(), + Status = item.Status, + Author = item.ApplicationUsers.MapToViewModelSimple().Single() + }); + } + + return tableViewModel; + } + + /// + /// Maps Post model into a PostViewModel + /// + /// + /// + public static PostViewModel MapToViewModel(this Post item) + { + return new PostViewModel + { + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Categories = item.Categories.MapToViewModelSimple(), + Author = item.ApplicationUsers.MapToViewModelSimple().Single() + }; + } + + /// + /// Maps Post model into a PostViewModel + /// + /// + /// + public static IEnumerable MapToViewModel(this IEnumerable posts) + { + var viewModels = new List(); + + foreach (var item in posts) + { + viewModels.Add(new PostViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Status = item.Status + }); + } + + return viewModels; + } + + /// + /// Maps Post model into a PostViewModel with Categories + /// + /// + /// + public static IEnumerable MapToViewModelWithCategories(this IEnumerable posts) + { + var viewModels = new List(); + + foreach (var item in posts) + { + viewModels.Add(new PostViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Status = item.Status, + Categories = item.PostCategories.Select(x => x.Category).MapToViewModel() + }); + } + + return viewModels; + } + + /// + /// Maps Post model into a PostViewModel with Tags + /// + /// + /// + public static IEnumerable MapToViewModelWithTags(this IEnumerable posts) + { + var viewModels = new List(); + + foreach (var item in posts) + { + viewModels.Add(new PostViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + DocumentContent = item.DocumentContent, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Status = item.Status, + Tags = item.PostTags.Select(x => x.Tag).MapToViewModel() + }); + } + + return viewModels; + } + + /// + /// Maps limited information for Partial Views + /// + /// + /// + public static IEnumerable MapToViewModelForPartialView(this IEnumerable posts) + { + var viewModels = new List(); + + foreach (var item in posts) + { + viewModels.Add(new PostViewModel + { + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Categories = item.Categories.MapToViewModelSimple(), + Author = item.ApplicationUsers.MapToViewModelSimple().Single() + }); + } + + return viewModels; + } + + /// + /// Maps limited information for Partial Views for Tags + /// + /// + /// + public static IEnumerable MapToViewModelForPartialViewForTags(this IEnumerable posts) + { + var viewModels = new List(); + + foreach (var item in posts) + { + viewModels.Add(new PostViewModel + { + VanityId = item.VanityId, + Title = item.Title, + Slug = item.Slug, + Description = item.Description, + HeaderImage = item.HeaderImage, + PublishedOn = item.PublishedOn.ToShortDateString(), + Categories = item.Categories.MapToViewModelSimple(), + Tags = item.Tags.MapToViewModelSimple(), + Author = item.ApplicationUsers.MapToViewModelSimple().Single() + }); + } + + return viewModels; + } +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs b/src/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs new file mode 100644 index 00000000..67a557cb --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/TagExtensions.cs @@ -0,0 +1,120 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class TagExtensions +{ + /// + /// Maps Tag model into a TagEditModel + /// + /// + /// + public static TagEditModel MapToEditModel(this Tag item) + { + return new TagEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug + }; + } + + /// + /// Maps a TagEditModel into a Tag + /// + /// + /// + public static Tag MapToModel(this TagEditModel item) + { + return new Tag + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug, + }; + } + + /// + /// Maps a TagEditModel into a specific Tag + /// + /// + /// + /// + public static Tag MapToModel(this TagEditModel item, Tag tag) + { + tag.Id = item.Id; + tag.VanityId = item.VanityId; + tag.Name = item.Name; + tag.Slug = item.Slug; + + return tag; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToTableViewModel(this IEnumerable tags) + { + var tableViewModel = new List(); + + foreach (var item in tags) + { + tableViewModel.Add(new TagTableViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug + }); + } + + return tableViewModel; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToViewModel(this IEnumerable tags) + { + var viewModel = new List(); + + foreach (var item in tags) + { + viewModel.Add(new TagViewModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug + }); + } + + return viewModel; + } + + /// + /// Maps VanityId, Name and Slug + /// + /// + /// + public static IEnumerable MapToViewModelSimple(this IEnumerable tags) + { + var viewModel = new List(); + + foreach (var item in tags) + { + viewModel.Add(new TagViewModel + { + VanityId = item.VanityId, + Name = item.Name, + Slug = item.Slug + }); + } + + return viewModel; + } +} diff --git a/src/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs b/src/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs new file mode 100644 index 00000000..8a768341 --- /dev/null +++ b/src/CmsEngine.Application/Extensions/Mapper/WebsiteExtensions.cs @@ -0,0 +1,140 @@ +namespace CmsEngine.Application.Extensions.Mapper; + +public static class WebsiteExtensions +{ + /// + /// Maps Website model into a WebsiteEditModel + /// + /// + /// + public static WebsiteEditModel MapToEditModel(this Website item) + { + return new WebsiteEditModel + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Description = item.Description, + Tagline = item.Tagline, + HeaderImage = item.HeaderImage, + Culture = item.Culture, + UrlFormat = item.UrlFormat, + DateFormat = item.DateFormat, + SiteUrl = item.SiteUrl, + ArticleLimit = item.ArticleLimit, + Address = item.Address, + Phone = item.Phone, + Email = item.Email, + FacebookAppId = item.FacebookAppId, + FacebookApiVersion = item.FacebookApiVersion, + DisqusShortName = item.DisqusShortName, + Facebook = item.Facebook, + Twitter = item.Twitter, + Instagram = item.Instagram, + LinkedIn = item.LinkedIn, + GoogleAnalytics = item.GoogleAnalytics, + GoogleRecaptchaSiteKey = item.GoogleRecaptchaSiteKey, + GoogleRecaptchaSecretKey = item.GoogleRecaptchaSecretKey + }; + } + + /// + /// Maps a WebsiteEditModel into a Website + /// + /// + /// + public static Website MapToModel(this WebsiteEditModel item) + { + return new Website + { + Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Description = item.Description, + Tagline = item.Tagline, + HeaderImage = item.HeaderImage, + Culture = item.Culture, + UrlFormat = item.UrlFormat, + DateFormat = item.DateFormat, + SiteUrl = item.SiteUrl, + ArticleLimit = item.ArticleLimit, + Address = item.Address, + Phone = item.Phone, + Email = item.Email, + FacebookAppId = item.FacebookAppId, + FacebookApiVersion = item.FacebookApiVersion, + DisqusShortName = item.DisqusShortName, + Facebook = item.Facebook, + Twitter = item.Twitter, + Instagram = item.Instagram, + LinkedIn = item.LinkedIn, + GoogleAnalytics = item.GoogleAnalytics, + GoogleRecaptchaSiteKey = item.GoogleRecaptchaSiteKey, + GoogleRecaptchaSecretKey = item.GoogleRecaptchaSecretKey + }; + } + + /// + /// Maps a WebsiteEditModel into a specific Website + /// + /// + /// + /// + public static Website MapToModel(this WebsiteEditModel item, Website website) + { + website.Id = item.Id; + website.VanityId = item.VanityId; + website.Name = item.Name; + website.Description = item.Description; + website.Tagline = item.Tagline; + website.HeaderImage = item.HeaderImage; + website.Culture = item.Culture; + website.UrlFormat = item.UrlFormat; + website.DateFormat = item.DateFormat; + website.SiteUrl = item.SiteUrl; + website.ArticleLimit = item.ArticleLimit; + website.Address = item.Address; + website.Phone = item.Phone; + website.Email = item.Email; + website.FacebookAppId = item.FacebookAppId; + website.FacebookApiVersion = item.FacebookApiVersion; + website.DisqusShortName = item.DisqusShortName; + website.Facebook = item.Facebook; + website.Twitter = item.Twitter; + website.Instagram = item.Instagram; + website.LinkedIn = item.LinkedIn; + website.GoogleAnalytics = item.GoogleAnalytics; + website.GoogleRecaptchaSiteKey = item.GoogleRecaptchaSiteKey; + website.GoogleRecaptchaSecretKey = item.GoogleRecaptchaSecretKey; + + return website; + } + + /// + /// Maps an IEnumerable into an IEnumerable + /// + /// + /// + public static IEnumerable MapToTableViewModel(this IEnumerable websites) + { + var tableViewModel = new List(); + + foreach (var item in websites) + { + tableViewModel.Add(new WebsiteTableViewModel + { + //Id = item.Id, + VanityId = item.VanityId, + Name = item.Name, + Tagline = item.Tagline, + Culture = item.Culture, + UrlFormat = item.UrlFormat, + DateFormat = item.DateFormat, + SiteUrl = item.SiteUrl, + GoogleAnalytics = item.GoogleAnalytics + }); + } + + return tableViewModel; + } +} diff --git a/src/CmsEngine.Application/GlobalUsings.cs b/src/CmsEngine.Application/GlobalUsings.cs new file mode 100644 index 00000000..31012368 --- /dev/null +++ b/src/CmsEngine.Application/GlobalUsings.cs @@ -0,0 +1,36 @@ +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using System.Drawing; +global using System.Drawing.Imaging; +global using System.Globalization; +global using System.Linq.Expressions; +global using System.Net; +global using System.Net.Mail; +global using System.Reflection; +global using System.Text; +global using System.Text.Encodings.Web; +global using System.Xml.Linq; +global using CmsEngine.Application.Attributes; +global using CmsEngine.Application.Extensions; +global using CmsEngine.Application.Extensions.Mapper; +global using CmsEngine.Application.Helpers; +global using CmsEngine.Application.Helpers.Email; +global using CmsEngine.Application.Models.EditModels; +global using CmsEngine.Application.Models.ViewModels; +global using CmsEngine.Application.Models.ViewModels.DataTablesViewModels; +global using CmsEngine.Application.Services.Interfaces; +global using CmsEngine.Core.Constants; +global using CmsEngine.Core.Exceptions; +global using CmsEngine.Core.Extensions; +global using CmsEngine.Core.Utils; +global using CmsEngine.Data; +global using CmsEngine.Data.Entities; +global using Microsoft.AspNetCore.Authentication; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Identity; +global using Microsoft.AspNetCore.Mvc.Rendering; +global using Microsoft.Extensions.Caching.Memory; +global using Microsoft.Extensions.Logging; +global using Microsoft.Extensions.Options; +global using Newtonsoft.Json; +global using Newtonsoft.Json.Linq; diff --git a/src/CmsEngine.Application/Helpers/DataTableHelper.cs b/src/CmsEngine.Application/Helpers/DataTableHelper.cs new file mode 100644 index 00000000..8187fefd --- /dev/null +++ b/src/CmsEngine.Application/Helpers/DataTableHelper.cs @@ -0,0 +1,74 @@ +namespace CmsEngine.Application.Helpers; + +public static class DataTableHelper +{ + public static DataTableViewModel BuildDataTable(IEnumerable listItems, int recordsTotal, int recordsFiltered, int draw, int start, int length) + { + var listString = new List>(); + + foreach (var item in listItems.Skip(start).Take(length)) + { + // Get the properties which should appear in the DataTable + var itemProperties = item.GetType() + .GetProperties() + .Where(p => Attribute.IsDefined(p, typeof(ShowOnDataTable))) + .OrderBy(o => o.GetCustomAttributes(false).OfType().First().Order); + + // An empty value must *always* be the first property because of the checkboxes + var listPropertes = new List { string.Empty }; + + // Loop through and add the properties found + foreach (var property in itemProperties) + { + listPropertes.Add(PrepareProperty(item, property)); + } + + // VanityId must *always* be the last property + listPropertes.Add(item.VanityId.ToString()); + + listString.Add(listPropertes); + } + + return new DataTableViewModel + { + Data = listString, + RecordsTotal = recordsTotal, + RecordsFiltered = recordsFiltered, + Draw = draw + }; + } + + private static string PrepareProperty(IViewModel item, PropertyInfo property) + { + GeneralStatus generalStatus; + var value = item.GetType().GetProperty(property.Name).GetValue(item); + + switch (property.PropertyType.Name) + { + case "DocumentStatus": + var documentStatus = value?.ToString() ?? ""; + switch (documentStatus) + { + case "Published": + generalStatus = GeneralStatus.Success; + break; + case "PendingApproval": + generalStatus = GeneralStatus.Warning; + break; + default: + generalStatus = GeneralStatus.Info; + break; + } + + return $"{documentStatus.ToEnum().GetName()}"; + case "UserViewModel": + var author = (UserViewModel)value; + return HtmlEncoder.Default.Encode(author?.FullName ?? ""); + case "Boolean": + generalStatus = (bool)value ? GeneralStatus.Success : GeneralStatus.Danger; + return $"{((bool)value).ToYesNo().ToUpper()}"; + default: + return HtmlEncoder.Default.Encode(value?.ToString() ?? ""); + } + } +} diff --git a/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs b/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs new file mode 100644 index 00000000..d9b67a5e --- /dev/null +++ b/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs @@ -0,0 +1,73 @@ +namespace CmsEngine.Application.Helpers.Email; + +public class CmsEngineEmailSender : ICmsEngineEmailSender +{ + private readonly EmailSettings _emailSettings; + private readonly ILogger _logger; + + public CmsEngineEmailSender(IOptions emailSettings, ILogger logger) + { + _emailSettings = emailSettings.Value; + _logger = logger; + } + + public async Task SendEmailAsync(ContactForm contactForm) + { + await ExecuteAsync(contactForm); + } + + private async Task ExecuteAsync(ContactForm contactForm) + { + _logger.LogDebug("SendEmailAsync(contactForm: {0})", contactForm.ToString()); + + var from = contactForm.From ?? _emailSettings.Username; + var body = $"From: {from}\r\nTo: {contactForm.To}\r\n-----\r\n\r\n{contactForm.Message}"; + + try + { + var message = new MailMessage + { + From = new MailAddress(from), + Subject = $"🌐 CmsEngine - {contactForm.Subject}", + SubjectEncoding = Encoding.UTF8, + IsBodyHtml = false, + Body = body, + BodyEncoding = Encoding.UTF8, + Priority = MailPriority.Normal + }; + + if (!string.IsNullOrWhiteSpace(contactForm.To)) + { + message.To.Add(contactForm.To); + } + + if (!string.IsNullOrWhiteSpace(_emailSettings.CcEmail)) + { + message.CC.Add(_emailSettings.CcEmail); + } + + if (!string.IsNullOrWhiteSpace(_emailSettings.BccEmail)) + { + message.Bcc.Add(_emailSettings.BccEmail); + } + + using (var smtp = new SmtpClient(_emailSettings.Domain, _emailSettings.Port)) + { + smtp.EnableSsl = true; + smtp.DeliveryMethod = SmtpDeliveryMethod.Network; + smtp.UseDefaultCredentials = false; + smtp.Credentials = new NetworkCredential(_emailSettings.Username, _emailSettings.Password); + + _logger.LogDebug("Message {0}", message.ToString()); + await smtp.SendMailAsync(message); + } + + _logger.LogDebug("Email sent from {0} to {1}", message.From, message.To[0]); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error when sending e-mail"); + throw new EmailException("Error when sending e-mail", ex); + } + } +} diff --git a/src/CmsEngine.Application/Helpers/Email/ContactForm.cs b/src/CmsEngine.Application/Helpers/Email/ContactForm.cs new file mode 100644 index 00000000..c93ad684 --- /dev/null +++ b/src/CmsEngine.Application/Helpers/Email/ContactForm.cs @@ -0,0 +1,39 @@ +namespace CmsEngine.Application.Helpers.Email; + +public class ContactForm +{ + [Required] + [DataType(DataType.EmailAddress)] + public string From { get; set; } + [DataType(DataType.EmailAddress)] + public string To { get; set; } + [Required] + [MaxLength(150)] + public string Subject { get; set; } + [Required] + [MaxLength(500)] + public string Message { get; set; } + + public ContactForm() + { + + } + + public ContactForm(string to, string subject, string message) + { + To = to; + Subject = subject; + Message = message; + } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("From", From), + new JProperty("To", To), + new JProperty("Subject", Subject), + new JProperty("Message", Message) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Application/Helpers/Email/EmailSettings.cs b/src/CmsEngine.Application/Helpers/Email/EmailSettings.cs new file mode 100644 index 00000000..96dd8a8c --- /dev/null +++ b/src/CmsEngine.Application/Helpers/Email/EmailSettings.cs @@ -0,0 +1,32 @@ +namespace CmsEngine.Application.Helpers.Email; + +public class EmailSettings +{ + public string Domain { get; set; } + + public int Port { get; set; } + + public string Username { get; set; } + + public string Password { get; set; } + + public string FromEmail { get; set; } + + public string CcEmail { get; set; } + + public string BccEmail { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Domain", Domain), + new JProperty("Port", Port), + new JProperty("Username", Username), + new JProperty("Password", Password), + new JProperty("FromEmail", FromEmail), + new JProperty("CcEmail", CcEmail), + new JProperty("BccEmail", BccEmail) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Application/Helpers/Email/ICmsEngineEmailSender.cs b/src/CmsEngine.Application/Helpers/Email/ICmsEngineEmailSender.cs new file mode 100644 index 00000000..ac905152 --- /dev/null +++ b/src/CmsEngine.Application/Helpers/Email/ICmsEngineEmailSender.cs @@ -0,0 +1,6 @@ +namespace CmsEngine.Application.Helpers.Email; + +public interface ICmsEngineEmailSender +{ + Task SendEmailAsync(ContactForm mailEditModel); +} diff --git a/src/CmsEngine.Application/Helpers/ExpressionBuilder.cs b/src/CmsEngine.Application/Helpers/ExpressionBuilder.cs new file mode 100644 index 00000000..6becfe6a --- /dev/null +++ b/src/CmsEngine.Application/Helpers/ExpressionBuilder.cs @@ -0,0 +1,120 @@ +namespace CmsEngine.Application.Helpers; + +public static class ExpressionBuilder +{ + private static readonly MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); + private static readonly MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }); + private static readonly MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }); + + + public static Expression> GetExpression(IList filters, LogicalOperator logicalOperator) + { + if (filters.Count == 0) + { + return null; + } + + var param = Expression.Parameter(typeof(T), "t"); + var exp = default(Expression); + + while (filters.Count > 0) + { + var f1 = filters[0]; + var nullCheck = new ExpressionFilter + { + PropertyName = f1.PropertyName, + Operation = Operation.NotEqual, + Value = null + }; + + if (exp == null) + { + exp = GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso); + } + else + { + switch (logicalOperator) + { + case LogicalOperator.And: + exp = Expression.And(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); + break; + case LogicalOperator.Or: + exp = Expression.Or(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); + break; + case LogicalOperator.OrElse: + exp = Expression.OrElse(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); + break; + case LogicalOperator.AndAlso: + default: + exp = Expression.AndAlso(exp, GetExpression(param, nullCheck, filters[0], LogicalOperator.AndAlso)); + break; + } + } + + filters.Remove(f1); + } + + return Expression.Lambda>(exp, param); + } + + private static Expression GetExpression(ParameterExpression param, ExpressionFilter filter) + { + var member = Expression.Property(param, filter.PropertyName); + var constant = Expression.Constant(filter.Value); + + switch (filter.Operation) + { + case Operation.Equals: + return Expression.Equal(member, constant); + + case Operation.NotEqual: + return Expression.NotEqual(member, constant); + + case Operation.GreaterThan: + return Expression.GreaterThan(member, constant); + + case Operation.GreaterThanOrEqual: + return Expression.GreaterThanOrEqual(member, constant); + + case Operation.LessThan: + return Expression.LessThan(member, constant); + + case Operation.LessThanOrEqual: + return Expression.LessThanOrEqual(member, constant); + + case Operation.Contains: + return Expression.Call(member, containsMethod, constant); + + case Operation.NotContain: + return Expression.Not(Expression.Call(member, containsMethod, constant)); + + case Operation.StartsWith: + return Expression.Call(member, startsWithMethod, constant); + + case Operation.EndsWith: + return Expression.Call(member, endsWithMethod, constant); + } + + return null; + } + + private static BinaryExpression GetExpression + (ParameterExpression param, ExpressionFilter filter1, ExpressionFilter filter2, LogicalOperator logicalOperator) + { + var bin1 = GetExpression(param, filter1); + var bin2 = GetExpression(param, filter2); + + switch (logicalOperator) + { + case LogicalOperator.And: + return Expression.And(bin1, bin2); + case LogicalOperator.Or: + return Expression.Or(bin1, bin2); + case LogicalOperator.OrElse: + return Expression.OrElse(bin1, bin2); + case LogicalOperator.AndAlso: + default: + return Expression.AndAlso(bin1, bin2); + } + } +} diff --git a/src/CmsEngine.Application/Helpers/ExpressionFilter.cs b/src/CmsEngine.Application/Helpers/ExpressionFilter.cs new file mode 100644 index 00000000..d13148e1 --- /dev/null +++ b/src/CmsEngine.Application/Helpers/ExpressionFilter.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Application.Helpers; + +public sealed class ExpressionFilter +{ + public string PropertyName { get; set; } + public Operation Operation { get; set; } + public object Value { get; set; } +} diff --git a/src/CmsEngine.Application/Helpers/FileHelper.cs b/src/CmsEngine.Application/Helpers/FileHelper.cs new file mode 100644 index 00000000..3f13d40f --- /dev/null +++ b/src/CmsEngine.Application/Helpers/FileHelper.cs @@ -0,0 +1,80 @@ +namespace CmsEngine.Application.Helpers; + +public static class FileHelper +{ + public static string FormatFileSize(string filename) + { + string[] sizes = { "B", "KB", "MB", "GB" }; + double len = new FileInfo(filename).Length; + var order = 0; + + while (len >= 1024 && order + 1 < sizes.Length) + { + order++; + len /= 1024; + } + + return string.Format("{0:0} {1}", len, sizes[order]); + } + + public static bool IsImage(string fileName) + { + return fileName.EndsWith(".jpeg", true, CultureInfo.InvariantCulture) + || fileName.EndsWith(".jpg", true, CultureInfo.InvariantCulture) + || fileName.EndsWith(".png", true, CultureInfo.InvariantCulture) + || fileName.EndsWith(".gif", true, CultureInfo.InvariantCulture) + || fileName.EndsWith(".bmp", true, CultureInfo.InvariantCulture); + } + + public static void ResizeImage(string originalFile, string newFile, int newWidth, int maxHeight, bool onlyResizeIfWider) + { + var fullsizeImage = Image.FromFile(originalFile); + + // Prevent using images internal thumbnail + fullsizeImage.RotateFlip(RotateFlipType.Rotate180FlipNone); + fullsizeImage.RotateFlip(RotateFlipType.Rotate180FlipNone); + + if (onlyResizeIfWider && fullsizeImage.Width <= newWidth) + { + newWidth = fullsizeImage.Width; + } + + var newHeight = fullsizeImage.Height * newWidth / fullsizeImage.Width; + if (newHeight > maxHeight) + { + // Resize with height instead + newWidth = fullsizeImage.Width * maxHeight / fullsizeImage.Height; + newHeight = maxHeight; + } + + var newImage = fullsizeImage.GetThumbnailImage(newWidth, newHeight, null, IntPtr.Zero); + + // Clear handle to original file so that we can overwrite it if necessary + fullsizeImage.Dispose(); + + var encoderParameters = new EncoderParameters(1); + encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 75L); + + // Save resized picture + newImage.Save(newFile, GetEncoderInfo("image/jpeg"), encoderParameters); + } + + private static ImageCodecInfo GetEncoderInfo(string mimeType) + { + var encoders = ImageCodecInfo.GetImageEncoders(); + for (var j = 0; j < encoders.Length; ++j) + { + if (encoders[j].MimeType == mimeType) + { + return encoders[j]; + } + } + return null; + } + + public static Bitmap CropImage(Image img, Rectangle cropArea) + { + var bmpImage = new Bitmap(img); + return bmpImage.Clone(cropArea, bmpImage.PixelFormat); + } +} diff --git a/src/CmsEngine.Application/Models/EditModels/BaseEditModel.cs b/src/CmsEngine.Application/Models/EditModels/BaseEditModel.cs new file mode 100644 index 00000000..101a496c --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/BaseEditModel.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class BaseEditModel +{ + public bool IsNew { + get { + return Id == 0 && VanityId == Guid.Empty; + } + } + + public int Id { get; set; } + + public Guid VanityId { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId) + ); + return jsonResult.ToString(); + } + +} diff --git a/src/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs b/src/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs new file mode 100644 index 00000000..b23e2b15 --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/CategoryEditModel.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class CategoryEditModel : BaseEditModel, IEditModel +{ + [Required] + [MaxLength(35, ErrorMessage = "The name must have less than 35 characters")] + public string Name { get; set; } + + public string Slug { get; set; } + + public string? Description { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId), + new JProperty("Name", Name), + new JProperty("Slug", Slug), + new JProperty("Description", Description) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs b/src/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs new file mode 100644 index 00000000..24736a57 --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/CheckboxEditModel.cs @@ -0,0 +1,9 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class CheckboxEditModel +{ + public string Label { get; set; } + public string Value { get; set; } + public bool Selected { get; set; } + public bool Enabled { get; set; } +} diff --git a/src/CmsEngine.Application/Models/EditModels/IEditModel.cs b/src/CmsEngine.Application/Models/EditModels/IEditModel.cs new file mode 100644 index 00000000..d6ceabf5 --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/IEditModel.cs @@ -0,0 +1,9 @@ +namespace CmsEngine.Application.Models.EditModels; + +public interface IEditModel +{ + bool IsNew { get; } + + int Id { get; set; } + Guid VanityId { get; set; } +} diff --git a/src/CmsEngine.Application/Models/EditModels/PageEditModel.cs b/src/CmsEngine.Application/Models/EditModels/PageEditModel.cs new file mode 100644 index 00000000..e9347dcb --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/PageEditModel.cs @@ -0,0 +1,45 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class PageEditModel : BaseEditModel, IEditModel +{ + public PageEditModel() + { + Status = DocumentStatus.Draft; + PublishedOn = DateTime.Now; + } + + [Required] + [MaxLength(100, ErrorMessage = "The title must have less than 100 characters")] + public string Title { get; set; } + + public string Slug { get; set; } + + public string HeaderImage { get; set; } + + [Required] + [MaxLength(150, ErrorMessage = "The description must have less than 150 characters")] + public string Description { get; set; } + + public string DocumentContent { get; set; } + + public DocumentStatus Status { get; set; } + + [Required] + //[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd hh:mm}", ApplyFormatInEditMode = true)] + public DateTime PublishedOn { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId), + new JProperty("Title", Title), + new JProperty("Slug", Slug), + new JProperty("HeaderImage", HeaderImage), + new JProperty("Description", Description), + new JProperty("Status", Status.ToString()), + new JProperty("PublishedOn", PublishedOn) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Application/Models/EditModels/PostEditModel.cs b/src/CmsEngine.Application/Models/EditModels/PostEditModel.cs new file mode 100644 index 00000000..20750d1d --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/PostEditModel.cs @@ -0,0 +1,48 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class PostEditModel : BaseEditModel, IEditModel +{ + [Required] + [MaxLength(100, ErrorMessage = "The title must have less than 100 characters")] + public string Title { get; set; } + + public string Slug { get; set; } + + public string? HeaderImage { get; set; } + + [Required] + [MaxLength(150, ErrorMessage = "The description must have less than 150 characters")] + public string Description { get; set; } + + public string DocumentContent { get; set; } + + public IEnumerable Categories { get; set; } = new List(); + + public IEnumerable SelectedCategories { get; set; } = new List(); + + // TODO: Perhaps replace the SelectListItem by something else in order to make it less ASP.NET Core dependent + public IEnumerable Tags { get; set; } = new List(); + + public IEnumerable SelectedTags { get; set; } = new List(); + + public DocumentStatus Status { get; set; } = DocumentStatus.Draft; + + [Required] + [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy HH:mm}", ApplyFormatInEditMode = true)] + public DateTime PublishedOn { get; set; } = DateTime.Now; + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId), + new JProperty("Title", Title), + new JProperty("Slug", Slug), + new JProperty("HeaderImage", HeaderImage), + new JProperty("Description", Description), + new JProperty("Status", Status.ToString()), + new JProperty("PublishedOn", PublishedOn) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Application/Models/EditModels/TagEditModel.cs b/src/CmsEngine.Application/Models/EditModels/TagEditModel.cs new file mode 100644 index 00000000..14b8a39b --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/TagEditModel.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class TagEditModel : BaseEditModel, IEditModel +{ + [Required] + [MaxLength(15, ErrorMessage = "The name must have less than 15 characters")] + public string Name { get; set; } + + public string Slug { get; set; } +} diff --git a/src/CmsEngine.Application/Models/EditModels/UserEditModel.cs b/src/CmsEngine.Application/Models/EditModels/UserEditModel.cs new file mode 100644 index 00000000..075e33a2 --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/UserEditModel.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class UserEditModel : BaseEditModel, IEditModel +{ + public string FullName { + get { + return $"{Name} {Surname}"; + } + } + + [Required] + public string Name { get; set; } + + [Required] + public string Surname { get; set; } + + [Required] + public string Email { get; set; } + + [Required] + public string Password { get; set; } + + public string UserName { get; set; } +} diff --git a/src/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs b/src/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs new file mode 100644 index 00000000..ca95468c --- /dev/null +++ b/src/CmsEngine.Application/Models/EditModels/WebsiteEditModel.cs @@ -0,0 +1,69 @@ +namespace CmsEngine.Application.Models.EditModels; + +public class WebsiteEditModel : BaseEditModel, IEditModel +{ + [Required] + [MaxLength(25)] + public string Name { get; set; } + + [MaxLength(200)] + public string? Tagline { get; set; } + + public string? Description { get; set; } + + public string? HeaderImage { get; set; } + + [Required] + [MaxLength(5)] + public string Culture { get; set; } + + [Required] + [MaxLength(100)] + public string UrlFormat { get; set; } = $"{Main.SiteUrl}/{Main.Type}/{Main.Slug}"; + + [Required] + [MaxLength(10)] + public string DateFormat { get; set; } = "MM/dd/yyyy"; + + [Required] + [MaxLength(250)] + public string SiteUrl { get; set; } + + [Required] + public int ArticleLimit { get; set; } + + [MaxLength(250)] + public string? Address { get; set; } + + [MaxLength(20)] + public string? Phone { get; set; } + + [Required] + [MaxLength(250)] + public string? Email { get; set; } + + [MaxLength(20)] + public string? Facebook { get; set; } + + [MaxLength(20)] + public string? Twitter { get; set; } + + [MaxLength(20)] + public string? Instagram { get; set; } + + [MaxLength(20)] + public string? LinkedIn { get; set; } + + [MaxLength(30)] + public string? FacebookAppId { get; set; } + + [MaxLength(10)] + public string? FacebookApiVersion { get; set; } + + [MaxLength(30)] + public string? DisqusShortName { get; set; } + + public string? GoogleAnalytics { get; set; } + public string? GoogleRecaptchaSiteKey { get; set; } + public string? GoogleRecaptchaSecretKey { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs new file mode 100644 index 00000000..2b689fdc --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ExternalLoginViewModel.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class ExternalLoginViewModel +{ + [Required] + [EmailAddress] + public string Email { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs new file mode 100644 index 00000000..06288b26 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ForgotPasswordViewModel.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class ForgotPasswordViewModel +{ + [Required] + [EmailAddress] + public string Email { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs new file mode 100644 index 00000000..ed3ad9a2 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginViewModel.cs @@ -0,0 +1,15 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class LoginViewModel +{ + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs new file mode 100644 index 00000000..623f945d --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWith2faViewModel.cs @@ -0,0 +1,15 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class LoginWith2faViewModel +{ + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Authenticator code")] + public string TwoFactorCode { get; set; } + + [Display(Name = "Remember this machine")] + public bool RememberMachine { get; set; } + + public bool RememberMe { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs new file mode 100644 index 00000000..cfc583fb --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/LoginWithRecoveryCodeViewModel.cs @@ -0,0 +1,9 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class LoginWithRecoveryCodeViewModel +{ + [Required] + [DataType(DataType.Text)] + [Display(Name = "Recovery Code")] + public string RecoveryCode { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs new file mode 100644 index 00000000..961e97db --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/RegisterViewModel.cs @@ -0,0 +1,20 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class RegisterViewModel +{ + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs new file mode 100644 index 00000000..fe759f06 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/AccountViewModels/ResetPasswordViewModel.cs @@ -0,0 +1,20 @@ +namespace CmsEngine.Application.Models.ViewModels.AccountViewModels; + +public class ResetPasswordViewModel +{ + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs new file mode 100644 index 00000000..2b5d4d9f --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ApiDetailsViewModel.cs @@ -0,0 +1,22 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class ApiDetailsViewModel +{ + public string FacebookAppId { get; set; } + public string FacebookApiVersion { get; set; } + + public bool HasFacebookDetails { + get { + return !string.IsNullOrWhiteSpace(FacebookAppId) && !string.IsNullOrWhiteSpace(FacebookApiVersion); + } + } + + public string DisqusShortName { get; set; } + + public bool HasDisqusDetails { + get { + return !string.IsNullOrWhiteSpace(DisqusShortName); + } + } + +} diff --git a/src/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs new file mode 100644 index 00000000..bbe169c1 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/BaseViewModel.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class BaseViewModel : IViewModel +{ + public int Id { get; set; } + public Guid VanityId { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs new file mode 100644 index 00000000..5ceee147 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/CategoryViewModel.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class CategoryViewModel : BaseViewModel, IViewModel +{ + public string Name { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public int PostCount { get; set; } + public IEnumerable Posts { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs new file mode 100644 index 00000000..07a2d230 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ContactDetailsViewModel.cs @@ -0,0 +1,14 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class ContactDetailsViewModel +{ + public string Address { get; set; } + public string Phone { get; set; } + public string Email { get; set; } + + public bool HasEmail { + get { + return !string.IsNullOrWhiteSpace(Email); + } + } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs new file mode 100644 index 00000000..326c703c --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/CategoryTableViewModel.cs @@ -0,0 +1,13 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class CategoryTableViewModel : BaseViewModel, IViewModel +{ + [Searchable, Orderable, ShowOnDataTable(0)] + public string Name { get; set; } + + [Searchable, Orderable, ShowOnDataTable(1)] + public string Slug { get; set; } + + [Searchable, Orderable, ShowOnDataTable(2)] + public string Description { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs new file mode 100644 index 00000000..29377e75 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataColumn.cs @@ -0,0 +1,19 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class DataColumn +{ + [JsonProperty(PropertyName = "data")] + public int Data { get; set; } + + [JsonProperty(PropertyName = "name")] + public string Name { get; set; } + + [JsonProperty(PropertyName = "searchable")] + public bool Searchable { get; set; } + + [JsonProperty(PropertyName = "orderable")] + public bool Orderable { get; set; } + + [JsonProperty(PropertyName = "search")] + public Search Search { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs new file mode 100644 index 00000000..8f33d58f --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataOrder.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class DataOrder +{ + [JsonProperty(PropertyName = "column")] + public int Column { get; set; } + + [JsonProperty(PropertyName = "dir")] + public string Dir { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs new file mode 100644 index 00000000..4116be64 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataParameters.cs @@ -0,0 +1,22 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class DataParameters +{ + [JsonProperty(PropertyName = "draw")] + public int Draw { get; set; } + + [JsonProperty(PropertyName = "columns")] + public List Columns { get; set; } + + [JsonProperty(PropertyName = "order")] + public List Order { get; set; } + + [JsonProperty(PropertyName = "start")] + public int Start { get; set; } + + [JsonProperty(PropertyName = "length")] + public int Length { get; set; } + + [JsonProperty(PropertyName = "search")] + public Search Search { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs new file mode 100644 index 00000000..39cfc4bb --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/DataTableViewModel.cs @@ -0,0 +1,16 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class DataTableViewModel +{ + [JsonProperty(PropertyName = "draw")] + public int Draw { get; set; } + + [JsonProperty(PropertyName = "recordsTotal")] + public int RecordsTotal { get; set; } + + [JsonProperty(PropertyName = "recordsFiltered")] + public int RecordsFiltered { get; set; } + + [JsonProperty(PropertyName = "data")] + public List> Data { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs new file mode 100644 index 00000000..e3f9b03f --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PageTableViewModel.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class PageTableViewModel : BaseViewModel, IViewModel +{ + [Searchable, Orderable, ShowOnDataTable(0)] + public string Title { get; set; } + + [Searchable, Orderable, ShowOnDataTable(2)] + public string Slug { get; set; } + + [Searchable, Orderable, ShowOnDataTable(1)] + public string Description { get; set; } + + public string DocumentContent { get; set; } + + [Searchable, Orderable, ShowOnDataTable(3)] + public UserViewModel Author { get; set; } + + [Orderable, ShowOnDataTable(5)] + public DocumentStatus Status { get; set; } + + [Orderable, ShowOnDataTable(4)] + public string PublishedOn { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs new file mode 100644 index 00000000..db9b4b05 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/PostTableViewModel.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class PostTableViewModel : BaseViewModel +{ + [Searchable, Orderable, ShowOnDataTable(0)] + public string Title { get; set; } + + [Searchable, Orderable, ShowOnDataTable(2)] + public string Slug { get; set; } + + [Searchable, Orderable, ShowOnDataTable(1)] + public string Description { get; set; } + + public string DocumentContent { get; set; } + + [Orderable, ShowOnDataTable(3)] + public UserViewModel Author { get; set; } + + [Orderable, ShowOnDataTable(5)] + public DocumentStatus Status { get; set; } + + [Orderable, ShowOnDataTable(4)] + public string PublishedOn { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs new file mode 100644 index 00000000..9b7a3971 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/Search.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class Search +{ + [JsonProperty(PropertyName = "regex")] + public bool Regex { get; set; } + + [JsonProperty(PropertyName = "value")] + public string Value { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs new file mode 100644 index 00000000..be19a018 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/TagTableViewModel.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class TagTableViewModel : BaseViewModel, IViewModel +{ + [Searchable, Orderable, ShowOnDataTable(0)] + public string Name { get; set; } + + [Searchable, Orderable, ShowOnDataTable(1)] + public string Slug { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs new file mode 100644 index 00000000..0a2fe7fe --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DataTableViewModels/WebsiteTableViewModel.cs @@ -0,0 +1,31 @@ +namespace CmsEngine.Application.Models.ViewModels.DataTablesViewModels; + +public class WebsiteTableViewModel : BaseViewModel, IViewModel +{ + [Searchable, Orderable, ShowOnDataTable(0)] + public string Name { get; set; } + + [Searchable, Orderable, ShowOnDataTable(1)] + public string Tagline { get; set; } + + [Searchable, Orderable, ShowOnDataTable(2)] + public string Culture { get; set; } + + [Searchable, Orderable, ShowOnDataTable(3)] + public string UrlFormat { get; set; } = $"{Main.SiteUrl}/{Main.Type}/{Main.Slug}"; + + [Searchable, Orderable, ShowOnDataTable(4)] + public string DateFormat { get; set; } = "MM/dd/yyyy"; + + [Searchable, Orderable, ShowOnDataTable(5)] + public string SiteUrl { get; set; } + + public string GoogleAnalytics { get; set; } + + [Orderable, ShowOnDataTable(6)] + public bool HasGoogleAnalytics { + get { + return !string.IsNullOrWhiteSpace(GoogleAnalytics); + } + } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs new file mode 100644 index 00000000..becfbefc --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/DocumentViewModel.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class DocumentViewModel : BaseViewModel +{ + public string Title { get; set; } + public string Slug { get; set; } + public string Description { get; set; } + public string HeaderImage { get; set; } + public string DocumentContent { get; set; } + public DocumentStatus Status { get; set; } + public string PublishedOn { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs new file mode 100644 index 00000000..c5bef517 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ErrorViewModel.cs @@ -0,0 +1,19 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class ErrorViewModel +{ + public string Title { get; } + public string Message { get; } + + public ErrorViewModel(string message) + { + Title = "Error"; + Message = message; + } + + public ErrorViewModel(string pageTitle, string message) + { + Title = pageTitle; + Message = message; + } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs new file mode 100644 index 00000000..042e0c4a --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/GoogleViewModel.cs @@ -0,0 +1,26 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class GoogleViewModel +{ + public string GoogleAnalytics { get; set; } + public string GoogleRecaptchaSiteKey { get; set; } + public string GoogleRecaptchaSecretKey { get; set; } + + public bool HasAnalytics { + get { + return !string.IsNullOrWhiteSpace(GoogleAnalytics); + } + } + + public bool HasRecaptchaSiteKey { + get { + return !string.IsNullOrWhiteSpace(GoogleRecaptchaSiteKey); + } + } + + public bool HasRecaptchaSecretKey { + get { + return !string.IsNullOrWhiteSpace(GoogleRecaptchaSecretKey); + } + } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/IViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/IViewModel.cs new file mode 100644 index 00000000..1b05c5e1 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/IViewModel.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public interface IViewModel +{ + int Id { get; set; } + + Guid VanityId { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs new file mode 100644 index 00000000..5ae3ad01 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/InstanceViewModel.cs @@ -0,0 +1,31 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class InstanceViewModel +{ + public int Id { get; set; } + public string Name { get; set; } + public string Tagline { get; set; } + public string Description { get; set; } + public string HeaderImage { get; set; } + public string Culture { get; set; } + public string UrlFormat { get; set; } + public string DateFormat { get; set; } + public string SiteUrl { get; set; } + public int ArticleLimit { get; set; } + public string GoogleAnalytics { get; set; } + public ContactDetailsViewModel ContactDetails { get; set; } + public ApiDetailsViewModel ApiDetails { get; set; } + public DocumentViewModel SelectedDocument { get; set; } + public ContactForm ContactForm { get; set; } + public SocialMediaViewModel SocialMedia { get; set; } + public GoogleViewModel Google { get; set; } + + public string PageTitle { get; set; } + + public PaginatedList PagedPosts { get; set; } + public IEnumerable LatestPosts { get; set; } // TODO: Maybe I need 2 versions of this property - For Index and for Footer + public IEnumerable Pages { get; set; } + public IEnumerable Categories { get; set; } + public IEnumerable CategoriesWithPosts { get; set; } + public IEnumerable Tags { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs new file mode 100644 index 00000000..49bfdd1f --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ChangePasswordViewModel.cs @@ -0,0 +1,22 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class ChangePasswordViewModel +{ + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string StatusMessage { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs new file mode 100644 index 00000000..f5db5941 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/EnableAuthenticatorViewModel.cs @@ -0,0 +1,15 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class EnableAuthenticatorViewModel +{ + [Required] + [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Text)] + [Display(Name = "Verification Code")] + public string Code { get; set; } + + [ReadOnly(true)] + public string SharedKey { get; set; } + + public string AuthenticatorUri { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs new file mode 100644 index 00000000..8ae9bd94 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/ExternalLoginsViewModel.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class ExternalLoginsViewModel +{ + public IList CurrentLogins { get; set; } + + public IList OtherLogins { get; set; } + + public bool ShowRemoveButton { get; set; } + + public string StatusMessage { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs new file mode 100644 index 00000000..6a1744e4 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/GenerateRecoveryCodesViewModel.cs @@ -0,0 +1,6 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class GenerateRecoveryCodesViewModel +{ + public string[] RecoveryCodes { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs new file mode 100644 index 00000000..35320d0d --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/IndexViewModel.cs @@ -0,0 +1,22 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class IndexViewModel +{ + public string Username { get; set; } + + public string Name { get; set; } + + public string Surname { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } + + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + + public string StatusMessage { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs new file mode 100644 index 00000000..0f11cddd --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/RemoveLoginViewModel.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class RemoveLoginViewModel +{ + public string LoginProvider { get; set; } + public string ProviderKey { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs new file mode 100644 index 00000000..32478114 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/SetPasswordViewModel.cs @@ -0,0 +1,17 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class SetPasswordViewModel +{ + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string StatusMessage { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs new file mode 100644 index 00000000..ef09ef86 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/ManageViewModels/TwoFactorAuthenticationViewModel.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Application.Models.ViewModels.ManageViewModels; + +public class TwoFactorAuthenticationViewModel +{ + public bool HasAuthenticator { get; set; } + + public int RecoveryCodesLeft { get; set; } + + public bool Is2faEnabled { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/PageViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/PageViewModel.cs new file mode 100644 index 00000000..01819964 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/PageViewModel.cs @@ -0,0 +1,6 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class PageViewModel : DocumentViewModel +{ + public UserViewModel Author { get; set; } = new UserViewModel(); +} diff --git a/src/CmsEngine.Application/Models/ViewModels/PostViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/PostViewModel.cs new file mode 100644 index 00000000..092bedad --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/PostViewModel.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class PostViewModel : DocumentViewModel +{ + public IEnumerable Categories { get; set; } = new List(); + public IEnumerable Tags { get; set; } = new List(); + public UserViewModel Author { get; set; } = new UserViewModel(); +} diff --git a/src/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs new file mode 100644 index 00000000..38247b91 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/SitemapViewModel.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class SitemapViewModel +{ + public string Url { get; set; } + public string PublishedOn { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs new file mode 100644 index 00000000..dafe3b94 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/SocialMediaViewModel.cs @@ -0,0 +1,39 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class SocialMediaViewModel +{ + public string Facebook { get; set; } + public string Twitter { get; set; } + public string Instagram { get; set; } + public string LinkedIn { get; set; } + + public bool HasFacebook { + get { + return !string.IsNullOrWhiteSpace(Facebook); + } + } + + public bool HasTwitter { + get { + return !string.IsNullOrWhiteSpace(Twitter); + } + } + + public bool HasInstagram { + get { + return !string.IsNullOrWhiteSpace(Instagram); + } + } + + public bool HasLinkedIn { + get { + return !string.IsNullOrWhiteSpace(LinkedIn); + } + } + + public bool HasSocialMedia { + get { + return HasFacebook || HasTwitter || HasInstagram || HasLinkedIn; + } + } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/TagViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/TagViewModel.cs new file mode 100644 index 00000000..b37d3f56 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/TagViewModel.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class TagViewModel : BaseViewModel, IViewModel +{ + public string Name { get; set; } + + public string Slug { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/UserViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/UserViewModel.cs new file mode 100644 index 00000000..02b69af3 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/UserViewModel.cs @@ -0,0 +1,21 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class UserViewModel : BaseViewModel, IViewModel +{ + [Searchable, Orderable, ShowOnDataTable(0)] + public string Name { get; set; } + + [Searchable, Orderable, ShowOnDataTable(2)] + public string Surname { get; set; } + + [Searchable, Orderable, ShowOnDataTable(1)] + public string Email { get; set; } + + public string FullName { + get { + return $"{Name} {Surname}"; + } + } + + public string UserName { get; set; } +} diff --git a/src/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs b/src/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs new file mode 100644 index 00000000..41e1c892 --- /dev/null +++ b/src/CmsEngine.Application/Models/ViewModels/WebsiteViewModel.cs @@ -0,0 +1,13 @@ +namespace CmsEngine.Application.Models.ViewModels; + +public class WebsiteViewModel : BaseViewModel, IViewModel +{ + public string Name { get; set; } + public string Tagline { get; set; } + public string Description { get; set; } + public string HeaderImagePath { get; set; } + public string Culture { get; set; } + public string UrlFormat { get; set; } = $"{Main.SiteUrl}/{Main.Type}/{Main.Slug}"; + public string DateFormat { get; set; } = "MM/dd/yyyy"; + public string SiteUrl { get; set; } +} diff --git a/src/CmsEngine.Application/Services/CategoryService.cs b/src/CmsEngine.Application/Services/CategoryService.cs new file mode 100644 index 00000000..d717ae42 --- /dev/null +++ b/src/CmsEngine.Application/Services/CategoryService.cs @@ -0,0 +1,174 @@ +namespace CmsEngine.Application.Services; + +public class CategoryService : Service, ICategoryService +{ + private readonly IUnitOfWork _unitOfWork; + + public CategoryService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + } + + public async Task Delete(Guid id) + { + var item = await _unitOfWork.Categories.GetByIdAsync(id); + + var returnValue = new ReturnValue($"Category '{item.Name}' deleted at {DateTime.Now:T}."); + + try + { + _unitOfWork.Categories.Delete(item); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the category"); + } + + return returnValue; + } + + public async Task DeleteRange(Guid[] ids) + { + var items = await _unitOfWork.Categories.GetByMultipleIdsAsync(ids); + + var returnValue = new ReturnValue($"Categories deleted at {DateTime.Now:T}."); + + try + { + _unitOfWork.Categories.DeleteRange(items); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the categories"); + } + + return returnValue; + } + + public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(searchValue)) + { + var searchableProperties = typeof(CategoryTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); + items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); + } + + return items; + } + + public async Task> GetCategoriesWithPost() + { + logger.LogDebug("CategoryService > GetCategoriesWithPost()"); + var items = await _unitOfWork.Categories.GetCategoriesWithPostOrderedByName(); + logger.LogDebug("Categories loaded: {0}", items.Count()); + return items.MapToViewModelWithPost(); + } + + public async Task> GetCategoriesWithPostCount() + { + logger.LogDebug("CategoryService > GetCategoriesWithPostCount()"); + var items = await _unitOfWork.Categories.GetCategoriesWithPostCountOrderedByName(); + logger.LogDebug("Categories loaded: {0}", items.Count()); + return items.MapToViewModelWithPostCount(); + } + + public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) + { + var items = await _unitOfWork.Categories.GetAllAsync(); + var recordsTotal = items.Count(); + + if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) + { + items = FilterForDataTable(parameters.Search.Value, items); + } + + items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); + + return (items.MapToTableViewModel(), recordsTotal, items.Count()); + } + + public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) + { + try + { + switch (column) + { + case 1: + items = direction == "asc" ? items.OrderBy(o => o.Name) : items.OrderByDescending(o => o.Name); + break; + case 2: + items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); + break; + case 3: + items = direction == "asc" ? items.OrderBy(o => o.Description) : items.OrderByDescending(o => o.Description); + break; + default: + items = items.OrderBy(o => o.Name); + break; + } + } + catch + { + throw; + } + + return items; + } + + public async Task Save(CategoryEditModel categoryEditModel) + { + logger.LogDebug("CmsService > Save(CategoryEditModel: {0})", categoryEditModel.ToString()); + + var returnValue = new ReturnValue($"Category '{categoryEditModel.Name}' saved."); + + try + { + if (categoryEditModel.IsNew) + { + logger.LogDebug("New category"); + var category = categoryEditModel.MapToModel(); + category.WebsiteId = Instance.Id; + + await unitOfWork.Categories.Insert(category); + } + else + { + logger.LogDebug("Update category"); + var category = categoryEditModel.MapToModel(await unitOfWork.Categories.GetByIdAsync(categoryEditModel.VanityId)); + category.WebsiteId = Instance.Id; + + _unitOfWork.Categories.Update(category); + } + + await _unitOfWork.Save(); + logger.LogDebug("Category saved"); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while saving the category"); + } + + return returnValue; + } + + public CategoryEditModel SetupEditModel() + { + logger.LogDebug("CmsService > SetupEditModel()"); + return new CategoryEditModel(); + } + + public async Task SetupEditModel(Guid id) + { + logger.LogDebug("CmsService > SetupCategoryEditModel(id: {0})", id); + var item = await _unitOfWork.Categories.GetByIdAsync(id); + logger.LogDebug("Category: {0}", item.ToString()); + + return item?.MapToEditModel(); + } +} diff --git a/src/CmsEngine.Application/Services/EmailService.cs b/src/CmsEngine.Application/Services/EmailService.cs new file mode 100644 index 00000000..eca793a3 --- /dev/null +++ b/src/CmsEngine.Application/Services/EmailService.cs @@ -0,0 +1,46 @@ +namespace CmsEngine.Application.Services; + +public class EmailService : Service, IEmailService +{ + private readonly IUnitOfWork _unitOfWork; + public EmailService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + } + + + public async Task> GetOrderedByDate() + { + logger.LogDebug("EmailService > GetOrderedByDate()"); + var items = await _unitOfWork.Emails.GetOrderedByDate(); + logger.LogDebug("E-mails loaded: {0}", items.Count()); + return items.MapToViewModel(); + } + + public async Task Save(ContactForm contactForm) + { + logger.LogDebug("CmsService > Save(contactForm: {0})", contactForm.ToString()); + + var returnValue = new ReturnValue($"E-mail saved."); + + try + { + logger.LogDebug("New e-mail"); + var message = contactForm.MapToModel(); + message.DateReceived = DateTime.Now; + + await _unitOfWork.Emails.Insert(message); + + await _unitOfWork.Save(); + logger.LogDebug("E-mail saved"); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while saving the e-mail"); + } + + return returnValue; + } +} diff --git a/src/CmsEngine.Application/Services/Interfaces/ICategoryService.cs b/src/CmsEngine.Application/Services/Interfaces/ICategoryService.cs new file mode 100644 index 00000000..c8796418 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/ICategoryService.cs @@ -0,0 +1,13 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface ICategoryService : IDataTableService +{ + CategoryEditModel SetupEditModel(); + Task SetupEditModel(Guid id); + Task Delete(Guid id); + Task DeleteRange(Guid[] ids); + Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); + Task Save(CategoryEditModel categoryEditModel); + Task> GetCategoriesWithPostCount(); + Task> GetCategoriesWithPost(); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IDataTableService.cs b/src/CmsEngine.Application/Services/Interfaces/IDataTableService.cs new file mode 100644 index 00000000..acf0702d --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IDataTableService.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IDataTableService where T : BaseEntity +{ + IEnumerable FilterForDataTable(string searchValue, IEnumerable items); + IEnumerable OrderForDataTable(int column, string direction, IEnumerable items); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IEmailService.cs b/src/CmsEngine.Application/Services/Interfaces/IEmailService.cs new file mode 100644 index 00000000..9697cb69 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IEmailService.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IEmailService +{ + Task Save(ContactForm contactForm); + Task> GetOrderedByDate(); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IPageService.cs b/src/CmsEngine.Application/Services/Interfaces/IPageService.cs new file mode 100644 index 00000000..5d39d6f8 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IPageService.cs @@ -0,0 +1,13 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IPageService : IDataTableService +{ + PageEditModel SetupEditModel(); + Task SetupEditModel(Guid id); + Task Delete(Guid id); + Task DeleteRange(Guid[] ids); + Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); + Task Save(PageEditModel pageEditModel); + Task GetBySlug(string slug); + Task> GetAllPublished(); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IPostService.cs b/src/CmsEngine.Application/Services/Interfaces/IPostService.cs new file mode 100644 index 00000000..d73055c8 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IPostService.cs @@ -0,0 +1,18 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IPostService : IDataTableService +{ + Task SetupEditModel(); + Task SetupEditModel(Guid id); + Task Delete(Guid id); + Task DeleteRange(Guid[] ids); + Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); + Task Save(PostEditModel postEditModel); + Task> GetPublishedOrderedByDate(int count = 0); + Task GetBySlug(string slug); + Task> GetPublishedByCategoryForPagination(string categorySlug, int page = 1); + Task> GetPublishedByTagForPagination(string tagSlug, int page = 1); + Task> GetPublishedForPagination(int page = 1); + Task> GetPublishedLatestPosts(int count); + Task> FindPublishedForPaginationOrderByDateDescending(string searchTerm = "", int page = 1); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IService.cs b/src/CmsEngine.Application/Services/Interfaces/IService.cs new file mode 100644 index 00000000..204d1de7 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IService.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IService +{ + InstanceViewModel Instance { get; } + UserViewModel CurrentUser { get; } +} diff --git a/src/CmsEngine.Application/Services/Interfaces/ITagService.cs b/src/CmsEngine.Application/Services/Interfaces/ITagService.cs new file mode 100644 index 00000000..c41b86f5 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/ITagService.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface ITagService : IDataTableService +{ + TagEditModel SetupEditModel(); + Task SetupEditModel(Guid id); + Task Delete(Guid id); + Task DeleteRange(Guid[] ids); + Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); + Task Save(TagEditModel tagEditModel); + Task> GetAllTags(); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs b/src/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs new file mode 100644 index 00000000..f2aab233 --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IWebsiteService.cs @@ -0,0 +1,11 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IWebsiteService : IDataTableService +{ + WebsiteEditModel SetupEditModel(); + Task SetupEditModel(Guid id); + Task Delete(Guid id); + Task DeleteRange(Guid[] ids); + Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters); + Task Save(WebsiteEditModel categoryEditModel); +} diff --git a/src/CmsEngine.Application/Services/Interfaces/IXmlService.cs b/src/CmsEngine.Application/Services/Interfaces/IXmlService.cs new file mode 100644 index 00000000..6bd6b69d --- /dev/null +++ b/src/CmsEngine.Application/Services/Interfaces/IXmlService.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Application.Services.Interfaces; + +public interface IXmlService +{ + Task GenerateFeed(); + Task GenerateSitemap(); +} diff --git a/src/CmsEngine.Application/Services/PageService.cs b/src/CmsEngine.Application/Services/PageService.cs new file mode 100644 index 00000000..4ce905c4 --- /dev/null +++ b/src/CmsEngine.Application/Services/PageService.cs @@ -0,0 +1,191 @@ +namespace CmsEngine.Application.Services; + +public class PageService : Service, IPageService +{ + private readonly IUnitOfWork _unitOfWork; + + public PageService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + } + + public async Task Delete(Guid id) + { + var item = await _unitOfWork.Pages.GetByIdAsync(id); + + var returnValue = new ReturnValue($"Page '{item.Title}' deleted at {DateTime.Now:T}."); + + try + { + _unitOfWork.Pages.Delete(item); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the page"); + } + + return returnValue; + } + + public async Task DeleteRange(Guid[] ids) + { + var items = await _unitOfWork.Pages.GetByMultipleIdsAsync(ids); + + var returnValue = new ReturnValue($"Pages deleted at {DateTime.Now:T}."); + + try + { + _unitOfWork.Pages.DeleteRange(items); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the pages"); + } + + return returnValue; + } + + public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(searchValue)) + { + var searchableProperties = typeof(PageTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); + items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); + } + return items; + } + + public async Task> GetAllPublished() + { + logger.LogDebug("PageService > GetPagesByStatusReadOnly()"); + var items = await _unitOfWork.Pages.GetByStatusOrderByDescending(DocumentStatus.Published); + logger.LogDebug("Pages loaded: {0}", items.Count()); + return items.MapToViewModel(); + } + + public async Task GetBySlug(string slug) + { + logger.LogDebug($"PageService > GetBySlug({slug})"); + var item = await _unitOfWork.Pages.GetBySlug(slug); + return item?.MapToViewModel(); + } + + public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) + { + var items = await _unitOfWork.Pages.GetForDataTable(); + var recordsTotal = items.Count(); + if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) + { + items = FilterForDataTable(parameters.Search.Value, items); + } + items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); + return (items.MapToTableViewModel(), recordsTotal, items.Count()); + } + + public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) + { + try + { + switch (column) + { + case 1: + items = direction == "asc" ? items.OrderBy(o => o.Title) : items.OrderByDescending(o => o.Title); + break; + case 2: + items = direction == "asc" ? items.OrderBy(o => o.Description) : items.OrderByDescending(o => o.Description); + break; + case 3: + items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); + break; + //case 4: + // items = direction == "asc" ? items.OrderBy(o => o.Author.FullName) : items.OrderByDescending(o => o.Author.FullName); + // break; + case 5: + items = direction == "asc" ? items.OrderBy(o => o.PublishedOn) : items.OrderByDescending(o => o.PublishedOn); + break; + case 6: + items = direction == "asc" ? items.OrderBy(o => o.Status) : items.OrderByDescending(o => o.Status); + break; + default: + items = items.OrderByDescending(o => o.PublishedOn); + break; + } + } + catch + { + throw; + } + + return items; + } + + public async Task Save(PageEditModel pageEditModel) + { + logger.LogDebug("PageService > Save(PageEditModel: {0})", pageEditModel.ToString()); + + var returnValue = new ReturnValue($"Page '{pageEditModel.Title}' saved."); + + try + { + if (pageEditModel.IsNew) + { + logger.LogDebug("New page"); + var page = pageEditModel.MapToModel(); + page.WebsiteId = Instance.Id; + + await PrepareRelatedPropertiesAsync(page); + await unitOfWork.Pages.Insert(page); + } + else + { + logger.LogDebug("Update page"); + var page = pageEditModel.MapToModel(await unitOfWork.Pages.GetForSavingById(pageEditModel.VanityId)); + page.WebsiteId = Instance.Id; + + _unitOfWork.Pages.RemoveRelatedItems(page); + await PrepareRelatedPropertiesAsync(page); + _unitOfWork.Pages.Update(page); + } + + await _unitOfWork.Save(); + logger.LogDebug("Page saved"); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while saving the page"); + } + + return returnValue; + } + + public PageEditModel SetupEditModel() + { + logger.LogDebug("PageService > SetupEditModel()"); + return new PageEditModel(); + } + + public async Task SetupEditModel(Guid id) + { + logger.LogDebug("PageService > SetupPageEditModel(id: {0})", id); + var item = await _unitOfWork.Pages.GetByIdAsync(id); + logger.LogDebug("Page: {0}", item.ToString()); + return item?.MapToEditModel(); + } + + private async Task PrepareRelatedPropertiesAsync(Page page) + { + var user = await GetCurrentUserAsync(); + page.PageApplicationUsers.Clear(); + page.PageApplicationUsers.Add(new PageApplicationUser + { + PageId = page.Id, + ApplicationUserId = user.Id + }); + } +} diff --git a/src/CmsEngine.Application/Services/PostService.cs b/src/CmsEngine.Application/Services/PostService.cs new file mode 100644 index 00000000..32e634e6 --- /dev/null +++ b/src/CmsEngine.Application/Services/PostService.cs @@ -0,0 +1,262 @@ +namespace CmsEngine.Application.Services; + +public class PostService : Service, IPostService +{ + private readonly IUnitOfWork _unitOfWork; + + public PostService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + } + + public async Task Delete(Guid id) + { + var item = await _unitOfWork.Posts.GetByIdAsync(id); + + var returnValue = new ReturnValue($"Post '{item.Title}' deleted at {DateTime.Now.ToString("T")}."); + + try + { + _unitOfWork.Posts.Delete(item); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the post"); + } + + return returnValue; + } + + public async Task DeleteRange(Guid[] ids) + { + var items = await _unitOfWork.Posts.GetByMultipleIdsAsync(ids); + + var returnValue = new ReturnValue($"Posts deleted at {DateTime.Now.ToString("T")}."); + + try + { + _unitOfWork.Posts.DeleteRange(items); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the posts"); + } + + return returnValue; + } + + public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(searchValue)) + { + var searchableProperties = typeof(PostTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); + items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); + } + return items; + } + + public async Task GetBySlug(string slug) + { + logger.LogDebug($"PostService > GetBySlug({slug})"); + var item = await _unitOfWork.Posts.GetBySlug(slug); + return item?.MapToViewModel(); + } + + public async Task> GetPublishedOrderedByDate(int count = 0) + { + logger.LogDebug("PostService > GetByStatus(count: {0})", count); + var items = await _unitOfWork.Posts.GetByStatusOrderByDescending(DocumentStatus.Published); + logger.LogDebug("Posts loaded: {0}", items.Count()); + + return items.MapToEditModel(); + } + + public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) + { + var items = await _unitOfWork.Posts.GetForDataTable(); + int recordsTotal = items.Count(); + if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) + { + items = FilterForDataTable(parameters.Search.Value, items); + } + items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); + return (items.MapToTableViewModel(), recordsTotal, items.Count()); + } + + public async Task> GetPublishedByCategoryForPagination(string categorySlug, int page = 1) + { + logger.LogDebug("CmsService > GetPublishedByCategoryForPagination(categorySlug: {0}, page: {1})", categorySlug, page); + var posts = await _unitOfWork.Posts.GetPublishedByCategoryForPagination(categorySlug, page, Instance.ArticleLimit); + return new PaginatedList(posts.Items.MapToViewModelForPartialView(), posts.Count, page, Instance.ArticleLimit); + } + + public async Task> GetPublishedByTagForPagination(string tagSlug, int page = 1) + { + logger.LogDebug("CmsService > GetPublishedByTagForPagination(tagSlug: {0}, page: {1})", tagSlug, page); + var posts = await _unitOfWork.Posts.GetPublishedByTagForPagination(tagSlug, page, Instance.ArticleLimit); + return new PaginatedList(posts.Items.MapToViewModelForPartialViewForTags(), posts.Count, page, Instance.ArticleLimit); + } + + public async Task> GetPublishedForPagination(int page = 1) + { + logger.LogDebug("CmsService > GetPublishedForPagination(page: {0})", page); + var posts = await _unitOfWork.Posts.GetPublishedForPagination(page, Instance.ArticleLimit); + return new PaginatedList(posts.Items.MapToViewModelForPartialView(), posts.Count, page, Instance.ArticleLimit); + } + + public async Task> GetPublishedLatestPosts(int count) + { + logger.LogDebug("CmsService > GetPublishedLatestPosts(count: {0})", count); + var posts = await _unitOfWork.Posts.GetPublishedLatestPosts(count); + return posts.MapToViewModelForPartialView(); + } + + public async Task> FindPublishedForPaginationOrderByDateDescending(string searchTerm = "", int page = 1) + { + logger.LogDebug("CmsService > FindPublishedForPaginationOrderByDateDescending(page: {0}, searchTerm: {1})", page, searchTerm); + var posts = await _unitOfWork.Posts.FindPublishedForPaginationOrderByDateDescending(page, searchTerm, Instance.ArticleLimit); + return new PaginatedList(posts.Items.MapToViewModelForPartialView(), posts.Count, page, Instance.ArticleLimit); + } + + public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) + { + try + { + switch (column) + { + case 1: + items = direction == "asc" ? items.OrderBy(o => o.Title) : items.OrderByDescending(o => o.Title); + break; + case 2: + items = direction == "asc" ? items.OrderBy(o => o.Description) : items.OrderByDescending(o => o.Description); + break; + case 3: + items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); + break; + //case 4: + // items = direction == "asc" ? items.OrderBy(o => o.Author.FullName) : items.OrderByDescending(o => o.Author.FullName); + // break; + case 5: + items = direction == "asc" ? items.OrderBy(o => o.PublishedOn) : items.OrderByDescending(o => o.PublishedOn); + break; + case 6: + items = direction == "asc" ? items.OrderBy(o => o.Status) : items.OrderByDescending(o => o.Status); + break; + default: + items = items.OrderByDescending(o => o.PublishedOn); + break; + } + } + catch + { + throw; + } + + return items; + } + + public async Task Save(PostEditModel postEditModel) + { + logger.LogDebug("PostService > Save(PostEditModel: {0})", postEditModel.ToString()); + var returnValue = new ReturnValue($"Post '{postEditModel.Title}' saved."); + + try + { + if (postEditModel.IsNew) + { + logger.LogDebug("New post"); + var post = postEditModel.MapToModel(); + post.WebsiteId = Instance.Id; + + await PrepareRelatedPropertiesAsync(postEditModel, post); + await unitOfWork.Posts.Insert(post); + } + else + { + logger.LogDebug("Update post"); + var post = postEditModel.MapToModel(await unitOfWork.Posts.GetForSavingById(postEditModel.VanityId)); + post.WebsiteId = Instance.Id; + + _unitOfWork.Posts.RemoveRelatedItems(post); + await PrepareRelatedPropertiesAsync(postEditModel, post); + _unitOfWork.Posts.Update(post); + } + + await _unitOfWork.Save(); + logger.LogDebug("Post saved"); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while saving the post"); + } + + return returnValue; + } + + public async Task SetupEditModel() + { + logger.LogDebug("PostService > SetupEditModel()"); + return new PostEditModel + { + Categories = (await unitOfWork.Categories.GetAllAsync()).MapToViewModelSimple().PopulateCheckboxList(), + Tags = (await unitOfWork.Tags.GetAllAsync()).MapToViewModelSimple().PopulateSelectList() + }; + } + + public async Task SetupEditModel(Guid id) + { + logger.LogDebug("PostService > SetupPostEditModel(id: {0})", id); + var item = await _unitOfWork.Posts.GetForEditingById(id); + logger.LogDebug("Post: {0}", item.ToString()); + var postEditModel = item.MapToEditModel(); + postEditModel.Categories = (await unitOfWork.Categories.GetAllAsync()).MapToViewModelSimple().PopulateCheckboxList(postEditModel.SelectedCategories); + postEditModel.Tags = (await unitOfWork.Tags.GetAllAsync()).MapToViewModelSimple().PopulateSelectList(postEditModel.SelectedTags); + + return postEditModel; + } + + private async Task PrepareRelatedPropertiesAsync(PostEditModel postEditModel, Post post) + { + post.PostCategories.Clear(); + if (postEditModel.SelectedCategories != null) + { + var categoryIds = await _unitOfWork.Categories.GetIdsByMultipleGuidsAsync(postEditModel.SelectedCategories.ToList().ConvertAll(Guid.Parse)); + foreach (int categoryId in categoryIds) + { + post.PostCategories.Add(new PostCategory + { + PostId = post.Id, + CategoryId = categoryId + }); + } + } + + post.PostTags.Clear(); + if (postEditModel.SelectedTags != null) + { + var tagIds = await _unitOfWork.Tags.GetIdsByMultipleGuidsAsync(postEditModel.SelectedTags.ToList().ConvertAll(Guid.Parse)); + foreach (int tagId in tagIds) + { + post.PostTags.Add(new PostTag + { + PostId = post.Id, + TagId = tagId + }); + } + } + + var user = await GetCurrentUserAsync(); + post.PostApplicationUsers.Clear(); + post.PostApplicationUsers.Add(new PostApplicationUser + { + PostId = post.Id, + ApplicationUserId = user.Id + }); + } +} diff --git a/src/CmsEngine.Application/Services/Service.cs b/src/CmsEngine.Application/Services/Service.cs new file mode 100644 index 00000000..635a7ba0 --- /dev/null +++ b/src/CmsEngine.Application/Services/Service.cs @@ -0,0 +1,145 @@ +namespace CmsEngine.Application.Services; + +public class Service : IService +{ + private readonly IHttpContextAccessor httpContextAccessor; + private readonly IMemoryCache memoryCache; + private readonly string instanceHost; + private readonly string instanceKey; + + protected readonly IUnitOfWork unitOfWork; + protected readonly ILogger logger; + + public InstanceViewModel Instance { + get { + return GetInstance(); + } + } + public UserViewModel CurrentUser { + get { + return GetCurrentUserViewModelAsync().GetAwaiter().GetResult(); + } + } + + public Service(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + { + unitOfWork = uow ?? throw new ArgumentNullException(nameof(uow)); + httpContextAccessor = hca; + logger = loggerFactory.CreateLogger("Service"); + this.memoryCache = memoryCache; + + instanceHost = httpContextAccessor.HttpContext.Request.Host.Host; + instanceKey = $"{Main.CacheKey.Instance}_{instanceHost}"; + } + + internal async Task GetCurrentUserAsync() + { + logger.LogDebug("GetCurrentUserAsync() for {0}", httpContextAccessor.HttpContext.User.Identity.Name); + + try + { + return await unitOfWork.Users.FindByNameAsync(httpContextAccessor.HttpContext.User.Identity.Name); + } + catch (Exception ex) + { + logger.LogError(ex, "Error when trying to load CurrentUser"); + throw; + } + } + + protected void SaveInstanceToCache(object instance) + { + var timeSpan = TimeSpan.FromDays(7); //TODO: Perhaps set this in the config file. Or DB + logger.LogDebug("Adding '{0}' to cache with expiration date to {1}", instanceKey, DateTime.Now.AddMilliseconds(timeSpan.TotalMilliseconds).ToString()); + var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(timeSpan); + memoryCache.Set(instanceKey, instance, cacheEntryOptions); + } + + private InstanceViewModel GetInstance() + { + logger.LogDebug("GetInstanceAsync()"); + + Website website; + InstanceViewModel instance; + + try + { + logger.LogDebug("Loading '{0}' from cache", instanceKey); + if (!memoryCache.TryGetValue(instanceKey, out instance)) + { + logger.LogDebug("Empty cache for '{0}'. Loading instance from DB", instanceKey); + website = unitOfWork.Websites.GetWebsiteInstanceByHost(instanceHost); + + if (website == null) + { + throw new NotFoundException($"Instance for '{instanceHost}' not found"); + } + + instance = new InstanceViewModel + { + Id = website.Id, + Name = website.Name, + Description = website.Description, + Tagline = website.Tagline, + HeaderImage = website.HeaderImage, + Culture = website.Culture, + UrlFormat = website.UrlFormat, + DateFormat = website.DateFormat, + SiteUrl = website.SiteUrl, + ArticleLimit = website.ArticleLimit, + PageTitle = website.Name, + ContactDetails = new ContactDetailsViewModel + { + Address = website.Address, + Phone = website.Phone, + Email = website.Email, + }, + ApiDetails = new ApiDetailsViewModel + { + FacebookAppId = website.FacebookAppId, + FacebookApiVersion = website.FacebookApiVersion, + DisqusShortName = website.DisqusShortName + }, + SocialMedia = new SocialMediaViewModel + { + Facebook = website.Facebook, + Twitter = website.Twitter, + Instagram = website.Instagram, + LinkedIn = website.LinkedIn + }, + Google = new GoogleViewModel + { + GoogleAnalytics = website.GoogleAnalytics, + GoogleRecaptchaSiteKey = website.GoogleRecaptchaSiteKey, + GoogleRecaptchaSecretKey = website.GoogleRecaptchaSecretKey + } + }; + + SaveInstanceToCache(instance); + } + } + catch (Exception ex) + { + logger.LogError(ex, "Error when trying to load Instance"); + throw; + } + + return instance; + } + + private async Task GetCurrentUserViewModelAsync() + { + var user = await GetCurrentUserAsync(); + + return user == null + ? null + : new UserViewModel + { + VanityId = Guid.Parse(user.Id), + Name = user.Name, + Surname = user.Surname, + Email = user.Email, + UserName = user.UserName + }; + } +} diff --git a/src/CmsEngine.Application/Services/TagService.cs b/src/CmsEngine.Application/Services/TagService.cs new file mode 100644 index 00000000..3ffc88fc --- /dev/null +++ b/src/CmsEngine.Application/Services/TagService.cs @@ -0,0 +1,158 @@ +namespace CmsEngine.Application.Services; + +public class TagService : Service, ITagService +{ + private readonly IUnitOfWork _unitOfWork; + + public TagService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + } + + public async Task Delete(Guid id) + { + var item = await _unitOfWork.Tags.GetByIdAsync(id); + + var returnValue = new ReturnValue($"Tag '{item.Name}' deleted at {DateTime.Now.ToString("T")}."); + + try + { + _unitOfWork.Tags.Delete(item); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the tag"); + } + + return returnValue; + } + + public async Task DeleteRange(Guid[] ids) + { + var items = await _unitOfWork.Tags.GetByMultipleIdsAsync(ids); + + var returnValue = new ReturnValue($"Tags deleted at {DateTime.Now.ToString("T")}."); + + try + { + _unitOfWork.Tags.DeleteRange(items); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the tags"); + } + + return returnValue; + } + + public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(searchValue)) + { + var searchableProperties = typeof(TagTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); + items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); + } + return items; + } + + public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) + { + var items = await _unitOfWork.Tags.GetAllAsync(); + int recordsTotal = items.Count(); + if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) + { + items = FilterForDataTable(parameters.Search.Value, items); + } + items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); + return (items.MapToTableViewModel(), recordsTotal, items.Count()); + } + + public async Task> GetAllTags() + { + logger.LogDebug("TagService > GetAllTags()"); + var items = await _unitOfWork.Tags.GetAllAsync(); + logger.LogDebug("Tags loaded: {0}", items.Count()); + return items.MapToViewModel(); + } + + public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) + { + try + { + switch (column) + { + case 1: + items = direction == "asc" ? items.OrderBy(o => o.Name) : items.OrderByDescending(o => o.Name); + break; + case 2: + items = direction == "asc" ? items.OrderBy(o => o.Slug) : items.OrderByDescending(o => o.Slug); + break; + default: + items = items.OrderBy(o => o.Name); + break; + } + } + catch + { + throw; + } + + return items; + } + + public async Task Save(TagEditModel tagEditModel) + { + logger.LogDebug("CmsService > Save(TagEditModel: {0})", tagEditModel.ToString()); + + var returnValue = new ReturnValue($"Tag '{tagEditModel.Name}' saved."); + + try + { + if (tagEditModel.IsNew) + { + logger.LogDebug("New tag"); + var tag = tagEditModel.MapToModel(); + tag.WebsiteId = Instance.Id; + + await unitOfWork.Tags.Insert(tag); + } + else + { + logger.LogDebug("Update tag"); + var tag = tagEditModel.MapToModel(await unitOfWork.Tags.GetByIdAsync(tagEditModel.VanityId)); + tag.WebsiteId = Instance.Id; + + _unitOfWork.Tags.Update(tag); + } + + await _unitOfWork.Save(); + logger.LogDebug("Tag saved"); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while saving the tag"); + } + + return returnValue; + } + + public TagEditModel SetupEditModel() + { + logger.LogDebug("CmsService > SetupEditModel()"); + return new TagEditModel(); + } + + public async Task SetupEditModel(Guid id) + { + logger.LogDebug("CmsService > SetupTagEditModel(id: {0})", id); + var item = await _unitOfWork.Tags.GetByIdAsync(id); + logger.LogDebug("Tag: {0}", item.ToString()); + return item?.MapToEditModel(); + } +} diff --git a/src/CmsEngine.Application/Services/WebsiteService.cs b/src/CmsEngine.Application/Services/WebsiteService.cs new file mode 100644 index 00000000..c0ab61ea --- /dev/null +++ b/src/CmsEngine.Application/Services/WebsiteService.cs @@ -0,0 +1,155 @@ +namespace CmsEngine.Application.Services; + +public class WebsiteService : Service, IWebsiteService +{ + private readonly IUnitOfWork _unitOfWork; + private readonly IMemoryCache _memoryCache; + + public WebsiteService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + _memoryCache = memoryCache; + } + + public async Task Delete(Guid id) + { + var item = await _unitOfWork.Websites.GetByIdAsync(id); + + var returnValue = new ReturnValue($"Website '{item.Name}' deleted at {DateTime.Now.ToString("T")}."); + + try + { + _unitOfWork.Websites.Delete(item); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the website"); + } + + return returnValue; + } + + public async Task DeleteRange(Guid[] ids) + { + var items = await _unitOfWork.Websites.GetByMultipleIdsAsync(ids); + + var returnValue = new ReturnValue($"Websites deleted at {DateTime.Now.ToString("T")}."); + + try + { + _unitOfWork.Websites.DeleteRange(items); + await _unitOfWork.Save(); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while deleting the websites"); + } + + return returnValue; + } + + public IEnumerable FilterForDataTable(string searchValue, IEnumerable items) + { + if (!string.IsNullOrWhiteSpace(searchValue)) + { + var searchableProperties = typeof(WebsiteTableViewModel).GetProperties().Where(p => Attribute.IsDefined(p, typeof(Searchable))); + items = items.Where(items.GetSearchExpression(searchValue, searchableProperties).Compile()); + } + return items; + } + + public async Task<(IEnumerable Data, int RecordsTotal, int RecordsFiltered)> GetForDataTable(DataParameters parameters) + { + var items = await _unitOfWork.Websites.GetForDataTable(); + int recordsTotal = items.Count(); + if (!string.IsNullOrWhiteSpace(parameters.Search.Value)) + { + items = FilterForDataTable(parameters.Search.Value, items); + } + items = OrderForDataTable(parameters.Order[0].Column, parameters.Order[0].Dir, items); + return (items.MapToTableViewModel(), recordsTotal, items.Count()); + } + + public IEnumerable OrderForDataTable(int column, string direction, IEnumerable items) + { + try + { + switch (column) + { + case 1: + items = direction == "asc" ? items.OrderBy(o => o.Name) : items.OrderByDescending(o => o.Name); + break; + case 2: + items = direction == "asc" ? items.OrderBy(o => o.Tagline) : items.OrderByDescending(o => o.Tagline); + break; + case 3: + items = direction == "asc" ? items.OrderBy(o => o.Culture) : items.OrderByDescending(o => o.Culture); + break; + default: + items = items.OrderBy(o => o.Name); + break; + } + } + catch + { + throw; + } + + return items; + } + + public async Task Save(WebsiteEditModel websiteEditModel) + { + logger.LogDebug("CmsService > Save(WebsiteEditModel: {0})", websiteEditModel.ToString()); + + var returnValue = new ReturnValue($"Website '{websiteEditModel.Name}' saved."); + + try + { + if (websiteEditModel.IsNew) + { + logger.LogDebug("New website"); + var website = websiteEditModel.MapToModel(); + + await unitOfWork.Websites.Insert(website); + } + else + { + logger.LogDebug("Update website"); + var website = websiteEditModel.MapToModel(await unitOfWork.Websites.GetByIdAsync(websiteEditModel.VanityId)); + + _unitOfWork.Websites.Update(website); + } + + await _unitOfWork.Save(); + + SaveInstanceToCache(websiteEditModel); + logger.LogDebug("Website saved"); + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + returnValue.SetErrorMessage("An error has occurred while saving the website"); + } + + return returnValue; + } + + public WebsiteEditModel SetupEditModel() + { + logger.LogDebug("CmsService > SetupEditModel()"); + return new WebsiteEditModel(); + } + + public async Task SetupEditModel(Guid id) + { + logger.LogDebug("CmsService > SetupEditModel(id: {0})", id); + var item = await _unitOfWork.Websites.GetByIdAsync(id); + logger.LogDebug("Website: {0}", item.ToString()); + return item?.MapToEditModel(); + } +} diff --git a/src/CmsEngine.Application/Services/XmlService.cs b/src/CmsEngine.Application/Services/XmlService.cs new file mode 100644 index 00000000..cfe5ee06 --- /dev/null +++ b/src/CmsEngine.Application/Services/XmlService.cs @@ -0,0 +1,87 @@ +namespace CmsEngine.Application.Services; + +public class XmlService : Service, IXmlService +{ + private readonly IUnitOfWork _unitOfWork; + + public XmlService(IUnitOfWork uow, IHttpContextAccessor hca, ILoggerFactory loggerFactory, IMemoryCache memoryCache) + : base(uow, hca, loggerFactory, memoryCache) + { + _unitOfWork = uow; + } + + public async Task GenerateFeed() + { + var articleList = new List(); + + foreach (var item in await _unitOfWork.Posts.GetPublishedPostsOrderByDescending(o => o.PublishedOn)) + { + var url = FormatUrl("blog/post", item.Slug); + articleList.Add(new XElement("item", + new XElement("title", item.Title), + new XElement("link", url), + new XElement("description", item.DocumentContent), + new XElement("pubDate", item.PublishedOn.ToString("r")), + new XElement("guid", url))); + } + + return new XDocument(new XDeclaration("1.0", "utf-8", null), new XElement("rss", + new XElement("channel", + new XElement("title", Instance.Name), + new XElement("link", FormatUrl(string.Empty)), + new XElement("description", Instance.Tagline), + new XElement("language", Instance.Culture.ToLowerInvariant()), + new XElement("generator", "MultiCMS"), + articleList + ), + new XAttribute("version", "2.0"))); + } + + public async Task GenerateSitemap() + { + var items = new List(); + + var orderedPosts = await _unitOfWork.Posts.GetPublishedPostsOrderByDescending(o => o.PublishedOn); + items.AddRange(orderedPosts.Select(x => new SitemapViewModel + { + Url = FormatUrl("blog/post", x.Slug), + PublishedOn = x.PublishedOn.ToString("yyyy-MM-dd") + })); + + var orderedPages = await _unitOfWork.Pages.GetOrderByDescending(o => o.PublishedOn); + items.AddRange(orderedPages.Select(x => new SitemapViewModel + { + Url = FormatUrl("page", x.Slug), + PublishedOn = x.PublishedOn.ToString("yyyy-MM-dd") + })); + + XNamespace ns = "http://www.sitemaps.org/schemas/sitemap/0.9"; + return new XDocument(new XDeclaration("1.0", "utf-8", null), + new XElement(ns + "urlset", + from item in items + select new XElement(ns + "url", + new XElement(ns + "loc", item.Url), + new XElement(ns + "lastmod", item.PublishedOn), + new XElement(ns + "changefreq", "monthly") + ))); + + } + + private string FormatUrl(string type, string slug = "") + { + var url = ""; + + if (!string.IsNullOrWhiteSpace(Instance.UrlFormat)) + { + url = Instance.UrlFormat.Replace("[site_url]", Instance.SiteUrl) + .Replace("[culture]", Instance.Culture) + .Replace("[short_culture]", Instance.Culture.Substring(0, 2)) + .Replace("[type]", type) + .Replace("[slug]", slug); + } + + url = url.EndsWith("/") ? url.Substring(0, url.LastIndexOf('/')) : url; + + return url; + } +} diff --git a/src/CmsEngine.Core/CmsEngine.Core.csproj b/src/CmsEngine.Core/CmsEngine.Core.csproj new file mode 100644 index 00000000..933c8b28 --- /dev/null +++ b/src/CmsEngine.Core/CmsEngine.Core.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/src/CmsEngine.Core/Constants/ContentTypeOptions.cs b/src/CmsEngine.Core/Constants/ContentTypeOptions.cs new file mode 100644 index 00000000..1130878e --- /dev/null +++ b/src/CmsEngine.Core/Constants/ContentTypeOptions.cs @@ -0,0 +1,17 @@ +namespace CmsEngine.Core.Constants; + +/// +/// X-Content-Type-Options-related constants. +/// +public static class ContentTypeOptions +{ + /// + /// Header value for X-Content-Type-Options + /// + public const string Header = "X-Content-Type-Options"; + + /// + /// Disables content sniffing + /// + public const string NoSniff = "nosniff"; +} diff --git a/src/CmsEngine.Core/Constants/FrameOptions.cs b/src/CmsEngine.Core/Constants/FrameOptions.cs new file mode 100644 index 00000000..7b4c1868 --- /dev/null +++ b/src/CmsEngine.Core/Constants/FrameOptions.cs @@ -0,0 +1,27 @@ +namespace CmsEngine.Core.Constants; + +/// +/// X-Frame-Options-related constants. +/// +public static class FrameOptions +{ + /// + /// The header value for X-Frame-Options + /// + public const string Header = "X-Frame-Options"; + + /// + /// The page cannot be displayed in a frame, regardless of the site attempting to do so. + /// + public const string Deny = "DENY"; + + /// + /// The page can only be displayed in a frame on the same origin as the page itself. + /// + public const string SameOrigin = "SAMEORIGIN"; + + /// + /// The page can only be displayed in a frame on the specified origin. {0} specifies the format string + /// + public const string AllowFromUri = "ALLOW-FROM {0}"; +} diff --git a/src/CmsEngine.Core/Constants/Main.cs b/src/CmsEngine.Core/Constants/Main.cs new file mode 100644 index 00000000..28cacbd2 --- /dev/null +++ b/src/CmsEngine.Core/Constants/Main.cs @@ -0,0 +1,26 @@ +namespace CmsEngine.Core.Constants; + +public static class Main +{ + public const string SiteUrl = "[site_url]"; + public const string Culture = "[culture]"; + public const string ShortCulture = "[short_culture]"; + public const string Type = "[type]"; + public const string Slug = "[slug]"; + + public const string WwwDot = "www."; + public const string Localhost = "localhost"; + + public static class ImagePath + { + public const string Default = "/image/{0}/{1}"; + public const string Path640 = "/image/{0}/640x426_{1}"; + public const string Path320 = "/image/{0}/320x213_{1}"; + public const string Path120 = "/image/{0}/120x120_{1}"; + } + + public static class CacheKey + { + public const string Instance = "Instance"; + } +} diff --git a/src/CmsEngine.Core/Constants/Message.cs b/src/CmsEngine.Core/Constants/Message.cs new file mode 100644 index 00000000..90e463b2 --- /dev/null +++ b/src/CmsEngine.Core/Constants/Message.cs @@ -0,0 +1,9 @@ +namespace CmsEngine.Core.Constants; + +public static class MessageConstants +{ + public const string InfoMessage = "InfoMessage"; + public const string SuccessMessage = "SuccessMessage"; + public const string WarningMessage = "WarningMessage"; + public const string DangerMessage = "DangerMessage"; +} diff --git a/src/CmsEngine.Core/Constants/Server.cs b/src/CmsEngine.Core/Constants/Server.cs new file mode 100644 index 00000000..efae66c2 --- /dev/null +++ b/src/CmsEngine.Core/Constants/Server.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Core.Constants; + +/// +/// Server headery-related constants. +/// +public static class Server +{ + /// + /// The header value for X-Powered-By + /// + public const string Header = "Server"; +} diff --git a/src/CmsEngine.Core/Constants/StrictTransportSecurity.cs b/src/CmsEngine.Core/Constants/StrictTransportSecurity.cs new file mode 100644 index 00000000..f966ef2d --- /dev/null +++ b/src/CmsEngine.Core/Constants/StrictTransportSecurity.cs @@ -0,0 +1,27 @@ +namespace CmsEngine.Core.Constants; + +/// +/// Strict-Transport-Security-related constants. +/// +public static class StrictTransportSecurity +{ + /// + /// Header value for Strict-Transport-Security + /// + public const string Header = "Strict-Transport-Security"; + + /// + /// Tells the user-agent to cache the domain in the STS list for the provided number of seconds {0} + /// + public const string MaxAge = "max-age={0}"; + + /// + /// Tells the user-agent to cache the domain in the STS list for the provided number of seconds {0} and include any sub-domains. + /// + public const string MaxAgeIncludeSubdomains = "max-age={0}; includeSubDomains"; + + /// + /// Tells the user-agent to remove, or not cache the host in the STS cache. + /// + public const string NoCache = "max-age=0"; +} diff --git a/src/CmsEngine.Core/Constants/XssProtection.cs b/src/CmsEngine.Core/Constants/XssProtection.cs new file mode 100644 index 00000000..8de4a765 --- /dev/null +++ b/src/CmsEngine.Core/Constants/XssProtection.cs @@ -0,0 +1,33 @@ +namespace CmsEngine.Core.Constants; + +/// +/// X-XSS-Protection-related constants. +/// +public static class XssProtection +{ + /// + /// Header value for X-XSS-Protection + /// + public const string Header = "X-XSS-Protection"; + + /// + /// Enables the XSS Protections + /// + public const string Enabled = "1"; + + /// + /// Disables the XSS Protections offered by the user-agent. + /// + public const string Disabled = "0"; + + /// + /// Enables XSS protections and instructs the user-agent to block the response in the event that script has been inserted from user input, instead of sanitizing. + /// + public const string Block = "1; mode=block"; + + /// + /// A partially supported directive that tells the user-agent to report potential XSS attacks to a single URL. Data will be POST'd to the report URL in JSON format. + /// {0} specifies the report url, including protocol + /// + public const string Report = "1; report={0}"; +} diff --git a/src/CmsEngine.Core/Exceptions/EmailException.cs b/src/CmsEngine.Core/Exceptions/EmailException.cs new file mode 100644 index 00000000..35e2dac3 --- /dev/null +++ b/src/CmsEngine.Core/Exceptions/EmailException.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Core.Exceptions; + +public class EmailException : Exception +{ + public EmailException(string message) : base(message) + { + } + + public EmailException(string message, Exception innerException) : base(message, innerException) + { + } +} diff --git a/src/CmsEngine.Core/Exceptions/NotFoundException.cs b/src/CmsEngine.Core/Exceptions/NotFoundException.cs new file mode 100644 index 00000000..f6452ff0 --- /dev/null +++ b/src/CmsEngine.Core/Exceptions/NotFoundException.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Core.Exceptions; + +public class NotFoundException : Exception +{ + public NotFoundException(string message) : base(message) + { + } + + public NotFoundException(string message, Exception innerException) : base(message, innerException) + { + } +} diff --git a/src/CmsEngine.Core/Extensions/BooleanExtensions.cs b/src/CmsEngine.Core/Extensions/BooleanExtensions.cs new file mode 100644 index 00000000..bab81780 --- /dev/null +++ b/src/CmsEngine.Core/Extensions/BooleanExtensions.cs @@ -0,0 +1,14 @@ +namespace CmsEngine.Core.Extensions; + +public static class BooleanExtensions +{ + /// + /// Convert boolean values to "Yes" or "No" + /// + /// + /// + public static string ToYesNo(this bool value) + { + return value ? "Yes" : "No"; + } +} diff --git a/src/CmsEngine.Core/Extensions/EnumExtensions.cs b/src/CmsEngine.Core/Extensions/EnumExtensions.cs new file mode 100644 index 00000000..3c97e858 --- /dev/null +++ b/src/CmsEngine.Core/Extensions/EnumExtensions.cs @@ -0,0 +1,52 @@ +namespace CmsEngine.Core.Extensions; + +public static class EnumExtensions +{ + /// + /// Returns the Name property from DisplayAttribute + /// + /// + /// + public static string GetName(this Enum value) + { + // Get attributes + var field = value.GetType().GetField(value.ToString()); + var attributes = field.GetCustomAttributes(false); + + dynamic? displayAttribute = null; + + if (attributes.Any()) + { + displayAttribute = attributes.ElementAt(0); + } + + // Return name + return displayAttribute?.Name ?? field.Name; + } + + /// + /// Returns the value of a DescriptionAttribute for a given Enum value + /// + /// Source: http://blogs.msdn.com/b/abhinaba/archive/2005/10/21/483337.aspx + /// + /// + public static string GetDescription(this Enum value) + { + + var type = value.GetType(); + var memberInfo = type.GetMember(value.ToString()); + + if (memberInfo != null && memberInfo.Length > 0) + { + var attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false).ToArray(); + + if (attrs != null && attrs.Length > 0) + { + return ((DescriptionAttribute)attrs[0]).Description; + } + } + + return value.ToString(); + + } +} diff --git a/src/CmsEngine.Core/Extensions/IEnumerableExtension.cs b/src/CmsEngine.Core/Extensions/IEnumerableExtension.cs new file mode 100644 index 00000000..4ab0c6f7 --- /dev/null +++ b/src/CmsEngine.Core/Extensions/IEnumerableExtension.cs @@ -0,0 +1,18 @@ +namespace CmsEngine.Core.Extensions; + +public static class IEnumerableExtensions +{ + public static IEnumerable Except(this IEnumerable items, IEnumerable other, Func getKeyFunc) + { + if (other == null) + { + return items; + } + + return items + .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems }) + .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp }) + .Where(t => t.temp == null || t.temp.Equals(default(T))) + .Select(t => t.t.item); + } +} diff --git a/src/CmsEngine.Core/Extensions/StringExtensions.cs b/src/CmsEngine.Core/Extensions/StringExtensions.cs new file mode 100644 index 00000000..18e39ead --- /dev/null +++ b/src/CmsEngine.Core/Extensions/StringExtensions.cs @@ -0,0 +1,9 @@ +namespace CmsEngine.Core.Extensions; + +public static class StringExtensions +{ + public static T ToEnum(this string value) + { + return (T)Enum.Parse(typeof(T), value, true); + } +} diff --git a/src/CmsEngine.Core/GlobalUsings.cs b/src/CmsEngine.Core/GlobalUsings.cs new file mode 100644 index 00000000..68c23420 --- /dev/null +++ b/src/CmsEngine.Core/GlobalUsings.cs @@ -0,0 +1,7 @@ +global using System.ComponentModel; +global using System.ComponentModel.DataAnnotations; +global using System.Reflection; +global using System.Security.Cryptography; +global using System.Text; +global using Newtonsoft.Json; +global using Newtonsoft.Json.Serialization; diff --git a/src/CmsEngine.Core/Utils/CustomResolver.cs b/src/CmsEngine.Core/Utils/CustomResolver.cs new file mode 100644 index 00000000..34d48bbb --- /dev/null +++ b/src/CmsEngine.Core/Utils/CustomResolver.cs @@ -0,0 +1,18 @@ +namespace CmsEngine.Core.Utils; + +public sealed class CustomResolver : DefaultContractResolver +{ + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + var prop = base.CreateProperty(member, memberSerialization); + var propInfo = member as PropertyInfo; + if (propInfo != null) + { + if (propInfo.GetMethod.IsVirtual && !propInfo.GetMethod.IsFinal) + { + prop.ShouldSerialize = obj => false; + } + } + return prop; + } +} diff --git a/src/CmsEngine.Core/Utils/Enums.cs b/src/CmsEngine.Core/Utils/Enums.cs new file mode 100644 index 00000000..6fa1c608 --- /dev/null +++ b/src/CmsEngine.Core/Utils/Enums.cs @@ -0,0 +1,130 @@ +namespace CmsEngine.Core.Utils; + +public enum Gender +{ + Male, + Female +} + +public enum Operation +{ + Coalesce, + Equals, + NotEqual, + GreaterThan, + LessThan, + GreaterThanOrEqual, + LessThanOrEqual, + Contains, + NotContain, + StartsWith, + EndsWith +} + +public enum LogicalOperator +{ + And, + AndAlso, + Or, + OrElse +} + +public enum SignInStatus +{ + Success, + LockedOut, + RequiresVerification, + Failure +} + +/// +/// Represents the status of the documents created in the website +/// +public enum DocumentStatus +{ + Published = 0, + + [Display(Name = "Pending approval")] + PendingApproval = 1, + + Draft = 2 +} + +/// +/// Represents the general status of an object +/// It's analogous to Bootstrap status colors +/// +public enum GeneralStatus +{ + Primary, + Secondary, + Success, + Danger, + Warning, + Info, + Light, + Dark +} + +public enum PageType +{ + Create, + Edit, + List +} + +/// +/// In addition to allowing you to use your own image, Gravatar has a number of built in options which you can also use as defaults. Most of these work by taking the requested email hash and using it to generate a themed image that is unique to that email address +/// +public enum DefaultImage +{ + /// Default Gravatar logo + [Description("")] + Default, + + /// 404 - do not load any image if none is associated with the email hash, instead return an HTTP 404 (File Not Found) response + [Description("404")] + Http404, + + /// Mystery-Man - a simple, cartoon-style silhouetted outline of a person (does not vary by email hash) + [Description("mm")] + MysteryMan, + + /// Identicon - a geometric pattern based on an email hash + [Description("identicon")] + Identicon, + + /// MonsterId - a generated 'monster' with different colors, faces, etc + [Description("monsterid")] + MonsterId, + + /// Wavatar - generated faces with differing features and backgrounds + [Description("wavatar")] + Wavatar, + + /// Retro - awesome generated, 8-bit arcade-style pixelated faces + [Description("retro")] + Retro +} + +/// +/// Gravatar allows users to self-rate their images so that they can indicate if an image is appropriate for a certain audience. By default, only 'G' rated images are displayed unless you indicate that you would like to see higher ratings +/// +public enum Rating +{ + /// Suitable for display on all websites with any audience type + [Description("g")] + G, + + /// May contain rude gestures, provocatively dressed individuals, the lesser swear words, or mild violence + [Description("pg")] + PG, + + /// May contain such things as harsh profanity, intense violence, nudity, or hard drug use + [Description("r")] + R, + + /// May contain hardcore sexual imagery or extremely disturbing violence + [Description("x")] + X +} diff --git a/src/CmsEngine.Core/Utils/GravatarUtilities.cs b/src/CmsEngine.Core/Utils/GravatarUtilities.cs new file mode 100644 index 00000000..5fe60031 --- /dev/null +++ b/src/CmsEngine.Core/Utils/GravatarUtilities.cs @@ -0,0 +1,29 @@ +namespace CmsEngine.Core.Utils; + +public static class GravatarUtilities +{ + /// + /// Generates an MD5 hash of the given string + /// + /// Source: http://msdn.microsoft.com/en-us/library/system.security.cryptography.md5.aspx + public static string GetMd5Hash(string input) + { + + // Convert the input string to a byte array and compute the hash. + var data = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(input)); + + // Create a new Stringbuilder to collect the bytes + // and create a string. + var sBuilder = new StringBuilder(); + + // Loop through each byte of the hashed data + // and format each one as a hexadecimal string. + for (var i = 0; i < data.Length; i++) + { + sBuilder.Append(data[i].ToString("x2")); + } + + // Return the hexadecimal string. + return sBuilder.ToString(); + } +} diff --git a/src/CmsEngine.Core/Utils/PaginatedList.cs b/src/CmsEngine.Core/Utils/PaginatedList.cs new file mode 100644 index 00000000..205b1353 --- /dev/null +++ b/src/CmsEngine.Core/Utils/PaginatedList.cs @@ -0,0 +1,27 @@ +namespace CmsEngine.Core.Utils; + +public class PaginatedList : List +{ + public int PageIndex { get; private set; } + public int TotalPages { get; private set; } + + public PaginatedList(IEnumerable items, int count, int pageIndex, int pageSize) + { + PageIndex = pageIndex; + TotalPages = (int)Math.Ceiling(count / (double)pageSize); + + AddRange(items); + } + + public bool HasPreviousPage { + get { + return PageIndex > 1; + } + } + + public bool HasNextPage { + get { + return PageIndex < TotalPages; + } + } +} diff --git a/src/CmsEngine.Core/Utils/ReturnValue.cs b/src/CmsEngine.Core/Utils/ReturnValue.cs new file mode 100644 index 00000000..14e4898f --- /dev/null +++ b/src/CmsEngine.Core/Utils/ReturnValue.cs @@ -0,0 +1,29 @@ +namespace CmsEngine.Core.Utils; + +public class ReturnValue +{ + public ReturnValue(string message, bool isError = false) + { + Message = message; + IsError = IsError; + } + + [JsonProperty(PropertyName = "message")] + public string Message { get; private set; } + + [JsonProperty(PropertyName = "isError")] + public bool IsError { get; private set; } + + [JsonProperty(PropertyName = "exception")] + public string Exception { get; private set; } + + [JsonProperty(PropertyName = "value")] + public object Value { get; set; } + + public void SetErrorMessage(string message, string exception = "") + { + Message = message; + Exception = exception; + IsError = true; + } +} diff --git a/src/CmsEngine.Core/Utils/TinyMceUploadResult.cs b/src/CmsEngine.Core/Utils/TinyMceUploadResult.cs new file mode 100644 index 00000000..09b43ac9 --- /dev/null +++ b/src/CmsEngine.Core/Utils/TinyMceUploadResult.cs @@ -0,0 +1,6 @@ +namespace CmsEngine.Core.Utils; + +public class TinyMceUploadResult +{ + public string Location { get; set; } +} diff --git a/src/CmsEngine.Core/Utils/UploadFilesResult.cs b/src/CmsEngine.Core/Utils/UploadFilesResult.cs new file mode 100644 index 00000000..f2407551 --- /dev/null +++ b/src/CmsEngine.Core/Utils/UploadFilesResult.cs @@ -0,0 +1,12 @@ +namespace CmsEngine.Core.Utils; + +public struct UploadFilesResult +{ + public string FileName { get; set; } + public string ThumbnailName { get; set; } + public string Path { get; set; } + public long Length { get; set; } + public string Size { get; set; } + public string ContentType { get; set; } + public bool IsImage { get; set; } +} diff --git a/CmsEngine.Data/CmsEngine.Data.csproj b/src/CmsEngine.Data/CmsEngine.Data.csproj similarity index 53% rename from CmsEngine.Data/CmsEngine.Data.csproj rename to src/CmsEngine.Data/CmsEngine.Data.csproj index 42f5bec1..88a55253 100644 --- a/CmsEngine.Data/CmsEngine.Data.csproj +++ b/src/CmsEngine.Data/CmsEngine.Data.csproj @@ -1,22 +1,21 @@ - net5.0 + net6.0 + enable + enable - - - - + + + + + - - - - diff --git a/src/CmsEngine.Data/CmsEngineContext.cs b/src/CmsEngine.Data/CmsEngineContext.cs new file mode 100644 index 00000000..167ebc01 --- /dev/null +++ b/src/CmsEngine.Data/CmsEngineContext.cs @@ -0,0 +1,63 @@ +namespace CmsEngine.Data; + +public class CmsEngineContext : IdentityDbContext +{ + private readonly IHttpContextAccessor httpContextAccessor; + + public DbSet Websites { get; set; } + public DbSet Pages { get; set; } + public DbSet Posts { get; set; } + public DbSet Tags { get; set; } + public DbSet Categories { get; set; } + public DbSet Emails { get; set; } + + public CmsEngineContext(DbContextOptions options, IHttpContextAccessor hca) : base(options) + { + //Database.SetInitializer(new CmsEngineInitializer()); + + ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; + httpContextAccessor = hca; + } + + protected override void OnModelCreating(ModelBuilder builder) + { + //modelBuilder.Conventions.Remove(); + + // Model configuration + builder.Entity(ModelConfiguration.ConfigureWebsite); + builder.Entity(ModelConfiguration.ConfigurePage); + builder.Entity(ModelConfiguration.ConfigurePost); + builder.Entity(ModelConfiguration.ConfigureTag); + builder.Entity(ModelConfiguration.ConfigureCategory); + builder.Entity(ModelConfiguration.ConfigureEmail); + builder.Entity(ModelConfiguration.ConfigurePostCategory); + builder.Entity(ModelConfiguration.ConfigurePostTag); + builder.Entity(ModelConfiguration.ConfigurePostApplicationUser); + builder.Entity(ModelConfiguration.ConfigurePageApplicationUser); + + base.OnModelCreating(builder); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + ChangeTracker.DetectChanges(); + + var timeStamp = DateTime.Now; + var entries = ChangeTracker.Entries().Where(e => e.Entity is BaseEntity && (e.State == EntityState.Added || e.State == EntityState.Modified)); + // TODO: Find a better way to get the user + var currentUsername = httpContextAccessor.HttpContext.User.Identity.Name; + foreach (var entry in entries) + { + if (entry.State == EntityState.Added) + { + entry.Property("DateCreated").CurrentValue = timeStamp; + entry.Property("UserCreated").CurrentValue = currentUsername; + } + + entry.Property("DateModified").CurrentValue = timeStamp; + entry.Property("UserModified").CurrentValue = currentUsername; + } + + return base.SaveChangesAsync(cancellationToken); + } +} diff --git a/src/CmsEngine.Data/Entities/ApplicationUser.cs b/src/CmsEngine.Data/Entities/ApplicationUser.cs new file mode 100644 index 00000000..d0848eb3 --- /dev/null +++ b/src/CmsEngine.Data/Entities/ApplicationUser.cs @@ -0,0 +1,11 @@ +namespace CmsEngine.Data.Entities; + +// Add profile data for application users by adding properties to the ApplicationUser class +public class ApplicationUser : IdentityUser +{ + public string Name { get; set; } + public string Surname { get; set; } + + public virtual ICollection PostApplicationUsers { get; set; } + public virtual ICollection PageApplicationUsers { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/BaseEntity.cs b/src/CmsEngine.Data/Entities/BaseEntity.cs new file mode 100644 index 00000000..3191079e --- /dev/null +++ b/src/CmsEngine.Data/Entities/BaseEntity.cs @@ -0,0 +1,27 @@ +namespace CmsEngine.Data.Entities; + +public class BaseEntity +{ + [NotMapped] + public bool IsNew { + get { + return Id == 0 && VanityId == Guid.Empty; + } + } + + public bool IsDeleted { get; set; } + + [Key] + public int Id { get; set; } + + public Guid VanityId { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Data/Entities/Category.cs b/src/CmsEngine.Data/Entities/Category.cs new file mode 100644 index 00000000..b38f7e58 --- /dev/null +++ b/src/CmsEngine.Data/Entities/Category.cs @@ -0,0 +1,32 @@ +namespace CmsEngine.Data.Entities; + +public class Category : BaseEntity +{ + public int? WebsiteId { get; set; } + public virtual Website? Website { get; set; } + + public virtual ICollection? PostCategories { get; set; } + + public string Name { get; set; } + public string Slug { get; set; } + public string? Description { get; set; } + + // Properties for data projection only + [NotMapped] + public IEnumerable Posts { get; set; } = new List(); + + [NotMapped] + public int PostCount { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId), + new JProperty("Name", Name), + new JProperty("Slug", Slug), + new JProperty("Description", Description) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Data/Entities/Document.cs b/src/CmsEngine.Data/Entities/Document.cs new file mode 100644 index 00000000..0ed1691e --- /dev/null +++ b/src/CmsEngine.Data/Entities/Document.cs @@ -0,0 +1,29 @@ +namespace CmsEngine.Data.Entities; + +public abstract class Document : BaseEntity +{ + public string Title { get; set; } + public string Slug { get; set; } + public string? HeaderImage { get; set; } + + public string Description { get; set; } + public string DocumentContent { get; set; } + public DocumentStatus Status { get; set; } = DocumentStatus.Draft; + + public DateTime PublishedOn { get; set; } = DateTime.Now; + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId), + new JProperty("Title", Title), + new JProperty("Slug", Slug), + new JProperty("HeaderImage", HeaderImage), + new JProperty("Description", Description), + new JProperty("Status", Status.ToString()), + new JProperty("PublishedOn", PublishedOn) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Data/Entities/Email.cs b/src/CmsEngine.Data/Entities/Email.cs new file mode 100644 index 00000000..86bfe790 --- /dev/null +++ b/src/CmsEngine.Data/Entities/Email.cs @@ -0,0 +1,19 @@ +namespace CmsEngine.Data.Entities; + +public class Email : BaseEntity +{ + public string? From { get; set; } + public string Subject { get; set; } + public string Message { get; set; } + public DateTime? DateReceived { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("From", From), + new JProperty("Subject", Subject), + new JProperty("Message", Message) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Data/Entities/IModel.cs b/src/CmsEngine.Data/Entities/IModel.cs new file mode 100644 index 00000000..ca8f4d22 --- /dev/null +++ b/src/CmsEngine.Data/Entities/IModel.cs @@ -0,0 +1,16 @@ +namespace CmsEngine.Data.Entities; + +public interface IModel +{ + bool IsNew { get; } + bool IsDeleted { get; set; } + + int Id { get; set; } + Guid VanityId { get; set; } + + DateTime DateCreated { get; set; } + DateTime DateModified { get; set; } + + string UserCreated { get; set; } + string UserModified { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/ModelConfiguration.cs b/src/CmsEngine.Data/Entities/ModelConfiguration.cs new file mode 100644 index 00000000..a02a460b --- /dev/null +++ b/src/CmsEngine.Data/Entities/ModelConfiguration.cs @@ -0,0 +1,255 @@ +namespace CmsEngine.Data.Entities; + +public static class ModelConfiguration +{ + public static void ConfigureWebsite(EntityTypeBuilder b) + { + // Fields + b.HasKey(model => model.Id); + + b.Property(model => model.Name) + .HasMaxLength(200) + .IsRequired(); + + b.Property(model => model.Tagline) + .HasMaxLength(200); + + b.Property(model => model.Culture) + .HasMaxLength(5) + .IsRequired(); + + b.Property(model => model.UrlFormat) + .HasMaxLength(100) + .IsRequired(); + + b.Property(model => model.DateFormat) + .HasMaxLength(10) + .IsRequired(); + + b.Property(model => model.SiteUrl) + .HasMaxLength(250) + .IsRequired(); + + b.Property(model => model.ArticleLimit) + .IsRequired(); + + b.Property(model => model.Address) + .HasMaxLength(250); + + b.Property(model => model.Phone) + .HasMaxLength(20); + + b.Property(model => model.Email) + .HasMaxLength(250); + + b.Property(model => model.Facebook) + .HasMaxLength(20); + + b.Property(model => model.Twitter) + .HasMaxLength(20); + + b.Property(model => model.Instagram) + .HasMaxLength(20); + + b.Property(model => model.LinkedIn) + .HasMaxLength(20); + + b.Property(model => model.FacebookAppId) + .HasMaxLength(30); + + b.Property(model => model.FacebookApiVersion) + .HasMaxLength(10); + + b.Property(model => model.VanityId) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("newid()"); + + AddPropertiesForAuditing(b); + + // Relationships + b.HasMany(model => model.Posts); + b.HasMany(model => model.Pages); + b.HasMany(model => model.Tags); + b.HasMany(model => model.Categories); + } + + public static void ConfigurePage(EntityTypeBuilder b) + { + // Fields + b.HasKey(model => model.Id); + + b.Property(model => model.Title) + .HasMaxLength(100) + .IsRequired(); + + b.Property(model => model.Slug) + .HasMaxLength(100) + .IsRequired(); + + b.Property(model => model.Description) + .HasMaxLength(150) + .IsRequired(); + + //b.Property(model => model.DocumentContent) + // .IsRequired(); + + b.Property(model => model.PublishedOn) + .IsRequired(); + + b.Property(model => model.VanityId) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("newid()"); + + AddPropertiesForAuditing(b); + + // Relationships + b.HasOne(model => model.Website) + .WithMany(model => model.Pages); + } + + public static void ConfigurePost(EntityTypeBuilder b) + { + // Fields + b.HasKey(model => model.Id); + + b.Property(model => model.Title) + .HasMaxLength(100) + .IsRequired(); + + b.Property(model => model.Slug) + .HasMaxLength(100) + .IsRequired(); + + b.Property(model => model.Description) + .HasMaxLength(150) + .IsRequired(); + + //b.Property(model => model.DocumentContent) + // .IsRequired(); + + + b.Property(model => model.PublishedOn) + .IsRequired(); + + b.Property(model => model.VanityId) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("newid()"); + + AddPropertiesForAuditing(b); + + // Relationships + b.HasOne(model => model.Website) + .WithMany(model => model.Posts); + } + + public static void ConfigureCategory(EntityTypeBuilder b) + { + // Fields + b.HasKey(model => model.Id); + + b.Property(model => model.Name) + .HasMaxLength(35) + .IsRequired(); + + b.Property(model => model.Slug) + .HasMaxLength(35) + .IsRequired(); + + b.Property(model => model.Description) + .HasMaxLength(200); + + b.Property(model => model.VanityId) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("newid()"); + + AddPropertiesForAuditing(b); + } + + public static void ConfigureTag(EntityTypeBuilder b) + { + // Fields + b.HasKey(model => model.Id); + b.Property(model => model.Name) + .HasMaxLength(25) + .IsRequired(); + b.Property(model => model.Slug) + .HasMaxLength(25) + .IsRequired(); + b.Property(model => model.VanityId) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("newid()"); + + AddPropertiesForAuditing(b); + } + + public static void ConfigureEmail(EntityTypeBuilder b) + { + // Fields + b.HasKey(model => model.Id); + b.Property(model => model.Subject) + .HasMaxLength(150) + .IsRequired(); + b.Property(model => model.Message) + .HasMaxLength(500) + .IsRequired(); + b.Property(model => model.VanityId) + .ValueGeneratedOnAdd() + .HasDefaultValueSql("newid()"); + + AddPropertiesForAuditing(b); + } + + // Many to many + + public static void ConfigurePostCategory(EntityTypeBuilder b) + { + b.HasKey(model => new { model.PostId, model.CategoryId }); + } + + public static void ConfigurePostTag(EntityTypeBuilder b) + { + b.HasKey(model => new { model.PostId, model.TagId }); + + b.HasOne(model => model.Post) + .WithMany(p => p.PostTags) + .HasForeignKey(model => model.PostId); + + b.HasOne(model => model.Tag) + .WithMany(c => c.PostTags) + .HasForeignKey(model => model.TagId); + } + + public static void ConfigurePostApplicationUser(EntityTypeBuilder b) + { + b.HasKey(model => new { model.PostId, model.ApplicationUserId }); + + b.HasOne(model => model.Post) + .WithMany(p => p.PostApplicationUsers) + .HasForeignKey(model => model.PostId); + + b.HasOne(model => model.ApplicationUser) + .WithMany(c => c.PostApplicationUsers) + .HasForeignKey(model => model.ApplicationUserId); + } + + public static void ConfigurePageApplicationUser(EntityTypeBuilder b) + { + b.HasKey(model => new { model.PageId, model.ApplicationUserId }); + + b.HasOne(model => model.Page) + .WithMany(p => p.PageApplicationUsers) + .HasForeignKey(model => model.PageId); + + b.HasOne(model => model.ApplicationUser) + .WithMany(c => c.PageApplicationUsers) + .HasForeignKey(model => model.ApplicationUserId); + } + + private static void AddPropertiesForAuditing(EntityTypeBuilder b) where T : BaseEntity + { + b.Property("DateCreated"); + b.Property("DateModified"); + b.Property("UserCreated").HasMaxLength(50); + b.Property("UserModified").HasMaxLength(50); + } +} diff --git a/src/CmsEngine.Data/Entities/Page.cs b/src/CmsEngine.Data/Entities/Page.cs new file mode 100644 index 00000000..c05e1700 --- /dev/null +++ b/src/CmsEngine.Data/Entities/Page.cs @@ -0,0 +1,19 @@ +namespace CmsEngine.Data.Entities; + +public class Page : Document +{ + public int WebsiteId { get; set; } + public virtual Website Website { get; set; } + public virtual ICollection PageApplicationUsers { get; set; } + + // Property used for data projection only + [NotMapped] + public IEnumerable ApplicationUsers { get; set; } + + public Page() + { + PageApplicationUsers = new List(); + + ApplicationUsers = new List(); + } +} diff --git a/src/CmsEngine.Data/Entities/PageApplicationUser.cs b/src/CmsEngine.Data/Entities/PageApplicationUser.cs new file mode 100644 index 00000000..c2625aac --- /dev/null +++ b/src/CmsEngine.Data/Entities/PageApplicationUser.cs @@ -0,0 +1,11 @@ +namespace CmsEngine.Data.Entities; + +[Table("PageAspNetUser")] +public class PageApplicationUser +{ + public int PageId { get; set; } + public virtual Page Page { get; set; } + + public string ApplicationUserId { get; set; } + public virtual ApplicationUser ApplicationUser { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/Post.cs b/src/CmsEngine.Data/Entities/Post.cs new file mode 100644 index 00000000..1d4f510f --- /dev/null +++ b/src/CmsEngine.Data/Entities/Post.cs @@ -0,0 +1,18 @@ +namespace CmsEngine.Data.Entities; + +public class Post : Document +{ + public int WebsiteId { get; set; } + public virtual Website Website { get; set; } + public virtual ICollection PostCategories { get; set; } = new List(); + public virtual ICollection PostTags { get; set; } = new List(); + public virtual ICollection PostApplicationUsers { get; set; } = new List(); + + // Properties used for data projection only + [NotMapped] + public IEnumerable Categories { get; set; } = new List(); + [NotMapped] + public IEnumerable Tags { get; set; } = new List(); + [NotMapped] + public IEnumerable ApplicationUsers { get; set; } = new List(); +} diff --git a/src/CmsEngine.Data/Entities/PostApplicationUser.cs b/src/CmsEngine.Data/Entities/PostApplicationUser.cs new file mode 100644 index 00000000..576e4432 --- /dev/null +++ b/src/CmsEngine.Data/Entities/PostApplicationUser.cs @@ -0,0 +1,11 @@ +namespace CmsEngine.Data.Entities; + +[Table("PostAspNetUser")] +public class PostApplicationUser +{ + public int PostId { get; set; } + public virtual Post Post { get; set; } + + public string ApplicationUserId { get; set; } + public virtual ApplicationUser ApplicationUser { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/PostCategory.cs b/src/CmsEngine.Data/Entities/PostCategory.cs new file mode 100644 index 00000000..63900347 --- /dev/null +++ b/src/CmsEngine.Data/Entities/PostCategory.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Data.Entities; + +public class PostCategory +{ + public int PostId { get; set; } + public virtual Post Post { get; set; } + + public int CategoryId { get; set; } + public virtual Category Category { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/PostTag.cs b/src/CmsEngine.Data/Entities/PostTag.cs new file mode 100644 index 00000000..61b5f5e9 --- /dev/null +++ b/src/CmsEngine.Data/Entities/PostTag.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Data.Entities; + +public class PostTag +{ + public int PostId { get; set; } + public virtual Post Post { get; set; } + + public int TagId { get; set; } + public virtual Tag Tag { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/Tag.cs b/src/CmsEngine.Data/Entities/Tag.cs new file mode 100644 index 00000000..8486cd2d --- /dev/null +++ b/src/CmsEngine.Data/Entities/Tag.cs @@ -0,0 +1,11 @@ +namespace CmsEngine.Data.Entities; + +public class Tag : BaseEntity +{ + public int WebsiteId { get; set; } + public virtual Website Website { get; set; } + public virtual ICollection? PostTags { get; set; } + + public string Name { get; set; } + public string Slug { get; set; } +} diff --git a/src/CmsEngine.Data/Entities/Website.cs b/src/CmsEngine.Data/Entities/Website.cs new file mode 100644 index 00000000..fc1514a1 --- /dev/null +++ b/src/CmsEngine.Data/Entities/Website.cs @@ -0,0 +1,48 @@ +namespace CmsEngine.Data.Entities; + +public class Website : BaseEntity +{ + public virtual ICollection Pages { get; set; } + public virtual ICollection Posts { get; set; } + public virtual ICollection Tags { get; set; } + public virtual ICollection Categories { get; set; } + + public string Name { get; set; } + public string? Tagline { get; set; } + public string? Description { get; set; } + public string? HeaderImage { get; set; } + public string Culture { get; set; } + public string UrlFormat { get; set; } = $"{Main.SiteUrl}/{Main.Type}/{Main.Slug}"; + public string DateFormat { get; set; } = "MM/dd/yyyy"; + public string SiteUrl { get; set; } + public int ArticleLimit { get; set; } + + // Contact details + public string? Address { get; set; } + public string? Phone { get; set; } + public string? Email { get; set; } + public string? Facebook { get; set; } + public string? Twitter { get; set; } + public string? Instagram { get; set; } + public string? LinkedIn { get; set; } + + // Api configuration + public string? FacebookAppId { get; set; } + public string? FacebookApiVersion { get; set; } + public string? DisqusShortName { get; set; } + public string? GoogleAnalytics { get; set; } + public string? GoogleRecaptchaSiteKey { get; set; } + public string? GoogleRecaptchaSecretKey { get; set; } + + public override string ToString() + { + var jsonResult = new JObject( + new JProperty("Id", Id), + new JProperty("VanityId", VanityId), + new JProperty("Name", Name), + new JProperty("SiteUrl", SiteUrl), + new JProperty("Tagline", Tagline) + ); + return jsonResult.ToString(); + } +} diff --git a/src/CmsEngine.Data/Extensions/DbContextExtensions.cs b/src/CmsEngine.Data/Extensions/DbContextExtensions.cs new file mode 100644 index 00000000..00119577 --- /dev/null +++ b/src/CmsEngine.Data/Extensions/DbContextExtensions.cs @@ -0,0 +1,10 @@ +namespace CmsEngine.Data.Extensions; + +public static class DbContextExtensions +{ + public static void TryUpdateManyToMany(this DbContext db, IEnumerable currentItems, IEnumerable newItems, Func getKey) where T : class + { + db.Set().RemoveRange(currentItems.Except(newItems, getKey)); + db.Set().AddRange(newItems.Except(currentItems, getKey)); + } +} diff --git a/src/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs b/src/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs new file mode 100644 index 00000000..38b9388f --- /dev/null +++ b/src/CmsEngine.Data/Extensions/ModelBuilderExtensions.cs @@ -0,0 +1,105 @@ +namespace CmsEngine.Data.Extensions; + +public static class ModelBuilderExtensions +{ + public static void Seed(this ModelBuilder builder) + { + var website = new Website + { + Id = 1, + VanityId = Guid.NewGuid(), + Name = "Sample Website", + Description = "This is a sample website", + Culture = "en-US", + UrlFormat = "http://[site_url]/[type]/[slug]", + DateFormat = "MM/dd/yyyy", + SiteUrl = "cmsengine.test", + ArticleLimit = 10 + }; + + var applicationUser = new ApplicationUser + { + Id = Guid.NewGuid().ToString(), + UserName = "john@doe.com", + Email = "john@doe.com", + ConcurrencyStamp = Guid.NewGuid().ToString(), + EmailConfirmed = true, + LockoutEnabled = false, + NormalizedEmail = "JOHN@DOE.COM", + NormalizedUserName = "JOHN@DOE.COM", + PasswordHash = "AQAAAAEAACcQAAAAEGIUaLe7RWZGw8Tr5/xoUMOooAzJsLFw550fDqZkrbk8CD+urHQzYjK1xY8vcDMekw==", // P@ssword1 + SecurityStamp = "NBTDBYKTNLGHKQ3HI7YFEHPQN5YRXWQC", + TwoFactorEnabled = false, + Name = "John", + Surname = "Doe" + }; + + var documentContent = @"

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed risus libero, egestas vel tempus id, venenatis nec tellus. Nullam hendrerit id magna quis venenatis. Pellentesque rhoncus leo vitae turpis tristique, nec placerat tellus scelerisque. Aenean vitae rhoncus urna, non posuere elit. Nullam quam libero, porttitor in lectus convallis, pellentesque finibus libero. Suspendisse potenti. Fusce quis purus egestas, malesuada massa sed, dignissim purus. Curabitur vitae rhoncus nulla, sit amet dignissim quam.

+

Mauris lorem urna, convallis in enim nec, tristique ullamcorper nisl. Fusce nec tellus et arcu imperdiet ullamcorper vestibulum vitae mi. Sed bibendum molestie dolor sit amet rhoncus.Duis consectetur convallis auctor. In hac habitasse platea dictumst.Duis lorem nibh, mattis ut purus interdum, scelerisque molestie est. Nullam molestie a est vel ornare. Maecenas rhoncus accumsan ligula, at pretium purus tempus ut. Aliquam erat nulla, pretium vel eros vitae, blandit aliquam nibh. Nulla tincidunt, justo et ullamcorper dictum, augue lectus dictum ligula, eget rutrum sem nibh non felis.Aenean elementum, sem sit amet pulvinar tempus, neque eros faucibus turpis, quis molestie nisi libero quis purus.

+

Donec quam massa, tincidunt eu lacus in, lacinia hendrerit urna. Pellentesque pretium orci a felis tincidunt, sit amet volutpat est dapibus. Donec laoreet, massa in imperdiet laoreet, enim ligula auctor est, non imperdiet nisi diam vitae quam. Integer nec porttitor ante. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Morbi non pretium risus, a lobortis eros. Etiam blandit diam tortor. Ut feugiat eros id erat auctor, ut vehicula odio vestibulum.

+

Nunc sed ex sed diam euismod eleifend. Proin blandit lorem sed placerat fermentum. Curabitur non gravida felis, ac sollicitudin nibh. Morbi ornare sapien vitae nisl condimentum cursus.Vivamus bibendum condimentum metus, ut gravida orci bibendum maximus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.Duis varius, tortor ac placerat faucibus, lectus mauris bibendum elit, id eleifend leo diam ac nulla.Aenean egestas urna facilisis purus ullamcorper vestibulum.Etiam commodo suscipit turpis, quis lobortis metus posuere sed.

+

Praesent in augue sit amet tortor ultricies maximus eu ac dui.Pellentesque et congue elit. Suspendisse potenti. Donec facilisis eu magna nec bibendum. Nullam in dignissim elit. Integer laoreet odio massa, vel vestibulum mauris varius et. Ut non ex sit amet nisl mollis laoreet.

"; + + var page = new Page + { + Id = 1, + VanityId = Guid.NewGuid(), + Title = "Sample page", + Slug = "sample-page", + Description = "This is a sample page from a sample website", + Status = DocumentStatus.Published, + PublishedOn = DateTime.Now, + DocumentContent = documentContent, + WebsiteId = website.Id + }; + + var post = new Post + { + Id = 1, + VanityId = Guid.NewGuid(), + Title = "Lorem Ipsum", + Slug = "lorem-ipsum", + Description = "Lorem ipsum dolor sit amet", + Status = DocumentStatus.Published, + PublishedOn = DateTime.Now, + DocumentContent = documentContent, + WebsiteId = website.Id + }; + + var category = new Category + { + Id = 1, + VanityId = Guid.NewGuid(), + Name = "Category example", + Slug = "category-example", + WebsiteId = website.Id + }; + + var postCategory = new PostCategory + { + CategoryId = category.Id, + PostId = post.Id + }; + + var postApplicationUser = new PostApplicationUser + { + ApplicationUserId = applicationUser.Id, + PostId = post.Id + }; + + var pageApplicationUser = new PageApplicationUser + { + ApplicationUserId = applicationUser.Id, + PageId = post.Id + }; + + builder.Entity().HasData(website); + builder.Entity().HasData(page); + builder.Entity().HasData(post); + builder.Entity().HasData(category); + builder.Entity().HasData(postCategory); + builder.Entity().HasData(applicationUser); + builder.Entity().HasData(postApplicationUser); + builder.Entity().HasData(pageApplicationUser); + } +} diff --git a/src/CmsEngine.Data/GlobalUsings.cs b/src/CmsEngine.Data/GlobalUsings.cs new file mode 100644 index 00000000..b05bdf8c --- /dev/null +++ b/src/CmsEngine.Data/GlobalUsings.cs @@ -0,0 +1,15 @@ +global using System.ComponentModel.DataAnnotations; +global using System.ComponentModel.DataAnnotations.Schema; +global using System.Linq.Expressions; +global using CmsEngine.Core.Constants; +global using CmsEngine.Core.Extensions; +global using CmsEngine.Core.Utils; +global using CmsEngine.Data.Entities; +global using CmsEngine.Data.Repositories.Interfaces; +global using Microsoft.AspNetCore.Http; +global using Microsoft.AspNetCore.Identity; +global using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.EntityFrameworkCore.ChangeTracking; +global using Microsoft.EntityFrameworkCore.Metadata.Builders; +global using Newtonsoft.Json.Linq; diff --git a/src/CmsEngine.Data/IUnitOfWork.cs b/src/CmsEngine.Data/IUnitOfWork.cs new file mode 100644 index 00000000..d62edde5 --- /dev/null +++ b/src/CmsEngine.Data/IUnitOfWork.cs @@ -0,0 +1,17 @@ +namespace CmsEngine.Data; + +public interface IUnitOfWork : IDisposable +{ + ICategoryRepository Categories { get; } + IPageRepository Pages { get; } + IPostRepository Posts { get; } + ITagRepository Tags { get; } + IWebsiteRepository Websites { get; } + UserManager Users { get; } + IEmailRepository Emails { get; } + + /// + /// Saves all pending changes into the database + /// + Task Save(); +} diff --git a/CmsEngine.Data/Migrations/20181231150109_InitialCreate.Designer.cs b/src/CmsEngine.Data/Migrations/20181231150109_InitialCreate.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20181231150109_InitialCreate.Designer.cs rename to src/CmsEngine.Data/Migrations/20181231150109_InitialCreate.Designer.cs diff --git a/CmsEngine.Data/Migrations/20181231150109_InitialCreate.cs b/src/CmsEngine.Data/Migrations/20181231150109_InitialCreate.cs similarity index 100% rename from CmsEngine.Data/Migrations/20181231150109_InitialCreate.cs rename to src/CmsEngine.Data/Migrations/20181231150109_InitialCreate.cs diff --git a/CmsEngine.Data/Migrations/20190211194130_DisqusShortName.Designer.cs b/src/CmsEngine.Data/Migrations/20190211194130_DisqusShortName.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20190211194130_DisqusShortName.Designer.cs rename to src/CmsEngine.Data/Migrations/20190211194130_DisqusShortName.Designer.cs diff --git a/CmsEngine.Data/Migrations/20190211194130_DisqusShortName.cs b/src/CmsEngine.Data/Migrations/20190211194130_DisqusShortName.cs similarity index 100% rename from CmsEngine.Data/Migrations/20190211194130_DisqusShortName.cs rename to src/CmsEngine.Data/Migrations/20190211194130_DisqusShortName.cs diff --git a/CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.Designer.cs b/src/CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.Designer.cs rename to src/CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.Designer.cs diff --git a/CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.cs b/src/CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.cs similarity index 100% rename from CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.cs rename to src/CmsEngine.Data/Migrations/20190422093305_Added_GoogleAnalytics.cs diff --git a/CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.Designer.cs b/src/CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.Designer.cs rename to src/CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.Designer.cs diff --git a/CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.cs b/src/CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.cs similarity index 100% rename from CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.cs rename to src/CmsEngine.Data/Migrations/20191020174931_GoogleRecaptchaFields.cs diff --git a/CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.Designer.cs b/src/CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.Designer.cs rename to src/CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.Designer.cs diff --git a/CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.cs b/src/CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.cs similarity index 100% rename from CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.cs rename to src/CmsEngine.Data/Migrations/20191020192050_IncreaseCategoryNameSize.cs diff --git a/CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.Designer.cs b/src/CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.Designer.cs rename to src/CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.Designer.cs diff --git a/CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.cs b/src/CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.cs similarity index 100% rename from CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.cs rename to src/CmsEngine.Data/Migrations/20191020221230_AddingEmailTable.cs diff --git a/CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.Designer.cs b/src/CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.Designer.cs rename to src/CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.Designer.cs diff --git a/CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.cs b/src/CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.cs similarity index 100% rename from CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.cs rename to src/CmsEngine.Data/Migrations/20200410203904_ShadowPropertiesForAuditing.cs diff --git a/CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.Designer.cs b/src/CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.Designer.cs similarity index 100% rename from CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.Designer.cs rename to src/CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.Designer.cs diff --git a/CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.cs b/src/CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.cs similarity index 100% rename from CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.cs rename to src/CmsEngine.Data/Migrations/20201212123624_IncreasedSizeForAuditingUserName.cs diff --git a/CmsEngine.Data/Migrations/CmsEngineContextModelSnapshot.cs b/src/CmsEngine.Data/Migrations/CmsEngineContextModelSnapshot.cs similarity index 100% rename from CmsEngine.Data/Migrations/CmsEngineContextModelSnapshot.cs rename to src/CmsEngine.Data/Migrations/CmsEngineContextModelSnapshot.cs diff --git a/src/CmsEngine.Data/Repositories/CategoryRepository.cs b/src/CmsEngine.Data/Repositories/CategoryRepository.cs new file mode 100644 index 00000000..005bf04f --- /dev/null +++ b/src/CmsEngine.Data/Repositories/CategoryRepository.cs @@ -0,0 +1,54 @@ +namespace CmsEngine.Data.Repositories; + +public class CategoryRepository : Repository, ICategoryRepository +{ + public CategoryRepository(CmsEngineContext context) : base(context) + { + } + + public async Task GetCategoryBySlug(string slug) + { + return await Get(q => q.Slug == slug).SingleOrDefaultAsync(); + } + + public async Task> GetCategoriesWithPostCountOrderedByName() + { + return await Get().Include(c => c.PostCategories) + .Where(q => q.PostCategories.Any(pc => pc.Post.Status == DocumentStatus.Published && pc.Post.IsDeleted == false)) + .Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug, + PostCount = c.PostCategories.Count() + }) + .OrderBy(o => o.Name).ToListAsync(); + } + + public async Task GetCategoryBySlugWithPosts(string slug) + { + return await Get(q => q.Slug == slug).Include(c => c.PostCategories).SingleOrDefaultAsync(); + } + + public async Task> GetCategoriesWithPostOrderedByName() + { + return await Get().Include(c => c.PostCategories) + .ThenInclude(pc => pc.Post) + .Where(q => q.PostCategories.Any(pc => pc.Post.Status == DocumentStatus.Published && pc.Post.IsDeleted == false)) + .Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug, + Posts = c.PostCategories.Select(pc => pc.Post).Where(q => q.IsDeleted == false).Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Description = p.Description, + Slug = p.Slug, + PublishedOn = p.PublishedOn + }) + }) + .OrderBy(o => o.Name).ToListAsync(); + } +} diff --git a/src/CmsEngine.Data/Repositories/EmailRepository.cs b/src/CmsEngine.Data/Repositories/EmailRepository.cs new file mode 100644 index 00000000..6a1d457c --- /dev/null +++ b/src/CmsEngine.Data/Repositories/EmailRepository.cs @@ -0,0 +1,14 @@ +namespace CmsEngine.Data.Repositories; + +public class EmailRepository : Repository, IEmailRepository +{ + public EmailRepository(CmsEngineContext context) : base(context) + { + } + + public async Task> GetOrderedByDate() + { + return await Get().OrderByDescending(o => o.DateReceived) + .ToListAsync(); + } +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/ICategoryRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/ICategoryRepository.cs new file mode 100644 index 00000000..84da0fe5 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/ICategoryRepository.cs @@ -0,0 +1,9 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface ICategoryRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository +{ + Task GetCategoryBySlug(string slug); + Task> GetCategoriesWithPostOrderedByName(); + Task> GetCategoriesWithPostCountOrderedByName(); + Task GetCategoryBySlugWithPosts(string slug); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRangeRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRangeRepository.cs new file mode 100644 index 00000000..d479a828 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRangeRepository.cs @@ -0,0 +1,22 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IDataModificationRangeRepository where TEntity : class +{ + /// + /// Inserts multiple records + /// + /// + Task InsertRange(IEnumerable entities); + + /// + /// Updates multiple records + /// + /// + void UpdateRange(IEnumerable entities); + + /// + /// Deletes multiple records + /// + /// + void DeleteRange(IEnumerable entities); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRepository.cs new file mode 100644 index 00000000..80370550 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IDataModificationRepository.cs @@ -0,0 +1,28 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IDataModificationRepository where TEntity : class +{ + /// + /// Inserts a record + /// + /// + Task Insert(TEntity entity); + + /// + /// Updates record + /// + /// + void Update(TEntity entity); + + /// + /// Deletes record from database + /// + /// + void Delete(TEntity entity); + + /// + /// Attaches non tracked entity to the DbContext + /// + /// + void Attach(TEntity entity); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IEmailRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IEmailRepository.cs new file mode 100644 index 00000000..f2fabec6 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IEmailRepository.cs @@ -0,0 +1,6 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IEmailRepository : IReadRepository, IDataModificationRepository +{ + Task> GetOrderedByDate(); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IPageRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IPageRepository.cs new file mode 100644 index 00000000..220edba1 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IPageRepository.cs @@ -0,0 +1,11 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IPageRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository +{ + Task> GetOrderByDescending(Expression> orderBy); + Task> GetByStatusOrderByDescending(DocumentStatus documentStatus); + Task> GetForDataTable(); + Task GetBySlug(string slug); + Task GetForSavingById(Guid id); + void RemoveRelatedItems(Page page); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IPostRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IPostRepository.cs new file mode 100644 index 00000000..52375877 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IPostRepository.cs @@ -0,0 +1,17 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IPostRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository +{ + Task> GetPublishedPostsOrderByDescending(Expression> orderBy); + Task> GetByStatusOrderByDescending(DocumentStatus documentStatus); + Task<(IEnumerable Items, int Count)> GetPublishedByCategoryForPagination(string categorySlug, int page, int articleLimit); + Task<(IEnumerable Items, int Count)> GetPublishedByTagForPagination(string tagSlug, int page, int articleLimit); + Task<(IEnumerable Items, int Count)> FindPublishedForPaginationOrderByDateDescending(int page, string searchTerm, int articleLimit); + Task<(IEnumerable Items, int Count)> GetPublishedForPagination(int page, int articleLimit); + Task> GetPublishedLatestPosts(int count); + Task> GetForDataTable(); + Task GetForSavingById(Guid id); + Task GetForEditingById(Guid id); + Task GetBySlug(string slug); + void RemoveRelatedItems(Post post); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IReadRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IReadRepository.cs new file mode 100644 index 00000000..a2cabe4d --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IReadRepository.cs @@ -0,0 +1,61 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IReadRepository where TEntity : class +{ + /// + /// Get all records which were not marked as deleted (IsDeleted == false) + /// + /// + Task> GetAllAsync(); + + /// + /// Gets item based on condition and includes extra table + /// + /// + /// + /// + IQueryable Get(Expression> filter = null, int count = 0); + + /// + /// Gets items based on condition for read-only + /// + /// + /// + /// + Task> GetReadOnlyAsync(Expression> filter = null); + + /// + /// Get record by id + /// + /// + /// + Task GetByIdAsync(int id); + + /// + /// Gets record by vanity id + /// + /// + /// + Task GetByIdAsync(Guid id); + + /// + /// Get multiple records by an array of ids + /// + /// + /// + Task> GetByMultipleIdsAsync(int[] ids); + + /// + /// Get multiple records by an array of vanity ids + /// + /// + /// + Task> GetByMultipleIdsAsync(Guid[] ids); + + /// + /// Get multiple records by an IEnumerable of vanity ids + /// + /// + /// + Task> GetIdsByMultipleGuidsAsync(IEnumerable ids); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/ITagRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/ITagRepository.cs new file mode 100644 index 00000000..3fb0d253 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/ITagRepository.cs @@ -0,0 +1,8 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface ITagRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository +{ + Task GetTagBySlug(string slug); + Task> GetTagsWithPosts(); + Task GetTagBySlugWithPosts(string slug); +} diff --git a/src/CmsEngine.Data/Repositories/Interfaces/IWebsiteRepository.cs b/src/CmsEngine.Data/Repositories/Interfaces/IWebsiteRepository.cs new file mode 100644 index 00000000..861141f0 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Interfaces/IWebsiteRepository.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Data.Repositories.Interfaces; + +public interface IWebsiteRepository : IReadRepository, IDataModificationRepository, IDataModificationRangeRepository +{ + Website GetWebsiteInstanceByHost(string host); + Task> GetForDataTable(); +} diff --git a/src/CmsEngine.Data/Repositories/PageRepository.cs b/src/CmsEngine.Data/Repositories/PageRepository.cs new file mode 100644 index 00000000..1dc4fa01 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/PageRepository.cs @@ -0,0 +1,73 @@ +namespace CmsEngine.Data.Repositories; + +public class PageRepository : Repository, IPageRepository +{ + public PageRepository(CmsEngineContext context) : base(context) + { + + } + + public async Task> GetOrderByDescending(Expression> orderBy) + { + return await Get().OrderByDescending(orderBy).ToListAsync(); + } + + public async Task> GetByStatusOrderByDescending(DocumentStatus documentStatus) + { + return await Get(q => q.Status == documentStatus).OrderByDescending(o => o.PublishedOn).ToListAsync(); + } + + public async Task GetBySlug(string slug) + { + return await Get(q => q.Slug == slug) + .Select(p => new Page + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + DocumentContent = p.DocumentContent, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + ApplicationUsers = p.PageApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }) + .SingleOrDefaultAsync(); + } + + public async Task> GetForDataTable() + { + return await Get().Select(p => new Page + { + VanityId = p.VanityId, + Title = p.Title, + Description = p.Description, + Slug = p.Slug, + PublishedOn = p.PublishedOn, + Status = p.Status, + ApplicationUsers = p.PageApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }).ToListAsync(); + } + + public async Task GetForSavingById(Guid id) + { + return await Get(q => q.VanityId == id).Include(p => p.PageApplicationUsers) + .SingleOrDefaultAsync(); + } + + public void RemoveRelatedItems(Page page) + { + dbContext.RemoveRange(page.PageApplicationUsers); + } +} diff --git a/src/CmsEngine.Data/Repositories/PostRepository.cs b/src/CmsEngine.Data/Repositories/PostRepository.cs new file mode 100644 index 00000000..af4d9e78 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/PostRepository.cs @@ -0,0 +1,273 @@ +namespace CmsEngine.Data.Repositories; + +public class PostRepository : Repository, IPostRepository +{ + public PostRepository(CmsEngineContext context) : base(context) + { + + } + + public async Task> GetPublishedPostsOrderByDescending(Expression> orderBy) + { + return await Get(q => q.Status == DocumentStatus.Published).OrderByDescending(orderBy).ToListAsync(); + } + + public async Task> GetByStatusOrderByDescending(DocumentStatus documentStatus) + { + return await Get(q => q.Status == documentStatus).OrderByDescending(o => o.PublishedOn).ToListAsync(); + } + + public async Task GetBySlug(string slug) + { + return await Get(q => q.Slug == slug) + .Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + DocumentContent = p.DocumentContent, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }) + .SingleOrDefaultAsync(); + } + + public async Task<(IEnumerable Items, int Count)> GetPublishedByCategoryForPagination(string categorySlug, int page, int articleLimit) + { + var posts = Get(q => q.Status == DocumentStatus.Published) + .Include(p => p.PostCategories) + .ThenInclude(pc => pc.Category) + .Include(p => p.PostApplicationUsers) + .ThenInclude(pau => pau.ApplicationUser) + .OrderByDescending(o => o.PublishedOn) + .Where(q => q.PostCategories.Any(pc => pc.Category.Slug == categorySlug)); + + var count = posts.Count(); + var items = await posts.Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); + + return (items, count); + } + + public async Task<(IEnumerable Items, int Count)> GetPublishedByTagForPagination(string tagSlug, int page, int articleLimit) + { + var posts = Get(q => q.Status == DocumentStatus.Published).Include(p => p.PostTags) + .ThenInclude(pt => pt.Tag) + .Include(p => p.PostCategories) + .ThenInclude(pc => pc.Category) + .Include(p => p.PostApplicationUsers) + .ThenInclude(pau => pau.ApplicationUser) + .OrderByDescending(o => o.PublishedOn) + .Where(q => q.PostTags.Any(pc => pc.Tag.Slug == tagSlug)); + + var count = posts.Count(); + var items = await posts.Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + Tags = p.PostTags.Select(pt => pt.Tag).Select(c => new Tag + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); + + return (items, count); + } + + public async Task<(IEnumerable Items, int Count)> FindPublishedForPaginationOrderByDateDescending(int page, string searchTerm, int articleLimit) + { + var posts = string.IsNullOrWhiteSpace(searchTerm) + ? Get(q => q.Status == DocumentStatus.Published) + .Include(p => p.PostApplicationUsers) + .ThenInclude(pau => pau.ApplicationUser) + : Get(q => (q.Title.Contains(searchTerm) || q.DocumentContent.Contains(searchTerm)) && q.Status == DocumentStatus.Published) + .Include(p => p.PostApplicationUsers) + .ThenInclude(pau => pau.ApplicationUser); + + var count = await posts.CountAsync(); + var items = await posts.Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }).OrderByDescending(o => o.PublishedOn).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); + + return (items, count); + } + + public async Task<(IEnumerable Items, int Count)> GetPublishedForPagination(int page, int articleLimit) + { + var posts = Get(q => q.Status == DocumentStatus.Published).Include(p => p.PostApplicationUsers) + .ThenInclude(pau => pau.ApplicationUser); + var count = posts.Count(); + var items = await posts.Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }).OrderByDescending(o => o.PublishedOn).Skip((page - 1) * articleLimit).Take(articleLimit).ToListAsync(); + + return (items, count); + } + + public async Task> GetPublishedLatestPosts(int count) + { + return await Get(q => q.Status == DocumentStatus.Published) + .Include(p => p.PostCategories) + .ThenInclude(pc => pc.Category) + .Include(p => p.PostApplicationUsers) + .ThenInclude(pau => pau.ApplicationUser) + .Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Slug = p.Slug, + Description = p.Description, + HeaderImage = p.HeaderImage, + PublishedOn = p.PublishedOn, + Categories = p.PostCategories.Select(pc => pc.Category).Select(c => new Category + { + VanityId = c.VanityId, + Name = c.Name, + Slug = c.Slug + }), + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }) + .OrderByDescending(o => o.PublishedOn).Take(count).ToListAsync(); + } + + public async Task> GetForDataTable() + { + return await Get().Select(p => new Post + { + VanityId = p.VanityId, + Title = p.Title, + Description = p.Description, + Slug = p.Slug, + PublishedOn = p.PublishedOn, + Status = p.Status, + ApplicationUsers = p.PostApplicationUsers.Select(pau => pau.ApplicationUser).Select(au => new ApplicationUser + { + Id = au.Id, + Name = au.Name, + Surname = au.Surname, + Email = au.Email + }) + }).ToListAsync(); + } + + public async Task GetForSavingById(Guid id) + { + return await Get(q => q.VanityId == id).Include(p => p.PostCategories) + .Include(p => p.PostTags) + .Include(p => p.PostApplicationUsers) + .SingleOrDefaultAsync(); + } + + public async Task GetForEditingById(Guid id) + { + return await Get(q => q.VanityId == id).Include(p => p.PostCategories) + .ThenInclude(pc => pc.Category) + .Include(p => p.PostTags) + .ThenInclude(pt => pt.Tag) + .SingleOrDefaultAsync(); + } + + public void RemoveRelatedItems(Post post) + { + dbContext.RemoveRange(post.PostApplicationUsers); + dbContext.RemoveRange(post.PostTags); + dbContext.RemoveRange(post.PostCategories); + } +} diff --git a/src/CmsEngine.Data/Repositories/Repository.cs b/src/CmsEngine.Data/Repositories/Repository.cs new file mode 100644 index 00000000..207b3b74 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/Repository.cs @@ -0,0 +1,155 @@ +namespace CmsEngine.Data.Repositories; + +public class Repository : IReadRepository, + IDataModificationRepository, + IDataModificationRangeRepository + where TEntity : BaseEntity +{ + protected readonly CmsEngineContext dbContext; + + public Repository(CmsEngineContext context) + { + dbContext = context ?? throw new ArgumentNullException("Repository - Context"); + } + + public async Task> GetAllAsync() + { + return await GetValidRecords().ToListAsync(); + } + + public IQueryable Get(Expression> filter = null, int count = 0) + { + var recods = GetValidRecords(); + + if (filter != null) + { + recods = recods.Where(filter); + } + + if (count > 0) + { + recods = recods.Take(count); + } + + return recods; + } + + public async Task> GetReadOnlyAsync(Expression> filter = null) + { + var records = GetValidRecords(); + + if (filter != null) + { + records = records.Where(filter); + } + + return await records.ToListAsync(); + } + + public async Task GetByIdAsync(int id) + { + return await Get(q => q.Id == id).SingleOrDefaultAsync(); + } + + public async Task GetByIdAsync(Guid id) + { + return await Get(q => q.VanityId == id).SingleOrDefaultAsync(); + } + + public async Task> GetByMultipleIdsAsync(int[] ids) + { + return await Get(q => ids.Contains(q.Id)).ToListAsync(); + } + + public async Task> GetByMultipleIdsAsync(Guid[] ids) + { + return await Get(q => ids.Contains(q.VanityId)).ToListAsync(); + } + + public async Task> GetIdsByMultipleGuidsAsync(IEnumerable ids) + { + return await Get(q => ids.Contains(q.VanityId)).Select(x => x.Id).ToListAsync(); + } + + public async Task Insert(TEntity entity) + { + if (entity is null) + { + throw new ArgumentNullException(nameof(entity)); + } + + await dbContext.AddAsync(entity); + } + + public async Task InsertRange(IEnumerable entities) + { + if (entities is null) + { + throw new ArgumentNullException(nameof(entities)); + } + + await dbContext.AddRangeAsync(entities); + } + + public void Update(TEntity entity) + { + if (entity is null) + { + throw new ArgumentNullException(nameof(entity)); + } + + dbContext.Update(entity); + } + + public void UpdateRange(IEnumerable entities) + { + if (entities is null) + { + throw new ArgumentNullException(nameof(entities)); + } + + dbContext.UpdateRange(entities); + } + + public void Delete(TEntity entity) + { + if (entity is null) + { + throw new ArgumentNullException(nameof(entity)); + } + + // We never delete anything, only update the IsDelete flag + entity.IsDeleted = true; + Update(entity); + } + + public void DeleteRange(IEnumerable entities) + { + if (entities is null) + { + throw new ArgumentNullException(nameof(entities)); + } + + for (var i = 0; i < entities.Count(); i++) + { + ((List)entities)[i].IsDeleted = true; + } + + // We never delete anything + UpdateRange(entities); + } + + public void Attach(TEntity entity) + { + EntityEntry dbEntityEntry = dbContext.Entry(entity); + if (dbEntityEntry.State == EntityState.Detached) + { + dbContext.Attach(entity); + } + } + + private IQueryable GetValidRecords() + { + return dbContext.Set().Where(q => q.IsDeleted == false); + } +} diff --git a/src/CmsEngine.Data/Repositories/TagRepository.cs b/src/CmsEngine.Data/Repositories/TagRepository.cs new file mode 100644 index 00000000..14a81ba2 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/TagRepository.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Data.Repositories; + +public class TagRepository : Repository, ITagRepository +{ + public TagRepository(CmsEngineContext context) : base(context) + { + + } + + public async Task GetTagBySlug(string slug) + { + return await Get(q => q.Slug == slug).SingleOrDefaultAsync(); + } + + public async Task GetTagBySlugWithPosts(string slug) + { + return await Get(q => q.Slug == slug).Include(t => t.PostTags).SingleOrDefaultAsync(); + } + + public async Task> GetTagsWithPosts() + { + return await Get().Include(t => t.PostTags).ToListAsync(); + } +} diff --git a/src/CmsEngine.Data/Repositories/WebsiteRepository.cs b/src/CmsEngine.Data/Repositories/WebsiteRepository.cs new file mode 100644 index 00000000..b999d343 --- /dev/null +++ b/src/CmsEngine.Data/Repositories/WebsiteRepository.cs @@ -0,0 +1,29 @@ +namespace CmsEngine.Data.Repositories; + +public class WebsiteRepository : Repository, IWebsiteRepository +{ + public WebsiteRepository(CmsEngineContext context) : base(context) + { + + } + + public async Task> GetForDataTable() + { + return await Get().Select(w => new Website + { + VanityId = w.VanityId, + Name = w.Name, + Tagline = w.Tagline, + Culture = w.Culture, + UrlFormat = w.UrlFormat, + DateFormat = w.DateFormat, + SiteUrl = w.SiteUrl, + GoogleAnalytics = w.GoogleAnalytics + }).ToListAsync(); + } + + public Website GetWebsiteInstanceByHost(string host) + { + return Get(q => q.SiteUrl == host).SingleOrDefault(); + } +} diff --git a/src/CmsEngine.Data/UnitOfWork.cs b/src/CmsEngine.Data/UnitOfWork.cs new file mode 100644 index 00000000..bf2126f0 --- /dev/null +++ b/src/CmsEngine.Data/UnitOfWork.cs @@ -0,0 +1,88 @@ +namespace CmsEngine.Data; + +public class UnitOfWork : IUnitOfWork +{ + private readonly CmsEngineContext _ctx; + private bool _disposed; + + public ICategoryRepository Categories { get; private set; } + public IPageRepository Pages { get; private set; } + public IPostRepository Posts { get; private set; } + public ITagRepository Tags { get; private set; } + public IWebsiteRepository Websites { get; private set; } + public UserManager Users { get; private set; } + public IEmailRepository Emails { get; private set; } + + public UnitOfWork(CmsEngineContext context, ICategoryRepository categoryRepository, IPageRepository pageRepository, + IPostRepository postRepository, ITagRepository tagRepository, IWebsiteRepository websiteRepository, + UserManager userManager, IEmailRepository emailRepository) + { + _ctx = context; + + Categories = categoryRepository; + Pages = pageRepository; + Posts = postRepository; + Tags = tagRepository; + Websites = websiteRepository; + Users = userManager; + Emails = emailRepository; + + _disposed = false; + } + + public async Task Save() + { + try + { + await _ctx.SaveChangesAsync(); + } + // TODO: Implement EF Core error handling + //catch (DbEntityValidationException ex) + //{ + // // Retrieve the error messages as a list of strings. + // var errorMessages = ex.EntityValidationErrors + // .SelectMany(x => x.ValidationErrors) + // .Select(x => x.ErrorMessage); + + // // Join the list to a single string. + // var fullErrorMessage = string.Join("; ", errorMessages); + + // // Combine the original exception message with the new one. + // var exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage); + + // // Throw a new DbEntityValidationException with the improved exception message. + // throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); + //} + catch (DbUpdateConcurrencyException ex) + { + ex.Entries[0].Reload(); + } + catch (DbUpdateException ex) + { + var innerException = ex.InnerException; + throw new DbUpdateException(innerException.Message, innerException); + } + catch + { + throw; + } + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _ctx.Dispose(); + } + } + _disposed = true; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } +} diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs new file mode 100644 index 00000000..c13bbcf3 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/BaseController.cs @@ -0,0 +1,160 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Authorize] +public class BaseController : Controller +{ + public IService Service { get; private set; } + public ILogger Logger { get; private set; } + + public BaseController(ILoggerFactory loggerFactory, IService service) + { + Guard.Against.Null(loggerFactory); + Guard.Against.Null(service); + + Logger = loggerFactory.CreateLogger("CmsBaseController"); + Service = service; + + var cultureInfo = new CultureInfo(service.Instance.Culture); + + CultureInfo.DefaultThreadCurrentCulture = cultureInfo; + CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; + } + + public override void OnActionExecuting(ActionExecutingContext context) + { + base.OnActionExecuting(context); + + ViewBag.CurrentUser = Service?.CurrentUser; + } + + protected void SetupMessages(string pageTitle) + { + ViewBag.PageTitle = pageTitle; + } + + protected void SetupMessages(string pageTitle, PageType pageType, string description = "", string panelTitle = "") + { + SetupMessages(pageTitle); + + ViewBag.PageType = pageType.ToString(); + ViewBag.PageDescription = description; + ViewBag.PanelTitle = panelTitle; + } + + protected void SetupMessages(string pageTitle, PageType pageType, string modelError, string generalError, string description = "", string panelTitle = "") + { + SetupMessages(pageTitle, pageType, description, panelTitle); + + if (!string.IsNullOrWhiteSpace(modelError)) + { + ModelState.AddModelError("", modelError); + } + + TempData[MessageConstants.DangerMessage] = generalError; + } + + protected async Task UploadImageAsync(string webrootPath, string folderName) + { + string folderPath = GetUploadFolderPath(webrootPath, folderName); + + var formFile = Request.Form.Files[0]; + + if (formFile.Length == 0) + { + return null; + } + + _ = await UploadFileAsync(folderPath, formFile); + + string pathUrl = $"/image/{folderName}/"; + + var returnImage = new TinyMceUploadResult + { + Location = $"{pathUrl}{formFile.FileName}" + }; + + return Content(JsonConvert.SerializeObject(returnImage).ToLowerInvariant(), "application/json"); + } + + protected async Task PrepareAndUploadFilesAsync(string webrootPath, string folderName) + { + string folderPath = GetUploadFolderPath(webrootPath, folderName); + + var fileList = new List(); + + foreach (var formFile in Request.Form.Files) + { + if (formFile.Length == 0) + { + continue; + } + + string originalFile = await UploadFileAsync(folderPath, formFile); + + string fileSize = FileHelper.FormatFileSize(originalFile); + bool isImage = FileHelper.IsImage(formFile.FileName); + string pathUrl; + + if (isImage) + { + var imageSizes = new List<(int Width, int Height)> + { + (120, 120), + (320, 213), + (640, 426) + }; + + foreach (var imageSize in imageSizes) + { + ResizeImages(folderPath, formFile, originalFile, imageSize.Width, imageSize.Height); + } + + pathUrl = $"/image/{folderName}/"; + } + else + { + pathUrl = $"/file/{folderName}/"; + } + + fileList.Add(new UploadFilesResult + { + FileName = formFile.FileName, + Path = pathUrl, + Length = formFile.Length, + ContentType = formFile.ContentType, + IsImage = isImage, + Size = fileSize + }); + } + return Content(JsonConvert.SerializeObject(fileList).ToLowerInvariant(), "application/json"); + } + + private static void ResizeImages(string folderPath, IFormFile formFile, string originalFile, int width, int height) + { + string thumbnailFileName = Path.Combine(folderPath, $"{width}x{height}_{formFile.FileName}"); + FileHelper.ResizeImage(originalFile, thumbnailFileName, width, height, true); + } + + private static async Task UploadFileAsync(string folderPath, IFormFile formFile) + { + string filePath = Path.Combine(folderPath, Path.GetFileName(formFile.FileName)); + using (var stream = new FileStream(filePath, FileMode.Create)) + { + await formFile.CopyToAsync(stream); + } + + return filePath; + } + + private static string GetUploadFolderPath(string webrootPath, string folderName) + { + string folder = Path.Combine(webrootPath, "UploadedFiles", folderName); + + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + + return folder; + } +} diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs new file mode 100644 index 00000000..1773fa01 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/CategoryController.cs @@ -0,0 +1,109 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Area("Cms")] +public class CategoryController : BaseController +{ + private readonly ICategoryService _categoryService; + + public CategoryController(ILoggerFactory loggerFactory, IService service, ICategoryService categoryService) + : base(loggerFactory, service) + { + _categoryService = categoryService; + } + + public IActionResult Index() + { + SetupMessages("Categories", PageType.List, panelTitle: "List of categories"); + return View("List"); + } + + public IActionResult Create() + { + SetupMessages("Category", PageType.Create, panelTitle: "Create a new category"); + var categoryEditModel = _categoryService.SetupEditModel(); + + return View("CreateEdit", categoryEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task CreateAsync(CategoryEditModel categoryEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Categories", PageType.Create, panelTitle: "Create a new category"); + return View("CreateEdit", categoryEditModel); + } + + return await SaveAsync(categoryEditModel, nameof(CategoryController.Create)); + } + + public async Task EditAsync(Guid vanityId) + { + SetupMessages("Categories", PageType.Edit, panelTitle: "Edit an existing category"); + var categoryEditModel = await _categoryService.SetupEditModel(vanityId); + + return View("CreateEdit", categoryEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EditAsync(CategoryEditModel categoryEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Categories", PageType.Edit, panelTitle: "Edit an existing category"); + TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; + return View("CreateEdit", categoryEditModel); + } + + var categoryToUpdate = await _categoryService.SetupEditModel(categoryEditModel.VanityId); + + if (await TryUpdateModelAsync(categoryToUpdate)) + { + return await SaveAsync(categoryEditModel, nameof(CategoryController.EditAsync)); + } + + TempData[MessageConstants.WarningMessage] = "The model could not be updated."; + return RedirectToAction(nameof(CategoryController.EditAsync), categoryEditModel); + } + + [HttpPost] + public async Task DeleteAsync(Guid vanityId) + { + return Ok(await _categoryService.Delete(vanityId)); + } + + [HttpPost("cms/category/bulk-delete")] + public async Task BulkDeleteAsync([FromForm] Guid[] vanityId) + { + return Ok(await _categoryService.DeleteRange(vanityId)); + } + + [HttpPost] + public async Task GetDataAsync([FromForm] DataParameters parameters) + { + Guard.Against.Null(parameters); + + var items = await _categoryService.GetForDataTable(parameters); + var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); + + return Ok(dataTable); + } + + private async Task SaveAsync(CategoryEditModel categoryEditModel, string sender) + { + var returnValue = await _categoryService.Save(categoryEditModel); + + if (!returnValue.IsError) + { + TempData[MessageConstants.SuccessMessage] = returnValue.Message; + return RedirectToAction(nameof(CategoryController.Index)); + } + else + { + TempData[MessageConstants.DangerMessage] = returnValue.Message; + return RedirectToAction(sender); + } + } +} diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs new file mode 100644 index 00000000..79bb257b --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/HomeController.cs @@ -0,0 +1,19 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Area("Cms")] +public class HomeController : BaseController +{ + private readonly IEmailService _emailService; + + public HomeController(ILoggerFactory loggerFactory, IService service, IEmailService emailService) + : base(loggerFactory, service) + { + _emailService = emailService; + } + + public async Task IndexAsync() + { + SetupMessages("Dashboard"); + return View(await _emailService.GetOrderedByDate()); + } +} \ No newline at end of file diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs new file mode 100644 index 00000000..e58c40c2 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/PageController.cs @@ -0,0 +1,123 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Area("Cms")] +public class PageController : BaseController +{ + private readonly IWebHostEnvironment _env; + private readonly IPageService _pageService; + + public PageController(ILoggerFactory loggerFactory, IService service, + IWebHostEnvironment env, IPageService pageService) : base(loggerFactory, service) + { + _env = env; + _pageService = pageService; + } + + public IActionResult Index() + { + SetupMessages("Pages", PageType.List, panelTitle: "List of pages"); + return View("List"); + } + + public IActionResult Create() + { + SetupMessages("Page", PageType.Create, panelTitle: "Create a new page"); + var pageEditModel = _pageService.SetupEditModel(); + + return View("CreateEdit", pageEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task CreateAsync(PageEditModel pageEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Pages", PageType.Create, panelTitle: "Create a new page"); + return View("CreateEdit", pageEditModel); + } + + return await SaveAsync(pageEditModel, nameof(PageController.Create)); + } + + public async Task EditAsync(Guid vanityId) + { + SetupMessages("Pages", PageType.Edit, panelTitle: "Edit an existing page"); + var pageEditModel = await _pageService.SetupEditModel(vanityId); + + return View("CreateEdit", pageEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EditAsync(PageEditModel pageEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Pages", PageType.Edit, panelTitle: "Edit an existing page"); + TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; + return View("CreateEdit", pageEditModel); + } + + var pageToUpdate = await _pageService.SetupEditModel(pageEditModel.VanityId); + + if (await TryUpdateModelAsync(pageToUpdate)) + { + return await SaveAsync(pageEditModel, nameof(PageController.EditAsync)); + } + + TempData[MessageConstants.WarningMessage] = "The model could not be updated."; + return RedirectToAction(nameof(PageController.EditAsync), pageEditModel); + } + + [HttpPost] + public async Task DeleteAsync(Guid vanityId) + { + return Ok(await _pageService.Delete(vanityId)); + } + + [HttpPost("cms/page/bulk-delete")] + public async Task BulkDeleteAsync([FromForm] Guid[] vanityId) + { + return Ok(await _pageService.DeleteRange(vanityId)); + } + + [HttpPost] + public async Task GetDataAsync([FromForm] DataParameters parameters) + { + Guard.Against.Equals(parameters); + + var items = await _pageService.GetForDataTable(parameters); + var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); + + return Ok(dataTable); + } + + [HttpPost] + public async Task UploadImagesAsync() + { + return await UploadImageAsync(_env.WebRootPath, "Page"); + } + + [HttpPost] + public async Task UploadFilesAsync() + { + return await PrepareAndUploadFilesAsync(_env.WebRootPath, "Page"); + } + + private async Task SaveAsync(PageEditModel pageEditModel, string sender) + { + var returnValue = await _pageService.Save(pageEditModel); + + if (!returnValue.IsError) + { + TempData[MessageConstants.SuccessMessage] = returnValue.Message; + return RedirectToAction(nameof(PageController.Index)); + } + else + { + TempData[MessageConstants.DangerMessage] = returnValue.Message; + return RedirectToAction(sender); + } + } +} diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs new file mode 100644 index 00000000..bd4d6cd6 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/PostController.cs @@ -0,0 +1,124 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Area("Cms")] +public class PostController : BaseController +{ + private readonly IWebHostEnvironment _env; + private readonly IPostService _postService; + + public PostController(ILoggerFactory loggerFactory, IService service, + IWebHostEnvironment env, IPostService postService) : base(loggerFactory, service) + { + _env = env; + _postService = postService; + } + + public IActionResult Index() + { + SetupMessages("Posts", PageType.List, panelTitle: "List of posts"); + return View("List"); + } + + public async Task CreateAsync() + { + SetupMessages("Post", PageType.Create, panelTitle: "Create a new post"); + var postEditModel = await _postService.SetupEditModel(); + + return View("CreateEdit", postEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task CreateAsync(PostEditModel postEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Posts", PageType.Create, panelTitle: "Create a new post"); + return View("CreateEdit", postEditModel); + } + + return await SaveAsync(postEditModel, nameof(PostController.CreateAsync)); + } + + public async Task EditAsync(Guid vanityId) + { + SetupMessages("Posts", PageType.Edit, panelTitle: "Edit an existing post"); + var postEditModel = await _postService.SetupEditModel(vanityId); + + return View("CreateEdit", postEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EditAsync(PostEditModel postEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Posts", PageType.Edit, panelTitle: "Edit an existing post"); + TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; + return View("CreateEdit", postEditModel); + } + + var postToUpdate = await _postService.SetupEditModel(postEditModel.VanityId); + + // TODO: Understand why I needed to use TryUpdateModelAsync like this + if (await TryUpdateModelAsync(postToUpdate, "", p => p.Id)) + { + return await SaveAsync(postEditModel, nameof(PostController.EditAsync)); + } + + TempData[MessageConstants.WarningMessage] = "The model could not be updated."; + return RedirectToAction(nameof(PostController.EditAsync), postEditModel); + } + + [HttpPost] + public async Task DeleteAsync(Guid vanityId) + { + return Ok(await _postService.Delete(vanityId)); + } + + [HttpPost("cms/post/bulk-delete")] + public async Task BulkDeleteAsync([FromForm] Guid[] vanityId) + { + return Ok(await _postService.DeleteRange(vanityId)); + } + + [HttpPost] + public async Task GetDataAsync([FromForm] DataParameters parameters) + { + Guard.Against.Equals(parameters); + + var items = await _postService.GetForDataTable(parameters); + var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); + + return Ok(dataTable); + } + + [HttpPost] + public async Task UploadImagesAsync() + { + return await UploadImageAsync(_env.WebRootPath, "Post"); + } + + [HttpPost] + public async Task UploadFilesAsync() + { + return await PrepareAndUploadFilesAsync(_env.WebRootPath, "Post"); + } + + private async Task SaveAsync(PostEditModel postEditModel, string sender) + { + var returnValue = await _postService.Save(postEditModel); + + if (!returnValue.IsError) + { + TempData[MessageConstants.SuccessMessage] = returnValue.Message; + return RedirectToAction(nameof(PostController.Index)); + } + else + { + TempData[MessageConstants.DangerMessage] = returnValue.Message; + return RedirectToAction(sender); + } + } +} diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs new file mode 100644 index 00000000..51a4f58c --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/TagController.cs @@ -0,0 +1,108 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Area("Cms")] +public class TagController : BaseController +{ + private readonly ITagService _tagService; + + public TagController(ILoggerFactory loggerFactory, IService service, ITagService tagService) + : base(loggerFactory, service) + { + _tagService = tagService; + } + + public IActionResult Index() + { + SetupMessages("Tags", PageType.List, panelTitle: "List of tags"); + //var tagViewModel = service.SetupViewModel(); + return View("List"); + } + + public IActionResult Create() + { + SetupMessages("Tag", PageType.Create, panelTitle: "Create a new tag"); + var tagEditModel = _tagService.SetupEditModel(); + + return View("CreateEdit", tagEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task CreateAsync(TagEditModel tagEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Tags", PageType.Create, panelTitle: "Create a new tag"); + return View("CreateEdit", tagEditModel); + } + + return await SaveAsync(tagEditModel, nameof(TagController.Create)); + } + + public async Task EditAsync(Guid vanityId) + { + SetupMessages("Tags", PageType.Edit, panelTitle: "Edit an existing tag"); + var tagEditModel = await _tagService.SetupEditModel(vanityId); + + return View("CreateEdit", tagEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EditAsync(TagEditModel tagEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Tags", PageType.Edit, panelTitle: "Edit an existing tag"); + return View("CreateEdit", tagEditModel); + } + + var tagToUpdate = await _tagService.SetupEditModel(tagEditModel.VanityId); + + if (await TryUpdateModelAsync(tagToUpdate)) + { + return await SaveAsync(tagEditModel, nameof(TagController.EditAsync)); + } + TempData[MessageConstants.WarningMessage] = "The model could not be updated."; + return RedirectToAction(nameof(TagController.EditAsync), tagEditModel); + } + + [HttpPost] + public async Task DeleteAsync(Guid vanityId) + { + return Ok(await _tagService.Delete(vanityId)); + } + + [HttpPost("cms/tag/bulk-delete")] + public async Task BulkDeleteAsync([FromForm] Guid[] vanityId) + { + return Ok(await _tagService.DeleteRange(vanityId)); + } + + [HttpPost] + public async Task GetDataAsync([FromForm] DataParameters parameters) + { + Guard.Against.Equals(parameters); + + var items = await _tagService.GetForDataTable(parameters); + var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); + + return Ok(dataTable); + } + + private async Task SaveAsync(TagEditModel tagEditModel, string sender) + { + var returnValue = await _tagService.Save(tagEditModel); + + if (!returnValue.IsError) + { + TempData[MessageConstants.SuccessMessage] = returnValue.Message; + return RedirectToAction(nameof(TagController.Index)); + } + else + { + TempData[MessageConstants.DangerMessage] = returnValue.Message; + return RedirectToAction(sender); + } + } +} diff --git a/src/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs b/src/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs new file mode 100644 index 00000000..d74942a0 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Controllers/WebsiteController.cs @@ -0,0 +1,121 @@ +namespace CmsEngine.Ui.Areas.Cms.Controllers; + +[Area("Cms")] +public class WebsiteController : BaseController +{ + private readonly IWebHostEnvironment _env; + private readonly IWebsiteService _websiteService; + + public WebsiteController(ILoggerFactory loggerFactory, IService service, + IWebHostEnvironment env, IWebsiteService websiteService) : base(loggerFactory, service) + { + _env = env; + _websiteService = websiteService; + } + + public IActionResult Index() + { + SetupMessages("Websites", PageType.List, panelTitle: "List of websites"); + return View("List"); + } + + public IActionResult Create() + { + SetupMessages("Website", PageType.Create, panelTitle: "Create a new website"); + var websiteEditModel = _websiteService.SetupEditModel(); + + return View("CreateEdit", websiteEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task CreateAsync(WebsiteEditModel websiteEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Websites", PageType.Create, panelTitle: "Create a new website"); + return View("CreateEdit", websiteEditModel); + } + + return await SaveAsync(websiteEditModel, nameof(WebsiteController.Create)); + } + + public async Task EditAsync(Guid vanityId) + { + SetupMessages("Websites", PageType.Edit, panelTitle: "Edit an existing website"); + var websiteEditModel = await _websiteService.SetupEditModel(vanityId); + + return View("CreateEdit", websiteEditModel); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task EditAsync(WebsiteEditModel websiteEditModel) + { + if (!ModelState.IsValid) + { + SetupMessages("Websites", PageType.Edit, panelTitle: "Edit an existing website"); + return View("CreateEdit", websiteEditModel); + } + + var websiteToUpdate = await _websiteService.SetupEditModel(websiteEditModel.VanityId); + + if (await TryUpdateModelAsync(websiteToUpdate)) + { + return await SaveAsync(websiteEditModel, nameof(WebsiteController.EditAsync)); + } + TempData[MessageConstants.WarningMessage] = "The model could not be updated."; + return RedirectToAction(nameof(WebsiteController.EditAsync), websiteEditModel); + } + + [HttpPost] + public async Task DeleteAsync(Guid vanityId) + { + return Ok(await _websiteService.Delete(vanityId)); + } + + [HttpPost("cms/website/bulk-delete")] + public async Task BulkDeleteAsync([FromForm] Guid[] vanityId) + { + return Ok(await _websiteService.DeleteRange(vanityId)); + } + + [HttpPost] + public async Task GetDataAsync([FromForm] DataParameters parameters) + { + Guard.Against.Equals(parameters); + + var items = await _websiteService.GetForDataTable(parameters); + var dataTable = DataTableHelper.BuildDataTable(items.Data, items.RecordsTotal, items.RecordsFiltered, parameters.Draw, parameters.Start, parameters.Length); + + return Ok(dataTable); + } + + [HttpPost] + public async Task UploadImagesAsync() + { + return await UploadImageAsync(_env.WebRootPath, "Website"); + } + + [HttpPost] + public async Task UploadFilesAsync() + { + return await PrepareAndUploadFilesAsync(_env.WebRootPath, "Website"); + } + + private async Task SaveAsync(WebsiteEditModel websiteEditModel, string sender) + { + var returnValue = await _websiteService.Save(websiteEditModel); + + if (!returnValue.IsError) + { + TempData[MessageConstants.SuccessMessage] = returnValue.Message; + return RedirectToAction(nameof(WebsiteController.Index)); + } + else + { + TempData[MessageConstants.DangerMessage] = returnValue.Message; + return RedirectToAction(sender); + } + } +} diff --git a/CmsEngine.Ui/Areas/Cms/Views/Category/CreateEdit.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Category/CreateEdit.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Category/CreateEdit.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Category/CreateEdit.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Category/List.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Category/List.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Category/List.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Category/List.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Home/Index.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Home/Index.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Home/Index.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Home/Index.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Page/CreateEdit.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Page/CreateEdit.cshtml similarity index 98% rename from CmsEngine.Ui/Areas/Cms/Views/Page/CreateEdit.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Page/CreateEdit.cshtml index b3a5d115..601c772a 100644 --- a/CmsEngine.Ui/Areas/Cms/Views/Page/CreateEdit.cshtml +++ b/src/CmsEngine.Ui/Areas/Cms/Views/Page/CreateEdit.cshtml @@ -63,7 +63,7 @@
@if (!string.IsNullOrWhiteSpace(Model.HeaderImage)) { - Image preview + Image preview }
diff --git a/CmsEngine.Ui/Areas/Cms/Views/Page/List.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Page/List.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Page/List.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Page/List.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Post/CreateEdit.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Post/CreateEdit.cshtml similarity index 98% rename from CmsEngine.Ui/Areas/Cms/Views/Post/CreateEdit.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Post/CreateEdit.cshtml index 1211597c..06cd0005 100644 --- a/CmsEngine.Ui/Areas/Cms/Views/Post/CreateEdit.cshtml +++ b/src/CmsEngine.Ui/Areas/Cms/Views/Post/CreateEdit.cshtml @@ -65,7 +65,7 @@
@if (!string.IsNullOrWhiteSpace(Model.HeaderImage)) { - Image preview + Image preview }
diff --git a/CmsEngine.Ui/Areas/Cms/Views/Post/List.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Post/List.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Post/List.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Post/List.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Shared/Error.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Shared/Error.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Shared/Error.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Shared/Error.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesScripts.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesScripts.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesScripts.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesScripts.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesStyles.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesStyles.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesStyles.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Shared/_DataTablesStyles.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Shared/_LoginPartial.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Shared/_LoginPartial.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Shared/_LoginPartial.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Shared/_LoginPartial.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Shared/_ValidationScriptsPartial.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Shared/_ValidationScriptsPartial.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Shared/_ValidationScriptsPartial.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Shared/_ValidationScriptsPartial.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Tag/CreateEdit.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Tag/CreateEdit.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Tag/CreateEdit.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Tag/CreateEdit.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Tag/List.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Tag/List.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Tag/List.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Tag/List.cshtml diff --git a/CmsEngine.Ui/Areas/Cms/Views/Website/CreateEdit.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Website/CreateEdit.cshtml similarity index 99% rename from CmsEngine.Ui/Areas/Cms/Views/Website/CreateEdit.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Website/CreateEdit.cshtml index a9cf7a17..7f0067d5 100644 --- a/CmsEngine.Ui/Areas/Cms/Views/Website/CreateEdit.cshtml +++ b/src/CmsEngine.Ui/Areas/Cms/Views/Website/CreateEdit.cshtml @@ -33,7 +33,7 @@
@if (!string.IsNullOrWhiteSpace(Model.HeaderImage)) { - Image preview + Image preview }
diff --git a/CmsEngine.Ui/Areas/Cms/Views/Website/List.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/Website/List.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/Website/List.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/Website/List.cshtml diff --git a/src/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml new file mode 100644 index 00000000..3b96dd52 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Cms/Views/_ViewImports.cshtml @@ -0,0 +1,10 @@ +@using Microsoft.AspNetCore.Identity +@using CmsEngine.Core +@using CmsEngine.Core.Constants +@using CmsEngine.Data.Entities +@using CmsEngine.Application.Models.EditModels +@using CmsEngine.Application.Models.ViewModels +@using CmsEngine.Application.Models.ViewModels.AccountViewModels +@using CmsEngine.Application.Models.ViewModels.ManageViewModels +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@addTagHelper *, CmsEngine.Ui diff --git a/CmsEngine.Ui/Areas/Cms/Views/_ViewStart.cshtml b/src/CmsEngine.Ui/Areas/Cms/Views/_ViewStart.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Cms/Views/_ViewStart.cshtml rename to src/CmsEngine.Ui/Areas/Cms/Views/_ViewStart.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/AccessDenied.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ConfirmEmail.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ExternalLogin.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs new file mode 100644 index 00000000..4920eb50 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPassword.cshtml.cs @@ -0,0 +1,52 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class ForgotPasswordModel : PageModel +{ + private readonly UserManager _userManager; + private readonly ICmsEngineEmailSender _emailSender; + + public ForgotPasswordModel(UserManager userManager, ICmsEngineEmailSender emailSender) + { + _userManager = userManager; + _emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + } + + public async Task OnPostAsync() + { + if (ModelState.IsValid) + { + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null || !(await _userManager.IsEmailConfirmedAsync(user))) + { + // Don't reveal that the user does not exist or is not confirmed + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + // For more information on how to enable account confirmation and password reset please + // visit https://go.microsoft.com/fwlink/?LinkID=532713 + var code = await _userManager.GeneratePasswordResetTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ResetPassword", + pageHandler: null, + values: new { code }, + protocol: Request.Scheme); + + await _emailSender.SendPasswordResetAsync(Input.Email, HtmlEncoder.Default.Encode(callbackUrl)); + + return RedirectToPage("./ForgotPasswordConfirmation"); + } + + return Page(); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ForgotPasswordConfirmation.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Lockout.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs new file mode 100644 index 00000000..08e042e1 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -0,0 +1,89 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class LoginModel : PageModel +{ + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LoginModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public IList ExternalLogins { get; set; } + + public string ReturnUrl { get; set; } + + [TempData] + public string ErrorMessage { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + + [Display(Name = "Remember me?")] + public bool RememberMe { get; set; } + } + + public async Task OnGetAsync(string returnUrl = null) + { + if (!string.IsNullOrEmpty(ErrorMessage)) + { + ModelState.AddModelError(string.Empty, ErrorMessage); + } + + returnUrl = returnUrl ?? Url.Content("~/cms"); + + // Clear the existing external cookie to ensure a clean login process + await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme); + + ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); + + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync() + { + var returnUrl = Url.Content("~/cms"); + + if (ModelState.IsValid) + { + // This doesn't count login failures towards account lockout + // To enable password failures to trigger account lockout, set lockoutOnFailure: true + var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: true); + if (result.Succeeded) + { + _logger.LogDebug("User logged in."); + return LocalRedirect(returnUrl); + } + if (result.RequiresTwoFactor) + { + return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe }); + } + if (result.IsLockedOut) + { + _logger.LogWarning("User account locked out."); + return RedirectToPage("./Lockout"); + } + else + { + ModelState.AddModelError(string.Empty, "Invalid login attempt."); + return Page(); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/LoginWithRecoveryCode.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs new file mode 100644 index 00000000..236ba708 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Logout.cshtml.cs @@ -0,0 +1,21 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class LogoutModel : PageModel +{ + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public LogoutModel(SignInManager signInManager, ILogger logger) + { + _signInManager = signInManager; + _logger = logger; + } + + public async Task OnGet() + { + await _signInManager.SignOutAsync(); + _logger.LogDebug("User logged out."); + return RedirectToAction(nameof(HomeController.IndexAsync), "Home", new { area = "cms" }); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs new file mode 100644 index 00000000..2fb07db3 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ChangePassword.cshtml.cs @@ -0,0 +1,97 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage; + +public class ChangePasswordModel : PageModel +{ + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + private readonly IService _service; + + public ChangePasswordModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger, + IService service) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + _service = service; + } + + [BindProperty] + public InputModel Input { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + [Display(Name = "Current password")] + public string OldPassword { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "New password")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm new password")] + [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public async Task OnGetAsync() + { + ViewData["CurrentUser"] = _service?.CurrentUser; + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var hasPassword = await _userManager.HasPasswordAsync(user); + if (!hasPassword) + { + return RedirectToPage("./SetPassword"); + } + + return Page(); + } + + public async Task OnPostAsync() + { + ViewData["CurrentUser"] = _service?.CurrentUser; + + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var changePasswordResult = await _userManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); + if (!changePasswordResult.Succeeded) + { + foreach (var error in changePasswordResult.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } + + await _signInManager.RefreshSignInAsync(user); + _logger.LogDebug("User changed their password successfully."); + StatusMessage = "Your password has been changed."; + + return RedirectToPage(); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs new file mode 100644 index 00000000..6ebd9304 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DeletePersonalData.cshtml.cs @@ -0,0 +1,74 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage; + +public class DeletePersonalDataModel : PageModel +{ + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ILogger _logger; + + public DeletePersonalDataModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _logger = logger; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } + } + + public bool RequirePassword { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + RequirePassword = await _userManager.HasPasswordAsync(user); + if (RequirePassword) + { + if (!await _userManager.CheckPasswordAsync(user, Input.Password)) + { + ModelState.AddModelError(string.Empty, "Password not correct."); + return Page(); + } + } + + var result = await _userManager.DeleteAsync(user); + var userId = await _userManager.GetUserIdAsync(user); + if (!result.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'."); + } + + await _signInManager.SignOutAsync(); + + _logger.LogDebug("User with ID '{UserId}' deleted themselves.", userId); + + return Redirect("~/"); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs new file mode 100644 index 00000000..302a7480 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs @@ -0,0 +1,53 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage; + +public class Disable2faModel : PageModel +{ + private readonly UserManager _userManager; + private readonly ILogger _logger; + + public Disable2faModel( + UserManager userManager, + ILogger logger) + { + _userManager = userManager; + _logger = logger; + } + + [TempData] + public string StatusMessage { get; set; } + + public async Task OnGet() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + if (!await _userManager.GetTwoFactorEnabledAsync(user)) + { + throw new InvalidOperationException($"Cannot disable 2FA for user with ID '{_userManager.GetUserId(User)}' as it's not currently enabled."); + } + + return Page(); + } + + public async Task OnPostAsync() + { + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var disable2faResult = await _userManager.SetTwoFactorEnabledAsync(user, false); + if (!disable2faResult.Succeeded) + { + throw new InvalidOperationException($"Unexpected error occurred disabling 2FA for user with ID '{_userManager.GetUserId(User)}'."); + } + + _logger.LogDebug("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); + StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; + return RedirectToPage("./TwoFactorAuthentication"); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/DownloadPersonalData.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ExternalLogins.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/GenerateRecoveryCodes.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs new file mode 100644 index 00000000..5d0e3246 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/Index.cshtml.cs @@ -0,0 +1,164 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage; + +public partial class IndexModel : PageModel +{ + private readonly UserManager _userManager; + private readonly SignInManager _signInManager; + private readonly ICmsEngineEmailSender _emailSender; + private readonly IService _service; + private readonly ILogger _logger; + + public IndexModel( + UserManager userManager, + SignInManager signInManager, + ICmsEngineEmailSender emailSender, + IService service, ILogger logger) + { + _userManager = userManager; + _signInManager = signInManager; + _emailSender = emailSender; + _service = service; + _logger = logger; + } + + public string Username { get; set; } + + public bool IsEmailConfirmed { get; set; } + + [TempData] + public string StatusMessage { get; set; } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + public string Username { get; set; } + + public string Name { get; set; } + + public string Surname { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } + + [Phone] + [Display(Name = "Phone number")] + public string PhoneNumber { get; set; } + } + + public async Task OnGetAsync() + { + ViewData["CurrentUser"] = _service?.CurrentUser; + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var userName = await _userManager.GetUserNameAsync(user); + var email = await _userManager.GetEmailAsync(user); + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + + Username = userName; + + Input = new InputModel + { + Username = user.UserName, + Name = user.Name, + Surname = user.Surname, + Email = email, + PhoneNumber = phoneNumber + }; + + IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user); + + return Page(); + } + + public async Task OnPostAsync() + { + _logger.LogDebug("Account > Manage > OnPostAsync()"); + ViewData["CurrentUser"] = _service?.CurrentUser; + + if (!ModelState.IsValid) + { + _logger.LogDebug("Invalid ModelState"); + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + _logger.LogDebug($"User not found. Id: {_userManager.GetUserId(User)}"); + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + var email = await _userManager.GetEmailAsync(user); + if (Input.Email != email) + { + var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email); + if (!setEmailResult.Succeeded) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'."); + } + } + + var phoneNumber = await _userManager.GetPhoneNumberAsync(user); + if (Input.PhoneNumber != phoneNumber) + { + var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber); + if (!setPhoneResult.Succeeded) + { + var userId = await _userManager.GetUserIdAsync(user); + throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'."); + } + } + + if (Input.Name != user.Name || Input.Surname != user.Surname) + { + user.Name = Input.Name; + user.Surname = Input.Surname; + await _userManager.UpdateAsync(user); + } + + await _signInManager.RefreshSignInAsync(user); + StatusMessage = "Your profile has been updated"; + + _logger.LogDebug($"User {_userManager.GetUserId(User)} updated"); + + return RedirectToPage(); + } + + public async Task OnPostSendVerificationEmailAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.GetUserAsync(User); + if (user == null) + { + return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'."); + } + + + var userId = await _userManager.GetUserIdAsync(user); + var email = await _userManager.GetEmailAsync(user); + var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); + var callbackUrl = Url.Page( + "/Account/ConfirmEmail", + pageHandler: null, + values: new { userId = userId, code = code }, + protocol: Request.Scheme); + + await _emailSender.SendEmailConfirmationAsync(email, HtmlEncoder.Default.Encode(callbackUrl)); + + StatusMessage = "Verification email sent. Please check your email."; + return RedirectToPage(); + } +} diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs new file mode 100644 index 00000000..437f32fe --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ManageNavPages.cs @@ -0,0 +1,62 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account.Manage; + +public static class ManageNavPages +{ + public static string Index => "Index"; + + public static string ChangePassword => "ChangePassword"; + + public static string DownloadPersonalData => "DownloadPersonalData"; + + public static string DeletePersonalData => "DeletePersonalData"; + + public static string ExternalLogins => "ExternalLogins"; + + public static string PersonalData => "PersonalData"; + + public static string TwoFactorAuthentication => "TwoFactorAuthentication"; + + public static string IndexNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, Index); + } + + public static string ChangePasswordNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, ChangePassword); + } + + public static string DownloadPersonalDataNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, DownloadPersonalData); + } + + public static string DeletePersonalDataNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, DeletePersonalData); + } + + public static string ExternalLoginsNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, ExternalLogins); + } + + public static string PersonalDataNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, PersonalData); + } + + public static string TwoFactorAuthenticationNavClass(ViewContext viewContext) + { + return PageNavClass(viewContext, TwoFactorAuthentication); + } + + public static string PageNavClass(ViewContext viewContext, string page) + { + Guard.Against.Null(viewContext); + + string activePage = viewContext.ViewData["ActivePage"] as string + ?? System.IO.Path.GetFileNameWithoutExtension(viewContext.ActionDescriptor.DisplayName); + return string.Equals(activePage, page, StringComparison.OrdinalIgnoreCase) ? "active" : null; + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/PersonalData.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/ResetAuthenticator.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/SetPassword.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/TwoFactorAuthentication.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_Layout.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_Layout.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_Layout.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_Layout.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ManageNav.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_StatusMessage.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Manage/_ViewImports.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs new file mode 100644 index 00000000..51a7feb8 --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -0,0 +1,84 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class RegisterModel : PageModel +{ + private readonly SignInManager signInManager; + private readonly UserManager userManager; + private readonly ILogger logger; + private readonly IEmailSender emailSender; + + public RegisterModel( + UserManager userManager, + SignInManager signInManager, + ILogger logger, + IEmailSender emailSender) + { + this.userManager = userManager; + this.signInManager = signInManager; + this.logger = logger; + this.emailSender = emailSender; + } + + [BindProperty] + public InputModel Input { get; set; } + + public string ReturnUrl { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + [Display(Name = "Email")] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + [Display(Name = "Password")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + } + + public void OnGet(string returnUrl = null) + { + ReturnUrl = returnUrl; + } + + public async Task OnPostAsync(string returnUrl = null) + { + returnUrl ??= Url.Content("~/"); + if (ModelState.IsValid) + { + var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; + var result = await userManager.CreateAsync(user, Input.Password); + if (result.Succeeded) + { + logger.LogDebug("User created a new account with password."); + + string code = await userManager.GenerateEmailConfirmationTokenAsync(user); + string callbackUrl = Url.Page("/Account/ConfirmEmail", + pageHandler: null, + values: new { userId = user.Id, code }, + protocol: Request.Scheme); + + await emailSender.SendEmailAsync(Input.Email, "Confirm your email", + $"Please confirm your account by clicking here."); + + await signInManager.SignInAsync(user, isPersistent: false); + return LocalRedirect(returnUrl); + } + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + } + + // If we got this far, something failed, redisplay form + return Page(); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml diff --git a/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs new file mode 100644 index 00000000..5232bc6d --- /dev/null +++ b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPassword.cshtml.cs @@ -0,0 +1,77 @@ +namespace CmsEngine.Ui.Areas.Identity.Pages.Account; + +[AllowAnonymous] +public class ResetPasswordModel : PageModel +{ + private readonly UserManager _userManager; + + public ResetPasswordModel(UserManager userManager) + { + _userManager = userManager; + } + + [BindProperty] + public InputModel Input { get; set; } + + public class InputModel + { + [Required] + [EmailAddress] + public string Email { get; set; } + + [Required] + [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] + [DataType(DataType.Password)] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "Confirm password")] + [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] + public string ConfirmPassword { get; set; } + + public string Code { get; set; } + } + + public IActionResult OnGet(string code = null) + { + if (code == null) + { + return BadRequest("A code must be supplied for password reset."); + } + else + { + Input = new InputModel + { + Code = code + }; + return Page(); + } + } + + public async Task OnPostAsync() + { + if (!ModelState.IsValid) + { + return Page(); + } + + var user = await _userManager.FindByEmailAsync(Input.Email); + if (user == null) + { + // Don't reveal that the user does not exist + return RedirectToPage("./ResetPasswordConfirmation"); + } + + var result = await _userManager.ResetPasswordAsync(user, Input.Code, Input.Password); + if (result.Succeeded) + { + return RedirectToPage("./ResetPasswordConfirmation"); + } + + foreach (var error in result.Errors) + { + ModelState.AddModelError(string.Empty, error.Description); + } + return Page(); + } +} diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/ResetPasswordConfirmation.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Account/_ViewImports.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Account/_ViewImports.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Account/_ViewImports.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Account/_ViewImports.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml.cs b/src/CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml.cs similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml.cs rename to src/CmsEngine.Ui/Areas/Identity/Pages/Error.cshtml.cs diff --git a/CmsEngine.Ui/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/_ValidationScriptsPartial.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/_ViewImports.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/_ViewImports.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/_ViewImports.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/_ViewImports.cshtml diff --git a/CmsEngine.Ui/Areas/Identity/Pages/_ViewStart.cshtml b/src/CmsEngine.Ui/Areas/Identity/Pages/_ViewStart.cshtml similarity index 100% rename from CmsEngine.Ui/Areas/Identity/Pages/_ViewStart.cshtml rename to src/CmsEngine.Ui/Areas/Identity/Pages/_ViewStart.cshtml diff --git a/src/CmsEngine.Ui/CmsEngine.Ui.csproj b/src/CmsEngine.Ui/CmsEngine.Ui.csproj new file mode 100644 index 00000000..843b3933 --- /dev/null +++ b/src/CmsEngine.Ui/CmsEngine.Ui.csproj @@ -0,0 +1,30 @@ + + + + net6.0 + enable + enable + aspnet-CmsEngine.Ui-4236C055-B60B-45F7-AA52-69581CC4669C + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CmsEngine.Ui/Controllers/BaseController.cs b/src/CmsEngine.Ui/Controllers/BaseController.cs new file mode 100644 index 00000000..3108d112 --- /dev/null +++ b/src/CmsEngine.Ui/Controllers/BaseController.cs @@ -0,0 +1,65 @@ +namespace CmsEngine.Ui.Controllers; + +public class BaseController : Controller +{ + //protected readonly IService service; + public InstanceViewModel Instance { get; private set; } + public ILogger Logger { get; private set; } + + private readonly ICategoryService _categoryService; + private readonly IPageService _pageService; + private readonly IPostService _postService; + private readonly ITagService _tagService; + + public BaseController(ILoggerFactory loggerFactory, IService service, ICategoryService categoryService, IPageService pageService, IPostService postService, ITagService tagService) + { + Guard.Against.Null(loggerFactory); + Guard.Against.Null(service); + + Logger = loggerFactory.CreateLogger("BaseController"); + Instance = service.Instance; + + _categoryService = categoryService; + _pageService = pageService; + _postService = postService; + _tagService = tagService; + + var cultureInfo = new CultureInfo(Instance.Culture); + + CultureInfo.DefaultThreadCurrentCulture = cultureInfo; + CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; + } + + public async override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + Guard.Against.Null(context); + + if (context.ActionArguments.TryGetValue("q", out object searchValue)) + { + // Showing searched posts + Instance.PagedPosts = await _postService.FindPublishedForPaginationOrderByDateDescending(searchValue.ToString()); + } + else + { + if (context.ActionArguments.TryGetValue("page", out object value) && int.TryParse(value.ToString(), out int page)) + { + // Showing posts after paging + Instance.PagedPosts = await _postService.GetPublishedForPagination(page); + } + else + { + // Showing regular posts + Instance.PagedPosts = await _postService.GetPublishedForPagination(); + } + } + + Instance.LatestPosts = await _postService.GetPublishedLatestPosts(3); + Instance.Pages = await _pageService.GetAllPublished(); + Instance.Categories = await _categoryService.GetCategoriesWithPostCount(); + Instance.CategoriesWithPosts = await _categoryService.GetCategoriesWithPost(); + Instance.Tags = await _tagService.GetAllTags(); + + + await base.OnActionExecutionAsync(context, next); + } +} diff --git a/src/CmsEngine.Ui/Controllers/BlogController.cs b/src/CmsEngine.Ui/Controllers/BlogController.cs new file mode 100644 index 00000000..a9a2ca08 --- /dev/null +++ b/src/CmsEngine.Ui/Controllers/BlogController.cs @@ -0,0 +1,78 @@ +namespace CmsEngine.Ui.Controllers; + +public class BlogController : BaseController +{ + private readonly IPostService _postService; + private readonly IXmlService _xmlService; + + public BlogController(ILoggerFactory loggerFactory, ICategoryService categoryService, IPageService pageService, IPostService postService, + ITagService tagService, IXmlService xmlService, IService service) + : base(loggerFactory, service, categoryService, pageService, postService, tagService) + { + _postService = postService; + _xmlService = xmlService; + } + + public IActionResult Index(int page = 1, string q = "") + { + if (string.IsNullOrWhiteSpace(q)) + { + Instance.PageTitle = page == 1 + ? $"Blog - {Instance.Name}" + : $"Blog - {Instance.Name} - Page {page}"; + } + else + { + Instance.PageTitle = $"Results for '{q}' - {Instance.Name}"; + } + + return View(Instance); + } + + public async Task PostAsync(string slug) + { + Instance.SelectedDocument = await _postService.GetBySlug(slug); + + if (Instance.SelectedDocument == null) + { + return NotFound(); + } + + Instance.PageTitle = $"{Instance.SelectedDocument.Title} - {Instance.Name}"; + return View(Instance); + } + + public async Task CategoryAsync(string slug, int page = 1) + { + Instance.PagedPosts = await _postService.GetPublishedByCategoryForPagination(slug, page); + string selectedCategory = Instance.PagedPosts.SelectMany(p => p.Categories.Where(c => c.Slug == slug).Select(x => x.Name)).FirstOrDefault(); + + if (selectedCategory == null) + { + return NotFound(); + } + + Instance.PageTitle = $"{selectedCategory} - {Instance.Name}"; + return View("Index", Instance); + } + + public async Task TagAsync(string slug, int page = 1) + { + Instance.PagedPosts = await _postService.GetPublishedByTagForPagination(slug, page); + string selectedTag = Instance.PagedPosts.SelectMany(p => p.Tags.Where(t => t.Slug == slug).Select(x => x.Name)).FirstOrDefault(); + + if (selectedTag == null) + { + return NotFound(); + } + + Instance.PageTitle = $"#{selectedTag} - {Instance.Name}"; + return View("Index", Instance); + } + + public async Task FeedAsync() + { + var feed = await _xmlService.GenerateFeed(); + return Content(feed.ToString(), "text/xml"); + } +} diff --git a/src/CmsEngine.Ui/Controllers/ErrorController.cs b/src/CmsEngine.Ui/Controllers/ErrorController.cs new file mode 100644 index 00000000..4dbfe26c --- /dev/null +++ b/src/CmsEngine.Ui/Controllers/ErrorController.cs @@ -0,0 +1,32 @@ +namespace CmsEngine.Ui.Controllers; + +public class ErrorController : Controller +{ + private readonly ILogger _logger; + + public ErrorController(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger("ErrorController"); + } + + [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] + public IActionResult Index(string code) + { + ErrorViewModel errorViewModel; + + switch (code) + { + case "404": + _logger.LogError("Page not found"); + errorViewModel = new ErrorViewModel("404 - Page not found", "Sorry but this page does not exist"); + break; + default: + _logger.LogError("Default error"); + errorViewModel = new ErrorViewModel("Something went wrong"); + break; + } + + ViewBag.ErrorCode = code; + return View(errorViewModel); + } +} diff --git a/src/CmsEngine.Ui/Controllers/HomeController.cs b/src/CmsEngine.Ui/Controllers/HomeController.cs new file mode 100644 index 00000000..7d5fbe25 --- /dev/null +++ b/src/CmsEngine.Ui/Controllers/HomeController.cs @@ -0,0 +1,87 @@ +namespace CmsEngine.Ui.Controllers; + +public class HomeController : BaseController +{ + private readonly ICmsEngineEmailSender _emailSender; + private readonly IPageService _pageService; + private readonly IXmlService _xmlService; + private readonly IEmailService _emailService; + + public HomeController(ILoggerFactory loggerFactory, ICmsEngineEmailSender emailSender, IPageService pageService, IXmlService xmlService, + ICategoryService categoryService, ITagService tagService, IService service, IPostService postService, + IEmailService emailService) + : base(loggerFactory, service, categoryService, pageService, postService, tagService) + { + _emailSender = emailSender; + _pageService = pageService; + _xmlService = xmlService; + _emailService = emailService; + } + + public IActionResult Index() + { + Instance.PageTitle = $"{Instance.Name}"; + return View(Instance); + } + + public async Task PageAsync(string slug) + { + Instance.SelectedDocument = await _pageService.GetBySlug(slug); + + if (Instance.SelectedDocument == null) + { + return NotFound(); + } + + Instance.PageTitle = $"{Instance.SelectedDocument.Title} - {Instance.Name}"; + return View(Instance); + } + + public IActionResult Archive() + { + Instance.PageTitle = $"Archive - {Instance.Name}"; + return View(Instance); + } + + public IActionResult Contact() + { + Instance.PageTitle = $"Contact - {Instance.Name}"; + return View(Instance); + } + + [HttpPost] + public async Task ContactAsync(ContactForm contactForm, string returnUrl = null) + { + if (!ModelState.IsValid) + { + TempData[MessageConstants.WarningMessage] = "Please double check the information in the form and try again."; + return View(Instance); + } + + ViewData["ReturnUrl"] = returnUrl; + contactForm.To = Instance.ContactDetails.Email; + + try + { + if ((await _emailService.Save(contactForm)).IsError) + { + throw new Exception("Error when saving e-mail"); + } + + await _emailSender.SendEmailAsync(contactForm); + TempData[MessageConstants.SuccessMessage] = "Your message was sent. I will answer as soon as I can."; + } + catch (EmailException) + { + TempData[MessageConstants.DangerMessage] = "We could not send the messsage. Please try other communication channels."; + } + + return RedirectToAction(nameof(HomeController.Contact)); + } + + public async Task SitemapAsync() + { + var sitemap = await _xmlService.GenerateSitemap(); + return Content(sitemap.ToString(), "text/xml"); + } +} diff --git a/src/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs b/src/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs new file mode 100644 index 00000000..70f06afb --- /dev/null +++ b/src/CmsEngine.Ui/Extensions/EmailSenderExtensions.cs @@ -0,0 +1,22 @@ +namespace CmsEngine.Ui.Extensions; + +public static class EmailSenderExtensions +{ + public static Task SendEmailConfirmationAsync(this ICmsEngineEmailSender emailSender, string email, string link) + { + var contactForm = new ContactForm(email, + "Confirm your email", + $"Please confirm your account by clicking this link: link"); + + return emailSender.SendEmailAsync(contactForm); + } + + public static Task SendPasswordResetAsync(this ICmsEngineEmailSender emailSender, string email, string link) + { + var contactForm = new ContactForm(email, + "Reset Password", + $"Please reset your password by clicking here: link"); + + return emailSender.SendEmailAsync(contactForm); + } +} diff --git a/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs b/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs new file mode 100644 index 00000000..925b6c95 --- /dev/null +++ b/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs @@ -0,0 +1,15 @@ +namespace CmsEngine.Ui.Extensions; + +public static class HtmlHelperExtensions +{ + public static string IsSelected(this IHtmlHelper htmlHelper, string controllers, string actions, string cssClass = "active") + { + Guard.Against.Null(htmlHelper); + + string currentAction = (htmlHelper.ViewContext.RouteData.Values["action"] as string)?.ToLower(); + string currentController = (htmlHelper.ViewContext.RouteData.Values["controller"] as string)?.ToLower(); + var acceptedActions = (actions ?? currentAction).Split(',').Select(x => x.Trim().ToLower()); + var acceptedControllers = (controllers ?? currentController).Split(',').Select(x => x.Trim().ToLower()); + return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ? cssClass : string.Empty; + } +} diff --git a/src/CmsEngine.Ui/Extensions/MiddlewareExtensions.cs b/src/CmsEngine.Ui/Extensions/MiddlewareExtensions.cs new file mode 100644 index 00000000..58e0b0b1 --- /dev/null +++ b/src/CmsEngine.Ui/Extensions/MiddlewareExtensions.cs @@ -0,0 +1,34 @@ +namespace CmsEngine.Ui.Extensions; + +public static class MiddlewareExtensions +{ + public static IApplicationBuilder ConfigureFileUpload(this IApplicationBuilder builder, FileUploadOptions options) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (options is null) + { + throw new ArgumentNullException(nameof(options)); + } + + return builder.UseMiddleware(options); + } + + public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder builder, SecurityHeadersBuilder securityHeadersBuilder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (securityHeadersBuilder is null) + { + throw new ArgumentNullException(nameof(securityHeadersBuilder)); + } + + return builder.UseMiddleware(securityHeadersBuilder.Build()); + } +} diff --git a/src/CmsEngine.Ui/GlobalUsings.cs b/src/CmsEngine.Ui/GlobalUsings.cs new file mode 100644 index 00000000..1a9435cf --- /dev/null +++ b/src/CmsEngine.Ui/GlobalUsings.cs @@ -0,0 +1,46 @@ +global using System.ComponentModel.DataAnnotations; +global using System.Globalization; +global using System.Net; +global using System.Security.Cryptography.X509Certificates; +global using System.Text; +global using System.Text.Encodings.Web; +global using Ardalis.GuardClauses; +global using CmsEngine.Application.Helpers; +global using CmsEngine.Application.Helpers.Email; +global using CmsEngine.Application.Models.EditModels; +global using CmsEngine.Application.Models.ViewModels; +global using CmsEngine.Application.Models.ViewModels.DataTablesViewModels; +global using CmsEngine.Application.Services; +global using CmsEngine.Application.Services.Interfaces; +global using CmsEngine.Core.Constants; +global using CmsEngine.Core.Exceptions; +global using CmsEngine.Core.Extensions; +global using CmsEngine.Core.Utils; +global using CmsEngine.Data; +global using CmsEngine.Data.Entities; +global using CmsEngine.Data.Repositories; +global using CmsEngine.Data.Repositories.Interfaces; +global using CmsEngine.Ui.Areas.Cms.Controllers; +global using CmsEngine.Ui.Extensions; +global using CmsEngine.Ui.Middleware; +global using CmsEngine.Ui.Middleware.SecurityHeaders; +global using CmsEngine.Ui.RewriteRules; +global using Microsoft.AspNetCore.Authentication; +global using Microsoft.AspNetCore.Authorization; +global using Microsoft.AspNetCore.Http.Extensions; +global using Microsoft.AspNetCore.Identity; +global using Microsoft.AspNetCore.Identity.UI.Services; +global using Microsoft.AspNetCore.Mvc; +global using Microsoft.AspNetCore.Mvc.Filters; +global using Microsoft.AspNetCore.Mvc.RazorPages; +global using Microsoft.AspNetCore.Mvc.Rendering; +global using Microsoft.AspNetCore.Razor.TagHelpers; +global using Microsoft.AspNetCore.Rewrite; +global using Microsoft.EntityFrameworkCore; +global using Microsoft.Extensions.DependencyInjection.Extensions; +global using Microsoft.Extensions.FileProviders; +global using Microsoft.Net.Http.Headers; +global using Newtonsoft.Json; +global using Serilog; +global using SameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode; +global using ILogger = Microsoft.Extensions.Logging.ILogger; diff --git a/src/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs b/src/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs new file mode 100644 index 00000000..3881f461 --- /dev/null +++ b/src/CmsEngine.Ui/Middleware/ConfigureFileUploadMiddleware.cs @@ -0,0 +1,24 @@ +namespace CmsEngine.Ui.Middleware; + +public class ConfigureFileUploadMiddleware +{ + private readonly RequestDelegate _next; + + public ConfigureFileUploadMiddleware(RequestDelegate next) + { + _next = next; + } + + public async Task InvokeAsync(HttpContext context, FileUploadOptions options) + { + string uploadPath = Path.Combine(options.Root, options.Folder); + + if (!Directory.Exists(uploadPath)) + { + Directory.CreateDirectory(uploadPath); + } + + // Call the next delegate/middleware in the pipeline + await _next(context); + } +} diff --git a/src/CmsEngine.Ui/Middleware/FileUploadOptions.cs b/src/CmsEngine.Ui/Middleware/FileUploadOptions.cs new file mode 100644 index 00000000..951a0683 --- /dev/null +++ b/src/CmsEngine.Ui/Middleware/FileUploadOptions.cs @@ -0,0 +1,7 @@ +namespace CmsEngine.Ui.Middleware; + +public class FileUploadOptions +{ + public string Root { get; set; } + public string Folder { get; set; } +} diff --git a/src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs b/src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs new file mode 100644 index 00000000..c033940b --- /dev/null +++ b/src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersBuilder.cs @@ -0,0 +1,208 @@ +namespace CmsEngine.Ui.Middleware.SecurityHeaders; + +/// +/// Exposes methods to build a policy. +/// +public class SecurityHeadersBuilder +{ + private readonly SecurityHeadersPolicy _policy = new(); + + /// + /// The number of seconds in one year + /// + public const int OneYearInSeconds = 60 * 60 * 24 * 365; + + /// + /// Add default headers in accordance with most secure approach + /// + public SecurityHeadersBuilder AddDefaultSecurePolicy() + { + // TODO: Have these settings in a configuration file + + AddFrameOptionsDeny(); + AddXssProtectionBlock(); + AddContentTypeOptionsNoSniff(); + AddStrictTransportSecurityMaxAge(); + RemoveServerHeader(); + + AddCustomHeader("Referrer-Policy", "strict-origin-when-cross-origin"); + AddCustomHeader("Feature-Policy", "geolocation 'none';midi 'none';notifications 'none';push 'none';sync-xhr 'none';" + + "microphone 'none';camera 'none';magnetometer 'none';gyroscope 'none';speaker 'self';" + + "vibrate 'none';fullscreen 'self';payment 'none';"); + AddCustomHeader("Content-Security-Policy", "default-src https: 'unsafe-inline' 'unsafe-eval'; " + + "img-src * 'self' data: https: blob:;" + + "style-src 'self' 'unsafe-inline' github.githubassets.com www.google.com platform.twitter.com cdn.syndication.twimg.com fonts.googleapis.com;" + + "script-src 'self' 'unsafe-inline' 'unsafe-eval' www.gstatic.com gist.github.com *.disqus.com www.googletagmanager.com www.google.com cse.google.com cdn.syndication.twimg.com platform.twitter.com cdn1.developermedia.com cdn2.developermedia.com apis.google.com www.googletagservices.com adservice.google.com securepubads.g.doubleclick.net ajax.aspnetcdn.com *.google-analytics.com"); + + RemoveHeader("X-Powered-By"); + + return this; + } + + /// + /// Add X-Frame-Options DENY to all requests. + /// The page cannot be displayed in a frame, regardless of the site attempting to do so + /// + public SecurityHeadersBuilder AddFrameOptionsDeny() + { + _policy.SetHeaders[FrameOptions.Header] = FrameOptions.Deny; + return this; + } + + /// + /// Add X-Frame-Options SAMEORIGIN to all requests. + /// The page can only be displayed in a frame on the same origin as the page itself. + /// + public SecurityHeadersBuilder AddFrameOptionsSameOrigin() + { + _policy.SetHeaders[FrameOptions.Header] = FrameOptions.SameOrigin; + return this; + } + + /// + /// Add X-Frame-Options ALLOW-FROM {uri} to all requests, where the uri is provided + /// The page can only be displayed in a frame on the specified origin. + /// + /// The uri of the origin in which the page may be displayed in a frame + public SecurityHeadersBuilder AddFrameOptionsSameOrigin(string uri) + { + _policy.SetHeaders[FrameOptions.Header] = string.Format(FrameOptions.AllowFromUri, uri); + return this; + } + + + /// + /// Add X-XSS-Protection 1 to all requests. + /// Enables the XSS Protections + /// + public SecurityHeadersBuilder AddXssProtectionEnabled() + { + _policy.SetHeaders[XssProtection.Header] = XssProtection.Enabled; + return this; + } + + /// + /// Add X-XSS-Protection 0 to all requests. + /// Disables the XSS Protections offered by the user-agent. + /// + public SecurityHeadersBuilder AddXssProtectionDisabled() + { + _policy.SetHeaders[XssProtection.Header] = XssProtection.Disabled; + return this; + } + + /// + /// Add X-XSS-Protection 1; mode=block to all requests. + /// Enables XSS protections and instructs the user-agent to block the response in the event that script has been inserted from user input, instead of sanitizing. + /// + public SecurityHeadersBuilder AddXssProtectionBlock() + { + _policy.SetHeaders[XssProtection.Header] = XssProtection.Block; + return this; + } + + /// + /// Add X-XSS-Protection 1; report=http://site.com/report to all requests. + /// A partially supported directive that tells the user-agent to report potential XSS attacks to a single URL. Data will be POST'd to the report URL in JSON format. + /// + public SecurityHeadersBuilder AddXssProtectionReport(string reportUrl) + { + _policy.SetHeaders[XssProtection.Header] = + string.Format(XssProtection.Report, reportUrl); + return this; + } + + /// + /// Add Strict-Transport-Security max-age= to all requests. + /// Tells the user-agent to cache the domain in the STS list for the number of seconds provided. + /// + public SecurityHeadersBuilder AddStrictTransportSecurityMaxAge(int maxAge = OneYearInSeconds) + { + _policy.SetHeaders[StrictTransportSecurity.Header] = + string.Format(StrictTransportSecurity.MaxAge, maxAge); + return this; + } + + /// + /// Add Strict-Transport-Security max-age=; includeSubDomains to all requests. + /// Tells the user-agent to cache the domain in the STS list for the number of seconds provided and include any sub-domains. + /// + public SecurityHeadersBuilder AddStrictTransportSecurityMaxAgeIncludeSubDomains(int maxAge = OneYearInSeconds) + { + _policy.SetHeaders[StrictTransportSecurity.Header] = + string.Format(StrictTransportSecurity.MaxAgeIncludeSubdomains, maxAge); + return this; + } + + /// + /// Add Strict-Transport-Security max-age=0 to all requests. + /// Tells the user-agent to remove, or not cache the host in the STS cache + /// + public SecurityHeadersBuilder AddStrictTransportSecurityNoCache() + { + _policy.SetHeaders[StrictTransportSecurity.Header] = + StrictTransportSecurity.NoCache; + return this; + } + + /// + /// Add X-Content-Type-Options nosniff to all requests. + /// Can be set to protect against MIME type confusion attacks. + /// + public SecurityHeadersBuilder AddContentTypeOptionsNoSniff() + { + _policy.SetHeaders[ContentTypeOptions.Header] = ContentTypeOptions.NoSniff; + return this; + } + + /// + /// Removes the Server header from all responses + /// + public SecurityHeadersBuilder RemoveServerHeader() + { + _policy.RemoveHeaders.Add(Server.Header); + return this; + } + + /// + /// Adds a custom header to all requests + /// + /// The header name + /// The value for the header + /// + public SecurityHeadersBuilder AddCustomHeader(string header, string value) + { + if (string.IsNullOrEmpty(header)) + { + throw new ArgumentNullException(nameof(header)); + } + + _policy.SetHeaders[header] = value; + return this; + } + + /// + /// Remove a header from all requests + /// + /// The to remove + /// + public SecurityHeadersBuilder RemoveHeader(string header) + { + if (string.IsNullOrEmpty(header)) + { + throw new ArgumentNullException(nameof(header)); + } + + _policy.RemoveHeaders.Add(header); + return this; + } + + /// + /// Builds a new using the entries added. + /// + /// The constructed . + public SecurityHeadersPolicy Build() + { + return _policy; + } +} diff --git a/src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs b/src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs new file mode 100644 index 00000000..90db5e94 --- /dev/null +++ b/src/CmsEngine.Ui/Middleware/SecurityHeaders/SecurityHeadersPolicy.cs @@ -0,0 +1,14 @@ +namespace CmsEngine.Ui.Middleware.SecurityHeaders; + +public class SecurityHeadersPolicy +{ + /// + /// A dictionary of Header, Value pairs that should be added to all requests + /// + public IDictionary SetHeaders { get; } = new Dictionary(); + + /// + /// A hashset of Headers that should be removed from all requests + /// + public ISet RemoveHeaders { get; } = new HashSet(); +} diff --git a/src/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs b/src/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs new file mode 100644 index 00000000..cbb99ca9 --- /dev/null +++ b/src/CmsEngine.Ui/Middleware/SecurityHeadersMiddleware.cs @@ -0,0 +1,55 @@ +namespace CmsEngine.Ui.Middleware; + +/// +/// An ASP.NET middleware for adding security headers. +/// +public class SecurityHeadersMiddleware +{ + private readonly RequestDelegate next; + private readonly SecurityHeadersPolicy policy; + + /// + /// Instantiates a new . + /// + /// The next middleware in the pipeline. + /// An instance of the which can be applied. + public SecurityHeadersMiddleware(RequestDelegate next, SecurityHeadersPolicy policy) + { + if (next == null) + { + throw new ArgumentNullException(nameof(next)); + } + + this.next = next ?? throw new ArgumentNullException(nameof(policy)); + this.policy = policy; + } + + public async Task Invoke(HttpContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var response = context.Response; + + if (response == null) + { + throw new ArgumentNullException(nameof(response)); + } + + var headers = response.Headers; + + foreach (var headerValuePair in policy.SetHeaders) + { + headers[headerValuePair.Key] = headerValuePair.Value; + } + + foreach (var header in policy.RemoveHeaders) + { + headers.Remove(header); + } + + await next(context); + } +} diff --git a/src/CmsEngine.Ui/Program.cs b/src/CmsEngine.Ui/Program.cs new file mode 100644 index 00000000..8c51372a --- /dev/null +++ b/src/CmsEngine.Ui/Program.cs @@ -0,0 +1,200 @@ +var builder = WebApplication.CreateBuilder(args); + +// Load json files +string environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development"; +builder.Configuration.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true) + .AddJsonFile("emailsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"emailsettings.{environment}.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + +// Initializing Logger +Log.Logger = new LoggerConfiguration().ReadFrom.Configuration(builder.Configuration) + .Enrich.FromLogContext().CreateLogger(); + +// Add certificate for development +// https://dotnetplaybook.com/custom-local-domain-using-https-kestrel-asp-net-core/ +if (builder.Environment.IsDevelopment()) +{ + var certificatePassword = builder.Configuration.GetSection("Kestrel:Certificates:Password").Value; + + Log.Debug("Dev environment: Using Kestrel with port 5001"); + builder.WebHost.ConfigureKestrel(options => + { + options.AddServerHeader = false; + options.Listen(IPAddress.Loopback, 5001, listenOptions => + { + listenOptions.UseHttps(new X509Certificate2("cmsengine.test.pfx", certificatePassword)); + }); + }); +} + +// Add services to the container. +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); +builder.Services.AddDbContext(options => options.EnableSensitiveDataLogging(builder.Environment.IsDevelopment()) + .UseSqlServer(connectionString, + o => o.MigrationsAssembly("CmsEngine.Data") + .UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery))); +builder.Services.AddDatabaseDeveloperPageExceptionFilter(); + +builder.Services.Configure(options => +{ + // This lambda determines whether user consent for non-essential cookies is needed for a given request. + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = SameSiteMode.None; +}); + +builder.Services.Configure(builder.Configuration.GetSection("EmailSettings")); + +builder.Services.AddDefaultIdentity(options => options.SignIn.RequireConfirmedAccount = true) + .AddEntityFrameworkStores() + .AddDefaultTokenProviders(); + +// Add HttpContextAccessor as .NET Core doesn't have HttpContext.Current anymore +builder.Services.TryAddSingleton(); + +// Add Repositories +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +// Add services +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); + +// Add Unit of Work +builder.Services.AddScoped(); + +builder.Services.AddSingleton(); + +builder.Services.AddControllersWithViews(); +builder.Services.AddRazorPages(); + +builder.Services.ConfigureApplicationCookie(options => +{ + options.LoginPath = $"/Identity/Account/Login"; + options.LogoutPath = $"/Identity/Account/Logout"; + options.AccessDeniedPath = $"/Identity/Account/AccessDenied"; +}); + +if (!builder.Environment.IsDevelopment()) +{ + builder.Services.AddHttpsRedirection(options => + { + options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect; + options.HttpsPort = 443; + }); +} + +builder.Host.UseSerilog(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); + app.UseMigrationsEndPoint(); +} +else +{ + app.UseStatusCodePagesWithReExecute("/error", "?code={0}"); + // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. + app.UseHsts(); +} + +const int http301 = StatusCodes.Status301MovedPermanently; + +// Added compatibility with the old davidsonsousa.net +var rewriteOptions = new RewriteOptions().Add(new RedirectToNonWwwRule(http301)) + .Add(new RedirectLowerCaseRule(http301)) + .AddRedirect("^en/(.*)", "blog/$1", http301) + .AddRedirect("^pt/(.*)", "blog/$1", http301) + .AddRedirect("^image/articles/(.*)", "image/post/$1", http301) + .AddRedirect("^image/pages/(.*)", "image/page/$1", http301) + .AddRedirect("^file/articles/(.*)", "file/post/$1", http301) + .AddRedirect("^file/pages/(.*)", "file/page/$1", http301); +app.UseRewriter(rewriteOptions); +app.UseHttpsRedirection(); + +// wwwroot +app.UseStaticFiles(); + +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.WebRootPath, "UploadedFiles")), + RequestPath = "/image" +}); + +app.UseStaticFiles(new StaticFileOptions +{ + FileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.WebRootPath, "UploadedFiles")), + RequestPath = "/file" +}); + +app.UseSecurityHeaders(new SecurityHeadersBuilder().AddDefaultSecurePolicy()); + +app.UseCookiePolicy(); + +app.UseRouting(); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllerRoute( + name: "areaRoute", + pattern: "{area:exists}/{controller=Home}/{action=Index}/{vanityId?}"); + +app.MapControllerRoute( + name: "blog", + pattern: "blog/{action}/{slug?}", + defaults: new { controller = "Blog", action = "Index" }); + +app.MapControllerRoute( + name: "main", + pattern: "", + defaults: new { controller = "Home", action = "Index" }); + +app.MapControllerRoute( + name: "sitemap", + pattern: "sitemap", + defaults: new { controller = "Home", action = "Sitemap" }); + +app.MapControllerRoute( + name: "archive", + pattern: "archive", + defaults: new { controller = "Home", action = "Archive" }); + +app.MapControllerRoute( + name: "contact", + pattern: "contact", + defaults: new { controller = "Home", action = "Contact" }); + +app.MapControllerRoute( + name: "error", + pattern: "error", + defaults: new { controller = "Error", action = "Index" }); + +app.MapControllerRoute( + name: "page", + pattern: "{slug}", + defaults: new { controller = "Home", action = "Page" }); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + +app.MapRazorPages(); + +app.Run(); diff --git a/CmsEngine.Ui/Properties/launchSettings.json b/src/CmsEngine.Ui/Properties/launchSettings.json similarity index 67% rename from CmsEngine.Ui/Properties/launchSettings.json rename to src/CmsEngine.Ui/Properties/launchSettings.json index 56097f75..949a46ee 100644 --- a/CmsEngine.Ui/Properties/launchSettings.json +++ b/src/CmsEngine.Ui/Properties/launchSettings.json @@ -1,27 +1,28 @@ -{ +{ "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, + "windowsAuthentication": false, + "anonymousAuthentication": true, "iisExpress": { - "applicationUrl": "http://localhost:59780", - "sslPort": 44305 + "applicationUrl": "http://localhost:14771", + "sslPort": 44321 } }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, + "CmsEngine.Ui": { + "commandName": "Project", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" - } + }, + "hotReloadEnabled": false, + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "dotnetRunMessages": true }, - "CmsEngine.Ui": { - "commandName": "Project", + "IIS Express": { + "commandName": "IISExpress", "launchBrowser": true, - "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } -} +} \ No newline at end of file diff --git a/src/CmsEngine.Ui/Properties/serviceDependencies.json b/src/CmsEngine.Ui/Properties/serviceDependencies.json new file mode 100644 index 00000000..d8177e07 --- /dev/null +++ b/src/CmsEngine.Ui/Properties/serviceDependencies.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "mssql1": { + "type": "mssql", + "connectionId": "ConnectionStrings:DefaultConnection" + } + } +} \ No newline at end of file diff --git a/src/CmsEngine.Ui/Properties/serviceDependencies.local.json b/src/CmsEngine.Ui/Properties/serviceDependencies.local.json new file mode 100644 index 00000000..299aa9aa --- /dev/null +++ b/src/CmsEngine.Ui/Properties/serviceDependencies.local.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "mssql1": { + "type": "mssql.local", + "connectionId": "ConnectionStrings:DefaultConnection" + } + } +} \ No newline at end of file diff --git a/src/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs b/src/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs new file mode 100644 index 00000000..9f504ef3 --- /dev/null +++ b/src/CmsEngine.Ui/RewriteRules/RedirectLowerCaseRule.cs @@ -0,0 +1,29 @@ +namespace CmsEngine.Ui.RewriteRules; + +public class RedirectLowerCaseRule : IRule +{ + private readonly int _statusCode; + public RedirectLowerCaseRule(int statusCode) + { + _statusCode = statusCode; + } + + public void ApplyRule(RewriteContext context) + { + var request = context.HttpContext.Request; + var path = context.HttpContext.Request.Path; + var host = context.HttpContext.Request.Host; + + if ((request.Method == "GET") && ((path.HasValue && path.Value.Any(char.IsUpper)) || (host.HasValue && host.Value.Any(char.IsUpper)))) + { + var response = context.HttpContext.Response; + response.StatusCode = _statusCode; + response.Headers[HeaderNames.Location] = (request.Scheme + "://" + host.Value + request.PathBase + request.Path).ToLower() + request.QueryString; + context.Result = RuleResult.EndResponse; + } + else + { + context.Result = RuleResult.ContinueRules; + } + } +} diff --git a/src/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs b/src/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs new file mode 100644 index 00000000..1e7b6df7 --- /dev/null +++ b/src/CmsEngine.Ui/RewriteRules/RedirectToNonWwwRule.cs @@ -0,0 +1,34 @@ +namespace CmsEngine.Ui.RewriteRules; + +public class RedirectToNonWwwRule : IRule +{ + private readonly int _statusCode; + + public RedirectToNonWwwRule(int statusCode) + { + _statusCode = statusCode; + } + + public virtual void ApplyRule(RewriteContext context) + { + var httpRequest = context.HttpContext.Request; + if (httpRequest.Host.Host.Equals(Main.Localhost, StringComparison.OrdinalIgnoreCase)) + { + context.Result = RuleResult.ContinueRules; + return; + } + + if (!httpRequest.Host.Value.StartsWith(Main.WwwDot, StringComparison.OrdinalIgnoreCase)) + { + context.Result = RuleResult.ContinueRules; + return; + } + + var wwwHost = new HostString(httpRequest.Host.Value.Replace(Main.WwwDot, string.Empty)); + var newUrl = UriHelper.BuildAbsolute(httpRequest.Scheme, wwwHost, httpRequest.PathBase, httpRequest.Path, httpRequest.QueryString); + var httpResponse = context.HttpContext.Response; + httpResponse.StatusCode = _statusCode; + httpResponse.Headers[HeaderNames.Location] = newUrl; + context.Result = RuleResult.EndResponse; + } +} diff --git a/src/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs b/src/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs new file mode 100644 index 00000000..f690230f --- /dev/null +++ b/src/CmsEngine.Ui/TagHelpers/CheckboxListTagHelper.cs @@ -0,0 +1,67 @@ +namespace CmsEngine.Ui.TagHelpers; + +public class CheckboxListTagHelper : TagHelper +{ + /// + /// Checkbox name, used to group all checkboxes in the list + /// + public string Name { get; set; } + + /// + /// Class to be assigned to the outer
container + ///
+ public string OuterContainerClass { get; set; } + + /// + /// Class to be assigned to the inner
container + ///
+ public string InnerContainerClass { get; set; } + + /// + /// Class to be assigned to the + public string LabelClass { get; set; } + + /// + /// Class to be assigned to the + /// + public string InputClass { get; set; } + + /// + /// Items to appear in the checkbox list + /// + public IEnumerable Items { get; set; } + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (Items == null) + { + return; + } + + output.TagName = "div"; + output.Attributes.SetAttribute("class", OuterContainerClass); + + InnerContainerClass = !string.IsNullOrWhiteSpace(InnerContainerClass) ? $" class=\"{InnerContainerClass}\"" : ""; + LabelClass = !string.IsNullOrWhiteSpace(LabelClass) ? $" class=\"{LabelClass}\"" : ""; + InputClass = !string.IsNullOrWhiteSpace(InputClass) ? $" class=\"{InputClass}\"" : ""; + + string isChecked; + string isEnabled; + var sb = new StringBuilder(); + + foreach (var item in Items) + { + isChecked = item.Selected ? " checked" : ""; + isEnabled = item.Enabled ? "" : " disabled"; + sb.Append("'); + sb.Append("'); + sb.Append("').Append(item.Label); + sb.Append(""); + } + + output.Content.SetHtmlContent(sb.ToString()); + output.TagMode = TagMode.StartTagAndEndTag; + } +} diff --git a/src/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs b/src/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs new file mode 100644 index 00000000..6a25d765 --- /dev/null +++ b/src/CmsEngine.Ui/TagHelpers/GravatarTagHelper.cs @@ -0,0 +1,69 @@ +namespace CmsEngine.Ui.TagHelpers; + +public class GravatarTagHelper : TagHelper +{ + /// + /// E-mail address registered on gravatar.com + /// + public string EmailAddress { get; set; } + + /// + /// Image size (default is 80) + /// + public int ImageSize { get; set; } = 80; + + /// + /// Default image loaded from gravatar.com (default is 0) + /// + public DefaultImage DefaultImage { get; set; } = DefaultImage.Default; + + /// + /// Default image url loaded from gravatar.com (default is "") + /// + public string DefaultImageUrl { get; set; } = ""; + public bool ForceDefaultImage { get; set; } + + /// + /// Image rating (default is G) + /// + public Rating Rating { get; set; } = Rating.G; + public bool ForceSecureRequest { get; set; } + public string AdditionalCssClasses { get; set; } + + public override void Process(TagHelperContext context, TagHelperOutput output) + { + if (output is null) + { + throw new ArgumentNullException(nameof(output)); + } + + output.TagName = "img"; + string email = string.IsNullOrWhiteSpace(EmailAddress) ? string.Empty : EmailAddress.ToLower(); + + output.Attributes.Add("src", + string.Format("{0}://{1}.gravatar.com/avatar/{2}?s={3}{4}{5}{6}", + "https", + "s", + GravatarUtilities.GetMd5Hash(email), + ImageSize, + "&d=" + (!string.IsNullOrEmpty(DefaultImageUrl) ? HtmlEncoder.Default.Encode(DefaultImageUrl) : DefaultImage.GetDescription()), + ForceDefaultImage ? "&f=y" : "", + "&r=" + Rating.GetDescription() + ) + ); + + if (!string.IsNullOrWhiteSpace(AdditionalCssClasses)) + { + if (output.Attributes.Any(x => string.Equals(x.Name, "class", StringComparison.OrdinalIgnoreCase))) + { + AdditionalCssClasses = output.Attributes.First(x => string.Equals(x.Name, "class", StringComparison.OrdinalIgnoreCase)).Value + " " + AdditionalCssClasses; + output.Attributes.Remove(output.Attributes.First(x => string.Equals(x.Name, "class", StringComparison.OrdinalIgnoreCase))); + } + + // Add the additional CSS classes + output.Attributes.Add("class", AdditionalCssClasses); + } + + base.Process(context, output); + } +} diff --git a/CmsEngine.Ui/Views/Blog/Index.cshtml b/src/CmsEngine.Ui/Views/Blog/Index.cshtml similarity index 96% rename from CmsEngine.Ui/Views/Blog/Index.cshtml rename to src/CmsEngine.Ui/Views/Blog/Index.cshtml index 51abb5eb..1a2e287d 100644 --- a/CmsEngine.Ui/Views/Blog/Index.cshtml +++ b/src/CmsEngine.Ui/Views/Blog/Index.cshtml @@ -2,7 +2,7 @@ @{ string headerImagePath = !string.IsNullOrWhiteSpace(Model.HeaderImage) - ? string.Format(CmsEngineConstants.ImagePath.Path640, "website", Model.HeaderImage) + ? string.Format(Main.ImagePath.Path640, "website", Model.HeaderImage) : "/img/no-image.png"; } diff --git a/CmsEngine.Ui/Views/Blog/Post.cshtml b/src/CmsEngine.Ui/Views/Blog/Post.cshtml similarity index 95% rename from CmsEngine.Ui/Views/Blog/Post.cshtml rename to src/CmsEngine.Ui/Views/Blog/Post.cshtml index b268cf54..bec0f4b6 100644 --- a/CmsEngine.Ui/Views/Blog/Post.cshtml +++ b/src/CmsEngine.Ui/Views/Blog/Post.cshtml @@ -2,7 +2,7 @@ @{ var selectedPost = (PostViewModel)Model.SelectedDocument; string imagePath = !string.IsNullOrWhiteSpace(selectedPost.HeaderImage) - ? string.Format(CmsEngineConstants.ImagePath.Default, "post", selectedPost.HeaderImage) + ? string.Format(Main.ImagePath.Default, "post", selectedPost.HeaderImage) : "/img/no-image.png"; } @@ -27,7 +27,7 @@
@if (!string.IsNullOrWhiteSpace(selectedPost.HeaderImage)) { - @selectedPost.Title + @selectedPost.Title }
diff --git a/CmsEngine.Ui/Views/Blog/_PagedPost.cshtml b/src/CmsEngine.Ui/Views/Blog/_PagedPost.cshtml similarity index 93% rename from CmsEngine.Ui/Views/Blog/_PagedPost.cshtml rename to src/CmsEngine.Ui/Views/Blog/_PagedPost.cshtml index 7c1415a8..f1e1ea4c 100644 --- a/CmsEngine.Ui/Views/Blog/_PagedPost.cshtml +++ b/src/CmsEngine.Ui/Views/Blog/_PagedPost.cshtml @@ -4,7 +4,7 @@ @{ string imagePath = !string.IsNullOrWhiteSpace(Model.HeaderImage) - ? imagePath = string.Format(CmsEngineConstants.ImagePath.Path640, "post", Model.HeaderImage) + ? imagePath = string.Format(Main.ImagePath.Path640, "post", Model.HeaderImage) : "/img/no-image.png"; } @Model.Title diff --git a/CmsEngine.Ui/Views/Error/Index.cshtml b/src/CmsEngine.Ui/Views/Error/Index.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Error/Index.cshtml rename to src/CmsEngine.Ui/Views/Error/Index.cshtml diff --git a/CmsEngine.Ui/Views/Home/Archive.cshtml b/src/CmsEngine.Ui/Views/Home/Archive.cshtml similarity index 96% rename from CmsEngine.Ui/Views/Home/Archive.cshtml rename to src/CmsEngine.Ui/Views/Home/Archive.cshtml index 245751b6..d8c9bb56 100644 --- a/CmsEngine.Ui/Views/Home/Archive.cshtml +++ b/src/CmsEngine.Ui/Views/Home/Archive.cshtml @@ -2,7 +2,7 @@ @{ string imagePath = !string.IsNullOrWhiteSpace(Model.HeaderImage) - ? string.Format(CmsEngineConstants.ImagePath.Default, "page", Model.HeaderImage) + ? string.Format(Main.ImagePath.Default, "page", Model.HeaderImage) : "/img/no-image.png"; } diff --git a/CmsEngine.Ui/Views/Home/Contact.cshtml b/src/CmsEngine.Ui/Views/Home/Contact.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Home/Contact.cshtml rename to src/CmsEngine.Ui/Views/Home/Contact.cshtml diff --git a/CmsEngine.Ui/Views/Home/Index.cshtml b/src/CmsEngine.Ui/Views/Home/Index.cshtml similarity index 85% rename from CmsEngine.Ui/Views/Home/Index.cshtml rename to src/CmsEngine.Ui/Views/Home/Index.cshtml index a0b2e063..f2915fca 100644 --- a/CmsEngine.Ui/Views/Home/Index.cshtml +++ b/src/CmsEngine.Ui/Views/Home/Index.cshtml @@ -2,7 +2,7 @@ @{ string headerImagePath = !string.IsNullOrWhiteSpace(Model.HeaderImage) - ? string.Format(CmsEngineConstants.ImagePath.Path640, "website", Model.HeaderImage) + ? string.Format(Main.ImagePath.Path640, "website", Model.HeaderImage) : "/img/no-image.png"; } @@ -20,7 +20,7 @@ } -
+
diff --git a/CmsEngine.Ui/Views/Home/Page.cshtml b/src/CmsEngine.Ui/Views/Home/Page.cshtml similarity index 95% rename from CmsEngine.Ui/Views/Home/Page.cshtml rename to src/CmsEngine.Ui/Views/Home/Page.cshtml index 08a6a101..e09a9ab4 100644 --- a/CmsEngine.Ui/Views/Home/Page.cshtml +++ b/src/CmsEngine.Ui/Views/Home/Page.cshtml @@ -3,7 +3,7 @@ @{ var selectedPage = (PageViewModel)Model.SelectedDocument; string imagePath = !string.IsNullOrWhiteSpace(selectedPage.HeaderImage) - ? string.Format(CmsEngineConstants.ImagePath.Default, "page", selectedPage.HeaderImage) + ? string.Format(Main.ImagePath.Default, "page", selectedPage.HeaderImage) : "/img/no-image.png"; } diff --git a/CmsEngine.Ui/Views/Home/Privacy.cshtml b/src/CmsEngine.Ui/Views/Home/Privacy.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Home/Privacy.cshtml rename to src/CmsEngine.Ui/Views/Home/Privacy.cshtml diff --git a/CmsEngine.Ui/Views/Home/_PostIndex.cshtml b/src/CmsEngine.Ui/Views/Home/_PostIndex.cshtml similarity index 94% rename from CmsEngine.Ui/Views/Home/_PostIndex.cshtml rename to src/CmsEngine.Ui/Views/Home/_PostIndex.cshtml index 94987135..1d0d26dd 100644 --- a/CmsEngine.Ui/Views/Home/_PostIndex.cshtml +++ b/src/CmsEngine.Ui/Views/Home/_PostIndex.cshtml @@ -2,7 +2,7 @@
@{ string imagePath = !string.IsNullOrWhiteSpace(Model.HeaderImage) - ? imagePath = string.Format(CmsEngineConstants.ImagePath.Path640, "post", Model.HeaderImage) + ? imagePath = string.Format(Main.ImagePath.Path640, "post", Model.HeaderImage) : "/img/no-image.png"; } diff --git a/CmsEngine.Ui/Views/Shared/Cms/_Dialog.cshtml b/src/CmsEngine.Ui/Views/Shared/Cms/_Dialog.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/Cms/_Dialog.cshtml rename to src/CmsEngine.Ui/Views/Shared/Cms/_Dialog.cshtml diff --git a/CmsEngine.Ui/Views/Shared/Cms/_ExternalLayout.cshtml b/src/CmsEngine.Ui/Views/Shared/Cms/_ExternalLayout.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/Cms/_ExternalLayout.cshtml rename to src/CmsEngine.Ui/Views/Shared/Cms/_ExternalLayout.cshtml diff --git a/CmsEngine.Ui/Views/Shared/Cms/_Layout.cshtml b/src/CmsEngine.Ui/Views/Shared/Cms/_Layout.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/Cms/_Layout.cshtml rename to src/CmsEngine.Ui/Views/Shared/Cms/_Layout.cshtml diff --git a/CmsEngine.Ui/Views/Shared/EditorTemplates/DateTime.cshtml b/src/CmsEngine.Ui/Views/Shared/EditorTemplates/DateTime.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/EditorTemplates/DateTime.cshtml rename to src/CmsEngine.Ui/Views/Shared/EditorTemplates/DateTime.cshtml diff --git a/CmsEngine.Ui/Views/Shared/_CookieConsentPartial.cshtml b/src/CmsEngine.Ui/Views/Shared/_CookieConsentPartial.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/_CookieConsentPartial.cshtml rename to src/CmsEngine.Ui/Views/Shared/_CookieConsentPartial.cshtml diff --git a/CmsEngine.Ui/Views/Shared/_Footer.cshtml b/src/CmsEngine.Ui/Views/Shared/_Footer.cshtml similarity index 98% rename from CmsEngine.Ui/Views/Shared/_Footer.cshtml rename to src/CmsEngine.Ui/Views/Shared/_Footer.cshtml index 5c785be0..5c90fe60 100644 --- a/CmsEngine.Ui/Views/Shared/_Footer.cshtml +++ b/src/CmsEngine.Ui/Views/Shared/_Footer.cshtml @@ -79,7 +79,7 @@
@{ string imagePath = !string.IsNullOrWhiteSpace(latestPost.HeaderImage) - ? imagePath = string.Format(CmsEngineConstants.ImagePath.Path120, "post", latestPost.HeaderImage) + ? imagePath = string.Format(Main.ImagePath.Path120, "post", latestPost.HeaderImage) : "/img/no-image.png"; } @latestPost.Title diff --git a/CmsEngine.Ui/Views/Shared/_Layout.cshtml b/src/CmsEngine.Ui/Views/Shared/_Layout.cshtml similarity index 99% rename from CmsEngine.Ui/Views/Shared/_Layout.cshtml rename to src/CmsEngine.Ui/Views/Shared/_Layout.cshtml index 190cac5c..974c5133 100644 --- a/CmsEngine.Ui/Views/Shared/_Layout.cshtml +++ b/src/CmsEngine.Ui/Views/Shared/_Layout.cshtml @@ -1,4 +1,3 @@ -@using CmsEngine.Ui.Extensions @model InstanceViewModel diff --git a/CmsEngine.Ui/Views/Shared/_LoginPartial.cshtml b/src/CmsEngine.Ui/Views/Shared/_LoginPartial.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/_LoginPartial.cshtml rename to src/CmsEngine.Ui/Views/Shared/_LoginPartial.cshtml diff --git a/CmsEngine.Ui/Views/Shared/_Messages.cshtml b/src/CmsEngine.Ui/Views/Shared/_Messages.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/_Messages.cshtml rename to src/CmsEngine.Ui/Views/Shared/_Messages.cshtml diff --git a/CmsEngine.Ui/Views/Shared/_SearchForm.cshtml b/src/CmsEngine.Ui/Views/Shared/_SearchForm.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/_SearchForm.cshtml rename to src/CmsEngine.Ui/Views/Shared/_SearchForm.cshtml diff --git a/CmsEngine.Ui/Views/Shared/_Sidebar.cshtml b/src/CmsEngine.Ui/Views/Shared/_Sidebar.cshtml similarity index 96% rename from CmsEngine.Ui/Views/Shared/_Sidebar.cshtml rename to src/CmsEngine.Ui/Views/Shared/_Sidebar.cshtml index 9d7ed404..385de221 100644 --- a/CmsEngine.Ui/Views/Shared/_Sidebar.cshtml +++ b/src/CmsEngine.Ui/Views/Shared/_Sidebar.cshtml @@ -21,7 +21,7 @@
@{ string imagePath = !string.IsNullOrWhiteSpace(latestPost.HeaderImage) - ? imagePath = string.Format(CmsEngineConstants.ImagePath.Path120, "post", latestPost.HeaderImage) + ? imagePath = string.Format(Main.ImagePath.Path120, "post", latestPost.HeaderImage) : "/img/no-image.png"; } @latestPost.Title diff --git a/CmsEngine.Ui/Views/Shared/_ValidationScriptsPartial.cshtml b/src/CmsEngine.Ui/Views/Shared/_ValidationScriptsPartial.cshtml similarity index 100% rename from CmsEngine.Ui/Views/Shared/_ValidationScriptsPartial.cshtml rename to src/CmsEngine.Ui/Views/Shared/_ValidationScriptsPartial.cshtml diff --git a/CmsEngine.Ui/Views/_ViewImports.cshtml b/src/CmsEngine.Ui/Views/_ViewImports.cshtml similarity index 68% rename from CmsEngine.Ui/Views/_ViewImports.cshtml rename to src/CmsEngine.Ui/Views/_ViewImports.cshtml index 066ba1d9..1778907c 100644 --- a/CmsEngine.Ui/Views/_ViewImports.cshtml +++ b/src/CmsEngine.Ui/Views/_ViewImports.cshtml @@ -1,7 +1,7 @@ @using CmsEngine.Core @using CmsEngine.Core.Constants -@using CmsEngine.Ui @using CmsEngine.Data.Entities -@using CmsEngine.Application.ViewModels +@using CmsEngine.Application.Models.ViewModels +@using CmsEngine.Ui.Extensions @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @addTagHelper *, CmsEngine.Ui diff --git a/CmsEngine.Ui/Views/_ViewStart.cshtml b/src/CmsEngine.Ui/Views/_ViewStart.cshtml similarity index 100% rename from CmsEngine.Ui/Views/_ViewStart.cshtml rename to src/CmsEngine.Ui/Views/_ViewStart.cshtml diff --git a/CmsEngine.Ui/appsettings.Development.json b/src/CmsEngine.Ui/appsettings.Development.json similarity index 100% rename from CmsEngine.Ui/appsettings.Development.json rename to src/CmsEngine.Ui/appsettings.Development.json diff --git a/CmsEngine.Ui/appsettings.json b/src/CmsEngine.Ui/appsettings.json similarity index 71% rename from CmsEngine.Ui/appsettings.json rename to src/CmsEngine.Ui/appsettings.json index 0089caa4..18ffc369 100644 --- a/CmsEngine.Ui/appsettings.json +++ b/src/CmsEngine.Ui/appsettings.json @@ -1,12 +1,15 @@ { "Logging": { - "LogLevel": { - "Default": "Warning" + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } } }, "Serilog": { "MinimumLevel": { - "Default": "Warning", + "Default": "Information", "Override": { "System": "Warning", "Microsoft": "Warning" diff --git a/CmsEngine.Ui/assets/fonts/blog.eot b/src/CmsEngine.Ui/assets/fonts/blog.eot similarity index 100% rename from CmsEngine.Ui/assets/fonts/blog.eot rename to src/CmsEngine.Ui/assets/fonts/blog.eot diff --git a/CmsEngine.Ui/assets/fonts/blog.svg b/src/CmsEngine.Ui/assets/fonts/blog.svg similarity index 100% rename from CmsEngine.Ui/assets/fonts/blog.svg rename to src/CmsEngine.Ui/assets/fonts/blog.svg diff --git a/CmsEngine.Ui/assets/fonts/blog.ttf b/src/CmsEngine.Ui/assets/fonts/blog.ttf similarity index 100% rename from CmsEngine.Ui/assets/fonts/blog.ttf rename to src/CmsEngine.Ui/assets/fonts/blog.ttf diff --git a/CmsEngine.Ui/assets/fonts/blog.woff b/src/CmsEngine.Ui/assets/fonts/blog.woff similarity index 100% rename from CmsEngine.Ui/assets/fonts/blog.woff rename to src/CmsEngine.Ui/assets/fonts/blog.woff diff --git a/CmsEngine.Ui/assets/img/favicon.ico b/src/CmsEngine.Ui/assets/img/favicon.ico similarity index 100% rename from CmsEngine.Ui/assets/img/favicon.ico rename to src/CmsEngine.Ui/assets/img/favicon.ico diff --git a/CmsEngine.Ui/assets/img/favicon.png b/src/CmsEngine.Ui/assets/img/favicon.png similarity index 100% rename from CmsEngine.Ui/assets/img/favicon.png rename to src/CmsEngine.Ui/assets/img/favicon.png diff --git a/CmsEngine.Ui/assets/img/flags/ASEAN.png b/src/CmsEngine.Ui/assets/img/flags/ASEAN.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/ASEAN.png rename to src/CmsEngine.Ui/assets/img/flags/ASEAN.png diff --git a/CmsEngine.Ui/assets/img/flags/Afghanistan.png b/src/CmsEngine.Ui/assets/img/flags/Afghanistan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Afghanistan.png rename to src/CmsEngine.Ui/assets/img/flags/Afghanistan.png diff --git a/CmsEngine.Ui/assets/img/flags/African Union.png b/src/CmsEngine.Ui/assets/img/flags/African Union.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/African Union.png rename to src/CmsEngine.Ui/assets/img/flags/African Union.png diff --git a/CmsEngine.Ui/assets/img/flags/Albania.png b/src/CmsEngine.Ui/assets/img/flags/Albania.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Albania.png rename to src/CmsEngine.Ui/assets/img/flags/Albania.png diff --git a/CmsEngine.Ui/assets/img/flags/Algeria.png b/src/CmsEngine.Ui/assets/img/flags/Algeria.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Algeria.png rename to src/CmsEngine.Ui/assets/img/flags/Algeria.png diff --git a/CmsEngine.Ui/assets/img/flags/American Samoa.png b/src/CmsEngine.Ui/assets/img/flags/American Samoa.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/American Samoa.png rename to src/CmsEngine.Ui/assets/img/flags/American Samoa.png diff --git a/CmsEngine.Ui/assets/img/flags/Andorra.png b/src/CmsEngine.Ui/assets/img/flags/Andorra.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Andorra.png rename to src/CmsEngine.Ui/assets/img/flags/Andorra.png diff --git a/CmsEngine.Ui/assets/img/flags/Angola.png b/src/CmsEngine.Ui/assets/img/flags/Angola.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Angola.png rename to src/CmsEngine.Ui/assets/img/flags/Angola.png diff --git a/CmsEngine.Ui/assets/img/flags/Anguilla.png b/src/CmsEngine.Ui/assets/img/flags/Anguilla.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Anguilla.png rename to src/CmsEngine.Ui/assets/img/flags/Anguilla.png diff --git a/CmsEngine.Ui/assets/img/flags/Antarctica.png b/src/CmsEngine.Ui/assets/img/flags/Antarctica.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Antarctica.png rename to src/CmsEngine.Ui/assets/img/flags/Antarctica.png diff --git a/CmsEngine.Ui/assets/img/flags/Antigua & Barbuda.png b/src/CmsEngine.Ui/assets/img/flags/Antigua & Barbuda.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Antigua & Barbuda.png rename to src/CmsEngine.Ui/assets/img/flags/Antigua & Barbuda.png diff --git a/CmsEngine.Ui/assets/img/flags/Arab League.png b/src/CmsEngine.Ui/assets/img/flags/Arab League.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Arab League.png rename to src/CmsEngine.Ui/assets/img/flags/Arab League.png diff --git a/CmsEngine.Ui/assets/img/flags/Argentina.png b/src/CmsEngine.Ui/assets/img/flags/Argentina.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Argentina.png rename to src/CmsEngine.Ui/assets/img/flags/Argentina.png diff --git a/CmsEngine.Ui/assets/img/flags/Armenia.png b/src/CmsEngine.Ui/assets/img/flags/Armenia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Armenia.png rename to src/CmsEngine.Ui/assets/img/flags/Armenia.png diff --git a/CmsEngine.Ui/assets/img/flags/Aruba.png b/src/CmsEngine.Ui/assets/img/flags/Aruba.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Aruba.png rename to src/CmsEngine.Ui/assets/img/flags/Aruba.png diff --git a/CmsEngine.Ui/assets/img/flags/Australia.png b/src/CmsEngine.Ui/assets/img/flags/Australia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Australia.png rename to src/CmsEngine.Ui/assets/img/flags/Australia.png diff --git a/CmsEngine.Ui/assets/img/flags/Austria.png b/src/CmsEngine.Ui/assets/img/flags/Austria.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Austria.png rename to src/CmsEngine.Ui/assets/img/flags/Austria.png diff --git a/CmsEngine.Ui/assets/img/flags/Azerbaijan.png b/src/CmsEngine.Ui/assets/img/flags/Azerbaijan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Azerbaijan.png rename to src/CmsEngine.Ui/assets/img/flags/Azerbaijan.png diff --git a/CmsEngine.Ui/assets/img/flags/Bahamas.png b/src/CmsEngine.Ui/assets/img/flags/Bahamas.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bahamas.png rename to src/CmsEngine.Ui/assets/img/flags/Bahamas.png diff --git a/CmsEngine.Ui/assets/img/flags/Bahrain.png b/src/CmsEngine.Ui/assets/img/flags/Bahrain.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bahrain.png rename to src/CmsEngine.Ui/assets/img/flags/Bahrain.png diff --git a/CmsEngine.Ui/assets/img/flags/Bangladesh.png b/src/CmsEngine.Ui/assets/img/flags/Bangladesh.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bangladesh.png rename to src/CmsEngine.Ui/assets/img/flags/Bangladesh.png diff --git a/CmsEngine.Ui/assets/img/flags/Barbados.png b/src/CmsEngine.Ui/assets/img/flags/Barbados.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Barbados.png rename to src/CmsEngine.Ui/assets/img/flags/Barbados.png diff --git a/CmsEngine.Ui/assets/img/flags/Belarus.png b/src/CmsEngine.Ui/assets/img/flags/Belarus.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Belarus.png rename to src/CmsEngine.Ui/assets/img/flags/Belarus.png diff --git a/CmsEngine.Ui/assets/img/flags/Belgium.png b/src/CmsEngine.Ui/assets/img/flags/Belgium.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Belgium.png rename to src/CmsEngine.Ui/assets/img/flags/Belgium.png diff --git a/CmsEngine.Ui/assets/img/flags/Belize.png b/src/CmsEngine.Ui/assets/img/flags/Belize.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Belize.png rename to src/CmsEngine.Ui/assets/img/flags/Belize.png diff --git a/CmsEngine.Ui/assets/img/flags/Benin.png b/src/CmsEngine.Ui/assets/img/flags/Benin.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Benin.png rename to src/CmsEngine.Ui/assets/img/flags/Benin.png diff --git a/CmsEngine.Ui/assets/img/flags/Bermuda.png b/src/CmsEngine.Ui/assets/img/flags/Bermuda.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bermuda.png rename to src/CmsEngine.Ui/assets/img/flags/Bermuda.png diff --git a/CmsEngine.Ui/assets/img/flags/Bhutan.png b/src/CmsEngine.Ui/assets/img/flags/Bhutan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bhutan.png rename to src/CmsEngine.Ui/assets/img/flags/Bhutan.png diff --git a/CmsEngine.Ui/assets/img/flags/Bolivia.png b/src/CmsEngine.Ui/assets/img/flags/Bolivia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bolivia.png rename to src/CmsEngine.Ui/assets/img/flags/Bolivia.png diff --git a/CmsEngine.Ui/assets/img/flags/Bosnia & Herzegovina.png b/src/CmsEngine.Ui/assets/img/flags/Bosnia & Herzegovina.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bosnia & Herzegovina.png rename to src/CmsEngine.Ui/assets/img/flags/Bosnia & Herzegovina.png diff --git a/CmsEngine.Ui/assets/img/flags/Botswana.png b/src/CmsEngine.Ui/assets/img/flags/Botswana.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Botswana.png rename to src/CmsEngine.Ui/assets/img/flags/Botswana.png diff --git a/CmsEngine.Ui/assets/img/flags/Brazil.png b/src/CmsEngine.Ui/assets/img/flags/Brazil.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Brazil.png rename to src/CmsEngine.Ui/assets/img/flags/Brazil.png diff --git a/CmsEngine.Ui/assets/img/flags/Brunei.png b/src/CmsEngine.Ui/assets/img/flags/Brunei.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Brunei.png rename to src/CmsEngine.Ui/assets/img/flags/Brunei.png diff --git a/CmsEngine.Ui/assets/img/flags/Bulgaria.png b/src/CmsEngine.Ui/assets/img/flags/Bulgaria.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Bulgaria.png rename to src/CmsEngine.Ui/assets/img/flags/Bulgaria.png diff --git a/CmsEngine.Ui/assets/img/flags/Burkina Faso.png b/src/CmsEngine.Ui/assets/img/flags/Burkina Faso.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Burkina Faso.png rename to src/CmsEngine.Ui/assets/img/flags/Burkina Faso.png diff --git a/CmsEngine.Ui/assets/img/flags/Burundi.png b/src/CmsEngine.Ui/assets/img/flags/Burundi.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Burundi.png rename to src/CmsEngine.Ui/assets/img/flags/Burundi.png diff --git a/CmsEngine.Ui/assets/img/flags/CARICOM.png b/src/CmsEngine.Ui/assets/img/flags/CARICOM.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/CARICOM.png rename to src/CmsEngine.Ui/assets/img/flags/CARICOM.png diff --git a/CmsEngine.Ui/assets/img/flags/CIS.png b/src/CmsEngine.Ui/assets/img/flags/CIS.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/CIS.png rename to src/CmsEngine.Ui/assets/img/flags/CIS.png diff --git a/CmsEngine.Ui/assets/img/flags/Cambodja.png b/src/CmsEngine.Ui/assets/img/flags/Cambodja.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cambodja.png rename to src/CmsEngine.Ui/assets/img/flags/Cambodja.png diff --git a/CmsEngine.Ui/assets/img/flags/Cameroon.png b/src/CmsEngine.Ui/assets/img/flags/Cameroon.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cameroon.png rename to src/CmsEngine.Ui/assets/img/flags/Cameroon.png diff --git a/CmsEngine.Ui/assets/img/flags/Canada.png b/src/CmsEngine.Ui/assets/img/flags/Canada.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Canada.png rename to src/CmsEngine.Ui/assets/img/flags/Canada.png diff --git a/CmsEngine.Ui/assets/img/flags/Cape Verde.png b/src/CmsEngine.Ui/assets/img/flags/Cape Verde.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cape Verde.png rename to src/CmsEngine.Ui/assets/img/flags/Cape Verde.png diff --git a/CmsEngine.Ui/assets/img/flags/Cayman Islands.png b/src/CmsEngine.Ui/assets/img/flags/Cayman Islands.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cayman Islands.png rename to src/CmsEngine.Ui/assets/img/flags/Cayman Islands.png diff --git a/CmsEngine.Ui/assets/img/flags/Central African Republic.png b/src/CmsEngine.Ui/assets/img/flags/Central African Republic.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Central African Republic.png rename to src/CmsEngine.Ui/assets/img/flags/Central African Republic.png diff --git a/CmsEngine.Ui/assets/img/flags/Chad.png b/src/CmsEngine.Ui/assets/img/flags/Chad.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Chad.png rename to src/CmsEngine.Ui/assets/img/flags/Chad.png diff --git a/CmsEngine.Ui/assets/img/flags/Chile.png b/src/CmsEngine.Ui/assets/img/flags/Chile.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Chile.png rename to src/CmsEngine.Ui/assets/img/flags/Chile.png diff --git a/CmsEngine.Ui/assets/img/flags/China.png b/src/CmsEngine.Ui/assets/img/flags/China.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/China.png rename to src/CmsEngine.Ui/assets/img/flags/China.png diff --git a/CmsEngine.Ui/assets/img/flags/Colombia.png b/src/CmsEngine.Ui/assets/img/flags/Colombia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Colombia.png rename to src/CmsEngine.Ui/assets/img/flags/Colombia.png diff --git a/CmsEngine.Ui/assets/img/flags/Commonwealth.png b/src/CmsEngine.Ui/assets/img/flags/Commonwealth.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Commonwealth.png rename to src/CmsEngine.Ui/assets/img/flags/Commonwealth.png diff --git a/CmsEngine.Ui/assets/img/flags/Comoros.png b/src/CmsEngine.Ui/assets/img/flags/Comoros.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Comoros.png rename to src/CmsEngine.Ui/assets/img/flags/Comoros.png diff --git a/CmsEngine.Ui/assets/img/flags/Congo-Brazzaville.png b/src/CmsEngine.Ui/assets/img/flags/Congo-Brazzaville.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Congo-Brazzaville.png rename to src/CmsEngine.Ui/assets/img/flags/Congo-Brazzaville.png diff --git a/CmsEngine.Ui/assets/img/flags/Congo-Kinshasa(Zaire).png b/src/CmsEngine.Ui/assets/img/flags/Congo-Kinshasa(Zaire).png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Congo-Kinshasa(Zaire).png rename to src/CmsEngine.Ui/assets/img/flags/Congo-Kinshasa(Zaire).png diff --git a/CmsEngine.Ui/assets/img/flags/Cook Islands.png b/src/CmsEngine.Ui/assets/img/flags/Cook Islands.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cook Islands.png rename to src/CmsEngine.Ui/assets/img/flags/Cook Islands.png diff --git a/CmsEngine.Ui/assets/img/flags/Costa Rica.png b/src/CmsEngine.Ui/assets/img/flags/Costa Rica.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Costa Rica.png rename to src/CmsEngine.Ui/assets/img/flags/Costa Rica.png diff --git a/CmsEngine.Ui/assets/img/flags/Cote d'Ivoire.png b/src/CmsEngine.Ui/assets/img/flags/Cote d'Ivoire.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cote d'Ivoire.png rename to src/CmsEngine.Ui/assets/img/flags/Cote d'Ivoire.png diff --git a/CmsEngine.Ui/assets/img/flags/Croatia.png b/src/CmsEngine.Ui/assets/img/flags/Croatia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Croatia.png rename to src/CmsEngine.Ui/assets/img/flags/Croatia.png diff --git a/CmsEngine.Ui/assets/img/flags/Cuba.png b/src/CmsEngine.Ui/assets/img/flags/Cuba.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cuba.png rename to src/CmsEngine.Ui/assets/img/flags/Cuba.png diff --git a/CmsEngine.Ui/assets/img/flags/Cyprus.png b/src/CmsEngine.Ui/assets/img/flags/Cyprus.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Cyprus.png rename to src/CmsEngine.Ui/assets/img/flags/Cyprus.png diff --git a/CmsEngine.Ui/assets/img/flags/Czech Republic.png b/src/CmsEngine.Ui/assets/img/flags/Czech Republic.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Czech Republic.png rename to src/CmsEngine.Ui/assets/img/flags/Czech Republic.png diff --git a/CmsEngine.Ui/assets/img/flags/Denmark.png b/src/CmsEngine.Ui/assets/img/flags/Denmark.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Denmark.png rename to src/CmsEngine.Ui/assets/img/flags/Denmark.png diff --git a/CmsEngine.Ui/assets/img/flags/Djibouti.png b/src/CmsEngine.Ui/assets/img/flags/Djibouti.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Djibouti.png rename to src/CmsEngine.Ui/assets/img/flags/Djibouti.png diff --git a/CmsEngine.Ui/assets/img/flags/Dominica.png b/src/CmsEngine.Ui/assets/img/flags/Dominica.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Dominica.png rename to src/CmsEngine.Ui/assets/img/flags/Dominica.png diff --git a/CmsEngine.Ui/assets/img/flags/Dominican Republic.png b/src/CmsEngine.Ui/assets/img/flags/Dominican Republic.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Dominican Republic.png rename to src/CmsEngine.Ui/assets/img/flags/Dominican Republic.png diff --git a/CmsEngine.Ui/assets/img/flags/Ecuador.png b/src/CmsEngine.Ui/assets/img/flags/Ecuador.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Ecuador.png rename to src/CmsEngine.Ui/assets/img/flags/Ecuador.png diff --git a/CmsEngine.Ui/assets/img/flags/Egypt.png b/src/CmsEngine.Ui/assets/img/flags/Egypt.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Egypt.png rename to src/CmsEngine.Ui/assets/img/flags/Egypt.png diff --git a/CmsEngine.Ui/assets/img/flags/El Salvador.png b/src/CmsEngine.Ui/assets/img/flags/El Salvador.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/El Salvador.png rename to src/CmsEngine.Ui/assets/img/flags/El Salvador.png diff --git a/CmsEngine.Ui/assets/img/flags/England.png b/src/CmsEngine.Ui/assets/img/flags/England.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/England.png rename to src/CmsEngine.Ui/assets/img/flags/England.png diff --git a/CmsEngine.Ui/assets/img/flags/Equatorial Guinea.png b/src/CmsEngine.Ui/assets/img/flags/Equatorial Guinea.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Equatorial Guinea.png rename to src/CmsEngine.Ui/assets/img/flags/Equatorial Guinea.png diff --git a/CmsEngine.Ui/assets/img/flags/Eritrea.png b/src/CmsEngine.Ui/assets/img/flags/Eritrea.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Eritrea.png rename to src/CmsEngine.Ui/assets/img/flags/Eritrea.png diff --git a/CmsEngine.Ui/assets/img/flags/Estonia.png b/src/CmsEngine.Ui/assets/img/flags/Estonia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Estonia.png rename to src/CmsEngine.Ui/assets/img/flags/Estonia.png diff --git a/CmsEngine.Ui/assets/img/flags/Ethiopia.png b/src/CmsEngine.Ui/assets/img/flags/Ethiopia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Ethiopia.png rename to src/CmsEngine.Ui/assets/img/flags/Ethiopia.png diff --git a/CmsEngine.Ui/assets/img/flags/European Union.png b/src/CmsEngine.Ui/assets/img/flags/European Union.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/European Union.png rename to src/CmsEngine.Ui/assets/img/flags/European Union.png diff --git a/CmsEngine.Ui/assets/img/flags/Faroes.png b/src/CmsEngine.Ui/assets/img/flags/Faroes.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Faroes.png rename to src/CmsEngine.Ui/assets/img/flags/Faroes.png diff --git a/CmsEngine.Ui/assets/img/flags/Fiji.png b/src/CmsEngine.Ui/assets/img/flags/Fiji.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Fiji.png rename to src/CmsEngine.Ui/assets/img/flags/Fiji.png diff --git a/CmsEngine.Ui/assets/img/flags/Finland.png b/src/CmsEngine.Ui/assets/img/flags/Finland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Finland.png rename to src/CmsEngine.Ui/assets/img/flags/Finland.png diff --git a/CmsEngine.Ui/assets/img/flags/France.png b/src/CmsEngine.Ui/assets/img/flags/France.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/France.png rename to src/CmsEngine.Ui/assets/img/flags/France.png diff --git a/CmsEngine.Ui/assets/img/flags/Gabon.png b/src/CmsEngine.Ui/assets/img/flags/Gabon.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Gabon.png rename to src/CmsEngine.Ui/assets/img/flags/Gabon.png diff --git a/CmsEngine.Ui/assets/img/flags/Gambia.png b/src/CmsEngine.Ui/assets/img/flags/Gambia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Gambia.png rename to src/CmsEngine.Ui/assets/img/flags/Gambia.png diff --git a/CmsEngine.Ui/assets/img/flags/Georgia.png b/src/CmsEngine.Ui/assets/img/flags/Georgia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Georgia.png rename to src/CmsEngine.Ui/assets/img/flags/Georgia.png diff --git a/CmsEngine.Ui/assets/img/flags/Germany.png b/src/CmsEngine.Ui/assets/img/flags/Germany.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Germany.png rename to src/CmsEngine.Ui/assets/img/flags/Germany.png diff --git a/CmsEngine.Ui/assets/img/flags/Ghana.png b/src/CmsEngine.Ui/assets/img/flags/Ghana.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Ghana.png rename to src/CmsEngine.Ui/assets/img/flags/Ghana.png diff --git a/CmsEngine.Ui/assets/img/flags/Gibraltar.png b/src/CmsEngine.Ui/assets/img/flags/Gibraltar.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Gibraltar.png rename to src/CmsEngine.Ui/assets/img/flags/Gibraltar.png diff --git a/CmsEngine.Ui/assets/img/flags/Greece.png b/src/CmsEngine.Ui/assets/img/flags/Greece.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Greece.png rename to src/CmsEngine.Ui/assets/img/flags/Greece.png diff --git a/CmsEngine.Ui/assets/img/flags/Greenland.png b/src/CmsEngine.Ui/assets/img/flags/Greenland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Greenland.png rename to src/CmsEngine.Ui/assets/img/flags/Greenland.png diff --git a/CmsEngine.Ui/assets/img/flags/Grenada.png b/src/CmsEngine.Ui/assets/img/flags/Grenada.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Grenada.png rename to src/CmsEngine.Ui/assets/img/flags/Grenada.png diff --git a/CmsEngine.Ui/assets/img/flags/Guadeloupe.png b/src/CmsEngine.Ui/assets/img/flags/Guadeloupe.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guadeloupe.png rename to src/CmsEngine.Ui/assets/img/flags/Guadeloupe.png diff --git a/CmsEngine.Ui/assets/img/flags/Guam.png b/src/CmsEngine.Ui/assets/img/flags/Guam.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guam.png rename to src/CmsEngine.Ui/assets/img/flags/Guam.png diff --git a/CmsEngine.Ui/assets/img/flags/Guatemala.png b/src/CmsEngine.Ui/assets/img/flags/Guatemala.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guatemala.png rename to src/CmsEngine.Ui/assets/img/flags/Guatemala.png diff --git a/CmsEngine.Ui/assets/img/flags/Guernsey.png b/src/CmsEngine.Ui/assets/img/flags/Guernsey.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guernsey.png rename to src/CmsEngine.Ui/assets/img/flags/Guernsey.png diff --git a/CmsEngine.Ui/assets/img/flags/Guinea-Bissau.png b/src/CmsEngine.Ui/assets/img/flags/Guinea-Bissau.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guinea-Bissau.png rename to src/CmsEngine.Ui/assets/img/flags/Guinea-Bissau.png diff --git a/CmsEngine.Ui/assets/img/flags/Guinea.png b/src/CmsEngine.Ui/assets/img/flags/Guinea.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guinea.png rename to src/CmsEngine.Ui/assets/img/flags/Guinea.png diff --git a/CmsEngine.Ui/assets/img/flags/Guyana.png b/src/CmsEngine.Ui/assets/img/flags/Guyana.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Guyana.png rename to src/CmsEngine.Ui/assets/img/flags/Guyana.png diff --git a/CmsEngine.Ui/assets/img/flags/Haiti.png b/src/CmsEngine.Ui/assets/img/flags/Haiti.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Haiti.png rename to src/CmsEngine.Ui/assets/img/flags/Haiti.png diff --git a/CmsEngine.Ui/assets/img/flags/Honduras.png b/src/CmsEngine.Ui/assets/img/flags/Honduras.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Honduras.png rename to src/CmsEngine.Ui/assets/img/flags/Honduras.png diff --git a/CmsEngine.Ui/assets/img/flags/Hong Kong.png b/src/CmsEngine.Ui/assets/img/flags/Hong Kong.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Hong Kong.png rename to src/CmsEngine.Ui/assets/img/flags/Hong Kong.png diff --git a/CmsEngine.Ui/assets/img/flags/Hungary.png b/src/CmsEngine.Ui/assets/img/flags/Hungary.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Hungary.png rename to src/CmsEngine.Ui/assets/img/flags/Hungary.png diff --git a/CmsEngine.Ui/assets/img/flags/Iceland.png b/src/CmsEngine.Ui/assets/img/flags/Iceland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Iceland.png rename to src/CmsEngine.Ui/assets/img/flags/Iceland.png diff --git a/CmsEngine.Ui/assets/img/flags/India.png b/src/CmsEngine.Ui/assets/img/flags/India.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/India.png rename to src/CmsEngine.Ui/assets/img/flags/India.png diff --git a/CmsEngine.Ui/assets/img/flags/Indonezia.png b/src/CmsEngine.Ui/assets/img/flags/Indonezia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Indonezia.png rename to src/CmsEngine.Ui/assets/img/flags/Indonezia.png diff --git a/CmsEngine.Ui/assets/img/flags/Iran.png b/src/CmsEngine.Ui/assets/img/flags/Iran.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Iran.png rename to src/CmsEngine.Ui/assets/img/flags/Iran.png diff --git a/CmsEngine.Ui/assets/img/flags/Iraq.png b/src/CmsEngine.Ui/assets/img/flags/Iraq.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Iraq.png rename to src/CmsEngine.Ui/assets/img/flags/Iraq.png diff --git a/CmsEngine.Ui/assets/img/flags/Ireland.png b/src/CmsEngine.Ui/assets/img/flags/Ireland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Ireland.png rename to src/CmsEngine.Ui/assets/img/flags/Ireland.png diff --git a/CmsEngine.Ui/assets/img/flags/Islamic Conference.png b/src/CmsEngine.Ui/assets/img/flags/Islamic Conference.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Islamic Conference.png rename to src/CmsEngine.Ui/assets/img/flags/Islamic Conference.png diff --git a/CmsEngine.Ui/assets/img/flags/Isle of Man.png b/src/CmsEngine.Ui/assets/img/flags/Isle of Man.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Isle of Man.png rename to src/CmsEngine.Ui/assets/img/flags/Isle of Man.png diff --git a/CmsEngine.Ui/assets/img/flags/Israel.png b/src/CmsEngine.Ui/assets/img/flags/Israel.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Israel.png rename to src/CmsEngine.Ui/assets/img/flags/Israel.png diff --git a/CmsEngine.Ui/assets/img/flags/Italy.png b/src/CmsEngine.Ui/assets/img/flags/Italy.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Italy.png rename to src/CmsEngine.Ui/assets/img/flags/Italy.png diff --git a/CmsEngine.Ui/assets/img/flags/Jamaica.png b/src/CmsEngine.Ui/assets/img/flags/Jamaica.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Jamaica.png rename to src/CmsEngine.Ui/assets/img/flags/Jamaica.png diff --git a/CmsEngine.Ui/assets/img/flags/Japan.png b/src/CmsEngine.Ui/assets/img/flags/Japan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Japan.png rename to src/CmsEngine.Ui/assets/img/flags/Japan.png diff --git a/CmsEngine.Ui/assets/img/flags/Jersey.png b/src/CmsEngine.Ui/assets/img/flags/Jersey.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Jersey.png rename to src/CmsEngine.Ui/assets/img/flags/Jersey.png diff --git a/CmsEngine.Ui/assets/img/flags/Jordan.png b/src/CmsEngine.Ui/assets/img/flags/Jordan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Jordan.png rename to src/CmsEngine.Ui/assets/img/flags/Jordan.png diff --git a/CmsEngine.Ui/assets/img/flags/Kazakhstan.png b/src/CmsEngine.Ui/assets/img/flags/Kazakhstan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Kazakhstan.png rename to src/CmsEngine.Ui/assets/img/flags/Kazakhstan.png diff --git a/CmsEngine.Ui/assets/img/flags/Kenya.png b/src/CmsEngine.Ui/assets/img/flags/Kenya.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Kenya.png rename to src/CmsEngine.Ui/assets/img/flags/Kenya.png diff --git a/CmsEngine.Ui/assets/img/flags/Kiribati.png b/src/CmsEngine.Ui/assets/img/flags/Kiribati.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Kiribati.png rename to src/CmsEngine.Ui/assets/img/flags/Kiribati.png diff --git a/CmsEngine.Ui/assets/img/flags/Kosovo.png b/src/CmsEngine.Ui/assets/img/flags/Kosovo.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Kosovo.png rename to src/CmsEngine.Ui/assets/img/flags/Kosovo.png diff --git a/CmsEngine.Ui/assets/img/flags/Kuwait.png b/src/CmsEngine.Ui/assets/img/flags/Kuwait.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Kuwait.png rename to src/CmsEngine.Ui/assets/img/flags/Kuwait.png diff --git a/CmsEngine.Ui/assets/img/flags/Kyrgyzstan.png b/src/CmsEngine.Ui/assets/img/flags/Kyrgyzstan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Kyrgyzstan.png rename to src/CmsEngine.Ui/assets/img/flags/Kyrgyzstan.png diff --git a/CmsEngine.Ui/assets/img/flags/Laos.png b/src/CmsEngine.Ui/assets/img/flags/Laos.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Laos.png rename to src/CmsEngine.Ui/assets/img/flags/Laos.png diff --git a/CmsEngine.Ui/assets/img/flags/Latvia.png b/src/CmsEngine.Ui/assets/img/flags/Latvia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Latvia.png rename to src/CmsEngine.Ui/assets/img/flags/Latvia.png diff --git a/CmsEngine.Ui/assets/img/flags/Lebanon.png b/src/CmsEngine.Ui/assets/img/flags/Lebanon.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Lebanon.png rename to src/CmsEngine.Ui/assets/img/flags/Lebanon.png diff --git a/CmsEngine.Ui/assets/img/flags/Lesotho.png b/src/CmsEngine.Ui/assets/img/flags/Lesotho.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Lesotho.png rename to src/CmsEngine.Ui/assets/img/flags/Lesotho.png diff --git a/CmsEngine.Ui/assets/img/flags/Liberia.png b/src/CmsEngine.Ui/assets/img/flags/Liberia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Liberia.png rename to src/CmsEngine.Ui/assets/img/flags/Liberia.png diff --git a/CmsEngine.Ui/assets/img/flags/Libya.png b/src/CmsEngine.Ui/assets/img/flags/Libya.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Libya.png rename to src/CmsEngine.Ui/assets/img/flags/Libya.png diff --git a/CmsEngine.Ui/assets/img/flags/Liechtenshein.png b/src/CmsEngine.Ui/assets/img/flags/Liechtenshein.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Liechtenshein.png rename to src/CmsEngine.Ui/assets/img/flags/Liechtenshein.png diff --git a/CmsEngine.Ui/assets/img/flags/Lithuania.png b/src/CmsEngine.Ui/assets/img/flags/Lithuania.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Lithuania.png rename to src/CmsEngine.Ui/assets/img/flags/Lithuania.png diff --git a/CmsEngine.Ui/assets/img/flags/Luxembourg.png b/src/CmsEngine.Ui/assets/img/flags/Luxembourg.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Luxembourg.png rename to src/CmsEngine.Ui/assets/img/flags/Luxembourg.png diff --git a/CmsEngine.Ui/assets/img/flags/Macao.png b/src/CmsEngine.Ui/assets/img/flags/Macao.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Macao.png rename to src/CmsEngine.Ui/assets/img/flags/Macao.png diff --git a/CmsEngine.Ui/assets/img/flags/Macedonia.png b/src/CmsEngine.Ui/assets/img/flags/Macedonia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Macedonia.png rename to src/CmsEngine.Ui/assets/img/flags/Macedonia.png diff --git a/CmsEngine.Ui/assets/img/flags/Madagascar.png b/src/CmsEngine.Ui/assets/img/flags/Madagascar.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Madagascar.png rename to src/CmsEngine.Ui/assets/img/flags/Madagascar.png diff --git a/CmsEngine.Ui/assets/img/flags/Malawi.png b/src/CmsEngine.Ui/assets/img/flags/Malawi.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Malawi.png rename to src/CmsEngine.Ui/assets/img/flags/Malawi.png diff --git a/CmsEngine.Ui/assets/img/flags/Malaysia.png b/src/CmsEngine.Ui/assets/img/flags/Malaysia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Malaysia.png rename to src/CmsEngine.Ui/assets/img/flags/Malaysia.png diff --git a/CmsEngine.Ui/assets/img/flags/Maldives.png b/src/CmsEngine.Ui/assets/img/flags/Maldives.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Maldives.png rename to src/CmsEngine.Ui/assets/img/flags/Maldives.png diff --git a/CmsEngine.Ui/assets/img/flags/Mali.png b/src/CmsEngine.Ui/assets/img/flags/Mali.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Mali.png rename to src/CmsEngine.Ui/assets/img/flags/Mali.png diff --git a/CmsEngine.Ui/assets/img/flags/Malta.png b/src/CmsEngine.Ui/assets/img/flags/Malta.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Malta.png rename to src/CmsEngine.Ui/assets/img/flags/Malta.png diff --git a/CmsEngine.Ui/assets/img/flags/Marshall Islands.png b/src/CmsEngine.Ui/assets/img/flags/Marshall Islands.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Marshall Islands.png rename to src/CmsEngine.Ui/assets/img/flags/Marshall Islands.png diff --git a/CmsEngine.Ui/assets/img/flags/Martinique.png b/src/CmsEngine.Ui/assets/img/flags/Martinique.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Martinique.png rename to src/CmsEngine.Ui/assets/img/flags/Martinique.png diff --git a/CmsEngine.Ui/assets/img/flags/Mauritania.png b/src/CmsEngine.Ui/assets/img/flags/Mauritania.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Mauritania.png rename to src/CmsEngine.Ui/assets/img/flags/Mauritania.png diff --git a/CmsEngine.Ui/assets/img/flags/Mauritius.png b/src/CmsEngine.Ui/assets/img/flags/Mauritius.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Mauritius.png rename to src/CmsEngine.Ui/assets/img/flags/Mauritius.png diff --git a/CmsEngine.Ui/assets/img/flags/Mexico.png b/src/CmsEngine.Ui/assets/img/flags/Mexico.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Mexico.png rename to src/CmsEngine.Ui/assets/img/flags/Mexico.png diff --git a/CmsEngine.Ui/assets/img/flags/Micronesia.png b/src/CmsEngine.Ui/assets/img/flags/Micronesia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Micronesia.png rename to src/CmsEngine.Ui/assets/img/flags/Micronesia.png diff --git a/CmsEngine.Ui/assets/img/flags/Moldova.png b/src/CmsEngine.Ui/assets/img/flags/Moldova.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Moldova.png rename to src/CmsEngine.Ui/assets/img/flags/Moldova.png diff --git a/CmsEngine.Ui/assets/img/flags/Monaco.png b/src/CmsEngine.Ui/assets/img/flags/Monaco.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Monaco.png rename to src/CmsEngine.Ui/assets/img/flags/Monaco.png diff --git a/CmsEngine.Ui/assets/img/flags/Mongolia.png b/src/CmsEngine.Ui/assets/img/flags/Mongolia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Mongolia.png rename to src/CmsEngine.Ui/assets/img/flags/Mongolia.png diff --git a/CmsEngine.Ui/assets/img/flags/Montenegro.png b/src/CmsEngine.Ui/assets/img/flags/Montenegro.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Montenegro.png rename to src/CmsEngine.Ui/assets/img/flags/Montenegro.png diff --git a/CmsEngine.Ui/assets/img/flags/Montserrat.png b/src/CmsEngine.Ui/assets/img/flags/Montserrat.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Montserrat.png rename to src/CmsEngine.Ui/assets/img/flags/Montserrat.png diff --git a/CmsEngine.Ui/assets/img/flags/Morocco.png b/src/CmsEngine.Ui/assets/img/flags/Morocco.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Morocco.png rename to src/CmsEngine.Ui/assets/img/flags/Morocco.png diff --git a/CmsEngine.Ui/assets/img/flags/Mozambique.png b/src/CmsEngine.Ui/assets/img/flags/Mozambique.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Mozambique.png rename to src/CmsEngine.Ui/assets/img/flags/Mozambique.png diff --git a/CmsEngine.Ui/assets/img/flags/Myanmar(Burma).png b/src/CmsEngine.Ui/assets/img/flags/Myanmar(Burma).png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Myanmar(Burma).png rename to src/CmsEngine.Ui/assets/img/flags/Myanmar(Burma).png diff --git a/CmsEngine.Ui/assets/img/flags/NATO.png b/src/CmsEngine.Ui/assets/img/flags/NATO.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/NATO.png rename to src/CmsEngine.Ui/assets/img/flags/NATO.png diff --git a/CmsEngine.Ui/assets/img/flags/Namibia.png b/src/CmsEngine.Ui/assets/img/flags/Namibia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Namibia.png rename to src/CmsEngine.Ui/assets/img/flags/Namibia.png diff --git a/CmsEngine.Ui/assets/img/flags/Nauru.png b/src/CmsEngine.Ui/assets/img/flags/Nauru.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Nauru.png rename to src/CmsEngine.Ui/assets/img/flags/Nauru.png diff --git a/CmsEngine.Ui/assets/img/flags/Nepal.png b/src/CmsEngine.Ui/assets/img/flags/Nepal.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Nepal.png rename to src/CmsEngine.Ui/assets/img/flags/Nepal.png diff --git a/CmsEngine.Ui/assets/img/flags/Netherlands Antilles.png b/src/CmsEngine.Ui/assets/img/flags/Netherlands Antilles.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Netherlands Antilles.png rename to src/CmsEngine.Ui/assets/img/flags/Netherlands Antilles.png diff --git a/CmsEngine.Ui/assets/img/flags/Netherlands.png b/src/CmsEngine.Ui/assets/img/flags/Netherlands.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Netherlands.png rename to src/CmsEngine.Ui/assets/img/flags/Netherlands.png diff --git a/CmsEngine.Ui/assets/img/flags/New Caledonia.png b/src/CmsEngine.Ui/assets/img/flags/New Caledonia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/New Caledonia.png rename to src/CmsEngine.Ui/assets/img/flags/New Caledonia.png diff --git a/CmsEngine.Ui/assets/img/flags/New Zealand.png b/src/CmsEngine.Ui/assets/img/flags/New Zealand.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/New Zealand.png rename to src/CmsEngine.Ui/assets/img/flags/New Zealand.png diff --git a/CmsEngine.Ui/assets/img/flags/Nicaragua.png b/src/CmsEngine.Ui/assets/img/flags/Nicaragua.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Nicaragua.png rename to src/CmsEngine.Ui/assets/img/flags/Nicaragua.png diff --git a/CmsEngine.Ui/assets/img/flags/Niger.png b/src/CmsEngine.Ui/assets/img/flags/Niger.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Niger.png rename to src/CmsEngine.Ui/assets/img/flags/Niger.png diff --git a/CmsEngine.Ui/assets/img/flags/Nigeria.png b/src/CmsEngine.Ui/assets/img/flags/Nigeria.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Nigeria.png rename to src/CmsEngine.Ui/assets/img/flags/Nigeria.png diff --git a/CmsEngine.Ui/assets/img/flags/North Korea.png b/src/CmsEngine.Ui/assets/img/flags/North Korea.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/North Korea.png rename to src/CmsEngine.Ui/assets/img/flags/North Korea.png diff --git a/CmsEngine.Ui/assets/img/flags/Northern Cyprus.png b/src/CmsEngine.Ui/assets/img/flags/Northern Cyprus.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Northern Cyprus.png rename to src/CmsEngine.Ui/assets/img/flags/Northern Cyprus.png diff --git a/CmsEngine.Ui/assets/img/flags/Northern Ireland.png b/src/CmsEngine.Ui/assets/img/flags/Northern Ireland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Northern Ireland.png rename to src/CmsEngine.Ui/assets/img/flags/Northern Ireland.png diff --git a/CmsEngine.Ui/assets/img/flags/Norway.png b/src/CmsEngine.Ui/assets/img/flags/Norway.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Norway.png rename to src/CmsEngine.Ui/assets/img/flags/Norway.png diff --git a/CmsEngine.Ui/assets/img/flags/OPEC.png b/src/CmsEngine.Ui/assets/img/flags/OPEC.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/OPEC.png rename to src/CmsEngine.Ui/assets/img/flags/OPEC.png diff --git a/CmsEngine.Ui/assets/img/flags/Olimpic Movement.png b/src/CmsEngine.Ui/assets/img/flags/Olimpic Movement.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Olimpic Movement.png rename to src/CmsEngine.Ui/assets/img/flags/Olimpic Movement.png diff --git a/CmsEngine.Ui/assets/img/flags/Oman.png b/src/CmsEngine.Ui/assets/img/flags/Oman.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Oman.png rename to src/CmsEngine.Ui/assets/img/flags/Oman.png diff --git a/CmsEngine.Ui/assets/img/flags/Pakistan.png b/src/CmsEngine.Ui/assets/img/flags/Pakistan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Pakistan.png rename to src/CmsEngine.Ui/assets/img/flags/Pakistan.png diff --git a/CmsEngine.Ui/assets/img/flags/Palau.png b/src/CmsEngine.Ui/assets/img/flags/Palau.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Palau.png rename to src/CmsEngine.Ui/assets/img/flags/Palau.png diff --git a/CmsEngine.Ui/assets/img/flags/Palestine.png b/src/CmsEngine.Ui/assets/img/flags/Palestine.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Palestine.png rename to src/CmsEngine.Ui/assets/img/flags/Palestine.png diff --git a/CmsEngine.Ui/assets/img/flags/Panama.png b/src/CmsEngine.Ui/assets/img/flags/Panama.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Panama.png rename to src/CmsEngine.Ui/assets/img/flags/Panama.png diff --git a/CmsEngine.Ui/assets/img/flags/Papua New Guinea.png b/src/CmsEngine.Ui/assets/img/flags/Papua New Guinea.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Papua New Guinea.png rename to src/CmsEngine.Ui/assets/img/flags/Papua New Guinea.png diff --git a/CmsEngine.Ui/assets/img/flags/Paraguay.png b/src/CmsEngine.Ui/assets/img/flags/Paraguay.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Paraguay.png rename to src/CmsEngine.Ui/assets/img/flags/Paraguay.png diff --git a/CmsEngine.Ui/assets/img/flags/Peru.png b/src/CmsEngine.Ui/assets/img/flags/Peru.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Peru.png rename to src/CmsEngine.Ui/assets/img/flags/Peru.png diff --git a/CmsEngine.Ui/assets/img/flags/Philippines.png b/src/CmsEngine.Ui/assets/img/flags/Philippines.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Philippines.png rename to src/CmsEngine.Ui/assets/img/flags/Philippines.png diff --git a/CmsEngine.Ui/assets/img/flags/Poland.png b/src/CmsEngine.Ui/assets/img/flags/Poland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Poland.png rename to src/CmsEngine.Ui/assets/img/flags/Poland.png diff --git a/CmsEngine.Ui/assets/img/flags/Portugal.png b/src/CmsEngine.Ui/assets/img/flags/Portugal.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Portugal.png rename to src/CmsEngine.Ui/assets/img/flags/Portugal.png diff --git a/CmsEngine.Ui/assets/img/flags/Puerto Rico.png b/src/CmsEngine.Ui/assets/img/flags/Puerto Rico.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Puerto Rico.png rename to src/CmsEngine.Ui/assets/img/flags/Puerto Rico.png diff --git a/CmsEngine.Ui/assets/img/flags/Qatar.png b/src/CmsEngine.Ui/assets/img/flags/Qatar.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Qatar.png rename to src/CmsEngine.Ui/assets/img/flags/Qatar.png diff --git a/CmsEngine.Ui/assets/img/flags/Red Cross.png b/src/CmsEngine.Ui/assets/img/flags/Red Cross.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Red Cross.png rename to src/CmsEngine.Ui/assets/img/flags/Red Cross.png diff --git a/CmsEngine.Ui/assets/img/flags/Reunion.png b/src/CmsEngine.Ui/assets/img/flags/Reunion.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Reunion.png rename to src/CmsEngine.Ui/assets/img/flags/Reunion.png diff --git a/CmsEngine.Ui/assets/img/flags/Romania.png b/src/CmsEngine.Ui/assets/img/flags/Romania.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Romania.png rename to src/CmsEngine.Ui/assets/img/flags/Romania.png diff --git a/CmsEngine.Ui/assets/img/flags/Russia.png b/src/CmsEngine.Ui/assets/img/flags/Russia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Russia.png rename to src/CmsEngine.Ui/assets/img/flags/Russia.png diff --git a/CmsEngine.Ui/assets/img/flags/Rwanda.png b/src/CmsEngine.Ui/assets/img/flags/Rwanda.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Rwanda.png rename to src/CmsEngine.Ui/assets/img/flags/Rwanda.png diff --git a/CmsEngine.Ui/assets/img/flags/Saint Lucia.png b/src/CmsEngine.Ui/assets/img/flags/Saint Lucia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Saint Lucia.png rename to src/CmsEngine.Ui/assets/img/flags/Saint Lucia.png diff --git a/CmsEngine.Ui/assets/img/flags/Samoa.png b/src/CmsEngine.Ui/assets/img/flags/Samoa.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Samoa.png rename to src/CmsEngine.Ui/assets/img/flags/Samoa.png diff --git a/CmsEngine.Ui/assets/img/flags/San Marino.png b/src/CmsEngine.Ui/assets/img/flags/San Marino.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/San Marino.png rename to src/CmsEngine.Ui/assets/img/flags/San Marino.png diff --git a/CmsEngine.Ui/assets/img/flags/Sao Tome & Principe.png b/src/CmsEngine.Ui/assets/img/flags/Sao Tome & Principe.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Sao Tome & Principe.png rename to src/CmsEngine.Ui/assets/img/flags/Sao Tome & Principe.png diff --git a/CmsEngine.Ui/assets/img/flags/Saudi Arabia.png b/src/CmsEngine.Ui/assets/img/flags/Saudi Arabia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Saudi Arabia.png rename to src/CmsEngine.Ui/assets/img/flags/Saudi Arabia.png diff --git a/CmsEngine.Ui/assets/img/flags/Scotland.png b/src/CmsEngine.Ui/assets/img/flags/Scotland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Scotland.png rename to src/CmsEngine.Ui/assets/img/flags/Scotland.png diff --git a/CmsEngine.Ui/assets/img/flags/Senegal.png b/src/CmsEngine.Ui/assets/img/flags/Senegal.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Senegal.png rename to src/CmsEngine.Ui/assets/img/flags/Senegal.png diff --git a/CmsEngine.Ui/assets/img/flags/Serbia(Yugoslavia).png b/src/CmsEngine.Ui/assets/img/flags/Serbia(Yugoslavia).png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Serbia(Yugoslavia).png rename to src/CmsEngine.Ui/assets/img/flags/Serbia(Yugoslavia).png diff --git a/CmsEngine.Ui/assets/img/flags/Seychelles.png b/src/CmsEngine.Ui/assets/img/flags/Seychelles.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Seychelles.png rename to src/CmsEngine.Ui/assets/img/flags/Seychelles.png diff --git a/CmsEngine.Ui/assets/img/flags/Sierra Leone.png b/src/CmsEngine.Ui/assets/img/flags/Sierra Leone.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Sierra Leone.png rename to src/CmsEngine.Ui/assets/img/flags/Sierra Leone.png diff --git a/CmsEngine.Ui/assets/img/flags/Singapore.png b/src/CmsEngine.Ui/assets/img/flags/Singapore.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Singapore.png rename to src/CmsEngine.Ui/assets/img/flags/Singapore.png diff --git a/CmsEngine.Ui/assets/img/flags/Slovakia.png b/src/CmsEngine.Ui/assets/img/flags/Slovakia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Slovakia.png rename to src/CmsEngine.Ui/assets/img/flags/Slovakia.png diff --git a/CmsEngine.Ui/assets/img/flags/Slovenia.png b/src/CmsEngine.Ui/assets/img/flags/Slovenia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Slovenia.png rename to src/CmsEngine.Ui/assets/img/flags/Slovenia.png diff --git a/CmsEngine.Ui/assets/img/flags/Solomon Islands.png b/src/CmsEngine.Ui/assets/img/flags/Solomon Islands.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Solomon Islands.png rename to src/CmsEngine.Ui/assets/img/flags/Solomon Islands.png diff --git a/CmsEngine.Ui/assets/img/flags/Somalia.png b/src/CmsEngine.Ui/assets/img/flags/Somalia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Somalia.png rename to src/CmsEngine.Ui/assets/img/flags/Somalia.png diff --git a/CmsEngine.Ui/assets/img/flags/Somaliland.png b/src/CmsEngine.Ui/assets/img/flags/Somaliland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Somaliland.png rename to src/CmsEngine.Ui/assets/img/flags/Somaliland.png diff --git a/CmsEngine.Ui/assets/img/flags/South Africa.png b/src/CmsEngine.Ui/assets/img/flags/South Africa.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/South Africa.png rename to src/CmsEngine.Ui/assets/img/flags/South Africa.png diff --git a/CmsEngine.Ui/assets/img/flags/South Korea.png b/src/CmsEngine.Ui/assets/img/flags/South Korea.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/South Korea.png rename to src/CmsEngine.Ui/assets/img/flags/South Korea.png diff --git a/CmsEngine.Ui/assets/img/flags/Spain.png b/src/CmsEngine.Ui/assets/img/flags/Spain.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Spain.png rename to src/CmsEngine.Ui/assets/img/flags/Spain.png diff --git a/CmsEngine.Ui/assets/img/flags/Sri Lanka.png b/src/CmsEngine.Ui/assets/img/flags/Sri Lanka.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Sri Lanka.png rename to src/CmsEngine.Ui/assets/img/flags/Sri Lanka.png diff --git a/CmsEngine.Ui/assets/img/flags/St Kitts & Nevis.png b/src/CmsEngine.Ui/assets/img/flags/St Kitts & Nevis.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/St Kitts & Nevis.png rename to src/CmsEngine.Ui/assets/img/flags/St Kitts & Nevis.png diff --git a/CmsEngine.Ui/assets/img/flags/St Vincent & the Grenadines.png b/src/CmsEngine.Ui/assets/img/flags/St Vincent & the Grenadines.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/St Vincent & the Grenadines.png rename to src/CmsEngine.Ui/assets/img/flags/St Vincent & the Grenadines.png diff --git a/CmsEngine.Ui/assets/img/flags/Sudan.png b/src/CmsEngine.Ui/assets/img/flags/Sudan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Sudan.png rename to src/CmsEngine.Ui/assets/img/flags/Sudan.png diff --git a/CmsEngine.Ui/assets/img/flags/Suriname.png b/src/CmsEngine.Ui/assets/img/flags/Suriname.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Suriname.png rename to src/CmsEngine.Ui/assets/img/flags/Suriname.png diff --git a/CmsEngine.Ui/assets/img/flags/Swaziland.png b/src/CmsEngine.Ui/assets/img/flags/Swaziland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Swaziland.png rename to src/CmsEngine.Ui/assets/img/flags/Swaziland.png diff --git a/CmsEngine.Ui/assets/img/flags/Sweden.png b/src/CmsEngine.Ui/assets/img/flags/Sweden.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Sweden.png rename to src/CmsEngine.Ui/assets/img/flags/Sweden.png diff --git a/CmsEngine.Ui/assets/img/flags/Switzerland.png b/src/CmsEngine.Ui/assets/img/flags/Switzerland.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Switzerland.png rename to src/CmsEngine.Ui/assets/img/flags/Switzerland.png diff --git a/CmsEngine.Ui/assets/img/flags/Syria.png b/src/CmsEngine.Ui/assets/img/flags/Syria.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Syria.png rename to src/CmsEngine.Ui/assets/img/flags/Syria.png diff --git a/CmsEngine.Ui/assets/img/flags/Tahiti(French Polinesia).png b/src/CmsEngine.Ui/assets/img/flags/Tahiti(French Polinesia).png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Tahiti(French Polinesia).png rename to src/CmsEngine.Ui/assets/img/flags/Tahiti(French Polinesia).png diff --git a/CmsEngine.Ui/assets/img/flags/Taiwan.png b/src/CmsEngine.Ui/assets/img/flags/Taiwan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Taiwan.png rename to src/CmsEngine.Ui/assets/img/flags/Taiwan.png diff --git a/CmsEngine.Ui/assets/img/flags/Tajikistan.png b/src/CmsEngine.Ui/assets/img/flags/Tajikistan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Tajikistan.png rename to src/CmsEngine.Ui/assets/img/flags/Tajikistan.png diff --git a/CmsEngine.Ui/assets/img/flags/Tanzania.png b/src/CmsEngine.Ui/assets/img/flags/Tanzania.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Tanzania.png rename to src/CmsEngine.Ui/assets/img/flags/Tanzania.png diff --git a/CmsEngine.Ui/assets/img/flags/Thailand.png b/src/CmsEngine.Ui/assets/img/flags/Thailand.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Thailand.png rename to src/CmsEngine.Ui/assets/img/flags/Thailand.png diff --git a/CmsEngine.Ui/assets/img/flags/Timor-Leste.png b/src/CmsEngine.Ui/assets/img/flags/Timor-Leste.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Timor-Leste.png rename to src/CmsEngine.Ui/assets/img/flags/Timor-Leste.png diff --git a/CmsEngine.Ui/assets/img/flags/Togo.png b/src/CmsEngine.Ui/assets/img/flags/Togo.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Togo.png rename to src/CmsEngine.Ui/assets/img/flags/Togo.png diff --git a/CmsEngine.Ui/assets/img/flags/Tonga.png b/src/CmsEngine.Ui/assets/img/flags/Tonga.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Tonga.png rename to src/CmsEngine.Ui/assets/img/flags/Tonga.png diff --git a/CmsEngine.Ui/assets/img/flags/Trinidad & Tobago.png b/src/CmsEngine.Ui/assets/img/flags/Trinidad & Tobago.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Trinidad & Tobago.png rename to src/CmsEngine.Ui/assets/img/flags/Trinidad & Tobago.png diff --git a/CmsEngine.Ui/assets/img/flags/Tunisia.png b/src/CmsEngine.Ui/assets/img/flags/Tunisia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Tunisia.png rename to src/CmsEngine.Ui/assets/img/flags/Tunisia.png diff --git a/CmsEngine.Ui/assets/img/flags/Turkey.png b/src/CmsEngine.Ui/assets/img/flags/Turkey.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Turkey.png rename to src/CmsEngine.Ui/assets/img/flags/Turkey.png diff --git a/CmsEngine.Ui/assets/img/flags/Turkmenistan.png b/src/CmsEngine.Ui/assets/img/flags/Turkmenistan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Turkmenistan.png rename to src/CmsEngine.Ui/assets/img/flags/Turkmenistan.png diff --git a/CmsEngine.Ui/assets/img/flags/Turks and Caicos Islands.png b/src/CmsEngine.Ui/assets/img/flags/Turks and Caicos Islands.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Turks and Caicos Islands.png rename to src/CmsEngine.Ui/assets/img/flags/Turks and Caicos Islands.png diff --git a/CmsEngine.Ui/assets/img/flags/Tuvalu.png b/src/CmsEngine.Ui/assets/img/flags/Tuvalu.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Tuvalu.png rename to src/CmsEngine.Ui/assets/img/flags/Tuvalu.png diff --git a/CmsEngine.Ui/assets/img/flags/USA.png b/src/CmsEngine.Ui/assets/img/flags/USA.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/USA.png rename to src/CmsEngine.Ui/assets/img/flags/USA.png diff --git a/CmsEngine.Ui/assets/img/flags/Uganda.png b/src/CmsEngine.Ui/assets/img/flags/Uganda.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Uganda.png rename to src/CmsEngine.Ui/assets/img/flags/Uganda.png diff --git a/CmsEngine.Ui/assets/img/flags/Ukraine.png b/src/CmsEngine.Ui/assets/img/flags/Ukraine.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Ukraine.png rename to src/CmsEngine.Ui/assets/img/flags/Ukraine.png diff --git a/CmsEngine.Ui/assets/img/flags/United Arab Emirates.png b/src/CmsEngine.Ui/assets/img/flags/United Arab Emirates.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/United Arab Emirates.png rename to src/CmsEngine.Ui/assets/img/flags/United Arab Emirates.png diff --git a/CmsEngine.Ui/assets/img/flags/United Nations.png b/src/CmsEngine.Ui/assets/img/flags/United Nations.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/United Nations.png rename to src/CmsEngine.Ui/assets/img/flags/United Nations.png diff --git a/CmsEngine.Ui/assets/img/flags/United-Kingdom.png b/src/CmsEngine.Ui/assets/img/flags/United-Kingdom.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/United-Kingdom.png rename to src/CmsEngine.Ui/assets/img/flags/United-Kingdom.png diff --git a/CmsEngine.Ui/assets/img/flags/Uruguay.png b/src/CmsEngine.Ui/assets/img/flags/Uruguay.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Uruguay.png rename to src/CmsEngine.Ui/assets/img/flags/Uruguay.png diff --git a/CmsEngine.Ui/assets/img/flags/Uzbekistan.png b/src/CmsEngine.Ui/assets/img/flags/Uzbekistan.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Uzbekistan.png rename to src/CmsEngine.Ui/assets/img/flags/Uzbekistan.png diff --git a/CmsEngine.Ui/assets/img/flags/Vanutau.png b/src/CmsEngine.Ui/assets/img/flags/Vanutau.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Vanutau.png rename to src/CmsEngine.Ui/assets/img/flags/Vanutau.png diff --git a/CmsEngine.Ui/assets/img/flags/Vatican City.png b/src/CmsEngine.Ui/assets/img/flags/Vatican City.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Vatican City.png rename to src/CmsEngine.Ui/assets/img/flags/Vatican City.png diff --git a/CmsEngine.Ui/assets/img/flags/Venezuela.png b/src/CmsEngine.Ui/assets/img/flags/Venezuela.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Venezuela.png rename to src/CmsEngine.Ui/assets/img/flags/Venezuela.png diff --git a/CmsEngine.Ui/assets/img/flags/Viet Nam.png b/src/CmsEngine.Ui/assets/img/flags/Viet Nam.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Viet Nam.png rename to src/CmsEngine.Ui/assets/img/flags/Viet Nam.png diff --git a/CmsEngine.Ui/assets/img/flags/Virgin Islands British.png b/src/CmsEngine.Ui/assets/img/flags/Virgin Islands British.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Virgin Islands British.png rename to src/CmsEngine.Ui/assets/img/flags/Virgin Islands British.png diff --git a/CmsEngine.Ui/assets/img/flags/Virgin Islands US.png b/src/CmsEngine.Ui/assets/img/flags/Virgin Islands US.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Virgin Islands US.png rename to src/CmsEngine.Ui/assets/img/flags/Virgin Islands US.png diff --git a/CmsEngine.Ui/assets/img/flags/Wales.png b/src/CmsEngine.Ui/assets/img/flags/Wales.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Wales.png rename to src/CmsEngine.Ui/assets/img/flags/Wales.png diff --git a/CmsEngine.Ui/assets/img/flags/Western Sahara.png b/src/CmsEngine.Ui/assets/img/flags/Western Sahara.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Western Sahara.png rename to src/CmsEngine.Ui/assets/img/flags/Western Sahara.png diff --git a/CmsEngine.Ui/assets/img/flags/Yemen.png b/src/CmsEngine.Ui/assets/img/flags/Yemen.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Yemen.png rename to src/CmsEngine.Ui/assets/img/flags/Yemen.png diff --git a/CmsEngine.Ui/assets/img/flags/Zambia.png b/src/CmsEngine.Ui/assets/img/flags/Zambia.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Zambia.png rename to src/CmsEngine.Ui/assets/img/flags/Zambia.png diff --git a/CmsEngine.Ui/assets/img/flags/Zimbabwe.png b/src/CmsEngine.Ui/assets/img/flags/Zimbabwe.png similarity index 100% rename from CmsEngine.Ui/assets/img/flags/Zimbabwe.png rename to src/CmsEngine.Ui/assets/img/flags/Zimbabwe.png diff --git a/CmsEngine.Ui/assets/img/logo-symbol.png b/src/CmsEngine.Ui/assets/img/logo-symbol.png similarity index 100% rename from CmsEngine.Ui/assets/img/logo-symbol.png rename to src/CmsEngine.Ui/assets/img/logo-symbol.png diff --git a/CmsEngine.Ui/assets/img/logo.png b/src/CmsEngine.Ui/assets/img/logo.png similarity index 100% rename from CmsEngine.Ui/assets/img/logo.png rename to src/CmsEngine.Ui/assets/img/logo.png diff --git a/CmsEngine.Ui/assets/img/no-image.png b/src/CmsEngine.Ui/assets/img/no-image.png similarity index 100% rename from CmsEngine.Ui/assets/img/no-image.png rename to src/CmsEngine.Ui/assets/img/no-image.png diff --git a/CmsEngine.Ui/assets/js/admin/0-configure.js b/src/CmsEngine.Ui/assets/js/admin/0-configure.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/0-configure.js rename to src/CmsEngine.Ui/assets/js/admin/0-configure.js diff --git a/CmsEngine.Ui/assets/js/admin/1-dialog.js b/src/CmsEngine.Ui/assets/js/admin/1-dialog.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/1-dialog.js rename to src/CmsEngine.Ui/assets/js/admin/1-dialog.js diff --git a/CmsEngine.Ui/assets/js/admin/2-dialog-events.js b/src/CmsEngine.Ui/assets/js/admin/2-dialog-events.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/2-dialog-events.js rename to src/CmsEngine.Ui/assets/js/admin/2-dialog-events.js diff --git a/CmsEngine.Ui/assets/js/admin/3-utils.js b/src/CmsEngine.Ui/assets/js/admin/3-utils.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/3-utils.js rename to src/CmsEngine.Ui/assets/js/admin/3-utils.js diff --git a/CmsEngine.Ui/assets/js/admin/4-navigation.js b/src/CmsEngine.Ui/assets/js/admin/4-navigation.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/4-navigation.js rename to src/CmsEngine.Ui/assets/js/admin/4-navigation.js diff --git a/CmsEngine.Ui/assets/js/admin/5-file-upload.js b/src/CmsEngine.Ui/assets/js/admin/5-file-upload.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/5-file-upload.js rename to src/CmsEngine.Ui/assets/js/admin/5-file-upload.js diff --git a/CmsEngine.Ui/assets/js/admin/99-init.js b/src/CmsEngine.Ui/assets/js/admin/99-init.js similarity index 100% rename from CmsEngine.Ui/assets/js/admin/99-init.js rename to src/CmsEngine.Ui/assets/js/admin/99-init.js diff --git a/CmsEngine.Ui/assets/js/site/front.js b/src/CmsEngine.Ui/assets/js/site/front.js similarity index 100% rename from CmsEngine.Ui/assets/js/site/front.js rename to src/CmsEngine.Ui/assets/js/site/front.js diff --git a/CmsEngine.Ui/assets/js/site/prism.js b/src/CmsEngine.Ui/assets/js/site/prism.js similarity index 100% rename from CmsEngine.Ui/assets/js/site/prism.js rename to src/CmsEngine.Ui/assets/js/site/prism.js diff --git a/CmsEngine.Ui/assets/scss/admin/_bootstrap-variables.scss b/src/CmsEngine.Ui/assets/scss/admin/_bootstrap-variables.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/_bootstrap-variables.scss rename to src/CmsEngine.Ui/assets/scss/admin/_bootstrap-variables.scss diff --git a/CmsEngine.Ui/assets/scss/admin/_core-variables.scss b/src/CmsEngine.Ui/assets/scss/admin/_core-variables.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/_core-variables.scss rename to src/CmsEngine.Ui/assets/scss/admin/_core-variables.scss diff --git a/CmsEngine.Ui/assets/scss/admin/_custom.scss b/src/CmsEngine.Ui/assets/scss/admin/_custom.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/_custom.scss rename to src/CmsEngine.Ui/assets/scss/admin/_custom.scss diff --git a/CmsEngine.Ui/assets/scss/admin/admin.scss b/src/CmsEngine.Ui/assets/scss/admin/admin.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/admin.scss rename to src/CmsEngine.Ui/assets/scss/admin/admin.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_animate.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_animate.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_animate.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_animate.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_aside.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_aside.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_aside.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_aside.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_avatars.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_avatars.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_avatars.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_avatars.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_badge.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_badge.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_badge.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_badge.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_breadcrumb-menu.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_breadcrumb-menu.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_breadcrumb-menu.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_breadcrumb-menu.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_breadcrumb.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_breadcrumb.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_breadcrumb.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_breadcrumb.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_buttons.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_buttons.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_buttons.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_buttons.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_callout.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_callout.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_callout.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_callout.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_card.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_card.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_card.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_card.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_charts.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_charts.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_charts.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_charts.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_dropdown-menu-right.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_dropdown-menu-right.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_dropdown-menu-right.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_dropdown-menu-right.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_dropdown.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_dropdown.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_dropdown.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_dropdown.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_footer.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_footer.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_footer.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_footer.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_grid.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_grid.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_grid.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_grid.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_input-group.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_input-group.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_input-group.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_input-group.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_layout.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_layout.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_layout.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_layout.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_loading.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_loading.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_loading.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_loading.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_mixins.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_mixins.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_mixins.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_mixins.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_mobile.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_mobile.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_mobile.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_mobile.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_modal.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_modal.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_modal.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_modal.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_nav.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_nav.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_nav.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_nav.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_navbar.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_navbar.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_navbar.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_navbar.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_others.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_others.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_others.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_others.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_progress.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_progress.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_progress.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_progress.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_rtl.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_rtl.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_rtl.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_rtl.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_sidebar.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_sidebar.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_sidebar.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_sidebar.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_switches.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_switches.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_switches.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_switches.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_tables.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_tables.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_tables.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_tables.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_temp.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_temp.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_temp.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_temp.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_typography.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_typography.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_typography.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_typography.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_utilities.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_utilities.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_utilities.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_utilities.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_variables.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_variables.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_variables.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_variables.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/_widgets.scss b/src/CmsEngine.Ui/assets/scss/admin/core/_widgets.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/_widgets.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/_widgets.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/core.scss b/src/CmsEngine.Ui/assets/scss/admin/core/core.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/core.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/core.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/utilities/_background.scss b/src/CmsEngine.Ui/assets/scss/admin/core/utilities/_background.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/utilities/_background.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/utilities/_background.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/utilities/_borders.scss b/src/CmsEngine.Ui/assets/scss/admin/core/utilities/_borders.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/utilities/_borders.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/utilities/_borders.scss diff --git a/CmsEngine.Ui/assets/scss/admin/core/utilities/_display.scss b/src/CmsEngine.Ui/assets/scss/admin/core/utilities/_display.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/core/utilities/_display.scss rename to src/CmsEngine.Ui/assets/scss/admin/core/utilities/_display.scss diff --git a/CmsEngine.Ui/assets/scss/admin/vendors/_variables.scss b/src/CmsEngine.Ui/assets/scss/admin/vendors/_variables.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/vendors/_variables.scss rename to src/CmsEngine.Ui/assets/scss/admin/vendors/_variables.scss diff --git a/CmsEngine.Ui/assets/scss/admin/vendors/chart.js/chart.scss b/src/CmsEngine.Ui/assets/scss/admin/vendors/chart.js/chart.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/admin/vendors/chart.js/chart.scss rename to src/CmsEngine.Ui/assets/scss/admin/vendors/chart.js/chart.scss diff --git a/CmsEngine.Ui/assets/scss/site/fontastic.scss b/src/CmsEngine.Ui/assets/scss/site/fontastic.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/site/fontastic.scss rename to src/CmsEngine.Ui/assets/scss/site/fontastic.scss diff --git a/CmsEngine.Ui/assets/scss/site/prism.scss b/src/CmsEngine.Ui/assets/scss/site/prism.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/site/prism.scss rename to src/CmsEngine.Ui/assets/scss/site/prism.scss diff --git a/CmsEngine.Ui/assets/scss/site/site.scss b/src/CmsEngine.Ui/assets/scss/site/site.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/site/site.scss rename to src/CmsEngine.Ui/assets/scss/site/site.scss diff --git a/CmsEngine.Ui/assets/scss/site/style.blue.scss b/src/CmsEngine.Ui/assets/scss/site/style.blue.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/site/style.blue.scss rename to src/CmsEngine.Ui/assets/scss/site/style.blue.scss diff --git a/CmsEngine.Ui/assets/scss/site/style.green.scss b/src/CmsEngine.Ui/assets/scss/site/style.green.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/site/style.green.scss rename to src/CmsEngine.Ui/assets/scss/site/style.green.scss diff --git a/CmsEngine.Ui/assets/scss/site/style.red.scss b/src/CmsEngine.Ui/assets/scss/site/style.red.scss similarity index 100% rename from CmsEngine.Ui/assets/scss/site/style.red.scss rename to src/CmsEngine.Ui/assets/scss/site/style.red.scss diff --git a/src/CmsEngine.Ui/emailsettings.development.json b/src/CmsEngine.Ui/emailsettings.development.json new file mode 100644 index 00000000..065562a3 --- /dev/null +++ b/src/CmsEngine.Ui/emailsettings.development.json @@ -0,0 +1,23 @@ +{ + "EmailSettings": { + "Domain": "smtp.gmail.com", + "Port": "587", + "Username": "diegoseculo@gmail.com", + "Password": "d#sousa12", + "FromEmail": "diegoseculo@gmail.com", + "CcEmail": "", + "BccEmail": "" + } +} + +//{ +// "EmailSettings": { +// "Domain": "m1.aspify.com", +// "Port": "465", +// "Username": "fromblog@davidsonsousa.net", +// "Password": "fWJUOFw2E677", +// "FromEmail": "fromblog@davidsonsousa.net", +// "CcEmail": "", +// "BccEmail": "" +// } +//} diff --git a/CmsEngine.Ui/emailsettings.json b/src/CmsEngine.Ui/emailsettings.json similarity index 100% rename from CmsEngine.Ui/emailsettings.json rename to src/CmsEngine.Ui/emailsettings.json diff --git a/CmsEngine.Ui/gulp-tasks/build-vendors.js b/src/CmsEngine.Ui/gulp-tasks/build-vendors.js similarity index 100% rename from CmsEngine.Ui/gulp-tasks/build-vendors.js rename to src/CmsEngine.Ui/gulp-tasks/build-vendors.js diff --git a/CmsEngine.Ui/gulpfile.js b/src/CmsEngine.Ui/gulpfile.js similarity index 100% rename from CmsEngine.Ui/gulpfile.js rename to src/CmsEngine.Ui/gulpfile.js diff --git a/CmsEngine.Ui/package.json b/src/CmsEngine.Ui/package.json similarity index 100% rename from CmsEngine.Ui/package.json rename to src/CmsEngine.Ui/package.json From 0eaff8360896a75f807a8835ccd2ab097ce1d988 Mon Sep 17 00:00:00 2001 From: Davidson Sousa Date: Sun, 6 Mar 2022 19:30:37 +0100 Subject: [PATCH 02/33] Features/fix email bugs (#110) * Moved files to src; Adapted C# code to a more modern approach; Minor refactoring everywhere * Removed old project folders/files * Removed temporary old solution * Refactored code; Set some members to nullable * Changed usage on TryUpdateModelAsync * Fixed e-mail issues --- .../Extensions/Mapper/ContactFormExtension.cs | 7 +------ src/CmsEngine.Application/Helpers/Email/ContactForm.cs | 5 ++++- src/CmsEngine.Data/Entities/Email.cs | 2 +- src/CmsEngine.Ui/Controllers/HomeController.cs | 3 +-- src/CmsEngine.Ui/Views/Home/Contact.cshtml | 2 +- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs b/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs index b6c61596..6b1aa6ac 100644 --- a/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs +++ b/src/CmsEngine.Application/Extensions/Mapper/ContactFormExtension.cs @@ -28,12 +28,7 @@ public static IEnumerable MapToViewModel(this IEnumerable em foreach (var item in emails) { - viewModel.Add(new ContactForm - { - From = item.From, - Subject = item.Subject, - Message = item.Message - }); + viewModel.Add(new ContactForm(item.From, item.Subject, item.Message)); } return viewModel; diff --git a/src/CmsEngine.Application/Helpers/Email/ContactForm.cs b/src/CmsEngine.Application/Helpers/Email/ContactForm.cs index c93ad684..65a1e52d 100644 --- a/src/CmsEngine.Application/Helpers/Email/ContactForm.cs +++ b/src/CmsEngine.Application/Helpers/Email/ContactForm.cs @@ -5,11 +5,14 @@ public class ContactForm [Required] [DataType(DataType.EmailAddress)] public string From { get; set; } + [DataType(DataType.EmailAddress)] - public string To { get; set; } + public string? To { get; set; } + [Required] [MaxLength(150)] public string Subject { get; set; } + [Required] [MaxLength(500)] public string Message { get; set; } diff --git a/src/CmsEngine.Data/Entities/Email.cs b/src/CmsEngine.Data/Entities/Email.cs index 86bfe790..0e5bc1d2 100644 --- a/src/CmsEngine.Data/Entities/Email.cs +++ b/src/CmsEngine.Data/Entities/Email.cs @@ -2,7 +2,7 @@ namespace CmsEngine.Data.Entities; public class Email : BaseEntity { - public string? From { get; set; } + public string From { get; set; } public string Subject { get; set; } public string Message { get; set; } public DateTime? DateReceived { get; set; } diff --git a/src/CmsEngine.Ui/Controllers/HomeController.cs b/src/CmsEngine.Ui/Controllers/HomeController.cs index 7d5fbe25..a6873fc6 100644 --- a/src/CmsEngine.Ui/Controllers/HomeController.cs +++ b/src/CmsEngine.Ui/Controllers/HomeController.cs @@ -50,7 +50,7 @@ public IActionResult Contact() } [HttpPost] - public async Task ContactAsync(ContactForm contactForm, string returnUrl = null) + public async Task ContactAsync(ContactForm contactForm) { if (!ModelState.IsValid) { @@ -58,7 +58,6 @@ public async Task ContactAsync(ContactForm contactForm, string re return View(Instance); } - ViewData["ReturnUrl"] = returnUrl; contactForm.To = Instance.ContactDetails.Email; try diff --git a/src/CmsEngine.Ui/Views/Home/Contact.cshtml b/src/CmsEngine.Ui/Views/Home/Contact.cshtml index ef771a4f..97edca6a 100644 --- a/src/CmsEngine.Ui/Views/Home/Contact.cshtml +++ b/src/CmsEngine.Ui/Views/Home/Contact.cshtml @@ -7,7 +7,7 @@ @{ await Html.RenderPartialAsync("_Messages"); }

Contact

-
+
From 77fbda859e56d825aac563b17e694f450ae8c8ff Mon Sep 17 00:00:00 2001 From: Davidson Sousa Date: Sun, 6 Mar 2022 20:27:29 +0100 Subject: [PATCH 03/33] Features/fix email bugs (#112) * Moved files to src; Adapted C# code to a more modern approach; Minor refactoring everywhere * Removed old project folders/files * Removed temporary old solution * Refactored code; Set some members to nullable * Changed usage on TryUpdateModelAsync * Fixed e-mail issues From f0f6a519e113ab7a446af4213a4f7f011dfec12e Mon Sep 17 00:00:00 2001 From: Davidson Sousa Date: Mon, 7 Mar 2022 15:37:19 +0100 Subject: [PATCH 04/33] Feature/fix email send issues (#114) * Removed development files * Updated .gitignore * Added MailKit library * Implemented e-mail sender with MailKit --- .gitignore | 5 +- .../CmsEngine.Application.csproj | 1 + src/CmsEngine.Application/GlobalUsings.cs | 4 +- .../Helpers/Email/CmsEngineEmailSender.cs | 72 +++++++++---------- src/CmsEngine.Ui/appsettings.Development.json | 18 ----- .../emailsettings.development.json | 23 ------ 6 files changed, 40 insertions(+), 83 deletions(-) delete mode 100644 src/CmsEngine.Ui/appsettings.Development.json delete mode 100644 src/CmsEngine.Ui/emailsettings.development.json diff --git a/.gitignore b/.gitignore index 358dd8b9..c8198aa5 100644 --- a/.gitignore +++ b/.gitignore @@ -257,8 +257,7 @@ paket-files/ # CmsEngine PublishOutput/ package-lock.json -/CmsEngine.Ui/certificate.Development.json -/CmsEngine.Ui/emailsettings.Development.json +*.Development.json [Rr]eadme.md -/CmsEngine.Ui/_logs +CmsEngine.Ui/_logs global.json diff --git a/src/CmsEngine.Application/CmsEngine.Application.csproj b/src/CmsEngine.Application/CmsEngine.Application.csproj index 14686bc9..e7c0373e 100644 --- a/src/CmsEngine.Application/CmsEngine.Application.csproj +++ b/src/CmsEngine.Application/CmsEngine.Application.csproj @@ -7,6 +7,7 @@ + diff --git a/src/CmsEngine.Application/GlobalUsings.cs b/src/CmsEngine.Application/GlobalUsings.cs index 31012368..ce57d61f 100644 --- a/src/CmsEngine.Application/GlobalUsings.cs +++ b/src/CmsEngine.Application/GlobalUsings.cs @@ -5,9 +5,7 @@ global using System.Globalization; global using System.Linq.Expressions; global using System.Net; -global using System.Net.Mail; global using System.Reflection; -global using System.Text; global using System.Text.Encodings.Web; global using System.Xml.Linq; global using CmsEngine.Application.Attributes; @@ -25,6 +23,7 @@ global using CmsEngine.Core.Utils; global using CmsEngine.Data; global using CmsEngine.Data.Entities; +global using MailKit.Net.Smtp; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Http; global using Microsoft.AspNetCore.Identity; @@ -32,5 +31,6 @@ global using Microsoft.Extensions.Caching.Memory; global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Options; +global using MimeKit; global using Newtonsoft.Json; global using Newtonsoft.Json.Linq; diff --git a/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs b/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs index d9b67a5e..cb81ae13 100644 --- a/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs +++ b/src/CmsEngine.Application/Helpers/Email/CmsEngineEmailSender.cs @@ -20,49 +20,19 @@ private async Task ExecuteAsync(ContactForm contactForm) { _logger.LogDebug("SendEmailAsync(contactForm: {0})", contactForm.ToString()); - var from = contactForm.From ?? _emailSettings.Username; - var body = $"From: {from}\r\nTo: {contactForm.To}\r\n-----\r\n\r\n{contactForm.Message}"; + var message = PrepareMailMessage(contactForm); try { - var message = new MailMessage + using (var smtpClient = new SmtpClient()) { - From = new MailAddress(from), - Subject = $"🌐 CmsEngine - {contactForm.Subject}", - SubjectEncoding = Encoding.UTF8, - IsBodyHtml = false, - Body = body, - BodyEncoding = Encoding.UTF8, - Priority = MailPriority.Normal - }; - - if (!string.IsNullOrWhiteSpace(contactForm.To)) - { - message.To.Add(contactForm.To); + await smtpClient.ConnectAsync(_emailSettings.Domain, _emailSettings.Port, true); + await smtpClient.AuthenticateAsync(_emailSettings.Username, _emailSettings.Password); + await smtpClient.SendAsync(message); + await smtpClient.DisconnectAsync(true); } - if (!string.IsNullOrWhiteSpace(_emailSettings.CcEmail)) - { - message.CC.Add(_emailSettings.CcEmail); - } - - if (!string.IsNullOrWhiteSpace(_emailSettings.BccEmail)) - { - message.Bcc.Add(_emailSettings.BccEmail); - } - - using (var smtp = new SmtpClient(_emailSettings.Domain, _emailSettings.Port)) - { - smtp.EnableSsl = true; - smtp.DeliveryMethod = SmtpDeliveryMethod.Network; - smtp.UseDefaultCredentials = false; - smtp.Credentials = new NetworkCredential(_emailSettings.Username, _emailSettings.Password); - - _logger.LogDebug("Message {0}", message.ToString()); - await smtp.SendMailAsync(message); - } - - _logger.LogDebug("Email sent from {0} to {1}", message.From, message.To[0]); + _logger.LogDebug("Email sent from {0} to {1}", message.From[0], message.To[0]); } catch (Exception ex) { @@ -70,4 +40,32 @@ private async Task ExecuteAsync(ContactForm contactForm) throw new EmailException("Error when sending e-mail", ex); } } + + private MimeMessage PrepareMailMessage(ContactForm contactForm) + { + var from = contactForm.From; + var body = contactForm.Message; + + if (string.IsNullOrWhiteSpace(from)) + { + from = _emailSettings.Username; + body = $"From: {from}\r\nTo: {contactForm.To}\r\n-----\r\n\r\n{body}"; + } + + var message = new MimeMessage(); + message.From.Add(new MailboxAddress(from, from)); + message.Subject = $"🌐 CmsEngine - {contactForm.Subject}"; + + message.Body = new TextPart("plain") + { + Text = body + }; + + if (!string.IsNullOrWhiteSpace(contactForm.To)) + { + message.To.Add(new MailboxAddress(contactForm.To, contactForm.To)); + } + + return message; + } } diff --git a/src/CmsEngine.Ui/appsettings.Development.json b/src/CmsEngine.Ui/appsettings.Development.json deleted file mode 100644 index 9da47f73..00000000 --- a/src/CmsEngine.Ui/appsettings.Development.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "ConnectionStrings": { - "DefaultConnection": "Server=.\\;Database=CmsEngine;Integrated Security=True;MultipleActiveResultSets=True" - }, - "Serilog": { - "MinimumLevel": { - "Default": "Debug", - "Override": { - "System": "Warning", - "Microsoft": "Warning" - } - }, - "WriteTo": [ - { "Name": "Console" } - ], - "Enrich": [ "FromLogContext" ] - } -} diff --git a/src/CmsEngine.Ui/emailsettings.development.json b/src/CmsEngine.Ui/emailsettings.development.json deleted file mode 100644 index 065562a3..00000000 --- a/src/CmsEngine.Ui/emailsettings.development.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "EmailSettings": { - "Domain": "smtp.gmail.com", - "Port": "587", - "Username": "diegoseculo@gmail.com", - "Password": "d#sousa12", - "FromEmail": "diegoseculo@gmail.com", - "CcEmail": "", - "BccEmail": "" - } -} - -//{ -// "EmailSettings": { -// "Domain": "m1.aspify.com", -// "Port": "465", -// "Username": "fromblog@davidsonsousa.net", -// "Password": "fWJUOFw2E677", -// "FromEmail": "fromblog@davidsonsousa.net", -// "CcEmail": "", -// "BccEmail": "" -// } -//} From 8773adb0ff3bccf8beca8f761c63cc3a6915a66b Mon Sep 17 00:00:00 2001 From: Davidson Sousa Date: Sun, 18 Sep 2022 12:39:17 +0200 Subject: [PATCH 05/33] Feature/minor changes (#116) * Updated nuget packages * Improved HtmlHelperExtensions logic and added possibility to use with slugs * Added "About me" menu item --- .../CmsEngine.Application.csproj | 2 +- src/CmsEngine.Data/CmsEngine.Data.csproj | 6 ++-- src/CmsEngine.Ui/CmsEngine.Ui.csproj | 23 ++++++++------- .../Extensions/HtmlHelperExtensions.cs | 28 ++++++++++++++----- src/CmsEngine.Ui/Views/Shared/_Layout.cshtml | 7 +++-- 5 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/CmsEngine.Application/CmsEngine.Application.csproj b/src/CmsEngine.Application/CmsEngine.Application.csproj index e7c0373e..7fb9e399 100644 --- a/src/CmsEngine.Application/CmsEngine.Application.csproj +++ b/src/CmsEngine.Application/CmsEngine.Application.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/CmsEngine.Data/CmsEngine.Data.csproj b/src/CmsEngine.Data/CmsEngine.Data.csproj index 88a55253..cbd576f7 100644 --- a/src/CmsEngine.Data/CmsEngine.Data.csproj +++ b/src/CmsEngine.Data/CmsEngine.Data.csproj @@ -8,9 +8,9 @@ - - - + + + diff --git a/src/CmsEngine.Ui/CmsEngine.Ui.csproj b/src/CmsEngine.Ui/CmsEngine.Ui.csproj index 843b3933..6d2ca7d2 100644 --- a/src/CmsEngine.Ui/CmsEngine.Ui.csproj +++ b/src/CmsEngine.Ui/CmsEngine.Ui.csproj @@ -8,16 +8,19 @@ - - - - - - - - - - + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs b/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs index 925b6c95..cf333125 100644 --- a/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs +++ b/src/CmsEngine.Ui/Extensions/HtmlHelperExtensions.cs @@ -2,14 +2,28 @@ namespace CmsEngine.Ui.Extensions; public static class HtmlHelperExtensions { - public static string IsSelected(this IHtmlHelper htmlHelper, string controllers, string actions, string cssClass = "active") + public static string IsSelected(this IHtmlHelper htmlHelper, string? controllers = null, string? actions = null, string? slugs = null, string cssClass = "active") { - Guard.Against.Null(htmlHelper); + var hasSlugs = !string.IsNullOrWhiteSpace(slugs) && CheckValues(htmlHelper, "slug", slugs); + var hasActions = !string.IsNullOrWhiteSpace(actions) && CheckValues(htmlHelper, "action", actions); + var hasController = !string.IsNullOrWhiteSpace(controllers) && CheckValues(htmlHelper, "controller", controllers); - string currentAction = (htmlHelper.ViewContext.RouteData.Values["action"] as string)?.ToLower(); - string currentController = (htmlHelper.ViewContext.RouteData.Values["controller"] as string)?.ToLower(); - var acceptedActions = (actions ?? currentAction).Split(',').Select(x => x.Trim().ToLower()); - var acceptedControllers = (controllers ?? currentController).Split(',').Select(x => x.Trim().ToLower()); - return acceptedActions.Contains(currentAction) && acceptedControllers.Contains(currentController) ? cssClass : string.Empty; + return hasSlugs || (hasActions && hasController) + ? cssClass + : string.Empty; + } + + private static bool CheckValues(IHtmlHelper htmlHelper, string valueName, string? values) + { + var currentValue = htmlHelper.ViewContext.RouteData.Values[valueName] as string; + + if (string.IsNullOrWhiteSpace(currentValue)) + { + return false; + } + + var acceptedValues = (values ?? currentValue).Split(',').Select(x => x.Trim()); + + return acceptedValues.Contains(currentValue, StringComparer.OrdinalIgnoreCase); } } diff --git a/src/CmsEngine.Ui/Views/Shared/_Layout.cshtml b/src/CmsEngine.Ui/Views/Shared/_Layout.cshtml index 974c5133..c3fe9b91 100644 --- a/src/CmsEngine.Ui/Views/Shared/_Layout.cshtml +++ b/src/CmsEngine.Ui/Views/Shared/_Layout.cshtml @@ -72,14 +72,17 @@

@selectedPost.Title