├── _config.yml ├── src ├── DemoModules │ ├── BookLibrary │ │ ├── Content │ │ │ └── module.css │ │ ├── Views │ │ │ ├── _ViewStart.cshtml │ │ │ └── Book │ │ │ │ └── AvailableBooks.cshtml │ │ ├── Dtos │ │ │ ├── ReturnBookDTO.cs │ │ │ └── RentBookDTO.cs │ │ ├── ModuleDefiniation.cs │ │ ├── ViewModels │ │ │ ├── BookListViewModel.cs │ │ │ └── BookDetailsViewModel.cs │ │ ├── Migrations │ │ │ └── Migration.1.0.0.cs │ │ ├── BookLibrary.csproj │ │ ├── DAL │ │ │ └── BookDAL.cs │ │ └── Controllers │ │ │ └── BookController.cs │ ├── DemoPlugin1 │ │ ├── Views │ │ │ ├── _ViewStart.cshtml │ │ │ └── Plugin1 │ │ │ │ └── HelloWorld.cshtml │ │ ├── Content │ │ │ └── module.css │ │ ├── plugin.json │ │ ├── Models │ │ │ ├── TestClass.cs │ │ │ └── BookViewModel.cs │ │ ├── ModuleDefiniation.cs │ │ ├── .template.config │ │ │ └── template.json │ │ ├── CoolCatModuleTemplate.nuspec │ │ ├── Migrations │ │ │ ├── Migration.1.0.0.cs │ │ │ └── Migration.1.1.0.cs │ │ ├── DemoPlugin1.csproj │ │ └── Controllers │ │ │ └── Plugin1Controller.cs │ ├── DemoPlugin2 │ │ ├── Views │ │ │ ├── Plugin2 │ │ │ │ └── HelloWorld.cshtml │ │ │ └── _ViewStart.cshtml │ │ ├── ModuleDefiniation.cs │ │ ├── LoadHelloWorldEventHandler.cs │ │ ├── DemoPlugin2.csproj │ │ ├── Controllers │ │ │ └── Plugin2Controller.cs │ │ ├── Migrations │ │ │ └── Migration.1.0.0.cs │ │ └── NotificationProvider.cs │ └── BookInventory │ │ └── BookInventory │ │ ├── Views │ │ ├── _ViewStart.cshtml │ │ └── BookInventory │ │ │ ├── Add.cshtml │ │ │ └── Books.cshtml │ │ ├── Content │ │ └── module.css │ │ ├── ModuleDefiniation.cs │ │ ├── Models │ │ └── Book.cs │ │ ├── ViewModels │ │ ├── BookListViewModel.cs │ │ └── BookDetailViewModel.cs │ │ ├── Dtos │ │ ├── UpdateBookDto.cs │ │ └── AddBookDto.cs │ │ ├── Migrations │ │ ├── Migration.1.1.0.cs │ │ └── Migration.1.0.0.cs │ │ ├── BookInEventHandler.cs │ │ ├── BookOutEventHandler.cs │ │ ├── NotificationProvider.cs │ │ ├── BookInventory.csproj │ │ ├── DataStores │ │ ├── AvailableBookQuery.cs │ │ └── BookDetailsQuery.cs │ │ ├── Controllers │ │ └── BookInventoryController.cs │ │ └── DAL │ │ └── BookDAL.cs ├── CoolCat │ ├── Views │ │ ├── _ViewStart.cshtml │ │ ├── User │ │ │ └── Index.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── Home │ │ │ └── Index.cshtml │ │ └── Shared │ │ │ ├── Error.cshtml │ │ │ ├── _CookieConsentPartial.cshtml │ │ │ └── _ValidationScriptsPartial.cshtml │ ├── Areas │ │ └── Admin │ │ │ ├── _ViewStart.cshtml │ │ │ ├── Controllers │ │ │ ├── HomeController.cs │ │ │ ├── PluginsController.cs │ │ │ └── SystemController.cs │ │ │ └── Views │ │ │ ├── Home │ │ │ └── Dashboard.cshtml │ │ │ ├── Plugins │ │ │ ├── Add.cshtml │ │ │ ├── Assemblies.cshtml │ │ │ ├── Index.cshtml │ │ │ └── Document.cshtml │ │ │ └── System │ │ │ ├── SiteSettings.cshtml │ │ │ ├── Login.cshtml │ │ │ └── Setup.cshtml │ ├── dockerfile │ ├── wwwroot │ │ ├── logo2.png │ │ ├── favicon.ico │ │ ├── logo_small.png │ │ ├── js │ │ │ ├── site.js │ │ │ └── setup.js │ │ ├── lib │ │ │ ├── jquery-validation-unobtrusive │ │ │ │ └── LICENSE.txt │ │ │ ├── jquery-validation │ │ │ │ └── LICENSE.md │ │ │ ├── bootstrap │ │ │ │ ├── LICENSE │ │ │ │ └── dist │ │ │ │ │ └── css │ │ │ │ │ └── bootstrap-reboot.min.css │ │ │ └── jquery │ │ │ │ └── LICENSE.txt │ │ └── css │ │ │ └── site.css │ ├── PresetModules │ │ ├── BookInventory.zip │ │ ├── BookLibrary.zip │ │ ├── DemoPlugin1.zip │ │ └── DemoPlugin2.zip │ ├── Models │ │ ├── SiteSettings.cs │ │ ├── SetupModulesModel.cs │ │ └── ErrorViewModel.cs │ ├── appsettings.Development.json │ ├── Controllers │ │ ├── UserController.cs │ │ ├── HomeController.cs │ │ └── CommonController.cs │ ├── appsettings.json │ ├── Dtos │ │ └── LoginDTO.cs │ ├── Utilities │ │ └── PresetPluginLoader.cs │ ├── Program.cs │ ├── CoolCat.csproj │ └── Startup.cs ├── dockerfile ├── InternalModules │ └── PluginManagement │ │ ├── Views │ │ └── _ViewStart.cshtml │ │ ├── Content │ │ └── module.css │ │ ├── plugin.json │ │ ├── ModuleDefiniation.cs │ │ ├── PluginManagement.csproj │ │ └── Controllers │ │ └── PluginController.cs ├── TestPlugins │ ├── BookLibrary │ │ └── BookLibrary.zip │ ├── DemoPlugin1 │ │ └── DemoPlugin1.zip │ ├── DemoPlugin2 │ │ └── DemoPlugin2.zip │ └── BookInventory │ │ └── BookInventory.zip ├── CoolCat.Core │ ├── Contracts │ │ ├── IAuthorization.cs │ │ ├── INotification.cs │ │ ├── IModule.cs │ │ ├── IDataStoreQuery.cs │ │ ├── ICollectibleAssemblyLoadContextProvider.cs │ │ ├── INotificationRegister.cs │ │ ├── IMigration.cs │ │ ├── IRefenerceLoader.cs │ │ ├── IMvcModuleSetup.cs │ │ ├── INotificationProvider.cs │ │ ├── IDataStore.cs │ │ ├── IDbConnectionFactory.cs │ │ ├── IDocumentation.cs │ │ ├── ISystemManager.cs │ │ ├── IReferenceContainer.cs │ │ └── IPluginManager.cs │ ├── Models │ │ ├── SiteSettings.cs │ │ ├── ConnectionStringSetting.cs │ │ └── ModuleDefiniation.cs │ ├── Consts │ │ ├── GlobalConst.cs │ │ └── SiteSettingConst.cs │ ├── DTOs │ │ ├── SiteSettingsDTO.cs │ │ └── AddPluginDTO.cs │ ├── DomainModel │ │ ├── CachedReferenceItemKey.cs │ │ ├── QueryDocumentItem.cs │ │ ├── PluginConfiguration.cs │ │ ├── ModuleBase.cs │ │ ├── DependanceItem.cs │ │ ├── BaseMigration.cs │ │ ├── Version.cs │ │ └── PluginPackage.cs │ ├── ViewModels │ │ ├── SiteSettingsViewModel.cs │ │ ├── KeyValueViewModel.cs │ │ ├── PluginViewModel.cs │ │ ├── PluginListItemViewModel.cs │ │ └── PageRouteViewModel.cs │ ├── Repositories │ │ ├── ISiteRepository.cs │ │ ├── IUnitOfWork.cs │ │ ├── Command.cs │ │ └── IPluginRepository.cs │ ├── Exceptions │ │ ├── WrongFormatConfigurationException.cs │ │ └── MissingConfigurationFileException.cs │ ├── Attributes │ │ ├── Page.cs │ │ ├── NoneRequestParameter.cs │ │ ├── ResponseType.cs │ │ └── RequestParameterType.cs │ ├── CoolCat.Core.csproj │ ├── Configurations │ │ └── DependanceConfiguration.cs │ ├── BusinessLogic │ │ └── SystemManager.cs │ ├── PluginsLoadContexts.cs │ ├── Helpers │ │ ├── DefaultReferenceContainer.cs │ │ └── DefaultReferenceLoader.cs │ └── CollectibleAssemblyLoadContext.cs ├── _config.yml ├── conf │ └── my.cnf ├── DemoReferenceLibrary │ ├── Demo.cs │ └── DemoReferenceLibrary.csproj ├── CoolCat.Database │ ├── Plugins.sql │ ├── PluginMigrations.sql │ ├── CoolCat.Database.refactorlog │ └── CoolCat.Database.sqlproj ├── CoolCat.Core.Mvc │ ├── Compiler │ │ ├── CoolCatAssemblyPart.cs │ │ ├── CoolCatModuleCompiledItemLoader.cs │ │ ├── CoolCatViewAssemblyPart.cs │ │ ├── CoolCatModuleViewCompiledItem.cs │ │ ├── CoolCatViewCompilerProvider.cs │ │ └── CoolCatViewCompiler.cs │ ├── Extensions │ │ ├── HttpRequestExtension.cs │ │ └── CollectibleAssemblyLoadContextExtension.cs │ ├── CoolCat.Core.Mvc.csproj │ ├── Infrastructure │ │ ├── CoolCatActionDescriptorChangeProvider.cs │ │ ├── CoolCatController.cs │ │ ├── CoolCatRouteConfiguration.cs │ │ ├── NotificationRegister.cs │ │ ├── DefaultDataStore.cs │ │ ├── ServiceCollectionExtensions.cs │ │ ├── CoolCatModuleDocumentation.cs │ │ └── CoolCatStartup.cs │ └── MvcModuleSetup.cs ├── .dockerignore ├── CoolCat.Core.Test │ ├── CoolCat.Core.Test.csproj │ └── VersionTest.cs ├── docker-compose.yml └── CoolCat.Core.Repository.MySql │ ├── Migrations │ ├── 202007262334.cs │ ├── 202007202310.cs │ └── 202005282045.cs │ ├── CoolCat.Core.Repository.MySql.csproj │ ├── MySqlConnectionFactory.cs │ ├── SiteRepository.cs │ └── UnitOfWork.cs ├── images └── logo.png ├── doc ├── images │ ├── logo.png │ ├── load_way.png │ ├── logo_small.png │ └── 20200726215825.png └── Question.md ├── init └── init.sql ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Content/module.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/CoolCat/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } 4 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/images/logo.png -------------------------------------------------------------------------------- /src/dockerfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/dockerfile -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_AdminLayout"; 3 | } 4 | -------------------------------------------------------------------------------- /doc/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/doc/images/logo.png -------------------------------------------------------------------------------- /doc/images/load_way.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/doc/images/load_way.png -------------------------------------------------------------------------------- /src/CoolCat/dockerfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/dockerfile -------------------------------------------------------------------------------- /doc/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/doc/images/logo_small.png -------------------------------------------------------------------------------- /doc/images/20200726215825.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/doc/images/20200726215825.png -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/logo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/wwwroot/logo2.png -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/Views/Plugin2/HelloWorld.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | 3 | } 4 | 5 |

@ViewBag.Content

6 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/wwwroot/favicon.ico -------------------------------------------------------------------------------- /src/InternalModules/PluginManagement/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/CoolCat/Views/User/Index.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewData["Title"] = "Index"; 4 | } 5 | 6 |

Index

7 | 8 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/wwwroot/logo_small.png -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "~/Views/Shared/_Layout.cshtml"; 3 | } -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Content/module.css: -------------------------------------------------------------------------------- 1 | .helloWorld { 2 | font-size: 14px; 3 | font-weight: bolder; 4 | } 5 | -------------------------------------------------------------------------------- /src/InternalModules/PluginManagement/Content/module.css: -------------------------------------------------------------------------------- 1 | .helloWorld { 2 | font-size: 14px; 3 | font-weight: bolder; 4 | } 5 | -------------------------------------------------------------------------------- /src/CoolCat/PresetModules/BookInventory.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/PresetModules/BookInventory.zip -------------------------------------------------------------------------------- /src/CoolCat/PresetModules/BookLibrary.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/PresetModules/BookLibrary.zip -------------------------------------------------------------------------------- /src/CoolCat/PresetModules/DemoPlugin1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/PresetModules/DemoPlugin1.zip -------------------------------------------------------------------------------- /src/CoolCat/PresetModules/DemoPlugin2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/CoolCat/PresetModules/DemoPlugin2.zip -------------------------------------------------------------------------------- /src/TestPlugins/BookLibrary/BookLibrary.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/TestPlugins/BookLibrary/BookLibrary.zip -------------------------------------------------------------------------------- /src/TestPlugins/DemoPlugin1/DemoPlugin1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/TestPlugins/DemoPlugin1/DemoPlugin1.zip -------------------------------------------------------------------------------- /src/TestPlugins/DemoPlugin2/DemoPlugin2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/TestPlugins/DemoPlugin2/DemoPlugin2.zip -------------------------------------------------------------------------------- /src/CoolCat/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using CoolCat 2 | @using CoolCat.Models 3 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 4 | -------------------------------------------------------------------------------- /src/TestPlugins/BookInventory/BookInventory.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lamondlu/CoolCat/HEAD/src/TestPlugins/BookInventory/BookInventory.zip -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IAuthorization.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface IAuthorization 4 | { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /init/init.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS plugindb; 2 | use mysql; 3 | 4 | ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'a@12345'; 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DemoPlugin1", 3 | "uniqueKey": "DemoPlugin1", 4 | "displayName": "DemoPlugin1", 5 | "version": "1.0.0" 6 | } -------------------------------------------------------------------------------- /src/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman 2 | title: Mystique 3 | description: A sample about how to create a dynamic plugins mechanism with ASP.NET Core Mvc at runtime 4 | -------------------------------------------------------------------------------- /src/CoolCat/Models/SiteSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Models 2 | { 3 | public class SiteSettings 4 | { 5 | public string MySQLConnection { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Models/TestClass.cs: -------------------------------------------------------------------------------- 1 | namespace DemoPlugin1.Models 2 | { 3 | public class TestClass 4 | { 5 | public string Message { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/INotification.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface INotificationHandler 4 | { 5 | void Handle(string data); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Models/SiteSettings.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Models 2 | { 3 | public class SiteSettings 4 | { 5 | public string MySQLConnection { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/InternalModules/PluginManagement/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "PluginManagement", 3 | "uniqueKey": "PluginManagement", 4 | "displayName": "PluginManagement", 5 | "version": "1.0.0" 6 | } -------------------------------------------------------------------------------- /src/CoolCat.Core/Models/ConnectionStringSetting.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Models 2 | { 3 | public class ConnectionStringSetting 4 | { 5 | public string ConnectionString { get; set; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IModule.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface IModule 4 | { 5 | string Name { get; } 6 | 7 | DomainModel.Version Version { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CoolCat/Models/SetupModulesModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CoolCat.Models 4 | { 5 | public class SetupModulesModel 6 | { 7 | public List Modules { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/conf/my.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | user=mysql 3 | default-storage-engine=INNODB 4 | lower_case_table_names=1 5 | character-set-server=utf8 6 | [client] 7 | default-character-set=utf8 8 | [mysql] 9 | default-character-set=utf8 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IDataStoreQuery.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface IDataStoreQuery 4 | { 5 | string QueryName { get; } 6 | 7 | string Query(string parameter); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/DemoReferenceLibrary/Demo.cs: -------------------------------------------------------------------------------- 1 | namespace DemoReferenceLibrary 2 | { 3 | public class Demo 4 | { 5 | public string SayHello() 6 | { 7 | return "Hello World. Version 2"; 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CoolCat/Models/ErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Models 2 | { 3 | public class ErrorViewModel 4 | { 5 | public string RequestId { get; set; } 6 | 7 | public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); 8 | } 9 | } -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/ICollectibleAssemblyLoadContextProvider.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface ICollectibleAssemblyLoadContextProvider 4 | { 5 | CollectibleAssemblyLoadContext Get(string moduleName); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | // for details on configuring this project to bundle and minify static web assets. 3 | 4 | // Write your JavaScript code. 5 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Consts/GlobalConst.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Consts 2 | { 3 | public static class GlobalConst 4 | { 5 | public const string PresetFolder = "PresetModules"; 6 | 7 | public const string ModulePrefix = "Modules"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DTOs/SiteSettingsDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.DTOs 4 | { 5 | public class SiteSettingsDTO 6 | { 7 | public string SiteCSS { get; set; } 8 | 9 | public Guid? SiteTemplateId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/CachedReferenceItemKey.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.DomainModel 2 | { 3 | public class CachedReferenceItemKey 4 | { 5 | public string ReferenceName { get; set; } 6 | 7 | public string Version { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Dtos/ReturnBookDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookLibrary.Dtos 4 | { 5 | public class ReturnBookDTO 6 | { 7 | public Guid RentId { get; set; } 8 | 9 | public DateTime ReturnDate { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Consts/SiteSettingConst.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Consts 2 | { 3 | public static class SiteSettingKeyConst 4 | { 5 | public const string SiteCSS = "SiteCSS"; 6 | 7 | public const string SiteTemplateId = "SiteTemplateId"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CoolCat/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information" 5 | } 6 | }, 7 | "ConnectionStringSetting": { 8 | "ConnectionString": "server=localhost;port=3306;database=plugindb;userid=root;pwd=a@12345" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CoolCat.Core/ViewModels/SiteSettingsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.ViewModels 4 | { 5 | public class SiteSettingsViewModel 6 | { 7 | public string SiteCSS { get; set; } 8 | 9 | public Guid? SiteTemplateId { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CoolCat/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace CoolCat.Controllers 4 | { 5 | public class UserController : Controller 6 | { 7 | public IActionResult Index() 8 | { 9 | return View(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CoolCat/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*", 8 | "ConnectionStringSetting": { 9 | "ConnectionString": "server=myapp_db;port=3306;database=plugindb;userid=root;pwd=a@12345" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/INotificationRegister.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface INotificationRegister 4 | { 5 | void Subscribe(string eventName, INotificationHandler handler); 6 | 7 | void Publish(string eventName, string data); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Content/module.css: -------------------------------------------------------------------------------- 1 | .out { 2 | background: #ef5a92; 3 | padding: 5px; 4 | border-radius: 3px; 5 | color: white; 6 | } 7 | 8 | .in { 9 | background: #5cbedf; 10 | padding: 5px; 11 | border-radius: 3px; 12 | color: white; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/DemoReferenceLibrary/DemoReferenceLibrary.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 1.0.2.0 6 | 1.0.2.0 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Contracts 4 | { 5 | public interface IMigration 6 | { 7 | DomainModel.Version Version { get; } 8 | 9 | void MigrateUp(Guid pluginId); 10 | 11 | void MigrateDown(Guid pluginId); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IRefenerceLoader.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace CoolCat.Core.Contracts 4 | { 5 | public interface IReferenceLoader 6 | { 7 | public void LoadStreamsIntoContext(CollectibleAssemblyLoadContext context, string moduleFolder, Assembly assembly); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IMvcModuleSetup.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Contracts 2 | { 3 | public interface IMvcModuleSetup 4 | { 5 | void DisableModule(string moduleName); 6 | 7 | 8 | void EnableModule(string moduleName); 9 | 10 | 11 | void DeleteModule(string moduleName); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/INotificationProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace CoolCat.Core.Contracts 4 | { 5 | public interface INotificationProvider 6 | { 7 | Dictionary> GetNotifications(IDbConnectionFactory dbConnectionFactory); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/QueryDocumentItem.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.DomainModel 2 | { 3 | public class QueryDocumentItem 4 | { 5 | public string QueryName { get; set; } 6 | 7 | public string RequestSample { get; set; } 8 | 9 | public string ResponseSample { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CoolCat.Database/Plugins.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[Plugins] 2 | ( 3 | [PluginId] UNIQUEIDENTIFIER NOT NULL PRIMARY KEY, 4 | [UniqueKey] NVARCHAR(50) NOT NULL, 5 | [Name] NVARCHAR(50) NOT NULL, 6 | [DisplayName] NVARCHAR(MAX) NULL, 7 | [Version] NVARCHAR(50) NOT NULL, 8 | [Enable] BIT NOT NULL 9 | ) 10 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Repositories/ISiteRepository.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.DTOs; 2 | using CoolCat.Core.ViewModels; 3 | 4 | namespace CoolCat.Core.Repositories 5 | { 6 | public interface ISiteRepository 7 | { 8 | SiteSettingsViewModel GetSiteSettings(); 9 | 10 | void SaveSiteSettings(SiteSettingsDTO dto); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/ModuleDefiniation.cs: -------------------------------------------------------------------------------- 1 | namespace BookLibrary 2 | { 3 | public class ModuleDefiniation : CoolCat.Core.Models.ModuleDefiniation 4 | { 5 | public const string MODULE_NAME = "BookLibrary"; 6 | 7 | public ModuleDefiniation() : base(MODULE_NAME) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/ModuleDefiniation.cs: -------------------------------------------------------------------------------- 1 | namespace DemoPlugin1 2 | { 3 | public class ModuleDefiniation : CoolCat.Core.Models.ModuleDefiniation 4 | { 5 | public const string MODULE_NAME = "DemoPlugin1"; 6 | 7 | public ModuleDefiniation() : base(MODULE_NAME) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/ModuleDefiniation.cs: -------------------------------------------------------------------------------- 1 | namespace DemoPlugin2 2 | { 3 | public class ModuleDefiniation : CoolCat.Core.Models.ModuleDefiniation 4 | { 5 | public const string MODULE_NAME = "DemoPlugin2"; 6 | 7 | public ModuleDefiniation() : base(MODULE_NAME) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Compiler/CoolCatAssemblyPart.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ApplicationParts; 2 | using System.Reflection; 3 | 4 | namespace CoolCat.Core.Mvc.Infrastructure 5 | { 6 | public class CoolCatAssemblyPart : AssemblyPart 7 | { 8 | public CoolCatAssemblyPart(Assembly assembly) : base(assembly) { } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Exceptions/WrongFormatConfigurationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Exceptions 4 | { 5 | public class WrongFormatConfigurationException : Exception 6 | { 7 | public WrongFormatConfigurationException() : base("The configuration file is wrong format.") 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/ModuleDefiniation.cs: -------------------------------------------------------------------------------- 1 | namespace BookInventory 2 | { 3 | public class ModuleDefiniation : CoolCat.Core.Models.ModuleDefiniation 4 | { 5 | public const string MODULE_NAME = "BookInventory"; 6 | 7 | public ModuleDefiniation() : base(MODULE_NAME) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/InternalModules/PluginManagement/ModuleDefiniation.cs: -------------------------------------------------------------------------------- 1 | namespace PluginManagement 2 | { 3 | public class ModuleDefiniation : CoolCat.Core.Models.ModuleDefiniation 4 | { 5 | public const string MODULE_NAME = "PluginManagement"; 6 | 7 | public ModuleDefiniation() : base(MODULE_NAME) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IDataStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Contracts 4 | { 5 | public interface IDataStore 6 | { 7 | string Query(string moduleName, string queryName, string parameter, string source = ""); 8 | 9 | void RegisterQuery(string moduleName, string queryName, Func query); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Exceptions/MissingConfigurationFileException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Exceptions 4 | { 5 | public class MissingConfigurationFileException : Exception 6 | { 7 | public MissingConfigurationFileException() : base("The plugin is missing the configuration file.") 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.vs 6 | **/.vscode 7 | **/*.*proj.user 8 | **/azds.yaml 9 | **/charts 10 | **/bin 11 | **/obj 12 | **/Dockerfile 13 | **/Dockerfile.develop 14 | **/docker-compose.yml 15 | **/docker-compose.*.yml 16 | **/*.dbmdl 17 | **/*.jfm 18 | **/secrets.dev.yaml 19 | **/values.dev.yaml 20 | **/.toolstarget -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IDbConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace CoolCat.Core.Contracts 9 | { 10 | public interface IDbConnectionFactory 11 | { 12 | IDbConnection GetConnection(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/PluginConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.DomainModel 2 | { 3 | public class PluginConfiguration 4 | { 5 | public string Name { get; set; } 6 | 7 | public string UniqueKey { get; set; } 8 | 9 | public string DisplayName { get; set; } 10 | 11 | public string Version { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/.template.config/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Lamond Lu", 3 | "classifications": [ "Web/Plugins" ], 4 | "name": "CoolCatModule", 5 | "identity": "CoolCatModule", 6 | "shortName": "ccm", 7 | "tags": { 8 | "language": "C#", 9 | "type": "project" 10 | }, 11 | "sourceName": "DemoPlugin1", 12 | "preferNameDirectory": true 13 | } -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Models/BookViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DemoPlugin1.Models 4 | { 5 | public class BookViewModel 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Attributes/Page.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] 6 | public class Page : Attribute 7 | { 8 | public Page(string name) 9 | { 10 | Name = name; 11 | } 12 | 13 | public string Name { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Attributes/NoneRequestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | public class NoneRequestParameterAttribute : RequestParameterTypeAttribute 7 | { 8 | public NoneRequestParameterAttribute() : base(null) 9 | { 10 | 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/CoolCat.Core/ViewModels/KeyValueViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CoolCat.Core.ViewModels 8 | { 9 | public class KeyValueViewModel 10 | { 11 | public string Key { get; set; } 12 | 13 | public string Value { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/ViewModels/BookListViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookLibrary.ViewModels 4 | { 5 | public class BookListViewModel 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IDocumentation.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.DomainModel; 2 | using System.Collections.Generic; 3 | 4 | namespace CoolCat.Core.Contracts 5 | { 6 | public interface IQueryDocumentation 7 | { 8 | void BuildDocumentation(string moduleName, IDataStoreQuery query); 9 | 10 | Dictionary> GetAllDocuments(); 11 | 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/ViewModels/BookDetailsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookLibrary.ViewModels 4 | { 5 | public class BookDetailsViewModel 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/ISystemManager.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.DTOs; 2 | using CoolCat.Core.ViewModels; 3 | 4 | namespace CoolCat.Core.Contracts 5 | { 6 | public interface ISystemManager 7 | { 8 | bool CheckInstall(); 9 | 10 | void MarkAsInstalled(); 11 | 12 | SiteSettingsViewModel GetSiteSettings(); 13 | 14 | void SaveSiteSettings(SiteSettingsDTO dto); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace CoolCat.Areas.Admin.Controllers 5 | { 6 | [Area("Admin")] 7 | [Authorize] 8 | public class HomeController : Controller 9 | { 10 | public IActionResult Dashboard() 11 | { 12 | return View(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/CoolCat/Dtos/LoginDTO.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace CoolCat 5 | { 6 | public class LoginDTO 7 | { 8 | [DisplayName("User Name")] 9 | [Required] 10 | public string UserName { get; set; } 11 | 12 | [DisplayName("User Name")] 13 | [Required] 14 | public string Password { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Dtos/RentBookDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookLibrary.Dtos 4 | { 5 | public class RentBookDTO 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | 15 | public DateTime RentDate { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Models/Book.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookInventory.Models 4 | { 5 | public class Book 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | 15 | public string Description { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Repositories/IUnitOfWork.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Repositories 2 | { 3 | public interface IUnitOfWork 4 | { 5 | IPluginRepository PluginRepository { get; } 6 | 7 | ISiteRepository SiteRepository { get; } 8 | 9 | 10 | bool CheckDatabase(); 11 | 12 | void MarkAsInstalled(); 13 | 14 | 15 | void Begin(); 16 | 17 | void RollBack(); 18 | 19 | 20 | void Commit(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/ViewModels/BookListViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookInventory.ViewModels 4 | { 5 | public class BookListViewModel 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | 15 | public bool Status { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/ViewModels/BookDetailViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace BookInventory.ViewModels 4 | { 5 | public class BookDetailViewModel 6 | { 7 | public Guid BookId { get; set; } 8 | 9 | public string BookName { get; set; } 10 | 11 | public string ISBN { get; set; } 12 | 13 | public DateTime DateIssued { get; set; } 14 | 15 | public string Description { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CoolCat.Core/ViewModels/PluginViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.ViewModels 4 | { 5 | public class PluginViewModel 6 | { 7 | public Guid PluginId { get; set; } 8 | 9 | public string UniqueKey { get; set; } 10 | 11 | public string Name { get; set; } 12 | 13 | public string DisplayName { get; set; } 14 | 15 | public string Version { get; set; } 16 | 17 | public bool IsEnable { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/CoolCatModuleTemplate.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CoolCatModule 5 | 1.0.2 6 | 7 | Creates a coolcat modules. 8 | 9 | Lamond Lu 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/CoolCat.Core/ViewModels/PluginListItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.ViewModels 4 | { 5 | public class PluginListItemViewModel 6 | { 7 | public Guid PluginId { get; set; } 8 | 9 | public string UniqueKey { get; set; } 10 | 11 | public string Name { get; set; } 12 | 13 | public string DisplayName { get; set; } 14 | 15 | public string Version { get; set; } 16 | 17 | public bool Enable { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CoolCat.Database/PluginMigrations.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[PluginMigrations] 2 | ( 3 | [PluginMigrationId] UNIQUEIDENTIFIER NOT NULL, 4 | [PluginId] UNIQUEIDENTIFIER NOT NULL , 5 | [Version] NVARCHAR(50) NOT NULL, 6 | [Up] NTEXT NOT NULL, 7 | [Down] NTEXT NULL 8 | PRIMARY KEY ([PluginMigrationId]) 9 | ) 10 | 11 | GO 12 | 13 | ALTER TABLE [dbo].[PluginMigrations] 14 | ADD CONSTRAINT FK_PluginMigrations_Plugins FOREIGN KEY(PluginId) REFERENCES [dbo].[Plugins](PluginId) 15 | 16 | GO -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/LoadHelloWorldEventHandler.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using System; 3 | 4 | namespace DemoPlugin2 5 | { 6 | public class LoadHelloWorldEventHandler : INotificationHandler 7 | { 8 | public void Handle(string data) 9 | { 10 | Console.WriteLine("Plugin2 handled hello world events." + data); 11 | } 12 | } 13 | 14 | public class LoadHelloWorldEvent 15 | { 16 | public string Str { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IReferenceContainer.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.DomainModel; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace CoolCat.Core.Contracts 6 | { 7 | public interface IReferenceContainer 8 | { 9 | List GetAll(); 10 | 11 | bool Exist(string name, string version); 12 | 13 | void SaveStream(string name, string version, Stream stream); 14 | 15 | Stream GetStream(string name, string version); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Models/ModuleDefiniation.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.Models 2 | { 3 | public abstract class ModuleDefiniation 4 | { 5 | private string _moduleName = string.Empty; 6 | 7 | public ModuleDefiniation(string moduleName) 8 | { 9 | _moduleName = moduleName; 10 | } 11 | 12 | public string ModuleName 13 | { 14 | get 15 | { 16 | return _moduleName; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Dtos/UpdateBookDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace BookInventory.Dtos 5 | { 6 | public class UpdateBookDto 7 | { 8 | [Required] 9 | public string BookName { get; set; } 10 | 11 | [Required] 12 | public string ISBN { get; set; } 13 | 14 | [Required] 15 | public DateTime DateIssued { get; set; } 16 | 17 | public string Description { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CoolCat/Utilities/PresetPluginLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace CoolCat.Utilities 7 | { 8 | public class PresetPluginLoader 9 | { 10 | public List LoadPlugins() 11 | { 12 | DirectoryInfo di = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PresetModules")); 13 | 14 | FileInfo[] files = di.GetFiles("*.zip"); 15 | 16 | return files.Select(p => p.Name).ToList(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DTOs/AddPluginDTO.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace CoolCat.Core.DTOs 5 | { 6 | public class AddPluginDTO 7 | { 8 | public AddPluginDTO() 9 | { 10 | PluginId = Guid.NewGuid(); 11 | } 12 | 13 | [JsonIgnore] 14 | public Guid PluginId { get; set; } 15 | 16 | public string UniqueKey { get; set; } 17 | 18 | public string Name { get; set; } 19 | 20 | public string DisplayName { get; set; } 21 | 22 | public string Version { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/Home/Dashboard.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | ViewData["Title"] = "Admin Home Page"; 3 | } 4 |
5 | 6 |

Welcome to CoolCat

7 | 10 | 11 |

12 | CoolCat is a framework to help you create a dynamic plugin
system under ASP.NET Core MVC. 13 |
14 | 15 |

16 |

17 | This is the admin page 18 |

19 |
20 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Dtos/AddBookDto.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace BookInventory.Dtos 5 | { 6 | public class AddBookDto 7 | { 8 | [Required] 9 | [Display(Name = "Book Name")] 10 | public string BookName { get; set; } 11 | 12 | [Required] 13 | public string ISBN { get; set; } 14 | 15 | [Required] 16 | [Display(Name = "Date Issued")] 17 | public DateTime DateIssued { get; set; } 18 | 19 | public string Description { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Repositories/Command.cs: -------------------------------------------------------------------------------- 1 | using MySql.Data.MySqlClient; 2 | using System.Collections.Generic; 3 | 4 | namespace CoolCat.Core.Repositories 5 | { 6 | public class Command 7 | { 8 | public Command() 9 | { 10 | 11 | } 12 | 13 | public Command(string sql, List parameters) 14 | { 15 | Sql = sql; 16 | Parameters = parameters; 17 | } 18 | 19 | public string Sql { get; set; } 20 | 21 | public List Parameters { get; set; } = new List(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Attributes/ResponseType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | public class ResponseTypeAttribute : Attribute 7 | { 8 | private Type _responseType; 9 | 10 | public ResponseTypeAttribute(Type responseType) 11 | { 12 | _responseType = responseType; 13 | } 14 | 15 | public Type ResponseType 16 | { 17 | get 18 | { 19 | return _responseType; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Attributes/RequestParameterType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace CoolCat.Core.Attributes 4 | { 5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 6 | public class RequestParameterTypeAttribute : Attribute 7 | { 8 | private Type _requestType; 9 | 10 | public RequestParameterTypeAttribute(Type requestType) 11 | { 12 | _requestType = requestType; 13 | } 14 | 15 | public Type RequestType 16 | { 17 | get 18 | { 19 | return _requestType; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Test/CoolCat.Core.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Extensions/HttpRequestExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace CoolCat.Core.Mvc.Extensions 7 | { 8 | public static class HttpRequestExtension 9 | { 10 | public static Stream GetPluginStream(this HttpRequest request) 11 | { 12 | if (request == null || request.Form.Files.Count == 0) 13 | { 14 | throw new Exception("The plugin package is missing."); 15 | } 16 | 17 | return request.Form.Files.First().OpenReadStream(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/Plugins/Add.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewData["Title"] = "Add"; 4 | } 5 | 6 |

Add

7 | 8 | @using (Html.BeginForm("Upload", "Plugins", FormMethod.Post, new { enctype = "multipart/form-data", @class = "form" })) 9 | { 10 |
11 | 12 | 13 | 14 | Upload a plugin package 15 | 16 |
17 | 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/CoolCat/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace CoolCat 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) 14 | { 15 | return Host.CreateDefaultBuilder(args) 16 | .ConfigureWebHostDefaults(webBuilder => 17 | { 18 | webBuilder.UseStartup(); 19 | }); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) .NET Foundation. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | these files except in compliance with the License. You may obtain a copy of the 5 | License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software distributed 10 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 11 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 12 | specific language governing permissions and limitations under the License. 13 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Contracts/IPluginManager.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.DomainModel; 2 | using CoolCat.Core.ViewModels; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace CoolCat.Core.Contracts 7 | { 8 | public interface IPluginManager 9 | { 10 | List GetAllPlugins(); 11 | 12 | void AddPlugins(PluginPackage pluginPackage); 13 | 14 | PluginViewModel GetPlugin(Guid pluginId); 15 | 16 | void DeletePlugin(Guid pluginId); 17 | 18 | void EnablePlugin(Guid pluginId); 19 | 20 | void DisablePlugin(Guid pluginId); 21 | 22 | List GetAllContexts(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/DemoPlugin2.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Test/VersionTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using Version = CoolCat.Core.DomainModel.Version; 4 | 5 | namespace CoolCat.Core.Test 6 | { 7 | [TestClass] 8 | public class VersionTest 9 | { 10 | [TestMethod] 11 | public void TestValidVersion() 12 | { 13 | Version version = new Version("1.1.1"); 14 | } 15 | 16 | [TestMethod] 17 | public void TestInvalidVersion() 18 | { 19 | Assert.ThrowsException(() => 20 | { 21 | Version version = new Version("abc"); 22 | }); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CoolCat/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | ViewData["Title"] = "Home Page"; 4 | } 5 | 6 |
7 | 8 |

Welcome to CoolCat

9 | 12 |

13 | CoolCat is a framework to help you create a dynamic plugin
system under ASP.NET Core MVC. 14 |

15 |

16 | 17 | /Admin/System/Login is the admin login page, the fake login account is admin/admin 18 |

19 |
20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/CoolCat.Core/ViewModels/PageRouteViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace CoolCat.Core.ViewModels 2 | { 3 | public class PageRouteViewModel 4 | { 5 | public PageRouteViewModel(string pageName, string area, string controller, string action) 6 | { 7 | PageName = pageName; 8 | Area = area; 9 | Controller = controller; 10 | Action = action; 11 | } 12 | 13 | public string PageName { get; set; } 14 | 15 | public string Area { get; set; } 16 | 17 | public string Controller { get; set; } 18 | 19 | public string Action { get; set; } 20 | 21 | public string Url => $"{Area}/{Controller}/{Action}"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | myapp: 4 | image: "lamondlu/coolcat:v2" 5 | container_name: app 6 | ports: 7 | - "5000:5000" 8 | hostname: myapp, 9 | links: 10 | - db 11 | depends_on: 12 | - db 13 | build: 14 | context: . 15 | dockerfile: dockerfile 16 | db: 17 | image: mysql:latest 18 | command: --default-authentication-plugin=mysql_native_password 19 | hostname: myapp_db 20 | container_name: myapp_db 21 | environment: 22 | MYSQL_ROOT_PASSWORD: a@12345 23 | MYSQL_ROOT_HOST: "%" 24 | volumes: 25 | - ./init:/docker-entrypoint-initdb.d 26 | - ./data:/var/lib/mysql 27 | - ./conf/my.cnf:/etc/my.cnf -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/System/SiteSettings.cshtml: -------------------------------------------------------------------------------- 1 | @model CoolCat.Core.ViewModels.SiteSettingsViewModel 2 | @{ 3 | 4 | } 5 | 6 |

Site Settings

7 | @using (Html.BeginForm("SiteSettings", "System", FormMethod.Post)) 8 | { 9 |
10 | 11 | @Html.DropDownListFor(p => p.SiteTemplateId, new List(), "-SELECT-", new { @class = "form-control" }) 12 |
13 |
14 | 15 | @Html.TextAreaFor(p => p.SiteCSS, 3, 50, new { @class = "form-control" }) 16 |
17 | 18 | } -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/Migrations/202007262334.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | 3 | namespace CoolCat.Core.Repository.MySql.Migrations 4 | { 5 | [Migration(202007262334)] 6 | public class AddSiteSetting : Migration 7 | { 8 | public override void Up() 9 | { 10 | Insert.IntoTable("SiteSettings").Row(new { Key = "SiteCSS", Value = "" }); 11 | Insert.IntoTable("SiteSettings").Row(new { Key = "SiteTemplateId", Value = "" }); 12 | } 13 | 14 | public override void Down() 15 | { 16 | Delete.FromTable("SiteSettings").Row(new {Key = "SiteCSS"}); 17 | Delete.FromTable("SiteSettings").Row(new {Key = "SiteTemplateId"}); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/Migrations/202007202310.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | 3 | namespace CoolCat.Core.Repository.MySql.Migrations 4 | { 5 | [Tags("System")] 6 | [Migration(202007202310)] 7 | public class AddGlobalSettings : Migration 8 | { 9 | public override void Up() 10 | { 11 | Create.Table("SiteSettings") 12 | .WithColumn("Key").AsString().PrimaryKey() 13 | .WithColumn("Value").AsCustom("text").NotNullable(); 14 | 15 | Insert.IntoTable("SiteSettings").Row(new { Key = "SYSTEM_INSTALLED", Value = "0" }); 16 | } 17 | 18 | public override void Down() 19 | { 20 | Delete.Table("SiteSettings"); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/Controllers/Plugin2Controller.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Attributes; 2 | using CoolCat.Core.Mvc.Infrastructure; 3 | using DemoReferenceLibrary; 4 | using Microsoft.AspNetCore.Mvc; 5 | 6 | namespace DemoPlugin2.Controllers 7 | { 8 | [Area("DemoPlugin2")] 9 | public class Plugin2Controller : CoolCatController 10 | { 11 | public Plugin2Controller() : base("DemoPlugin2", null) 12 | { 13 | 14 | } 15 | 16 | 17 | [Page("Plugin Two")] 18 | [HttpGet] 19 | public IActionResult HelloWorld() 20 | { 21 | string content = new Demo().SayHello() + "Version"; 22 | ViewBag.Content = content; 23 | return View(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Migrations/Migration.1.1.0.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | 4 | namespace BookInventory.Migrations 5 | { 6 | public class Migration_1_1_0 : BaseMigration 7 | { 8 | private static readonly CoolCat.Core.DomainModel.Version _version = new CoolCat.Core.DomainModel.Version("1.1.0"); 9 | 10 | public override string UpScripts => @"ALTER TABLE Book ADD COLUMN Status BIT NOT NULL DEFAULT 0"; 11 | 12 | public override string DownScripts => @"ALTER TABLE Book DROP COLUMN Status"; 13 | 14 | public Migration_1_1_0(IDbConnectionFactory dbConnectionFactory) : base(_version, dbConnectionFactory) 15 | { 16 | 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Migrations/Migration.1.0.0.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | 4 | namespace DemoPlugin1.Migrations 5 | { 6 | public class Migration_1_0_0 : BaseMigration 7 | { 8 | private static readonly CoolCat.Core.DomainModel.Version _version = new CoolCat.Core.DomainModel.Version("1.0.0"); 9 | 10 | public override string UpScripts => @"CREATE TABLE `test` ( 11 | `TestId` char(36) NOT NULL, 12 | PRIMARY KEY (`TestId`) 13 | ) ENGINE = InnoDB"; 14 | 15 | public override string DownScripts => @"DROP TABLE `test`"; 16 | 17 | public Migration_1_0_0(IDbConnectionFactory dbConnectionFactory) : base(_version, dbConnectionFactory) 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/Migrations/Migration.1.0.0.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | 4 | namespace DemoPlugin2.Migrations 5 | { 6 | public class Migration_1_0_0 : BaseMigration 7 | { 8 | private static readonly CoolCat.Core.DomainModel.Version _version = new CoolCat.Core.DomainModel.Version("1.0.0"); 9 | 10 | public Migration_1_0_0(IDbConnectionFactory dbConnectionFactory) : base(_version, dbConnectionFactory) 11 | { 12 | 13 | } 14 | 15 | public override string UpScripts => @"CREATE TABLE `test3` ( 16 | `TestId` char(36) NOT NULL, 17 | PRIMARY KEY (`TestId`) 18 | ) ENGINE = InnoDB"; 19 | 20 | public override string DownScripts => @"DROP TABLE `test3`"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin2/NotificationProvider.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using System.Collections.Generic; 3 | 4 | namespace DemoPlugin2 5 | { 6 | public class NotificationProvider : INotificationProvider 7 | { 8 | public Dictionary> GetNotifications(IDbConnectionFactory dbConnectionFactory) 9 | { 10 | List handlers = new List { new LoadHelloWorldEventHandler() }; 11 | Dictionary> result = new Dictionary> 12 | { 13 | { "LoadHelloWorldEvent", handlers } 14 | }; 15 | 16 | return result; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Migrations/Migration.1.1.0.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | 4 | namespace DemoPlugin1.Migrations 5 | { 6 | public class Migration_1_1_0 : BaseMigration 7 | { 8 | private static readonly CoolCat.Core.DomainModel.Version _version = new CoolCat.Core.DomainModel.Version("1.1.0"); 9 | 10 | 11 | public override string UpScripts => @"CREATE TABLE `test2` ( 12 | `TestId` char(36) NOT NULL, 13 | PRIMARY KEY (`TestId`) 14 | ) ENGINE = InnoDB"; 15 | 16 | public override string DownScripts => @"DROP TABLE `test2`"; 17 | 18 | public Migration_1_1_0(IDbConnectionFactory dbConnectionFactory) : base(_version, dbConnectionFactory) 19 | { 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CoolCat/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace CoolCat.Controllers 5 | { 6 | public class HomeController : Controller 7 | { 8 | private readonly ISystemManager _systemManager = null; 9 | 10 | public HomeController(ISystemManager systemManager) 11 | { 12 | _systemManager = systemManager; 13 | } 14 | 15 | public IActionResult Index() 16 | { 17 | if (_systemManager.CheckInstall()) 18 | { 19 | return View(); 20 | } 21 | else 22 | { 23 | return RedirectToAction("Setup", "System", new { Area = "Admin" }); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/System/Login.cshtml: -------------------------------------------------------------------------------- 1 | @model CoolCat.LoginDTO 2 | @{ 3 | 4 | } 5 | 6 |

Login

7 | 8 |
9 | @using (Html.BeginForm(FormMethod.Post)) 10 | { 11 | @Html.ValidationSummary(); 12 |
13 | @Html.LabelFor(p => p.UserName) 14 | @Html.TextBoxFor(p => p.UserName, new { @class = "form-control", placeholder = "User Name" }) 15 |
16 |
17 | @Html.LabelFor(p => p.Password) 18 | @Html.PasswordFor(p => p.Password, new { @class = "form-control", placeholder = "Password" }) 19 |
20 |
21 | 22 |
23 | } 24 |
25 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/CoolCat.Core.Mvc.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | true 6 | Lamond Lu 7 | Lamond Lu 8 | CoolCat 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/CoolCatActionDescriptorChangeProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Infrastructure; 2 | using Microsoft.Extensions.Primitives; 3 | using System.Threading; 4 | 5 | namespace CoolCat.Mvc.Infrastructure 6 | { 7 | public class CoolCatActionDescriptorChangeProvider : IActionDescriptorChangeProvider 8 | { 9 | public static CoolCatActionDescriptorChangeProvider Instance { get; } = new CoolCatActionDescriptorChangeProvider(); 10 | 11 | public CancellationTokenSource TokenSource { get; private set; } 12 | 13 | public bool HasChanged { get; set; } 14 | 15 | public IChangeToken GetChangeToken() 16 | { 17 | TokenSource = new CancellationTokenSource(); 18 | return new CancellationChangeToken(TokenSource.Token); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Repositories/IPluginRepository.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.DTOs; 2 | using CoolCat.Core.ViewModels; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace CoolCat.Core.Repositories 7 | { 8 | public interface IPluginRepository 9 | { 10 | List GetAllPlugins(); 11 | 12 | List GetAllEnabledPlugins(); 13 | 14 | void AddPlugin(AddPluginDTO dto); 15 | 16 | void UpdatePluginVersion(Guid pluginId, string version); 17 | 18 | PluginViewModel GetPlugin(Guid pluginId); 19 | 20 | PluginViewModel GetPlugin(string pluginName); 21 | 22 | void SetPluginStatus(Guid pluginId, bool enable); 23 | 24 | void DeletePlugin(Guid pluginId); 25 | 26 | void RunDownMigrations(Guid pluginId); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Compiler/CoolCatModuleCompiledItemLoader.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Razor.Hosting; 2 | using System; 3 | 4 | namespace CoolCat.Core.Mvc.Infrastructure 5 | { 6 | public class CoolCatModuleViewCompiledItemLoader : RazorCompiledItemLoader 7 | { 8 | public string ModuleName { get; } 9 | 10 | public CoolCatModuleViewCompiledItemLoader(string moduleName) 11 | { 12 | ModuleName = moduleName; 13 | } 14 | 15 | protected override RazorCompiledItem CreateItem(RazorCompiledItemAttribute attribute) 16 | { 17 | if (attribute == null) 18 | { 19 | throw new ArgumentNullException(nameof(attribute)); 20 | } 21 | 22 | return new CoolCatModuleViewCompiledItem(attribute, ModuleName); 23 | } 24 | 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/Plugins/Assemblies.cshtml: -------------------------------------------------------------------------------- 1 | @model List 2 | @{ 3 | ViewData["Title"] = "Index"; 4 | } 5 | 6 |

Assemblies

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | @if (Model != null && Model.Count > 0) 17 | { 18 | foreach (var row in Model) 19 | { 20 | 21 | 22 | 23 | 24 | } 25 | } 26 | else 27 | { 28 | 29 | 30 | 31 | } 32 | 33 |
Assembly NameVersion
@row.ReferenceName@row.Version
No Data.
34 | 35 | 36 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/ModuleBase.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | 3 | namespace CoolCat.Core.DomainModel 4 | { 5 | public class ModuleBase : IModule 6 | { 7 | public ModuleBase(string name) 8 | { 9 | Name = name; 10 | Version = "1.0.0"; 11 | } 12 | 13 | public ModuleBase(string name, string version) 14 | { 15 | Name = name; 16 | Version = version; 17 | } 18 | 19 | public ModuleBase(string name, Version version) 20 | { 21 | Name = name; 22 | Version = version; 23 | } 24 | 25 | public string Name 26 | { 27 | get; 28 | private set; 29 | } 30 | 31 | public Version Version 32 | { 33 | get; 34 | private set; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/CoolCat.Core/CoolCat.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | CoolCat.Core 6 | CoolCat.Core 7 | true 8 | Lamond Lu 9 | Lamond Lu 10 | CoolCat 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/BookInEventHandler.cs: -------------------------------------------------------------------------------- 1 | using BookInventory.DAL; 2 | using CoolCat.Core.Contracts; 3 | using Newtonsoft.Json; 4 | using System; 5 | 6 | namespace BookInventory 7 | { 8 | public class BookInEventHandler : INotificationHandler 9 | { 10 | private BookDAL _bookDAL = null; 11 | 12 | public BookInEventHandler(IDbConnectionFactory dbConnectionFactory) 13 | { 14 | _bookDAL = new BookDAL(dbConnectionFactory); 15 | } 16 | 17 | public void Handle(string data) 18 | { 19 | var obj = JsonConvert.DeserializeObject(data); 20 | _bookDAL.UpdateBookStatus(obj.BookId, true); 21 | } 22 | } 23 | 24 | public class BookInEvent 25 | { 26 | public Guid BookId { get; set; } 27 | 28 | public DateTime InDate { get; set; } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/BookOutEventHandler.cs: -------------------------------------------------------------------------------- 1 | using BookInventory.DAL; 2 | using CoolCat.Core.Contracts; 3 | using Newtonsoft.Json; 4 | using System; 5 | 6 | namespace BookInventory 7 | { 8 | public class BookOutEventHandler : INotificationHandler 9 | { 10 | private BookDAL _bookDAL = null; 11 | 12 | public BookOutEventHandler(IDbConnectionFactory connectionFactory) 13 | { 14 | _bookDAL = new BookDAL(connectionFactory); 15 | } 16 | 17 | public void Handle(string data) 18 | { 19 | var obj = JsonConvert.DeserializeObject(data); 20 | _bookDAL.UpdateBookStatus(obj.BookId, true); 21 | } 22 | } 23 | 24 | public class BookOutEvent 25 | { 26 | public Guid BookId { get; set; } 27 | 28 | public DateTime OutDate { get; set; } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/DependanceItem.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace CoolCat.Core.DomainModel 4 | { 5 | public class DependanceItem 6 | { 7 | public DependanceItem(string packageName, string version, string assemblyVersion, string dllPath) 8 | { 9 | PackageName = packageName; 10 | 11 | if (!string.IsNullOrEmpty(assemblyVersion)) 12 | { 13 | Version = assemblyVersion; 14 | } 15 | else 16 | { 17 | Version = version; 18 | } 19 | 20 | DLLPath = dllPath; 21 | } 22 | 23 | public string PackageName { get; private set; } 24 | 25 | public string Version { get; private set; } 26 | 27 | public string DLLPath { get; private set; } 28 | 29 | public string FileName => DLLPath.Split('/').Last(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/CoolCat.Core.Repository.MySql.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Lamond Lu 6 | Lamond Lu 7 | CoolCat 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Migrations/Migration.1.0.0.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | 4 | namespace BookInventory.Migrations 5 | { 6 | public class Migration_1_0_0 : BaseMigration 7 | { 8 | private static readonly CoolCat.Core.DomainModel.Version _version = new CoolCat.Core.DomainModel.Version("1.0.0"); 9 | 10 | public override string UpScripts => @"CREATE TABLE `book`( 11 | `BookId` char(36) NOT NULL, 12 | `BookName` varchar(255) NULL DEFAULT NULL, 13 | `DateIssued` datetime(0) NULL DEFAULT NULL, 14 | `ISBN` varchar(255)NULL DEFAULT NULL, 15 | `Description` text NULL, 16 | PRIMARY KEY (`BookId`) USING BTREE 17 | )"; 18 | 19 | public override string DownScripts => @"DROP TABLE Book"; 20 | 21 | public Migration_1_0_0(IDbConnectionFactory dbConnectionFactory) : base(_version, dbConnectionFactory) 22 | { 23 | 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/NotificationProvider.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using System.Collections.Generic; 3 | 4 | namespace BookInventory 5 | { 6 | public class NotificationProvider : INotificationProvider 7 | { 8 | public Dictionary> GetNotifications(IDbConnectionFactory dbConnectionFactory) 9 | { 10 | List inHandlers = new List { new BookInEventHandler(dbConnectionFactory) }; 11 | List outHandlers = new List { new BookOutEventHandler(dbConnectionFactory) }; 12 | Dictionary> result = new Dictionary> 13 | { 14 | { "BookInEvent", inHandlers }, 15 | { "BookOutEvent",outHandlers } 16 | }; 17 | 18 | return result; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/CoolCat/Views/Shared/Error.cshtml: -------------------------------------------------------------------------------- 1 | @model ErrorViewModel 2 | @{ 3 | ViewData["Title"] = "Error"; 4 | } 5 | 6 |

Error.

7 |

An error occurred while processing your request.

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

12 | Request ID: @Model.RequestId 13 |

14 | } 15 | 16 |

Development Mode

17 |

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

20 |

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

26 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/CoolCatController.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace CoolCat.Core.Mvc.Infrastructure 5 | { 6 | public class CoolCatController : Controller 7 | { 8 | private string _moduleName = string.Empty; 9 | private IDataStore _dataStore; 10 | 11 | public CoolCatController(string moduleName, IDataStore dataStore) 12 | { 13 | _moduleName = moduleName; 14 | _dataStore = dataStore; 15 | } 16 | 17 | protected string Query(string moduleName, string queryName, string parameter) 18 | { 19 | return _dataStore.Query(moduleName, queryName, parameter, source: _moduleName); 20 | } 21 | 22 | public override RedirectToActionResult RedirectToAction(string actionName, string controllerName) 23 | { 24 | return base.RedirectToAction(actionName, controllerName, new { Area = _moduleName }); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Configurations/DependanceConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace CoolCat.Core.Configurations 4 | { 5 | public class DependanceConfiguration 6 | { 7 | public RuntimeTarget RuntimeTarget { get; set; } 8 | 9 | public CompilationOptions CompilationOptions { get; set; } 10 | 11 | public JObject Targets { get; set; } 12 | 13 | public JObject Libraries { get; set; } 14 | } 15 | 16 | public class RuntimeTarget 17 | { 18 | public string Name { get; set; } 19 | 20 | public string Signature { get; set; } 21 | } 22 | 23 | public class CompilationOptions 24 | { 25 | 26 | } 27 | 28 | public class LibraryConfiguration 29 | { 30 | public string Type { get; set; } 31 | 32 | public string Serviceable { get; set; } 33 | 34 | public string Sha512 { get; set; } 35 | 36 | public string Path { get; set; } 37 | 38 | public string HashPath { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/MySqlConnectionFactory.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.Models; 3 | using Microsoft.Extensions.Options; 4 | using MySql.Data.MySqlClient; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Data; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace CoolCat.Core.Repository.MySql 13 | { 14 | public class MySqlConnectionFactory : IDbConnectionFactory 15 | { 16 | private MySqlConnection _mySqlConnection = null; 17 | private ConnectionStringSetting _setting = null; 18 | 19 | public MySqlConnectionFactory(IOptions settingAccessor) 20 | { 21 | _setting = settingAccessor.Value; 22 | } 23 | 24 | public IDbConnection GetConnection() 25 | { 26 | _mySqlConnection = new MySqlConnection(_setting.ConnectionString); 27 | _mySqlConnection.Open(); 28 | 29 | return _mySqlConnection; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Migrations/Migration.1.0.0.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | 4 | namespace BookLibrary.Migrations 5 | { 6 | public class Migration_1_0_0 : BaseMigration 7 | { 8 | private static readonly CoolCat.Core.DomainModel.Version _version = new CoolCat.Core.DomainModel.Version("1.0.0"); 9 | 10 | public override string UpScripts => @"CREATE TABLE `rent_history`( 11 | `RentId` char(36) NOT NULL, 12 | `BookId` char(36) NOT NULL, 13 | `BookName` varchar(255) NULL DEFAULT NULL, 14 | `ISBN` varchar(255)NULL DEFAULT NULL, 15 | `DateIssued` datetime(0) NULL DEFAULT NULL, 16 | `RentDate` datetime(0) NULL DEFAULT NULL, 17 | `ReturnDate` datetime(0) NULL DEFAULT NULL, 18 | PRIMARY KEY (`RentId`) USING BTREE 19 | )"; 20 | 21 | public override string DownScripts => @"DROP TABLE rent_history"; 22 | 23 | public Migration_1_0_0(IDbConnectionFactory dbConnectionFactory) : base(_version, dbConnectionFactory) 24 | { 25 | 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/BookLibrary.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Always 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./doc/images/logo_small.png) 2 | 3 | # CoolCat 4 | A sample about how to create a dynamic plugins mechanism with ASP.NET Core Mvc based on the AssemblyLoadContext. 5 | 6 | This whole project is built under .NET Core 3.1 and .NET 5. 7 | 8 | ## What i will do and what i will not do 9 | I want to build a runtime plugin mechanism based on .NET Core 3.1 and .NET 5. Each plugin will be isolated by a custom AssemlyLoadContext. So the framework allow you to reference same library with different version. 10 | 11 | ![](./doc/images/load_way.png) 12 | 13 | ## Getting Started 14 | - Clone the source code 15 | - Run `docker-compose up` 16 | - Install the pre-set modules 17 | ![](./doc/images/20200726215825.png) 18 | - Start to use the system 19 | 20 | ## How to create and publish a plugin 21 | - Run `dotnet new -i CoolCatModule`, it will install the CoolCatModule on your machine 22 | - Run `dotnet new CoolCatModule -n {your plugin name}` 23 | - Build the plugin with VisualStudio 2019 or `dotnet publish` 24 | - Package the release files into a zip package 25 | 26 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/DemoPlugin1.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | Always 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/CoolCatRouteConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | 3 | namespace CoolCat.Core.Mvc.Infrastructure 4 | { 5 | public static class CoolCatRouteConfiguration 6 | { 7 | public static IApplicationBuilder CoolCatRoute(this IApplicationBuilder app) 8 | { 9 | app.UseRouting(); 10 | 11 | app.UseAuthentication(); 12 | app.UseAuthorization(); 13 | 14 | app.UseEndpoints(routes => 15 | { 16 | routes.MapAreaControllerRoute( 17 | "Admin", "Admin", 18 | "Admin/{controller}/{action}/{id?}"); 19 | 20 | routes.MapControllerRoute( 21 | name: "Customer", 22 | pattern: "Modules/{area}/{controller=Home}/{action=Dashboard}/{id?}"); 23 | 24 | routes.MapControllerRoute( 25 | name: "Customer", 26 | pattern: "{controller=Home}/{action=Index}/{id?}"); 27 | }); 28 | 29 | return app; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Compiler/CoolCatViewAssemblyPart.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ApplicationParts; 2 | using Microsoft.AspNetCore.Razor.Hosting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Reflection; 6 | 7 | namespace CoolCat.Core.Mvc.Infrastructure 8 | { 9 | public class CoolCatRazorAssemblyPart : ApplicationPart, IRazorCompiledItemProvider 10 | { 11 | public CoolCatRazorAssemblyPart(Assembly assembly, string areaName) 12 | { 13 | Assembly = assembly ?? throw new ArgumentNullException(nameof(assembly)); 14 | AreaName = areaName; 15 | } 16 | 17 | public string AreaName { get; } 18 | 19 | public Assembly Assembly { get; } 20 | 21 | public override string Name => Assembly.GetName().Name; 22 | 23 | IEnumerable IRazorCompiledItemProvider.CompiledItems 24 | { 25 | get 26 | { 27 | var loader = new CoolCatModuleViewCompiledItemLoader(AreaName); 28 | return loader.LoadItems(Assembly); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/CoolCat.Core/BusinessLogic/SystemManager.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DTOs; 3 | using CoolCat.Core.Repositories; 4 | using CoolCat.Core.ViewModels; 5 | 6 | namespace CoolCat.Core.BusinessLogic 7 | { 8 | public class SystemManager : ISystemManager 9 | { 10 | private readonly IUnitOfWork _unitOfWork = null; 11 | 12 | public SystemManager(IUnitOfWork unitOfWork) 13 | { 14 | _unitOfWork = unitOfWork; 15 | } 16 | 17 | public bool CheckInstall() 18 | { 19 | return _unitOfWork.CheckDatabase(); 20 | } 21 | 22 | public void MarkAsInstalled() 23 | { 24 | _unitOfWork.MarkAsInstalled(); 25 | _unitOfWork.Commit(); 26 | } 27 | 28 | public SiteSettingsViewModel GetSiteSettings() 29 | { 30 | return _unitOfWork.SiteRepository.GetSiteSettings(); 31 | } 32 | 33 | public void SaveSiteSettings(SiteSettingsDTO dto) 34 | { 35 | _unitOfWork.SiteRepository.SaveSiteSettings(dto); 36 | _unitOfWork.Commit(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/CoolCat/Views/Shared/_CookieConsentPartial.cshtml: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Http.Features 2 | 3 | @{ 4 | var consentFeature = Context.Features.Get(); 5 | var showBanner = !consentFeature?.CanTrack ?? false; 6 | var cookieString = consentFeature?.CreateConsentCookie(); 7 | } 8 | 9 | @if (showBanner) 10 | { 11 | 17 | 25 | } 26 | -------------------------------------------------------------------------------- /doc/Question.md: -------------------------------------------------------------------------------- 1 | ## Pain spot and solutions 2 | I just list down all the pain spot when i approach this feature, If you are good at Chinese, you can reference my cnblog (https://www.cnblogs.com/lwqlun/p/13208980.html). I have list down all of these in my blog. I will translate a English version later. 3 | 4 | ### How to unload plugin at runtime? 5 | Although a new method called Unload imported in the .net core 3.0, but when you unload the AssemblyLoadContext, it would still show the error message that the file is using, you could not remove them. 6 | 7 | So my solution is that my app use the `LoadFromStream` method to load plugin assembly instead of `LoadFromAssemblyPath`. 8 | 9 | The another benifit for this is that you can cache and reuse the library stream. So if pluginA reference Newtonsoft.Json v11.0.0 and plugin B reference the same. After pluginA loaded, system will not load the same library when loading the PluginB. 10 | 11 | ### How to enable controller/action at runtime? 12 | Coming soon.. 13 | 14 | ### How to enable views at runtime? 15 | Coming soon.. 16 | 17 | ### How to debug the plugin? 18 | Coming soon.. 19 | 20 | ### How to build a dynamic menu 21 | Coming soon.. 22 | 23 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/NotificationRegister.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using System.Collections.Generic; 3 | 4 | namespace CoolCat.Core.Mvc.Infrastructure 5 | { 6 | public class NotificationRegister : INotificationRegister 7 | { 8 | private static readonly Dictionary> 9 | _containers = new Dictionary>(); 10 | 11 | public void Publish(string eventName, string data) 12 | { 13 | if (_containers.ContainsKey(eventName)) 14 | { 15 | foreach (INotificationHandler item in _containers[eventName]) 16 | { 17 | item.Handle(data); 18 | } 19 | } 20 | } 21 | 22 | public void Subscribe(string eventName, INotificationHandler handler) 23 | { 24 | if (_containers.ContainsKey(eventName)) 25 | { 26 | _containers[eventName].Add(handler); 27 | } 28 | else 29 | { 30 | _containers[eventName] = new List() { handler }; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/lib/jquery-validation/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Copyright Jörn Zaefferer 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/CoolCat/Views/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/BookInventory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Always 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Compiler/CoolCatModuleViewCompiledItem.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Consts; 2 | using Microsoft.AspNetCore.Razor.Hosting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace CoolCat.Core.Mvc.Infrastructure 8 | { 9 | public class CoolCatModuleViewCompiledItem : RazorCompiledItem 10 | { 11 | public override string Identifier { get; } 12 | 13 | public override string Kind { get; } 14 | 15 | public override IReadOnlyList Metadata { get; } 16 | 17 | public override Type Type { get; } 18 | 19 | public CoolCatModuleViewCompiledItem(RazorCompiledItemAttribute attr, string moduleName) 20 | { 21 | Type = attr.Type; 22 | Kind = attr.Kind; 23 | Identifier = $"/{GlobalConst.ModulePrefix}/{moduleName}{attr.Identifier}"; 24 | 25 | Metadata = Type.GetCustomAttributes(inherit: true).Select(o => 26 | o is RazorSourceChecksumAttribute rsca 27 | ? new RazorSourceChecksumAttribute(rsca.ChecksumAlgorithm, rsca.Checksum, $"/{GlobalConst.ModulePrefix}/{moduleName}{rsca.Identifier}") 28 | : o).ToList(); 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Compiler/CoolCatViewCompilerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.ApplicationParts; 2 | using Microsoft.AspNetCore.Mvc.Razor.Compilation; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace CoolCat.Core.Mvc.Infrastructure 6 | { 7 | public class CoolCatViewCompilerProvider : IViewCompilerProvider 8 | { 9 | private CoolCatViewCompiler _compiler; 10 | private ApplicationPartManager _applicationPartManager; 11 | private ILoggerFactory _loggerFactory; 12 | 13 | public CoolCatViewCompilerProvider( 14 | ApplicationPartManager applicationPartManager, 15 | ILoggerFactory loggerFactory) 16 | { 17 | _applicationPartManager = applicationPartManager; 18 | _loggerFactory = loggerFactory; 19 | Refresh(); 20 | } 21 | 22 | public void Refresh() 23 | { 24 | var feature = new ViewsFeature(); 25 | _applicationPartManager.PopulateFeature(feature); 26 | 27 | _compiler = new CoolCatViewCompiler(feature.ViewDescriptors, _loggerFactory.CreateLogger()); 28 | } 29 | 30 | public IViewCompiler GetCompiler() => _compiler; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/lib/bootstrap/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2018 Twitter, Inc. 4 | Copyright (c) 2011-2018 The Bootstrap Authors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /src/InternalModules/PluginManagement/PluginManagement.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | Always 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Views/BookInventory/Add.cshtml: -------------------------------------------------------------------------------- 1 | @model BookInventory.Dtos.AddBookDto 2 | @{ 3 | ViewData["Title"] = "Add Book"; 4 | 5 | var action = Url.Action("Add", "BookInventory", new { Area = BookInventory.ModuleDefiniation.MODULE_NAME }); 6 | } 7 | 8 |

Add

9 | 10 |
11 |
12 | 13 | @Html.LabelFor(p => p.BookName) 14 | @Html.TextBoxFor(p => p.BookName, new { @class = "form-control" }) 15 | 16 |
17 |
18 | 19 | @Html.LabelFor(p => p.ISBN) 20 | @Html.TextBoxFor(p => p.ISBN, new { @class = "form-control" }) 21 | 22 |
23 |
24 | 25 | @Html.LabelFor(p => p.DateIssued) 26 | @Html.TextBoxFor(p => p.DateIssued, new { @class = "form-control" }) 27 | 28 |
29 |
30 | 31 | @Html.LabelFor(p => p.Description) 32 | @Html.TextBoxFor(p => p.BookName, new { @class = "form-control" }) 33 | 34 |
35 | 36 |
37 | 38 | @section Scripts{ 39 | 44 | } -------------------------------------------------------------------------------- /src/CoolCat.Core/PluginsLoadContexts.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace CoolCat.Core 5 | { 6 | public static class PluginsLoadContexts 7 | { 8 | private static readonly Dictionary _pluginContexts = null; 9 | 10 | static PluginsLoadContexts() 11 | { 12 | _pluginContexts = new Dictionary(); 13 | } 14 | 15 | public static List All() 16 | { 17 | return _pluginContexts.Select(p => p.Value).ToList(); 18 | } 19 | 20 | public static bool Any(string pluginName) 21 | { 22 | return _pluginContexts.ContainsKey(pluginName); 23 | } 24 | 25 | public static void Remove(string pluginName) 26 | { 27 | if (_pluginContexts.ContainsKey(pluginName)) 28 | { 29 | _pluginContexts[pluginName].Unload(); 30 | _pluginContexts.Remove(pluginName); 31 | } 32 | } 33 | 34 | public static CollectibleAssemblyLoadContext Get(string pluginName) 35 | { 36 | return _pluginContexts[pluginName]; 37 | } 38 | 39 | public static void Add(string pluginName, CollectibleAssemblyLoadContext context) 40 | { 41 | _pluginContexts.Add(pluginName, context); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/Migrations/202005282045.cs: -------------------------------------------------------------------------------- 1 | using FluentMigrator; 2 | 3 | namespace CoolCat.Core.Repository.MySql.Migrations 4 | { 5 | [Tags("System")] 6 | [Migration(202005282045)] 7 | public class InitialDB : Migration 8 | { 9 | public override void Up() 10 | { 11 | 12 | Create.Table("Plugins") 13 | .WithColumn("PluginId").AsGuid().PrimaryKey() 14 | .WithColumn("UniqueKey").AsString().NotNullable() 15 | .WithColumn("Name").AsString().NotNullable() 16 | .WithColumn("DisplayName").AsString().NotNullable() 17 | .WithColumn("Version").AsString().NotNullable() 18 | .WithColumn("Enable").AsInt16().NotNullable(); 19 | 20 | Create.Table("PluginMigrations") 21 | .WithColumn("PluginMigrationId").AsGuid().PrimaryKey() 22 | .WithColumn("PluginId").AsGuid().ForeignKey("FK_PluginMigrations_PluginId_Plugins_PluginId", "Plugins", "PluginId") 23 | .WithColumn("Version").AsString().NotNullable() 24 | .WithColumn("Up").AsCustom("text") 25 | .WithColumn("Down").AsCustom("text"); 26 | } 27 | 28 | public override void Down() 29 | { 30 | Delete.Table("PluginMigrations"); 31 | Delete.ForeignKey("FK_PluginMigrations_PluginId_Plugins_PluginId"); 32 | Delete.Table("Plugins"); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Views/Plugin1/HelloWorld.cshtml: -------------------------------------------------------------------------------- 1 | @using DemoPlugin1.Models; 2 | @model TestClass 3 | @{ 4 | var books = (List)ViewBag.Books; 5 | } 6 | 7 | @section Header{ 8 | 9 | } 10 | 11 |

@ViewBag.Content

12 |

@Model.Message

13 |

Test style container

14 | 15 |

Book Data

16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @if (books != null && books.Count > 0) 29 | { 30 | foreach (var item in books) 31 | { 32 | 33 | 34 | 35 | 36 | 37 | 38 | } 39 | } 40 | else 41 | { 42 | 43 | 44 | 45 | } 46 | 47 | 48 |
BookIdBook NameISBNDate Issued
@item.BookId@item.BookName@item.ISBN@item.DateIssued.ToString("yyyy-MM-dd")
No Records.
49 |
50 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/DataStores/AvailableBookQuery.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Attributes; 2 | using CoolCat.Core.Contracts; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Data; 7 | using System.Linq; 8 | using Dapper; 9 | 10 | namespace BookInventory.DataStores 11 | { 12 | [NoneRequestParameter] 13 | [ResponseType(typeof(List))] 14 | public class AvailableBookQuery : IDataStoreQuery 15 | { 16 | private IDbConnectionFactory _dbConnectionFactory = null; 17 | 18 | public AvailableBookQuery(IDbConnectionFactory dbConnectionFactory) 19 | { 20 | _dbConnectionFactory = dbConnectionFactory; 21 | } 22 | 23 | public string QueryName 24 | { 25 | get 26 | { 27 | return "Available_Books"; 28 | } 29 | } 30 | 31 | public string Query(string parameter) 32 | { 33 | using (var connection = _dbConnectionFactory.GetConnection()) 34 | { 35 | var sql = "SELECT * FROM Book WHERE Status=0"; 36 | var books = connection.Query(sql).ToList(); 37 | return JsonConvert.SerializeObject(books); 38 | } 39 | } 40 | } 41 | 42 | public class AvailableBookViewModel 43 | { 44 | public Guid BookId { get; set; } 45 | 46 | public string BookName { get; set; } 47 | 48 | public string ISBN { get; set; } 49 | 50 | public DateTime DateIssued { get; set; } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/CoolCat/Controllers/CommonController.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core; 2 | using CoolCat.Core.Contracts; 3 | using Microsoft.AspNetCore.Mvc; 4 | 5 | namespace CoolCat.Controllers 6 | { 7 | public class CommonController : Controller 8 | { 9 | private readonly ISystemManager _systemManager; 10 | private readonly IPluginManager _pluginManager; 11 | 12 | 13 | public CommonController(ISystemManager systemManager, IPluginManager pluginManager) 14 | { 15 | _systemManager = systemManager; 16 | _pluginManager = pluginManager; 17 | } 18 | 19 | [HttpGet("~/Common/GetSiteCSS")] 20 | public IActionResult GetSiteCSS() 21 | { 22 | var settings = _systemManager.GetSiteSettings(); 23 | 24 | if (!string.IsNullOrEmpty(settings.SiteCSS)) 25 | { 26 | return Content(settings.SiteCSS, "text/css"); 27 | } 28 | 29 | return null; 30 | } 31 | 32 | 33 | [HttpGet("~/Common/GetModuleCSS")] 34 | public IActionResult GetModuleCSS(string moduleName, string fileName) 35 | { 36 | var fileContent = PluginsLoadContexts.Get(moduleName).LoadResource(fileName); 37 | 38 | return new FileContentResult(fileContent, "text/css"); 39 | } 40 | 41 | [HttpGet("~/CommonS/GetModuleScript")] 42 | public IActionResult GetModuleScript(string moduleName, string fileName) 43 | { 44 | var fileContent = PluginsLoadContexts.Get(moduleName).LoadResource(fileName); 45 | return new FileContentResult(fileContent, "text/javascript"); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Helpers/DefaultReferenceContainer.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace CoolCat.Core.Helpers 8 | { 9 | public class DefaultReferenceContainer : IReferenceContainer 10 | { 11 | private static readonly Dictionary _cachedReferences = new Dictionary(); 12 | 13 | 14 | public List GetAll() 15 | { 16 | return _cachedReferences.Keys.ToList(); 17 | } 18 | 19 | public bool Exist(string name, string version) 20 | { 21 | return _cachedReferences.Keys.Any(p => p.ReferenceName == name 22 | && p.Version == version); 23 | } 24 | 25 | public void SaveStream(string name, string version, Stream stream) 26 | { 27 | if (Exist(name, version)) 28 | { 29 | return; 30 | } 31 | 32 | 33 | _cachedReferences.Add(new CachedReferenceItemKey { ReferenceName = name, Version = version }, stream); 34 | } 35 | 36 | public Stream GetStream(string name, string version) 37 | { 38 | CachedReferenceItemKey key = _cachedReferences.Keys.FirstOrDefault(p => p.ReferenceName == name 39 | && p.Version == version); 40 | 41 | if (key != null) 42 | { 43 | _cachedReferences[key].Position = 0; 44 | return _cachedReferences[key]; 45 | } 46 | 47 | return null; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/lib/jquery/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors, https://js.foundation/ 2 | 3 | This software consists of voluntary contributions made by many 4 | individuals. For exact contribution history, see the revision history 5 | available at https://github.com/jquery/jquery 6 | 7 | The following license applies to all parts of this software except as 8 | documented below: 9 | 10 | ==== 11 | 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | "Software"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | 20 | The above copyright notice and this permission notice shall be 21 | included in all copies or substantial portions of the Software. 22 | 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 27 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 28 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 29 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ==== 32 | 33 | All files located in the node_modules and external directories are 34 | externally maintained libraries used by this software which have their 35 | own licenses; we recommend you read them, as their terms may differ from 36 | the terms above. 37 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Views/Book/AvailableBooks.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | 4 | } 5 | 6 | @section Header{ 7 | 8 | } 9 | 10 | 11 |

Available Books

12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @if (Model != null && Model.Count > 0) 26 | { 27 | foreach (var item in Model) 28 | { 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | } 37 | } 38 | else 39 | { 40 | 41 | 42 | 43 | } 44 | 45 |
BookIdBook NameISBNDate IssuedAction
@item.BookId@item.BookName@item.ISBN@item.DateIssued.ToString("yyyy-MM-dd")Rent
No Records.
46 |
47 | 48 | @section Scripts{ 49 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/DefaultDataStore.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace CoolCat.Core.Mvc.Infrastructure 8 | { 9 | public class DefaultDataStore : IDataStore 10 | { 11 | private ILogger _logger = null; 12 | 13 | public DefaultDataStore(ILogger logger) 14 | { 15 | _logger = logger; 16 | } 17 | 18 | private List _queryItems = new List(); 19 | 20 | public string Query(string moduleName, string queryName, string parameter, string source = "") 21 | { 22 | var query = _queryItems.FirstOrDefault(p => p.ModuleName == moduleName && p.QueryName == queryName); 23 | 24 | if (query != null) 25 | { 26 | _logger.LogDebug($"Module '{source}' try to query the '{queryName}' from '{moduleName}' with parameter '{parameter}'"); 27 | 28 | return query.Query(parameter); 29 | } 30 | else 31 | { 32 | return string.Empty; 33 | } 34 | } 35 | 36 | public void RegisterQuery(string moduleName, string queryName, Func query) 37 | { 38 | _queryItems.Add(new QueryItem 39 | { 40 | ModuleName = moduleName, 41 | QueryName = queryName, 42 | Query = query 43 | }); 44 | } 45 | } 46 | 47 | public class QueryItem 48 | { 49 | public string ModuleName { get; set; } 50 | 51 | public string QueryName { get; set; } 52 | 53 | public Func Query { get; set; } 54 | 55 | public string Run(string parameter) 56 | { 57 | return Query(parameter); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/CoolCat.Database/CoolCat.Database.refactorlog: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Controllers/BookInventoryController.cs: -------------------------------------------------------------------------------- 1 | using BookInventory.DAL; 2 | using BookInventory.Dtos; 3 | using BookInventory.ViewModels; 4 | using CoolCat.Core.Attributes; 5 | using CoolCat.Core.Contracts; 6 | using CoolCat.Core.Mvc.Infrastructure; 7 | using Microsoft.AspNetCore.Mvc; 8 | using System; 9 | 10 | namespace BookInventory.Controllers 11 | { 12 | [Area(ModuleDefiniation.MODULE_NAME)] 13 | public class BookInventoryController : CoolCatController 14 | { 15 | private BookDAL _bookDAL = null; 16 | 17 | public BookInventoryController(IDbConnectionFactory dbConnectionFactory, IDataStore dataStore) : base(ModuleDefiniation.MODULE_NAME, dataStore) 18 | { 19 | _bookDAL = new BookDAL(dbConnectionFactory); 20 | } 21 | 22 | [Page("Book Inventory")] 23 | [HttpGet] 24 | public IActionResult Books() 25 | { 26 | var result = _bookDAL.GetBooks(); 27 | 28 | return View(result); 29 | } 30 | 31 | [HttpGet] 32 | public IActionResult Add() 33 | { 34 | return View(); 35 | } 36 | 37 | [HttpPost] 38 | public IActionResult Add(AddBookDto dto) 39 | { 40 | if (dto == null || !ModelState.IsValid) 41 | { 42 | return View(); 43 | } 44 | 45 | _bookDAL.AddBook(dto); 46 | 47 | return RedirectToAction("Books", "BookInventory"); 48 | } 49 | 50 | [HttpDelete] 51 | public IActionResult Delete(Guid bookId) 52 | { 53 | return View(); 54 | } 55 | 56 | [HttpGet] 57 | public IActionResult Details(Guid bookId) 58 | { 59 | return View(new BookDetailViewModel()); 60 | } 61 | 62 | [HttpPut] 63 | public IActionResult Update(Guid bookId, [FromForm] UpdateBookDto dto) 64 | { 65 | return View(); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Extensions/CollectibleAssemblyLoadContextExtension.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Attributes; 2 | using CoolCat.Core.Mvc.Infrastructure; 3 | using CoolCat.Core.ViewModels; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace CoolCat.Core.Mvc.Extensions 9 | { 10 | public static class CollectibleAssemblyLoadContextExtension 11 | { 12 | public static List GetPages(this CollectibleAssemblyLoadContext context) 13 | { 14 | System.Reflection.Assembly entryPointAssembly = context.GetEntryPointAssembly(); 15 | List result = new List(); 16 | 17 | if (entryPointAssembly == null || !context.IsEnabled) 18 | { 19 | return result; 20 | } 21 | 22 | string areaName = context.PluginName; 23 | 24 | IEnumerable types = entryPointAssembly.GetExportedTypes().Where(p => p.BaseType == typeof(CoolCatController)); 25 | 26 | if (types.Any()) 27 | { 28 | foreach (Type type in types) 29 | { 30 | 31 | string controllerName = type.Name.Replace("Controller", ""); 32 | 33 | List actions = type.GetMethods().Where(p => p.GetCustomAttributes(false).Any(x => x.GetType() == typeof(Page))).ToList(); 34 | 35 | foreach (System.Reflection.MethodInfo action in actions) 36 | { 37 | string actionName = action.Name; 38 | 39 | Page pageAttribute = (Page)action.GetCustomAttributes(false).First(p => p.GetType() == typeof(Page)); 40 | result.Add(new PageRouteViewModel(pageAttribute.Name, areaName, controllerName, actionName)); 41 | } 42 | } 43 | 44 | return result; 45 | } 46 | else 47 | { 48 | return result; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/DataStores/BookDetailsQuery.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Attributes; 2 | using CoolCat.Core.Contracts; 3 | using MySql.Data.MySqlClient; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Data; 7 | using System.Linq; 8 | using Dapper; 9 | 10 | namespace BookInventory.DataStores 11 | { 12 | [RequestParameterType(typeof(BookDetailsQueryParameter))] 13 | [ResponseType(typeof(BookDetailViewModel))] 14 | public class BookDetailsQuery : IDataStoreQuery 15 | { 16 | private IDbConnectionFactory _dbConnectionFactory = null; 17 | 18 | public BookDetailsQuery(IDbConnectionFactory dbConnectionFactory) 19 | { 20 | _dbConnectionFactory = dbConnectionFactory; 21 | } 22 | 23 | public string QueryName 24 | { 25 | get 26 | { 27 | return "Book_Details"; 28 | } 29 | } 30 | 31 | public string Query(string parameter) 32 | { 33 | using (var connection = _dbConnectionFactory.GetConnection()) 34 | { 35 | var param = JsonConvert.DeserializeObject(parameter); 36 | 37 | var sql = "SELECT * FROM Book WHERE BookId=@id"; 38 | 39 | var items = connection.Query(sql, new { id = param.BookId }).ToList(); 40 | 41 | if (items.Count == 1) 42 | { 43 | return JsonConvert.SerializeObject(items.First()); 44 | } 45 | else 46 | { 47 | return null; 48 | } 49 | } 50 | } 51 | } 52 | 53 | public class BookDetailsQueryParameter 54 | { 55 | public Guid BookId { get; set; } 56 | } 57 | 58 | public class BookDetailViewModel 59 | { 60 | public Guid BookId { get; set; } 61 | 62 | public string BookName { get; set; } 63 | 64 | public string ISBN { get; set; } 65 | 66 | public DateTime DateIssued { get; set; } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/Views/BookInventory/Books.cshtml: -------------------------------------------------------------------------------- 1 | 2 | @{ 3 | var action = Url.Action("Add", "BookInventory", new { Area = BookInventory.ModuleDefiniation.MODULE_NAME }); 4 | } 5 | 6 | @section Header{ 7 | 8 | 9 | 24 | } 25 | 26 |

Books

27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | @if (Model != null && Model.Count > 0) 43 | { 44 | foreach (var item in Model) 45 | { 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | } 54 | } 55 | else 56 | { 57 | 58 | 59 | 60 | } 61 | 62 | 63 |
BookIdBook NameISBNDate IssuedStatus
@item.BookId@item.BookName@item.ISBN@item.DateIssued.ToString("yyyy-MM-dd")@(item.Status?"Out":"In")
No Records.
64 |
65 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/Plugins/Index.cshtml: -------------------------------------------------------------------------------- 1 | @model List 2 | @{ 3 | ViewData["Title"] = "Index"; 4 | } 5 | 6 |

Plugin Management

7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | @if (Model != null && Model.Count > 0) 23 | { 24 | foreach (var row in Model) 25 | { 26 | 27 | 28 | 29 | 30 | 31 | 32 | 44 | 45 | } 46 | } 47 | else 48 | { 49 | 50 | 51 | 52 | } 53 | 54 |
PluginIdUniqueKeyVersionNameDisplayName
@row.PluginId@row.UniqueKey@row.Version@row.Name@row.DisplayName 33 | @if (row.Enable) 34 | { 35 | 36 | } 37 | else 38 | { 39 | 40 | } 41 | 42 | 43 |
No Data.
55 | 56 | 57 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/js/setup.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | $('.nav-tabs > li a[title]').tooltip(); 3 | 4 | //Wizard 5 | $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { 6 | 7 | var target = $(e.target); 8 | 9 | if (target.parent().hasClass('disabled')) { 10 | return false; 11 | } 12 | }); 13 | 14 | $(".next-step").click(function (e) { 15 | 16 | var active = $('.wizard .nav-tabs li.active'); 17 | active.next().removeClass('disabled'); 18 | nextTab(active); 19 | 20 | }); 21 | $(".prev-step").click(function (e) { 22 | 23 | var active = $('.wizard .nav-tabs li.active'); 24 | prevTab(active); 25 | 26 | }); 27 | 28 | $('#btnInstall').click(function () { 29 | 30 | var data = []; 31 | 32 | if ($('#presetModules').val()) { 33 | 34 | 35 | var modules = $('#presetModules').val(); 36 | modules.forEach((o) => { 37 | data.push(o); 38 | }); 39 | } 40 | 41 | $.ajax({ 42 | url: '/System/Install', 43 | type: 'POST', 44 | data: JSON.stringify({ "modules": data }), 45 | contentType: "application/json", 46 | success: function () { 47 | window.location = '/Home/Index'; 48 | } 49 | }); 50 | }); 51 | 52 | $('#btnChoosePlugin').click(function () { 53 | var modules = $('#presetModules').val(); 54 | 55 | modules.forEach(function (o) { 56 | 57 | $('#summaryPanel').append(""); 58 | }); 59 | }); 60 | }); 61 | 62 | function nextTab(elem) { 63 | $(elem).next().find('a[data-toggle="tab"]').click(); 64 | } 65 | function prevTab(elem) { 66 | $(elem).prev().find('a[data-toggle="tab"]').click(); 67 | } 68 | 69 | 70 | $('.nav-tabs').on('click', 'li', function () { 71 | $('.nav-tabs li.active').removeClass('active'); 72 | $(this).addClass('active'); 73 | }); 74 | 75 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/DAL/BookDAL.cs: -------------------------------------------------------------------------------- 1 | using BookLibrary.Dtos; 2 | using CoolCat.Core.Contracts; 3 | using MySql.Data.MySqlClient; 4 | using System; 5 | using System.Collections.Generic; 6 | using Dapper; 7 | 8 | namespace BookLibrary.DAL 9 | { 10 | public class BookDAL 11 | { 12 | private IDbConnectionFactory _dbConnectionFactory = null; 13 | 14 | public BookDAL(IDbConnectionFactory dbConnectionFactory) 15 | { 16 | _dbConnectionFactory = dbConnectionFactory; 17 | } 18 | 19 | public void RentBook(RentBookDTO dto) 20 | { 21 | using (var connection = _dbConnectionFactory.GetConnection()) 22 | { 23 | var sql = @"INSERT INTO rent_history(RentId, BookId, BookName, ISBN, RentDate, DateIssued) 24 | VALUES(@rentId, @bookId, @bookName, @isbn, @rentDate, @dateIssued)"; 25 | 26 | connection.Execute(sql, new 27 | { 28 | rentId = Guid.NewGuid(), 29 | bookId = dto.BookId, 30 | bookName = dto.BookName, 31 | isbn = dto.ISBN, 32 | rentDate = dto.RentDate, 33 | dateIssued = dto.DateIssued 34 | }); 35 | } 36 | } 37 | 38 | public void ReturnBook(ReturnBookDTO dto) 39 | { 40 | using (var connection = _dbConnectionFactory.GetConnection()) 41 | { 42 | var sql = "UPDATE rent_history SET ReturnDate=@returnDate WHERE RentId=@rentId"; 43 | 44 | connection.Execute(sql, new { rentId = dto.RentId, returnDate = dto.ReturnDate }); 45 | } 46 | } 47 | 48 | public Guid? GetBookId(Guid rentId) 49 | { 50 | using (var connection = _dbConnectionFactory.GetConnection()) 51 | { 52 | var sql = "SELECT BookId FROM rent_history WHERE RentId=@rentId"; 53 | 54 | var result = connection.QueryFirstOrDefault(sql, new { rentId }); 55 | 56 | return result; 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/SiteRepository.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Consts; 2 | using CoolCat.Core.Contracts; 3 | using CoolCat.Core.DTOs; 4 | using CoolCat.Core.Repositories; 5 | using CoolCat.Core.ViewModels; 6 | using MySql.Data.MySqlClient; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Data; 10 | using System.Linq; 11 | using Dapper; 12 | 13 | namespace CoolCat.Core.Repository.MySql 14 | { 15 | public class SiteRepository : ISiteRepository 16 | { 17 | private readonly IDbConnection _dbConnection = null; 18 | private readonly List _commands = null; 19 | 20 | public SiteRepository(IDbConnection dbConnection, List commands) 21 | { 22 | _dbConnection = dbConnection; 23 | _commands = commands; 24 | } 25 | 26 | public SiteSettingsViewModel GetSiteSettings() 27 | { 28 | var sql = "SELECT * FROM SiteSettings"; 29 | 30 | var keyPairs = _dbConnection.Query(sql); 31 | 32 | var result = new SiteSettingsViewModel(); 33 | 34 | if (keyPairs.Any(p => p.Key == SiteSettingKeyConst.SiteCSS)) 35 | { 36 | result.SiteCSS = keyPairs.First(p => p.Key == SiteSettingKeyConst.SiteCSS).Value; 37 | } 38 | 39 | if (keyPairs.Any(p => p.Key == SiteSettingKeyConst.SiteTemplateId)) 40 | { 41 | var val = keyPairs.First(p => p.Key == SiteSettingKeyConst.SiteTemplateId).Value; 42 | 43 | Guid guidVal; 44 | if (!string.IsNullOrWhiteSpace(val) && Guid.TryParse(val, out guidVal)) 45 | { 46 | result.SiteTemplateId = guidVal; 47 | } 48 | } 49 | 50 | return result; 51 | } 52 | 53 | public void SaveSiteSettings(SiteSettingsDTO dto) 54 | { 55 | var sql = "UPDATE SiteSettings SET `Value`=@val WHERE `Key`=@key"; 56 | 57 | _dbConnection.Execute(sql, new { val = dto.SiteCSS, key = SiteSettingKeyConst.SiteCSS }); 58 | _dbConnection.Execute(sql, new { val = dto.SiteTemplateId?.ToString(), key = SiteSettingKeyConst.SiteTemplateId }); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/CoolCat/CoolCat.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Linux 6 | 7 | true 8 | Lamond Lu 9 | Lamond Lu 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | all 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Always 41 | 42 | 43 | Always 44 | 45 | 46 | Always 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/BaseMigration.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Data; 5 | using Dapper; 6 | 7 | namespace CoolCat.Core.DomainModel 8 | { 9 | public abstract class BaseMigration : IMigration 10 | { 11 | private readonly Version _version = null; 12 | private readonly IDbConnection _dbConnection = null; 13 | 14 | public BaseMigration(Version version, IDbConnectionFactory dbConnectionFactory) 15 | { 16 | _version = version; 17 | _dbConnection = dbConnectionFactory.GetConnection(); 18 | } 19 | 20 | public Version Version => _version; 21 | 22 | public abstract string UpScripts 23 | { 24 | get; 25 | } 26 | 27 | public abstract string DownScripts 28 | { 29 | get; 30 | } 31 | 32 | protected void SQL(string sql) 33 | { 34 | _dbConnection.Execute(sql); 35 | } 36 | 37 | public void MigrateUp(Guid pluginId) 38 | { 39 | SQL(UpScripts); 40 | WriteMigrationScripts(pluginId); 41 | } 42 | 43 | public void MigrateDown(Guid pluginId) 44 | { 45 | SQL(DownScripts); 46 | RemoveMigrationScripts(pluginId); 47 | } 48 | 49 | private void RemoveMigrationScripts(Guid pluginId) 50 | { 51 | string sql = "DELETE PluginMigrations WHERE PluginId = @pluginId AND Version = @version"; 52 | 53 | _dbConnection.Execute(sql, new 54 | { 55 | pluginId, 56 | version = _version.VersionNumber 57 | }); 58 | } 59 | 60 | private void WriteMigrationScripts(Guid pluginId) 61 | { 62 | string sql = @"INSERT INTO PluginMigrations(PluginMigrationId, PluginId, Version, Up, Down) 63 | VALUES(@pluginMigrationId, @pluginId, @version, @up, @down)"; 64 | 65 | _dbConnection.Execute(sql, new 66 | { 67 | pluginMigrationId = Guid.NewGuid(), 68 | pluginId, 69 | version = _version.VersionNumber, 70 | up = UpScripts, 71 | down = DownScripts 72 | }); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/DemoModules/DemoPlugin1/Controllers/Plugin1Controller.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Attributes; 2 | using CoolCat.Core.Contracts; 3 | using CoolCat.Core.Mvc.Infrastructure; 4 | using DemoPlugin1.Models; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Newtonsoft.Json; 8 | using System.Collections.Generic; 9 | 10 | namespace DemoPlugin1.Controllers 11 | { 12 | [Area(ModuleDefiniation.MODULE_NAME)] 13 | public class Plugin1Controller : CoolCatController 14 | { 15 | private readonly INotificationRegister _notificationRegister; 16 | private readonly IDataStore _dataStore; 17 | 18 | public Plugin1Controller(INotificationRegister notificationRegister, IDataStore dataStore) : base(ModuleDefiniation.MODULE_NAME, dataStore) 19 | { 20 | _notificationRegister = notificationRegister; 21 | _dataStore = dataStore; 22 | } 23 | 24 | [Page("Plugin One")] 25 | [HttpGet] 26 | public IActionResult HelloWorld() 27 | { 28 | TestClass testClass = new TestClass 29 | { 30 | Message = "Hello World" 31 | }; 32 | 33 | _notificationRegister.Publish("LoadHelloWorldEvent", JsonConvert.SerializeObject(new LoadHelloWorldEvent() { Str = "Hello World" })); 34 | 35 | ViewBag.Books = JsonConvert.DeserializeObject>(_dataStore.Query("BookInventory", "Available_Books", string.Empty, source: ModuleDefiniation.MODULE_NAME)); 36 | 37 | return View(testClass); 38 | } 39 | 40 | [HttpGet] 41 | public IActionResult Register() 42 | { 43 | CoolCatStartup.Services.AddScoped(); 44 | return Content("OK"); 45 | } 46 | 47 | [HttpGet] 48 | public IActionResult Show() 49 | { 50 | ServiceProvider provider = CoolCatStartup.Services.BuildServiceProvider(); 51 | using (IServiceScope scope = provider.CreateScope()) 52 | { 53 | IHandler handler = scope.ServiceProvider.GetService(); 54 | return Content(handler.Work()); 55 | } 56 | 57 | } 58 | } 59 | 60 | public interface IHandler 61 | { 62 | string Work(); 63 | } 64 | 65 | public class MyHandler : IHandler 66 | { 67 | public string Work() 68 | { 69 | return "My Handler Work"; 70 | } 71 | } 72 | 73 | public class LoadHelloWorldEvent 74 | { 75 | public string Str { get; set; } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace CoolCat.Core.Mvc.Infrastructure 7 | { 8 | public static class ServiceCollectionExtensions 9 | { 10 | public static IServiceCollection Replace(this IServiceCollection services) 11 | where TImplementation : TService 12 | { 13 | return services.Replace(typeof(TImplementation)); 14 | } 15 | 16 | public static IServiceCollection Replace(this IServiceCollection services, Type implementationType) 17 | { 18 | return services.Replace(typeof(TService), implementationType); 19 | } 20 | 21 | public static IServiceCollection Replace(this IServiceCollection services, Type serviceType, Type implementationType) 22 | { 23 | if (services == null) 24 | { 25 | throw new ArgumentNullException(nameof(services)); 26 | } 27 | 28 | if (serviceType == null) 29 | { 30 | throw new ArgumentNullException(nameof(serviceType)); 31 | } 32 | 33 | if (implementationType == null) 34 | { 35 | throw new ArgumentNullException(nameof(implementationType)); 36 | } 37 | 38 | if (!services.TryGetDescriptors(serviceType, out var descriptors)) 39 | { 40 | throw new ArgumentException($"No services found for {serviceType.FullName}.", nameof(serviceType)); 41 | } 42 | 43 | foreach (var descriptor in descriptors) 44 | { 45 | var index = services.IndexOf(descriptor); 46 | 47 | services.Insert(index, descriptor.WithImplementationType(implementationType)); 48 | 49 | services.Remove(descriptor); 50 | } 51 | 52 | return services; 53 | } 54 | 55 | private static bool TryGetDescriptors(this IServiceCollection services, Type serviceType, out ICollection descriptors) 56 | { 57 | return (descriptors = services.Where(service => service.ServiceType == serviceType).ToArray()).Any(); 58 | } 59 | 60 | private static ServiceDescriptor WithImplementationType(this ServiceDescriptor descriptor, Type implementationType) 61 | { 62 | return new ServiceDescriptor(descriptor.ServiceType, implementationType, descriptor.Lifetime); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/CoolCat.Core/CollectibleAssemblyLoadContext.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using System.Runtime.Loader; 4 | 5 | namespace CoolCat.Core 6 | { 7 | public class CollectibleAssemblyLoadContext : AssemblyLoadContext 8 | { 9 | private Assembly _entryPoint = null; 10 | private bool _isEnabled = false; 11 | private readonly string _pluginName = string.Empty; 12 | private Dictionary _resourceItems = null; 13 | 14 | public CollectibleAssemblyLoadContext(string pluginName) : base(isCollectible: true) 15 | { 16 | _pluginName = pluginName; 17 | _resourceItems = new Dictionary(); 18 | } 19 | 20 | public string PluginName => _pluginName; 21 | 22 | public bool IsEnabled => _isEnabled; 23 | 24 | public void Enable() 25 | { 26 | _isEnabled = true; 27 | } 28 | 29 | public void RegisterResource(string path, byte[] fileContent) 30 | { 31 | if (_resourceItems.ContainsKey(path)) 32 | { 33 | _resourceItems[path] = fileContent; 34 | } 35 | else 36 | { 37 | _resourceItems.Add(path, fileContent); 38 | } 39 | } 40 | 41 | public void RemoveResource(string path) 42 | { 43 | if (_resourceItems.ContainsKey(path)) 44 | { 45 | _resourceItems.Remove(path); 46 | } 47 | } 48 | 49 | public byte[] LoadResource(string virtualPath) 50 | { 51 | var className = $"{PluginName}.{virtualPath.Replace("~/", string.Empty).Replace("/", ".")}"; 52 | 53 | if (_resourceItems.ContainsKey(className)) 54 | { 55 | var fileContent = _resourceItems[className]; 56 | 57 | return fileContent; 58 | } 59 | else 60 | { 61 | return null; 62 | } 63 | } 64 | 65 | public void ClearResource() 66 | { 67 | _resourceItems.Clear(); 68 | } 69 | 70 | public void Disable() 71 | { 72 | _isEnabled = false; 73 | } 74 | 75 | public void SetEntryPoint(Assembly entryPoint) 76 | { 77 | _entryPoint = entryPoint; 78 | } 79 | 80 | public Assembly GetEntryPointAssembly() 81 | { 82 | return _entryPoint; 83 | } 84 | 85 | protected override Assembly Load(AssemblyName name) 86 | { 87 | return null; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Controllers/PluginsController.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | using CoolCat.Core.Mvc.Extensions; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.AspNetCore.Mvc; 6 | using System; 7 | 8 | namespace CoolCat.Controllers 9 | { 10 | [Area("Admin")] 11 | [Authorize] 12 | public class PluginsController : Controller 13 | { 14 | private readonly IPluginManager _pluginManager = null; 15 | private readonly IReferenceContainer _referenceContainer = null; 16 | private readonly IDbConnectionFactory _dbConnectionFactory = null; 17 | private readonly IQueryDocumentation _queryDocumentation = null; 18 | 19 | public PluginsController(IPluginManager pluginManager, IReferenceContainer referenceContainer, IDbConnectionFactory dbConnectionFactory, IQueryDocumentation queryDocumentation) 20 | { 21 | _pluginManager = pluginManager; 22 | _referenceContainer = referenceContainer; 23 | _dbConnectionFactory = dbConnectionFactory; 24 | _queryDocumentation = queryDocumentation; 25 | } 26 | 27 | 28 | public IActionResult Document() 29 | { 30 | var documents = _queryDocumentation.GetAllDocuments(); 31 | return View(documents); 32 | } 33 | 34 | public IActionResult Assemblies() 35 | { 36 | System.Collections.Generic.List items = _referenceContainer.GetAll(); 37 | 38 | return View(items); 39 | } 40 | 41 | public IActionResult Index() 42 | { 43 | return View(_pluginManager.GetAllPlugins()); 44 | } 45 | 46 | [HttpGet] 47 | public IActionResult Add() 48 | { 49 | return View(); 50 | } 51 | 52 | [HttpPost] 53 | public IActionResult Upload() 54 | { 55 | PluginPackage package = new PluginPackage(Request.GetPluginStream(), _dbConnectionFactory); 56 | _pluginManager.AddPlugins(package); 57 | return RedirectToAction("Index"); 58 | } 59 | 60 | public IActionResult Enable(Guid id) 61 | { 62 | _pluginManager.EnablePlugin(id); 63 | return RedirectToAction("Index"); 64 | } 65 | 66 | public IActionResult Disable(Guid id) 67 | { 68 | _pluginManager.DisablePlugin(id); 69 | return RedirectToAction("Index"); 70 | } 71 | 72 | public IActionResult Delete(Guid id) 73 | { 74 | _pluginManager.DeletePlugin(id); 75 | 76 | return RedirectToAction("Index"); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/InternalModules/PluginManagement/Controllers/PluginController.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.DomainModel; 3 | using CoolCat.Core.Mvc.Extensions; 4 | using CoolCat.Core.Mvc.Infrastructure; 5 | using Microsoft.AspNetCore.Mvc; 6 | using System; 7 | 8 | namespace PluginManagement.Controllers 9 | { 10 | [Area(ModuleDefiniation.MODULE_NAME)] 11 | public class PluginController : CoolCatController 12 | { 13 | private readonly IPluginManager _pluginManager = null; 14 | private readonly IReferenceContainer _referenceContainer = null; 15 | private readonly IDbConnectionFactory _dbConnectionFactory = null; 16 | private readonly IQueryDocumentation _queryDocumentation = null; 17 | 18 | public PluginController(IPluginManager pluginManager, IReferenceContainer referenceContainer, IDbConnectionFactory dbConnectionFactory, IQueryDocumentation queryDocumentation, IDataStore dataStore) : base(ModuleDefiniation.MODULE_NAME, dataStore) 19 | { 20 | _pluginManager = pluginManager; 21 | _referenceContainer = referenceContainer; 22 | _dbConnectionFactory = dbConnectionFactory; 23 | _queryDocumentation = queryDocumentation; 24 | } 25 | 26 | 27 | public IActionResult Document() 28 | { 29 | var documents = _queryDocumentation.GetAllDocuments(); 30 | return View(documents); 31 | } 32 | 33 | public IActionResult Assemblies() 34 | { 35 | System.Collections.Generic.List items = _referenceContainer.GetAll(); 36 | 37 | return View(items); 38 | } 39 | 40 | public IActionResult Index() 41 | { 42 | return View(_pluginManager.GetAllPlugins()); 43 | } 44 | 45 | [HttpGet] 46 | public IActionResult Add() 47 | { 48 | return View(); 49 | } 50 | 51 | [HttpPost] 52 | public IActionResult Upload() 53 | { 54 | PluginPackage package = new PluginPackage(Request.GetPluginStream(), _dbConnectionFactory); 55 | _pluginManager.AddPlugins(package); 56 | return RedirectToAction("Index"); 57 | } 58 | 59 | public IActionResult Enable(Guid id) 60 | { 61 | _pluginManager.EnablePlugin(id); 62 | return RedirectToAction("Index"); 63 | } 64 | 65 | public IActionResult Disable(Guid id) 66 | { 67 | _pluginManager.DisablePlugin(id); 68 | return RedirectToAction("Index"); 69 | } 70 | 71 | public IActionResult Delete(Guid id) 72 | { 73 | _pluginManager.DeletePlugin(id); 74 | 75 | return RedirectToAction("Index"); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/DemoModules/BookLibrary/Controllers/BookController.cs: -------------------------------------------------------------------------------- 1 | using BookLibrary.DAL; 2 | using BookLibrary.ViewModels; 3 | using CoolCat.Core.Attributes; 4 | using CoolCat.Core.Contracts; 5 | using CoolCat.Core.Mvc.Infrastructure; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Newtonsoft.Json; 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace BookLibrary.Controllers 12 | { 13 | [Area(ModuleDefiniation.MODULE_NAME)] 14 | public class BookController : CoolCatController 15 | { 16 | private readonly BookDAL _bookDAL; 17 | private readonly INotificationRegister _notificationRegister; 18 | 19 | public BookController(IDataStore dataStore, IDbConnectionFactory dbConnectionFactory, INotificationRegister notificationRegister) : base(ModuleDefiniation.MODULE_NAME, dataStore) 20 | { 21 | _bookDAL = new BookDAL(dbConnectionFactory); 22 | _notificationRegister = notificationRegister; 23 | } 24 | 25 | [HttpGet] 26 | [Page("Book Library")] 27 | public IActionResult AvailableBooks() 28 | { 29 | var books = JsonConvert.DeserializeObject>(Query("BookInventory", "Available_Books", string.Empty)); 30 | 31 | return View(books); 32 | } 33 | 34 | [HttpPut] 35 | public IActionResult RentBook(Guid bookId) 36 | { 37 | var book = JsonConvert.DeserializeObject(Query("BookInventory", "Book_Details", JsonConvert.SerializeObject(new { bookId }))); 38 | 39 | var date = DateTime.Today; 40 | 41 | _bookDAL.RentBook(new Dtos.RentBookDTO 42 | { 43 | BookId = bookId, 44 | BookName = book.BookName, 45 | DateIssued = book.DateIssued, 46 | ISBN = book.ISBN, 47 | RentDate = date 48 | }); 49 | 50 | _notificationRegister.Publish("BookOutEvent", JsonConvert.SerializeObject(new 51 | { 52 | BookId = bookId, 53 | OutDate = date 54 | })); 55 | 56 | return Json(new 57 | { 58 | result = true 59 | }); 60 | } 61 | 62 | [HttpPut] 63 | public IActionResult ReturnBook(Guid rentId) 64 | { 65 | var bookId = _bookDAL.GetBookId(rentId); 66 | var date = DateTime.Today; 67 | 68 | _bookDAL.ReturnBook(new Dtos.ReturnBookDTO 69 | { 70 | RentId = rentId, 71 | ReturnDate = DateTime.Now 72 | }); 73 | 74 | _notificationRegister.Publish("BookInEvent", JsonConvert.SerializeObject(new 75 | { 76 | BookId = bookId, 77 | OutDate = date 78 | })); 79 | 80 | return Json(new 81 | { 82 | result = true 83 | }); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/CoolCat.Core/Helpers/DefaultReferenceLoader.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Consts; 2 | using CoolCat.Core.Contracts; 3 | using Microsoft.Extensions.Logging; 4 | using System.IO; 5 | using System.Reflection; 6 | 7 | namespace CoolCat.Core.Helpers 8 | { 9 | public class DefaultReferenceLoader : IReferenceLoader 10 | { 11 | private readonly IReferenceContainer _referenceContainer = null; 12 | private readonly ILogger _logger = null; 13 | 14 | public DefaultReferenceLoader(IReferenceContainer referenceContainer, ILogger logger) 15 | { 16 | _referenceContainer = referenceContainer; 17 | _logger = logger; 18 | } 19 | 20 | public void LoadStreamsIntoContext(CollectibleAssemblyLoadContext context, string moduleFolder, Assembly assembly) 21 | { 22 | AssemblyName[] references = assembly.GetReferencedAssemblies(); 23 | 24 | foreach (AssemblyName item in references) 25 | { 26 | string name = item.Name; 27 | 28 | string version = item.Version.ToString(); 29 | 30 | Stream stream = _referenceContainer.GetStream(name, version); 31 | 32 | if (stream != null) 33 | { 34 | _logger.LogDebug($"Found the cached reference '{name}' v.{version}"); 35 | context.LoadFromStream(stream); 36 | } 37 | else 38 | { 39 | 40 | if (IsSharedFreamwork(name) || name.Contains("CoolCat.Core")) 41 | { 42 | continue; 43 | } 44 | 45 | string dllName = $"{name}.dll"; 46 | string filePath = $"{moduleFolder}/{dllName}"; 47 | 48 | if (!File.Exists(filePath)) 49 | { 50 | _logger.LogWarning($"The package '{dllName}' in '{filePath}' is missing."); 51 | continue; 52 | } 53 | 54 | using (FileStream fs = new FileStream(filePath, FileMode.Open)) 55 | { 56 | Assembly referenceAssembly = context.LoadFromStream(fs); 57 | 58 | MemoryStream memoryStream = new MemoryStream(); 59 | 60 | fs.Position = 0; 61 | fs.CopyTo(memoryStream); 62 | fs.Position = 0; 63 | memoryStream.Position = 0; 64 | _referenceContainer.SaveStream(name, version, memoryStream); 65 | 66 | LoadStreamsIntoContext(context, moduleFolder, referenceAssembly); 67 | } 68 | } 69 | } 70 | } 71 | 72 | private bool IsSharedFreamwork(string name) 73 | { 74 | return SharedFrameworkConst.SharedFrameworkDLLs.Contains($"{name}.dll"); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/CoolCatModuleDocumentation.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Attributes; 2 | using CoolCat.Core.Contracts; 3 | using CoolCat.Core.DomainModel; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace CoolCat.Core.Mvc.Infrastructure 10 | { 11 | public class CoolCatModuleDocumentation : IQueryDocumentation 12 | { 13 | private Dictionary> _documentations = new Dictionary>(); 14 | 15 | public void BuildDocumentation(string moduleName, IDataStoreQuery query) 16 | { 17 | if (!_documentations.ContainsKey(moduleName)) 18 | { 19 | _documentations.Add(moduleName, new List()); 20 | } 21 | 22 | _documentations[moduleName].Add(RefactorDocuments(query)); 23 | 24 | } 25 | 26 | public Dictionary> GetAllDocuments() 27 | { 28 | return _documentations; 29 | } 30 | 31 | private QueryDocumentItem RefactorDocuments(IDataStoreQuery query) 32 | { 33 | var type = query.GetType(); 34 | 35 | var responseType = ((ResponseTypeAttribute[])Attribute.GetCustomAttributes(type, typeof(ResponseTypeAttribute))).FirstOrDefault(); 36 | 37 | var requestType = ((RequestParameterTypeAttribute[])Attribute.GetCustomAttributes(type, typeof(RequestParameterTypeAttribute))).FirstOrDefault(); 38 | 39 | var item = new QueryDocumentItem(); 40 | item.QueryName = query.QueryName; 41 | 42 | if (requestType is NoneRequestParameterAttribute) 43 | { 44 | item.RequestSample = "No Request Parameter."; 45 | } 46 | else 47 | { 48 | item.RequestSample = BuildSampleFromType(requestType.RequestType); 49 | } 50 | 51 | item.ResponseSample = BuildSampleFromType(responseType.ResponseType); 52 | 53 | return item; 54 | } 55 | 56 | private string BuildSampleFromType(Type t) 57 | { 58 | Object obj = null; 59 | 60 | if (t.IsGenericType) 61 | { 62 | var innerType = t.GetGenericArguments()[0]; 63 | 64 | var listType = typeof(List<>).MakeGenericType(innerType); 65 | var list = Activator.CreateInstance(listType); 66 | 67 | var addMethod = listType.GetMethod("Add"); 68 | addMethod.Invoke(list, new object[] { innerType.Assembly.CreateInstance(innerType.FullName) }); 69 | 70 | var sample = JsonConvert.SerializeObject(list); 71 | return sample; 72 | } 73 | else 74 | { 75 | obj = t.Assembly.CreateInstance(t.FullName); 76 | var sample = JsonConvert.SerializeObject(obj); 77 | return sample; 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/DemoModules/BookInventory/BookInventory/DAL/BookDAL.cs: -------------------------------------------------------------------------------- 1 | using BookInventory.Dtos; 2 | using BookInventory.ViewModels; 3 | using CoolCat.Core.Contracts; 4 | using MySql.Data.MySqlClient; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Data; 8 | using System.Linq; 9 | using Dapper; 10 | 11 | namespace BookInventory.DAL 12 | { 13 | public class BookDAL : IDisposable 14 | { 15 | private IDbConnection _dbConnection = null; 16 | 17 | public BookDAL(IDbConnectionFactory dbConnectionFactory) 18 | { 19 | _dbConnection = dbConnectionFactory.GetConnection(); 20 | } 21 | 22 | public List GetBooks() 23 | { 24 | var sql = "SELECT * FROM book"; 25 | 26 | var books = _dbConnection.Query(sql).ToList(); 27 | return books; 28 | } 29 | 30 | public void AddBook(AddBookDto dto) 31 | { 32 | var sql = @"INSERT INTO Book(BookId, BookName, ISBN, DateIssued, Description) 33 | VALUES(@id, @bookName, @isbn, @dateIssued, @description)"; 34 | 35 | _dbConnection.Execute(sql, new 36 | { 37 | id = Guid.NewGuid(), 38 | bookName = dto.BookName, 39 | isbn = dto.ISBN, 40 | dateIssued = dto.DateIssued, 41 | description = dto.Description 42 | }); 43 | } 44 | 45 | public BookDetailViewModel GetBook(Guid bookId) 46 | { 47 | var sql = "SELECT * FROM Book WHERE BookId=@id"; 48 | var book = _dbConnection.QueryFirstOrDefault(sql, new { id = bookId }); 49 | return book; 50 | } 51 | 52 | public void UpdateBook(Guid bookId, UpdateBookDto dto) 53 | { 54 | var sql = "UPDATE Book SET BookName=@bookName, ISBN=@isbn, DateIssued=@dateIssued, Description=@description WHERE BookId=@id"; 55 | 56 | _dbConnection.Execute(sql, new 57 | { 58 | id = bookId, 59 | bookName = dto.BookName, 60 | isbn = dto.ISBN, 61 | dateIssued = dto.DateIssued, 62 | description = dto.Description 63 | }); 64 | } 65 | 66 | public void DeleteBook(Guid bookId) 67 | { 68 | var sql = "DELETE FROM Book WHERE BookId=@id"; 69 | 70 | _dbConnection.Execute(sql, new 71 | { 72 | id = bookId 73 | }); 74 | } 75 | 76 | public void UpdateBookStatus(Guid bookId, bool isOut) 77 | { 78 | var sql = "UPDATE Book SET Status=@status WHERE BookId=@id"; 79 | 80 | _dbConnection.Execute(sql, new 81 | { 82 | id = bookId, 83 | status = isOut 84 | }); 85 | } 86 | 87 | public void Dispose() 88 | { 89 | _dbConnection.Close(); 90 | _dbConnection.Dispose(); 91 | _dbConnection = null; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Repository.MySql/UnitOfWork.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.Repositories; 3 | using Dapper; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Data; 7 | 8 | namespace CoolCat.Core.Repository.MySql 9 | { 10 | public class UnitOfWork : IUnitOfWork, IDisposable 11 | { 12 | private IDbConnectionFactory _dbConnectionFactory = null; 13 | private IDbConnection _dbConnection = null; 14 | private IPluginRepository _pluginRepository = null; 15 | private ISiteRepository _siteRepository = null; 16 | private IDbTransaction _transactionScope = null; 17 | private readonly List _commands; 18 | 19 | public UnitOfWork(IDbConnectionFactory dbConnectionFactory) 20 | { 21 | _commands = new List(); 22 | _dbConnectionFactory = dbConnectionFactory; 23 | _dbConnection = _dbConnectionFactory.GetConnection(); 24 | } 25 | 26 | public IPluginRepository PluginRepository 27 | { 28 | get 29 | { 30 | if (_pluginRepository == null) 31 | { 32 | _pluginRepository = new PluginRepository(_dbConnection, _commands); 33 | } 34 | 35 | return _pluginRepository; 36 | } 37 | } 38 | 39 | public ISiteRepository SiteRepository 40 | { 41 | get 42 | { 43 | if (_siteRepository == null) 44 | { 45 | _siteRepository = new SiteRepository(_dbConnection, _commands); 46 | } 47 | 48 | return _siteRepository; 49 | } 50 | } 51 | 52 | public void Begin() 53 | { 54 | _transactionScope = _dbConnection.BeginTransaction(); 55 | } 56 | 57 | public void RollBack() 58 | { 59 | if (_transactionScope == null) 60 | { 61 | throw new Exception("Transaction is missing. Please call the Begin method first."); 62 | } 63 | 64 | _transactionScope.Rollback(); 65 | } 66 | 67 | public void Commit() 68 | { 69 | if (_transactionScope == null) 70 | { 71 | throw new Exception("Transaction is missing. Please call the Begin method first."); 72 | } 73 | 74 | _transactionScope.Commit(); 75 | } 76 | 77 | public bool CheckDatabase() 78 | { 79 | var dbSetting = _dbConnection.QueryFirstOrDefault("SELECT `Value` FROM SiteSettings WHERE `Key` = @key", new { key = "SYSTEM_INSTALLED" }); 80 | return (dbSetting != null && dbSetting.ToString() == "1"); 81 | } 82 | 83 | public void MarkAsInstalled() 84 | { 85 | _dbConnection.Execute("UPDATE SiteSettings SET `Value`='1' WHERE `Key`=@key", new { key = "SYSTEM_INSTALLED" }); 86 | } 87 | 88 | public void Dispose() 89 | { 90 | _dbConnection.Close(); 91 | _dbConnection.Dispose(); 92 | _dbConnection = null; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/Plugins/Document.cshtml: -------------------------------------------------------------------------------- 1 | @model Dictionary> 2 | @{ 3 | ViewData["Title"] = "Documentation"; 4 | } 5 | 6 |

Module Query Documentation

7 | 8 | @foreach (var item in Model) 9 | { 10 |
11 |
@item.Key
12 | 13 | @foreach (var d in item.Value) 14 | { 15 |
16 |
@d.QueryName
17 |
18 |
19 |
Request Sample:
20 |
21 | 22 |
23 |
24 |
25 |
Response Sample:
26 |
27 | 28 |
29 |
30 |
31 |
Calling Sample:
32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 | } 40 |
41 | } 42 | 43 | @section Scripts{ 44 | 45 | 89 | 90 | } -------------------------------------------------------------------------------- /src/CoolCat.Database/CoolCat.Database.sqlproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | Mystique.Database 7 | 2.0 8 | 4.1 9 | {6e246a60-a973-4c51-ac97-ff607d7cec1b} 10 | Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider 11 | Database 12 | 13 | 14 | Mystique.Database 15 | Mystique.Database 16 | 1033, CI 17 | BySchemaAndSchemaType 18 | True 19 | v4.5 20 | CS 21 | Properties 22 | False 23 | True 24 | True 25 | 26 | 27 | bin\Release\ 28 | $(MSBuildProjectName).sql 29 | False 30 | pdbonly 31 | true 32 | false 33 | true 34 | prompt 35 | 4 36 | 37 | 38 | bin\Debug\ 39 | $(MSBuildProjectName).sql 40 | false 41 | true 42 | full 43 | false 44 | true 45 | true 46 | prompt 47 | 4 48 | 49 | 50 | 11.0 51 | 52 | True 53 | 11.0 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Views/System/Setup.cshtml: -------------------------------------------------------------------------------- 1 | @model List 2 | @using CoolCat.Utilities; 3 | @{ 4 | ViewData["Title"] = "Setup"; 5 | 6 | var presetPluginLoader = new PresetPluginLoader(); 7 | var plugins = presetPluginLoader.LoadPlugins(); 8 | } 9 | 10 | @section Header{ 11 | 12 | } 13 | 14 |

Setup

15 | 16 |

17 | Welcome to use CoolCat. The following step will help you to install the framework. 18 |

19 | 20 | 21 | 77 | 78 | 79 | @section Scripts{ 80 | 81 | 82 | 83 | } 84 | 85 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.3.1 (https://getbootstrap.com/) 3 | * Copyright 2011-2019 The Bootstrap Authors 4 | * Copyright 2011-2019 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}select{word-wrap:normal}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/Version.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace CoolCat.Core.DomainModel 6 | { 7 | public class Version : IComparable 8 | { 9 | private const string _pattern = "^[0-9]*$"; 10 | private static readonly Regex _regex = new Regex(_pattern); 11 | 12 | public Version(string versionNumber) 13 | { 14 | if (Validate(versionNumber)) 15 | { 16 | VersionNumber = versionNumber; 17 | } 18 | else 19 | { 20 | throw new ArgumentException("The version number is invalid."); 21 | } 22 | 23 | } 24 | 25 | public int PrimaryVersion => Convert.ToInt32(VersionNumber.Split('.')[0]); 26 | 27 | public int SecondaryVersion => Convert.ToInt32(VersionNumber.Split('.')[1]); 28 | 29 | public int MinorVersion => Convert.ToInt32(VersionNumber.Split('.')[2]); 30 | 31 | private bool Validate(string versionNumber) 32 | { 33 | if (!string.IsNullOrEmpty(versionNumber) && versionNumber.Split(".").Length == 3) 34 | { 35 | string primary = versionNumber.Split('.')[0]; 36 | string secondray = versionNumber.Split('.')[1]; 37 | string minor = versionNumber.Split('.')[2]; 38 | 39 | return _regex.IsMatch(primary) 40 | && _regex.IsMatch(secondray) 41 | && _regex.IsMatch(minor); 42 | } 43 | else 44 | { 45 | return false; 46 | } 47 | } 48 | 49 | public string VersionNumber { get; set; } 50 | 51 | public int CompareTo([AllowNull] Version other) 52 | { 53 | if (PrimaryVersion > other.PrimaryVersion 54 | || (PrimaryVersion == other.PrimaryVersion && SecondaryVersion > other.SecondaryVersion) 55 | || (PrimaryVersion == other.PrimaryVersion && SecondaryVersion == other.SecondaryVersion && MinorVersion > other.MinorVersion)) 56 | { 57 | return 1; 58 | } 59 | else if (PrimaryVersion == other.PrimaryVersion 60 | && SecondaryVersion == other.SecondaryVersion 61 | && MinorVersion == other.MinorVersion) 62 | { 63 | return 0; 64 | } 65 | else 66 | { 67 | return -1; 68 | } 69 | } 70 | 71 | public static bool operator ==(Version left, Version right) 72 | { 73 | if (left == null || right == null) 74 | { 75 | return false; 76 | } 77 | 78 | return left.VersionNumber.Equals(right.VersionNumber); 79 | } 80 | 81 | public static bool operator !=(Version left, Version right) 82 | { 83 | return !(left == right); 84 | } 85 | 86 | public static implicit operator Version(string versionNumber) 87 | { 88 | return new Version(versionNumber); 89 | } 90 | 91 | public static bool operator >(Version x, Version y) 92 | { 93 | return x.PrimaryVersion > y.PrimaryVersion || 94 | (x.PrimaryVersion == y.PrimaryVersion && x.SecondaryVersion > y.SecondaryVersion) 95 | || (x.PrimaryVersion == y.PrimaryVersion && x.SecondaryVersion == y.SecondaryVersion && x.MinorVersion > y.MinorVersion); 96 | } 97 | 98 | public static bool operator <(Version x, Version y) 99 | { 100 | return x.PrimaryVersion < y.PrimaryVersion || 101 | (x.PrimaryVersion == y.PrimaryVersion && x.SecondaryVersion < y.SecondaryVersion) 102 | || (x.PrimaryVersion == y.PrimaryVersion && x.SecondaryVersion == y.SecondaryVersion && x.MinorVersion < y.MinorVersion); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/MvcModuleSetup.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.Mvc.Infrastructure; 3 | using CoolCat.Mvc.Infrastructure; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.AspNetCore.Mvc.ApplicationParts; 6 | using Microsoft.AspNetCore.Mvc.Razor.Compilation; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using System; 9 | using System.IO; 10 | using System.Linq; 11 | 12 | namespace CoolCat.Core.Mvc 13 | { 14 | public class MvcModuleSetup : IMvcModuleSetup 15 | { 16 | private readonly ApplicationPartManager _partManager; 17 | private readonly IReferenceLoader _referenceLoader = null; 18 | private readonly IHttpContextAccessor _context; 19 | 20 | public MvcModuleSetup(ApplicationPartManager partManager, IReferenceLoader referenceLoader, IHttpContextAccessor httpContextAccessor) 21 | { 22 | _partManager = partManager; 23 | _referenceLoader = referenceLoader; 24 | _context = httpContextAccessor; 25 | } 26 | 27 | public void EnableModule(string moduleName) 28 | { 29 | ServiceProvider provider = CoolCatStartup.Services.BuildServiceProvider(); 30 | var contextProvider = new CollectibleAssemblyLoadContextProvider(); 31 | 32 | using (IServiceScope scope = provider.CreateScope()) 33 | { 34 | var dataStore = scope.ServiceProvider.GetService(); 35 | var documentation = scope.ServiceProvider.GetService(); 36 | 37 | var context = contextProvider.Get(moduleName, _partManager, scope, dataStore, documentation); 38 | PluginsLoadContexts.Add(moduleName, context); 39 | context.Enable(); 40 | } 41 | 42 | ResetControllActions(); 43 | 44 | //if (!PluginsLoadContexts.Any(moduleName)) 45 | //{ 46 | // ServiceProvider provider = CoolCatStartup.Services.BuildServiceProvider(); 47 | // var contextProvider = new CollectibleAssemblyLoadContextProvider(); 48 | 49 | // using (IServiceScope scope = provider.CreateScope()) 50 | // { 51 | // var dataStore = scope.ServiceProvider.GetService(); 52 | // var documentation = scope.ServiceProvider.GetService(); 53 | 54 | // var context = contextProvider.Get(moduleName, _partManager, scope, dataStore, documentation); 55 | // PluginsLoadContexts.Add(moduleName, context); 56 | // } 57 | 58 | // ResetControllActions(); 59 | //} 60 | } 61 | 62 | public void DisableModule(string moduleName) 63 | { 64 | var controller = _partManager.ApplicationParts.First(p => p.Name == moduleName); 65 | _partManager.ApplicationParts.Remove(controller); 66 | 67 | var ui = _partManager.ApplicationParts.First(p => p.Name == $"{moduleName}.Views"); 68 | _partManager.ApplicationParts.Remove(ui); 69 | 70 | var context = PluginsLoadContexts.Get(moduleName); 71 | context.Disable(); 72 | 73 | PluginsLoadContexts.Remove(moduleName); 74 | 75 | GC.Collect(); 76 | GC.WaitForPendingFinalizers(); 77 | 78 | ResetControllActions(); 79 | } 80 | 81 | public void DeleteModule(string moduleName) 82 | { 83 | PluginsLoadContexts.Remove(moduleName); 84 | 85 | var directory = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", moduleName)); 86 | directory.Delete(true); 87 | } 88 | 89 | private void ResetControllActions() 90 | { 91 | var provider = _context.HttpContext.RequestServices.GetService(typeof(IViewCompilerProvider)) as CoolCatViewCompilerProvider; 92 | provider.Refresh(); 93 | CoolCatActionDescriptorChangeProvider.Instance.HasChanged = true; 94 | CoolCatActionDescriptorChangeProvider.Instance.TokenSource.Cancel(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/CoolCat/Startup.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Models; 2 | using CoolCat.Core.Mvc.Infrastructure; 3 | using CoolCat.Core.Repository.MySql.Migrations; 4 | using FluentMigrator.Runner; 5 | using FluentMigrator.Runner.Initialization; 6 | using Microsoft.AspNetCore.Authentication.Cookies; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.FileProviders; 12 | using MySql.Data.MySqlClient; 13 | using System; 14 | using System.Threading; 15 | 16 | namespace CoolCat 17 | { 18 | public class Startup 19 | { 20 | public Startup(IConfiguration configuration) 21 | { 22 | Configuration = configuration; 23 | } 24 | 25 | public IConfiguration Configuration { get; } 26 | 27 | // This method gets called by the runtime. Use this method to add services to the container. 28 | public void ConfigureServices(IServiceCollection services) 29 | { 30 | services.AddOptions(); 31 | services.Configure(Configuration.GetSection("ConnectionStringSetting")); 32 | 33 | ConnectionStringSetting siteSettings = new ConnectionStringSetting(); 34 | 35 | Configuration.Bind("ConnectionStringSetting", siteSettings); 36 | 37 | TryToConnect(siteSettings); 38 | 39 | using (IServiceScope scope = CreateServices(siteSettings).CreateScope()) 40 | { 41 | IMigrationRunner runner = scope.ServiceProvider.GetRequiredService(); 42 | runner.MigrateUp(); 43 | } 44 | 45 | services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) 46 | .AddCookie(o => 47 | { 48 | o.LoginPath = "/Admin/System/Login"; 49 | }); 50 | services.CoolCatSetup(Configuration); 51 | } 52 | 53 | private static int ErrorCount = 0; 54 | 55 | private void TryToConnect(ConnectionStringSetting siteSettings) 56 | { 57 | while (ErrorCount < 10) 58 | { 59 | try 60 | { 61 | using (var connection = new MySqlConnection(siteSettings.ConnectionString)) 62 | { 63 | connection.Open(); 64 | Console.WriteLine("The target database connected."); 65 | return; 66 | } 67 | } 68 | catch 69 | { 70 | Console.WriteLine("The target database hasn't been prepared."); 71 | ErrorCount++; 72 | } 73 | 74 | Thread.Sleep(10000); 75 | } 76 | } 77 | 78 | private static IServiceProvider CreateServices(ConnectionStringSetting settings) 79 | { 80 | //Console.WriteLine(settings.ConnectionString); 81 | 82 | return new ServiceCollection().AddFluentMigratorCore().ConfigureRunner(rb => 83 | rb.AddMySql5() 84 | .WithGlobalConnectionString(settings.ConnectionString) 85 | .ScanIn(typeof(InitialDB).Assembly) 86 | .For 87 | .Migrations()) 88 | .Configure(opt => 89 | { 90 | opt.Tags = new[] { "System" }; 91 | }) 92 | .AddLogging(lb => lb.AddFluentMigratorConsole()) 93 | .BuildServiceProvider(false); 94 | } 95 | 96 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 97 | { 98 | 99 | app.UseDeveloperExceptionPage(); 100 | 101 | 102 | #if DEBUG 103 | app.UseStaticFiles(new StaticFileOptions 104 | { 105 | FileProvider = new PhysicalFileProvider(@"F:\D1\CoolCat\src\CoolCat\wwwroot") 106 | //FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), @"wwwroot")) 107 | }); 108 | #endif 109 | 110 | // #if !DEBUG 111 | 112 | //app.UseStaticFiles(); 113 | // #endif 114 | 115 | 116 | 117 | app.CoolCatRoute(); 118 | } 119 | } 120 | } 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/CoolCat/wwwroot/css/site.css: -------------------------------------------------------------------------------- 1 | /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification 2 | for details on configuring this project to bundle and minify static web assets. */ 3 | 4 | a.navbar-brand { 5 | white-space: normal; 6 | text-align: center; 7 | word-break: break-all; 8 | } 9 | 10 | /* Sticky footer styles 11 | -------------------------------------------------- */ 12 | html { 13 | font-size: 14px; 14 | } 15 | body { 16 | font-size: .9rem !important; 17 | } 18 | .form-control{ 19 | font-size: .9rem !important; 20 | } 21 | .display-4 { 22 | font-size: 3.5rem !important; 23 | } 24 | @media (min-width: 768px) { 25 | html { 26 | font-size: 16px; 27 | } 28 | } 29 | .dropdown-menu{ 30 | padding: 0; 31 | font-size: .9rem !important; 32 | } 33 | h1{ 34 | margin-bottom: 1rem; 35 | font-size: 1.5rem !important; 36 | } 37 | .document-title { 38 | font-size: 1.4rem !important; 39 | font-weight: 500; 40 | } 41 | .document-query-name { 42 | font-size: 1.2rem !important; 43 | font-weight: 500; 44 | margin-bottom: .4rem; 45 | margin-top: .8rem; 46 | color: #5cbedf; 47 | } 48 | button.accept-policy { 49 | font-size: 1rem; 50 | line-height: inherit; 51 | } 52 | .dropdown-menu li{ 53 | border-bottom: 1px solid #ececec; 54 | } 55 | .dropdown-menu li:last-child{ 56 | border-bottom: 0px solid #ececec; 57 | } 58 | .dropdown-menu li:hover{ 59 | background-color: #5cbedf; 60 | } 61 | .dropdown-menu li:hover .text-dark{ 62 | color: #fff !important; 63 | } 64 | .navbar-expand-sm .navbar-nav .nav-link{ 65 | padding-left: .8rem; 66 | padding-right: .8rem; 67 | } 68 | .border-top { 69 | border-top: 1px solid #e5e5e5; 70 | } 71 | .border-bottom { 72 | border-bottom: 1px solid #e5e5e5; 73 | } 74 | .mb-3, .my-3{ 75 | margin-bottom: 2rem !important; 76 | } 77 | .marginBottom1rem { 78 | margin-bottom: 1rem; 79 | } 80 | 81 | .btn-danger { 82 | box-shadow: none; 83 | border-radius: .25rem; 84 | background-color: #ef5a92; 85 | border: 0; 86 | color: #fff; 87 | padding: .375rem .75rem; 88 | } 89 | .btn-danger:hover { 90 | box-shadow: none; 91 | border-radius: .25rem; 92 | background-color: #fd2274; 93 | border: 0; 94 | color: #fff; 95 | padding: .375rem .75rem; 96 | } 97 | .btn-disabled { 98 | box-shadow: none; 99 | border-radius: .25rem; 100 | background-color: #d8d8d8; 101 | border: 0; 102 | color: #fff; 103 | padding: .375rem .75rem; 104 | } 105 | .btn-disabled:hover { 106 | box-shadow: none; 107 | border-radius: .25rem; 108 | background-color: #5cbedf; 109 | border: 0; 110 | color: #fff; 111 | padding: .375rem .75rem; 112 | } 113 | .btn-primary{ 114 | color: #fff; 115 | background-color: #5cbedf; 116 | border-color: #5cbedf; 117 | border: 0; 118 | border-radius: .25rem; 119 | padding: .375rem .75rem; 120 | } 121 | .box-shadow { 122 | box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); 123 | } 124 | 125 | .table thead th { 126 | background-color: #5cbedf; 127 | color: #fff; 128 | border-top: 0; 129 | border-bottom: 0; 130 | } 131 | .table thead th:first-child{ 132 | border-top-left-radius:.4rem; 133 | border-bottom-left-radius: .4rem; 134 | } 135 | .table thead th:last-child{ 136 | border-top-right-radius: .4rem; 137 | border-bottom-right-radius: .4rem; 138 | } 139 | .table tbody tr:first-child td{ 140 | border-top: 0; 141 | } 142 | .table td { 143 | vertical-align: middle; 144 | } 145 | .sample-content { 146 | border-radius: .25rem; 147 | margin-top: .5rem; 148 | padding: .5rem; 149 | border-color: #ced4da 150 | } 151 | 152 | .footLeft{ 153 | float: left; 154 | } 155 | .footRight{ 156 | float: right; 157 | } 158 | .footRight img{ 159 | width: 120px; 160 | } 161 | /* Sticky footer styles 162 | -------------------------------------------------- */ 163 | html { 164 | position: relative; 165 | min-height: 100%; 166 | } 167 | 168 | body { 169 | /* Margin bottom by footer height */ 170 | margin-bottom: 60px; 171 | } 172 | .footer { 173 | position: absolute; 174 | bottom: 0; 175 | width: 100%; 176 | white-space: nowrap; 177 | /* Set the fixed height of the footer here */ 178 | height: 60px; 179 | line-height: 60px; /* Vertically center the text there */ 180 | } 181 | 182 | .out { 183 | background: #ef5a92; 184 | padding: 5px; 185 | border-radius: 3px; 186 | color: white; 187 | } 188 | 189 | .in { 190 | background: #5cbedf; 191 | padding: 5px; 192 | border-radius: 3px; 193 | color: white; 194 | } 195 | 196 | -------------------------------------------------------------------------------- /src/CoolCat.Core/DomainModel/PluginPackage.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Contracts; 2 | using CoolCat.Core.Exceptions; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.IO.Compression; 8 | using System.Linq; 9 | using ZipTool = System.IO.Compression.ZipArchive; 10 | 11 | namespace CoolCat.Core.DomainModel 12 | { 13 | public class PluginPackage 14 | { 15 | private PluginConfiguration _pluginConfiguration = null; 16 | private Stream _zipStream = null; 17 | private string _tempFolderName = string.Empty; 18 | private string _folderName = string.Empty; 19 | private IDbConnectionFactory _dbConnectionFactory = null; 20 | 21 | public PluginConfiguration Configuration => _pluginConfiguration; 22 | 23 | public PluginPackage(Stream stream, IDbConnectionFactory dbConnectionFactory) 24 | { 25 | _zipStream = stream; 26 | _dbConnectionFactory = dbConnectionFactory; 27 | Initialize(stream); 28 | } 29 | 30 | public List GetAllMigrations(string connectionString) 31 | { 32 | CollectibleAssemblyLoadContext context = new CollectibleAssemblyLoadContext(_pluginConfiguration.Name); 33 | string assemblyPath = Path.Combine(_tempFolderName, $"{_pluginConfiguration.Name}.dll"); 34 | 35 | using (FileStream fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read)) 36 | { 37 | 38 | System.Reflection.Assembly assembly = context.LoadFromStream(fs); 39 | IEnumerable migrationTypes = assembly.ExportedTypes.Where(p => p.GetInterfaces().Contains(typeof(IMigration))); 40 | 41 | List migrations = new List(); 42 | foreach (Type migrationType in migrationTypes) 43 | { 44 | System.Reflection.ConstructorInfo constructor = migrationType.GetConstructors().First(p => p.GetParameters().Count() == 1 && p.GetParameters()[0].ParameterType == typeof(IDbConnectionFactory)); 45 | 46 | migrations.Add((IMigration)constructor.Invoke(new object[] { _dbConnectionFactory })); 47 | } 48 | 49 | context.Unload(); 50 | 51 | GC.Collect(); 52 | GC.WaitForPendingFinalizers(); 53 | 54 | return migrations.OrderBy(p => p.Version).ToList(); 55 | } 56 | } 57 | 58 | public void Initialize(Stream stream) 59 | { 60 | _zipStream = stream; 61 | _tempFolderName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{ Guid.NewGuid()}"); 62 | ZipTool archive = new ZipTool(_zipStream, ZipArchiveMode.Read); 63 | 64 | archive.ExtractToDirectory(_tempFolderName); 65 | 66 | DirectoryInfo folder = new DirectoryInfo(_tempFolderName); 67 | 68 | FileInfo[] files = folder.GetFiles(); 69 | 70 | FileInfo configFile = files.SingleOrDefault(p => p.Name == "plugin.json"); 71 | 72 | if (configFile == null) 73 | { 74 | throw new MissingConfigurationFileException(); 75 | } 76 | else 77 | { 78 | using (FileStream s = configFile.OpenRead()) 79 | { 80 | LoadConfiguration(s); 81 | } 82 | } 83 | } 84 | 85 | public void SetupFolder() 86 | { 87 | ZipTool archive = new ZipTool(_zipStream, ZipArchiveMode.Read); 88 | _zipStream.Position = 0; 89 | _folderName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules", $"{_pluginConfiguration.Name}"); 90 | 91 | archive.ExtractToDirectory(_folderName, true); 92 | 93 | DirectoryInfo folder = new DirectoryInfo(_tempFolderName); 94 | folder.Delete(true); 95 | } 96 | 97 | private void LoadConfiguration(Stream stream) 98 | { 99 | using (StreamReader sr = new StreamReader(stream)) 100 | { 101 | string content = sr.ReadToEnd(); 102 | _pluginConfiguration = JsonConvert.DeserializeObject(content); 103 | 104 | if (_pluginConfiguration == null) 105 | { 106 | throw new WrongFormatConfigurationException(); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Compiler/CoolCatViewCompiler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc.Razor.Compilation; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Primitives; 4 | using System; 5 | using System.Collections.Concurrent; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using System.Threading.Tasks; 9 | 10 | namespace CoolCat.Core.Mvc.Infrastructure 11 | { 12 | public class CoolCatViewCompiler : IViewCompiler 13 | { 14 | private readonly Dictionary> _compiledViews; 15 | private readonly ConcurrentDictionary _normalizedPathCache; 16 | private readonly ILogger _logger; 17 | 18 | public CoolCatViewCompiler( 19 | IList compiledViews, 20 | ILogger logger) 21 | { 22 | if (compiledViews == null) 23 | { 24 | throw new ArgumentNullException(nameof(compiledViews)); 25 | } 26 | 27 | if (logger == null) 28 | { 29 | throw new ArgumentNullException(nameof(logger)); 30 | } 31 | 32 | _logger = logger; 33 | _normalizedPathCache = new ConcurrentDictionary(StringComparer.Ordinal); 34 | 35 | _compiledViews = new Dictionary>( 36 | compiledViews.Count, 37 | StringComparer.OrdinalIgnoreCase); 38 | 39 | foreach (var compiledView in compiledViews) 40 | { 41 | 42 | if (!_compiledViews.ContainsKey(compiledView.RelativePath)) 43 | { 44 | _compiledViews.Add(compiledView.RelativePath, Task.FromResult(compiledView)); 45 | } 46 | } 47 | 48 | if (_compiledViews.Count == 0) 49 | { 50 | 51 | } 52 | } 53 | 54 | public Task CompileAsync(string relativePath) 55 | { 56 | if (relativePath == null) 57 | { 58 | throw new ArgumentNullException(nameof(relativePath)); 59 | } 60 | 61 | if (_compiledViews.TryGetValue(relativePath, out var cachedResult)) 62 | { 63 | return cachedResult; 64 | } 65 | 66 | var normalizedPath = GetNormalizedPath(relativePath); 67 | if (_compiledViews.TryGetValue(normalizedPath, out cachedResult)) 68 | { 69 | 70 | return cachedResult; 71 | } 72 | 73 | return Task.FromResult(new CompiledViewDescriptor 74 | { 75 | RelativePath = normalizedPath, 76 | ExpirationTokens = Array.Empty(), 77 | }); 78 | } 79 | 80 | private string GetNormalizedPath(string relativePath) 81 | { 82 | Debug.Assert(relativePath != null); 83 | if (relativePath.Length == 0) 84 | { 85 | return relativePath; 86 | } 87 | 88 | if (!_normalizedPathCache.TryGetValue(relativePath, out var normalizedPath)) 89 | { 90 | normalizedPath = NormalizePath(relativePath); 91 | _normalizedPathCache[relativePath] = normalizedPath; 92 | } 93 | 94 | return normalizedPath; 95 | } 96 | 97 | public static string NormalizePath(string path) 98 | { 99 | var addLeadingSlash = path[0] != '\\' && path[0] != '/'; 100 | var transformSlashes = path.IndexOf('\\') != -1; 101 | 102 | if (!addLeadingSlash && !transformSlashes) 103 | { 104 | return path; 105 | } 106 | 107 | var length = path.Length; 108 | if (addLeadingSlash) 109 | { 110 | length++; 111 | } 112 | 113 | return string.Create(length, (path, addLeadingSlash), (span, tuple) => 114 | { 115 | var (pathValue, addLeadingSlashValue) = tuple; 116 | var spanIndex = 0; 117 | 118 | if (addLeadingSlashValue) 119 | { 120 | span[spanIndex++] = '/'; 121 | } 122 | 123 | foreach (var ch in pathValue) 124 | { 125 | span[spanIndex++] = ch == '\\' ? '/' : ch; 126 | } 127 | }); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/CoolCat/Areas/Admin/Controllers/SystemController.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.Consts; 2 | using CoolCat.Core.Contracts; 3 | using CoolCat.Core.DTOs; 4 | using CoolCat.Models; 5 | using CoolCat.Utilities; 6 | using Microsoft.AspNetCore.Authentication; 7 | using Microsoft.AspNetCore.Authentication.Cookies; 8 | using Microsoft.AspNetCore.Authorization; 9 | using Microsoft.AspNetCore.Mvc; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.IO; 13 | using System.Security.Claims; 14 | 15 | namespace CoolCat.Controllers 16 | { 17 | [Area("Admin")] 18 | public class SystemController : Controller 19 | { 20 | private readonly ISystemManager _systemManager; 21 | private readonly IPluginManager _pluginManager; 22 | private readonly IDbConnectionFactory _dbConnectionFactory; 23 | 24 | 25 | public SystemController(ISystemManager systemManager, IPluginManager pluginManager, IDbConnectionFactory dbConnectionFactory) 26 | { 27 | _systemManager = systemManager; 28 | _pluginManager = pluginManager; 29 | _dbConnectionFactory = dbConnectionFactory; 30 | } 31 | 32 | [HttpGet] 33 | public IActionResult Login() 34 | { 35 | if (User != null) 36 | { 37 | return RedirectToAction("Dashboard", "Home", new { Area = "Admin" }); 38 | } 39 | 40 | return View(); 41 | } 42 | 43 | [HttpPost] 44 | public IActionResult Login(LoginDTO dto) 45 | { 46 | if (ModelState.IsValid) 47 | { 48 | //Fake login 49 | if (dto.UserName == "admin" && dto.Password == "admin") 50 | { 51 | var authProperties = new AuthenticationProperties 52 | { 53 | 54 | RedirectUri = "/Admin/Home/Dashboard" 55 | }; 56 | 57 | 58 | var claims = new List 59 | { 60 | new Claim(ClaimTypes.Name, dto.UserName) 61 | }; 62 | 63 | var claimsIdentity = new ClaimsIdentity( 64 | claims, CookieAuthenticationDefaults.AuthenticationScheme); 65 | 66 | HttpContext.SignInAsync( 67 | CookieAuthenticationDefaults.AuthenticationScheme, 68 | new ClaimsPrincipal(claimsIdentity), 69 | authProperties); 70 | } 71 | else 72 | { 73 | ModelState.AddModelError("", "User name or password is wrong."); 74 | } 75 | } 76 | 77 | return View(); 78 | } 79 | 80 | public IActionResult Setup() 81 | { 82 | PresetPluginLoader presetPluginLoader = new PresetPluginLoader(); 83 | List plugins = presetPluginLoader.LoadPlugins(); 84 | 85 | if (plugins.Count == 0) 86 | { 87 | _systemManager.MarkAsInstalled(); 88 | return RedirectToAction("Index", "Home"); 89 | } 90 | 91 | return View(plugins); 92 | } 93 | 94 | [HttpPost] 95 | public IActionResult Install([FromBody] SetupModulesModel model) 96 | { 97 | if (model != null && model.Modules != null) 98 | { 99 | foreach (string module in model.Modules) 100 | { 101 | using (FileStream fs = new FileStream(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, GlobalConst.PresetFolder, module), FileMode.Open)) 102 | { 103 | _pluginManager.AddPlugins(new Core.DomainModel.PluginPackage(fs, _dbConnectionFactory)); 104 | } 105 | } 106 | } 107 | 108 | _systemManager.MarkAsInstalled(); 109 | 110 | return Ok(); 111 | } 112 | 113 | [HttpGet("SiteSettings")] 114 | [Authorize] 115 | public IActionResult SiteSettings() 116 | { 117 | var settings = _systemManager.GetSiteSettings(); 118 | return View(settings); 119 | } 120 | 121 | [HttpPost("SiteSettings")] 122 | [Authorize] 123 | public IActionResult SiteSettings(SiteSettingsDTO dto) 124 | { 125 | _systemManager.SaveSiteSettings(dto); 126 | 127 | var settings = _systemManager.GetSiteSettings(); 128 | return View(settings); 129 | } 130 | 131 | 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/CoolCat.Core.Mvc/Infrastructure/CoolCatStartup.cs: -------------------------------------------------------------------------------- 1 | using CoolCat.Core.BusinessLogic; 2 | using CoolCat.Core.Contracts; 3 | using CoolCat.Core.Helpers; 4 | using CoolCat.Core.Repositories; 5 | using CoolCat.Core.Repository.MySql; 6 | using CoolCat.Mvc.Infrastructure; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.AspNetCore.Mvc.Infrastructure; 9 | using Microsoft.AspNetCore.Mvc.Razor; 10 | using Microsoft.AspNetCore.Mvc.Razor.Compilation; 11 | using Microsoft.Extensions.Configuration; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Logging; 14 | using System; 15 | using System.Collections.Generic; 16 | using System.Linq; 17 | using System.Reflection; 18 | using System.Runtime.Loader; 19 | 20 | namespace CoolCat.Core.Mvc.Infrastructure 21 | { 22 | public static class CoolCatStartup 23 | { 24 | private static IServiceCollection _serviceCollection; 25 | 26 | public static IServiceCollection Services => _serviceCollection; 27 | 28 | public static void CoolCatSetup(this IServiceCollection services, IConfiguration configuration) 29 | { 30 | _serviceCollection = services; 31 | 32 | services.AddSingleton(); 33 | services.AddScoped(); 34 | services.AddSingleton(); 35 | services.AddScoped(); 36 | services.AddScoped(); 37 | services.AddScoped(); 38 | services.AddSingleton(); 39 | services.AddSingleton(CoolCatActionDescriptorChangeProvider.Instance); 40 | services.AddSingleton(); 41 | services.AddSingleton(); 42 | 43 | var documentation = new CoolCatModuleDocumentation(); 44 | 45 | services.AddSingleton(documentation); 46 | services.AddSingleton(CoolCatActionDescriptorChangeProvider.Instance); 47 | 48 | IMvcBuilder mvcBuilder = services.AddMvc(); 49 | 50 | ServiceProvider provider = services.BuildServiceProvider(); 51 | using (IServiceScope scope = provider.CreateScope()) 52 | { 53 | IUnitOfWork unitOfWork = scope.ServiceProvider.GetService(); 54 | var dataStore = new DefaultDataStore(scope.ServiceProvider.GetService>()); 55 | services.AddSingleton(dataStore); 56 | var contextProvider = new CollectibleAssemblyLoadContextProvider(); 57 | 58 | if (unitOfWork.CheckDatabase()) 59 | { 60 | List allEnabledPlugins = unitOfWork.PluginRepository.GetAllEnabledPlugins(); 61 | IReferenceLoader loader = scope.ServiceProvider.GetService(); 62 | 63 | foreach (ViewModels.PluginListItemViewModel plugin in allEnabledPlugins) 64 | { 65 | var context = contextProvider.Get(plugin.Name, mvcBuilder, scope, dataStore, documentation); 66 | PluginsLoadContexts.Add(plugin.Name, context); 67 | } 68 | } 69 | } 70 | 71 | AssemblyLoadContextResoving(); 72 | 73 | services.Configure(o => 74 | { 75 | o.AreaViewLocationFormats.Add("/Modules/{2}/Views/{1}/{0}" + RazorViewEngine.ViewExtension); 76 | o.AreaViewLocationFormats.Add("/Views/Shared/{0}.cshtml"); 77 | }); 78 | 79 | services.Replace(); 80 | } 81 | 82 | private static void AssemblyLoadContextResoving() 83 | { 84 | AssemblyLoadContext.Default.Resolving += (context, assembly) => 85 | { 86 | Func filter = p => p.Assemblies.Any(p => p.GetName().Name == assembly.Name 87 | && p.GetName().Version == assembly.Version); 88 | 89 | if (PluginsLoadContexts.All().Any(filter)) 90 | { 91 | Assembly ass = PluginsLoadContexts.All().First(filter) 92 | .Assemblies.First(p => p.GetName().Name == assembly.Name 93 | && p.GetName().Version == assembly.Version); 94 | return ass; 95 | } 96 | 97 | return null; 98 | }; 99 | } 100 | 101 | 102 | } 103 | 104 | } 105 | --------------------------------------------------------------------------------