From 5eb45263d97704a0c52e5098678f51fe5a952666 Mon Sep 17 00:00:00 2001 From: Uncled1023 Date: Thu, 14 Jun 2018 17:57:03 -0700 Subject: [PATCH] Ported existing projects to Asp.Net Core (Except ServerMaint) --- .editorconfig | 10 +- .gitignore | 149 +- .../ApiConfig.cs | 8 +- .../BlogConfig.cs | 7 +- .../Configuration => Configuration}/Config.cs | 60 +- Configuration/Configuration.csproj | 17 + .../ContactConfig.cs | 9 +- .../DatabaseConfig.cs | 0 .../EmailAccount.cs | 8 +- .../EmailConfig.cs | 7 +- .../GitConfig.cs | 8 +- .../IRCConfig.cs | 8 +- .../LoggingConfig.cs | 7 +- .../PasteConfig.cs | 8 +- .../PiwikConfig.cs | 8 +- .../PodcastConfig.cs | 6 +- .../ShortenerConfig.cs | 7 +- .../StatsConfig.cs | 8 +- .../StreamConfig.cs | 6 +- .../UploadConfig.cs | 3 - .../UserConfig.cs | 7 - .../VaultConfig.cs | 7 +- GitVersionConfig.yaml | 2 - {Utilities/Logging => Logging}/LogMessage.cs | 9 +- Logging/Logger.cs | 161 + Logging/LoggerExtensions.cs | 17 + Logging/LoggerProvider.cs | 30 + Logging/Logging.csproj | 14 + MailService/HMailService.cs | 126 + MailService/IMailService.cs | 27 + MailService/MailService.cs | 29 + MailService/MailService.csproj | 19 + MailService/MysqlDatabase.cs | 173 + MailService/lib/Teknik.hMailServer.dll | Bin 0 -> 166912 bytes Piwik/Piwik.csproj | 13 + Piwik/Reporting.cs | 96 + Piwik/Tracking.cs | 93 + {Utilities/Piwik => Piwik}/VisitorData.cs | 0 Teknik.sln | 141 +- Teknik/App_Start/BundleConfig.cs | 54 - Teknik/App_Start/CustomRazorViewEngine.cs | 51 - Teknik/App_Start/FilterConfig.cs | 17 - Teknik/App_Start/RouteConfig.cs | 23 - Teknik/App_Start/SubdomainRoute.cs | 101 - Teknik/App_Start/SubdomainRouteExtension.cs | 154 - Teknik/Areas/API/APIAreaRegistration.cs | 70 - Teknik/Areas/API/Controllers/APIController.cs | 13 +- .../Areas/API/Controllers/APIv1Controller.cs | 262 +- Teknik/Areas/API/Models/APIv1PasteModel.cs | 9 +- Teknik/Areas/API/Models/APIv1UploadModel.cs | 5 +- Teknik/Areas/API/Views/web.config | 36 - Teknik/Areas/About/AboutAreaRegistration.cs | 30 - .../About/Controllers/AboutController.cs | 16 +- .../Areas/About/ViewModels/AboutViewModel.cs | 4 +- Teknik/Areas/About/Views/About/Index.cshtml | 14 +- Teknik/Areas/About/Views/web.config | 36 - Teknik/Areas/Abuse/AbuseAreaRegistration.cs | 30 - .../Abuse/Controllers/AbuseController.cs | 14 +- Teknik/Areas/Abuse/Views/Abuse/Index.cshtml | 1 - Teknik/Areas/Abuse/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Abuse/Views/web.config | 36 - .../ResourceOwnerPasswordValidator.cs | 36 + Teknik/Areas/Admin/AdminAreaRegistration.cs | 77 - .../Admin/Controllers/AdminController.cs | 67 +- .../Areas/Admin/Views/Admin/Dashboard.cshtml | 2 - .../Admin/Views/Admin/UploadResult.cshtml | 2 - .../Admin/Views/Admin/UploadSearch.cshtml | 12 +- .../Areas/Admin/Views/Admin/UserInfo.cshtml | 12 +- .../Areas/Admin/Views/Admin/UserResult.cshtml | 2 - .../Admin/Views/Admin/UserResults.cshtml | 2 +- .../Areas/Admin/Views/Admin/UserSearch.cshtml | 12 +- Teknik/Areas/Admin/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Admin/Views/web.config | 36 - Teknik/Areas/Blog/BlogAreaRegistration.cs | 74 - .../Areas/Blog/Controllers/BlogController.cs | 224 +- Teknik/Areas/Blog/Models/BlogPost.cs | 2 +- Teknik/Areas/Blog/Models/BlogPostTag.cs | 20 + Teknik/Areas/Blog/ViewModels/BlogViewModel.cs | 18 +- .../Areas/Blog/ViewModels/CommentViewModel.cs | 3 - .../Blog/ViewModels/CreatePostViewModel.cs | 10 +- .../Blog/ViewModels/EditPostViewModel.cs | 10 +- Teknik/Areas/Blog/ViewModels/PostViewModel.cs | 6 +- Teknik/Areas/Blog/Views/Blog/Blog.cshtml | 32 +- Teknik/Areas/Blog/Views/Blog/Comment.cshtml | 2 - Teknik/Areas/Blog/Views/Blog/Comments.cshtml | 2 +- Teknik/Areas/Blog/Views/Blog/EditPost.cshtml | 44 +- Teknik/Areas/Blog/Views/Blog/NewPost.cshtml | 44 +- Teknik/Areas/Blog/Views/Blog/Post.cshtml | 2 - Teknik/Areas/Blog/Views/Blog/Posts.cshtml | 2 +- Teknik/Areas/Blog/Views/Blog/ViewPost.cshtml | 26 +- Teknik/Areas/Blog/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Blog/Views/web.config | 36 - .../Areas/Contact/ContactAreaRegistration.cs | 44 - .../Contact/Controllers/ContactController.cs | 52 +- .../Areas/Contact/Views/Contact/Index.cshtml | 19 +- Teknik/Areas/Contact/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Contact/Views/web.config | 36 - Teknik/Areas/Dev/Controllers/DevController.cs | 17 +- Teknik/Areas/Dev/DevAreaRegistration.cs | 31 - Teknik/Areas/Dev/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Dev/Views/web.config | 36 - .../Error/Controllers/ErrorController.cs | 188 +- Teknik/Areas/Error/ErrorAreaRegistration.cs | 61 - .../Areas/Error/ViewModels/ErrorViewModel.cs | 5 +- .../Error/ViewModels/SubmitReportViewModel.cs | 12 +- .../Areas/Error/Views/Error/Exception.cshtml | 7 +- Teknik/Areas/Error/Views/Error/Http401.cshtml | 2 - Teknik/Areas/Error/Views/Error/Http403.cshtml | 2 - Teknik/Areas/Error/Views/Error/Http404.cshtml | 2 - Teknik/Areas/Error/Views/Error/Http500.cshtml | 7 +- .../{General.cshtml => HttpGeneral.cshtml} | 6 +- Teknik/Areas/Error/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Error/Views/web.config | 36 - Teknik/Areas/FAQ/Controllers/FAQController.cs | 18 +- Teknik/Areas/FAQ/FAQAreaRegistration.cs | 36 - Teknik/Areas/FAQ/Views/FAQ/Index.cshtml | 8 +- Teknik/Areas/FAQ/Views/_ViewStart.cshtml | 3 - Teknik/Areas/FAQ/Views/web.config | 36 - .../Areas/Help/Controllers/HelpController.cs | 63 +- Teknik/Areas/Help/HelpAreaRegistration.cs | 116 - Teknik/Areas/Help/Views/Help/API/API.cshtml | 6 +- .../Areas/Help/Views/Help/API/v1/Paste.cshtml | 11 +- .../Help/Views/Help/API/v1/Shorten.cshtml | 4 +- .../Help/Views/Help/API/v1/Upload.cshtml | 8 +- Teknik/Areas/Help/Views/Help/Blog.cshtml | 2 - Teknik/Areas/Help/Views/Help/Git.cshtml | 2 - Teknik/Areas/Help/Views/Help/Index.cshtml | 2 - Teknik/Areas/Help/Views/Help/Irc.cshtml | 4 +- Teknik/Areas/Help/Views/Help/Mail.cshtml | 18 +- Teknik/Areas/Help/Views/Help/Markdown.cshtml | 2 - Teknik/Areas/Help/Views/Help/Mumble.cshtml | 4 +- Teknik/Areas/Help/Views/Help/RSS.cshtml | 2 - Teknik/Areas/Help/Views/Help/Tools.cshtml | 2 - Teknik/Areas/Help/Views/Help/Upload.cshtml | 10 +- Teknik/Areas/Help/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Help/Views/web.config | 36 - .../Areas/Home/Controllers/HomeController.cs | 45 +- Teknik/Areas/Home/HomeAreaRegistration.cs | 79 - Teknik/Areas/Home/Scripts/Home.js | 1 - Teknik/Areas/Home/Views/Home/Index.cshtml | 11 +- Teknik/Areas/Home/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Home/Views/web.config | 36 - Teknik/Areas/IRC/Content/IRC.css | 46 - Teknik/Areas/IRC/Controllers/IRCController.cs | 23 - Teknik/Areas/IRC/IrcAreaRegistration.cs | 42 - Teknik/Areas/IRC/Scripts/IRC.js | 247 - .../Areas/IRC/ViewModels/ClientViewModel.cs | 12 - Teknik/Areas/IRC/Views/IRC/Client.cshtml | 90 - Teknik/Areas/IRC/Views/_ViewStart.cshtml | 3 - Teknik/Areas/IRC/Views/web.config | 36 - .../Paste/Controllers/PasteController.cs | 107 +- Teknik/Areas/Paste/PasteAreaRegistration.cs | 94 - Teknik/Areas/Paste/PasteHelper.cs | 6 +- .../Paste/ViewModels/PasteCreateViewModel.cs | 9 +- .../Areas/Paste/ViewModels/PasteViewModel.cs | 6 - Teknik/Areas/Paste/Views/Paste/Full.cshtml | 7 +- Teknik/Areas/Paste/Views/Paste/Index.cshtml | 30 +- .../Paste/Views/Paste/PasswordNeeded.cshtml | 6 +- Teknik/Areas/Paste/Views/Paste/Simple.cshtml | 20 +- Teknik/Areas/Paste/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Paste/Views/web.config | 37 - .../Podcast/Controllers/PodcastController.cs | 595 +- Teknik/Areas/Podcast/Models/Podcast.cs | 2 +- Teknik/Areas/Podcast/Models/PodcastFile.cs | 2 +- Teknik/Areas/Podcast/Models/PodcastTag.cs | 20 + .../Areas/Podcast/PodcastAreaRegistration.cs | 65 - .../Podcast/ViewModels/PodcastViewModel.cs | 4 +- .../Podcast/Views/Podcast/Comment.cshtml | 2 - .../Areas/Podcast/Views/Podcast/Main.cshtml | 21 +- .../Podcast/Views/Podcast/Podcast.cshtml | 1 - .../Podcast/Views/Podcast/ViewPodcast.cshtml | 26 +- Teknik/Areas/Podcast/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Podcast/Views/web.config | 36 - .../Privacy/Controllers/PrivacyController.cs | 19 +- .../Areas/Privacy/PrivacyAreaRegistration.cs | 30 - .../Areas/Privacy/Views/Privacy/Index.cshtml | 4 +- Teknik/Areas/Privacy/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Privacy/Views/web.config | 36 - Teknik/Areas/RSS/Controllers/RSSController.cs | 238 +- Teknik/Areas/RSS/RSSAreaRegistration.cs | 46 - .../Controllers/ShortenerController.cs | 81 +- .../Shortener/ShortenerAreaRegistration.cs | 60 - .../{Shortener.cs => ShortenerHelper.cs} | 3 +- .../Shortener/Views/Shortener/Index.cshtml | 9 +- .../Shortener/Views/Shortener/Verify.cshtml | 3 +- .../Areas/Shortener/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Shortener/Views/web.config | 36 - .../Stats/Controllers/StatsController.cs | 265 +- Teknik/Areas/Stats/StatsAreaRegistration.cs | 46 - Teknik/Areas/Stats/Views/Stats/Index.cshtml | 29 +- .../Stats/Views/Stats/Transactions.cshtml | 6 +- Teknik/Areas/Stats/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Stats/Views/web.config | 36 - .../Stream/Controllers/StreamController.cs | 55 - Teknik/Areas/Stream/StreamAreaRegistration.cs | 38 - .../Stream/ViewModels/StreamViewModel.cs | 19 - Teknik/Areas/Stream/Views/Stream/Index.cshtml | 26 - Teknik/Areas/Stream/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Stream/Views/web.config | 36 - Teknik/Areas/TOS/Controllers/TOSController.cs | 19 +- Teknik/Areas/TOS/TOSAreaRegistration.cs | 30 - Teknik/Areas/TOS/Views/TOS/Index.cshtml | 2 +- Teknik/Areas/TOS/Views/_ViewStart.cshtml | 3 - Teknik/Areas/TOS/Views/web.config | 36 - Teknik/Areas/Upload/Content/Upload.css | 1 - .../Upload/Controllers/UploadController.cs | 339 +- Teknik/Areas/Upload/UploadAreaRegistration.cs | 99 - Teknik/Areas/Upload/Uploader.cs | 13 +- .../Areas/Upload/Views/Upload/Download.cshtml | 12 +- Teknik/Areas/Upload/Views/Upload/Index.cshtml | 29 +- Teknik/Areas/Upload/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Upload/Views/web.config | 36 - .../Areas/User/Controllers/UserController.cs | 1403 +-- Teknik/Areas/User/Models/BlogSettings.cs | 1 - Teknik/Areas/User/Models/Group.cs | 21 - Teknik/Areas/User/Models/InviteCode.cs | 4 + Teknik/Areas/User/Models/Role.cs | 2 +- Teknik/Areas/User/Models/SecuritySettings.cs | 1 - Teknik/Areas/User/Models/UploadSettings.cs | 1 - Teknik/Areas/User/Models/User.cs | 6 +- Teknik/Areas/User/Models/UserRole.cs | 20 + Teknik/Areas/User/Models/UserSettings.cs | 1 - Teknik/Areas/User/UserAreaRegistration.cs | 208 - Teknik/Areas/User/Utility/UserHelper.cs | 255 +- .../User/ViewModels/EditSettingsViewModel.cs | 14 +- .../Areas/User/ViewModels/LoginViewModel.cs | 7 - .../ViewModels/ProfileSettingsViewModel.cs | 9 - .../User/ViewModels/RegisterViewModel.cs | 7 +- .../ViewModels/SecuritySettingsViewModel.cs | 12 +- .../User/ViewModels/SetPasswordViewModel.cs | 12 +- .../Areas/User/Views/User/GetPremium.cshtml | 14 +- Teknik/Areas/User/Views/User/Login.cshtml | 10 +- Teknik/Areas/User/Views/User/Register.cshtml | 14 +- .../User/Views/User/ResetPassword.cshtml | 12 +- .../User/ResetPasswordVerification.cshtml | 12 +- .../Views/User/Settings/BlogSettings.cshtml | 11 +- .../Views/User/Settings/InviteCode.cshtml | 1 - .../Views/User/Settings/InviteSettings.cshtml | 11 +- .../User/Settings/ProfileSettings.cshtml | 11 +- .../User/Settings/SecuritySettings.cshtml | 18 +- .../User/Views/User/Settings/Settings.cshtml | 9 +- .../Views/User/Settings/UploadSettings.cshtml | 12 +- .../User/Views/User/TwoFactorCheck.cshtml | 8 +- Teknik/Areas/User/Views/User/ViewLogin.cshtml | 2 +- .../Areas/User/Views/User/ViewProfile.cshtml | 17 +- .../User/Views/User/ViewRegistration.cshtml | 2 +- .../User/Views/User/_LoginModalPartial.cshtml | 10 +- .../User/Views/User/_LoginPartial.cshtml | 13 +- Teknik/Areas/User/Views/_ViewStart.cshtml | 3 - Teknik/Areas/User/Views/web.config | 36 - .../Vault/Controllers/VaultController.cs | 548 +- Teknik/Areas/Vault/VaultAreaRegistration.cs | 98 - .../Vault/ViewModels/ModifyVaultViewModel.cs | 5 - .../Vault/Views/Vault/ModifyVault.cshtml | 29 +- .../Vault/Views/Vault/ModifyVaultItem.cshtml | 4 +- .../Areas/Vault/Views/Vault/PasteItem.cshtml | 15 +- .../Areas/Vault/Views/Vault/UploadItem.cshtml | 2 - .../Areas/Vault/Views/Vault/ViewVault.cshtml | 14 +- Teknik/Areas/Vault/Views/_ViewStart.cshtml | 3 - Teknik/Areas/Vault/Views/web.config | 36 - Teknik/Attributes/TeknikAuthorizeAttribute.cs | 314 +- Teknik/BaseViewPage.cs | 25 - .../Blog/Content => Content/Blog}/Blog.css | 0 .../Content/Bootstra.386/bootstrap-theme.css | 187 - Teknik/Content/Bootstra.386/bootstrap.css | 5956 --------- .../FAQ/Content => Content/FAQ}/FAQ.css | 0 .../Help/Content => Content/Help}/Help.css | 0 Teknik/Content/Highlight/agate.css | 108 - Teknik/Content/Highlight/androidstudio.css | 66 - Teknik/Content/Highlight/arduino-light.css | 88 - Teknik/Content/Highlight/arta.css | 73 - Teknik/Content/Highlight/ascetic.css | 45 - .../Content/Highlight/atelier-cave-dark.css | 83 - .../Content/Highlight/atelier-cave-light.css | 85 - .../Content/Highlight/atelier-dune-dark.css | 69 - .../Content/Highlight/atelier-dune-light.css | 69 - .../Highlight/atelier-estuary-dark.css | 84 - .../Highlight/atelier-estuary-light.css | 84 - .../Content/Highlight/atelier-forest-dark.css | 69 - .../Highlight/atelier-forest-light.css | 69 - .../Content/Highlight/atelier-heath-dark.css | 69 - .../Content/Highlight/atelier-heath-light.css | 69 - .../Highlight/atelier-lakeside-dark.css | 69 - .../Highlight/atelier-lakeside-light.css | 69 - .../Highlight/atelier-plateau-dark.css | 84 - .../Highlight/atelier-plateau-light.css | 84 - .../Highlight/atelier-savanna-dark.css | 84 - .../Highlight/atelier-savanna-light.css | 84 - .../Highlight/atelier-seaside-dark.css | 69 - .../Highlight/atelier-seaside-light.css | 69 - .../Highlight/atelier-sulphurpool-dark.css | 69 - .../Highlight/atelier-sulphurpool-light.css | 69 - Teknik/Content/Highlight/brown-paper.css | 64 - Teknik/Content/Highlight/brown-papersq.png | Bin 18198 -> 0 bytes Teknik/Content/Highlight/codepen-embed.css | 60 - Teknik/Content/Highlight/color-brewer.css | 71 - Teknik/Content/Highlight/dark.css | 63 - Teknik/Content/Highlight/darkula.css | 74 - Teknik/Content/Highlight/default.css | 77 - Teknik/Content/Highlight/docco.css | 97 - Teknik/Content/Highlight/far.css | 71 - Teknik/Content/Highlight/foundation.css | 88 - Teknik/Content/Highlight/github-gist.css | 71 - Teknik/Content/Highlight/github.css | 99 - Teknik/Content/Highlight/googlecode.css | 89 - Teknik/Content/Highlight/grayscale.css | 101 - Teknik/Content/Highlight/hopscotch.css | 83 - Teknik/Content/Highlight/hybrid.css | 102 - Teknik/Content/Highlight/idea.css | 97 - Teknik/Content/Highlight/ir-black.css | 73 - Teknik/Content/Highlight/kimbie.dark.css | 74 - Teknik/Content/Highlight/kimbie.light.css | 74 - Teknik/Content/Highlight/magula.css | 70 - Teknik/Content/Highlight/mono-blue.css | 59 - Teknik/Content/Highlight/monokai-sublime.css | 83 - Teknik/Content/Highlight/monokai.css | 70 - Teknik/Content/Highlight/obsidian.css | 88 - Teknik/Content/Highlight/paraiso-dark.css | 72 - Teknik/Content/Highlight/paraiso-light.css | 72 - Teknik/Content/Highlight/pojoaque.css | 83 - Teknik/Content/Highlight/pojoaque.jpg | Bin 1186 -> 0 bytes Teknik/Content/Highlight/railscasts.css | 106 - Teknik/Content/Highlight/rainbow.css | 85 - Teknik/Content/Highlight/school-book.css | 72 - Teknik/Content/Highlight/school-book.png | Bin 486 -> 0 bytes Teknik/Content/Highlight/solarized-dark.css | 84 - Teknik/Content/Highlight/solarized-light.css | 84 - Teknik/Content/Highlight/sunburst.css | 102 - .../Content/Highlight/tomorrow-night-blue.css | 75 - .../Highlight/tomorrow-night-bright.css | 74 - .../Highlight/tomorrow-night-eighties.css | 74 - Teknik/Content/Highlight/tomorrow-night.css | 75 - Teknik/Content/Highlight/tomorrow.css | 72 - Teknik/Content/Highlight/vs.css | 68 - Teknik/Content/Highlight/xcode.css | 93 - Teknik/Content/Highlight/zenburn.css | 80 - .../Home/Content => Content/Home}/Home.css | 0 .../Paste/Content => Content/Paste}/Paste.css | 0 .../Content => Content/Podcast}/Podcast.css | 0 .../Vault/Content => Content/Vault}/Vault.css | 0 Teknik/Content/audioplayer.css | 266 - Teknik/Content/bootstrap-flat-extras.css | 1209 -- Teknik/Content/bootstrap-flat-extras.min.css | 10 - Teknik/Content/bootstrap-flat.css | 338 - Teknik/Content/bootstrap-flat.min.css | 10 - Teknik/Content/bootstrap-markdown.min.css | 1 - .../bootstrap2/bootstrap-switch.css | 519 - .../bootstrap2/bootstrap-switch.min.css | 22 - .../bootstrap3/bootstrap-switch.css | 196 - .../bootstrap3/bootstrap-switch.min.css | 22 - Teknik/Content/bootstrap-theme.css | 587 - Teknik/Content/bootstrap-theme.css.map | 1 - Teknik/Content/bootstrap-theme.min.css | 6 - Teknik/Content/bootstrap-theme.min.css.map | 1 - Teknik/Content/bootstrap.css | 6757 ---------- Teknik/Content/bootstrap.css.map | 1 - Teknik/Content/bootstrap.min.css | 6 - Teknik/Content/bootstrap.min.css.map | 1 - Teknik/Content/dropzone.css | 410 - Teknik/Content/font-awesome.css | 2337 ---- Teknik/Content/font-awesome.min.css | 4 - Teknik/Content/jquery.tocify.css | 75 - Teknik/Content/mdd_styles.css | 211 - Teknik/Controllers/DefaultController.cs | 158 +- .../Data/DbInitializer.cs | 5 +- Teknik/Data/TeknikEntities.cs | 239 + Teknik/Filters/CORSActionFilter.cs | 7 +- Teknik/Filters/TrackDownload.cs | 39 +- Teknik/Filters/TrackLink.cs | 38 +- Teknik/Filters/TrackPageView.cs | 60 +- Teknik/Global.asax | 1 - Teknik/Global.asax.cs | 191 - Teknik/Hubs/IRCClientHub.cs | 164 - Teknik/Hubs/ServerUsageHub.cs | 92 - Teknik/Middleware/BlacklistMiddleware.cs | 119 + Teknik/Middleware/CORSMiddleware.cs | 71 + Teknik/Middleware/CSPMiddleware.cs | 60 + Teknik/Middleware/ErrorHandlerMiddleware.cs | 98 + .../PerformanceMonitorMiddleware.cs | 68 + .../Middleware/SetupHttpContextMiddleware.cs | 42 + Teknik/Migrations/Configuration.cs | 98 - Teknik/Migrations/InitializeTransfers.sql | 20 - Teknik/Migrations/SetCaseSensitivity.sql | 29 - Teknik/Models/TeknikEntities.cs | 191 - Teknik/Models/TransferTypes.cs | 8 +- Teknik/Modules/BlacklistModule.cs | 135 - Teknik/Modules/CORSModule.cs | 59 - Teknik/Modules/CSPModule.cs | 48 - Teknik/Modules/CompressionModule.cs | 40 - Teknik/Modules/PerformanceMonitorModule.cs | 47 - Teknik/Modules/UserAuthModule.cs | 112 - Teknik/Program.cs | 34 + Teknik/Properties/AssemblyInfo.cs | 33 - .../PublishProfiles/FolderProfile.pubxml | 22 + ...ubxml => Teknik Development (AMS1).pubxml} | 11 +- .../PublishProfiles/Teknik Production.pubxml | 24 - Teknik/Properties/launchSettings.json | 38 + Teknik/Routes.cs | 780 ++ .../Scripts => Scripts/Admin}/UploadSearch.js | 0 .../Scripts => Scripts/Admin}/UserInfo.js | 0 .../Scripts => Scripts/Admin}/UserSearch.js | 0 .../Blog/Scripts => Scripts/Blog}/Blog.js | 20 +- Teknik/Scripts/Bootstra.386/bootstrap.js | 2419 ---- .../Scripts => Scripts/Contact}/Contact.js | 0 Teknik/Scripts/Crypto-js/aes.js | 35 - Teknik/Scripts/Crypto-js/enc-base64.js | 109 - Teknik/Scripts/Crypto-js/enc-utf16.js | 135 - Teknik/Scripts/Crypto-js/lib-typedarrays.js | 62 - Teknik/Scripts/Crypto-js/mode-ctr.js | 44 - Teknik/Scripts/Crypto-js/pad-nopadding.js | 16 - Teknik/Scripts/Dropzone/dropzone.js | 1752 --- .../Error/Scripts => Scripts/Error}/Error.js | 2 +- Teknik/Scripts/FileSaver.js | 270 - Teknik/Scripts/FileSize/filesize.min.js | 6 - Teknik/Scripts/Highcharts/highcharts-3d.js | 51 - Teknik/Scripts/Highcharts/highcharts-more.js | 60 - Teknik/Scripts/Highcharts/highcharts.js | 386 - Teknik/Scripts/Highlight/highlight.pack.js | 4 - Teknik/Scripts/MarkdownDeep License.txt | 16 - .../Scripts/MarkdownDeep Quick Reference.txt | 60 - Teknik/Scripts/MarkdownDeepLib.min.js | 443 - Teknik/Scripts/PageDown/Markdown.Converter.js | 1519 --- Teknik/Scripts/PageDown/Markdown.Editor.js | 2239 ---- Teknik/Scripts/PageDown/Markdown.Sanitizer.js | 108 - .../Paste/Scripts => Scripts/Paste}/Paste.js | 0 Teknik/Scripts/Paste/Simple.js | 1 + .../Scripts => Scripts/Podcast}/Podcast.js | 0 .../Shortener}/Shortener.js | 0 .../Stats/Scripts => Scripts/Stats}/Stats.js | 29 - .../Scripts => Scripts/Upload}/Download.js | 6 +- .../Upload}/EncryptionWorker.js | 0 .../Scripts => Scripts/Upload}/Upload.js | 4 +- .../Scripts => Scripts/User}/BlogSettings.js | 0 .../Scripts => Scripts/User}/CheckAuthCode.js | 0 .../User}/InviteSettings.js | 0 .../User/Scripts => Scripts/User}/Profile.js | 0 .../User}/ProfileSettings.js | 0 .../Scripts => Scripts/User}/ResetPass.js | 0 .../User}/SecuritySettings.js | 0 .../User/Scripts => Scripts/User}/Settings.js | 0 .../User}/UploadSettings.js | 0 .../Vault/Scripts => Scripts/Vault}/Vault.js | 16 +- Teknik/Scripts/_references.js | 61 - Teknik/Scripts/audioplayer.min.js | 6 - Teknik/Scripts/bootbox/bootbox.min.js | 6 - Teknik/Scripts/bootstrap-markdown.js | 1392 --- Teknik/Scripts/bootstrap-select.js | 467 - Teknik/Scripts/bootstrap-switch.js | 710 -- Teknik/Scripts/bootstrap-switch.min.js | 22 - Teknik/Scripts/bootstrap.js | 2377 ---- Teknik/Scripts/bootstrap.min.js | 7 - Teknik/Scripts/common.js | 16 +- Teknik/Scripts/jquery-2.1.4.intellisense.js | 2670 ---- Teknik/Scripts/jquery-3.1.1.intellisense.js | 2670 ---- Teknik/Scripts/jquery-3.3.1.intellisense.js | 2670 ---- Teknik/Scripts/jquery-3.3.1.js | 10364 ---------------- Teknik/Scripts/jquery-3.3.1.min.js | 2 - Teknik/Scripts/jquery-3.3.1.min.map | 1 - Teknik/Scripts/jquery-3.3.1.slim.js | 8269 ------------ Teknik/Scripts/jquery-3.3.1.slim.min.js | 2 - Teknik/Scripts/jquery-3.3.1.slim.min.map | 1 - Teknik/Scripts/jquery-ui.widgets.js | 516 - Teknik/Scripts/jquery.blockUI.js | 619 - Teknik/Scripts/jquery.fileupload.js | 1457 --- Teknik/Scripts/jquery.iframe-transport.js | 214 - Teknik/Scripts/jquery.signalR-2.2.2.js | 2958 ----- Teknik/Scripts/jquery.signalR-2.2.2.min.js | 9 - Teknik/Scripts/jquery.tocify.min.js | 4 - Teknik/Scripts/jquery.validate-vsdoc.js | 1288 -- Teknik/Scripts/jquery.validate.js | 1601 --- Teknik/Scripts/jquery.validate.min.js | 4 - Teknik/Scripts/jquery.validate.unobtrusive.js | 429 - .../jquery.validate.unobtrusive.min.js | 19 - Teknik/Scripts/{ => lib}/Blob.js | 0 Teknik/Scripts/modernizr-2.8.3.js | 1406 --- Teknik/Scripts/ocupload/1.1.2/ocupload.js | 241 - Teknik/Scripts/respond.js | 224 - .../Scripts/respond.matchmedia.addListener.js | 273 - .../respond.matchmedia.addListener.min.js | 5 - Teknik/Scripts/respond.min.js | 5 - .../TeknikCookieAuthenticationEvents.cs | 47 + Teknik/Security/TeknikPrincipal.cs | 5 +- Teknik/SignalR/IRCClient.cs | 362 - Teknik/SignalR/ServerUsageTicker.cs | 157 - Teknik/Startup.cs | 164 +- Teknik/TagHelpers/BundleTagHelper.cs | 114 + Teknik/TagHelpers/NonceTagHelper.cs | 24 + Teknik/Teknik.csproj | 972 +- Teknik/TransformWebConfig/original/Web.config | 78 - Teknik/ViewModels/ViewModelBase.cs | 21 +- Teknik/Views/Shared/_Footer.cshtml | 44 +- Teknik/Views/Shared/_Layout.cshtml | 33 +- Teknik/Views/Shared/_Navbar.cshtml | 20 +- Teknik/Views/Web.config | 43 - Teknik/Views/_ViewStart.cshtml | 3 - Teknik/Web.Debug.config | 30 - Teknik/Web.Release.config | 31 - Teknik/Web.config | 210 - Teknik/_ViewImports.cshtml | 11 + .../{Areas/About/Views => }/_ViewStart.cshtml | 0 Teknik/appsettings.json | 11 + Teknik/bundleconfig.json | 262 + Teknik/bundleconfig.json.bindings | 1 + Teknik/docs/MySqlCommand.xml | 934 -- Teknik/docs/MySqlCommandBuilder.xml | 321 - Teknik/docs/MySqlConnection.xml | 1248 -- Teknik/docs/MySqlConnectionStringBuilder.xml | 55 - Teknik/docs/MySqlDataAdapter.xml | 801 -- Teknik/docs/MySqlDataReader.xml | 452 - Teknik/docs/MySqlException.xml | 53 - Teknik/docs/MySqlHelper.xml | 53 - Teknik/docs/MySqlParameter.xml | 45 - Teknik/docs/MySqlParameterCollection.xml | 45 - Teknik/docs/MySqlTransaction.xml | 329 - Teknik/fonts/FontAwesome.otf | Bin 134808 -> 0 bytes Teknik/fonts/fontawesome-webfont.eot | Bin 165742 -> 0 bytes Teknik/fonts/fontawesome-webfont.svg | 2671 ---- Teknik/fonts/fontawesome-webfont.ttf | Bin 165548 -> 0 bytes Teknik/fonts/fontawesome-webfont.woff | Bin 98024 -> 0 bytes Teknik/fonts/fontawesome-webfont.woff2 | Bin 77160 -> 0 bytes Teknik/fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes Teknik/fonts/glyphicons-halflings-regular.svg | 288 - Teknik/fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes Teknik/gulpfile.js | 78 + Teknik/package-lock.json | 3797 ++++++ Teknik/package.json | 54 + Teknik/packages.config | 47 - TeknikStreaming/App.config | 44 - TeknikStreaming/Program.cs | 25 - TeknikStreaming/Properties/AssemblyInfo.cs | 36 - TeknikStreaming/Server.Designer.cs | 37 - TeknikStreaming/Server.cs | 56 - TeknikStreaming/TeknikStreaming.csproj | 141 - TeknikStreaming/packages.config | 5 - TeknikTests/Properties/AssemblyInfo.cs | 36 - TeknikTests/TeknikTests.csproj | 91 - TeknikTests/packages.config | 13 - Utilities/{Utilities => }/AccountStatus.cs | 0 Utilities/{Utilities => }/AccountType.cs | 0 Utilities/BufferedFileStreamResult.cs | 44 + Utilities/{Utilities => }/ByteExtensions.cs | 4 - Utilities/ByteHelper.cs | 14 + Utilities/Configuration/Configuration.csproj | 95 - .../Configuration/Properties/AssemblyInfo.cs | 34 - Utilities/Configuration/app.config | 19 - Utilities/Configuration/packages.config | 5 - Utilities/{Utilities => }/Constants.cs | 2 + Utilities/{Utilities => }/Cryptography/AES.cs | 6 +- .../{Utilities => }/Cryptography/Aes128CFB.cs | 2 - .../Cryptography/AesCounterManaged.cs | 2 - .../Cryptography/AesCounterMode.cs | 4 - .../Cryptography/AesCounterStream.cs | 4 - Utilities/{Utilities => }/Cryptography/MD5.cs | 3 - Utilities/{Utilities => }/Cryptography/PGP.cs | 3 - .../{Utilities => }/Cryptography/SHA256.cs | 3 - .../{Utilities => }/Cryptography/SHA384.cs | 6 +- Utilities/{Utilities => }/CurrencyHelper.cs | 3 - Utilities/CurrencyType.cs | 8 + Utilities/{Utilities => }/DateTimeHelper.cs | 4 - .../{Utilities => }/ExceptionExtensions.cs | 0 Utilities/{Utilities => }/FileHelper.cs | 13 +- .../{Utilities => }/HttpRequestExtensions.cs | 5 +- .../{Utilities => }/HttpWebResponseResult.cs | 11 +- Utilities/Logging/LogLevel.cs | 17 - Utilities/Logging/Logger.cs | 162 - Utilities/Logging/Logging.csproj | 81 - Utilities/Logging/Properties/AssemblyInfo.cs | 34 - Utilities/Logging/app.config | 19 - Utilities/Logging/packages.config | 4 - Utilities/MarkdownHelper.cs | 64 + Utilities/{Utilities => }/MysqlDatabase.cs | 0 Utilities/{Utilities => }/ObjectHelper.cs | 3 +- Utilities/Piwik/Piwik.csproj | 115 - Utilities/Piwik/Properties/AssemblyInfo.cs | 34 - Utilities/Piwik/Reporting.cs | 99 - Utilities/Piwik/Tracking.cs | 98 - Utilities/Piwik/app.config | 19 - Utilities/Piwik/packages.config | 10 - Utilities/RequestExtensions.cs | 35 + Utilities/{Utilities => }/RequestHelper.cs | 90 +- Utilities/{Utilities => }/ResponseHelper.cs | 9 +- Utilities/SessionExtensions.cs | 23 + Utilities/{Utilities => }/StringExtensions.cs | 6 +- Utilities/{Utilities => }/StringHelper.cs | 1 - Utilities/SubdomainRoute.cs | 106 + Utilities/SubdomainRouteExtension.cs | 41 + Utilities/{Utilities => }/UrlExtensions.cs | 40 +- Utilities/Utilities.csproj | 19 + Utilities/Utilities/BundleExtensions.cs | 59 - Utilities/Utilities/ByteHelper.cs | 32 - Utilities/Utilities/CurrencyType.cs | 13 - Utilities/Utilities/FileGenerateResult.cs | 49 - Utilities/Utilities/MarkdownHelper.cs | 42 - .../Utilities/Properties/AssemblyInfo.cs | 34 - Utilities/Utilities/RSSFeedResult.cs | 36 - Utilities/Utilities/Utilities.csproj | 184 - Utilities/Utilities/ViewExtensions.cs | 45 - Utilities/Utilities/app.config | 24 - Utilities/Utilities/docs/MySqlCommand.xml | 934 -- .../Utilities/docs/MySqlCommandBuilder.xml | 321 - Utilities/Utilities/docs/MySqlConnection.xml | 1248 -- .../docs/MySqlConnectionStringBuilder.xml | 55 - Utilities/Utilities/docs/MySqlDataAdapter.xml | 801 -- Utilities/Utilities/docs/MySqlDataReader.xml | 452 - Utilities/Utilities/docs/MySqlException.xml | 53 - Utilities/Utilities/docs/MySqlHelper.xml | 53 - Utilities/Utilities/docs/MySqlParameter.xml | 45 - .../docs/MySqlParameterCollection.xml | 45 - Utilities/Utilities/docs/MySqlTransaction.xml | 329 - Utilities/Utilities/packages.config | 17 - Utilities/ViewExtensions.cs | 35 + .../{Utilities => }/WebClientExtension.cs | 3 - UtilitiesTests/Cryptography/Aes128Tests.cs | 37 - UtilitiesTests/Properties/AssemblyInfo.cs | 36 - UtilitiesTests/UtilitiesTests.csproj | 99 - UtilitiesTests/app.config | 19 - UtilitiesTests/packages.config | 13 - 619 files changed, 11016 insertions(+), 101090 deletions(-) rename {Utilities/Configuration => Configuration}/ApiConfig.cs (60%) rename {Utilities/Configuration => Configuration}/BlogConfig.cs (84%) rename {Utilities/Configuration => Configuration}/Config.cs (88%) create mode 100644 Configuration/Configuration.csproj rename {Utilities/Configuration => Configuration}/ContactConfig.cs (66%) rename {Utilities/Configuration => Configuration}/DatabaseConfig.cs (100%) rename {Utilities/Configuration => Configuration}/EmailAccount.cs (80%) rename {Utilities/Configuration => Configuration}/EmailConfig.cs (84%) rename {Utilities/Configuration => Configuration}/GitConfig.cs (79%) rename {Utilities/Configuration => Configuration}/IRCConfig.cs (70%) rename {Utilities/Configuration => Configuration}/LoggingConfig.cs (89%) rename {Utilities/Configuration => Configuration}/PasteConfig.cs (75%) rename {Utilities/Configuration => Configuration}/PiwikConfig.cs (75%) rename {Utilities/Configuration => Configuration}/PodcastConfig.cs (87%) rename {Utilities/Configuration => Configuration}/ShortenerConfig.cs (73%) rename {Utilities/Configuration => Configuration}/StatsConfig.cs (82%) rename {Utilities/Configuration => Configuration}/StreamConfig.cs (71%) rename {Utilities/Configuration => Configuration}/UploadConfig.cs (97%) rename {Utilities/Configuration => Configuration}/UserConfig.cs (90%) rename {Utilities/Configuration => Configuration}/VaultConfig.cs (66%) delete mode 100644 GitVersionConfig.yaml rename {Utilities/Logging => Logging}/LogMessage.cs (86%) create mode 100644 Logging/Logger.cs create mode 100644 Logging/LoggerExtensions.cs create mode 100644 Logging/LoggerProvider.cs create mode 100644 Logging/Logging.csproj create mode 100644 MailService/HMailService.cs create mode 100644 MailService/IMailService.cs create mode 100644 MailService/MailService.cs create mode 100644 MailService/MailService.csproj create mode 100644 MailService/MysqlDatabase.cs create mode 100644 MailService/lib/Teknik.hMailServer.dll create mode 100644 Piwik/Piwik.csproj create mode 100644 Piwik/Reporting.cs create mode 100644 Piwik/Tracking.cs rename {Utilities/Piwik => Piwik}/VisitorData.cs (100%) delete mode 100644 Teknik/App_Start/BundleConfig.cs delete mode 100644 Teknik/App_Start/CustomRazorViewEngine.cs delete mode 100644 Teknik/App_Start/FilterConfig.cs delete mode 100644 Teknik/App_Start/RouteConfig.cs delete mode 100644 Teknik/App_Start/SubdomainRoute.cs delete mode 100644 Teknik/App_Start/SubdomainRouteExtension.cs delete mode 100644 Teknik/Areas/API/APIAreaRegistration.cs delete mode 100644 Teknik/Areas/API/Views/web.config delete mode 100644 Teknik/Areas/About/AboutAreaRegistration.cs delete mode 100644 Teknik/Areas/About/Views/web.config delete mode 100644 Teknik/Areas/Abuse/AbuseAreaRegistration.cs delete mode 100644 Teknik/Areas/Abuse/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Abuse/Views/web.config create mode 100644 Teknik/Areas/Accounts/ResourceOwnerPasswordValidator.cs delete mode 100644 Teknik/Areas/Admin/AdminAreaRegistration.cs delete mode 100644 Teknik/Areas/Admin/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Admin/Views/web.config delete mode 100644 Teknik/Areas/Blog/BlogAreaRegistration.cs create mode 100644 Teknik/Areas/Blog/Models/BlogPostTag.cs delete mode 100644 Teknik/Areas/Blog/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Blog/Views/web.config delete mode 100644 Teknik/Areas/Contact/ContactAreaRegistration.cs delete mode 100644 Teknik/Areas/Contact/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Contact/Views/web.config delete mode 100644 Teknik/Areas/Dev/DevAreaRegistration.cs delete mode 100644 Teknik/Areas/Dev/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Dev/Views/web.config delete mode 100644 Teknik/Areas/Error/ErrorAreaRegistration.cs rename Teknik/Areas/Error/Views/Error/{General.cshtml => HttpGeneral.cshtml} (84%) delete mode 100644 Teknik/Areas/Error/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Error/Views/web.config delete mode 100644 Teknik/Areas/FAQ/FAQAreaRegistration.cs delete mode 100644 Teknik/Areas/FAQ/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/FAQ/Views/web.config delete mode 100644 Teknik/Areas/Help/HelpAreaRegistration.cs delete mode 100644 Teknik/Areas/Help/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Help/Views/web.config delete mode 100644 Teknik/Areas/Home/HomeAreaRegistration.cs delete mode 100644 Teknik/Areas/Home/Scripts/Home.js delete mode 100644 Teknik/Areas/Home/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Home/Views/web.config delete mode 100644 Teknik/Areas/IRC/Content/IRC.css delete mode 100644 Teknik/Areas/IRC/Controllers/IRCController.cs delete mode 100644 Teknik/Areas/IRC/IrcAreaRegistration.cs delete mode 100644 Teknik/Areas/IRC/Scripts/IRC.js delete mode 100644 Teknik/Areas/IRC/ViewModels/ClientViewModel.cs delete mode 100644 Teknik/Areas/IRC/Views/IRC/Client.cshtml delete mode 100644 Teknik/Areas/IRC/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/IRC/Views/web.config delete mode 100644 Teknik/Areas/Paste/PasteAreaRegistration.cs delete mode 100644 Teknik/Areas/Paste/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Paste/Views/web.config create mode 100644 Teknik/Areas/Podcast/Models/PodcastTag.cs delete mode 100644 Teknik/Areas/Podcast/PodcastAreaRegistration.cs delete mode 100644 Teknik/Areas/Podcast/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Podcast/Views/web.config delete mode 100644 Teknik/Areas/Privacy/PrivacyAreaRegistration.cs delete mode 100644 Teknik/Areas/Privacy/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Privacy/Views/web.config delete mode 100644 Teknik/Areas/RSS/RSSAreaRegistration.cs delete mode 100644 Teknik/Areas/Shortener/ShortenerAreaRegistration.cs rename Teknik/Areas/Shortener/{Shortener.cs => ShortenerHelper.cs} (93%) delete mode 100644 Teknik/Areas/Shortener/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Shortener/Views/web.config delete mode 100644 Teknik/Areas/Stats/StatsAreaRegistration.cs delete mode 100644 Teknik/Areas/Stats/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Stats/Views/web.config delete mode 100644 Teknik/Areas/Stream/Controllers/StreamController.cs delete mode 100644 Teknik/Areas/Stream/StreamAreaRegistration.cs delete mode 100644 Teknik/Areas/Stream/ViewModels/StreamViewModel.cs delete mode 100644 Teknik/Areas/Stream/Views/Stream/Index.cshtml delete mode 100644 Teknik/Areas/Stream/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Stream/Views/web.config delete mode 100644 Teknik/Areas/TOS/TOSAreaRegistration.cs delete mode 100644 Teknik/Areas/TOS/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/TOS/Views/web.config delete mode 100644 Teknik/Areas/Upload/Content/Upload.css delete mode 100644 Teknik/Areas/Upload/UploadAreaRegistration.cs delete mode 100644 Teknik/Areas/Upload/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Upload/Views/web.config delete mode 100644 Teknik/Areas/User/Models/Group.cs create mode 100644 Teknik/Areas/User/Models/UserRole.cs delete mode 100644 Teknik/Areas/User/UserAreaRegistration.cs delete mode 100644 Teknik/Areas/User/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/User/Views/web.config delete mode 100644 Teknik/Areas/Vault/VaultAreaRegistration.cs delete mode 100644 Teknik/Areas/Vault/Views/_ViewStart.cshtml delete mode 100644 Teknik/Areas/Vault/Views/web.config delete mode 100644 Teknik/BaseViewPage.cs rename Teknik/{Areas/Blog/Content => Content/Blog}/Blog.css (100%) delete mode 100644 Teknik/Content/Bootstra.386/bootstrap-theme.css delete mode 100644 Teknik/Content/Bootstra.386/bootstrap.css rename Teknik/{Areas/FAQ/Content => Content/FAQ}/FAQ.css (100%) rename Teknik/{Areas/Help/Content => Content/Help}/Help.css (100%) delete mode 100644 Teknik/Content/Highlight/agate.css delete mode 100644 Teknik/Content/Highlight/androidstudio.css delete mode 100644 Teknik/Content/Highlight/arduino-light.css delete mode 100644 Teknik/Content/Highlight/arta.css delete mode 100644 Teknik/Content/Highlight/ascetic.css delete mode 100644 Teknik/Content/Highlight/atelier-cave-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-cave-light.css delete mode 100644 Teknik/Content/Highlight/atelier-dune-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-dune-light.css delete mode 100644 Teknik/Content/Highlight/atelier-estuary-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-estuary-light.css delete mode 100644 Teknik/Content/Highlight/atelier-forest-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-forest-light.css delete mode 100644 Teknik/Content/Highlight/atelier-heath-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-heath-light.css delete mode 100644 Teknik/Content/Highlight/atelier-lakeside-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-lakeside-light.css delete mode 100644 Teknik/Content/Highlight/atelier-plateau-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-plateau-light.css delete mode 100644 Teknik/Content/Highlight/atelier-savanna-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-savanna-light.css delete mode 100644 Teknik/Content/Highlight/atelier-seaside-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-seaside-light.css delete mode 100644 Teknik/Content/Highlight/atelier-sulphurpool-dark.css delete mode 100644 Teknik/Content/Highlight/atelier-sulphurpool-light.css delete mode 100644 Teknik/Content/Highlight/brown-paper.css delete mode 100644 Teknik/Content/Highlight/brown-papersq.png delete mode 100644 Teknik/Content/Highlight/codepen-embed.css delete mode 100644 Teknik/Content/Highlight/color-brewer.css delete mode 100644 Teknik/Content/Highlight/dark.css delete mode 100644 Teknik/Content/Highlight/darkula.css delete mode 100644 Teknik/Content/Highlight/default.css delete mode 100644 Teknik/Content/Highlight/docco.css delete mode 100644 Teknik/Content/Highlight/far.css delete mode 100644 Teknik/Content/Highlight/foundation.css delete mode 100644 Teknik/Content/Highlight/github-gist.css delete mode 100644 Teknik/Content/Highlight/github.css delete mode 100644 Teknik/Content/Highlight/googlecode.css delete mode 100644 Teknik/Content/Highlight/grayscale.css delete mode 100644 Teknik/Content/Highlight/hopscotch.css delete mode 100644 Teknik/Content/Highlight/hybrid.css delete mode 100644 Teknik/Content/Highlight/idea.css delete mode 100644 Teknik/Content/Highlight/ir-black.css delete mode 100644 Teknik/Content/Highlight/kimbie.dark.css delete mode 100644 Teknik/Content/Highlight/kimbie.light.css delete mode 100644 Teknik/Content/Highlight/magula.css delete mode 100644 Teknik/Content/Highlight/mono-blue.css delete mode 100644 Teknik/Content/Highlight/monokai-sublime.css delete mode 100644 Teknik/Content/Highlight/monokai.css delete mode 100644 Teknik/Content/Highlight/obsidian.css delete mode 100644 Teknik/Content/Highlight/paraiso-dark.css delete mode 100644 Teknik/Content/Highlight/paraiso-light.css delete mode 100644 Teknik/Content/Highlight/pojoaque.css delete mode 100644 Teknik/Content/Highlight/pojoaque.jpg delete mode 100644 Teknik/Content/Highlight/railscasts.css delete mode 100644 Teknik/Content/Highlight/rainbow.css delete mode 100644 Teknik/Content/Highlight/school-book.css delete mode 100644 Teknik/Content/Highlight/school-book.png delete mode 100644 Teknik/Content/Highlight/solarized-dark.css delete mode 100644 Teknik/Content/Highlight/solarized-light.css delete mode 100644 Teknik/Content/Highlight/sunburst.css delete mode 100644 Teknik/Content/Highlight/tomorrow-night-blue.css delete mode 100644 Teknik/Content/Highlight/tomorrow-night-bright.css delete mode 100644 Teknik/Content/Highlight/tomorrow-night-eighties.css delete mode 100644 Teknik/Content/Highlight/tomorrow-night.css delete mode 100644 Teknik/Content/Highlight/tomorrow.css delete mode 100644 Teknik/Content/Highlight/vs.css delete mode 100644 Teknik/Content/Highlight/xcode.css delete mode 100644 Teknik/Content/Highlight/zenburn.css rename Teknik/{Areas/Home/Content => Content/Home}/Home.css (100%) rename Teknik/{Areas/Paste/Content => Content/Paste}/Paste.css (100%) rename Teknik/{Areas/Podcast/Content => Content/Podcast}/Podcast.css (100%) rename Teknik/{Areas/Vault/Content => Content/Vault}/Vault.css (100%) delete mode 100644 Teknik/Content/audioplayer.css delete mode 100644 Teknik/Content/bootstrap-flat-extras.css delete mode 100644 Teknik/Content/bootstrap-flat-extras.min.css delete mode 100644 Teknik/Content/bootstrap-flat.css delete mode 100644 Teknik/Content/bootstrap-flat.min.css delete mode 100644 Teknik/Content/bootstrap-markdown.min.css delete mode 100644 Teknik/Content/bootstrap-switch/bootstrap2/bootstrap-switch.css delete mode 100644 Teknik/Content/bootstrap-switch/bootstrap2/bootstrap-switch.min.css delete mode 100644 Teknik/Content/bootstrap-switch/bootstrap3/bootstrap-switch.css delete mode 100644 Teknik/Content/bootstrap-switch/bootstrap3/bootstrap-switch.min.css delete mode 100644 Teknik/Content/bootstrap-theme.css delete mode 100644 Teknik/Content/bootstrap-theme.css.map delete mode 100644 Teknik/Content/bootstrap-theme.min.css delete mode 100644 Teknik/Content/bootstrap-theme.min.css.map delete mode 100644 Teknik/Content/bootstrap.css delete mode 100644 Teknik/Content/bootstrap.css.map delete mode 100644 Teknik/Content/bootstrap.min.css delete mode 100644 Teknik/Content/bootstrap.min.css.map delete mode 100644 Teknik/Content/dropzone.css delete mode 100644 Teknik/Content/font-awesome.css delete mode 100644 Teknik/Content/font-awesome.min.css delete mode 100644 Teknik/Content/jquery.tocify.css delete mode 100644 Teknik/Content/mdd_styles.css rename Utilities/Utilities/EntityExtensions.cs => Teknik/Data/DbInitializer.cs (57%) create mode 100644 Teknik/Data/TeknikEntities.cs delete mode 100644 Teknik/Global.asax delete mode 100644 Teknik/Global.asax.cs delete mode 100644 Teknik/Hubs/IRCClientHub.cs delete mode 100644 Teknik/Hubs/ServerUsageHub.cs create mode 100644 Teknik/Middleware/BlacklistMiddleware.cs create mode 100644 Teknik/Middleware/CORSMiddleware.cs create mode 100644 Teknik/Middleware/CSPMiddleware.cs create mode 100644 Teknik/Middleware/ErrorHandlerMiddleware.cs create mode 100644 Teknik/Middleware/PerformanceMonitorMiddleware.cs create mode 100644 Teknik/Middleware/SetupHttpContextMiddleware.cs delete mode 100644 Teknik/Migrations/Configuration.cs delete mode 100644 Teknik/Migrations/InitializeTransfers.sql delete mode 100644 Teknik/Migrations/SetCaseSensitivity.sql delete mode 100644 Teknik/Models/TeknikEntities.cs delete mode 100644 Teknik/Modules/BlacklistModule.cs delete mode 100644 Teknik/Modules/CORSModule.cs delete mode 100644 Teknik/Modules/CSPModule.cs delete mode 100644 Teknik/Modules/CompressionModule.cs delete mode 100644 Teknik/Modules/PerformanceMonitorModule.cs delete mode 100644 Teknik/Modules/UserAuthModule.cs create mode 100644 Teknik/Program.cs delete mode 100644 Teknik/Properties/AssemblyInfo.cs create mode 100644 Teknik/Properties/PublishProfiles/FolderProfile.pubxml rename Teknik/Properties/PublishProfiles/{Teknik Dev.pubxml => Teknik Development (AMS1).pubxml} (73%) delete mode 100644 Teknik/Properties/PublishProfiles/Teknik Production.pubxml create mode 100644 Teknik/Properties/launchSettings.json create mode 100644 Teknik/Routes.cs rename Teknik/{Areas/Admin/Scripts => Scripts/Admin}/UploadSearch.js (100%) rename Teknik/{Areas/Admin/Scripts => Scripts/Admin}/UserInfo.js (100%) rename Teknik/{Areas/Admin/Scripts => Scripts/Admin}/UserSearch.js (100%) rename Teknik/{Areas/Blog/Scripts => Scripts/Blog}/Blog.js (94%) delete mode 100644 Teknik/Scripts/Bootstra.386/bootstrap.js rename Teknik/{Areas/Contact/Scripts => Scripts/Contact}/Contact.js (100%) delete mode 100644 Teknik/Scripts/Crypto-js/aes.js delete mode 100644 Teknik/Scripts/Crypto-js/enc-base64.js delete mode 100644 Teknik/Scripts/Crypto-js/enc-utf16.js delete mode 100644 Teknik/Scripts/Crypto-js/lib-typedarrays.js delete mode 100644 Teknik/Scripts/Crypto-js/mode-ctr.js delete mode 100644 Teknik/Scripts/Crypto-js/pad-nopadding.js delete mode 100644 Teknik/Scripts/Dropzone/dropzone.js rename Teknik/{Areas/Error/Scripts => Scripts/Error}/Error.js (98%) delete mode 100644 Teknik/Scripts/FileSaver.js delete mode 100644 Teknik/Scripts/FileSize/filesize.min.js delete mode 100644 Teknik/Scripts/Highcharts/highcharts-3d.js delete mode 100644 Teknik/Scripts/Highcharts/highcharts-more.js delete mode 100644 Teknik/Scripts/Highcharts/highcharts.js delete mode 100644 Teknik/Scripts/Highlight/highlight.pack.js delete mode 100644 Teknik/Scripts/MarkdownDeep License.txt delete mode 100644 Teknik/Scripts/MarkdownDeep Quick Reference.txt delete mode 100644 Teknik/Scripts/MarkdownDeepLib.min.js delete mode 100644 Teknik/Scripts/PageDown/Markdown.Converter.js delete mode 100644 Teknik/Scripts/PageDown/Markdown.Editor.js delete mode 100644 Teknik/Scripts/PageDown/Markdown.Sanitizer.js rename Teknik/{Areas/Paste/Scripts => Scripts/Paste}/Paste.js (100%) create mode 100644 Teknik/Scripts/Paste/Simple.js rename Teknik/{Areas/Podcast/Scripts => Scripts/Podcast}/Podcast.js (100%) rename Teknik/{Areas/Shortener/Scripts => Scripts/Shortener}/Shortener.js (100%) rename Teknik/{Areas/Stats/Scripts => Scripts/Stats}/Stats.js (88%) rename Teknik/{Areas/Upload/Scripts => Scripts/Upload}/Download.js (97%) rename Teknik/{Areas/Upload/Scripts => Scripts/Upload}/EncryptionWorker.js (100%) rename Teknik/{Areas/Upload/Scripts => Scripts/Upload}/Upload.js (99%) rename Teknik/{Areas/User/Scripts => Scripts/User}/BlogSettings.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/CheckAuthCode.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/InviteSettings.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/Profile.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/ProfileSettings.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/ResetPass.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/SecuritySettings.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/Settings.js (100%) rename Teknik/{Areas/User/Scripts => Scripts/User}/UploadSettings.js (100%) rename Teknik/{Areas/Vault/Scripts => Scripts/Vault}/Vault.js (97%) delete mode 100644 Teknik/Scripts/_references.js delete mode 100644 Teknik/Scripts/audioplayer.min.js delete mode 100644 Teknik/Scripts/bootbox/bootbox.min.js delete mode 100644 Teknik/Scripts/bootstrap-markdown.js delete mode 100644 Teknik/Scripts/bootstrap-select.js delete mode 100644 Teknik/Scripts/bootstrap-switch.js delete mode 100644 Teknik/Scripts/bootstrap-switch.min.js delete mode 100644 Teknik/Scripts/bootstrap.js delete mode 100644 Teknik/Scripts/bootstrap.min.js delete mode 100644 Teknik/Scripts/jquery-2.1.4.intellisense.js delete mode 100644 Teknik/Scripts/jquery-3.1.1.intellisense.js delete mode 100644 Teknik/Scripts/jquery-3.3.1.intellisense.js delete mode 100644 Teknik/Scripts/jquery-3.3.1.js delete mode 100644 Teknik/Scripts/jquery-3.3.1.min.js delete mode 100644 Teknik/Scripts/jquery-3.3.1.min.map delete mode 100644 Teknik/Scripts/jquery-3.3.1.slim.js delete mode 100644 Teknik/Scripts/jquery-3.3.1.slim.min.js delete mode 100644 Teknik/Scripts/jquery-3.3.1.slim.min.map delete mode 100644 Teknik/Scripts/jquery-ui.widgets.js delete mode 100644 Teknik/Scripts/jquery.blockUI.js delete mode 100644 Teknik/Scripts/jquery.fileupload.js delete mode 100644 Teknik/Scripts/jquery.iframe-transport.js delete mode 100644 Teknik/Scripts/jquery.signalR-2.2.2.js delete mode 100644 Teknik/Scripts/jquery.signalR-2.2.2.min.js delete mode 100644 Teknik/Scripts/jquery.tocify.min.js delete mode 100644 Teknik/Scripts/jquery.validate-vsdoc.js delete mode 100644 Teknik/Scripts/jquery.validate.js delete mode 100644 Teknik/Scripts/jquery.validate.min.js delete mode 100644 Teknik/Scripts/jquery.validate.unobtrusive.js delete mode 100644 Teknik/Scripts/jquery.validate.unobtrusive.min.js rename Teknik/Scripts/{ => lib}/Blob.js (100%) delete mode 100644 Teknik/Scripts/modernizr-2.8.3.js delete mode 100644 Teknik/Scripts/ocupload/1.1.2/ocupload.js delete mode 100644 Teknik/Scripts/respond.js delete mode 100644 Teknik/Scripts/respond.matchmedia.addListener.js delete mode 100644 Teknik/Scripts/respond.matchmedia.addListener.min.js delete mode 100644 Teknik/Scripts/respond.min.js create mode 100644 Teknik/Security/TeknikCookieAuthenticationEvents.cs delete mode 100644 Teknik/SignalR/IRCClient.cs delete mode 100644 Teknik/SignalR/ServerUsageTicker.cs create mode 100644 Teknik/TagHelpers/BundleTagHelper.cs create mode 100644 Teknik/TagHelpers/NonceTagHelper.cs delete mode 100644 Teknik/TransformWebConfig/original/Web.config delete mode 100644 Teknik/Views/Web.config delete mode 100644 Teknik/Views/_ViewStart.cshtml delete mode 100644 Teknik/Web.Debug.config delete mode 100644 Teknik/Web.Release.config delete mode 100644 Teknik/Web.config create mode 100644 Teknik/_ViewImports.cshtml rename Teknik/{Areas/About/Views => }/_ViewStart.cshtml (100%) create mode 100644 Teknik/appsettings.json create mode 100644 Teknik/bundleconfig.json create mode 100644 Teknik/bundleconfig.json.bindings delete mode 100644 Teknik/docs/MySqlCommand.xml delete mode 100644 Teknik/docs/MySqlCommandBuilder.xml delete mode 100644 Teknik/docs/MySqlConnection.xml delete mode 100644 Teknik/docs/MySqlConnectionStringBuilder.xml delete mode 100644 Teknik/docs/MySqlDataAdapter.xml delete mode 100644 Teknik/docs/MySqlDataReader.xml delete mode 100644 Teknik/docs/MySqlException.xml delete mode 100644 Teknik/docs/MySqlHelper.xml delete mode 100644 Teknik/docs/MySqlParameter.xml delete mode 100644 Teknik/docs/MySqlParameterCollection.xml delete mode 100644 Teknik/docs/MySqlTransaction.xml delete mode 100644 Teknik/fonts/FontAwesome.otf delete mode 100644 Teknik/fonts/fontawesome-webfont.eot delete mode 100644 Teknik/fonts/fontawesome-webfont.svg delete mode 100644 Teknik/fonts/fontawesome-webfont.ttf delete mode 100644 Teknik/fonts/fontawesome-webfont.woff delete mode 100644 Teknik/fonts/fontawesome-webfont.woff2 delete mode 100644 Teknik/fonts/glyphicons-halflings-regular.eot delete mode 100644 Teknik/fonts/glyphicons-halflings-regular.svg delete mode 100644 Teknik/fonts/glyphicons-halflings-regular.ttf delete mode 100644 Teknik/fonts/glyphicons-halflings-regular.woff delete mode 100644 Teknik/fonts/glyphicons-halflings-regular.woff2 create mode 100644 Teknik/gulpfile.js create mode 100644 Teknik/package-lock.json create mode 100644 Teknik/package.json delete mode 100644 Teknik/packages.config delete mode 100644 TeknikStreaming/App.config delete mode 100644 TeknikStreaming/Program.cs delete mode 100644 TeknikStreaming/Properties/AssemblyInfo.cs delete mode 100644 TeknikStreaming/Server.Designer.cs delete mode 100644 TeknikStreaming/Server.cs delete mode 100644 TeknikStreaming/TeknikStreaming.csproj delete mode 100644 TeknikStreaming/packages.config delete mode 100644 TeknikTests/Properties/AssemblyInfo.cs delete mode 100644 TeknikTests/TeknikTests.csproj delete mode 100644 TeknikTests/packages.config rename Utilities/{Utilities => }/AccountStatus.cs (100%) rename Utilities/{Utilities => }/AccountType.cs (100%) create mode 100644 Utilities/BufferedFileStreamResult.cs rename Utilities/{Utilities => }/ByteExtensions.cs (78%) create mode 100644 Utilities/ByteHelper.cs delete mode 100644 Utilities/Configuration/Configuration.csproj delete mode 100644 Utilities/Configuration/Properties/AssemblyInfo.cs delete mode 100644 Utilities/Configuration/app.config delete mode 100644 Utilities/Configuration/packages.config rename Utilities/{Utilities => }/Constants.cs (92%) rename Utilities/{Utilities => }/Cryptography/AES.cs (95%) rename Utilities/{Utilities => }/Cryptography/Aes128CFB.cs (98%) rename Utilities/{Utilities => }/Cryptography/AesCounterManaged.cs (98%) rename Utilities/{Utilities => }/Cryptography/AesCounterMode.cs (98%) rename Utilities/{Utilities => }/Cryptography/AesCounterStream.cs (98%) rename Utilities/{Utilities => }/Cryptography/MD5.cs (96%) rename Utilities/{Utilities => }/Cryptography/PGP.cs (97%) rename Utilities/{Utilities => }/Cryptography/SHA256.cs (96%) rename Utilities/{Utilities => }/Cryptography/SHA384.cs (78%) rename Utilities/{Utilities => }/CurrencyHelper.cs (97%) create mode 100644 Utilities/CurrencyType.cs rename Utilities/{Utilities => }/DateTimeHelper.cs (78%) rename Utilities/{Utilities => }/ExceptionExtensions.cs (100%) rename Utilities/{Utilities => }/FileHelper.cs (93%) rename Utilities/{Utilities => }/HttpRequestExtensions.cs (70%) rename Utilities/{Utilities => }/HttpWebResponseResult.cs (90%) delete mode 100644 Utilities/Logging/LogLevel.cs delete mode 100644 Utilities/Logging/Logger.cs delete mode 100644 Utilities/Logging/Logging.csproj delete mode 100644 Utilities/Logging/Properties/AssemblyInfo.cs delete mode 100644 Utilities/Logging/app.config delete mode 100644 Utilities/Logging/packages.config create mode 100644 Utilities/MarkdownHelper.cs rename Utilities/{Utilities => }/MysqlDatabase.cs (100%) rename Utilities/{Utilities => }/ObjectHelper.cs (94%) delete mode 100644 Utilities/Piwik/Piwik.csproj delete mode 100644 Utilities/Piwik/Properties/AssemblyInfo.cs delete mode 100644 Utilities/Piwik/Reporting.cs delete mode 100644 Utilities/Piwik/Tracking.cs delete mode 100644 Utilities/Piwik/app.config delete mode 100644 Utilities/Piwik/packages.config create mode 100644 Utilities/RequestExtensions.cs rename Utilities/{Utilities => }/RequestHelper.cs (58%) rename Utilities/{Utilities => }/ResponseHelper.cs (79%) create mode 100644 Utilities/SessionExtensions.cs rename Utilities/{Utilities => }/StringExtensions.cs (90%) rename Utilities/{Utilities => }/StringHelper.cs (99%) create mode 100644 Utilities/SubdomainRoute.cs create mode 100644 Utilities/SubdomainRouteExtension.cs rename Utilities/{Utilities => }/UrlExtensions.cs (76%) create mode 100644 Utilities/Utilities.csproj delete mode 100644 Utilities/Utilities/BundleExtensions.cs delete mode 100644 Utilities/Utilities/ByteHelper.cs delete mode 100644 Utilities/Utilities/CurrencyType.cs delete mode 100644 Utilities/Utilities/FileGenerateResult.cs delete mode 100644 Utilities/Utilities/MarkdownHelper.cs delete mode 100644 Utilities/Utilities/Properties/AssemblyInfo.cs delete mode 100644 Utilities/Utilities/RSSFeedResult.cs delete mode 100644 Utilities/Utilities/Utilities.csproj delete mode 100644 Utilities/Utilities/ViewExtensions.cs delete mode 100644 Utilities/Utilities/app.config delete mode 100644 Utilities/Utilities/docs/MySqlCommand.xml delete mode 100644 Utilities/Utilities/docs/MySqlCommandBuilder.xml delete mode 100644 Utilities/Utilities/docs/MySqlConnection.xml delete mode 100644 Utilities/Utilities/docs/MySqlConnectionStringBuilder.xml delete mode 100644 Utilities/Utilities/docs/MySqlDataAdapter.xml delete mode 100644 Utilities/Utilities/docs/MySqlDataReader.xml delete mode 100644 Utilities/Utilities/docs/MySqlException.xml delete mode 100644 Utilities/Utilities/docs/MySqlHelper.xml delete mode 100644 Utilities/Utilities/docs/MySqlParameter.xml delete mode 100644 Utilities/Utilities/docs/MySqlParameterCollection.xml delete mode 100644 Utilities/Utilities/docs/MySqlTransaction.xml delete mode 100644 Utilities/Utilities/packages.config create mode 100644 Utilities/ViewExtensions.cs rename Utilities/{Utilities => }/WebClientExtension.cs (90%) delete mode 100644 UtilitiesTests/Cryptography/Aes128Tests.cs delete mode 100644 UtilitiesTests/Properties/AssemblyInfo.cs delete mode 100644 UtilitiesTests/UtilitiesTests.csproj delete mode 100644 UtilitiesTests/app.config delete mode 100644 UtilitiesTests/packages.config diff --git a/.editorconfig b/.editorconfig index 3fc4e8a..407b45b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,7 @@ -root = true - [*] -charset = utf-8 -indent_style = space indent_size = 4 +indent_style = space end_of_line = crlf -trim_trailing_whitespace = false -insert_final_newline = true \ No newline at end of file + +[*.cshtml] +indent_size = 4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index eaaade2..70e5420 100644 --- a/.gitignore +++ b/.gitignore @@ -4,26 +4,34 @@ # User-specific files *.suo *.user +*.userosscache *.sln.docstates +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ +[Rr]eleases/ x64/ -build/ +x86/ bld/ [Bb]in/ [Oo]bj/ +[Ll]og/ -# Roslyn cache directories -*.ide/ +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -#NUNIT +# NUNIT *.VisualState.xml TestResult.xml @@ -32,6 +40,11 @@ TestResult.xml [Rr]eleasePS/ dlldata.c +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + *_i.c *_p.c *_i.h @@ -64,14 +77,18 @@ _Chutzpah* ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile +*.VC.db +*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx +*.sap # TFS 2012 Local Workspace $tf/ @@ -84,7 +101,7 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding addin-in +# JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in @@ -96,6 +113,7 @@ _TeamCity* # NCrunch _NCrunch_* .*crunch*.local.xml +nCrunchTemp_* # MightyMoose *.mm.* @@ -123,43 +141,63 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -## TODO: Comment the next line if you want to checkin your -## web deploy settings but do note that will include unencrypted -## passwords +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted #*.pubxml +*.publishproj -# NuGet Packages Directory -packages/* -## TODO: If the tool you use requires repositories.config -## uncomment the next line -#!packages/repositories.config +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ -# Enable "build/" folder in the NuGet Packages folder since -# NuGet packages use it for MSBuild targets. -# This line needs to be after the ignore of the build folder -# (and the packages folder if the line above has been uncommented) -!packages/build/ +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets -# Windows Azure Build Output +# Microsoft Azure Build Output csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview +*.jfm *.pfx *.publishsettings node_modules/ -bower_components/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ # RIA/Silverlight projects Generated_Code/ @@ -184,26 +222,45 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ -# LightSwitch generated files -GeneratedArtifacts/ -_Pvt_Extensions/ -ModelManifest.xml -/Teknik/ConnectionStrings.config -/Teknik/App_Data/Config.json -/.vs/config/applicationhost.config -/Teknik/TransformWebConfig/assist/Web.config -/Teknik/Properties/PublishProfiles/IIS.pubxml -/Teknik/App_Data/ConnectionStrings.config -/Teknik/App_Data/Config.json.old +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc /Teknik/App_Data/MachineKey.config -/.vs/Teknik/v15/sqlite3/storage.ide -/.vs/Teknik/v15/sqlite3/storage.ide-wal -/.vs/Teknik/v15/sqlite3/storage.ide-shm -/.vs/Teknik/v15/sqlite3/db.lock -/.vs/Teknik/v15/sqlite3 -/.vs/Teknik/v15/Server/sqlite3/storage.ide-wal -/.vs/Teknik/v15/Server/sqlite3/storage.ide-shm -/.vs/Teknik/v15/Server/sqlite3/storage.ide -/.vs/Teknik/v15/Server/sqlite3/db.lock -/.vs/Teknik/v15/Server/sqlite3 -/.vs/Teknik/v15 +/Teknik/App_Data/ConnectionStrings.config +/Teknik/App_Data/Config.json +/Teknik/appsettings.Production.json +/Teknik/appsettings.Development.json diff --git a/Utilities/Configuration/ApiConfig.cs b/Configuration/ApiConfig.cs similarity index 60% rename from Utilities/Configuration/ApiConfig.cs rename to Configuration/ApiConfig.cs index 91cdd57..8d9b9b8 100644 --- a/Utilities/Configuration/ApiConfig.cs +++ b/Configuration/ApiConfig.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class ApiConfig { diff --git a/Utilities/Configuration/BlogConfig.cs b/Configuration/BlogConfig.cs similarity index 84% rename from Utilities/Configuration/BlogConfig.cs rename to Configuration/BlogConfig.cs index d4c2435..f3dd869 100644 --- a/Utilities/Configuration/BlogConfig.cs +++ b/Configuration/BlogConfig.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class BlogConfig { diff --git a/Utilities/Configuration/Config.cs b/Configuration/Config.cs similarity index 88% rename from Utilities/Configuration/Config.cs rename to Configuration/Config.cs index 47b47ec..a089a9c 100644 --- a/Utilities/Configuration/Config.cs +++ b/Configuration/Config.cs @@ -1,10 +1,7 @@ +using Newtonsoft.Json; using System; using System.IO; using System.Threading; -using System.Web; -using System.Web.Caching; -using Newtonsoft.Json; -using Teknik.Utilities; using Teknik.Utilities.Cryptography; namespace Teknik.Configuration @@ -12,9 +9,13 @@ namespace Teknik.Configuration public class Config { private const string _ConfigCacheKey = "ConfigCache"; + private const string _ConfigFileName = "Config.json"; private static Config _Config { get; set; } - + private static string _FileHash { get; set; } + + private ReaderWriterLockSlim _ConfigRWLock; + private ReaderWriterLockSlim _ConfigFileRWLock; private JsonSerializerSettings _JsonSettings; private bool _DevEnvironment; @@ -117,6 +118,8 @@ namespace Teknik.Configuration public Config() { + _ConfigRWLock = new ReaderWriterLockSlim(); + _ConfigFileRWLock = new ReaderWriterLockSlim(); _JsonSettings = new JsonSerializerSettings(); _JsonSettings.Formatting = Formatting.Indented; @@ -168,39 +171,26 @@ namespace Teknik.Configuration return JsonConvert.SerializeObject(config, Formatting.Indented); } - public static Config Load() - { - HttpContext context = HttpContext.Current; - if (context != null) - _Config = (Config)context.Cache[_ConfigCacheKey]; - if (_Config == null) - { - string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - _Config = Load(path); - context?.Cache.Insert(_ConfigCacheKey, _Config, new CacheDependency(path)); - } - return _Config; - } - public static Config Load(string path) { - Config config = new Config(); - if (!File.Exists(Path.Combine(path, "Config.json"))) - { - Save(Path.Combine(path, "Config.json"), config); - } - else - { - string configContents = File.ReadAllText(Path.Combine(path, "Config.json")); - config = Deserialize(configContents); - } - return config; - } + string newHash = string.Empty; + string fullPath = Path.Combine(path, _ConfigFileName); - public static void Save(Config config) - { - string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString(); - Save(Path.Combine(path, "Config.json"), config); + if (!File.Exists(fullPath)) + { + Config config = new Config(); + Save(fullPath, config); + } + + newHash = MD5.FileHash(fullPath); + + if (_Config == null || _FileHash == null || newHash != _FileHash) + { + string configContents = File.ReadAllText(fullPath); + _Config = Deserialize(configContents); + _FileHash = newHash; + } + return _Config; } public static void Save(string path, Config config) diff --git a/Configuration/Configuration.csproj b/Configuration/Configuration.csproj new file mode 100644 index 0000000..f57773a --- /dev/null +++ b/Configuration/Configuration.csproj @@ -0,0 +1,17 @@ + + + + netcoreapp2.1 + Teknik.Configuration + Teknik.Configuration + + + + + + + + + + + diff --git a/Utilities/Configuration/ContactConfig.cs b/Configuration/ContactConfig.cs similarity index 66% rename from Utilities/Configuration/ContactConfig.cs rename to Configuration/ContactConfig.cs index 05a28b9..66511cf 100644 --- a/Utilities/Configuration/ContactConfig.cs +++ b/Configuration/ContactConfig.cs @@ -1,11 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Net.Mail; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class ContactConfig { diff --git a/Utilities/Configuration/DatabaseConfig.cs b/Configuration/DatabaseConfig.cs similarity index 100% rename from Utilities/Configuration/DatabaseConfig.cs rename to Configuration/DatabaseConfig.cs diff --git a/Utilities/Configuration/EmailAccount.cs b/Configuration/EmailAccount.cs similarity index 80% rename from Utilities/Configuration/EmailAccount.cs rename to Configuration/EmailAccount.cs index 37cf81a..c5a493b 100644 --- a/Utilities/Configuration/EmailAccount.cs +++ b/Configuration/EmailAccount.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class EmailAccount { diff --git a/Utilities/Configuration/EmailConfig.cs b/Configuration/EmailConfig.cs similarity index 84% rename from Utilities/Configuration/EmailConfig.cs rename to Configuration/EmailConfig.cs index f698e5d..b2d2c93 100644 --- a/Utilities/Configuration/EmailConfig.cs +++ b/Configuration/EmailConfig.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class EmailConfig { diff --git a/Utilities/Configuration/GitConfig.cs b/Configuration/GitConfig.cs similarity index 79% rename from Utilities/Configuration/GitConfig.cs rename to Configuration/GitConfig.cs index a1f9448..596e35c 100644 --- a/Utilities/Configuration/GitConfig.cs +++ b/Configuration/GitConfig.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class GitConfig { diff --git a/Utilities/Configuration/IRCConfig.cs b/Configuration/IRCConfig.cs similarity index 70% rename from Utilities/Configuration/IRCConfig.cs rename to Configuration/IRCConfig.cs index 4249ffc..1df9efc 100644 --- a/Utilities/Configuration/IRCConfig.cs +++ b/Configuration/IRCConfig.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class IRCConfig { diff --git a/Utilities/Configuration/LoggingConfig.cs b/Configuration/LoggingConfig.cs similarity index 89% rename from Utilities/Configuration/LoggingConfig.cs rename to Configuration/LoggingConfig.cs index 0fd5563..a36cae3 100644 --- a/Utilities/Configuration/LoggingConfig.cs +++ b/Configuration/LoggingConfig.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class LoggingConfig { diff --git a/Utilities/Configuration/PasteConfig.cs b/Configuration/PasteConfig.cs similarity index 75% rename from Utilities/Configuration/PasteConfig.cs rename to Configuration/PasteConfig.cs index 4afc356..d6f04a8 100644 --- a/Utilities/Configuration/PasteConfig.cs +++ b/Configuration/PasteConfig.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class PasteConfig { diff --git a/Utilities/Configuration/PiwikConfig.cs b/Configuration/PiwikConfig.cs similarity index 75% rename from Utilities/Configuration/PiwikConfig.cs rename to Configuration/PiwikConfig.cs index 0a78db7..d93b4fc 100644 --- a/Utilities/Configuration/PiwikConfig.cs +++ b/Configuration/PiwikConfig.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class PiwikConfig { diff --git a/Utilities/Configuration/PodcastConfig.cs b/Configuration/PodcastConfig.cs similarity index 87% rename from Utilities/Configuration/PodcastConfig.cs rename to Configuration/PodcastConfig.cs index cf3cc4a..ad17967 100644 --- a/Utilities/Configuration/PodcastConfig.cs +++ b/Configuration/PodcastConfig.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Web; +using System.IO; namespace Teknik.Configuration { diff --git a/Utilities/Configuration/ShortenerConfig.cs b/Configuration/ShortenerConfig.cs similarity index 73% rename from Utilities/Configuration/ShortenerConfig.cs rename to Configuration/ShortenerConfig.cs index dd3a4a6..8f7326c 100644 --- a/Utilities/Configuration/ShortenerConfig.cs +++ b/Configuration/ShortenerConfig.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class ShortenerConfig { diff --git a/Utilities/Configuration/StatsConfig.cs b/Configuration/StatsConfig.cs similarity index 82% rename from Utilities/Configuration/StatsConfig.cs rename to Configuration/StatsConfig.cs index f745d43..d9297d3 100644 --- a/Utilities/Configuration/StatsConfig.cs +++ b/Configuration/StatsConfig.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class StatsConfig { diff --git a/Utilities/Configuration/StreamConfig.cs b/Configuration/StreamConfig.cs similarity index 71% rename from Utilities/Configuration/StreamConfig.cs rename to Configuration/StreamConfig.cs index 67d6e5a..2090111 100644 --- a/Utilities/Configuration/StreamConfig.cs +++ b/Configuration/StreamConfig.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace Teknik.Configuration { diff --git a/Utilities/Configuration/UploadConfig.cs b/Configuration/UploadConfig.cs similarity index 97% rename from Utilities/Configuration/UploadConfig.cs rename to Configuration/UploadConfig.cs index f60514a..9a9bb54 100644 --- a/Utilities/Configuration/UploadConfig.cs +++ b/Configuration/UploadConfig.cs @@ -1,8 +1,5 @@ -using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Web; namespace Teknik.Configuration { diff --git a/Utilities/Configuration/UserConfig.cs b/Configuration/UserConfig.cs similarity index 90% rename from Utilities/Configuration/UserConfig.cs rename to Configuration/UserConfig.cs index 60a602f..8de0d1c 100644 --- a/Utilities/Configuration/UserConfig.cs +++ b/Configuration/UserConfig.cs @@ -1,10 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Teknik.Utilities; - namespace Teknik.Configuration { public class UserConfig diff --git a/Utilities/Configuration/VaultConfig.cs b/Configuration/VaultConfig.cs similarity index 66% rename from Utilities/Configuration/VaultConfig.cs rename to Configuration/VaultConfig.cs index 7680af9..d75e996 100644 --- a/Utilities/Configuration/VaultConfig.cs +++ b/Configuration/VaultConfig.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; - -namespace Teknik.Configuration +namespace Teknik.Configuration { public class VaultConfig { diff --git a/GitVersionConfig.yaml b/GitVersionConfig.yaml deleted file mode 100644 index aa7fc6f..0000000 --- a/GitVersionConfig.yaml +++ /dev/null @@ -1,2 +0,0 @@ -assembly-versioning-scheme: MajorMinorPatch -next-version: 2.0.6 diff --git a/Utilities/Logging/LogMessage.cs b/Logging/LogMessage.cs similarity index 86% rename from Utilities/Logging/LogMessage.cs rename to Logging/LogMessage.cs index 59fee83..6a55d6a 100644 --- a/Utilities/Logging/LogMessage.cs +++ b/Logging/LogMessage.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System; using Teknik.Utilities; namespace Teknik.Logging @@ -21,7 +18,7 @@ namespace Teknik.Logging public void SetDefaults() { - Level = LogLevel.Info; + Level = LogLevel.Information; EntryDate = DateTime.Now; Message = string.Empty; Exception = null; diff --git a/Logging/Logger.cs b/Logging/Logger.cs new file mode 100644 index 0000000..169baca --- /dev/null +++ b/Logging/Logger.cs @@ -0,0 +1,161 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Mail; +using System.Text; +using Teknik.Configuration; +using Teknik.Utilities; + +namespace Teknik.Logging +{ + public class Logger : ILogger + { + private static readonly object Locker = new object(); + + private readonly string _name; + private readonly Config _config; + + public Logger(string name, Config config) + { + _name = name; + _config = config; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + return; + + // write an entry to the logs + LogMessage log = new LogMessage(); + log.Level = logLevel; + log.Message = formatter(state, exception); + log.Exception = exception; + + WriteLogMessage(log); + } + + public bool IsEnabled(LogLevel logLevel) + { + if (_config.LoggingConfig.Enabled) + { + // Do we want to write a log for this level? (Default to Error) + LogLevel minLogLevel = LogLevel.Error; + Enum.TryParse(_config.LoggingConfig.LogLevel, out minLogLevel); + if (logLevel >= minLogLevel) + return true; + } + return false; + } + + public IDisposable BeginScope(TState state) + { + return null; + } + + private void WriteLogMessage(LogMessage log) + { + + try + { + // Lock the file processing so only 1 thread is working on the log file at a time + lock (Locker) + { + if (!Directory.Exists(_config.LoggingConfig.OutputDirectory)) + { + Directory.CreateDirectory(_config.LoggingConfig.OutputDirectory); + } + // Get current log file + string fileName = Constants.LOG_FILE_NAME_PREFIX + Constants.LOG_FILE_EXT; + string logFile = Path.Combine(_config.LoggingConfig.OutputDirectory, fileName); + + if (File.Exists(logFile)) + { + // File already exists, so lets see if we need to rotate it + if (_config.LoggingConfig.RotateLogs) + { + FileInfo info = new FileInfo(logFile); + if (_config.LoggingConfig.MaxSize < info.Length && _config.LoggingConfig.MaxSize > 0) + { + // File is too large, so let's create a new name for it based on todays date + string newFileName = Constants.LOG_FILE_NAME_PREFIX + "_" + DateTime.Now.ToString("yyyyMMdd") + Constants.LOG_FILE_EXT; + newFileName = FileHelper.MakeUniqueFilename(newFileName, _config.LoggingConfig.OutputDirectory); + string newLog = Path.Combine(_config.LoggingConfig.OutputDirectory, newFileName); + + // Move the current file to the new file + File.Move(logFile, newLog); + } + + // Make sure we have less than the max number of logs + List totalFiles = Directory.GetFiles(_config.LoggingConfig.OutputDirectory, string.Format("{0}*{1}", Constants.LOG_FILE_NAME_PREFIX, Constants.LOG_FILE_EXT), SearchOption.TopDirectoryOnly).ToList(); + if (totalFiles.Count + 1 > _config.LoggingConfig.MaxCount && _config.LoggingConfig.MaxCount > 0) + { + // We will have too many logs, so let's remove the last one + totalFiles.Sort(); + string fileToRemove = totalFiles[totalFiles.Count - 1]; + File.Delete(fileToRemove); + } + } + } + + // We have rotated if needed, so let's write the entry + File.AppendAllText(logFile, log.ToString() + Environment.NewLine); + } + } + catch (Exception) { } // If we throw when writing the log, still try to send the email if needed + + try + { + // Send Email Message if enabled + if (_config.LoggingConfig.SendEmail) + { + // Do we want to send an email for this level? (Default to error) + LogLevel minEmailLevel = LogLevel.Error; + Enum.TryParse(_config.LoggingConfig.EmailLevel, out minEmailLevel); + if (log.Level >= minEmailLevel) + { + string subject = string.Format("{0} Log Message", log.Level); + string message = "Message: " + log.Message; + if (log.Exception != null) + { + message += Environment.NewLine + Environment.NewLine + "Exception: " + log.Exception.GetFullMessage(true, true); + } + SendErrorEmail(subject, message); + } + } + } + catch (Exception) + { + // Can't do anything about it. :/ + } + } + + private void SendErrorEmail(string subject, string message) + { + try + { + // Let's also email the message to support + SmtpClient client = new SmtpClient(); + client.Host = _config.LoggingConfig.SenderAccount.Host; + client.Port = _config.LoggingConfig.SenderAccount.Port; + client.EnableSsl = _config.LoggingConfig.SenderAccount.SSL; + client.DeliveryMethod = SmtpDeliveryMethod.Network; + client.UseDefaultCredentials = true; + client.Credentials = new System.Net.NetworkCredential(_config.LoggingConfig.SenderAccount.Username, _config.LoggingConfig.SenderAccount.Password); + client.Timeout = 5000; + + MailMessage mail = new MailMessage(_config.LoggingConfig.SenderAccount.EmailAddress, _config.LoggingConfig.RecipientEmailAddress); + mail.Subject = subject; + mail.Body = message; + mail.BodyEncoding = UTF8Encoding.UTF8; + mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never; + + client.Send(mail); + } + catch (Exception ex) { /* don't handle something in the handler */ + } + } + } +} diff --git a/Logging/LoggerExtensions.cs b/Logging/LoggerExtensions.cs new file mode 100644 index 0000000..6ea03a5 --- /dev/null +++ b/Logging/LoggerExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Text; +using Teknik.Configuration; + +namespace Teknik.Logging +{ + public static class LoggerExtensions + { + public static ILoggerFactory AddLogger(this ILoggerFactory loggerFactory, Config config) + { + loggerFactory.AddProvider(new LoggerProvider(config)); + return loggerFactory; + } + } +} diff --git a/Logging/LoggerProvider.cs b/Logging/LoggerProvider.cs new file mode 100644 index 0000000..9d8f321 --- /dev/null +++ b/Logging/LoggerProvider.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Text; +using Teknik.Configuration; + +namespace Teknik.Logging +{ + public class LoggerProvider : ILoggerProvider + { + private readonly Config _config; + private readonly ConcurrentDictionary _loggers = new ConcurrentDictionary(); + + public LoggerProvider(Config config) + { + _config = config; + } + + public ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, name => new Logger(name, _config)); + } + + public void Dispose() + { + _loggers.Clear(); + } + } +} diff --git a/Logging/Logging.csproj b/Logging/Logging.csproj new file mode 100644 index 0000000..6278cb6 --- /dev/null +++ b/Logging/Logging.csproj @@ -0,0 +1,14 @@ + + + + netcoreapp2.1 + Teknik.Logging + Teknik.Logging + + + + + + + + diff --git a/MailService/HMailService.cs b/MailService/HMailService.cs new file mode 100644 index 0000000..29dafae --- /dev/null +++ b/MailService/HMailService.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Teknik.MailService +{ + public class HMailService : MailService + { + private readonly hMailServer.Application _App; + + private string _Username { get; set; } + private string _Password { get; set; } + private string _Domain { get; set; } + + private string _CounterServer { get; set; } + private string _CounterDatabase { get; set; } + private string _CounterUsername { get; set; } + private string _CounterPassword { get; set; } + private int _CounterPort { get; set; } + + public HMailService(string username, string password, string domain, string counterServer, string counterDatabase, string counterUsername, string counterPassword, int counterPort) + { + _Username = username; + _Password = password; + _Domain = domain; + + _CounterServer = counterServer; + _CounterDatabase = counterDatabase; + _CounterUsername = counterUsername; + _CounterPassword = counterPassword; + _CounterPort = counterPort; + + _App = InitApp(); + } + + public override void CreateAccount(string username, string password, int size) + { + var domain = _App.Domains.ItemByName[_Domain]; + var newAccount = domain.Accounts.Add(); + newAccount.Address = username; + newAccount.Password = password; + newAccount.Active = true; + newAccount.MaxSize = size; + + newAccount.Save(); + } + + public override bool AccountExists(string username) + { + try + { + GetAccount(username); + // We didn't error out, so the email exists + return true; + } + catch { } + return false; + } + + public override void Delete(string username) + { + throw new NotImplementedException(); + } + + public override void Enable(string username) + { + EditActivity(username, true); + } + + public override void Disable(string username) + { + EditActivity(username, false); + } + + public override void EditActivity(string username, bool active) + { + var account = GetAccount(username); + account.Active = active; + account.Save(); + } + + public override void EditMaxEmailsPerDay(string username, int maxPerDay) + { + //We need to check the actual git database + MysqlDatabase mySQL = new MysqlDatabase(_CounterServer, _CounterDatabase, _CounterUsername, _CounterPassword, _CounterPort); + string sql = @"INSERT INTO mailcounter.counts (qname, lastdate, qlimit, count) VALUES ({1}, NOW(), {0}, 0) + ON DUPLICATE KEY UPDATE qlimit = {0}"; + mySQL.Execute(sql, new object[] { maxPerDay, username }); + } + + public override void EditMaxSize(string username, int size) + { + var account = GetAccount(username); + account.MaxSize = size; + account.Save(); + } + + public override void EditPassword(string username, string password) + { + var account = GetAccount(username); + account.Password = password; + account.Save(); + } + + public override DateTime LastActive(string username) + { + var account = GetAccount(username); + return (DateTime)account.LastLogonTime; + } + + private hMailServer.Application InitApp() + { + var app = new hMailServer.Application(); + app.Connect(); + app.Authenticate(_Username, _Password); + + return app; + } + + private hMailServer.Account GetAccount(string username) + { + var domain = _App.Domains.ItemByName[_Domain]; + return domain.Accounts.ItemByAddress[username]; + } + } +} diff --git a/MailService/IMailService.cs b/MailService/IMailService.cs new file mode 100644 index 0000000..ee902df --- /dev/null +++ b/MailService/IMailService.cs @@ -0,0 +1,27 @@ +using System; + +namespace Teknik.MailService +{ + public interface IMailService + { + bool AccountExists(string username); + + DateTime LastActive(string username); + + void CreateAccount(string username, string password, int size); + + void EditActivity(string username, bool active); + + void EditPassword(string username, string password); + + void EditMaxSize(string username, int size); + + void EditMaxEmailsPerDay(string username, int maxPerDay); + + void Enable(string username); + + void Disable(string username); + + void Delete(string username); + } +} diff --git a/MailService/MailService.cs b/MailService/MailService.cs new file mode 100644 index 0000000..7eb519e --- /dev/null +++ b/MailService/MailService.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Teknik.MailService +{ + public abstract class MailService : IMailService + { + public abstract void CreateAccount(string username, string password, int size); + + public abstract bool AccountExists(string username); + + public abstract void Delete(string username); + + public abstract void Disable(string username); + + public abstract void EditActivity(string username, bool active); + + public abstract void EditMaxEmailsPerDay(string username, int maxPerDay); + + public abstract void EditMaxSize(string username, int size); + + public abstract void EditPassword(string username, string password); + + public abstract void Enable(string username); + + public abstract DateTime LastActive(string username); + } +} diff --git a/MailService/MailService.csproj b/MailService/MailService.csproj new file mode 100644 index 0000000..293f231 --- /dev/null +++ b/MailService/MailService.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + Teknik.MailService + Teknik.MailService + + + + + + + + + lib\Teknik.hMailServer.dll + + + + diff --git a/MailService/MysqlDatabase.cs b/MailService/MysqlDatabase.cs new file mode 100644 index 0000000..f1100a5 --- /dev/null +++ b/MailService/MysqlDatabase.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using MySql.Data.MySqlClient; + +namespace Teknik.MailService +{ + public class MysqlDatabase + { + public event EventHandler MysqlErrorEvent; + + private bool Connected { get; set; } + private MySqlConnection Connection { get; set; } + private ReaderWriterLockSlim DatabaseLock { get; set; } + + public MysqlDatabase(string server, string database, string username, string password, int port) + { + Connected = false; + Connection = null; + DatabaseLock = new ReaderWriterLockSlim(); + Connect(server, database, username, password, port); + } + + public List> Query(string query, params object[] args) + { + List> rows = new List>(); + if (Connected) + { + DatabaseLock.EnterWriteLock(); + MySqlCommand cmd = PrepareQuery(query, args); + try + { + MySqlDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + Dictionary row = new Dictionary(); + for (int i = 0; i < reader.FieldCount; i++) + { + row.Add(reader.GetName(i), reader.GetValue(i)); + } + rows.Add(row); + } + reader.Close(); + } + catch (MySqlException exception) + { + if (MysqlErrorEvent != null) + { + MysqlErrorEvent(this, exception.Message); + } + } + catch (Exception exception) + { + if (MysqlErrorEvent != null) + { + MysqlErrorEvent(this, exception.Message); + } + } + DatabaseLock.ExitWriteLock(); + } + return rows; + } + + public object ScalarQuery(string query, params object[] args) + { + if (Connected) + { + DatabaseLock.EnterWriteLock(); + MySqlCommand cmd = PrepareQuery(query, args); + object result = null; + try + { + + result = cmd.ExecuteScalar(); + } + catch (MySqlException exception) + { + if (MysqlErrorEvent != null) + { + MysqlErrorEvent(this, exception.Message); + } + } + catch (Exception exception) + { + if (MysqlErrorEvent != null) + { + MysqlErrorEvent(this, exception.Message); + } + } + DatabaseLock.ExitWriteLock(); + return result; + } + return null; + } + + public void Execute(string query, params object[] args) + { + if (Connected) + { + DatabaseLock.EnterWriteLock(); + MySqlCommand cmd = PrepareQuery(query, args); + try + { + int result = cmd.ExecuteNonQuery(); + } + catch (MySqlException exception) + { + if (MysqlErrorEvent != null) + { + MysqlErrorEvent(this, exception.Message); + } + } + catch (Exception exception) + { + if (MysqlErrorEvent != null) + { + MysqlErrorEvent(this, exception.Message); + } + } + DatabaseLock.ExitWriteLock(); + } + } + + private void Connect(string server, string database, string username, string password, int port) + { + if (Connection == null) + { + if (!string.IsNullOrEmpty(server) && !string.IsNullOrEmpty(database) && !string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password)) + { + string strCon = string.Format("Server={0}; database={1}; user={2}; password={3}; port={4}; charset=utf8; Allow Zero Datetime=true;", server, database, username, password, port); + Connection = new MySqlConnection(strCon); + try + { + Connection.Open(); + Connected = true; + } + catch (MySqlException ex) + { + Connected = false; + } + } + } + } + + private void Disconnect() + { + if (Connection != null && Connected) + { + Connected = false; + Connection.Close(); + } + } + + private MySqlCommand PrepareQuery(string query, object[] args) + { + if (Connected) + { + MySqlCommand cmd = new MySqlCommand(); + cmd.Connection = Connection; + for (int i = 0; i < args.Length; i++) + { + string param = "{" + i + "}"; + string paramName = "@DBVar_" + i; + query = query.Replace(param, paramName); + cmd.Parameters.AddWithValue(paramName, args[i]); + } + cmd.CommandText = query; + return cmd; + } + return null; + } + } +} \ No newline at end of file diff --git a/MailService/lib/Teknik.hMailServer.dll b/MailService/lib/Teknik.hMailServer.dll new file mode 100644 index 0000000000000000000000000000000000000000..b58cd2875085e512172295ec8cb4faea0f3091e4 GIT binary patch literal 166912 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

aes!t z+1hV6Z27lo?WRq~&o<4RO`|pMlEaRH=(LA2d%54{R;VB orv29s%u58zy(KP$1Iz!p_&Gqw?5z+KdM2G*c`;P+u0)Z|4hX4Qo literal 0 HcmV?d00001 diff --git a/Piwik/Piwik.csproj b/Piwik/Piwik.csproj new file mode 100644 index 0000000..544abc7 --- /dev/null +++ b/Piwik/Piwik.csproj @@ -0,0 +1,13 @@ + + + + netcoreapp2.1 + Teknik.Piwik + Teknik.Piwik + + + + + + + diff --git a/Piwik/Reporting.cs b/Piwik/Reporting.cs new file mode 100644 index 0000000..d75945b --- /dev/null +++ b/Piwik/Reporting.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Teknik.Configuration; + +namespace Teknik.Piwik +{ + public static class Reporting + { + public static List GetVisitSummaryByDays(Config config, int days) + { + List visitorData = new List(); + try + { + if (config.PiwikConfig.Enabled) + { + //PiwikAnalytics.URL = config.PiwikConfig.API; + //VisitsSummary visitSummary = new VisitsSummary(); + //visitSummary.setTokenAuth(config.PiwikConfig.TokenAuth); + + //Hashtable results = visitSummary.Get( + // config.PiwikConfig.SiteId, + // PiwikPeriod.DAY, + // RelativeRangeDate.LAST(days) + // ); + + //foreach (string period in results.Keys) + //{ + // // Create a new object to return + // VisitorData data = new VisitorData(); + + // // Set Period Date + // DateTime date = new DateTime(1900, 1, 1); + // DateTime.TryParse(period, out date); + // data.Date = date; + + // // Pull Out Data + // if (results[period].GetType() == typeof(Hashtable)) + // { + // Hashtable result = (Hashtable) results[period]; + + // int UniqueVisitors = 0; + // int.TryParse(result["nb_uniq_visitors"].ToString(), out UniqueVisitors); + // data.UniqueVisitors = UniqueVisitors; + + // int visits = 0; + // int.TryParse(result[VisitsSummary.NB_VISITS].ToString(), out visits); + // data.Visits = visits; + + // int VisitsConverted = 0; + // int.TryParse(result[VisitsSummary.NB_VISITS_CONVERTED].ToString(), out VisitsConverted); + // data.VisitsConverted = VisitsConverted; + + // int Actions = 0; + // int.TryParse(result[VisitsSummary.NB_ACTIONS].ToString(), out Actions); + // data.Actions = Actions; + + // decimal ActionsPerVisit = 0; + // decimal.TryParse(result[VisitsSummary.NB_ACTIONS_PER_VISIT].ToString(), out ActionsPerVisit); + // data.ActionsPerVisit = ActionsPerVisit; + + // int MaxActions = 0; + // int.TryParse(result[VisitsSummary.MAX_ACTIONS].ToString(), out MaxActions); + // data.MaxActions = MaxActions; + + // int BounceCount = 0; + // int.TryParse(result[VisitsSummary.BOUNCE_COUNT].ToString(), out BounceCount); + // data.BounceCount = BounceCount; + + // data.BounceRate = result[VisitsSummary.BOUNCE_RATE].ToString(); + + // int AverageTimeOnSite = 0; + // int.TryParse(result[VisitsSummary.AVG_TIME_ON_SITE].ToString(), out AverageTimeOnSite); + // data.AverageTimeOnSite = AverageTimeOnSite; + + // int VisitLengthTotal = 0; + // int.TryParse(result[VisitsSummary.SUM_VISIT_LENGTH].ToString(), out VisitLengthTotal); + // data.VisitLengthTotal = VisitLengthTotal; + // } + + // visitorData.Add(data); + //} + } + } + catch (Exception ex) + { + + } + + return visitorData; + } + } +} diff --git a/Piwik/Tracking.cs b/Piwik/Tracking.cs new file mode 100644 index 0000000..3c72336 --- /dev/null +++ b/Piwik/Tracking.cs @@ -0,0 +1,93 @@ +using System; +using System.Web; +using Teknik.Configuration; +using Teknik.Utilities; + +namespace Teknik.Piwik +{ + public static class Tracking + { + public static void TrackPageView(Config config, string title, string sub, string clientIp, string url, string urlReferrer, string userAgent, int pixelWidth, int pixelHeight, bool hasCookies, string acceptLang, bool hasJava) + { + try + { + if (config.PiwikConfig.Enabled) + { + if (config.DevEnvironment) + { + sub = "dev - " + sub; + } + + //PiwikTracker.URL = config.PiwikConfig.Url; + //PiwikTracker tracker = new PiwikTracker(config.PiwikConfig.SiteId, config.PiwikConfig.Url); + + //// Set Request Info + //tracker.setIp(clientIp); + //tracker.setTokenAuth(config.PiwikConfig.TokenAuth); + + //tracker.setUserAgent(userAgent); + + //// Set browser info + //tracker.setResolution(pixelWidth, pixelHeight); + //tracker.setBrowserHasCookies(hasCookies); + //if (!string.IsNullOrEmpty(acceptLang)) + // tracker.setBrowserLanguage(acceptLang); + //tracker.setPlugins(new BrowserPlugins {java = hasJava}); + + //// Get Referral + //if (!string.IsNullOrEmpty(urlReferrer)) + // tracker.setUrlReferrer(urlReferrer); + + //if (!string.IsNullOrEmpty(url)) + // tracker.setUrl(url); + + //// Send the tracking request + //tracker.doTrackPageView(string.Format("{0}/{1}", sub, title)); + } + } + catch (Exception ex) + { + + } + } + + public static void TrackDownload(Config config, string userAgent, string clientIp, string url, string urlReferrer) + { + //TrackAction(config. PiwikTracker.ActionType.download, userAgent, clientIp, url, urlReferrer); + } + + public static void TrackLink(Config config, string userAgent, string clientIp, string url, string urlReferrer) + { + //TrackAction(config.PiwikTracker.ActionType.link, userAgent, clientIp, url, urlReferrer); + } + + //private static void TrackAction(Config config, PiwikTracker.ActionType type, string userAgent, string clientIp, string url, string urlReferrer) + //{ + // try + // { + // if (config.PiwikConfig.Enabled) + // { + // PiwikTracker tracker = new PiwikTracker(config.PiwikConfig.SiteId, config.PiwikConfig.Url); + + // tracker.setUserAgent(userAgent); + + // tracker.setIp(clientIp); + // tracker.setTokenAuth(config.PiwikConfig.TokenAuth); + + // // Get Referral + // if (!string.IsNullOrEmpty(urlReferrer)) + // tracker.setUrlReferrer(urlReferrer); + + // if (!string.IsNullOrEmpty(url)) + // tracker.setUrl(url); + + // tracker.doTrackAction(url, type); + // } + // } + // catch (Exception ex) + // { + + // } + //} + } +} diff --git a/Utilities/Piwik/VisitorData.cs b/Piwik/VisitorData.cs similarity index 100% rename from Utilities/Piwik/VisitorData.cs rename to Piwik/VisitorData.cs diff --git a/Teknik.sln b/Teknik.sln index 333abcc..64ba001 100644 --- a/Teknik.sln +++ b/Teknik.sln @@ -1,125 +1,62 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.15 +VisualStudioVersion = 15.0.27512.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Teknik", "Teknik\Teknik.csproj", "{B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Teknik", "Teknik\Teknik.csproj", "{1E52F0D0-9E89-4022-A905-C685EF3564E1}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{725ABF52-FD44-4682-81BB-D93598787643}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Configuration", "Configuration\Configuration.csproj", "{7A1F40CA-7C37-4B7B-973A-CDCDD424F31F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Utilities", "Utilities\Utilities.csproj", "{DD521101-7F10-407A-9788-49283D946FDA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Logging", "Logging\Logging.csproj", "{3CAB11F5-9B07-4D17-BB00-725149087AB0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C208CBF0-078E-409D-A433-DC1BC15248CE}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig - GitVersionConfig.yaml = GitVersionConfig.yaml - README.md = README.md + .gitattributes = .gitattributes + .gitignore = .gitignore EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServerMaint", "ServerMaint\ServerMaint.csproj", "{E08975F9-1B84-41B0-875A-CEC9778C4F9E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Piwik", "Piwik\Piwik.csproj", "{F8823907-092C-4055-9F8E-D6756793C24A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{783361EC-DCD6-4A34-8479-5476DF752C34}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utilities", "Utilities\Utilities\Utilities.csproj", "{F45DE6FC-3754-4954-A20A-4277362CC6C1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Logging", "Utilities\Logging\Logging.csproj", "{77E865FD-F08B-4F07-9676-BC2FDCC7244C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Piwik", "Utilities\Piwik\Piwik.csproj", "{C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Configuration", "Utilities\Configuration\Configuration.csproj", "{F0DA1B67-AF92-4B4A-8669-7E81645FF996}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeknikStreaming", "TeknikStreaming\TeknikStreaming.csproj", "{7695CE9A-A0DB-4D73-BC9B-2206481F0254}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{FAC9FE6E-9AA9-45AD-AA72-40DDF7DC44C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UtilitiesTests", "UtilitiesTests\UtilitiesTests.csproj", "{88DEB506-3F7E-4B39-8264-861976DC7434}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeknikTests", "TeknikTests\TeknikTests.csproj", "{9D7A805E-2629-476E-B36A-040AD332C7DC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MailService", "MailService\MailService.csproj", "{03636C30-DA61-4307-8934-2FCC3BAC3255}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Debug|x64.ActiveCfg = Debug|x64 - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Debug|x64.Build.0 = Debug|x64 - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Debug|x64.Deploy.0 = Debug|x64 - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Release|Any CPU.Build.0 = Release|Any CPU - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Release|x64.ActiveCfg = Release|x64 - {B20317CD-76C6-4A7B-BCE1-E4BEF8E4F964}.Release|x64.Build.0 = Release|x64 - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Debug|x64.ActiveCfg = Debug|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Debug|x64.Build.0 = Debug|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Release|Any CPU.Build.0 = Release|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Release|x64.ActiveCfg = Release|Any CPU - {E08975F9-1B84-41B0-875A-CEC9778C4F9E}.Release|x64.Build.0 = Release|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Debug|x64.ActiveCfg = Debug|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Debug|x64.Build.0 = Debug|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Release|Any CPU.Build.0 = Release|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Release|x64.ActiveCfg = Release|Any CPU - {F45DE6FC-3754-4954-A20A-4277362CC6C1}.Release|x64.Build.0 = Release|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Debug|x64.ActiveCfg = Debug|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Debug|x64.Build.0 = Debug|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Release|Any CPU.Build.0 = Release|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Release|x64.ActiveCfg = Release|Any CPU - {77E865FD-F08B-4F07-9676-BC2FDCC7244C}.Release|x64.Build.0 = Release|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Debug|x64.ActiveCfg = Debug|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Debug|x64.Build.0 = Debug|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Release|Any CPU.Build.0 = Release|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Release|x64.ActiveCfg = Release|Any CPU - {C492C2C6-D45A-498B-84A2-6D4C8BF9DE77}.Release|x64.Build.0 = Release|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Debug|x64.ActiveCfg = Debug|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Debug|x64.Build.0 = Debug|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Release|Any CPU.Build.0 = Release|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Release|x64.ActiveCfg = Release|Any CPU - {F0DA1B67-AF92-4B4A-8669-7E81645FF996}.Release|x64.Build.0 = Release|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Debug|x64.ActiveCfg = Debug|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Debug|x64.Build.0 = Debug|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|Any CPU.Build.0 = Release|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|x64.ActiveCfg = Release|Any CPU - {7695CE9A-A0DB-4D73-BC9B-2206481F0254}.Release|x64.Build.0 = Release|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Debug|Any CPU.Build.0 = Debug|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Debug|x64.ActiveCfg = Debug|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Debug|x64.Build.0 = Debug|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Release|Any CPU.ActiveCfg = Release|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Release|Any CPU.Build.0 = Release|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Release|x64.ActiveCfg = Release|Any CPU - {88DEB506-3F7E-4B39-8264-861976DC7434}.Release|x64.Build.0 = Release|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Debug|x64.ActiveCfg = Debug|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Debug|x64.Build.0 = Debug|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Release|Any CPU.Build.0 = Release|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Release|x64.ActiveCfg = Release|Any CPU - {9D7A805E-2629-476E-B36A-040AD332C7DC}.Release|x64.Build.0 = Release|Any CPU + {1E52F0D0-9E89-4022-A905-C685EF3564E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E52F0D0-9E89-4022-A905-C685EF3564E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E52F0D0-9E89-4022-A905-C685EF3564E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E52F0D0-9E89-4022-A905-C685EF3564E1}.Release|Any CPU.Build.0 = Release|Any CPU + {7A1F40CA-7C37-4B7B-973A-CDCDD424F31F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A1F40CA-7C37-4B7B-973A-CDCDD424F31F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A1F40CA-7C37-4B7B-973A-CDCDD424F31F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A1F40CA-7C37-4B7B-973A-CDCDD424F31F}.Release|Any CPU.Build.0 = Release|Any CPU + {DD521101-7F10-407A-9788-49283D946FDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD521101-7F10-407A-9788-49283D946FDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD521101-7F10-407A-9788-49283D946FDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD521101-7F10-407A-9788-49283D946FDA}.Release|Any CPU.Build.0 = Release|Any CPU + {3CAB11F5-9B07-4D17-BB00-725149087AB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CAB11F5-9B07-4D17-BB00-725149087AB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CAB11F5-9B07-4D17-BB00-725149087AB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CAB11F5-9B07-4D17-BB00-725149087AB0}.Release|Any CPU.Build.0 = Release|Any CPU + {F8823907-092C-4055-9F8E-D6756793C24A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8823907-092C-4055-9F8E-D6756793C24A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8823907-092C-4055-9F8E-D6756793C24A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8823907-092C-4055-9F8E-D6756793C24A}.Release|Any CPU.Build.0 = Release|Any CPU + {03636C30-DA61-4307-8934-2FCC3BAC3255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03636C30-DA61-4307-8934-2FCC3BAC3255}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03636C30-DA61-4307-8934-2FCC3BAC3255}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03636C30-DA61-4307-8934-2FCC3BAC3255}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {E08975F9-1B84-41B0-875A-CEC9778C4F9E} = {783361EC-DCD6-4A34-8479-5476DF752C34} - {88DEB506-3F7E-4B39-8264-861976DC7434} = {FAC9FE6E-9AA9-45AD-AA72-40DDF7DC44C6} - {9D7A805E-2629-476E-B36A-040AD332C7DC} = {FAC9FE6E-9AA9-45AD-AA72-40DDF7DC44C6} + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D5D09AC6-1E27-476C-BF7E-980E1640F432} EndGlobalSection EndGlobal diff --git a/Teknik/App_Start/BundleConfig.cs b/Teknik/App_Start/BundleConfig.cs deleted file mode 100644 index fff3f94..0000000 --- a/Teknik/App_Start/BundleConfig.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Web; -using System.Web.Optimization; -using Teknik.Configuration; -using Teknik.Utilities; - -namespace Teknik -{ - public class BundleConfig - { - // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 - public static void RegisterBundles(BundleCollection bundles) - { - // Load the config options - Config config = Config.Load(); - - // Set if we are using Cdn - bundles.UseCdn = config.UseCdn; - - BundleTable.EnableOptimizations = false; -#if !DEBUG - BundleTable.EnableOptimizations = true; -#endif - - bundles.Add(new CdnStyleBundle("~/Content/Common", config.CdnHost).Include( - "~/Content/bootstrap.css", - "~/Content/font-awesome.css", - "~/Content/common.css")); - - bundles.Add(new CdnScriptBundle("~/bundles/common", config.CdnHost).Include( - "~/Scripts/jquery-{version}.js", - "~/Scripts/jquery.validate*", - "~/Scripts/bootstrap.js", - "~/Scripts/bootstrap-select.js", - "~/Scripts/common.js", - "~/Scripts/respond.js")); - - bundles.Add(new CdnScriptBundle("~/bundles/jquery", config.CdnHost).Include( - "~/Scripts/jquery-{version}.js", - "~/Scripts/jquery.validate*")); - - // Use the development version of Modernizr to develop with and learn from. Then, when you're - // ready for production, use the build tool at http://modernizr.com to pick only the tests you need. - bundles.Add(new CdnScriptBundle("~/bundles/modernizr", config.CdnHost).Include( - "~/Scripts/modernizr-*")); - - bundles.Add(new CdnScriptBundle("~/bundles/markdown", config.CdnHost).Include( - "~/Scripts/PageDown/Markdown.Converter.js", - "~/Scripts/PageDown/Markdown.Sanitizer.js")); - - bundles.Add(new CdnScriptBundle("~/bundles/signalr", config.CdnHost).Include( - "~/Scripts/jquery.signalR-{version}.js")); - } - } -} diff --git a/Teknik/App_Start/CustomRazorViewEngine.cs b/Teknik/App_Start/CustomRazorViewEngine.cs deleted file mode 100644 index 55a60a4..0000000 --- a/Teknik/App_Start/CustomRazorViewEngine.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Web.Mvc; - -namespace Teknik -{ - public class CustomRazorViewEngine : RazorViewEngine - { - public CustomRazorViewEngine() - { - base.AreaViewLocationFormats = new string[] - { - "~/Areas/{2}/Views/{1}/{0}.cshtml", - "~/Areas/{2}/Views/Shared/{0}.cshtml" - }; - - base.AreaMasterLocationFormats = new string[] - { - "~/Areas/{2}/Views/{1}/{0}.cshtml", - "~/Areas/{2}/Views/Shared/{0}.cshtml" - }; - - base.AreaPartialViewLocationFormats = new string[] - { - "~/Areas/{2}/Views/{1}/{0}.cshtml", - "~/Areas/{2}/Views/Shared/{0}.cshtml" - }; - - base.ViewLocationFormats = new string[] - { - "~/Views/{1}/{0}.cshtml", - "~/Views/Shared/{0}.cshtml" - }; - - base.PartialViewLocationFormats = new string[] - { - "~/Views/{1}/{0}.cshtml", - "~/Views/Shared/{0}.cshtml" - }; - - base.MasterLocationFormats = new string[] - { - "~/Views/{1}/{0}.cshtml", - "~/Views/Shared/{0}.cshtml" - }; - } - } -} diff --git a/Teknik/App_Start/FilterConfig.cs b/Teknik/App_Start/FilterConfig.cs deleted file mode 100644 index 8386953..0000000 --- a/Teknik/App_Start/FilterConfig.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Web; -using System.Web.Mvc; -using Teknik.Attributes; -using Teknik.Filters; - -namespace Teknik -{ - public class FilterConfig - { - public static void RegisterGlobalFilters(GlobalFilterCollection filters) - { - //filters.Add(new HandleErrorAttribute()); - //filters.Add(new TeknikAuthorizeAttribute()); - filters.Add(new RequireHttpsAttribute()); - } - } -} diff --git a/Teknik/App_Start/RouteConfig.cs b/Teknik/App_Start/RouteConfig.cs deleted file mode 100644 index af259e5..0000000 --- a/Teknik/App_Start/RouteConfig.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Web.Mvc; -using System.Web.Routing; - -namespace Teknik -{ - public class RouteConfig - { - public static void RegisterRoutes(RouteCollection routes) - { - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - - // Add this code to handle non-existing urls - routes.MapRoute( - name: "404-PageNotFound", - // This will handle any non-existing urls - url: "{*url}", - // "Shared" is the name of your error controller, and "Error" is the action/page - // that handles all your custom errors - defaults: new { controller = "Default", action = "NotFound" } - ); - } - } -} diff --git a/Teknik/App_Start/SubdomainRoute.cs b/Teknik/App_Start/SubdomainRoute.cs deleted file mode 100644 index 4729072..0000000 --- a/Teknik/App_Start/SubdomainRoute.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; -using Teknik.Utilities; - -namespace Teknik -{ - public class SubdomainRoute : Route - { - public List Subdomains { get; set; } - - public List Domains { get; set; } - - public SubdomainRoute(List subdomains, List domains, string url, IRouteHandler handler) - : base(url, handler) - { - this.Subdomains = subdomains; - this.Domains = domains; - } - public SubdomainRoute(List subdomains, List domains, string url, RouteValueDictionary defaults, IRouteHandler handler) - : base(url, defaults, handler) - { - this.Subdomains = subdomains; - this.Domains = domains; - } - - public SubdomainRoute(List subdomains, List domains, string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler handler) - : base(url, defaults, constraints, handler) - { - this.Subdomains = subdomains; - this.Domains = domains; - } - - public SubdomainRoute(List subdomains, List domains, string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler handler) - : base(url, defaults, constraints, dataTokens, handler) - { - this.Subdomains = subdomains; - this.Domains = domains; - } - - public override RouteData GetRouteData(HttpContextBase httpContext) - { - var routeData = base.GetRouteData(httpContext); - if (routeData == null) return null; // Only look at the subdomain if this route matches in the first place. - string subdomain = httpContext.Request.QueryString["sub"]; // A subdomain specified as a query parameter takes precedence over the hostname. - string host = httpContext.Request.Headers["Host"]; - if (host == null) return null; // no host, so don't process it - string domain = host.GetDomain(); - string curSub = host.GetSubdomain(); - - // special consideration for 'dev' subdomain - if (subdomain == null || subdomain == "dev") - { - if (!string.IsNullOrEmpty(curSub) && curSub == "dev") - { - // if we are on dev, and the param is dev or empty, we need to initialize it to 'www' - subdomain = "www"; - } - } - - if (subdomain == null) - { - subdomain = curSub; - } - else - { - if (routeData.Values["sub"] == null) - { - routeData.Values["sub"] = subdomain; - } - else - { - subdomain = routeData.Values["sub"].ToString(); - } - } - - // Check if this route is valid for the current domain - if (httpContext.Request.IsLocal || Domains.Contains(domain)) - { - // Check if this route is valid for the current subdomain ('*' means any subdomain is valid) - if (Subdomains.Contains("*") || Subdomains.Contains(subdomain)) - { - routeData.Values["sub"] = subdomain; - return routeData; - } - } - return null; - } - - public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) - { - object subdomainParam = requestContext.HttpContext.Request.QueryString["sub"]; - if (subdomainParam != null && values["sub"] == null) - values["sub"] = subdomainParam; - return base.GetVirtualPath(requestContext, values); // we now have the route based on subdomain - } - } -} \ No newline at end of file diff --git a/Teknik/App_Start/SubdomainRouteExtension.cs b/Teknik/App_Start/SubdomainRouteExtension.cs deleted file mode 100644 index 6083c12..0000000 --- a/Teknik/App_Start/SubdomainRouteExtension.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using System.Web.Routing; - -namespace Teknik -{ - public static class SubdomainRouteExtension - { - public static SubdomainRoute MapSubdomainRoute(this RouteCollection routes, string name, List subDomains, List domains, string url, object defaults) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new MvcRouteHandler()); - routes.Add(name, route); - - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this RouteCollection routes, string name, List subDomains, List domains, string url, object defaults, object constraints) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(constraints), - new MvcRouteHandler()); - routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this RouteCollection routes, string name, List subDomains, List domains, string area, string url, object defaults, string[] namespaces) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(new { }), - new RouteValueDictionary(new { Area = area, Namespaces = namespaces }), - new MvcRouteHandler()); - routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, object defaults) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(new {}), - new RouteValueDictionary(new {Area = context.AreaName}), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, object defaults, object constraints) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(constraints), - new RouteValueDictionary(new {Area = context.AreaName}), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, object defaults, string[] namespaces) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(new {}), - new RouteValueDictionary(new { Area = context.AreaName, Namespaces = namespaces }), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, object defaults, object constraints, string[] namespaces) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(constraints), - new RouteValueDictionary(new { Area = context.AreaName, Namespaces = namespaces }), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, string area, object defaults) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(new { }), - new RouteValueDictionary(new { Area = area }), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, string area, object defaults, object constraints) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(constraints), - new RouteValueDictionary(new { Area = area }), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - - public static SubdomainRoute MapSubdomainRoute(this AreaRegistrationContext context, string name, List subDomains, List domains, string url, string area, object defaults, string[] namespaces) - { - SubdomainRoute route = new SubdomainRoute( - subDomains, - domains, - url, - new RouteValueDictionary(defaults), - new RouteValueDictionary(new { }), - new RouteValueDictionary(new { Area = area, Namespaces = namespaces }), - new MvcRouteHandler()); - - context.Routes.Add(name, route); - return route; - } - } -} \ No newline at end of file diff --git a/Teknik/Areas/API/APIAreaRegistration.cs b/Teknik/Areas/API/APIAreaRegistration.cs deleted file mode 100644 index 7fc52d4..0000000 --- a/Teknik/Areas/API/APIAreaRegistration.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using Teknik.Configuration; - -namespace Teknik.Areas.API -{ - public class APIAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "API"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - #region API v1 - // Base Routing - context.MapSubdomainRoute( - "API.v1.Index", // Route name - new List() { "api" }, - new List() { config.Host }, - "v1", // URL with parameters - new { controller = "APIv1", action = "Index" }, // Parameter defaults - new[] { typeof(Controllers.APIv1Controller).Namespace } - ); - // Uploads - context.MapSubdomainRoute( - "API.v1.Upload", // Route name - new List() { "api" }, - new List() { config.Host }, - "v1/Upload", // URL with parameters - new { controller = "APIv1", action = "Upload" }, // Parameter defaults - new[] { typeof(Controllers.APIv1Controller).Namespace } - ); - // Pastes - context.MapSubdomainRoute( - "API.v1.Paste", // Route name - new List() { "api" }, - new List() { config.Host }, - "v1/Paste", // URL with parameters - new { controller = "APIv1", action = "Paste" }, // Parameter defaults - new[] { typeof(Controllers.APIv1Controller).Namespace } - ); - // Url Shortening - context.MapSubdomainRoute( - "API.v1.Shortener", // Route name - new List() { "api" }, - new List() { config.Host }, - "v1/Shorten", // URL with parameters - new { controller = "APIv1", action = "Shorten" }, // Parameter defaults - new[] { typeof(Controllers.APIv1Controller).Namespace } - ); - #endregion - - // Default Routing - context.MapSubdomainRoute( - "API.Index", // Route name - new List() { "api" }, - new List() { config.Host }, - "", // URL with parameters - new { controller = "API", action = "Index" }, // Parameter defaults - new[] { typeof(Controllers.APIController).Namespace } - ); - } - } -} \ No newline at end of file diff --git a/Teknik/Areas/API/Controllers/APIController.cs b/Teknik/Areas/API/Controllers/APIController.cs index f09c847..d666f9e 100644 --- a/Teknik/Areas/API/Controllers/APIController.cs +++ b/Teknik/Areas/API/Controllers/APIController.cs @@ -1,23 +1,30 @@ using System; using System.Collections.Generic; -using System.Data.Entity; using System.IO; using System.Linq; using System.Web; -using System.Web.Mvc; using Teknik.Areas.Upload; using Teknik.Controllers; using Teknik.Utilities; using Teknik.Models; using Teknik.Attributes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Logging; +using Teknik.Configuration; +using Teknik.Data; +using Microsoft.AspNetCore.Mvc; +using Teknik.Logging; namespace Teknik.Areas.API.Controllers { [TeknikAuthorize] + [Area("API")] public class APIController : DefaultController { + public APIController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [AllowAnonymous] - public ActionResult Index() + public IActionResult Index() { return Redirect(Url.SubRouteUrl("help", "Help.API")); } diff --git a/Teknik/Areas/API/Controllers/APIv1Controller.cs b/Teknik/Areas/API/Controllers/APIv1Controller.cs index f729867..2231ce6 100644 --- a/Teknik/Areas/API/Controllers/APIv1Controller.cs +++ b/Teknik/Areas/API/Controllers/APIv1Controller.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; -using System.Data.Entity; using System.IO; using System.Linq; using System.Web; -using System.Web.Mvc; using Teknik.Areas.Upload; using Teknik.Areas.Paste; using Teknik.Controllers; @@ -20,14 +18,26 @@ using Teknik.Areas.API.Models; using Teknik.Areas.Users.Models; using Teknik.Areas.Users.Utility; using Teknik.Attributes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Logging; +using Teknik.Configuration; +using Teknik.Data; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using System.Threading.Tasks; +using Teknik.Areas.Shortener; +using Teknik.Logging; namespace Teknik.Areas.API.Controllers { [TeknikAuthorize(AuthType.Basic)] + [Area("APIv1")] public class APIv1Controller : DefaultController { + public APIv1Controller(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [AllowAnonymous] - public ActionResult Index() + public IActionResult Index() { return Redirect(Url.SubRouteUrl("help", "Help.API")); } @@ -35,35 +45,36 @@ namespace Teknik.Areas.API.Controllers [HttpPost] [AllowAnonymous] [TrackPageView] - public ActionResult Upload(APIv1UploadModel model) + public async Task UploadAsync(APIv1UploadModel model) { try { - if (Config.UploadConfig.UploadEnabled) + if (_config.UploadConfig.UploadEnabled) { if (model.file != null) { - long maxUploadSize = Config.UploadConfig.MaxUploadSize; + long maxUploadSize = _config.UploadConfig.MaxUploadSize; if (User.Identity.IsAuthenticated) { - maxUploadSize = Config.UploadConfig.MaxUploadSizeBasic; - if (User.Info.AccountType == AccountType.Premium) + maxUploadSize = _config.UploadConfig.MaxUploadSizeBasic; + User user = UserHelper.GetUser(_dbContext, User.Identity.Name); + if (user.AccountType == AccountType.Premium) { - maxUploadSize = Config.UploadConfig.MaxUploadSizePremium; + maxUploadSize = _config.UploadConfig.MaxUploadSizePremium; } } - if (model.file.ContentLength <= maxUploadSize) + if (model.file.Length <= maxUploadSize) { // convert file to bytes string fileExt = Path.GetExtension(model.file.FileName); - int contentLength = model.file.ContentLength; + long contentLength = model.file.Length; // Scan the file to detect a virus - if (Config.UploadConfig.VirusScanEnable) + if (_config.UploadConfig.VirusScanEnable) { - ClamClient clam = new ClamClient(Config.UploadConfig.ClamServer, Config.UploadConfig.ClamPort); + ClamClient clam = new ClamClient(_config.UploadConfig.ClamServer, _config.UploadConfig.ClamPort); clam.MaxStreamSize = maxUploadSize; - ClamScanResult scanResult = clam.SendAndScanFile(model.file.InputStream); + ClamScanResult scanResult = await clam.SendAndScanFileAsync(model.file.OpenReadStream()); switch (scanResult.Result) { @@ -85,13 +96,16 @@ namespace Teknik.Areas.API.Controllers if (string.IsNullOrEmpty(model.contentType)) { - model.file.InputStream.Seek(0, SeekOrigin.Begin); - FileType fileType = model.file.InputStream.GetFileType(); - if (fileType != null) - model.contentType = fileType.Mime; - if (string.IsNullOrEmpty(model.contentType)) + using (System.IO.Stream fileStream = model.file.OpenReadStream()) { - model.contentType = "application/octet-stream"; + fileStream.Seek(0, SeekOrigin.Begin); + FileType fileType = fileStream.GetFileType(); + if (fileType != null) + model.contentType = fileType.Mime; + if (string.IsNullOrEmpty(model.contentType)) + { + model.contentType = "application/octet-stream"; + } } } } @@ -99,7 +113,7 @@ namespace Teknik.Areas.API.Controllers // Check content type restrictions (Only for encrypting server side if (model.encrypt || !string.IsNullOrEmpty(model.key)) { - if (Config.UploadConfig.RestrictedContentTypes.Contains(model.contentType) || Config.UploadConfig.RestrictedExtensions.Contains(fileExt)) + if (_config.UploadConfig.RestrictedContentTypes.Contains(model.contentType) || _config.UploadConfig.RestrictedExtensions.Contains(fileExt)) { return Json(new { error = new { message = "File Type Not Allowed" } }); } @@ -107,66 +121,64 @@ namespace Teknik.Areas.API.Controllers // Initialize the key size and block size if empty if (model.keySize <= 0) - model.keySize = Config.UploadConfig.KeySize; + model.keySize = _config.UploadConfig.KeySize; if (model.blockSize <= 0) - model.blockSize = Config.UploadConfig.BlockSize; + model.blockSize = _config.UploadConfig.BlockSize; - using (TeknikEntities db = new TeknikEntities()) + // Save the file data + Upload.Models.Upload upload = Uploader.SaveFile(_dbContext, _config, model.file.OpenReadStream(), model.contentType, contentLength, model.encrypt, fileExt, model.iv, model.key, model.keySize, model.blockSize); + + if (upload != null) { - // Save the file data - Upload.Models.Upload upload = Uploader.SaveFile(db, Config, model.file.InputStream, model.contentType, contentLength, model.encrypt, fileExt, model.iv, model.key, model.keySize, model.blockSize); + string fileKey = upload.Key; - if (upload != null) + // Associate this with the user if they provided an auth key + if (User.Identity.IsAuthenticated) { - string fileKey = upload.Key; - - // Associate this with the user if they provided an auth key - if (User.Identity.IsAuthenticated) + User foundUser = UserHelper.GetUser(_dbContext, User.Identity.Name); + if (foundUser != null) { - User foundUser = UserHelper.GetUser(db, User.Identity.Name); - if (foundUser != null) - { - upload.UserId = foundUser.UserId; - db.Entry(upload).State = EntityState.Modified; - db.SaveChanges(); - } + upload.UserId = foundUser.UserId; + _dbContext.Entry(upload).State = EntityState.Modified; + _dbContext.SaveChanges(); } - - // Generate delete key only if asked to - if (!model.genDeletionKey) - { - upload.DeleteKey = string.Empty; - db.Entry(upload).State = EntityState.Modified; - db.SaveChanges(); - } - - // remove the key if we don't want to save it - if (!model.saveKey) - { - upload.Key = null; - db.Entry(upload).State = EntityState.Modified; - db.SaveChanges(); - } - - // Pull all the information together - string fullUrl = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url }); - var returnData = new - { - url = (model.saveKey || string.IsNullOrEmpty(fileKey)) ? fullUrl : fullUrl + "#" + fileKey, - fileName = upload.Url, - contentType = upload.ContentType, - contentLength = upload.ContentLength, - key = fileKey, - keySize = upload.KeySize, - iv = upload.IV, - blockSize = upload.BlockSize, - deletionKey = upload.DeleteKey - - }; - return Json(new { result = returnData }); } - return Json(new { error = new { message = "Unable to save file" } }); + + // Generate delete key only if asked to + if (!model.genDeletionKey) + { + upload.DeleteKey = string.Empty; + _dbContext.Entry(upload).State = EntityState.Modified; + _dbContext.SaveChanges(); + } + + // remove the key if we don't want to save it + if (!model.saveKey) + { + upload.Key = null; + _dbContext.Entry(upload).State = EntityState.Modified; + _dbContext.SaveChanges(); + } + + // Pull all the information together + string fullUrl = Url.SubRouteUrl("u", "Upload.Download", new { file = upload.Url }); + var returnData = new + { + url = (model.saveKey || string.IsNullOrEmpty(fileKey)) ? fullUrl : fullUrl + "#" + fileKey, + fileName = upload.Url, + contentType = upload.ContentType, + contentLength = upload.ContentLength, + key = fileKey, + keySize = upload.KeySize, + iv = upload.IV, + blockSize = upload.BlockSize, + deletionKey = upload.DeleteKey + + }; + return Json(new { result = returnData }); } + return Json(new { error = new { message = "Unable to save file" } }); + } else { @@ -186,42 +198,39 @@ namespace Teknik.Areas.API.Controllers [HttpPost] [AllowAnonymous] [TrackPageView] - public ActionResult Paste(APIv1PasteModel model) + public IActionResult Paste(APIv1PasteModel model) { try { if (model != null && model.code != null) { - using (TeknikEntities db = new TeknikEntities()) + Paste.Models.Paste paste = PasteHelper.CreatePaste(_config, _dbContext, model.code, model.title, model.syntax, model.expireUnit, model.expireLength, model.password, model.hide); + + // Associate this with the user if they are logged in + if (User.Identity.IsAuthenticated) { - Paste.Models.Paste paste = PasteHelper.CreatePaste(db, model.code, model.title, model.syntax, model.expireUnit, model.expireLength, model.password, model.hide); - - // Associate this with the user if they are logged in - if (User.Identity.IsAuthenticated) + User foundUser = UserHelper.GetUser(_dbContext, User.Identity.Name); + if (foundUser != null) { - User foundUser = UserHelper.GetUser(db, User.Identity.Name); - if (foundUser != null) - { - paste.UserId = foundUser.UserId; - } + paste.UserId = foundUser.UserId; } - - db.Pastes.Add(paste); - db.SaveChanges(); - - return Json(new - { - result = new - { - id = paste.Url, - url = Url.SubRouteUrl("p", "Paste.View", new { type = "Full", url = paste.Url, password = model.password }), - title = paste.Title, - syntax = paste.Syntax, - expiration = paste.ExpireDate, - password = model.password - } - }); } + + _dbContext.Pastes.Add(paste); + _dbContext.SaveChanges(); + + return Json(new + { + result = new + { + id = paste.Url, + url = Url.SubRouteUrl("p", "Paste.View", new { type = "Full", url = paste.Url, password = model.password }), + title = paste.Title, + syntax = paste.Syntax, + expiration = paste.ExpireDate, + password = model.password + } + }); } return Json(new { error = new { message = "Invalid Paste Request" } }); } @@ -234,44 +243,41 @@ namespace Teknik.Areas.API.Controllers [HttpPost] [AllowAnonymous] [TrackPageView] - public ActionResult Shorten(APIv1ShortenModel model) + public IActionResult Shorten(APIv1ShortenModel model) { try { if (model.url.IsValidUrl()) { - using (TeknikEntities db = new TeknikEntities()) + ShortenedUrl newUrl = ShortenerHelper.ShortenUrl(_dbContext, model.url, _config.ShortenerConfig.UrlLength); + + // Associate this with the user if they are logged in + if (User.Identity.IsAuthenticated) { - ShortenedUrl newUrl = Shortener.Shortener.ShortenUrl(db, model.url, Config.ShortenerConfig.UrlLength); - - // Associate this with the user if they are logged in - if (User.Identity.IsAuthenticated) + User foundUser = UserHelper.GetUser(_dbContext, User.Identity.Name); + if (foundUser != null) { - User foundUser = UserHelper.GetUser(db, User.Identity.Name); - if (foundUser != null) - { - newUrl.UserId = foundUser.UserId; - } + newUrl.UserId = foundUser.UserId; } - - db.ShortenedUrls.Add(newUrl); - db.SaveChanges(); - - string shortUrl = string.Format("{0}://{1}/{2}", HttpContext.Request.Url.Scheme, Config.ShortenerConfig.ShortenerHost, newUrl.ShortUrl); - if (Config.DevEnvironment) - { - shortUrl = Url.SubRouteUrl("shortened", "Shortener.View", new { url = newUrl.ShortUrl }); - } - - return Json(new - { - result = new - { - shortUrl = shortUrl, - originalUrl = model.url - } - }); } + + _dbContext.ShortenedUrls.Add(newUrl); + _dbContext.SaveChanges(); + + string shortUrl = string.Format("{0}://{1}/{2}", HttpContext.Request.Scheme, _config.ShortenerConfig.ShortenerHost, newUrl.ShortUrl); + if (_config.DevEnvironment) + { + shortUrl = Url.SubRouteUrl("shortened", "Shortener.View", new { url = newUrl.ShortUrl }); + } + + return Json(new + { + result = new + { + shortUrl = shortUrl, + originalUrl = model.url + } + }); } return Json(new { error = new { message = "Must be a valid Url" } }); } diff --git a/Teknik/Areas/API/Models/APIv1PasteModel.cs b/Teknik/Areas/API/Models/APIv1PasteModel.cs index f743388..2914e3b 100644 --- a/Teknik/Areas/API/Models/APIv1PasteModel.cs +++ b/Teknik/Areas/API/Models/APIv1PasteModel.cs @@ -1,14 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; - -namespace Teknik.Areas.API.Models +namespace Teknik.Areas.API.Models { public class APIv1PasteModel : APIv1BaseModel { - [AllowHtml] public string code { get; set; } public string title { get; set; } diff --git a/Teknik/Areas/API/Models/APIv1UploadModel.cs b/Teknik/Areas/API/Models/APIv1UploadModel.cs index e9e8000..57bf3fe 100644 --- a/Teknik/Areas/API/Models/APIv1UploadModel.cs +++ b/Teknik/Areas/API/Models/APIv1UploadModel.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.AspNetCore.Http; +using System; using System.Collections.Generic; using System.Linq; using System.Web; @@ -7,7 +8,7 @@ namespace Teknik.Areas.API.Models { public class APIv1UploadModel : APIv1BaseModel { - public HttpPostedFileWrapper file { get; set; } + public IFormFile file { get; set; } public string contentType { get; set; } diff --git a/Teknik/Areas/API/Views/web.config b/Teknik/Areas/API/Views/web.config deleted file mode 100644 index 013d8bf..0000000 --- a/Teknik/Areas/API/Views/web.config +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Teknik/Areas/About/AboutAreaRegistration.cs b/Teknik/Areas/About/AboutAreaRegistration.cs deleted file mode 100644 index afccc06..0000000 --- a/Teknik/Areas/About/AboutAreaRegistration.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using Teknik.Configuration; - -namespace Teknik.Areas.About -{ - public class AboutAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "About"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - context.MapSubdomainRoute( - "About.Index", // Route name - new List() { "about" }, - new List() { config.Host }, - "", // URL with parameters - new { controller = "About", action = "Index" }, // Parameter defaults - new[] { typeof(Controllers.AboutController).Namespace } - ); - } - } -} \ No newline at end of file diff --git a/Teknik/Areas/About/Controllers/AboutController.cs b/Teknik/Areas/About/Controllers/AboutController.cs index c140f75..d561326 100644 --- a/Teknik/Areas/About/Controllers/AboutController.cs +++ b/Teknik/Areas/About/Controllers/AboutController.cs @@ -1,23 +1,31 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; -using System.Web.Mvc; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Teknik.Areas.About.ViewModels; using Teknik.Attributes; +using Teknik.Configuration; using Teknik.Controllers; +using Teknik.Data; using Teknik.Filters; +using Teknik.Logging; namespace Teknik.Areas.About.Controllers { [TeknikAuthorize] + [Area("About")] public class AboutController : DefaultController { + public AboutController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [AllowAnonymous] [TrackPageView] - public ActionResult Index() + public IActionResult Index([FromServices] Config config) { - ViewBag.Title = "About - " + Config.Title; + ViewBag.Title = "About - " + config.Title; ViewBag.Description = "What is Teknik?"; return View(new AboutViewModel()); diff --git a/Teknik/Areas/About/ViewModels/AboutViewModel.cs b/Teknik/Areas/About/ViewModels/AboutViewModel.cs index 108b5f1..577ac42 100644 --- a/Teknik/Areas/About/ViewModels/AboutViewModel.cs +++ b/Teknik/Areas/About/ViewModels/AboutViewModel.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Web; +using System.Threading.Tasks; using Teknik.ViewModels; namespace Teknik.Areas.About.ViewModels @@ -9,4 +9,4 @@ namespace Teknik.Areas.About.ViewModels public class AboutViewModel : ViewModelBase { } -} \ No newline at end of file +} diff --git a/Teknik/Areas/About/Views/About/Index.cshtml b/Teknik/Areas/About/Views/About/Index.cshtml index 313d628..3f6906d 100644 --- a/Teknik/Areas/About/Views/About/Index.cshtml +++ b/Teknik/Areas/About/Views/About/Index.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.About.ViewModels.AboutViewModel -@using Teknik.Utilities - @{ string lifetimeAccount = "The account will not get deleted for inactivity as defined in the ToS"; string apiAccountAccess = "Passing user credentials allows linking for Uploads/Pastes/Shortened URLs and other account specific features"; @@ -35,7 +33,7 @@ - $@Model.Config.UserConfig.PremiumAccountPrice @Model.Config.UserConfig.PaymentType + $@Config.UserConfig.PremiumAccountPrice @Config.UserConfig.PaymentType Free Free @@ -113,15 +111,15 @@ Max Upload Filesize - @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSizePremium) - @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSizeBasic) - @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSize) + @StringHelper.GetBytesReadable(Config.UploadConfig.MaxUploadSizePremium) + @StringHelper.GetBytesReadable(Config.UploadConfig.MaxUploadSizeBasic) + @StringHelper.GetBytesReadable(Config.UploadConfig.MaxUploadSize) Max Embedded Filesize Unlimited - @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxDownloadSize) - @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxDownloadSize) + @StringHelper.GetBytesReadable(Config.UploadConfig.MaxDownloadSize) + @StringHelper.GetBytesReadable(Config.UploadConfig.MaxDownloadSize) Url Shortening diff --git a/Teknik/Areas/About/Views/web.config b/Teknik/Areas/About/Views/web.config deleted file mode 100644 index 013d8bf..0000000 --- a/Teknik/Areas/About/Views/web.config +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Teknik/Areas/Abuse/AbuseAreaRegistration.cs b/Teknik/Areas/Abuse/AbuseAreaRegistration.cs deleted file mode 100644 index af8798c..0000000 --- a/Teknik/Areas/Abuse/AbuseAreaRegistration.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using Teknik.Configuration; - -namespace Teknik.Areas.Abuse -{ - public class AbuseAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "Abuse"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - context.MapSubdomainRoute( - "Abuse.Index", // Route name - new List() { "abuse" }, // Subdomains - new List() { config.Host }, // domains - "", // URL with parameters - new { controller = "Abuse", action = "Index" }, // Parameter defaults - new[] { typeof(Controllers.AbuseController).Namespace } - ); - } - } -} diff --git a/Teknik/Areas/Abuse/Controllers/AbuseController.cs b/Teknik/Areas/Abuse/Controllers/AbuseController.cs index c3e0e54..ce91654 100644 --- a/Teknik/Areas/Abuse/Controllers/AbuseController.cs +++ b/Teknik/Areas/Abuse/Controllers/AbuseController.cs @@ -1,23 +1,31 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Web; -using System.Web.Mvc; using Teknik.Areas.Abuse.ViewModels; using Teknik.Attributes; +using Teknik.Configuration; using Teknik.Controllers; +using Teknik.Data; using Teknik.Filters; +using Teknik.Logging; namespace Teknik.Areas.Abuse.Controllers { [TeknikAuthorize] + [Area("Abuse")] public class AbuseController : DefaultController { + public AbuseController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [TrackPageView] [AllowAnonymous] - public ActionResult Index() + public IActionResult Index() { - ViewBag.Title = "Abuse Reporting - " + Config.Title; + ViewBag.Title = "Abuse Reporting - " + _config.Title; ViewBag.Description = "Methods for reporting abuse reports to Teknik Services."; return View(new AbuseViewModel()); diff --git a/Teknik/Areas/Abuse/Views/Abuse/Index.cshtml b/Teknik/Areas/Abuse/Views/Abuse/Index.cshtml index 82eaa8d..29f0737 100644 --- a/Teknik/Areas/Abuse/Views/Abuse/Index.cshtml +++ b/Teknik/Areas/Abuse/Views/Abuse/Index.cshtml @@ -1,4 +1,3 @@ -@using Teknik.Utilities @model Teknik.Areas.Abuse.ViewModels.AbuseViewModel
diff --git a/Teknik/Areas/Abuse/Views/_ViewStart.cshtml b/Teknik/Areas/Abuse/Views/_ViewStart.cshtml deleted file mode 100644 index 2de6241..0000000 --- a/Teknik/Areas/Abuse/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "~/Views/Shared/_Layout.cshtml"; -} diff --git a/Teknik/Areas/Abuse/Views/web.config b/Teknik/Areas/Abuse/Views/web.config deleted file mode 100644 index ada5aaf..0000000 --- a/Teknik/Areas/Abuse/Views/web.config +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Teknik/Areas/Accounts/ResourceOwnerPasswordValidator.cs b/Teknik/Areas/Accounts/ResourceOwnerPasswordValidator.cs new file mode 100644 index 0000000..4dd731f --- /dev/null +++ b/Teknik/Areas/Accounts/ResourceOwnerPasswordValidator.cs @@ -0,0 +1,36 @@ +using IdentityServer4.Validation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Teknik.Areas.Users.Models; +using Teknik.Areas.Users.Utility; +using Teknik.Configuration; +using Teknik.Data; + +namespace Teknik.Areas.Accounts +{ + public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator + { + private readonly TeknikEntities _dbContext; + private readonly Config _config; + + public ResourceOwnerPasswordValidator(TeknikEntities dbContext, Config config) + { + _dbContext = dbContext; + _config = config; + } + + public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) + { + // Get the user + string username = context.UserName; + User user = UserHelper.GetUser(_dbContext, context.UserName); + if (user != null) + { + bool userValid = UserHelper.UserPasswordCorrect(_dbContext, _config, user, context.Password); + } + return null; + } + } +} diff --git a/Teknik/Areas/Admin/AdminAreaRegistration.cs b/Teknik/Areas/Admin/AdminAreaRegistration.cs deleted file mode 100644 index 822a9f7..0000000 --- a/Teknik/Areas/Admin/AdminAreaRegistration.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using System.Web.Optimization; -using Teknik.Configuration; -using Teknik.Utilities; - -namespace Teknik.Areas.Admin -{ - public class AdminAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "Admin"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - context.MapSubdomainRoute( - "Admin.Dashboard", // Route name - new List() { "admin" }, // Subdomains - new List() { config.Host }, - "", // URL with parameters - new { controller = "Admin", action = "Dashboard" }, // Parameter defaults - new[] { typeof(Controllers.AdminController).Namespace } - ); - context.MapSubdomainRoute( - "Admin.UserSearch", // Route name - new List() { "admin" }, // Subdomains - new List() { config.Host }, - "Search/Users", // URL with parameters - new { controller = "Admin", action = "UserSearch" }, // Parameter defaults - new[] { typeof(Controllers.AdminController).Namespace } - ); - context.MapSubdomainRoute( - "Admin.UploadSearch", // Route name - new List() { "admin" }, // Subdomains - new List() { config.Host }, - "Search/Uploads", // URL with parameters - new { controller = "Admin", action = "UploadSearch" }, // Parameter defaults - new[] { typeof(Controllers.AdminController).Namespace } - ); - context.MapSubdomainRoute( - "Admin.UserInfo", // Route name - new List() { "admin" }, // Subdomains - new List() { config.Host }, - "User/{username}", // URL with parameters - new { controller = "Admin", action = "UserInfo", username = string.Empty }, // Parameter defaults - new[] { typeof(Controllers.AdminController).Namespace } - ); - context.MapSubdomainRoute( - "Admin.Action", // Route name - new List() { "admin" }, // Subdomains - new List() { config.Host }, - "Action/{controller}/{action}", // URL with parameters - new { controller = "Admin", action = "Dashboard" }, // Parameter defaults - new[] { typeof(Controllers.AdminController).Namespace } - ); - - // Register Script Bundles - BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/UserSearch", config.CdnHost).Include( - "~/Areas/Admin/Scripts/UserSearch.js")); - - // Register Script Bundles - BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/UploadSearch", config.CdnHost).Include( - "~/Scripts/bootbox/bootbox.min.js", - "~/Areas/Admin/Scripts/UploadSearch.js")); - - BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/UserInfo", config.CdnHost).Include( - "~/Scripts/bootbox/bootbox.min.js", - "~/Areas/Admin/Scripts/UserInfo.js")); - } - } -} diff --git a/Teknik/Areas/Admin/Controllers/AdminController.cs b/Teknik/Areas/Admin/Controllers/AdminController.cs index f3b5706..d91e577 100644 --- a/Teknik/Areas/Admin/Controllers/AdminController.cs +++ b/Teknik/Areas/Admin/Controllers/AdminController.cs @@ -1,24 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ViewEngines; +using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; -using System.Web; -using System.Web.Mvc; +using System.Threading.Tasks; using Teknik.Areas.Admin.ViewModels; using Teknik.Areas.Users.Models; using Teknik.Areas.Users.Utility; using Teknik.Attributes; +using Teknik.Configuration; using Teknik.Controllers; +using Teknik.Data; using Teknik.Filters; using Teknik.Models; using Teknik.Utilities; using Teknik.ViewModels; +using Teknik.Logging; namespace Teknik.Areas.Admin.Controllers { [TeknikAuthorize(Roles = "Admin")] + [Area("Admin")] public class AdminController : DefaultController { - private TeknikEntities db = new TeknikEntities(); + public AdminController(ILogger logger, Config config, TeknikEntities dbContext) : base (logger, config, dbContext) { } [HttpGet] [TrackPageView] @@ -40,9 +46,9 @@ namespace Teknik.Areas.Admin.Controllers [TrackPageView] public ActionResult UserInfo(string username) { - if (UserHelper.UserExists(db, username)) + if (UserHelper.UserExists(_dbContext, username)) { - User user = UserHelper.GetUser(db, username); + User user = UserHelper.GetUser(_dbContext, username); UserInfoViewModel model = new UserInfoViewModel(); model.Username = user.Username; model.AccountType = user.AccountType; @@ -60,11 +66,11 @@ namespace Teknik.Areas.Admin.Controllers } [HttpPost] - public ActionResult GetUserSearchResults(string query) + public async Task GetUserSearchResults(string query, [FromServices] ICompositeViewEngine viewEngine) { List models = new List(); - var results = db.Users.Where(u => u.Username.Contains(query)).ToList(); + var results = _dbContext.Users.Where(u => u.Username.Contains(query)).ToList(); if (results != null) { foreach (User user in results) @@ -73,12 +79,12 @@ namespace Teknik.Areas.Admin.Controllers { UserResultViewModel model = new UserResultViewModel(); model.Username = user.Username; - if (Config.EmailConfig.Enabled) + if (_config.EmailConfig.Enabled) { - model.Email = string.Format("{0}@{1}", user.Username, Config.EmailConfig.Domain); + model.Email = string.Format("{0}@{1}", user.Username, _config.EmailConfig.Domain); } model.JoinDate = user.JoinDate; - model.LastSeen = UserHelper.GetLastAccountActivity(db, Config, user); + model.LastSeen = UserHelper.GetLastAccountActivity(_dbContext, _config, user); models.Add(model); } catch (Exception ex) @@ -88,13 +94,15 @@ namespace Teknik.Areas.Admin.Controllers } } - return Json(new { result = new { html = PartialView("~/Areas/Admin/Views/Admin/UserResults.cshtml", models).RenderToString() } }); + string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/Admin/Views/Admin/UserResults.cshtml", models); + + return Json(new { result = new { html = renderedView } }); } [HttpPost] - public ActionResult GetUploadSearchResults(string url) + public async Task GetUploadSearchResults(string url, [FromServices] ICompositeViewEngine viewEngine) { - Upload.Models.Upload foundUpload = db.Uploads.Where(u => u.Url == url).FirstOrDefault(); + Upload.Models.Upload foundUpload = _dbContext.Uploads.Where(u => u.Url == url).FirstOrDefault(); if (foundUpload != null) { UploadResultViewModel model = new UploadResultViewModel(); @@ -106,7 +114,9 @@ namespace Teknik.Areas.Admin.Controllers model.Downloads = foundUpload.Downloads; model.DeleteKey = foundUpload.DeleteKey; - return Json(new { result = new { html = PartialView("~/Areas/Admin/Views/Admin/UploadResult.cshtml", model).RenderToString() } }); + string renderedView = await RenderPartialViewToString(viewEngine, "~/Areas/Admin/Views/Admin/UploadResult.cshtml", model); + + return Json(new { result = new { html = renderedView } }); } return Json(new { error = new { message = "Upload does not exist" } }); } @@ -115,10 +125,10 @@ namespace Teknik.Areas.Admin.Controllers [ValidateAntiForgeryToken] public ActionResult EditUserAccountType(string username, AccountType accountType) { - if (UserHelper.UserExists(db, username)) + if (UserHelper.UserExists(_dbContext, username)) { // Edit the user's account type - UserHelper.EditAccountType(db, Config, username, accountType); + UserHelper.EditAccountType(_dbContext, _config, username, accountType); return Json(new { result = new { success = true } }); } return Redirect(Url.SubRouteUrl("error", "Error.Http404")); @@ -128,10 +138,10 @@ namespace Teknik.Areas.Admin.Controllers [ValidateAntiForgeryToken] public ActionResult EditUserAccountStatus(string username, AccountStatus accountStatus) { - if (UserHelper.UserExists(db, username)) + if (UserHelper.UserExists(_dbContext, username)) { // Edit the user's account type - UserHelper.EditAccountStatus(db, Config, username, accountStatus); + UserHelper.EditAccountStatus(_dbContext, _config, username, accountStatus); return Json(new { result = new { success = true } }); } return Redirect(Url.SubRouteUrl("error", "Error.Http404")); @@ -141,15 +151,15 @@ namespace Teknik.Areas.Admin.Controllers [ValidateAntiForgeryToken] public ActionResult CreateInviteCode(string username) { - if (UserHelper.UserExists(db, username)) + if (UserHelper.UserExists(_dbContext, username)) { - User user = UserHelper.GetUser(db, username); - InviteCode inviteCode = db.InviteCodes.Create(); + User user = UserHelper.GetUser(_dbContext, username); + InviteCode inviteCode = new InviteCode(); inviteCode.Active = true; inviteCode.Code = Guid.NewGuid().ToString(); inviteCode.Owner = user; - db.InviteCodes.Add(inviteCode); - db.SaveChanges(); + _dbContext.InviteCodes.Add(inviteCode); + _dbContext.SaveChanges(); return Json(new { result = new { code = inviteCode.Code } }); } @@ -162,14 +172,11 @@ namespace Teknik.Areas.Admin.Controllers { try { - using (TeknikEntities db = new TeknikEntities()) + User user = UserHelper.GetUser(_dbContext, username); + if (user != null) { - User user = UserHelper.GetUser(db, username); - if (user != null) - { - UserHelper.DeleteAccount(db, Config, user); - return Json(new { result = true }); - } + UserHelper.DeleteAccount(_dbContext, _config, user); + return Json(new { result = true }); } } catch (Exception ex) diff --git a/Teknik/Areas/Admin/Views/Admin/Dashboard.cshtml b/Teknik/Areas/Admin/Views/Admin/Dashboard.cshtml index fea3490..1cc0ae1 100644 --- a/Teknik/Areas/Admin/Views/Admin/Dashboard.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/Dashboard.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Admin.ViewModels.DashboardViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Admin/Views/Admin/UploadResult.cshtml b/Teknik/Areas/Admin/Views/Admin/UploadResult.cshtml index 006e5a5..b0bbef9 100644 --- a/Teknik/Areas/Admin/Views/Admin/UploadResult.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UploadResult.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Admin.ViewModels.UploadResultViewModel -@using Teknik.Utilities -
@Url.SubRouteUrl("u", "Upload.Download", new { file = Model.Url }) diff --git a/Teknik/Areas/Admin/Views/Admin/UploadSearch.cshtml b/Teknik/Areas/Admin/Views/Admin/UploadSearch.cshtml index 8490e9b..5c2dbd3 100644 --- a/Teknik/Areas/Admin/Views/Admin/UploadSearch.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UploadSearch.cshtml @@ -1,23 +1,19 @@ @model Teknik.Areas.Admin.ViewModels.UploadSearchViewModel -@using Teknik.Utilities - - -@Scripts.Render("~/bundles/UploadSearch") -
-
+
-
+
@@ -26,3 +22,5 @@
+ + diff --git a/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml b/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml index 4ccf355..5429faf 100644 --- a/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UserInfo.cshtml @@ -1,8 +1,6 @@ @model Teknik.Areas.Admin.ViewModels.UserInfoViewModel -@using Teknik.Utilities - - -@Scripts.Render("~/bundles/UserInfo") -
@@ -33,7 +29,7 @@ @{ foreach (AccountType value in Enum.GetValues(typeof(AccountType))) { - + @value.ToString() } } @@ -49,7 +45,7 @@ @{ foreach (AccountStatus value in Enum.GetValues(typeof(AccountStatus))) { - + @value.ToString() } } @@ -68,3 +64,5 @@
+ + diff --git a/Teknik/Areas/Admin/Views/Admin/UserResult.cshtml b/Teknik/Areas/Admin/Views/Admin/UserResult.cshtml index b3f2995..eb1b995 100644 --- a/Teknik/Areas/Admin/Views/Admin/UserResult.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UserResult.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Admin.ViewModels.UserResultViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Admin/Views/Admin/UserResults.cshtml b/Teknik/Areas/Admin/Views/Admin/UserResults.cshtml index 9f25406..988aa39 100644 --- a/Teknik/Areas/Admin/Views/Admin/UserResults.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UserResults.cshtml @@ -10,7 +10,7 @@
foreach (var post in Model) { - @Html.Partial("UserResult", post) + @await Html.PartialAsync("UserResult", post) } } else diff --git a/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml b/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml index 41cf673..47c68fa 100644 --- a/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml +++ b/Teknik/Areas/Admin/Views/Admin/UserSearch.cshtml @@ -1,22 +1,18 @@ @model Teknik.Areas.Admin.ViewModels.UserSearchViewModel -@using Teknik.Utilities - - -@Scripts.Render("~/bundles/UserSearch") -
-
+
-
+
@@ -28,3 +24,5 @@
+ + diff --git a/Teknik/Areas/Admin/Views/_ViewStart.cshtml b/Teknik/Areas/Admin/Views/_ViewStart.cshtml deleted file mode 100644 index 2de6241..0000000 --- a/Teknik/Areas/Admin/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "~/Views/Shared/_Layout.cshtml"; -} diff --git a/Teknik/Areas/Admin/Views/web.config b/Teknik/Areas/Admin/Views/web.config deleted file mode 100644 index 013d8bf..0000000 --- a/Teknik/Areas/Admin/Views/web.config +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Teknik/Areas/Blog/BlogAreaRegistration.cs b/Teknik/Areas/Blog/BlogAreaRegistration.cs deleted file mode 100644 index 121e3a5..0000000 --- a/Teknik/Areas/Blog/BlogAreaRegistration.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using System.Web.Optimization; -using Teknik.Configuration; -using Teknik.Utilities; - -namespace Teknik.Areas.Blog -{ - public class BlogAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "Blog"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - context.MapSubdomainRoute( - "Blog.Blog", // Route name - new List() { "blog" }, // Subdomains - new List() { config.Host }, - "{username}", // URL with parameters - new { controller = "Blog", action = "Blog", username = string.Empty }, // Parameter defaults - new[] { typeof(Controllers.BlogController).Namespace } - ); - context.MapSubdomainRoute( - "Blog.New", // Route name - new List() { "blog" }, // Subdomains - new List() { config.Host }, - "{username}/New", // URL with parameters - new { controller = "Blog", action = "NewPost", username = "" }, // Parameter defaults - new[] { typeof(Controllers.BlogController).Namespace } - ); - context.MapSubdomainRoute( - "Blog.Edit", // Route name - new List() { "blog" }, // Subdomains - new List() { config.Host }, - "{username}/Edit/{id}", // URL with parameters - new { controller = "Blog", action = "EditPost", username = "", id = 0 }, // Parameter defaults - new[] { typeof(Controllers.BlogController).Namespace } - ); - context.MapSubdomainRoute( - "Blog.Post", // Route name - new List() { "blog" }, // Subdomains - new List() { config.Host }, - "{username}/p/{id}", // URL with parameters - new { controller = "Blog", action = "Post", username = "", id = 0 }, // Parameter defaults - new[] { typeof(Controllers.BlogController).Namespace } - ); - context.MapSubdomainRoute( - "Blog.Action", // Route name - new List() { "blog" }, // Subdomains - new List() { config.Host }, - "Action/{controller}/{action}", // URL with parameters - new { controller = "Blog", action = "Blog" }, // Parameter defaults - new[] { typeof(Controllers.BlogController).Namespace } - ); - - // Register Script Bundles - BundleTable.Bundles.Add(new CdnScriptBundle("~/bundles/blog", config.CdnHost).Include( - "~/Scripts/bootbox/bootbox.min.js", - "~/Scripts/MarkdownDeepLib.min.js", - "~/Areas/Blog/Scripts/Blog.js")); - // Register Style Bundles - BundleTable.Bundles.Add(new CdnStyleBundle("~/Content/blog", config.CdnHost).Include( - "~/Content/mdd_styles.css", - "~/Areas/Blog/Content/Blog.css")); - } - } -} \ No newline at end of file diff --git a/Teknik/Areas/Blog/Controllers/BlogController.cs b/Teknik/Areas/Blog/Controllers/BlogController.cs index ec59d8f..cea7938 100644 --- a/Teknik/Areas/Blog/Controllers/BlogController.cs +++ b/Teknik/Areas/Blog/Controllers/BlogController.cs @@ -1,11 +1,9 @@ using System; using System.Collections.Generic; using System.Data; -using System.Data.Entity; using System.Linq; using System.Net; using System.Web; -using System.Web.Mvc; using Teknik.Areas.Blog.Models; using Teknik.Areas.Blog.ViewModels; using Teknik.Areas.Users.Models; @@ -15,61 +13,103 @@ using Teknik.Filters; using Teknik.Utilities; using Teknik.Models; using Teknik.Attributes; +using Microsoft.Extensions.Logging; +using Teknik.Configuration; +using Teknik.Data; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Teknik.Logging; namespace Teknik.Areas.Blog.Controllers { [TeknikAuthorize] + [Area("Blog")] public class BlogController : DefaultController { - private TeknikEntities db = new TeknikEntities(); - - // GET: Blogs/Details/5 + public BlogController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [TrackPageView] [AllowAnonymous] - public ActionResult Blog(string username) + public IActionResult Blog(string username) { BlogViewModel model = new BlogViewModel(); // The blog is the main site's blog if (string.IsNullOrEmpty(username)) { - ViewBag.Title = Config.BlogConfig.Title + " - " + Config.Title; - ViewBag.Description = Config.BlogConfig.Description; + ViewBag.Title = _config.BlogConfig.Title + " - " + _config.Title; + ViewBag.Description = _config.BlogConfig.Description; bool isAuth = User.IsInRole("Admin"); - var foundPosts = db.BlogPosts.Where(p => ((p.System || isAuth) && p.Published)); model = new BlogViewModel(); - model.BlogId = Config.BlogConfig.ServerBlogId; + model.BlogId = _config.BlogConfig.ServerBlogId; - User user = (User.IsInRole("Admin")) ? UserHelper.GetUser(db, User.Identity.Name) : null; + User user = (User.IsInRole("Admin")) ? UserHelper.GetUser(_dbContext, User.Identity.Name) : null; model.UserId = (user != null) ? user.UserId : 0; model.User = user; - model.Title = Config.BlogConfig.Title; - model.Description = Config.BlogConfig.Description; - model.HasPosts = (foundPosts != null && foundPosts.Any()); + model.Title = _config.BlogConfig.Title; + model.Description = _config.BlogConfig.Description; + var posts = _dbContext.BlogPosts + .Include(p => p.Blog) + .Include(p => p.Blog.User) + .Include(p => p.Comments) + .Include(p => p.Tags) + .Where(p => (p.System || isAuth) && p.Published).OrderByDescending(p => p.DatePosted) + .OrderByDescending(p => p.DatePosted) + .Take(_config.BlogConfig.PostsToLoad).ToList(); + + List postViews = new List(); + if (posts != null) + { + foreach (BlogPost post in posts) + { + postViews.Add(new PostViewModel(post)); + } + } + model.Posts = postViews; return View(model); } else // A user specific blog { - Models.Blog blog = db.Blogs.Where(p => p.User.Username == username && p.BlogId != Config.BlogConfig.ServerBlogId).FirstOrDefault(); + Models.Blog blog = _dbContext.Blogs + .Include(b => b.User) + .Include(b => b.User.BlogSettings) + .Where(p => p.User.Username == username && p.BlogId != _config.BlogConfig.ServerBlogId) + .FirstOrDefault(); // find the blog specified if (blog != null) { - ViewBag.Title = blog.User.Username + "'s Blog - " + Config.Title; + ViewBag.Title = blog.User.Username + "'s Blog - " + _config.Title; if (!string.IsNullOrEmpty(blog.User.BlogSettings.Title)) { ViewBag.Title = blog.User.BlogSettings.Title + " - " + ViewBag.Title; } ViewBag.Description = blog.User.BlogSettings.Description; bool isAuth = User.IsInRole("Admin"); - var foundPosts = db.BlogPosts.Where(p => (p.BlogId == blog.BlogId && !p.System) && - (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)).FirstOrDefault(); model = new BlogViewModel(); model.BlogId = blog.BlogId; model.UserId = blog.UserId; model.User = blog.User; model.Title = blog.User.BlogSettings.Title; model.Description = blog.User.BlogSettings.Description; - model.HasPosts = (foundPosts != null); + var posts = _dbContext.BlogPosts + .Include(p => p.Blog) + .Include(p => p.Blog.User) + .Include(p => p.Comments) + .Include(p => p.Tags) + .Where(p => (p.BlogId == blog.BlogId && !p.System) && (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)) + .OrderByDescending(p => p.DatePosted) + .Take(_config.BlogConfig.PostsToLoad).ToList(); + + List postViews = new List(); + if (posts != null) + { + foreach (BlogPost post in posts) + { + postViews.Add(new PostViewModel(post)); + } + } + model.Posts = postViews; return View(model); } @@ -81,28 +121,34 @@ namespace Teknik.Areas.Blog.Controllers #region Posts [TrackPageView] [AllowAnonymous] - public ActionResult Post(string username, int id) + public IActionResult Post(string username, int id) { if (string.IsNullOrEmpty(username)) { - return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + return new StatusCodeResult((int)HttpStatusCode.BadRequest); } PostViewModel model = new PostViewModel(); // find the post specified bool isAuth = User.IsInRole("Admin"); - var post = db.BlogPosts.Where(p => p.BlogPostId == id && (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)).FirstOrDefault(); + var post = _dbContext.BlogPosts + .Include(p => p.Blog) + .Include(p => p.Blog.User) + .Include(p => p.Comments) + .Include(p => p.Tags) + .Where(p => p.BlogPostId == id && (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)) + .FirstOrDefault(); if (post != null) { model = new PostViewModel(post); if (post.System) { - ViewBag.Title = model.Title + " - " + Config.BlogConfig.Title + " - " + Config.Title; - ViewBag.Description = Config.BlogConfig.Description; + ViewBag.Title = model.Title + " - " + _config.BlogConfig.Title + " - " + _config.Title; + ViewBag.Description = _config.BlogConfig.Description; } else { - ViewBag.Title = username + "'s Blog - " + Config.Title; + ViewBag.Title = username + "'s Blog - " + _config.Title; if (!string.IsNullOrEmpty(post.Blog.User.BlogSettings.Title)) { ViewBag.Title = post.Blog.User.BlogSettings.Title + " - " + ViewBag.Title; @@ -117,27 +163,31 @@ namespace Teknik.Areas.Blog.Controllers return View("~/Areas/Blog/Views/Blog/ViewPost.cshtml", model); } - public ActionResult NewPost(string username, int blogID) + public IActionResult NewPost(string username, int blogID) { if (string.IsNullOrEmpty(username)) { - return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + return new StatusCodeResult((int)HttpStatusCode.BadRequest); } BlogViewModel model = new BlogViewModel(); // find the post specified bool isAuth = User.IsInRole("Admin"); - var blog = db.Blogs.Where(p => (p.BlogId == blogID) && (p.User.Username == User.Identity.Name || isAuth)).FirstOrDefault(); + var blog = _dbContext.Blogs + .Include(b => b.User) + .Include(b => b.User.BlogSettings) + .Where(p => (p.BlogId == blogID) && (p.User.Username == User.Identity.Name || isAuth)) + .FirstOrDefault(); if (blog != null) { model = new BlogViewModel(blog); if (blog.User.Username == Constants.SERVERUSER) { - ViewBag.Title = "Create Post - " + Config.BlogConfig.Title + " - " + Config.Title; - ViewBag.Description = Config.BlogConfig.Description; + ViewBag.Title = "Create Post - " + _config.BlogConfig.Title + " - " + _config.Title; + ViewBag.Description = _config.BlogConfig.Description; } else { - ViewBag.Title = username + "'s Blog - " + Config.Title; + ViewBag.Title = username + "'s Blog - " + _config.Title; if (!string.IsNullOrEmpty(blog.User.BlogSettings.Title)) { ViewBag.Title = blog.User.BlogSettings.Title + " - " + ViewBag.Title; @@ -151,29 +201,36 @@ namespace Teknik.Areas.Blog.Controllers model.ErrorMessage = "Blog does not exist."; return View("~/Areas/Blog/Views/Blog/Blog.cshtml", model); } - public ActionResult EditPost(string username, int id) + + public IActionResult EditPost(string username, int id) { if (string.IsNullOrEmpty(username)) { - return new HttpStatusCodeResult(HttpStatusCode.BadRequest); + return new StatusCodeResult((int)HttpStatusCode.BadRequest); } PostViewModel model = new PostViewModel(); // find the post specified bool isAuth = User.IsInRole("Admin"); - var post = db.BlogPosts.Where(p => (p.Blog.User.Username == username && p.BlogPostId == id) && - (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)).FirstOrDefault(); + var post = _dbContext.BlogPosts + .Include(p => p.Blog) + .Include(p => p.Blog.User) + .Include(p => p.Comments) + .Include(p => p.Tags) + .Where(p => (p.Blog.User.Username == username && p.BlogPostId == id) && + (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)) + .FirstOrDefault(); if (post != null) { model = new PostViewModel(post); if (post.System) { - ViewBag.Title = "Edit Post - " + model.Title + " - " + Config.BlogConfig.Title + " - " + Config.Title; - ViewBag.Description = Config.BlogConfig.Description; + ViewBag.Title = "Edit Post - " + model.Title + " - " + _config.BlogConfig.Title + " - " + _config.Title; + ViewBag.Description = _config.BlogConfig.Description; } else { - ViewBag.Title = username + "'s Blog - " + Config.Title; + ViewBag.Title = username + "'s Blog - " + _config.Title; if (!string.IsNullOrEmpty(post.Blog.User.BlogSettings.Title)) { ViewBag.Title = post.Blog.User.BlogSettings.Title + " - " + ViewBag.Title; @@ -190,11 +247,20 @@ namespace Teknik.Areas.Blog.Controllers [HttpPost] [AllowAnonymous] - public ActionResult GetPosts(int blogID, int startPostID, int count) + public IActionResult GetPosts(int blogID, int startPostID, int count) { bool isAuth = User.IsInRole("Admin"); - var posts = db.BlogPosts.Where(p => ((p.BlogId == blogID && !p.System) || (p.System && blogID == Config.BlogConfig.ServerBlogId)) && - (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)).OrderByDescending(p => p.DatePosted).Skip(startPostID).Take(count).ToList(); + var posts = _dbContext.BlogPosts + .Include(p => p.Blog) + .Include(p => p.Blog.User) + .Include(p => p.Comments) + .Include(p => p.Tags) + .Where(p => ((p.BlogId == blogID && !p.System) || (p.System && blogID == _config.BlogConfig.ServerBlogId)) && + (p.Published || p.Blog.User.Username == User.Identity.Name || isAuth)) + .OrderByDescending(p => p.DatePosted) + .Skip(startPostID) + .Take(count) + .ToList(); List postViews = new List(); if (posts != null) { @@ -207,16 +273,16 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult CreatePost(CreatePostViewModel data) + public IActionResult CreatePost(CreatePostViewModel data) { BlogViewModel model = new BlogViewModel(); if (ModelState.IsValid) { bool isAuth = User.IsInRole("Admin"); - var blog = db.Blogs.Where(p => (p.BlogId == data.BlogId) && (p.User.Username == User.Identity.Name || isAuth)).FirstOrDefault(); + var blog = _dbContext.Blogs.Where(p => (p.BlogId == data.BlogId) && (p.User.Username == User.Identity.Name || isAuth)).FirstOrDefault(); if (blog != null) { - if (User.IsInRole("Admin") || db.Blogs.Where(b => b.User.Username == User.Identity.Name).FirstOrDefault() != null) + if (User.IsInRole("Admin") || _dbContext.Blogs.Where(b => b.User.Username == User.Identity.Name).FirstOrDefault() != null) { // Validate the fields if (string.IsNullOrEmpty(data.Title)) @@ -233,16 +299,16 @@ namespace Teknik.Areas.Blog.Controllers return View("~/Areas/Blog/Views/Blog/NewPost.cshtml", model); } - bool system = (data.BlogId == Config.BlogConfig.ServerBlogId); + bool system = (data.BlogId == _config.BlogConfig.ServerBlogId); if (system) { - var user = db.Blogs.Where(b => b.User.Username == User.Identity.Name); + var user = _dbContext.Blogs.Where(b => b.User.Username == User.Identity.Name); if (user != null) { data.BlogId = user.First().BlogId; } } - BlogPost post = db.BlogPosts.Create(); + BlogPost post = new BlogPost(); post.BlogId = data.BlogId; post.Title = data.Title; post.Article = data.Article; @@ -251,8 +317,8 @@ namespace Teknik.Areas.Blog.Controllers post.DatePublished = DateTime.Now; post.DateEdited = DateTime.Now; - db.BlogPosts.Add(post); - db.SaveChanges(); + _dbContext.BlogPosts.Add(post); + _dbContext.SaveChanges(); return Redirect(Url.SubRouteUrl("blog", "Blog.Post", new { username = blog.User.Username, id = post.BlogPostId })); } model.Error = true; @@ -269,12 +335,12 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult EditPost(EditPostViewModel data) + public IActionResult EditPost(EditPostViewModel data) { PostViewModel model = new PostViewModel(); if (ModelState.IsValid) { - BlogPost post = db.BlogPosts.Where(p => p.BlogPostId == data.PostId).FirstOrDefault(); + BlogPost post = _dbContext.BlogPosts.Where(p => p.BlogPostId == data.PostId).FirstOrDefault(); if (post != null) { model = new PostViewModel(post); @@ -298,8 +364,8 @@ namespace Teknik.Areas.Blog.Controllers post.Title = data.Title; post.Article = data.Article; post.DateEdited = DateTime.Now; - db.Entry(post).State = EntityState.Modified; - db.SaveChanges(); + _dbContext.Entry(post).State = EntityState.Modified; + _dbContext.SaveChanges(); return Redirect(Url.SubRouteUrl("blog", "Blog.Post", new { username = post.Blog.User.Username, id = post.BlogPostId })); } model.Error = true; @@ -316,11 +382,11 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult PublishPost(int postID, bool publish) + public IActionResult PublishPost(int postID, bool publish) { if (ModelState.IsValid) { - BlogPost post = db.BlogPosts.Where(p => p.BlogPostId == postID).FirstOrDefault(); + BlogPost post = _dbContext.BlogPosts.Where(p => p.BlogPostId == postID).FirstOrDefault(); if (post != null) { if (User.IsInRole("Admin") || post.Blog.User.Username == User.Identity.Name) @@ -328,8 +394,8 @@ namespace Teknik.Areas.Blog.Controllers post.Published = publish; if (publish) post.DatePublished = DateTime.Now; - db.Entry(post).State = EntityState.Modified; - db.SaveChanges(); + _dbContext.Entry(post).State = EntityState.Modified; + _dbContext.SaveChanges(); return Json(new { result = true }); } return Json(new { error = "You are not authorized to publish this post" }); @@ -340,17 +406,17 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult DeletePost(int postID) + public IActionResult DeletePost(int postID) { if (ModelState.IsValid) { - BlogPost post = db.BlogPosts.Where(p => p.BlogPostId == postID).FirstOrDefault(); + BlogPost post = _dbContext.BlogPosts.Where(p => p.BlogPostId == postID).FirstOrDefault(); if (post != null) { if (User.IsInRole("Admin") || post.Blog.User.Username == User.Identity.Name) { - db.BlogPosts.Remove(post); - db.SaveChanges(); + _dbContext.BlogPosts.Remove(post); + _dbContext.SaveChanges(); return Json(new { result = true }); } return Json(new { error = "You are not authorized to delete this post" }); @@ -364,9 +430,9 @@ namespace Teknik.Areas.Blog.Controllers #region Comments [HttpPost] [AllowAnonymous] - public ActionResult GetComments(int postID, int startCommentID, int count) + public IActionResult GetComments(int postID, int startCommentID, int count) { - var comments = db.BlogComments.Where(p => (p.BlogPostId == postID)).OrderByDescending(p => p.DatePosted).Skip(startCommentID).Take(count).ToList(); + var comments = _dbContext.BlogPostComments.Where(p => (p.BlogPostId == postID)).OrderByDescending(p => p.DatePosted).Skip(startCommentID).Take(count).ToList(); List commentViews = new List(); if (comments != null) { @@ -380,9 +446,9 @@ namespace Teknik.Areas.Blog.Controllers [HttpPost] [AllowAnonymous] - public ActionResult GetCommentArticle(int commentID) + public IActionResult GetCommentArticle(int commentID) { - BlogPostComment comment = db.BlogComments.Where(p => (p.BlogPostCommentId == commentID)).First(); + BlogPostComment comment = _dbContext.BlogPostComments.Where(p => (p.BlogPostCommentId == commentID)).First(); if (comment != null) { return Json(new { result = comment.Article }); @@ -391,21 +457,21 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult CreateComment(int postID, string article) + public IActionResult CreateComment(int postID, string article) { if (ModelState.IsValid) { - if (db.BlogPosts.Where(p => p.BlogPostId == postID).FirstOrDefault() != null) + if (_dbContext.BlogPosts.Where(p => p.BlogPostId == postID).FirstOrDefault() != null) { - BlogPostComment comment = db.BlogComments.Create(); + BlogPostComment comment = new BlogPostComment(); comment.BlogPostId = postID; - comment.UserId = UserHelper.GetUser(db, User.Identity.Name).UserId; + comment.UserId = UserHelper.GetUser(_dbContext, User.Identity.Name).UserId; comment.Article = article; comment.DatePosted = DateTime.Now; comment.DateEdited = DateTime.Now; - db.BlogComments.Add(comment); - db.SaveChanges(); + _dbContext.BlogPostComments.Add(comment); + _dbContext.SaveChanges(); return Json(new { result = true }); } return Json(new { error = "The post does not exist" }); @@ -414,19 +480,19 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult EditComment(int commentID, string article) + public IActionResult EditComment(int commentID, string article) { if (ModelState.IsValid) { - BlogPostComment comment = db.BlogComments.Where(c => c.BlogPostCommentId == commentID).FirstOrDefault(); + BlogPostComment comment = _dbContext.BlogPostComments.Where(c => c.BlogPostCommentId == commentID).FirstOrDefault(); if (comment != null) { if (comment.User.Username == User.Identity.Name || User.IsInRole("Admin")) { comment.Article = article; comment.DateEdited = DateTime.Now; - db.Entry(comment).State = EntityState.Modified; - db.SaveChanges(); + _dbContext.Entry(comment).State = EntityState.Modified; + _dbContext.SaveChanges(); return Json(new { result = true }); } return Json(new { error = "You don't have permission to edit this comment" }); @@ -437,17 +503,17 @@ namespace Teknik.Areas.Blog.Controllers } [HttpPost] - public ActionResult DeleteComment(int commentID) + public IActionResult DeleteComment(int commentID) { if (ModelState.IsValid) { - BlogPostComment comment = db.BlogComments.Where(c => c.BlogPostCommentId == commentID).FirstOrDefault(); + BlogPostComment comment = _dbContext.BlogPostComments.Where(c => c.BlogPostCommentId == commentID).FirstOrDefault(); if (comment != null) { if (comment.User.Username == User.Identity.Name || User.IsInRole("Admin")) { - db.BlogComments.Remove(comment); - db.SaveChanges(); + _dbContext.BlogPostComments.Remove(comment); + _dbContext.SaveChanges(); return Json(new { result = true }); } return Json(new { error = "You don't have permission to delete this comment" }); diff --git a/Teknik/Areas/Blog/Models/BlogPost.cs b/Teknik/Areas/Blog/Models/BlogPost.cs index 846138a..d6bd7f3 100644 --- a/Teknik/Areas/Blog/Models/BlogPost.cs +++ b/Teknik/Areas/Blog/Models/BlogPost.cs @@ -26,7 +26,7 @@ namespace Teknik.Areas.Blog.Models public string Article { get; set; } - public List Tags { get; set; } + public virtual ICollection Tags { get; set; } public virtual ICollection Comments { get; set; } } diff --git a/Teknik/Areas/Blog/Models/BlogPostTag.cs b/Teknik/Areas/Blog/Models/BlogPostTag.cs new file mode 100644 index 0000000..020e122 --- /dev/null +++ b/Teknik/Areas/Blog/Models/BlogPostTag.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Teknik.Areas.Blog.Models +{ + public class BlogPostTag + { + public int BlogPostTagId { get; set; } + + public int BlogPostId { get; set; } + + public virtual BlogPost BlogPost { get; set; } + + public string Name { get; set; } + + public string Description { get; set; } + } +} diff --git a/Teknik/Areas/Blog/ViewModels/BlogViewModel.cs b/Teknik/Areas/Blog/ViewModels/BlogViewModel.cs index 58804b3..a8947b4 100644 --- a/Teknik/Areas/Blog/ViewModels/BlogViewModel.cs +++ b/Teknik/Areas/Blog/ViewModels/BlogViewModel.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; using Teknik.Areas.Blog.Models; using Teknik.Areas.Users.Models; using Teknik.ViewModels; @@ -21,7 +17,15 @@ namespace Teknik.Areas.Blog.ViewModels public User User { get; set; } - public bool HasPosts { get; set; } + public bool HasPosts + { + get + { + return Posts != null && Posts.Count > 0; + } + } + + public List Posts { get; set; } public BlogViewModel() { @@ -35,7 +39,7 @@ namespace Teknik.Areas.Blog.ViewModels Title = blog.User.BlogSettings.Title; Description = blog.User.BlogSettings.Description; User = blog.User; - HasPosts = false; + Posts = new List(); } } } diff --git a/Teknik/Areas/Blog/ViewModels/CommentViewModel.cs b/Teknik/Areas/Blog/ViewModels/CommentViewModel.cs index ddadb4f..32254ec 100644 --- a/Teknik/Areas/Blog/ViewModels/CommentViewModel.cs +++ b/Teknik/Areas/Blog/ViewModels/CommentViewModel.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; using Teknik.Areas.Blog.Models; using Teknik.Areas.Users.Models; using Teknik.ViewModels; diff --git a/Teknik/Areas/Blog/ViewModels/CreatePostViewModel.cs b/Teknik/Areas/Blog/ViewModels/CreatePostViewModel.cs index 1194df4..54e1359 100644 --- a/Teknik/Areas/Blog/ViewModels/CreatePostViewModel.cs +++ b/Teknik/Areas/Blog/ViewModels/CreatePostViewModel.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using Teknik.ViewModels; +using Teknik.ViewModels; namespace Teknik.Areas.Blog.ViewModels { @@ -12,8 +7,7 @@ namespace Teknik.Areas.Blog.ViewModels public int BlogId { get; set; } public string Title { get; set; } - - [AllowHtml] + public string Article { get; set; } } } \ No newline at end of file diff --git a/Teknik/Areas/Blog/ViewModels/EditPostViewModel.cs b/Teknik/Areas/Blog/ViewModels/EditPostViewModel.cs index a65fe47..8d6f244 100644 --- a/Teknik/Areas/Blog/ViewModels/EditPostViewModel.cs +++ b/Teknik/Areas/Blog/ViewModels/EditPostViewModel.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using Teknik.ViewModels; +using Teknik.ViewModels; namespace Teknik.Areas.Blog.ViewModels { @@ -12,8 +7,7 @@ namespace Teknik.Areas.Blog.ViewModels public int PostId { get; set; } public string Title { get; set; } - - [AllowHtml] + public string Article { get; set; } } } \ No newline at end of file diff --git a/Teknik/Areas/Blog/ViewModels/PostViewModel.cs b/Teknik/Areas/Blog/ViewModels/PostViewModel.cs index 1bfd542..cfecaf9 100644 --- a/Teknik/Areas/Blog/ViewModels/PostViewModel.cs +++ b/Teknik/Areas/Blog/ViewModels/PostViewModel.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using Teknik.Areas.Blog.Models; -using System.Security.Principal; using System.Linq; -using System.Web; using Teknik.ViewModels; namespace Teknik.Areas.Blog.ViewModels @@ -30,7 +28,7 @@ namespace Teknik.Areas.Blog.ViewModels public string Article { get; set; } - public List Tags { get; set; } + public List Tags { get; set; } public List Comments { get; set; } @@ -50,7 +48,7 @@ namespace Teknik.Areas.Blog.ViewModels DatePublished = post.DatePublished; DateEdited = post.DateEdited; Title = post.Title; - Tags = post.Tags; + Tags = post.Tags.ToList(); Article = post.Article; Comments = post.Comments.ToList(); } diff --git a/Teknik/Areas/Blog/Views/Blog/Blog.cshtml b/Teknik/Areas/Blog/Views/Blog/Blog.cshtml index ec93366..53047ec 100644 --- a/Teknik/Areas/Blog/Views/Blog/Blog.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/Blog.cshtml @@ -1,8 +1,6 @@ @model Teknik.Areas.Blog.ViewModels.BlogViewModel -@using Teknik.Utilities - - -@Styles.Render("~/Content/blog") -@Scripts.Render("~/bundles/blog") +
@if (!Model.Error) @@ -33,7 +30,7 @@

- +

@@ -46,14 +43,25 @@
} + + if (Model.HasPosts) { -
- } else diff --git a/Teknik/Areas/Blog/Views/Blog/Comment.cshtml b/Teknik/Areas/Blog/Views/Blog/Comment.cshtml index c7b45c0..0e2d733 100644 --- a/Teknik/Areas/Blog/Views/Blog/Comment.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/Comment.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Blog.ViewModels.CommentViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Blog/Views/Blog/Comments.cshtml b/Teknik/Areas/Blog/Views/Blog/Comments.cshtml index 75e1b66..f054cb8 100644 --- a/Teknik/Areas/Blog/Views/Blog/Comments.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/Comments.cshtml @@ -2,5 +2,5 @@ @foreach (var comment in Model) { - @Html.Partial("Comment", comment) + @await Html.PartialAsync("Comment", comment) } diff --git a/Teknik/Areas/Blog/Views/Blog/EditPost.cshtml b/Teknik/Areas/Blog/Views/Blog/EditPost.cshtml index 0b9e168..d3bf5f0 100644 --- a/Teknik/Areas/Blog/Views/Blog/EditPost.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/EditPost.cshtml @@ -1,14 +1,11 @@ @model Teknik.Areas.Blog.ViewModels.PostViewModel -@using Teknik.Utilities - - -@Styles.Render("~/Content/blog") -@Scripts.Render("~/bundles/blog") +
@if (Model.Error) @@ -25,23 +22,33 @@

Edit Post

-
+
-
+
+
+
+
+
-
-
-
- -
- +
+
+
-
+
+
+
Article
+
+
+ +
+
+
+
Preview
@@ -50,10 +57,7 @@
-
-
- -
-
- +
+ + diff --git a/Teknik/Areas/Blog/Views/Blog/NewPost.cshtml b/Teknik/Areas/Blog/Views/Blog/NewPost.cshtml index bb8707a..320fbac 100644 --- a/Teknik/Areas/Blog/Views/Blog/NewPost.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/NewPost.cshtml @@ -1,14 +1,11 @@ @model Teknik.Areas.Blog.ViewModels.BlogViewModel -@using Teknik.Utilities - - -@Styles.Render("~/Content/blog") -@Scripts.Render("~/bundles/blog") +
@if (Model.Error) @@ -25,23 +22,33 @@

Create a New Post

-
+
-
+
+
+
+
+
-
-
-
- -
- +
+
+
-
+
+
+
Article
+
+
+ +
+
+
+
Preview
@@ -50,10 +57,7 @@
-
-
- -
-
- +
+ + diff --git a/Teknik/Areas/Blog/Views/Blog/Post.cshtml b/Teknik/Areas/Blog/Views/Blog/Post.cshtml index 575cecf..1a231eb 100644 --- a/Teknik/Areas/Blog/Views/Blog/Post.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/Post.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Blog.ViewModels.PostViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Blog/Views/Blog/Posts.cshtml b/Teknik/Areas/Blog/Views/Blog/Posts.cshtml index a15e8f5..538c886 100644 --- a/Teknik/Areas/Blog/Views/Blog/Posts.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/Posts.cshtml @@ -2,5 +2,5 @@ @foreach (var post in Model) { - @Html.Partial("Post", post) + @await Html.PartialAsync("Post", post) } \ No newline at end of file diff --git a/Teknik/Areas/Blog/Views/Blog/ViewPost.cshtml b/Teknik/Areas/Blog/Views/Blog/ViewPost.cshtml index 506108b..7f4c06c 100644 --- a/Teknik/Areas/Blog/Views/Blog/ViewPost.cshtml +++ b/Teknik/Areas/Blog/Views/Blog/ViewPost.cshtml @@ -1,8 +1,6 @@ @model Teknik.Areas.Blog.ViewModels.PostViewModel -@using Teknik.Utilities - - -@Styles.Render("~/Content/blog") -@Scripts.Render("~/bundles/blog") + +
@if (!Model.Error) { - @Html.Partial("../../Areas/Blog/Views/Blog/Post", Model) + @await Html.PartialAsync("../../Areas/Blog/Views/Blog/Post", Model) - if (Request.IsAuthenticated) + if (User.Identity.IsAuthenticated) {
@@ -42,7 +40,7 @@ }
- @@ -50,3 +47,5 @@
+ + diff --git a/Teknik/Areas/Error/Views/Error/Http401.cshtml b/Teknik/Areas/Error/Views/Error/Http401.cshtml index 316a2c7..9a17c10 100644 --- a/Teknik/Areas/Error/Views/Error/Http401.cshtml +++ b/Teknik/Areas/Error/Views/Error/Http401.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Error.ViewModels.ErrorViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Error/Views/Error/Http403.cshtml b/Teknik/Areas/Error/Views/Error/Http403.cshtml index 0c96ccf..ad3c47c 100644 --- a/Teknik/Areas/Error/Views/Error/Http403.cshtml +++ b/Teknik/Areas/Error/Views/Error/Http403.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Error.ViewModels.ErrorViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Error/Views/Error/Http404.cshtml b/Teknik/Areas/Error/Views/Error/Http404.cshtml index 83c0f7d..e2a7b60 100644 --- a/Teknik/Areas/Error/Views/Error/Http404.cshtml +++ b/Teknik/Areas/Error/Views/Error/Http404.cshtml @@ -1,7 +1,5 @@ @model Teknik.Areas.Error.ViewModels.ErrorViewModel -@using Teknik.Utilities -
diff --git a/Teknik/Areas/Error/Views/Error/Http500.cshtml b/Teknik/Areas/Error/Views/Error/Http500.cshtml index 64d37a3..79d1aee 100644 --- a/Teknik/Areas/Error/Views/Error/Http500.cshtml +++ b/Teknik/Areas/Error/Views/Error/Http500.cshtml @@ -1,9 +1,6 @@ @model Teknik.Areas.Error.ViewModels.ErrorViewModel -@using Teknik.Utilities - -@Scripts.Render("~/bundles/error") - @@ -52,4 +49,4 @@
- + \ No newline at end of file diff --git a/Teknik/Areas/Error/Views/Error/General.cshtml b/Teknik/Areas/Error/Views/Error/HttpGeneral.cshtml similarity index 84% rename from Teknik/Areas/Error/Views/Error/General.cshtml rename to Teknik/Areas/Error/Views/Error/HttpGeneral.cshtml index ec40e28..600ed01 100644 --- a/Teknik/Areas/Error/Views/Error/General.cshtml +++ b/Teknik/Areas/Error/Views/Error/HttpGeneral.cshtml @@ -1,13 +1,11 @@ @model Teknik.Areas.Error.ViewModels.ErrorViewModel -@using Teknik.Utilities -
-

Http Exception

-

Status Code: @((Model.Exception as HttpException).GetHttpCode())

+

Http Error

+

Status Code: @Model.StatusCode

Sorry, an error has occured: @Model.Description
diff --git a/Teknik/Areas/Error/Views/_ViewStart.cshtml b/Teknik/Areas/Error/Views/_ViewStart.cshtml deleted file mode 100644 index 2de6241..0000000 --- a/Teknik/Areas/Error/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "~/Views/Shared/_Layout.cshtml"; -} diff --git a/Teknik/Areas/Error/Views/web.config b/Teknik/Areas/Error/Views/web.config deleted file mode 100644 index 013d8bf..0000000 --- a/Teknik/Areas/Error/Views/web.config +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Teknik/Areas/FAQ/Controllers/FAQController.cs b/Teknik/Areas/FAQ/Controllers/FAQController.cs index f95abda..25fbb5a 100644 --- a/Teknik/Areas/FAQ/Controllers/FAQController.cs +++ b/Teknik/Areas/FAQ/Controllers/FAQController.cs @@ -1,23 +1,27 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Teknik.Areas.FAQ.ViewModels; using Teknik.Attributes; +using Teknik.Configuration; using Teknik.Controllers; +using Teknik.Data; using Teknik.Filters; +using Teknik.Logging; namespace Teknik.Areas.FAQ.Controllers { [TeknikAuthorize] + [Area("FAQ")] public class FAQController : DefaultController { + public FAQController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [TrackPageView] [AllowAnonymous] - public ActionResult Index() + public IActionResult Index() { - ViewBag.Title = "Frequently Asked Questions - " + Config.Title; + ViewBag.Title = "Frequently Asked Questions - " + _config.Title; FAQViewModel model = new FAQViewModel(); return View(model); } diff --git a/Teknik/Areas/FAQ/FAQAreaRegistration.cs b/Teknik/Areas/FAQ/FAQAreaRegistration.cs deleted file mode 100644 index 6426e63..0000000 --- a/Teknik/Areas/FAQ/FAQAreaRegistration.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using System.Web.Optimization; -using Teknik.Configuration; -using Teknik.Utilities; - -namespace Teknik.Areas.FAQ -{ - public class FAQAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "FAQ"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - context.MapSubdomainRoute( - "FAQ.Index", // Route name - new List() { "faq" }, - new List() { config.Host }, - "", // URL with parameters - new { controller = "FAQ", action = "Index" }, // Parameter defaults - new[] { typeof(Controllers.FAQController).Namespace } - ); - - // Register Style Bundle - BundleTable.Bundles.Add(new CdnStyleBundle("~/Content/faq", config.CdnHost).Include( - "~/Areas/FAQ/Content/FAQ.css")); - } - } -} \ No newline at end of file diff --git a/Teknik/Areas/FAQ/Views/FAQ/Index.cshtml b/Teknik/Areas/FAQ/Views/FAQ/Index.cshtml index 84e4d60..3ecb075 100644 --- a/Teknik/Areas/FAQ/Views/FAQ/Index.cshtml +++ b/Teknik/Areas/FAQ/Views/FAQ/Index.cshtml @@ -1,8 +1,6 @@ @model Teknik.Areas.FAQ.ViewModels.FAQViewModel -@using Teknik.Utilities - -@Styles.Render("~/Content/faq") +
@@ -42,7 +40,7 @@
- You can contact us via the Contact page or email us at @Model.Config.SupportEmail. + You can contact us via the Contact page or email us at @Config.SupportEmail.
@@ -121,7 +119,7 @@
- The max file size for everyone is @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSize), basic accounts are @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSizeBasic), and Premium accounts are @StringHelper.GetBytesReadable(Model.Config.UploadConfig.MaxUploadSizePremium). + The max file size for everyone is @StringHelper.GetBytesReadable(Config.UploadConfig.MaxUploadSize), basic accounts are @StringHelper.GetBytesReadable(Config.UploadConfig.MaxUploadSizeBasic), and Premium accounts are @StringHelper.GetBytesReadable(Config.UploadConfig.MaxUploadSizePremium).
diff --git a/Teknik/Areas/FAQ/Views/_ViewStart.cshtml b/Teknik/Areas/FAQ/Views/_ViewStart.cshtml deleted file mode 100644 index 2de6241..0000000 --- a/Teknik/Areas/FAQ/Views/_ViewStart.cshtml +++ /dev/null @@ -1,3 +0,0 @@ -@{ - Layout = "~/Views/Shared/_Layout.cshtml"; -} diff --git a/Teknik/Areas/FAQ/Views/web.config b/Teknik/Areas/FAQ/Views/web.config deleted file mode 100644 index 013d8bf..0000000 --- a/Teknik/Areas/FAQ/Views/web.config +++ /dev/null @@ -1,36 +0,0 @@ - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Teknik/Areas/Help/Controllers/HelpController.cs b/Teknik/Areas/Help/Controllers/HelpController.cs index 0f1b1ce..816403c 100644 --- a/Teknik/Areas/Help/Controllers/HelpController.cs +++ b/Teknik/Areas/Help/Controllers/HelpController.cs @@ -1,123 +1,126 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; using Teknik.Areas.Help.ViewModels; using Teknik.Attributes; +using Teknik.Configuration; using Teknik.Controllers; +using Teknik.Data; using Teknik.Filters; +using Teknik.Logging; namespace Teknik.Areas.Help.Controllers { [TeknikAuthorize] + [Area("Help")] public class HelpController : DefaultController { - // GET: Help/Help + public HelpController(ILogger logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { } + [TrackPageView] [AllowAnonymous] - public ActionResult Index() + public IActionResult Index() { - ViewBag.Title = "Help - " + Config.Title; + ViewBag.Title = "Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View(model); } [TrackPageView] [AllowAnonymous] - public ActionResult API(string version, string service) + public IActionResult API(string version, string service) { HelpViewModel model = new HelpViewModel(); if (string.IsNullOrEmpty(version) && string.IsNullOrEmpty(service)) { - ViewBag.Title = "API Help - " + Config.Title; + ViewBag.Title = "API Help - " + _config.Title; return View("~/Areas/Help/Views/Help/API/API.cshtml", model); } else if(!string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(service)) { - ViewBag.Title = service + " API " + version + " Help - " + Config.Title; + ViewBag.Title = service + " API " + version + " Help - " + _config.Title; return View("~/Areas/Help/Views/Help/API/" + version + "/" + service + ".cshtml", model); } - return RedirectToRoute("*.Error.Http404"); + return RedirectToRoute("Error.Http404"); } [TrackPageView] [AllowAnonymous] - public ActionResult Blog() + public IActionResult Blog() { - ViewBag.Title = "Blogging Help - " + Config.Title; + ViewBag.Title = "Blogging Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Blog.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult Git() + public IActionResult Git() { - ViewBag.Title = "Git Service Help - " + Config.Title; + ViewBag.Title = "Git Service Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Git.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult IRC() + public IActionResult IRC() { - ViewBag.Title = "IRC Server Help - " + Config.Title; + ViewBag.Title = "IRC Server Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/IRC.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult Mail() + public IActionResult Mail() { - ViewBag.Title = "Mail Server Help - " + Config.Title; + ViewBag.Title = "Mail Server Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Mail.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult Markdown() + public IActionResult Markdown() { - ViewBag.Title = "Markdown Help - " + Config.Title; + ViewBag.Title = "Markdown Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Markdown.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult Mumble() + public IActionResult Mumble() { - ViewBag.Title = "Mumble Server Help - " + Config.Title; + ViewBag.Title = "Mumble Server Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Mumble.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult RSS() + public IActionResult RSS() { - ViewBag.Title = "RSS Help - " + Config.Title; + ViewBag.Title = "RSS Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/RSS.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult Tools() + public IActionResult Tools() { - ViewBag.Title = "Tool Help - " + Config.Title; + ViewBag.Title = "Tool Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Tools.cshtml", model); } [TrackPageView] [AllowAnonymous] - public ActionResult Upload() + public IActionResult Upload() { - ViewBag.Title = "Upload Service Help - " + Config.Title; + ViewBag.Title = "Upload Service Help - " + _config.Title; HelpViewModel model = new HelpViewModel(); return View("~/Areas/Help/Views/Help/Upload.cshtml", model); } diff --git a/Teknik/Areas/Help/HelpAreaRegistration.cs b/Teknik/Areas/Help/HelpAreaRegistration.cs deleted file mode 100644 index 23218ca..0000000 --- a/Teknik/Areas/Help/HelpAreaRegistration.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using System.Web.Optimization; -using Teknik.Configuration; -using Teknik.Utilities; - -namespace Teknik.Areas.Help -{ - public class HelpAreaRegistration : AreaRegistration - { - public override string AreaName - { - get - { - return "Help"; - } - } - - public override void RegisterArea(AreaRegistrationContext context) - { - Config config = Config.Load(); - context.MapSubdomainRoute( - "Help.Index", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "", // URL with parameters - new { controller = "Help", action = "Index" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.API", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "API/{version}/{service}", // URL with parameters - new { controller = "Help", action = "API", version = UrlParameter.Optional, service = UrlParameter.Optional }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Blog", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Blog", // URL with parameters - new { controller = "Help", action = "Blog" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Git", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Git", // URL with parameters - new { controller = "Help", action = "Git" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.IRC", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "IRC", // URL with parameters - new { controller = "Help", action = "IRC" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Mail", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Mail", // URL with parameters - new { controller = "Help", action = "Mail" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Markdown", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Markdown", // URL with parameters - new { controller = "Help", action = "Markdown" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Mumble", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Mumble", // URL with parameters - new { controller = "Help", action = "Mumble" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.RSS", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "RSS", // URL with parameters - new { controller = "Help", action = "RSS" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Tools", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Tools", // URL with parameters - new { controller = "Help", action = "Tools" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - context.MapSubdomainRoute( - "Help.Upload", // Route name - new List() { "help" }, // Subdomains - new List() { config.Host }, // domains - "Upload", // URL with parameters - new { controller = "Help", action = "Upload" }, // Parameter defaults - new[] { typeof(Controllers.HelpController).Namespace } - ); - - // Register Style Bundles - BundleTable.Bundles.Add(new CdnStyleBundle("~/Content/help", config.CdnHost).Include( - "~/Areas/Help/Content/Help.css")); - } - } -} \ No newline at end of file diff --git a/Teknik/Areas/Help/Views/Help/API/API.cshtml b/Teknik/Areas/Help/Views/Help/API/API.cshtml index 6784879..ac2649e 100644 --- a/Teknik/Areas/Help/Views/Help/API/API.cshtml +++ b/Teknik/Areas/Help/Views/Help/API/API.cshtml @@ -1,8 +1,6 @@ @model Teknik.Areas.Help.ViewModels.HelpViewModel -@using Teknik.Utilities - -@Styles.Render("~/Content/help"); +