Added audit log
This commit is contained in:
113
.idea/.idea.HopFrame/.idea/workspace.xml
generated
113
.idea/.idea.HopFrame/.idea/workspace.xml
generated
@@ -11,7 +11,31 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="0648788e-7696-4e60-bf12-5d5601f33d8c" name="Changes" comment="" />
|
<list default="true" id="0648788e-7696-4e60-bf12-5d5601f33d8c" name="Changes" comment="">
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/HopFrame.Core/Services/IPrimaryKeyFinder.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/HopFrame.Core/Services/Implementations/PrimaryKeyFinder.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/HopFrame.Web/AuditLogging/AuditLogContext.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/HopFrame.Web/AuditLogging/AuditLogEntry.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/HopFrame.Web/AuditLogging/AuditLogPlugin.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/src/HopFrame.Web/AuditLogging/ConfiguratorExtensions.cs" afterDir="false" />
|
||||||
|
<change afterPath="$PROJECT_DIR$/testing/HopFrame.Testing/AuthHandler.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/.idea/.idea.HopFrame/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.HopFrame/.idea/workspace.xml" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Core/ServiceCollectionExtensions.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Core/ServiceCollectionExtensions.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Components/Dialogs/HopFrameEditor.razor" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Components/Dialogs/HopFrameEditor.razor" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Components/Pages/HopFrameTablePage.razor" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Components/Pages/HopFrameTablePage.razor" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/EntryEvent.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/EntryEvent.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/HopFrameEventArgs.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/HopFrameEventArgs.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/PageChangeEvent.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/PageChangeEvent.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/ReloadEvent.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/ReloadEvent.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/SearchEvent.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/SearchEvent.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/TableInitializedEvent.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/TableInitializedEvent.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/ValidationEvent.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Events/ValidationEvent.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Internal/ExporterPlugin.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Internal/ExporterPlugin.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Internal/PluginOrchestrator.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Plugins/Internal/PluginOrchestrator.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Services/IFileService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Services/IFileService.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/src/HopFrame.Web/Services/Implementation/FileService.cs" beforeDir="false" afterPath="$PROJECT_DIR$/src/HopFrame.Web/Services/Implementation/FileService.cs" afterDir="false" />
|
||||||
|
<change beforePath="$PROJECT_DIR$/testing/HopFrame.Testing/Program.cs" beforeDir="false" afterPath="$PROJECT_DIR$/testing/HopFrame.Testing/Program.cs" afterDir="false" />
|
||||||
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||||
@@ -52,53 +76,13 @@
|
|||||||
}
|
}
|
||||||
}</component>
|
}</component>
|
||||||
<component name="HighlightingSettingsPerFile">
|
<component name="HighlightingSettingsPerFile">
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/02/6ae7626a/IList.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/5b/a350be00/IEnumerable.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/62/1fb63ed0/IDisposable.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/8b/db8582a3/IList`1.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/a0/0a968c53/IEnumerable`1.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/ad/ba9a50e7/ICollection.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/DecompilerCache/decompiler/c73b3c6c598640c592fd3c6fa226c286e90908/fc/6f7933d2/ICollection`1.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/0f73968de5cdfe0aa57817b8dd2a3c5d1db615ba4ae4629a5af59bb6c8922/RemoteNavigationManager.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/0f73968de5cdfe0aa57817b8dd2a3c5d1db615ba4ae4629a5af59bb6c8922/RemoteNavigationManager.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/10c66a9a1e137111895f7182a2ae246eabe06a261578c3fa495a45f6f177d35/IconVariant.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2f2b1d92f1ffcf2dabd664473f8c9dafa6039595ac228ce4b75a0f1619c5991/AsyncTaskMethodBuilderT.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/1b81cb3be224213a6a73519b6e340a628d9a1fb8629c351a186a26f6376669/List.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/24dd1164ba47541cb1d3eb011e638e16953dbea3ae3f4dc208c3bbf3e96298a/ServiceCollectionServiceExtensions.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/26c9a2fb5243863babc926e4be763daf4128d4f97c4a769cdce1e2e3e5c532/FluentButton.razor.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2751d5afefca5424bfc4b21347f581372f7a739c0ae4df661ea557fcb97ef20/EnumExtensions.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/2a4d2ce4c06ab596b3676c5cf06066b4391ec7dd93cdf8f0334b69dc1a9de/TextReader.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/439c4ee753b23e743cc14119593bc889751f9eb0b38997577d8e4c47c4fed/ToCollection.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/4c41a7d338749915157d56585365d1693fbad6be8231d3d583b1cf10d16896d9/FluentIcon.razor.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/4ee221fd7e91e9a4c14ff82aae2ee938edecde35a934133e991aba56aa9499/Icon.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/558c1d46e1e21d2e78ee2ab67a674f6927bf95355b2f245f35d74bb5ec0f92/CancellationTokenSource.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/5a69b82eed595b731b82667db08722b69b82482e275cf32dfb219190e3dc49/CollectionEntry.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/60e7b22380df80ef6fefe43138047f49ec6eff4b25c12b42ce3d6ed5aac/MethodInvokerCommon.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/6354a7b35d7821629924d3676acd7e67a6f7f94343e0e66ec439aa2bd6ed5/ThrowHelper.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/642391624bd5c30b3411a11434588aba4906207335166b784bf3a4325f6c7/NavigationEntry.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/6d1d64f05e7045295fa180276a8c2aef0302c9e96eb53b3431ab13db4579/FluentAppBarItem.razor.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/6fe785cceb29ca2d1da78e157315815a7c4372b582a20a71c28b210f9d56e/IconsExtensions.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/7ad7d2d0ae865063993eb8a03427815ea3bdb6a774e0a2f95512e9f669a4f489/MemberEntry.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/876cd892fc66a9dc8f6afd3704c264acebdfc46aed08089463e8117c21a532/String.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/87c584767b46b5fd42769be76547105558e6690f785614efddca134b2d682/Type.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/8d5d6cbff46ddc7b152381f92ae1ae51d3e7b57b14dd23840a11f5aaaaed396/InternalEntityEntry.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/a882d183338544fdbcbdfc7b6d3dcb78916630765551644a221b5be9c45a121b/Int32.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/aa3ea54f92373c58ec1149fbd41215869a98bd385c30584bc6db2fa3c6e88443/Filled24.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/adcd2c45092dd8e4fc412325c8adb75d6e7d8b3e90a9523f167583fb9c60/ServiceCollectionExtensions.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/b3ccb66df3646cb51df73ad51716136ebd2eefb4edb1308dd52a7e999582d59e/IBindableColumn.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/bfff78ecaa39c818519fc918bb2d4bbdca6ad93d7170f5cf325f67ccd0b97d43/BooleanAsserts.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d0165cb640e16fb3b8fe6932c042fc2917cd7f2770ff123cf7b9d11b5bfc6/Task.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d04a416cac8afac0341a8be0e859b230f2eae64924298eef48c317ba35916/RenderTreeBuilder.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d1287462d4ec4078c61b8e92a0952fb7de3e7e877d279e390a4c136a6365126/Stream.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d39923abb31e6a6e7a9e8173e217da584c54925ce63e568126a2b89b9ab/DefaultRazorComponentsServiceOptionsConfiguration.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/d858ddb35a8e36df5573b7612542f9ad50f426b8ab43818587d1ac65fab14829/DatabaseGeneratedAttribute.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/dac3553c90d47a746e7e7f02faecb1a5e581090/Components_AppBar_FluentAppBarItem_razor.g.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/e26a4f2df232f16e374b9719f883c1b2419f6341838d94b7581db9c7d2de17/IconInfo.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/eab2d6b892f743a27cb49a139ba782855897baf1233febd2dfd2092f3/EntityEntry.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/ece8533187fe96ce67b3ef1c9cc3502ef8da5510aadb132a9b21c5605d7c2119/PropertyColumn.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/ee4d234452e240d83e3de396c2e85cbf9ac9fb9add618b955eea196c81aaf8/IDialogContentComponent.cs" root0="SKIP_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/fc2027f7e776fc105cddb56b1a25eeb3895b3ae6f3aac854d786e63bd01f75e2/CallSiteFactory.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/fd57398b7dc3a8ce7da2786f2c67289c3d974658a9e90d0c1e84db3d965fbf1/Console.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$APPLICATION_CONFIG_DIR$/resharper-host/SourcesCache/ff37d54b3bf4d2756237fb789635831532603376e940f63d634b869d26d74c/Regular16.cs" root0="FORCE_HIGHLIGHTING" />
|
|
||||||
<setting file="file://$PROJECT_DIR$/src/HopFrame.Core/Config/DbContextConfig.cs" root0="FORCE_HIGHLIGHTING" />
|
<setting file="file://$PROJECT_DIR$/src/HopFrame.Core/Config/DbContextConfig.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="file://$PROJECT_DIR$/src/HopFrame.Web/AuditLogging/AuditLogEntry.cs" root0="FORCE_HIGHLIGHTING" />
|
||||||
|
<setting file="mock://C:/Users/leon/Documents/Projekte/HopFrame/tests/HopFrame.Tests.Core/Services/ContextExplorerTests.cs" root0="SKIP_HIGHLIGHTING" />
|
||||||
|
<setting file="mock://C:/Users/leon/Documents/Projekte/HopFrame/tests/HopFrame.Tests.Core/Services/DisplayPropertyTests.cs" root0="SKIP_HIGHLIGHTING" />
|
||||||
|
<setting file="mock://C:/Users/leon/Documents/Projekte/HopFrame/tests/HopFrame.Tests.Core/Services/TableManagerTests.cs" root0="SKIP_HIGHLIGHTING" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KubernetesApiPersistence">{}</component>
|
<component name="KubernetesApiPersistence">{}</component>
|
||||||
<component name="KubernetesApiProvider">{
|
<component name="KubernetesApiProvider">{
|
||||||
@@ -128,7 +112,7 @@
|
|||||||
"RunOnceActivity.git.unshallow": "true",
|
"RunOnceActivity.git.unshallow": "true",
|
||||||
"b5f11219-dfc4-47a1-b02c-90ab603034fb.executor": "Debug",
|
"b5f11219-dfc4-47a1-b02c-90ab603034fb.executor": "Debug",
|
||||||
"dcdf1689-dc07-47e4-8824-2e60a4fbf301.executor": "Debug",
|
"dcdf1689-dc07-47e4-8824-2e60a4fbf301.executor": "Debug",
|
||||||
"git-widget-placeholder": "feature/test-reports",
|
"git-widget-placeholder": "!37 on feature/audit-logging",
|
||||||
"list.type.of.created.stylesheet": "CSS",
|
"list.type.of.created.stylesheet": "CSS",
|
||||||
"node.js.detected.package.eslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.tslint": "true",
|
||||||
@@ -267,14 +251,7 @@
|
|||||||
<workItem from="1744966207145" duration="5231000" />
|
<workItem from="1744966207145" duration="5231000" />
|
||||||
<workItem from="1751713720880" duration="8243000" />
|
<workItem from="1751713720880" duration="8243000" />
|
||||||
<workItem from="1751741813788" duration="4623000" />
|
<workItem from="1751741813788" duration="4623000" />
|
||||||
</task>
|
<workItem from="1751803290506" duration="7489000" />
|
||||||
<task id="LOCAL-00016" summary="Added documentation for the configurators and service extensions methods">
|
|
||||||
<option name="closed" value="true" />
|
|
||||||
<created>1737208088933</created>
|
|
||||||
<option name="number" value="00016" />
|
|
||||||
<option name="presentableId" value="LOCAL-00016" />
|
|
||||||
<option name="project" value="LOCAL" />
|
|
||||||
<updated>1737208088933</updated>
|
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00017" summary="Created tests for the core module">
|
<task id="LOCAL-00017" summary="Created tests for the core module">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -660,7 +637,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1751750495636</updated>
|
<updated>1751750495636</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="65" />
|
<task id="LOCAL-00065" summary="pipeline cleanup">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1751803366875</created>
|
||||||
|
<option name="number" value="00065" />
|
||||||
|
<option name="presentableId" value="LOCAL-00065" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1751803366876</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="66" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -690,7 +675,6 @@
|
|||||||
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
|
<component name="UnityProjectConfiguration" hasMinimizedUI="false" />
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
||||||
<MESSAGE value="Added default button removal feature" />
|
|
||||||
<MESSAGE value="Added custom search functionality" />
|
<MESSAGE value="Added custom search functionality" />
|
||||||
<MESSAGE value="Added fully virtual properties" />
|
<MESSAGE value="Added fully virtual properties" />
|
||||||
<MESSAGE value="Added basic export and import feature" />
|
<MESSAGE value="Added basic export and import feature" />
|
||||||
@@ -715,6 +699,19 @@
|
|||||||
<MESSAGE value="Updated coverage extraction" />
|
<MESSAGE value="Updated coverage extraction" />
|
||||||
<MESSAGE value="fixed coverage percentage printing" />
|
<MESSAGE value="fixed coverage percentage printing" />
|
||||||
<MESSAGE value="fixed echo cmd" />
|
<MESSAGE value="fixed echo cmd" />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="fixed echo cmd" />
|
<MESSAGE value="pipeline cleanup" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="pipeline cleanup" />
|
||||||
|
</component>
|
||||||
|
<component name="XDebuggerManager">
|
||||||
|
<breakpoint-manager>
|
||||||
|
<breakpoints>
|
||||||
|
<breakpoint enabled="true" type="DotNet_Exception_Breakpoints">
|
||||||
|
<properties exception="Microsoft.AspNetCore.Components.NavigationException" breakIfHandledByUserCode="true" breakIfHandledByOtherCode="false" isInternal="false" displayValue="NavigationException" namespaceName="Microsoft.AspNetCore.Components">
|
||||||
|
<option name="internal" value="false" />
|
||||||
|
</properties>
|
||||||
|
<option name="timeStamp" value="3" />
|
||||||
|
</breakpoint>
|
||||||
|
</breakpoints>
|
||||||
|
</breakpoint-manager>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -18,6 +18,7 @@ public static class ServiceCollectionExtensions {
|
|||||||
services.TryAddScoped<IHopFrameAuthHandler, DefaultAuthHandler>();
|
services.TryAddScoped<IHopFrameAuthHandler, DefaultAuthHandler>();
|
||||||
services.TryAddScoped<ICallbackEmitter, CallbackEmitter>();
|
services.TryAddScoped<ICallbackEmitter, CallbackEmitter>();
|
||||||
services.AddScoped<ISearchExpressionBuilder, SearchExpressionBuilder>();
|
services.AddScoped<ISearchExpressionBuilder, SearchExpressionBuilder>();
|
||||||
|
services.AddScoped<IPrimaryKeyFinder, PrimaryKeyFinder>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
8
src/HopFrame.Core/Services/IPrimaryKeyFinder.cs
Normal file
8
src/HopFrame.Core/Services/IPrimaryKeyFinder.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HopFrame.Core.Config;
|
||||||
|
|
||||||
|
namespace HopFrame.Core.Services;
|
||||||
|
|
||||||
|
public interface IPrimaryKeyFinder {
|
||||||
|
PropertyInfo? GetPrimaryKeyInfo(TableConfig config);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Reflection;
|
||||||
|
using HopFrame.Core.Config;
|
||||||
|
|
||||||
|
namespace HopFrame.Core.Services.Implementations;
|
||||||
|
|
||||||
|
internal sealed class PrimaryKeyFinder(IContextExplorer explorer) : IPrimaryKeyFinder {
|
||||||
|
public PropertyInfo? GetPrimaryKeyInfo(TableConfig config) {
|
||||||
|
if (config.ContextConfig is RepositoryGroupConfig repoConfig) {
|
||||||
|
return repoConfig.KeyProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config.TableType
|
||||||
|
.GetProperties()
|
||||||
|
.FirstOrDefault(prop => prop
|
||||||
|
.GetCustomAttributes(true)
|
||||||
|
.Any(attr => attr is KeyAttribute));
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/HopFrame.Web/AuditLogging/AuditLogContext.cs
Normal file
9
src/HopFrame.Web/AuditLogging/AuditLogContext.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace HopFrame.Web.AuditLogging;
|
||||||
|
|
||||||
|
public sealed class AuditLogContext(DbContextOptions<AuditLogContext> options) : DbContext(options) {
|
||||||
|
|
||||||
|
public DbSet<AuditLogEntry> AuditLog { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
28
src/HopFrame.Web/AuditLogging/AuditLogEntry.cs
Normal file
28
src/HopFrame.Web/AuditLogging/AuditLogEntry.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace HopFrame.Web.AuditLogging;
|
||||||
|
|
||||||
|
public sealed class AuditLogEntry {
|
||||||
|
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id { get; init; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public required string User { get; init; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public required string Table { get; init; }
|
||||||
|
|
||||||
|
[MaxLength(255)]
|
||||||
|
public required string Record { get; init; }
|
||||||
|
|
||||||
|
public required AuditLogType Type { get; init; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AuditLogType : byte {
|
||||||
|
Create = 0x00,
|
||||||
|
Update = 0x01,
|
||||||
|
Delete = 0x02
|
||||||
|
}
|
||||||
72
src/HopFrame.Web/AuditLogging/AuditLogPlugin.cs
Normal file
72
src/HopFrame.Web/AuditLogging/AuditLogPlugin.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using HopFrame.Core.Config;
|
||||||
|
using HopFrame.Core.Services;
|
||||||
|
using HopFrame.Web.Plugins.Annotations;
|
||||||
|
using HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
|
namespace HopFrame.Web.AuditLogging;
|
||||||
|
|
||||||
|
internal sealed class AuditLogPlugin(AuditLogContext context, IContextExplorer explorer, IPrimaryKeyFinder keyFinder) {
|
||||||
|
|
||||||
|
[EventHandler]
|
||||||
|
public void OnInitialized(TableInitializedEvent e) {
|
||||||
|
if (e.Table.TableType != typeof(AuditLogEntry)) return;
|
||||||
|
|
||||||
|
e.DefaultButtons.ShowAddEntityButton = false;
|
||||||
|
e.DefaultButtons.ShowDeleteButton = false;
|
||||||
|
|
||||||
|
e.PluginButtons.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
[EventHandler]
|
||||||
|
public async Task OnCreate(CreateEntryEvent e) {
|
||||||
|
if (e.Table.TableType == typeof(AuditLogEntry)) return;
|
||||||
|
|
||||||
|
var record = new AuditLogEntry {
|
||||||
|
Type = AuditLogType.Create,
|
||||||
|
Table = e.Table.DisplayName,
|
||||||
|
Record = PrintEntity(e.Entity, e.Table),
|
||||||
|
User = e.Username
|
||||||
|
};
|
||||||
|
|
||||||
|
await context.AuditLog.AddAsync(record);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[EventHandler]
|
||||||
|
public async Task OnUpdate(UpdateEntryEvent e) {
|
||||||
|
if (e.Table.TableType == typeof(AuditLogEntry)) return;
|
||||||
|
|
||||||
|
var record = new AuditLogEntry {
|
||||||
|
Type = AuditLogType.Update,
|
||||||
|
Table = e.Table.DisplayName,
|
||||||
|
Record = PrintEntity(e.Entity, e.Table),
|
||||||
|
User = e.Username
|
||||||
|
};
|
||||||
|
|
||||||
|
await context.AuditLog.AddAsync(record);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[EventHandler]
|
||||||
|
public async Task OnDelete(DeleteEntryEvent e) {
|
||||||
|
if (e.Table.TableType == typeof(AuditLogEntry)) return;
|
||||||
|
|
||||||
|
var record = new AuditLogEntry {
|
||||||
|
Type = AuditLogType.Delete,
|
||||||
|
Table = e.Table.DisplayName,
|
||||||
|
Record = PrintEntity(e.Entity, e.Table),
|
||||||
|
User = e.Username
|
||||||
|
};
|
||||||
|
|
||||||
|
await context.AuditLog.AddAsync(record);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string PrintEntity(object entity, TableConfig config) {
|
||||||
|
var manager = explorer.GetTableManager(config.TableType);
|
||||||
|
|
||||||
|
var key = keyFinder.GetPrimaryKeyInfo(config);
|
||||||
|
return key?.GetValue(entity)?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
src/HopFrame.Web/AuditLogging/ConfiguratorExtensions.cs
Normal file
30
src/HopFrame.Web/AuditLogging/ConfiguratorExtensions.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using HopFrame.Core.Config;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace HopFrame.Web.AuditLogging;
|
||||||
|
|
||||||
|
public static class ConfiguratorExtensions {
|
||||||
|
|
||||||
|
public static HopFrameConfigurator AddAuditLogging(this HopFrameConfigurator configurator, IServiceCollection services, Action<DbContextOptionsBuilder> optionsBuilder) {
|
||||||
|
services.AddDbContext<AuditLogContext>(optionsBuilder);
|
||||||
|
|
||||||
|
configurator
|
||||||
|
.AddDbContext<AuditLogContext>()
|
||||||
|
.Table<AuditLogEntry>(table => {
|
||||||
|
table.Property(l => l.Id)
|
||||||
|
.List(false);
|
||||||
|
|
||||||
|
table.InnerConfig.Properties
|
||||||
|
.ForEach(p => p.Editable = false);
|
||||||
|
|
||||||
|
table.SetOrderIndex(int.MinValue);
|
||||||
|
table.SetDisplayName("Audit Log");
|
||||||
|
});
|
||||||
|
|
||||||
|
configurator.AddPlugin<AuditLogPlugin>();
|
||||||
|
|
||||||
|
return configurator;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -385,7 +385,7 @@
|
|||||||
if (value is null && property.IsRequired)
|
if (value is null && property.IsRequired)
|
||||||
errorList.Add($"{property.Name} is required");
|
errorList.Add($"{property.Name} is required");
|
||||||
|
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(new ValidationEvent(this) {
|
var eventResult = await PluginOrchestrator.DispatchEvent(new ValidationEvent(this, await Handler.GetCurrentUserDisplayNameAsync()) {
|
||||||
Errors = errorList,
|
Errors = errorList,
|
||||||
Property = property,
|
Property = property,
|
||||||
Table = Content.Config
|
Table = Content.Config
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
Sortable="@property.Sortable"/>
|
Sortable="@property.Sortable"/>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (DisplayActions && (_hasDeletePolicy || _hasUpdatePolicy)) {
|
@if (DisplayActions && (_hasDeletePolicy || _hasUpdatePolicy) && (_buttonToggles.ShowEditButton || _buttonToggles.ShowDeleteButton && _pluginButtons.Any(pb => pb.IsForTable(_config)))) {
|
||||||
<TemplateColumn Title="Actions" Align="@Align.End" Style="min-height: 44px; min-width: max-content">
|
<TemplateColumn Title="Actions" Align="@Align.End" Style="min-height: 44px; min-width: max-content">
|
||||||
@foreach (var button in _pluginButtons.Where(pb => pb.IsForTable(_config)).Where(pb => pb.Position == PluginButtonPosition.OnEntry)) {
|
@foreach (var button in _pluginButtons.Where(pb => pb.IsForTable(_config)).Where(pb => pb.Position == PluginButtonPosition.OnEntry)) {
|
||||||
<FluentButton OnClick="() => button.Handler.Invoke(context, _config!)">
|
<FluentButton OnClick="() => button.Handler.Invoke(context, _config!)">
|
||||||
@@ -226,10 +226,9 @@
|
|||||||
private List<PluginButton> _pluginButtons = new();
|
private List<PluginButton> _pluginButtons = new();
|
||||||
private DefaultButtonToggles _buttonToggles = new();
|
private DefaultButtonToggles _buttonToggles = new();
|
||||||
|
|
||||||
internal static HopFrameTablePage? CurrentInstance { get; private set; }
|
private string? _currentUser;
|
||||||
|
|
||||||
protected override void OnInitialized() {
|
protected override void OnInitialized() {
|
||||||
CurrentInstance = this;
|
|
||||||
_config ??= Explorer.GetTable(TableDisplayName);
|
_config ??= Explorer.GetTable(TableDisplayName);
|
||||||
|
|
||||||
if (_config is null || (_config.Ignored && DialogData is null)) {
|
if (_config is null || (_config.Ignored && DialogData is null)) {
|
||||||
@@ -243,7 +242,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(new TableInitializedEvent(this) {
|
_currentUser = await Handler.GetCurrentUserDisplayNameAsync();
|
||||||
|
|
||||||
|
var eventResult = await PluginOrchestrator.DispatchEvent(new TableInitializedEvent(this, _currentUser!) {
|
||||||
Table = _config!
|
Table = _config!
|
||||||
});
|
});
|
||||||
if (eventResult.IsCanceled) return;
|
if (eventResult.IsCanceled) return;
|
||||||
@@ -283,7 +284,7 @@
|
|||||||
|
|
||||||
await Task.Delay(500, _searchCancel.Token);
|
await Task.Delay(500, _searchCancel.Token);
|
||||||
|
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(new SearchEvent(this) {
|
var eventResult = await PluginOrchestrator.DispatchEvent(new SearchEvent(this, _currentUser!) {
|
||||||
SearchTerm = _searchTerm,
|
SearchTerm = _searchTerm,
|
||||||
Table = _config!,
|
Table = _config!,
|
||||||
CurrentPage = _currentPage
|
CurrentPage = _currentPage
|
||||||
@@ -333,7 +334,7 @@
|
|||||||
public async Task Reload() {
|
public async Task Reload() {
|
||||||
_loading = true;
|
_loading = true;
|
||||||
|
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(new ReloadEvent(this) {
|
var eventResult = await PluginOrchestrator.DispatchEvent(new ReloadEvent(this, _currentUser!) {
|
||||||
Table = _config!
|
Table = _config!
|
||||||
}, _tokenSource.Token);
|
}, _tokenSource.Token);
|
||||||
if (eventResult.IsCanceled) {
|
if (eventResult.IsCanceled) {
|
||||||
@@ -352,7 +353,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task ChangePage(int page) {
|
public async Task ChangePage(int page) {
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(new PageChangeEvent(this) {
|
var eventResult = await PluginOrchestrator.DispatchEvent(new PageChangeEvent(this, _currentUser!) {
|
||||||
CurrentPage = _currentPage,
|
CurrentPage = _currentPage,
|
||||||
NewPage = page,
|
NewPage = page,
|
||||||
TotalPages = _totalPages,
|
TotalPages = _totalPages,
|
||||||
@@ -372,16 +373,16 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(new DeleteEntryEvent(this) {
|
var dialog = await Dialogs.ShowConfirmationAsync("Do you really want to delete this entry?");
|
||||||
|
var result = await dialog.Result;
|
||||||
|
if (result.Cancelled) return;
|
||||||
|
|
||||||
|
var eventResult = await PluginOrchestrator.DispatchEvent(new DeleteEntryEvent(this, _currentUser!) {
|
||||||
Entity = element,
|
Entity = element,
|
||||||
Table = _config!
|
Table = _config!
|
||||||
}, _tokenSource.Token);
|
}, _tokenSource.Token);
|
||||||
if (eventResult.IsCanceled) return;
|
if (eventResult.IsCanceled) return;
|
||||||
|
|
||||||
var dialog = await Dialogs.ShowConfirmationAsync("Do you really want to delete this entry?");
|
|
||||||
var result = await dialog.Result;
|
|
||||||
if (result.Cancelled) return;
|
|
||||||
|
|
||||||
await _manager!.DeleteItem(element);
|
await _manager!.DeleteItem(element);
|
||||||
await Emitter.DispatchCallback(CallbackTypes.DeleteEntry(_config!), element);
|
await Emitter.DispatchCallback(CallbackTypes.DeleteEntry(_config!), element);
|
||||||
await Reload();
|
await Reload();
|
||||||
@@ -393,22 +394,6 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
HopFrameTablePageEventArgs eventArgs;
|
|
||||||
if (element is null) {
|
|
||||||
eventArgs = new CreateEntryEvent(this) {
|
|
||||||
Table = _config!
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
eventArgs = new UpdateEntryEvent(this) {
|
|
||||||
Table = _config!,
|
|
||||||
Entity = element
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var eventResult = await PluginOrchestrator.DispatchEvent(eventArgs, _tokenSource.Token);
|
|
||||||
if (eventResult.IsCanceled) return;
|
|
||||||
|
|
||||||
var panel = await Dialogs.ShowPanelAsync<HopFrameEditor>(new EditorDialogData(_config!, element), new DialogParameters {
|
var panel = await Dialogs.ShowPanelAsync<HopFrameEditor>(new EditorDialogData(_config!, element), new DialogParameters {
|
||||||
TrapFocus = false
|
TrapFocus = false
|
||||||
});
|
});
|
||||||
@@ -417,6 +402,23 @@
|
|||||||
|
|
||||||
if (result.Cancelled) return;
|
if (result.Cancelled) return;
|
||||||
|
|
||||||
|
HopFrameTablePageEventArgs eventArgs;
|
||||||
|
if (element is null) {
|
||||||
|
eventArgs = new CreateEntryEvent(this, _currentUser!) {
|
||||||
|
Table = _config!,
|
||||||
|
Entity = data!.CurrentObject!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
eventArgs = new UpdateEntryEvent(this, _currentUser!) {
|
||||||
|
Table = _config!,
|
||||||
|
Entity = data!.CurrentObject!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventResult = await PluginOrchestrator.DispatchEvent(eventArgs, _tokenSource.Token);
|
||||||
|
if (eventResult.IsCanceled) return;
|
||||||
|
|
||||||
if (element is null) {
|
if (element is null) {
|
||||||
await _manager!.AddItem(data!.CurrentObject!);
|
await _manager!.AddItem(data!.CurrentObject!);
|
||||||
await Emitter.DispatchCallback(CallbackTypes.CreateEntry(_config!), data.CurrentObject!);
|
await Emitter.DispatchCallback(CallbackTypes.CreateEntry(_config!), data.CurrentObject!);
|
||||||
@@ -430,7 +432,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void SelectItem(object item, bool selected) {
|
private void SelectItem(object item, bool selected) {
|
||||||
var eventResult = PluginOrchestrator.DispatchEvent(new SelectEntryEvent(this) {
|
var eventResult = PluginOrchestrator.DispatchEvent(new SelectEntryEvent(this, _currentUser!) {
|
||||||
Entity = item,
|
Entity = item,
|
||||||
Selected = selected,
|
Selected = selected,
|
||||||
Table = _config!
|
Table = _config!
|
||||||
|
|||||||
@@ -2,17 +2,19 @@
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public sealed class DeleteEntryEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
public sealed class DeleteEntryEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
public required object Entity { get; init; }
|
public required object Entity { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CreateEntryEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender);
|
public sealed class CreateEntryEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
|
|
||||||
public sealed class UpdateEntryEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
|
||||||
public required object Entity { get; init; }
|
public required object Entity { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class SelectEntryEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
public sealed class UpdateEntryEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
|
public required object Entity { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SelectEntryEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
public required object Entity { get; init; }
|
public required object Entity { get; init; }
|
||||||
public required bool Selected { get; set; }
|
public required bool Selected { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,25 @@ using HopFrame.Web.Components.Pages;
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public abstract class HopFrameEventArgs(object internalSender) {
|
public abstract class HopFrameEventArgs(object internalSender, string user) {
|
||||||
internal object InternalSender { get; } = internalSender;
|
internal object InternalSender { get; } = internalSender;
|
||||||
public bool IsCanceled { get; protected set; }
|
public bool IsCanceled { get; protected set; }
|
||||||
|
public string Username { get; set; } = user;
|
||||||
|
|
||||||
|
|
||||||
public void SetCancelled(bool canceled) => IsCanceled = canceled;
|
public void SetCancelled(bool canceled) => IsCanceled = canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class HopFrameEventArgs<TSender>(TSender sender) : HopFrameEventArgs(sender) where TSender : class {
|
public abstract class HopFrameEventArgs<TSender>(TSender sender, string user) : HopFrameEventArgs(sender, user) where TSender : class {
|
||||||
public TSender Sender => (TSender)InternalSender;
|
public TSender Sender => (TSender)InternalSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class HopFrameTablePageEventArgs(HopFrameTablePage sender)
|
public abstract class HopFrameTablePageEventArgs(HopFrameTablePage sender, string user)
|
||||||
: HopFrameEventArgs<HopFrameTablePage>(sender) {
|
: HopFrameEventArgs<HopFrameTablePage>(sender, user) {
|
||||||
public required TableConfig Table { get; init; }
|
public required TableConfig Table { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class HopFrameEditorEventArgs(HopFrameEditor sender)
|
public abstract class HopFrameEditorEventArgs(HopFrameEditor sender, string user)
|
||||||
: HopFrameEventArgs<HopFrameEditor>(sender) {
|
: HopFrameEventArgs<HopFrameEditor>(sender, user) {
|
||||||
public required TableConfig Table { get; init; }
|
public required TableConfig Table { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public sealed class PageChangeEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
public sealed class PageChangeEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
public required int CurrentPage { get; init; }
|
public required int CurrentPage { get; init; }
|
||||||
public required int TotalPages { get; init; }
|
public required int TotalPages { get; init; }
|
||||||
public required int NewPage { get; set; }
|
public required int NewPage { get; set; }
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public sealed class ReloadEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
public sealed class ReloadEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ using HopFrame.Web.Components.Pages;
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public sealed class SearchEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
public sealed class SearchEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
public required string SearchTerm { get; set; }
|
public required string SearchTerm { get; set; }
|
||||||
public required int CurrentPage { get; init; }
|
public required int CurrentPage { get; init; }
|
||||||
internal IEnumerable<object>? SearchResult { get; set; }
|
internal IEnumerable<object>? SearchResult { get; set; }
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Microsoft.FluentUI.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public class TableInitializedEvent(HopFrameTablePage sender) : HopFrameTablePageEventArgs(sender) {
|
public class TableInitializedEvent(HopFrameTablePage sender, string user) : HopFrameTablePageEventArgs(sender, user) {
|
||||||
public List<PluginButton> PluginButtons { get; } = new();
|
public List<PluginButton> PluginButtons { get; } = new();
|
||||||
public DefaultButtonToggles DefaultButtons { get; set; } = new();
|
public DefaultButtonToggles DefaultButtons { get; set; } = new();
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ public enum PluginButtonPosition {
|
|||||||
OnEntry = 2
|
OnEntry = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct DefaultButtonToggles() {
|
public class DefaultButtonToggles {
|
||||||
public bool ShowRefreshButton { get; set; } = true;
|
public bool ShowRefreshButton { get; set; } = true;
|
||||||
public bool ShowAddEntityButton { get; set; } = true;
|
public bool ShowAddEntityButton { get; set; } = true;
|
||||||
public bool ShowDeleteButton { get; set; } = true;
|
public bool ShowDeleteButton { get; set; } = true;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using HopFrame.Web.Components.Dialogs;
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Events;
|
namespace HopFrame.Web.Plugins.Events;
|
||||||
|
|
||||||
public sealed class ValidationEvent(HopFrameEditor sender) : HopFrameEditorEventArgs(sender) {
|
public sealed class ValidationEvent(HopFrameEditor sender, string user) : HopFrameEditorEventArgs(sender, user) {
|
||||||
public required IList<string> Errors { get; init; }
|
public required IList<string> Errors { get; init; }
|
||||||
public required PropertyConfig Property { get; init; }
|
public required PropertyConfig Property { get; init; }
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@ using Microsoft.FluentUI.AspNetCore.Components;
|
|||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Internal;
|
namespace HopFrame.Web.Plugins.Internal;
|
||||||
|
|
||||||
internal sealed class ExporterPlugin(IContextExplorer explorer, IToastService toasts, IFileService files) {
|
internal sealed class ExporterPlugin(IContextExplorer explorer, IToastService toasts, IFileService files, IPrimaryKeyFinder finder) {
|
||||||
public const char Separator = ';';
|
public const char Separator = ';';
|
||||||
|
|
||||||
[EventHandler]
|
[EventHandler]
|
||||||
@@ -51,7 +51,7 @@ internal sealed class ExporterPlugin(IContextExplorer explorer, IToastService to
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task Import(TableConfig table, HopFrameTablePage target) {
|
private async Task Import(TableConfig table, HopFrameTablePage target) {
|
||||||
var file = await files.UploadFile();
|
var file = await files.UploadFile(target);
|
||||||
|
|
||||||
var stream = file.OpenReadStream();
|
var stream = file.OpenReadStream();
|
||||||
var reader = new StreamReader(stream);
|
var reader = new StreamReader(stream);
|
||||||
@@ -178,17 +178,8 @@ internal sealed class ExporterPlugin(IContextExplorer explorer, IToastService to
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Type? GetPrimaryKeyType(Type tableType) {
|
private Type? GetPrimaryKeyType(Type tableType) {
|
||||||
var table = explorer.GetTable(tableType);
|
var table = explorer.GetTable(tableType)!;
|
||||||
if (table?.ContextConfig is RepositoryGroupConfig repoConfig) {
|
return finder.GetPrimaryKeyInfo(table)?.PropertyType;
|
||||||
return repoConfig.KeyProperty.PropertyType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableType
|
|
||||||
.GetProperties()
|
|
||||||
.FirstOrDefault(prop => prop
|
|
||||||
.GetCustomAttributes(true)
|
|
||||||
.Any(attr => attr is KeyAttribute))?
|
|
||||||
.PropertyType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private object? ParseString(string input, Type targetType) {
|
private object? ParseString(string input, Type targetType) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using HopFrame.Web.Plugins.Annotations;
|
using HopFrame.Web.Plugins.Annotations;
|
||||||
using HopFrame.Web.Plugins.Events;
|
using HopFrame.Web.Plugins.Events;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace HopFrame.Web.Plugins.Internal;
|
namespace HopFrame.Web.Plugins.Internal;
|
||||||
|
|
||||||
@@ -24,15 +25,18 @@ internal sealed class PluginOrchestrator(IServiceProvider services) : IPluginOrc
|
|||||||
Handler = method
|
Handler = method
|
||||||
};
|
};
|
||||||
collection.AddSingleton(container);
|
collection.AddSingleton(container);
|
||||||
collection.AddScoped(plugin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collection.AddScoped(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TEvent> DispatchEvent<TEvent>(TEvent @event, CancellationToken ct = new()) where TEvent : HopFrameEventArgs {
|
public async Task<TEvent> DispatchEvent<TEvent>(TEvent @event, CancellationToken ct = new()) where TEvent : HopFrameEventArgs {
|
||||||
var eventContainers = services.GetRequiredService<IEnumerable<PluginEventContainer>>()
|
var eventContainers = services.GetRequiredService<IEnumerable<PluginEventContainer>>()
|
||||||
.Where(container => container.EventType == typeof(TEvent));
|
.Where(container => container.EventType == @event.GetType())
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
var eventType = typeof(TEvent);
|
|
||||||
|
var eventType = @event.GetType();
|
||||||
var tokenType = typeof(CancellationToken);
|
var tokenType = typeof(CancellationToken);
|
||||||
foreach (var container in eventContainers) {
|
foreach (var container in eventContainers) {
|
||||||
var plugin = services.GetRequiredService(container.Handler.DeclaringType!);
|
var plugin = services.GetRequiredService(container.Handler.DeclaringType!);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.AspNetCore.Components.Forms;
|
using HopFrame.Web.Components.Pages;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
|
||||||
namespace HopFrame.Web.Services;
|
namespace HopFrame.Web.Services;
|
||||||
|
|
||||||
@@ -18,6 +19,6 @@ public interface IFileService {
|
|||||||
/// Allows the user to upload a file and returns the uploaded file for processing.
|
/// Allows the user to upload a file and returns the uploaded file for processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A task that returns an IBrowserFile representing the uploaded file.</returns>
|
/// <returns>A task that returns an IBrowserFile representing the uploaded file.</returns>
|
||||||
public Task<IBrowserFile> UploadFile();
|
public Task<IBrowserFile> UploadFile(HopFrameTablePage page);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -12,19 +12,16 @@ internal sealed class FileService(IJSRuntime runtime) : IFileService {
|
|||||||
await runtime.InvokeVoidAsync("downloadFileFromStream", name, stream);
|
await runtime.InvokeVoidAsync("downloadFileFromStream", name, stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IBrowserFile> UploadFile() {
|
public Task<IBrowserFile> UploadFile(HopFrameTablePage page) {
|
||||||
var result = new TaskCompletionSource<IBrowserFile>();
|
var result = new TaskCompletionSource<IBrowserFile>();
|
||||||
|
|
||||||
if (HopFrameTablePage.CurrentInstance is null)
|
page.OnFileUpload = files => {
|
||||||
result.SetException(new InvalidOperationException("No table page visible"));
|
|
||||||
|
|
||||||
HopFrameTablePage.CurrentInstance!.OnFileUpload = files => {
|
|
||||||
result.SetResult(files.First());
|
result.SetResult(files.First());
|
||||||
HopFrameTablePage.CurrentInstance.OnFileUpload = null;
|
page.OnFileUpload = null;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
};
|
};
|
||||||
|
|
||||||
runtime.InvokeVoidAsync("triggerClick", HopFrameTablePage.CurrentInstance.FileInputElement!.Element);
|
runtime.InvokeVoidAsync("triggerClick", page.FileInputElement!.Element);
|
||||||
return result.Task;
|
return result.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
testing/HopFrame.Testing/AuthHandler.cs
Normal file
12
testing/HopFrame.Testing/AuthHandler.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using HopFrame.Core.Services;
|
||||||
|
|
||||||
|
namespace HopFrame.Testing;
|
||||||
|
|
||||||
|
public class AuthHandler : IHopFrameAuthHandler {
|
||||||
|
public Task<bool> IsAuthenticatedAsync(string? policy) {
|
||||||
|
return Task.FromResult(true);
|
||||||
|
}
|
||||||
|
public Task<string> GetCurrentUserDisplayNameAsync() {
|
||||||
|
return Task.FromResult("Leon Hoppe");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
|
using HopFrame.Core.Services;
|
||||||
using HopFrame.Testing;
|
using HopFrame.Testing;
|
||||||
using Microsoft.FluentUI.AspNetCore.Components;
|
using Microsoft.FluentUI.AspNetCore.Components;
|
||||||
using HopFrame.Testing.Components;
|
using HopFrame.Testing.Components;
|
||||||
using HopFrame.Testing.Models;
|
using HopFrame.Testing.Models;
|
||||||
using HopFrame.Web;
|
using HopFrame.Web;
|
||||||
|
using HopFrame.Web.AuditLogging;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Message = HopFrame.Testing.Models.Message;
|
using Message = HopFrame.Testing.Models.Message;
|
||||||
|
|
||||||
@@ -113,10 +115,15 @@ builder.Services.AddHopFrame(options => {
|
|||||||
.ForceRelation()
|
.ForceRelation()
|
||||||
.Format((u, _) => u.Username ?? string.Empty);
|
.Format((u, _) => u.Username ?? string.Empty);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
options.AddAuditLogging(builder.Services, contextBuilder => {
|
||||||
|
contextBuilder.UseInMemoryDatabase("audit-logging");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddSingleton<MessageRepository>();
|
builder.Services.AddSingleton<MessageRepository>();
|
||||||
builder.Services.AddSingleton<GuestRepository>();
|
builder.Services.AddSingleton<GuestRepository>();
|
||||||
|
builder.Services.AddScoped<IHopFrameAuthHandler, AuthHandler>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user